11 - Maven Useful Tips

Maven Best practices

Development best practices should be used by all the developers. To enforce the best practices, usage of some good tools will help to verify if they are applied.

Maven for Enterprise

Properties should not be inherited. Changing a parent property value could break the build because the property could be used also by a child POM.

Properties must be used for values that will change in the future and also values used in several places.

Each Technology (Hibernate, Spring, C++) must have its own POM.

Dependency versions should be defined by properties. This helps to visualize fast the used versions without searching them through the POM.

Dependency management section in parent POM is useful to avoid duplicating dependencies in child modules.

In child POMs the versions, scope and exclusions are inherited from the parent POM and they have not to be declared only the names of the dependencies.

Run often the command mvn dependency:analyze to find the possible used dependencies and undeclared or unused and declared. The goal can be executed also in test-compile phase.

To know how the dependencies are organized hierarchically the following command can be used:

       mvn dependency:tree

For example if the a part of the spring framework is used as dependency in pom :

<project..>
    <dependencies>
        <dependency>
                <groupId>org.springframework</groupId>
                <artifactId>spring</artifactId>
                <version>2.5.6</version>
            </dependency>
            <dependency>
                <groupId>org.springframework</groupId>
                <artifactId>spring-agent</artifactId>
                <version>2.5</version>
            </dependency>
            <dependency>
                <groupId>org.springframework</groupId>
                <artifactId>spring-aspects</artifactId>
                <version>2.5.3</version>
                <exclusions>
                    <exclusion>
                        <groupId>commons-logging</groupId>
                        <artifactId>commons-logging</artifactId>
                    </exclusion>
                </exclusions>
            </dependency>
            <dependency>
                <groupId>org.springframework.security</groupId>
                <artifactId>spring-security-core</artifactId>
                <version>2.0.4</version>
                <exclusions>
                    <exclusion>
                        <groupId>org.springframework</groupId>
                        <artifactId>spring-aop</artifactId>
                    </exclusion>
                </exclusions>
            </dependency>            
            <dependency>
                <groupId>org.springframework</groupId>
                <artifactId>spring-webmvc-struts</artifactId>
                <version>2.5.6</version>
            </dependency>
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>3.8.1</version>
            <scope>test</scope>
        </dependency>
    </dependencies>    
….
    <project>

In the console will be displayed all the direct dependencies and their sub-dependencies. All the dependencies marked with + are required by their above dependency in the tree. The exclusions are marked with – (minus) sign .

The dependency tree can save time for debugging a failed build.

The result in the console is:

[INFO] Scanning for projects…
[INFO]                                                                        
[INFO] ------------------------------------------------------------------------
[INFO] Building somecompany-app 1.0-SNAPSHOT
[INFO] ------------------------------------------------------------------------
[INFO]
[INFO] --- maven-dependency-plugin:2.8:tree (default-cli) @ somecompany-app ---
[INFO] com.somecompany.app:somecompany-app:jar:1.0-SNAPSHOT
[INFO] +- org.springframework:spring:jar:2.5.6:compile
[INFO] |  \- commons-logging:commons-logging:jar:1.1.1:compile
[INFO] +- org.springframework:spring-agent:jar:2.5:compile
[INFO] +- org.springframework:spring-aspects:jar:2.5.3:compile
[INFO] |  +- org.aspectj:aspectjrt:jar:1.5.4:compile
[INFO] |  +- org.aspectj:aspectjweaver:jar:1.5.4:compile
[INFO] |  \- org.springframework:spring-beans:jar:2.5.3:compile
[INFO] +- org.springframework.security:spring-security-core:jar:2.0.4:compile
[INFO] |  +- org.springframework:spring-core:jar:2.0.8:compile
[INFO] |  +- org.springframework:spring-context:jar:2.0.8:compile
[INFO] |  |  \- aopalliance:aopalliance:jar:1.0:compile
[INFO] |  +- org.springframework:spring-support:jar:2.0.8:runtime
[INFO] |  +- commons-codec:commons-codec:jar:1.3:compile
[INFO] |  \- commons-collections:commons-collections:jar:3.2:compile
[INFO] +- org.springframework:spring-webmvc-struts:jar:2.5.6:compile
[INFO] |  +- org.springframework:spring-web:jar:2.5.6:compile
[INFO] |  \- org.springframework:spring-webmvc:jar:2.5.6:compile
[INFO] |     \- org.springframework:spring-context-support:jar:2.5.6:compile
[INFO] \- junit:junit:jar:3.8.1:test
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
[INFO] Total time: 1.082 s
[INFO] Finished at: 2014-10-22T21:33:32+03:00
[INFO] Final Memory: 8M/20M
[INFO] ------------------------------------------------------------------------

Dependencies can be automatically ordered alphabetically and dependency resolution is useful to compare the dependencies of some projects and plug-in dependencies. The following goals have to be used:

mvn dependency:resolve
mvn dependency:resolve plugins

Built versions in the development environment should be always SNAPSHOTS because Maven provides always the latest (timestamped) version.

SNAPSHOTs should be built only locally.

