57 45 4c 4c 20 44 4f 4e 45 21
                   
Mar 17

Maven Version Clash Detection (and Software)

Comments Off
Posted on Tuesday, March 17, 2009 in Articles, Programming.

Maven is a great tool for building Java projects, there’s no doubt about that. Unfortunately there is one great feature still lacking of Maven which is important when you use third party dependencies, namely detecting when your components use dependencies of different versions without your knowledge.

What does that mean? It’s probably easier to explain by example. Imagine you have a project that has two dependencies (can be your own created jars or third party, or anything Maven-buildable really). Your first dependency uses Log4J v1.0. Your second dependency uses Log4J v1.1. When you compile this project Maven will by default use the “nearest wins” strategy, meaning it might pick Log4J v1.0 or Log4J v1.1, whichever is nearer in the transitive hull of dependencies. What you probably wanted however, was for the compilation to tell you that you have two “modules” depending on the same library, but different versions of that library. Log4J is perhaps not the best example, but imagine that the dependency in question is a mission-critical dependency, and that any version discrepancy may cause catastrophic failures that can be very hard to track down via debugging.

To solve this I wrote a Maven plugin nearly a year ago that we are now using for every Maven-built project in our company. It’s a simple plugin that does the complex task of detecting all version clashes (even deeply nested ones) and will report them back to you either as warnings or errors (or ignore them). This project was submitted to the Maven team a while back but has not been integrated as a default Maven plugin yet, so it will now become a downloadable Software Project on Hexapixel until it hopefully becomes a standard module.

So how do you use it? Here’s a brief explanation and some examples of usage. If you’d rather skip to the plugin “homepage”, please follow this link.

Usage / Configuration

You can either run the clash-detector manually from the command line, or (the best option) is to add it to your POM (ideally your parent POM that all your projects use, you do have one right?) so that it is run automatically whenever you do builds.

There are only two options you can configure, one is the level of warning the plugin should do. By default the level is FAIL, meaning your build will fail if the plugin detects any clashes. The level can be changed either on the command line by supplying the parameter -Dhalt= where the right hand of the equals sign is one of FAIL, WARN or DEBUG. For the POM the same parameter is configured by supplying the same keyword inside a <halt /> tag such as: <halt>WARN</halt>

The second parameter is the level of detection the plugin should do. There are two modes, SHALLOW and DEEP. By default the level is DEEP where clash detection is not limited to the immediate level of the dependency, but also deeply nested dependencies. Shallow detection is only one level down and will not detect deeply nested dependency clashes. This is for example good for when you only care about clashes of your own jars (although I would strongly advice never using it unless really necessary).

Here are two snippets showing how to run the clash detector from the command line and how to configure it inside your POM.

Command Line

To invoke it from the command line you simply run it like this:

mvn breakonversionclash:clash-detection -Dhalt=FAIL -Ddepth=SHALLOW 

In your POM

<build>
	<plugins>
		<plugin>
			<groupId>com.hexapixel.maven.plugins</groupId>
			<artifactId>
				maven-breakonversionclash-plugin
			</artifactId>
			<version>1.0.1</version>
			<executions>
				<execution>
					<configuration>
						<!-- Options: FAIL, WARN, DEBUG (default is FAIL) -->
						<halt>WARN</halt>
						<!-- Options: SHALLOW, DEEP (default is DEEP) -->
						<depth>DEEP</depth>
					</configuration>
					<phase>compile</phase>
					<goals>
						<goal>clash-detection</goal>
					</goals>
				</execution>
			</executions>
		</plugin>
	</plugins>
</build>

Plugin in Action

Here is a typical output the plugin will produce:

 [INFO] [breakonversionclash:clash-detection {execution: default}]
 [INFO] Checking for dependency version clashes...
 [INFO] Halt mode is: 'warn'. Depth mode is: 'deep'.
 [WARNING] ------------------------------------------------------------------------
 [WARNING] Dependency version clash on 'commons-logging' [1.0.4, 1.1.1]
 [WARNING] ------------------------------------------------------------------------
 [WARNING] Clash 1: commons-logging:commons-logging:1.0.4
 [WARNING] Clash 2: commons-logging:commons-logging:1.1.1
 [WARNING]
 [WARNING] Artifacts using different versions of 'commons-logging:commons-logging':
 [WARNING] - org.hibernate:hibernate:jar:3.2.6.ga
 [WARNING] - com.our.project:plugin-domain:jar:1.0.0.0-SNAPSHOT
 [WARNING]
 [WARNING] Dependency trail for 'commons-logging:commons-logging:1.0.4'
 [WARNING] - Node 1: com.our.project:plugin-testdata:pom:1.0.0.0-SNAPSHOT
 [WARNING] - Node 2: com.our.project:plugin-testdata-source:zip:small-datasets:1.0.0.0-SNAPSHOT
 [WARNING] - Node 3: com.our.project:plugin-testdata-generator:jar:1.0.0.0-SNAPSHOT
 [WARNING] - Node 4: com.our.project:plugin-domain:jar:1.0.0.0-SNAPSHOT
 [WARNING] - Node 5: org.hibernate:hibernate:jar:3.2.6.ga
 [WARNING]
 [WARNING] Dependency trail for 'commons-logging:commons-logging:1.1.1'
 [WARNING] - Node 1: com.our.project:plugin-testdata:pom:1.0.0.0-SNAPSHOT
 [WARNING] - Node 2: com.our.project:plugin-testdata-source:zip:small-datasets:1.0.0.0-SNAPSHOT
 [WARNING] - Node 3: com.our.project:plugin-testdata-generator:jar:1.0.0.0-SNAPSHOT
 [WARNING] - Node 4: com.our.project:plugin-domain:jar:1.0.0.0-SNAPSHOT
 [WARNING] Found 1 dependency version clash

Let’s go over this even though it might be mostly self explanatory.

We have configured the plugin to only warn us when there is a clash, and to do a “deep” check, meaning it will not only check immediate dependencies, but also dependencies of dependencies.

It tells us that we have a clash on the usage of ‘commons-logging’ and that the two clashing versions are version 1.0.4 and version 1.1.1 of commons-logging.

Then it tells us what jars are using commons-logging, in this case it’s our dependency on Hibernate and our own plugin-domain project.

It then shows us the dependency “trail” for each clash, meaning, it shows what depends on what all the way to the actual dependency of ‘commons-logging’, so that you can easily pinpoint what jars need adjusting so that the clash goes away.

Download / More Info

For downloads (binary and source) and more info, please go to the Version Clash Plugin Page.

March 17, 2009 • Tags: , , • Posted in: Articles, Programming • Comments OffPrint This Post