Google

Jul 18, 2014

Which Jar has a particular class? or from which jar a class was loaded? solving jar hell issues

The following 3 questions are frequently asked by Java developers as an industrial strength Java project will have 100+ jar files. How often have you come across a Java application that requires different versions of the same library? How often do you see exceptions like NoSuchMethodError or IllegalArgumentException. Here are some tips to solve the JAR hell problem. These 6 tips will go a long way in resolving your jar problems.

Q1. Which Jar has a particular class?

Tip#1: go to findJAR.com and search for the class file. For example, I want to find the jar file that has org.apache.commons.io.FileUtils



You can drill through to find a Maven download link.




Tip#2: Unix command "find" and grep

find ./lib -name "*.jar" -exec sh -c 'jar -tf {}|grep -H --label {} 'org.apache.commons.io.FileUtils'' \;




Tip#3: In Windows or DOS

 
for %i in (*.jar) do @jar tvf %i | find "org/apache/commons/io/FileUtils.class"



Q2. from which jar a class was loaded?

Tip#4. To identify from which jar a particular class was loaded from, add the following snippet of code to  a location where it gets executed.

   Class klass = org.apache.commons.io.FileUtils.class;

   CodeSource codeSource = klass.getProtectionDomain().getCodeSource();

    if ( codeSource != null) {
        System.out.println(codeSource.getLocation());
    }


At run time it will print the jar file from which "FileUtils" was loaded.

package test;

import java.security.CodeSource;

public class WhichJarLoadedTest {

 public static void main(String[] args) {
  Class klass = org.apache.commons.io.FileUtils.class;

  CodeSource codeSource = klass.getProtectionDomain().getCodeSource();

  if (codeSource != null) {
   System.out.println(codeSource.getLocation());
  }
 }

}


Output:

 file:/C:/Users/akumaras/workspace/WtsPlayProject/lib/commons-io-2.4.jar
 

 Q3. Why commons-io version 2.1 was chosen over version 2.4?


Tip #5: In Maven, due to its transitive dependencies behavior, multiple versions of same jar could be pulled in. You need to determine which jar is bringing in the this duplicate or wrong version of the jar and exclude it in the pom.xml file. The verbose flag instructs the dependency tree to display conflicting dependencies that were omitted from the resolved dependency tree. For example, to see why commons-io 2.0 was chosen over commons-io 2.4

  mvn dependency:tree -Dverbose -Dincludes=commons-io
  
The 3 handy commands to solve jar hell issues in maven are mvn dependency:tree, mvn dependency:analyze, and mvn help:effective-pom. The IDEs like eclipse provide tools to analyze dependencies. In eclipse, double click on a pom.xml file, and then select the "Dependency Hierachy" tab to analyze why a particular jar was chosen.

<dependency>
 <groupId>org.jboss.resteasy</groupId>
 <artifactId>resteasy-jaxrs</artifactId>
 <version>${resteasy.version}</version>
</dependency>



commons-io-1.4 is a very old version. Now to to exclude older version and include commons-io-2.4, you need to do the following in the pom.xml file.

  
<dependency>
 <groupId>org.jboss.resteasy</groupId>
 <artifactId>resteasy-jaxrs</artifactId>
 <version>${resteasy.version}</version>
 <exclusions>
      <exclusion>
          <groupId>commons-io</groupId>
          <artifactId>commons-io</artifactId>
      </exclusion>
 </exclusions> 
</dependency>
<dependency>
        <groupId>commons-io</groupId>
     <artifactId>commons-io</artifactId>
     <version>2.4</version>
</dependency>




Tip #6:  uber-jar is an "over-jar", and uber is the German word for above or over. uber-jar is defined as one that contains both your package and all its dependencies in one single JAR file (Note: jars cannot have other jars). The advantage is that you can distribute your uber-jar and not care at all whether or not dependencies are installed at the destination, as your uber-jar actually has no dependencies. Maven has a plugin known as the "Apache Maven Shade Plugin".

 This plugin can also be used in scenarios where you are using two jars X and Y. Y is a library and X has classes that uses some old classes from library Y with same names causing a jar hell issue.  You can solve your problem by renaming the package of the class that you don't want.

<plugin>
    <groupId>org.apache.maven.plugins</groupId>
    <artifactId>maven-shade-plugin</artifactId>
    <executions>
        <execution>
        <phase>package</phase>
        <goals>
            <goal>shade</goal>
            </goals>
        <configuration>
                <relocations>
                    <relocation>
                            <pattern>com.myapp.MyClass</pattern>
                               <shadedPattern>com.myapp.rename.MyClass</shadedPattern>
                    </relocation>
                </relocations>
                    <promoteTransitiveDependencies>true</promoteTransitiveDependencies>
        </configuration>
        </execution>
    </executions>
</plugin>


Labels: ,

3 Comments:

Blogger Juan Jose said...

Great post! I had the luck to see your blog today and see that it's great.

8:28 AM, July 20, 2014  
Blogger cmen said...

Your Articles are explained very clear. Thanks for your valuable time.

12:27 PM, July 21, 2014  
Blogger Unknown said...

Glad you liked it.

4:34 PM, July 21, 2014  

Post a Comment

Subscribe to Post Comments [Atom]

<< Home