Other Modules

Now that you have one module working it is time to move on to the other modules. If you follow the order of the modules described at the beginning of the chapter you will be fine, otherwise you will find that the main classes from some of the modules reference classes from modules that have not yet been built. See figure 8-1 to get the overall picture of the interdependencies between the Spring modules.

Avoiding Duplication

As soon as you begin migrating the second module, you will find that you are repeating yourself. For instance, you will be adding the Surefire plugin configuration settings repeatedly for each module that you convert. To avoid duplication, move these configuration settings to the parent POM instead. That way, each of the modules will be able to inherit the required Surefire configuration.
In the same way, instead of repeatedly adding the same dependency version information to each module, use the parent POM’s dependencyManagement section to specify this information once, and remove the versions from the individual modules (see Chapter 3 for more information).
Using the parent POM to centralize this information makes it possible to upgrade a dependency version across all sub-projects from a single location.

<dependencyManagement>
  <dependencies>
    <dependency>
      <groupId>commons-logging</groupId>
      <artifactId>commons-logging</artifactId>
      <version>1.0.4</version>
    </dependency>
  </dependencies>
</dependencyManagement>

The following are some variables that may also be helpful to reduce duplication:

  • ${project.version}: version of the current POM being built
  • ${project.groupId}: groupId of the current POM being built

For example, you can refer to spring-core from spring-beans with the following, since they have the same groupId and version:

<dependency>
  <groupId>${project.groupId}</groupId>
  <artifactId>spring-core</artifactId>
  <version>${project.version}</version>
</dependency>

Referring to Test Classes from Other Modules

If you have tests from one component that refer to tests from other modules, there is a procedure you can use. Although it is typically not recommended, in this case it is necessary to avoid refactoring the test source code. First, make sure that when you run mvn install, that a JAR that contains the test classes is also installed in the repository:

<plugin>
  <groupId>org.apache.maven.plugins</groupId>
  <artifactId>maven-jar-plugin</artifactId>
  <executions>
    <execution>
      <goals>
        <goal>test-jar</goal>
      </goals>
    </execution>
  </executions>
</plugin>

Once that JAR is installed, you can use it as a dependency for other components, by specifying the test-jar type. However, be sure to put that JAR in the test scope as follows:

<dependency>
  <groupId>${project.groupId}</groupId>
  <artifactId>spring-beans</artifactId>
  <version>${project.version}</version>
  <type>test-jar</type>
  <scope>test</scope>
</dependency>

A final note on referring to test classes from other modules: if you have all of Spring’s mock classes inside the same module, this can cause previously-described cyclic dependencies problem. To eliminate this problem, you can split Spring’s mock classes into spring-context-mock, with only those classes related to spring-context module, and spring-web-mock, with only those classes related to spring-web. Generally with Maven, it’s easier to deal with small modules, particularly in light of transitive dependencies.

Building Java 5 Classes

Some of Spring’s modules include Java 5 classes from the tiger folder. As the compiler plugin was earlier configured to compile with Java 1.3 compatibility, how can the Java 1.5 sources be added? To do this with Maven, you need to create a new module with only Java 5 classes instead of adding them to the same module and mixing classes with different requirements. So, you will need to create a new spring-beans-tiger module.
Consider that if you include some classes compiled for Java 1.3 and some compiled for Java 5 in the same JAR, any users, attempting to use one of the Java 5 classes under Java 1.3 or 1.4, would experience runtime errors. By splitting them into different modules, users will know that if they depend on the module composed of Java 5 classes, they will need to run them under Java 5.
As with the other modules that have been covered, the Java 5 modules will share a common configuration for the compiler. The best way to split them is to create a tiger folder with the Java 5 parent POM, and then a directory for each one of the individual tiger modules, as follows:


Figure 1-3: A tiger module directory
The final directory structure should appear as follows:

Figure 1-4: The final directory structure

Figure 1-5: Dependency relationship, with all modules
In the tiger POM, you will need to add a module entry for each of the directories, as well as build sections with source folders and compiler options:

<modules>
  <module>spring-beans-tiger</module>
  <module>spring-aop-tiger</module>
  <module>spring-dao-tiger</module>
  <module>spring-jdbc-tiger</module>
  <module>spring-support-tiger</module>
  <module>spring-hibernate3-tiger</module>
  <module>spring-aspects</module>
</modules>
<build>
  <sourceDirectory>../../../tiger/src</sourceDirectory>
  <testSourceDirectory>../../../tiger/test</testSourceDirectory>
  <plugins>
    <plugin>
      <groupId>org.apache.maven.plugins</groupId>
      <artifactId>maven-compiler-plugin</artifactId>
      <configuration>
        <source>1.5</source>
        <target>1.5</target>
      </configuration>
    </plugin>
  </plugins>
