Maven Global dependency exclusion

With Maven becoming the build tool of choice for Jave EE developers, one of the problems that a developer faces frequently is that of Transitive dependencies. Problems like ClassCastException and LinkageError  are encountered during deployment of an application because of the same class being loaded by different ClassLoaders  or presence of different versions of the same set of APIs as a result of transitive dependency.

Such issues become even more elusive to debug if the Application server your using comes bundled with a whole galaxy of commonly used jar files(yes i am talking ’bout JBoss :D).  Lets see couple of such cases.

Jersey Spring Transitive dependency

The jersey-spring integration jar file has  following dependency declaration-

 <dependency>
<groupId>com.sun.jersey.contribs</groupId>
<artifactId>jersey-spring</artifactId>
<version>1.4</version>
</dependency>

Now the above jersey-spring dependency has a transitive dependency on spring 2.5.x version while the application was already using spring 3.x thus this conflict in spring jars on the classpath led to the following exception-

org.springframework.beans.factory.BeanDefinitionStoreException: Unexpected exception parsing XML document from class path resource [cloverjazz-web-context.xml]; nested exception is java.lang.NoSuchMethodError: org.springframework.beans.factory.xml.BeanDefinitionParserDelegate.getLocalName(Lorg/w3c/dom/Node;)Ljava/lang/String;
at org.springframework.beans.factory.xml.XmlBeanDefinitionReader.doLoadBeanDefinitions(XmlBeanDefinitionReader.java:420)
at org.springframework.beans.factory.xml.XmlBeanDefinitionReader.loadBeanDefinitions(XmlBeanDefinitionReader.java:342)
at org.springframework.beans.factory.xml.XmlBeanDefinitionReader.loadBeanDefinitions(XmlBeanDefinitionReader.java:310)
at org.springframework.beans.factory.support.AbstractBeanDefinitionReader.loadBeanDefinitions(AbstractBeanDefinitionReader.java:143)
at org.springframework.beans.factory.support.AbstractBeanDefinitionReader.loadBeanDefinitions(AbstractBeanDefinitionReader.java:178)
at org.springframework.beans.factory.support.AbstractBeanDefinitionReader.loadBeanDefinitions(AbstractBeanDefinitionReader.java:149)
at org.springframework.web.context.support.XmlWebApplicationContext.loadBeanDefinitions(XmlWebApplicationContext.java:124)
at org.springframework.web.context.support.XmlWebApplicationContext.loadBeanDefinitions(XmlWebApplicationContext.java:92)
at org.springframework.context.support.AbstractRefreshableApplicationContext.refreshBeanFactory(AbstractRefreshableApplicationContext.java:123)
at org.springframework.context.support.AbstractApplicationContext.obtainFreshBeanFactory(AbstractApplicationContext.java:422)
at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:352)
at org.springframework.web.context.ContextLoader.createWebApplicationContext(ContextLoader.java:255)
at org.springframework.web.context.ContextLoader.initWebApplicationContext(ContextLoader.java:199)

The above jersey-spring transitive dependency issue was fixed after I made the following changes in the POM

<dependency>
   <groupId>com.sun.jersey.contribs</groupId>
   <artifactId>jersey-spring</artifactId>
   <version>1.2</version>
   <exclusions>
        <exclusion>
         <groupId>org.springframework</groupId>
         <artifactId>spring</artifactId>
        </exclusion>
        <exclusion>
        <groupId>org.springframework</groupId>
        <artifactId>spring-core</artifactId>
        </exclusion>
        <exclusion>
        <groupId>org.springframework</groupId>
        <artifactId>spring-web</artifactId>
        </exclusion>
        <exclusion>
        <groupId>org.springframework</groupId>
        <artifactId>spring-beans</artifactId>
        </exclusion>
        <exclusion>
        <groupId>org.springframework</groupId>
       <artifactId>spring-context</artifactId>
       </exclusion>
   </exclusions>
</dependency>

Now as you can see from the above snippet I had to add the exclusion dependencies tag inline(within a dependency). Since jersey-spring was having spring as transitive dependency thus this case was quickly identified and fixed but what about the scenario in which you are using lot of open-source frameworks and most of them have a common transitive dependency having different versions. Case in point is DOM4J , this jar file is a transitive dependency of many leading frameworks  and also comes bundled  with JBoss.  Thus  writing a inline dependency exclusion for dom4j within so many dependencies doesn’t seem to be the best solution. The most apt thing would be to globally exclude the dom4j dependency.

<dependency>
<groupId>dom4j</groupId>
<artifactId>dom4j</artifactId>
<version>1.4</version>
<scope>provided</scope>
</dependency>

The above XML snippet prohibits the packaging of DOM4j in the Web-INF/lib. Since the jar files isn’t present Application’s lib thus it’s loaded by the Jboss classloader as it’s present in the JBoss’s lib.

Conclusion – By using the <scope>provided</scope> for a dependency  you can globally exclude the dependency from getting packaged in the application lib instead it’s loaded by the server classloader thus avoiding any LinkageError or ClassCastExceptions from arising due to jar conflicts.

About these ads

About Tarun Sapra
Agile Java developer @ Xebia India

One Response to Maven Global dependency exclusion

  1. Pingback: JavaPins

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

Follow

Get every new post delivered to your Inbox.

%d bloggers like this: