Dependencies in Maven
Dependencies are one of the things I appreciate about Maven. They have drawbacks, though, especially transitive dependencies. If you’re new to Maven, a transitive dependency is something your code depends on indirectly. Say component A depends on B, and B depends on C. From A’s point of view, B is a direct dependency and C is an indirect one.
You may be wondering where the problem is. It starts when your project inherits transitive dependencies it doesn’t need or when versions conflict. Commons Logging 1.1 is a classic example. Say you decide to use it for logging and declare the dependency. You’ve just inherited an unwanted dependency on Servlet API 2.3. Commons Logging defines that dependency incorrectly, specifically by giving it the wrong scope. Maven uses the scope to decide which classpaths need the dependency. Without a scope, it ends up on every classpath. That means Servlet API 2.3 can get bundled into the resulting package, say a WAR, where it certainly doesn’t belong.
Licenses can also be a problem. Transitive dependencies can introduce licenses incompatible with the product’s licensing policy. As if that weren’t enough, imagine that A depends on X and Y. Both X and Y depend on Commons Logging, but they each specify a different version.
There are only a few ways to influence transitive dependencies. You can use an exclusion to tell Maven to ignore one. You can also use what you know about Maven’s version-conflict algorithm, the so-called shortest path. To pick a common version for X and Y, make A depend directly on that version of Commons Logging. None of this is especially convenient when you have to do it for multiple dependencies. You also have to keep in mind that any dependency can pull in something you don’t want.
Thinking it through, these problems come from poorly defined dependencies. Maven can do little except give you tools to find them. There’s still a problem to solve, and the only solution I see is to take full control of your dependencies. Here’s what that means.
- Disconnect the internal artifact repository from the internet. That way nothing gets into the repository unless you put it there yourself. The downside is that Maven can no longer retrieve the latest plug-ins, which also come through the repository.
- Post-process all POM files in the repository. You can automate some of this, but unwanted dependencies must be found and corrected by hand. Use dependency management to resolve version conflicts. Remove the versions from all transitive dependencies or replace them with a variable. Then set its value in the main project POM or in a profile.
Given Maven’s popularity, I’m surprised there aren’t commercial repositories offering these services professionally. They could guarantee that dependency trees are consistent in versions and licenses and certify provenance through signed JARs. They could also ensure that dependency sources and Javadoc are available. Repository versioning would be another nice feature, letting you keep snapshots of the repository at different points in time and go back to them. If people already sell infrastructure such as version control, issue trackers and wikis, why not an artifact repository too?
Related articles
Related CZ Podcast episodes
- #261 · Leadership
On leading people and teams. · Czech audio
- #3 · Java 6 SE, Vývoj webových aplikací
Dagi and Filemon discuss Java 6 and web application frameworks. · Czech audio
- #1 · Vývojová prostředí v Javě
The first CZ Podcast episode looks at Java development environments. · Czech audio