Building Spring with maven2

Probably you already noticed that maven2 beta-1 is out,
and I’m quite sure that you are wondering if it’s ready to be used in a
real world. Well, you can try to build the Spring Framework with it as
a example of a not too simple application, with a lot of dependencies.

What do you need:

  • Spring 1.2.4 distribution
  • Maven 2 beta-1
  • A bunch of non redistributable Sun jars due to its licensing
    schema (hopefully they’re changing that stupid restriction). When maven
    doesn’t find the jars it will show where you can download them, getting
    that information from the poms in the repository. If not you can fill an issue.
    Of course you can get them also from the Spring distribution, geronimo
    open source ones,… Copy them to your local repo, by default under
    $HOME/.m2/repository.
    • javax/transaction/jta/1.0.1B/jta-1.0.1B.jar
    • javax/jdo/jdo/2.0-20050809.1515/jdo-2.0-20050809.1515.jar
    • javax/jms/jms/1.1/jms-1.1.jar
    • javax/ejb/ejb/2.0/ejb-2.0.jar
    • javax/xml/jaxrpc/1.1/jaxrpc-1.1.jar
    • javax/resource/connector/1.0/connector-1.0.jar
    • javax/mail/mail/1.3.2/mail-1.3.2.jar
    • javax/faces/jsf-api/1.1/jsf-api-1.1.jar

Split
the sources in the
different spring subprojects, because they come in the same directory.
You can use this ant target, adding it to the end of spring build.xml
file and run “ant
splitsources”, which will create a “m2″ folder with a subfolder for
each subproject.

    <target name="splitsources" description="Split the sources in subprojects">

        <copy todir="m2/all/src/main/java">
            <fileset dir="${src.dir}"/>
        </copy>

        <copy todir="m2/all/src/main/java">
            <fileset dir="tiger/src"/>
        </copy>

        <copy todir="m2/all/src/test/java">
            <fileset dir="${test.dir}"/>
        </copy>

        <copy todir="m2/all/src/test/java">
            <fileset dir="tiger/test"/>
        </copy>

        <copy todir="m2/spring-core/src/main/java">
            <fileset dir="m2/all/src/main/java">
                <include name="org/springframework/core/**"/>
                <include name="org/springframework/util/**"/>
            </fileset>
        </copy>

        <copy todir="m2/spring-beans/src/main/java">
            <fileset dir="m2/all/src/main/java">
                <include name="org/springframework/beans/**"/>
            </fileset>
        </copy>

        <copy todir="m2/spring-aop/src/main/java">
            <fileset dir="m2/all/src/main/java">
                <include name="org/springframework/aop/**"/>
                <include name="org/springframework/metadata/**"/>
            </fileset>
        </copy>

        <copy todir="m2/spring-context/src/main/java">
            <fileset dir="m2/all/src/main/java">
                <include name="org/springframework/context/**"/>
                <include name="org/springframework/jndi/**"/>
                <include name="org/springframework/ui/**"/>
                <include name="org/springframework/validation/**"/>
            </fileset>
        </copy>

        <copy todir="m2/spring-dao/src/main/java">
            <fileset dir="m2/all/src/main/java">
                <include name="org/springframework/dao/**"/>
                <include name="org/springframework/transaction/**"/>
            </fileset>
        </copy>

        <copy todir="m2/spring-jdbc/src/main/java">
            <fileset dir="m2/all/src/main/java">
                <include name="org/springframework/jdbc/**"/>
            </fileset>
        </copy>

        <copy todir="m2/spring-support/src/main/java">
            <fileset dir="m2/all/src/main/java">
                <include name="org/springframework/cache/**"/>
                <include name="org/springframework/jca/**"/>
                <include name="org/springframework/jmx/**"/>
                <include name="org/springframework/mail/**"/>
                <include name="org/springframework/scheduling/**"/>
            </fileset>
        </copy>

        <copy todir="m2/spring-web/src/main/java">
            <fileset dir="m2/all/src/main/java">
                <include name="org/springframework/web/**"/>
                <exclude name="org/springframework/web/servlet/**"/>
            </fileset>
        </copy>

        <copy todir="m2/spring-webmvc/src/main/java">
            <fileset dir="m2/all/src/main/java">
                <include name="META-INF/*.tld"/>
                <include name="org/springframework/web/servlet/**"/>
            </fileset>
        </copy>

        <copy todir="m2/spring-remoting/src/main/java">
            <fileset dir="m2/all/src/main/java">
                <include name="org/springframework/ejb/**"/>
                <include name="org/springframework/jms/**"/>
                <include name="org/springframework/remoting/**"/>
            </fileset>
        </copy>

        <copy todir="m2/spring-orm/src/main/java">
            <fileset dir="m2/all/src/main/java">
                <include name="org/springframework/orm/**"/>
                <exclude name="org/springframework/orm/hibernate/**"/>
                <exclude name="org/springframework/orm/hibernate3/**"/>
            </fileset>
        </copy>

        <copy todir="m2/spring-hibernate/src/main/java">
            <fileset dir="m2/all/src/main/java">
                <include name="org/springframework/orm/hibernate/**"/>
                <include name="org/springframework/orm/hibernate3/**"/>
            </fileset>
        </copy>

        <copy todir="m2/spring-mock/src/main/java">
            <fileset dir="${mock.dir}"/>
        </copy>

        <copy todir="m2/spring-test/src/test/java">
            <fileset dir="m2/all/src/test/java"/>
        </copy>

    </target>

