Is there a tool to discover if the same class exists in multiple jars in the classpath?

StackOverflow https://stackoverflow.com/questions/135971

  •  02-07-2019
  •  | 
  •  

Question

If you have two jars in your classpath that contain different versions of the same class, the classpath order becomes critical.

I am looking for a tool that can detect and flag such potential conflicts in a given classpath or set of folders.

Certainly a script that starts:

classes=`mktemp`
for i in `find . -name "*.jar"`
do
    echo "File: $i" > $classes
    jar tf $i > $classes
    ...
done

with some clever sort/uniq/diff/grep/awk later on has potential, but I was wondering if anyone knows of any existing solutions.

Was it helpful?

Solution

The Tattletale tool from JBoss is another candidate: "Spot if a class/package is located in multiple JAR files"

OTHER TIPS

Looks like jarfish will do what you want with its "dupes" command.

I think it wouldn't be too hard to write a tool for your self.

You can get the classpath entries with System.getProperty("java.class.path");

And then walk through the jars, zips, or directories listed there and collect all the information about the classes and findout those that might cause trouble.

This task would take 1 or 2 days at most. Then you can load this class directly in your application and generate a report.

Probably java.class.path property wont's show all the classes if you run in some infrastructure with complex custom class loading ( for instance I once saw an app that load the classes from the LDAP ) but it would certainly work for most of the cases.

Heres a tool you might find useful, I've never use it my self, but give it a try and let us know the result.

http://www.jgoodies.com/freeware/jpathreport/features.html

If you are going to create your own tool, here is the code I use for the same shell script posted before, but that I use on my Windows machine. It runs faster when there are tons of jar files.

You can use it and modify it so instead of recursively walk a directory, read the class path and compare the .class time attribute.

There is a Command class you can subclass if needed, I was thinking in the -execute option of "find"

This my own code, so it was not intended to be "production ready", just to do the work.

import java.io.*;
import java.util.zip.*;


public class ListZipContent{
    public static void main( String [] args ) throws IOException {
        System.out.println( "start " + new java.util.Date() );
        String pattern = args.length == 1 ? args[0] : "OracleDriver.class";// Guess which class I was looking for :) 
        File file = new File(".");
        FileFilter fileFilter = new FileFilter(){
            public boolean accept( File file ){
                return file.isDirectory() || file.getName().endsWith( "jar" );
            }
        };
        Command command = new Command( pattern );
        executeRecursively( command, file, fileFilter );
        System.out.println( "finish  " + new java.util.Date() );
    }
    private static void executeRecursively( Command command, File dir , FileFilter filter ) throws IOException {
        if( !dir.isDirectory() ){
            System.out.println( "not a directory " + dir );
            return;
        }
        for( File file : dir.listFiles( filter ) ){
            if( file.isDirectory()){
                executeRecursively( command,file , filter );
            }else{
                command.executeOn( file );
            }
        }
    }
}
class Command {

    private String pattern;
    public Command( String pattern ){
        this.pattern = pattern;
    }

    public void executeOn( File file ) throws IOException {
        if( pattern == null ) { 
            System.out.println( "Pattern is null ");
            return;
        }

        String fileName = file.getName();
        boolean jarNameAlreadyPrinted = false;

        ZipInputStream zis = null;
        try{
            zis = new ZipInputStream( new FileInputStream( file ) );

            ZipEntry ze;
            while(( ze = zis.getNextEntry() ) != null ) {
                if( ze.getName().endsWith( pattern )){
                    if( !jarNameAlreadyPrinted ){
                        System.out.println("Contents of: " + file.getCanonicalPath()  );
                        jarNameAlreadyPrinted = true;
                    }
                    System.out.println( "    " + ze.getName() );
                }
                zis.closeEntry();
            }
        }finally{
            if( zis != null ) try {
                zis.close();
            }catch( Throwable t ){}
        }
    }
}

I hope this helps.

Classpath Helper is an Eclipse plug-in that helps a little bit.

If you dislike downloading and installing stuff you can use this one line command to find jar conflicts with standard gnu tools. It is rudimentary but you can expand it as you will.

ls *.jar | xargs -n1 -iFILE unzip -l FILE | grep class | sed "s,.* ,," | tr "/" "." | sort | uniq -d | xargs -n1 -iCLASS grep -l CLASS *.jar | sort -u

(it is a bit slow to run if you have a lot of jars)

Explanation: It lists all the files in all the jars, greps for class files, finds dupes, then greps the original jars to see where they appeared. It could be made more efficient with a more complicated script.

jarclassfinder is another eclipse plugin option

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top