</build>

In the parent POM, you just need a new module entry for the tiger folder, but to still be able to build the other modules when using Java 1.4 you will add that module in a profile that will be triggered only when using 1.5 JDK.

<profiles>
  <profile>
    <id>jdk1.5</id>
    <activation>
      <jdk>1.5</jdk>
    </activation>
    <modules>
      <module>tiger</module>
    </modules>
  </profile>
</profiles>

Using Ant Tasks From Inside Maven
In certain migration cases, you may find that Maven does not have a plugin for a particular task or an Ant target is so small that it may not be worth creating a new plugin. In this case, Maven can call Ant tasks directly from a POM using the maven-antrun-plugin.
For example, with the Spring migration, you need to use the Ant task in the spring-remoting module to use the RMI compiler.
From Ant, this is:

<rmic base="${target.classes.dir}"
  classname="org.springframework.remoting.rmi.RmiInvocationWrapper"/>

<rmic base="${target.classes.dir}"
  classname="org.springframework.remoting.rmi.RmiInvocationWrapper" iiop="true">
  <classpath refid="all-libs"/>
</rmic>

To include this in Maven build, add:

<plugin>
  <groupId>org.apache.maven.plugins</groupId>
  <artifactId>maven-antrun-plugin</artifactId>
  <executions>
    <execution>
      <phase>process-classes</phase>
      <configuration>
        <tasks>
         <echo>Running rmic</echo>
         <rmic base="${project.build.directory}/classes"
          classname="org.springframework.remoting.rmi.RmiInvocationWrapper"/>
         <rmic base="${project.build.directory}/classes"
          classname="org.springframework.remoting.rmi.RmiInvocationWrapper"
               iiop="true"/>
         <classpath refid="maven.compile.classpath"/>
        </rmic>
        </tasks>
      </configuration>
      <goals>
        <goal>run</goal>
      </goals>
    </execution>
  </executions>
  <dependencies>
    <dependency>
      <groupId>com.sun</groupId>
      <artifactId>tools</artifactId>
      <scope>system</scope>
      <version>1.4</version>
      <systemPath>${java.home}/../lib/tools.jar</systemPath>
    </dependency>
  </dependencies>
</plugin>

As shown in the code snippet above, there are some references available already, such as ${project.build.directory} and maven.compile.classpath, which is a classpath reference constructed from all of the dependencies in the compile scope or lower. There are also references for anything that was added to the plugin’s dependencies section, which applies to that plugin only, such as the reference to the tools.jar above, which is bundled with the JDK, and required by the RMI task.
To complete the configuration, you will need to determine when Maven should run the Ant task. In this case, the rmic task, will take the compiled classes and generate the rmi skeleton, stub and tie classes from them. So, the most appropriate phase in which to run this Ant task is in the process-classes phase.
Non-redistributable Jars
You will find that some of the modules in the Spring build depend on JARs that are not available in the Maven central repository. For example, Sun’s Activation Framework and JavaMail are not redistributable from the repository due to constraints in their licenses. You may need to download them yourself from the Sun site or get them from the lib directory in the example code for this chapter. You can then install them in your local repository with the following command.

mvn install:install-file -Dfile= -DgroupId= -DartifactId= -Dversion= -Dpackaging=

For instance, to install JavaMail:

mvn install:install-file -Dfile=[path_to_file]/mail.jar -DgroupId=javax.mail -DartifactId=mail -Dversion=1.3.2 -Dpackaging=jar

You will only need to do this process once for all of your projects or you may use a corporate repository to share them across your organization. For more information on dealing with this issue, see http://maven.apache.org/guides/mini/guide-coping-with-sun-jars.html.

Some Special Cases

In addition to the procedures outlined previously for migrating Spring to Maven, there are two additional, special cases that must be handled. These issues were shared with the Spring developer community and are listed below:

  • Moving one test class, NamespaceHandlerUtilsTests, from test directory to tiger/test directory – as it depends on a tiger class
  • Fixing toplink tests that don’t compile against the oracle toplink jar (Spring developers use a different version than the official one from Oracle)

In this example it is necessary to comment out two unit tests, which used relative paths in Log4JConfigurerTests class, as these test cases will not work in both Maven and Ant. Using classpath resources is recommended over using file system resources.

There is some additional configuration required for some modules, such as spring-aspects, which uses AspectJ for weaving the classes. These can be viewed in the example code.

Thank you for requesting a Maestro evaluation! This is our passion, and we want you to be successful. Please let us know how we may help!

Please enter your name, company email address and phone, and we will send you a link to your pre-built hosted evaluation within minutes.






I have read and agree to the Terms and Conditions.