Wednesday, 21 September 2011

Dissecting Spring's MVC Project POM

One of the good things about Spring’s STS is that is provides a whole bunch of useful Spring project templates that you can use to generate empty or stub projects from which to start writing some code. The list of projects includes a ‘Simple Spring Utility Project’ and a ‘Spring Batch Admin WebApp’.



I’ve not had need to generate all these types of project, but I know that when you create a ‘Spring MVC Project’, you get a complete Maven webapp with a very complete POM file.

Opening up the WebApp’s pom.xml the first thing that strikes you is what a lot stuff you get - value for money or what? The file contains loads of dependencies and plugins and all kinds of stuff and it's all just to build a simple webapp. It does make you wonder whether it’s all needed, especially when you’re sitting in a cafe’ waiting whilst the empty application shell is downloading an endless string of JARs via a 3G connection.

This blog takes a look at the webapp’s POM file and tries to shed a little more light on what’s in there and why, and I’ve done that by commenting the original POM, which is below.

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <groupId>com.captaindebug</groupId>
    <artifactId>test-and-delete</artifactId>
    <name>My Temp Web-App Project Name</name>
    <packaging>war</packaging>
    <version>1.0.0-BUILD-SNAPSHOT</version>
    <!-- 
    This is one of my favourite Maven tricks - specify the various dependency version
    out-right at the top of the file - making updating version very easy
     -->
    <properties>
        <java-version>1.6</java-version>
        <org.springframework-version>3.0.5.RELEASE</org.springframework-version>
        <org.springframework.roo-version>1.0.2.RELEASE</org.springframework.roo-version>
        <org.aspectj-version>1.6.9</org.aspectj-version>
        <org.slf4j-version>1.5.10</org.slf4j-version>
    </properties>
    <dependencies>
        <!-- These are the Spring dependencies, you need two 'context' and 'mvc' -->
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-context</artifactId>
            <version>${org.springframework-version}</version>
            <!-- 
            It seems that the Guys at Spring have forsaken Common logging in 
            favour of SLF4J - not surprising as Commons Logging is notorious
            for having Class Loader problems (although I've never had any)
             -->
            <exclusions>
                <!-- Exclude Commons Logging in favor of SLF4j -->
                <exclusion>
                    <groupId>commons-logging</groupId>
                    <artifactId>commons-logging</artifactId>
                 </exclusion>
            </exclusions>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-webmvc</artifactId>
            <version>${org.springframework-version}</version>
        </dependency>
        
        <!--
        Okay, this is optional for a Java app and you can delete it. 
         -->
        <!-- Roo dependencies -->
        <dependency>
            <groupId>org.springframework.roo</groupId>
            <artifactId>org.springframework.roo.annotations</artifactId>
            <version>${org.springframework.roo-version}</version>
            <scope>provided</scope>
        </dependency>
                
        <!-- AspectJ optional if you're not into AOP -->
        <dependency>
            <groupId>org.aspectj</groupId>
            <artifactId>aspectjrt</artifactId>
            <version>${org.aspectj-version}</version>
        </dependency>    
        
        <!-- 
        Logging - this is the SLF4J configuration linking into Log4J 
        -->
        <dependency>
            <groupId>org.slf4j</groupId>
            <artifactId>slf4j-api</artifactId>
            <version>${org.slf4j-version}</version>
        </dependency>
        <dependency>
            <groupId>org.slf4j</groupId>
            <artifactId>jcl-over-slf4j</artifactId>
            <version>${org.slf4j-version}</version>
            <scope>runtime</scope>
        </dependency>
        <dependency>
            <groupId>org.slf4j</groupId>
            <artifactId>slf4j-log4j12</artifactId>
            <version>${org.slf4j-version}</version>
            <scope>runtime</scope>
        </dependency>
        <dependency>
            <groupId>log4j</groupId>
            <artifactId>log4j</artifactId>
            <version>1.2.15</version>
            <exclusions>
                <exclusion>
                    <groupId>javax.mail</groupId>
                    <artifactId>mail</artifactId>
                </exclusion>
                <exclusion>
                    <groupId>javax.jms</groupId>
                    <artifactId>jms</artifactId>
                </exclusion>
                <exclusion>
                    <groupId>com.sun.jdmk</groupId>
                    <artifactId>jmxtools</artifactId>
                </exclusion>
                <exclusion>
                    <groupId>com.sun.jmx</groupId>
                    <artifactId>jmxri</artifactId>
                </exclusion>
            </exclusions>
            <scope>runtime</scope>
        </dependency>

        <!-- 
        @Inject 
        This is JSR-330: Dependency Injection for Java - 
        the basic webapp will work without it
        -->
        <dependency>
            <groupId>javax.inject</groupId>
            <artifactId>javax.inject</artifactId>
            <version>1</version>
        </dependency>
                
        <!-- 
        These are straight forward Servlet/Web App/JSP dependencies
        nothing to see here
        -->
        <dependency>
            <groupId>javax.servlet</groupId>
            <artifactId>servlet-api</artifactId>
            <version>2.5</version>
            <scope>provided</scope>
        </dependency>
        <dependency>
            <groupId>javax.servlet.jsp</groupId>
            <artifactId>jsp-api</artifactId>
            <version>2.1</version>
            <scope>provided</scope>
        </dependency>
        <dependency>
            <groupId>javax.servlet</groupId>
            <artifactId>jstl</artifactId>
            <version>1.2</version>
        </dependency>
    
        <!-- 
        Test dependencies - add in other libs like EasyMock here 
        -->
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.7</version>
            <scope>test</scope>
        </dependency>
        
    </dependencies>
    <!-- 
    If you're building a straight forward web-app with released versions 
    available at Maven Central then you don't really need this bit. 
    If you work for Spring and you're developing/testing then this 
    could be important
    -->
    <repositories>
        <!-- For testing against latest Spring snapshots -->
        <repository>
            <id>org.springframework.maven.snapshot</id>
            <name>Spring Maven Snapshot Repository</name>
            <url>http://maven.springframework.org/snapshot</url>
            <releases><enabled>false</enabled></releases>
            <snapshots><enabled>true</enabled></snapshots>
        </repository>
        <!-- For developing against latest Spring milestones -->
        <repository>
            <id>org.springframework.maven.milestone</id>
            <name>Spring Maven Milestone Repository</name>
            <url>http://maven.springframework.org/milestone</url>
            <snapshots><enabled>false</enabled></snapshots>
        </repository>
    </repositories>
    <build>
        <plugins>
            <!-- Okay, so you do need a compiler -->
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <configuration>
                    <source>${java-version}</source>
                    <target>${java-version}</target>
                </configuration>
            </plugin>
            <!-- 
            The WAR plugin is optional - it's used here to name the WAR output
            file - in this case abc.war
            -->
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-war-plugin</artifactId>
                <configuration>
                    <warName>abc</warName>
                </configuration>
            </plugin>
            <!-- 
            Resolves all the project's sources from the repositories 
            Optional - if you don't want to get hold of sources.
            -->
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-dependency-plugin</artifactId>
                <executions>
                    <execution>
                        <id>install</id>
                        <phase>install</phase>
                        <goals>
                            <goal>sources</goal>
                        </goals>
                    </execution>
                </executions>
            </plugin>
            <!-- 
            This is required for AspectJ - allows you to weave in aspectJ stuff
            It's optional - if you're not using aspectj then you can 
            remove this plugin 
            -->
            <plugin>
                <groupId>org.codehaus.mojo</groupId>
                <artifactId>aspectj-maven-plugin</artifactId>
                <!-- Have to use version 1.2 since version 1.3 does not appear to work with ITDs -->
                <version>1.2</version>
                <dependencies>
                    <!-- You must use Maven 2.0.9 or above or these are ignored (see MNG-2972) -->
                    <dependency>
                        <groupId>org.aspectj</groupId>
                        <artifactId>aspectjrt</artifactId>
                        <version>${org.aspectj-version}</version>
                    </dependency>
                    <dependency>
                        <groupId>org.aspectj</groupId>
                        <artifactId>aspectjtools</artifactId>
                        <version>${org.aspectj-version}</version>
                    </dependency>
                </dependencies>
                <executions>
                    <execution>
                        <goals>
                            <goal>compile</goal>
                            <goal>test-compile</goal>
                        </goals>
                    </execution>
                </executions>
                <configuration>
                    <outxml>true</outxml>
                    <source>${java-version}</source>
                    <target>${java-version}</target>
                </configuration>
            </plugin>
            <!-- 
            Surefire plugin - do some tests and generate some reports - 
            in this case exclude all Roo files
            -->
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-surefire-plugin</artifactId>
                <configuration>
                    <junitArtifactName>junit:junit</junitArtifactName>
                    <excludes>
                        <exclude>**/*_Roo_*</exclude>
                    </excludes>
                </configuration>
            </plugin>
            <!-- 
            Tomcat deployment plugin
            -->
            <plugin>
                <groupId>org.codehaus.mojo</groupId>
                <artifactId>tomcat-maven-plugin</artifactId>
                <version>1.0-beta-1</version>
            </plugin>                        
        </plugins>
    </build>
</project>

I guess that the pom.xml should really be taken for what it is: a working sample. Most organisations I’ve come across generally organise their POM files in to some kind of hierarchy with a super-pom at the top containing the most general dependencies such as compiler plugin and version, or JUnit version etc, down to individual project POMs with the idea being that, to sprinkle in a few cliche's, you can line all your ducks up in a row, without duplicating effort and repeating yourself

No comments: