Keeping Java Code up to Date with Maven

Keeping track of all the dependencies used in a sizable Java project is no small task, and that's one job of the pom.xml file in Maven. The pom.xml does a lot more than keep an inventory of dependencies used within a Java project, but for the purposes of this article the dependencies section is the focus.

There is plenty of documentation on the pom.xml of Maven, but for a brief example this is a snippet of a pom.xml:

	<dependencies>
		<dependency>
			<groupId>ch.qos.logback</groupId>
			<artifactId>logback-classic</artifactId>
			<version>1.1.7</version>
		</dependency>
		<dependency>
			<groupId>commons-pool</groupId>
			<artifactId>commons-pool</artifactId>
			<version>1.6</version>
			<type>jar</type>
		</dependency>
		<dependency>
			<groupId>commons-io</groupId>
			<artifactId>commons-io/artifactId>
			<version>2.4</version>
		</dependency>
	</dependencies>

This is all very good for a single jar, but when a large project is broken up into may jars the management of all dependencies may become an issue. Here again, it's Maven to the rescue, for Maven supplies the dependency:tree and dependency:analyze goals. An example of the output from mvn dependency:tree is:

$ mvn dependency:tree
[INFO] Scanning for projects...
[INFO]
[INFO] ------------------------------------------------------------------------
[INFO] Building Populate 1.2
[INFO] ------------------------------------------------------------------------
[INFO]
[INFO] --- maven-dependency-plugin:2.8:tree (default-cli) @ Populate ---
[INFO] GMS:Populate:jar:1.2
[INFO] +- GMS:GMSFramework:jar:1.1:compile
[INFO] |  +- ch.qos.logback:logback-classic:jar:1.1.7:compile
[INFO] |  |  +- ch.qos.logback:logback-core:jar:1.1.7:compile
[INFO] |  |  \- org.slf4j:slf4j-api:jar:1.7.20:compile
[INFO] |  +- commons-pool:commons-pool:jar:1.6:compile
[INFO] |  +- commons-io:commons-io:jar:2.4:compile
[INFO] |  +- mysql:mysql-connector-java:jar:5.1.35:compile
[INFO] |  +- postgresql:postgresql:jar:9.1-901-1.jdbc4:compile
[INFO] |  +- net.htmlparser.jericho:jericho-html:jar:3.4:compile
[INFO] |  +- com.jcraft:jsch:jar:0.1.53:compile
[INFO] |  \- org.apache.derby:derby:jar:10.12.1.1:compile
[INFO] +- GMS:GMSStringComp:jar:1.0:compile
[INFO] +- org.springframework:spring-core:jar:4.2.4.RELEASE:compile
[INFO] |  \- commons-logging:commons-logging:jar:1.2:compile
[INFO] +- org.springframework:spring-expression:jar:4.2.4.RELEASE:compile
[INFO] +- org.springframework:spring-beans:jar:4.2.4.RELEASE:compile
[INFO] +- org.springframework:spring-aop:jar:4.2.4.RELEASE:compile
[INFO] |  \- aopalliance:aopalliance:jar:1.0:compile
[INFO] +- org.springframework:spring-context:jar:4.2.4.RELEASE:compile
[INFO] +- org.jbehave:jbehave-core:jar:4.0.5:test
[INFO] |  +- junit:junit:jar:4.11:test
[INFO] |  +- org.hamcrest:hamcrest-core:jar:1.3:test
[INFO] |  +- org.hamcrest:hamcrest-library:jar:1.3:test
[INFO] |  +- org.hamcrest:hamcrest-integration:jar:1.3:test
[INFO] |  +- commons-collections:commons-collections:jar:3.2.1:test
[INFO] |  +- commons-lang:commons-lang:jar:2.6:test
[INFO] |  +- org.codehaus.plexus:plexus-utils:jar:3.0.10:test
[INFO] |  +- org.freemarker:freemarker:jar:2.3.20:test
[INFO] |  +- com.thoughtworks.paranamer:paranamer:jar:2.4:test
[INFO] |  \- com.thoughtworks.xstream:xstream:jar:1.4.7:test
[INFO] |     +- xmlpull:xmlpull:jar:1.1.3.1:test
[INFO] |     \- xpp3:xpp3_min:jar:1.1.4c:test
[INFO] +- org.jbehave:jbehave-maven-plugin:jar:4.0.5:test
[INFO] |  +- org.apache.maven:maven-plugin-api:jar:2.0.11:test
[INFO] |  +- org.apache.maven:maven-artifact:jar:2.0.11:test
[INFO] |  +- org.apache.maven:maven-project:jar:2.0.11:test
[INFO] |  |  +- org.apache.maven:maven-settings:jar:2.0.11:test
[INFO] |  |  +- org.apache.maven:maven-profile:jar:2.0.11:test
[INFO] |  |  +- org.apache.maven:maven-model:jar:2.0.11:test
[INFO] |  |  +- org.apache.maven:maven-artifact-manager:jar:2.0.11:test
[INFO] |  |  |  +- org.apache.maven:maven-repository-metadata:jar:2.0.11:test
[INFO] |  |  |  \- org.apache.maven.wagon:wagon-provider-api:jar:1.0-beta-2:test
[INFO] |  |  +- org.apache.maven:maven-plugin-registry:jar:2.0.11:test
[INFO] |  |  +- org.codehaus.plexus:plexus-interpolation:jar:1.1:test
[INFO] |  |  \- org.codehaus.plexus:plexus-container-default:jar:1.0-alpha-9-stable-1:test
[INFO] |  |     \- classworlds:classworlds:jar:1.1-alpha-2:test
[INFO] |  \- org.codehaus.plexus:plexus-archiver:jar:1.2:test
[INFO] |     \- org.codehaus.plexus:plexus-io:jar:1.0.1:test
[INFO] +- org.jsoup:jsoup:jar:1.8.3:compile
[INFO] \- sunjce:sunjce:jar:1.8.0_40:compile
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
[INFO] Total time: 11.736s
[INFO] Finished at: Wed Apr 13 19:54:29 MDT 2016
[INFO] Final Memory: 12M/110M
[INFO] ------------------------------------------------------------------------

This is a great way to produce a list of dependencies in your project. Knowing what is declares and used within a project is noble on its own, but this information becomes even more critical when questions of licensing are involved.

In addition to knowing what dependencies are used in a project, it is also very useful to know if there are updates available to the dependencies within a project. Components are updated all the time (and sadly never on the schedule of your project!) for bug fixes, performance enhancements and security patches. Is it especially critical to know if there are security related patches available as who wants to ship a product riddled with security flaws? Remember, all those third party software components shipped with a product are all part of the product and it's highly doubtful any paying customer will accept that a security flaw is the responsibility of a third party software component and not responsibility of the product they bought.

Maven again supplies a methodology to find updated dependencies with the mvn versions:display-dependency-updates goal. One caveat is that this functionality relies on the repositories, so if a custom repository is in use that isn't mirroring out to the official Maven repositories there may be a discrepancy between a custom repository and official and up-to-date repsoitories.

Here's an example of using Maven to find updates:

$ mvn versions:display-dependency-updates
[INFO] Scanning for projects...
[INFO]
[INFO] ------------------------------------------------------------------------
[INFO] Building Populate 1.2
[INFO] ------------------------------------------------------------------------
[INFO]
[INFO] --- versions-maven-plugin:1.3.1:display-dependency-updates (default-cli) @ Populate ---
[INFO] artifact GMS:GMSFramework: checking for updates from central
[INFO] artifact GMS:GMSStringComp: checking for updates from central
[INFO] artifact org.jbehave:jbehave-core: checking for updates from central
[INFO] artifact org.jbehave:jbehave-maven-plugin: checking for updates from central
[INFO] artifact org.jsoup:jsoup: checking for updates from central
[INFO] artifact org.springframework:spring-aop: checking for updates from central
[INFO] artifact org.springframework:spring-beans: checking for updates from central
[INFO] artifact org.springframework:spring-context: checking for updates from central
[INFO] artifact org.springframework:spring-core: checking for updates from central
[INFO] artifact org.springframework:spring-expression: checking for updates from central
[INFO] artifact sunjce:sunjce: checking for updates from central
[INFO] The following dependencies in Dependencies are using the newest version:
[INFO]   GMS:GMSFramework ................................................. 1.1
[INFO]   GMS:GMSStringComp ................................................ 1.0
[INFO]   org.jbehave:jbehave-core ....................................... 4.0.5
[INFO]   org.jbehave:jbehave-maven-plugin ............................... 4.0.5
[INFO]   org.jsoup:jsoup ................................................ 1.8.3
[INFO]   sunjce:sunjce ............................................... 1.8.0_40
[INFO]
[INFO] The following dependencies in Dependencies have newer versions:
[INFO]   org.springframework:spring-aop ........ 4.2.4.RELEASE -> 4.2.5.RELEASE
[INFO]   org.springframework:spring-beans ...... 4.2.4.RELEASE -> 4.2.5.RELEASE
[INFO]   org.springframework:spring-context .... 4.2.4.RELEASE -> 4.2.5.RELEASE
[INFO]   org.springframework:spring-core ....... 4.2.4.RELEASE -> 4.2.5.RELEASE
[INFO]   org.springframework:spring-expression ...
[INFO]                                           4.2.4.RELEASE -> 4.2.5.RELEASE
[INFO]
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
[INFO] Total time: 6.071s
[INFO] Finished at: Wed Apr 13 20:04:04 MDT 2016
[INFO] Final Memory: 14M/110M
[INFO] ------------------------------------------------------------------------

This output shows that the project is referencing Spring, and using an out of date version.

Given this information, it is now possible to make an informed decision if an update is required or prudent for the project. Granted, Maven does not relay CVE numbers for security fixes, so there is an assumption that the source website for third party dependencies is known and a change log is published to help make the determination of when an update is necessary.

These two Maven goals work to help keep Java projects up to date. Happy Maven-ing.

(For more on incorporating automation in your builds, read Chapter 8 of this book.)