Release versions should never have SNAPSHOT dependencies.

If we want to use the latest version of a library that releases too often we can use a version greater than or equal to a value as version range:

<version>[1.1.0)</version>

The version for the project can be set with the command:

mvn versions:set –DnewVersion=1.1

Scopes of the dependencies should be used only it is necessary

Example:

       runtime (jdbc drivers)

       test(Junit, DBUnit)

       provided( Java EE jars)

Dependencies are loaded in the order in which they are declared in the pom.xml.  Dependencies from inheritance have to be added last. This help for debugging easily NoClassDefFoundError.

If a project depends on an external dependency is recommended to depend on the latest release version.

Dependencies with newer versions available can be displayed with the command:

mvn versions:display-dependency-updates

The result in the console is:

mvn versions:display-dependency-updates
[INFO] Scanning for projects...
...............
[INFO]                                                                        
[INFO] ------------------------------------------------------------------------
[INFO] Building somecompany-app 1.0-SNAPSHOT
[INFO] ------------------------------------------------------------------------
............
[INFO] artifact org.springframework:spring-aspects: checking for updates from central
[INFO] artifact org.springframework:spring-webmvc-struts: checking for updates from central
[INFO] artifact org.springframework.security:spring-security-core: checking for updates from central
[INFO] The following dependencies in Dependencies have newer versions:
[INFO]   junit:junit ..................................... 3.8.1 -> 4.12-beta-2
[INFO]   org.springframework:spring ...................... 2.5.6 -> 2.5.6.SEC03
[INFO]   org.springframework:spring-agent .................. 2.5 -> 2.5.6.SEC03
[INFO]   org.springframework:spring-aspects ............ 2.5.3 -> 4.1.1.RELEASE
[INFO]   org.springframework:spring-webmvc-struts ........ 2.5.6 -> 2.5.6.SEC03
[INFO]   org.springframework.security:spring-security-core ...
[INFO]                                                   2.0.4 -> 3.2.5.RELEASE
[INFO]
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
[INFO] Total time: 10.086 s
[INFO] Finished at: 2014-10-22T22:48:07+03:00
[INFO] Final Memory: 10M/24M
[INFO] ------------------------------------------------------------------------

If a local repository is used in order to download dependencies it has to be declared in the file settings.xml.

<localRepository>c:/temp/mavenrepo</localRepository>

Source code organization has to follow the structure:

Src/main/java
Src/webapp
Src/test/java

Do not change this structure.

Individual modules should be release separately if they are that type of modules used in more applications. In general applications in maintenance should be release as a whole.

Use Maven Enforcer plugin in order to get control over environmental constraints:

Maven version >=2.2.1

JDK version >=1.6

Spring jars version >=3.1

Commons – logging or commons-logging-api dependencies should not be used

Log4j dependencies should not be used

SLF4J version>=1.5

 

Use Maven CheckStyle plugin in order to check that code rules are respected.

Add this plugin to the plugins tag inside the POM.

<project….>
  …
  <build>
    …
      <plugin>
        <groupId>org.apache.maven.plugins</groupId>
        <artifactId>maven-checkstyle-plugin</artifactId>
        <version>2.14</version>
<configuration> 
                 <configLocation>checkstyle.xml</configLocation> 
               </configuration>
      </plugin>
    </plugins>
  </build>
</project>

The checkstyle.xml file will be located in the same folder with the file pom.xml.

We can run builds separately for one module from command line with the statement with –rf argument that means resume:

mvn –rf myproject-webapp

or two modules:

mvn –pl myproject-database,myproject-logon clean install.

Profiles can be automatically activated and is useful to know which profiles are used by Maven for the build. We can use the command

mvn help:active-profiles

Projects and sub-projects can be updated recursively if the SCM is configured in the parent POM. The following statement has to be used:

mvn scm:update-subprojects

SCM – source code/control management or version control (CVS,SVN,etc) has to be defined in POM if the Maven project uses such a system.

<project..>
  ...
  <scm>
    <connection>scm:svn:http://127.0.0.1/svn/my-project</connection>
    <developerConnection>scm:svn:https://127.0.0.1/svn/my-project</developerConnection>
    <tag>HEAD</tag>
    <url>http://127.0.0.1/websvn/my-project</url>
  </scm>
  …
</project>

If tests or build consumes a lot of time it is recommended to launch them in 2 threads per core with the command

mvn clean tests –T2C
mvn install –T2C

Maven can be used with Jenkins which is a CI ( continuous integration server) to build projects ( maven projects) and to produce the corresponding assembly ( war/jar/ear). Jenkins monitors what Maven is doing and reacts automatically. The Surefire unit tests will be automatically captured. Javadoc is captured too. So Jenkins records what Maven did.

For large module projects Jenkins can produce parallel builds. Jenkins can build only modules that are affected of new commits and the modules depending on those modules.

The projects are continuous built whenenver their dependencies change providing a rapid error feedback time.

Maven can be used not only for builds but also for  project life-cycle management too. Knowing more hidden features of Maven will help a developer to be more efficient while using Maven.

Like us on Facebook