Where to Begin?

With Maven, the rule of thumb to use is to produce one artifact (JAR, WAR, etc.) per Maven project file. In the Spring example, that means you will need to have a Maven project (a POM) for each of the modules listed above.

To start, you will create a subdirectory called ‘m2’ to keep all the necessary Maven changes clearly separated from the current build system. Inside the ‘m2’ directory, you will need to create a directory for each of Spring’s modules.


Figure 8-2: A sample spring module directory

In the m2 directory, you will need to create a parent POM. You will use the parent POM to store the common configuration settings that apply to all of the modules. For example, each module will inherit the following values (settings) from the parent POM.

  • groupId: this setting indicates your area of influence, company, department, project, etc., and it should mimic standard package naming conventions to avoid duplicate values. For this example, you will use com.exist.m2book.migrating, as it is our ‘unofficial’ example version of Spring; however, the Spring team would use org.springframework
  • artifactId: the setting specifies the name of this module (for example, spring-parent)
  • version: this setting should always represent the next release version number appended with - SNAPSHOT – that is, the version you are developing in order to release. Recall from previous chapters that during the release process, Maven will convert to the definitive, non-snapshot version for a short period of time, in order to tag the release in your SCM.
  • packaging: the jar, war, and ear values should be obvious to you (a pom value means that this project is used for metadata only)

The other values are not strictly required, and are primarily used for documentation purposes.

<groupId>com.exist.m2book.migrating</groupId>
<artifactId>spring-parent</artifactId>
<version>2.0-m1-SNAPSHOT</version>
<name>Spring parent</name>
<packaging>pom</packaging>
<description>Spring Framework</description>
<inceptionYear>2002</inceptionYear>
<url>http://www.springframework.org</url>
<organization>
    <name>The Spring Framework Project</name>
</organization>

In this parent POM we can also add dependencies such as JUnit, which will be used for testing in every module, thereby eliminating the requirement to specify the dependency repeatedly across multiple modules.

<dependencies>
    <dependency>
        <groupId>junit</groupId>
        <artifactId>junit</artifactId>
        <version>3.8.1</version>
        <scope>test</scope>
    </dependency>
</dependencies>

As explained previously, in Spring, the main source and test directories are src and test, respectively. Let’s begin with these directories.

Using the following code snippet from Spring’s Ant build script, in the buildmain target, you can retrieve some of the configuration parameters for the compiler.

<javac destdir="${target.classes.dir}" source="1.3" target="1.3" debug="${debug}"
 deprecation="false" optimize="false" failonerror="true">
 <src path="${src.dir}"/>
    <!-- Include Commons Attributes generated Java sources -->
    <src path="${commons.attributes.tempdir.src}"/>
    <classpath refid="all-libs"/>
</javac>

As you can see these include the source and target compatibility (1.3), deprecation and optimize (false), and failonerror (true) values. These last three properties use Maven’s default values, so there is no need for you to add the configuration parameters.

For the debug attribute, Spring’s Ant script uses a debug parameter, so to specify the required debug function in Maven, you will need to append -Dmaven.compiler.debug=false to the mvn command (by default this is set to true). For now, you don’t have to worry about the commons-attributes generated sources mentioned in the snippet, as you will learn about that later in this chapter. Recall from Chapter 2, that Maven automatically manages the classpath from its list of dependencies.

At this point, your build section will look like this:

<build>
    <sourceDirectory>../../src</sourceDirectory>
    <testSourceDirectory>../../test</testSourceDirectory>
    <plugins>
        <plugin>
            <groupId>org.apache.maven.plugins</groupId>
            <artifactId>maven-compiler-plugin</artifactId>
            <configuration>
                <source>1.3</source>
                <target>1.3</target>
            </configuration>
         </plugin>
    </plugins>
</build>

The other configuration that will be shared is related to the JUnit tests. From the tests target in the Ant script:

<junit forkmode="perBatch" printsummary="yes" haltonfailure="yes" haltonerror="yes">

    <jvmarg line="-Djava.awt.headless=true -XX:MaxPermSize=128m -Xmx128m"/>

    <!-- Must go first to ensure any jndi.properties files etc take precedence -->
    <classpath location="${target.testclasses.dir}"/>
    <classpath location="${target.mockclasses.dir}"/>
    <classpath location="${target.classes.dir}"/>

    <!-- Need files loaded as resources -->
    <classpath location="${test.dir}"/>

    <classpath refid="all-libs"/>

    <formatter type="plain" usefile="false"/>
    <formatter type="xml"/>

    <batchtest fork="yes" todir="${reports.dir}">
        <fileset dir="${target.testclasses.dir}" includes="${test.includes}" excludes="${test.excludes}"/>
    </batchtest>