Download the poms
and uncompress them in the directory you uncompressed spring. Go to the
m2 directory and run any m2 goal, like “m2 install”, that will copy to
your local repo all the spring jars (remember to remove them so the
official ones get downloaded from ibiblio next time you use maven2 for
any other purpose).

You can take a look at how simple are the poms now, for instance the
spring-hibernate project, that needs a lot of jars in the classpath to
run is as simple as:

<project>
  <parent>
    <artifactId>spring-parent</artifactId>
    <groupId>springframework</groupId>
    <version>1.2.4</version>
  </parent>
  <modelVersion>4.0.0</modelVersion>
  <artifactId>spring-hibernate</artifactId>
  <version>1.2.4</version>
  <dependencies>
    <dependency>
      <groupId>springframework</groupId>
      <artifactId>spring-orm</artifactId>
      <version>1.2.4</version>
    </dependency>
    <dependency>
      <groupId>org.hibernate</groupId>
      <artifactId>hibernate-annotations</artifactId>
      <version>3.0beta2</version>
    </dependency>
    <dependency>
      <groupId>org.hibernate</groupId>
      <artifactId>hibernate</artifactId>
      <version>3.0.5</version>
    </dependency>
    <dependency>
      <groupId>net.sf.hibernate</groupId>
      <artifactId>hibernate</artifactId>
      <version>2.1.8</version>
    </dependency>
  </dependencies>
</project>

You’ll notice that tests are not running, the reason is that spring
test sources can’t be splitted in subprojects because they don’t follow
the modular desing and have circular dependencies, eg. a test class in
spring-core uses a class in spring-beans whenspring-beans depends in spring-core. I’ll post an entry about this soon.

Writing file system independent applications with commons-vfs

Lately I’ve been playing with commons virtual file system
(commons-vfs), which is a really cool way to make your application file
system independent. You can write the same code to access files in your
local file system, samba servers, sftp servers, http, webdav,… see the complete list.

        FileSystemManager fileSystemManager = VFS.getManager();
        FileObject from = fileSystemManager.resolveFile(getFrom());
        FileObject to = fileSystemManager.resolveFile(getTo(), getFileSystemOptions());
        to.copyFrom(from, new AllFileSelector());

The application context with Spring would be something like the
following. You can see that setting the file system options it’s a pita
because they don’t follow the beans convention and you need to call
different methods instead of setting bean properties. I hop they change
ths in next versions.

    <bean id="myBean" class="TestBean">
        <property name="from" value="http://www.myserver.com/filename"/>
        <property name="to" value="sftp://username:password@myserver.com/home/whatever"/>
        <property name="fileSystemOptions" ref="sftpFileSystemOptions"/>
    </bean>

    <bean id="sftpFileSystemConfigBuilder"
          class="org.apache.commons.vfs.provider.sftp.SftpFileSystemConfigBuilder"
          factory-method="getInstance"/>

    <bean id="sftpFileSystemOptions" class="org.apache.commons.vfs.FileSystemOptions"/>

    <bean id="sftpFileSystemOptions_disableStrictHostKeyChecking"
          class="org.springframework.beans.factory.config.MethodInvokingFactoryBean">
        <property name="targetObject" ref="sftpFileSystemConfigBuilder"/>
        <property name="targetMethod" value="setStrictHostKeyChecking"/>
        <property name="arguments">
            <list>
                <ref local="sftpFileSystemOptions"/>
                <value>no</value>
            </list>
        </property>
    </bean>

    <!-- use private key instead of password -->
    <bean id="sftpFileSystemOptions_setIdentities"
          class="org.springframework.beans.factory.config.MethodInvokingFactoryBean">
        <property name="targetObject" ref="sftpFileSystemConfigBuilder"/>
        <property name="targetMethod" value="setIdentities"/>
        <property name="arguments">
            <list>
               <ref local="sftpFileSystemOptions"/>
               <list><value>Path to the ssh identity file</value></list>
            </list>
        </property>
    </bean>

Another drawback is that you need to build from sources, no releases are available yet :(