</junit>

You can extract some configuration information from the previous code:

  • forkMode=”perBatch” matches with Maven’s forkMode parameter with a value of once, since the concept of a batch for testing does not exist.
  • You will not need any printsummary, haltonfailure and haltonerror settings, as Maven prints the test summary and stops for any test error or failure, by default.
  • formatter elements are not required as Maven generates both plain text and xml reports.
  • The nested element jvmarg is mapped to the configuration parameter argLine
  • As previously noted, classpath is automatically managed by Maven from the list of dependencies.
  • Maven sets the reports destination directory (todir) to target/surefire-reports, by default, and this doesn’t need to be changed.
  • You will need to specify the value of the properties test.includes and test.excludes from the nested fileset; this value is read from the project.properties file loaded from the Ant script (refer to the code snippet below for details).
  • Maven uses the default value from the compiler plugin, so you will not need to locate the test classes directory (dir).
# Wildcards to be matched by JUnit tests.
# Convention is that our JUnit test classes have XXXTests-style names.
test.includes=**/*Tests.class
#
# Wildcards to exclude among JUnit tests.
# Second exclude needs to be used for JDK 1.3, due to Hibernate 3.1
# being compiled with target JDK 1.4.
test.excludes=**/Abstract*
#test.excludes=**/Abstract* org/springframework/orm/hibernate3/**

The includes and excludes referenced above, translate directly into the include/exclude elements of the POM’s plugin configuration.

Note: Since Maven requires JDK 1.4 to run you do not need to exclude hibernate3 tests. Note that it is possible to use another lower JVM to run tests if you wish – refer to the Surefire plugin reference documentation for more information.
<plugin>
    <groupId>org.apache.maven.plugins</groupId>
    <artifactId>maven-surefire-plugin</artifactId>
    <configuration>
        <forkMode>once</forkMode>
        <childDelegation>false</childDelegation>
        <argLine>
            -Djava.awt.headless=true -XX:MaxPermSize=128m -Xmx128m
        </argLine>
        <includes>
            <include>**/*Tests.class</include>
        </includes>
        <excludes>
            <exclude>**/Abstract*</exclude>
        </excludes>
    </configuration>
</plugin>

The childDelegation option is required to prevent conflicts when running under Java 5 between the XML parser provided by the JDK and the one included in the dependencies in some modules, mandatory when building in JDK 1.4. It makes tests run using the standard classloader delegation instead of the default Maven isolated classloader. When building only on Java 5 you could remove that option and the XML parser (Xerces) and APIs (xml-apis) dependencies.

Spring’s Ant build script also makes use of the commons-attributes compiler in its compileattr and compiletestattr targets, which are processed prior to the compilation. The commons-attributes compiler processes javadoc style annotations – it was created before Java supported annotations in the core language on JDK 1.5 – and generates sources from them that have to be compiled with the normal Java compiler.

From compileattr:

<!-- Compile to a temp directory: Commons Attributes will place Java Source here. -->
<attribute-compiler destdir="${commons.attributes.tempdir.src}">
    <!--
    Only the PathMap attribute in the org.springframework.web.servlet.handler.metadata
    package currently needs to be shipped with an attribute, to support indexing.
    -->
    <fileset dir="${src.dir}" includes="**/metadata/*.java"/>
</attribute-compiler>

From compiletestattr:

<!-- Compile to a temp directory: Commons Attributes will place Java Source here. -->
<attribute-compiler destdir="${commons.attributes.tempdir.test}">
    <fileset dir="${test.dir}" includes="org/springframework/aop/**/*.java"/>
    <fileset dir="${test.dir}" includes="org/springframework/jmx/**/*.java"/>
</attribute-compiler>

In Maven, this same function can be accomplished by adding the commons-attributes plugin to the build section in the POM. Maven handles the source and destination directories automatically, so you will only need to add the inclusions for the main source and test source compilation.

<plugin>
    <groupId>org.codehaus.mojo</groupId>
    <artifactId>commons-attributes-maven-plugin</artifactId>
    <executions>
        <execution>
            <configuration>
                <includes>
                    <include>**/metadata/*.java</include>
                </includes>
                <testIncludes>
                    <include>org/springframework/aop/**/*.java</include>
                    <include>org/springframework/jmx/**/*.java</include>
                </testIncludes>
            </configuration>
            <goals>
                <goal>compile</goal>
                <goal>test-compile</goal>
            </goals>
        </execution>
    </executions>
</plugin>

Later in this chapter you will need to modify these test configurations.

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.