Find where a Java class comes from, recursively 

There are 3 notes for this topic, click above title to see all notes.
Joined:
04/09/2007
Posts:
565

January 14, 2010 00:28:27    Last update: May 26, 2010 04:01:23
A task that a Java developer does so frequently is to find out where a certain class can be found - to resolve compilation errors, classpath issues, or version conflicts of the same class introduced by multiple class loaders. A long while back I wrote a simple Perl script to perform the task. Later I was informed that there are Swing based Jar Browser and Jars Browser.

Then, there are a couple of shell one-liners:
# one liner 1
find -name "*.jar" -print0 | xargs -0 grep -i SomeClassName

# one liner 2
for i in $(find . -iname \*.*ar) ; do echo $i ;jar tf $i | grep $1; done


But all of them share the same problem: if a class is in a jar nested in another jar, it cannot be found. Such is the case for a class inside a jar under the WEB-INF/lib directory of a war file packaged in an ear. This is a simple Java program that solves the problem. It also demonstrates how to read a jar nested inside another jar without using a temporary file.
import java.io.*;
import java.util.zip.*;

public class FindClass {
    private static final int BUFFER_SIZE = 4096;
    private static final String[] JAR_EXTENSIONS = {
                                                     ".zip", 
                                                     ".jar", 
                                                     ".war", 
                                                     ".ear", 
                                                     ".rar", 
                                                 };

    public static void findClass(String path,
                                 String classToLookFor,
                                 boolean recursive) 
                       throws Exception {
        if (path == null) {
            return;
        }

        // normalize class name
        if (!classToLookFor.contains("/")) {
            classToLookFor = classToLookFor.replace('.', '/');
        }

        // normalize path, change \ to /
        path = path.replace(File.separatorChar, '/'); 
        File f = new File(path);
        if (f.isDirectory()) {
            findClassInDirectory(path, classToLookFor, recursive);
        }
        else if (f.isFile() && hasJarExtension(path)) {
            ZipInputStream in = new ZipInputStream(new FileInputStream(path));
            findClassInJar(in, classToLookFor, path, recursive);
            in.close();
        }
        else if (f.isFile() && path.contains(classToLookFor)) {
            showPath(path);
        }
    }

    private static void findClassInJar(ZipInputStream in, 
                                      String classToLookFor,
                                      String path, 
                                      boolean recursive) 
                       throws Exception {
        ZipEntry next = in.getNextEntry();
        while (next != null) {
            if (!next.isDirectory()) {
                if (next.getName().contains(classToLookFor)) {
                    showPath(path + ": " + next.getName());
                }
                else if (recursive && hasJarExtension(next.getName())) {
                    ByteArrayOutputStream out = new ByteArrayOutputStream();
                    byte[] buffer = new byte[BUFFER_SIZE];
                    int n = in.read(buffer, 0, BUFFER_SIZE);
                    while (n >= 0) {  // read returns -1 if end of current entry is reached.
                        out.write(buffer, 0, n);
                        n = in.read(buffer, 0, BUFFER_SIZE);
                    }

                    ZipInputStream in2 = new ZipInputStream(
                                                new ByteArrayInputStream(
                                                    out.toByteArray()
                                                )
                                         );
                    findClassInJar(in2, 
                                   classToLookFor, 
                                   path + "/" + next.getName(), 
                                   recursive);
                    in2.close();
                }
            }
            next = in.getNextEntry();
        }
    }

    private static void findClassInDirectory(String dir,
                                             String classToLookFor,
                                             boolean recursive)
                        throws Exception {
        File d = new File(dir);
        String[] files = d.list();
        for (int i = 0; i < files.length; i++) {
            String filePath = dir + "/" + files[i];
            File file = new File(filePath);
            if (file.isFile() && filePath.contains(classToLookFor)) {
                int idx = filePath.indexOf(classToLookFor);
                showPath(filePath.substring(0, idx) + ": " + filePath.substring(idx));
            }
            else if (recursive) {
                if (file.isDirectory()) {
                    findClassInDirectory(filePath, classToLookFor, recursive);
                }
                else if (hasJarExtension(filePath)) {
                    ZipInputStream in = new ZipInputStream(new FileInputStream(file));
                    findClassInJar(in, classToLookFor, filePath, recursive);
                    in.close();
                }
            }
        }
    }

    private static boolean hasJarExtension(String fileName) {
        fileName = fileName.toLowerCase();
        for (int i = 0; i < JAR_EXTENSIONS.length; i++) {
            if (fileName.endsWith(JAR_EXTENSIONS[i])) {
                return true;
            }
        }
        return false;
    }

    private static void showPath(String p) {
        System.out.println(p);
    }

    public static void main(String[] args) throws Exception {
        if (args.length < 2) {
            System.out.println("Usage: java FindClass <jarFileName> <classToLookFor>");
            System.exit(1);
        }
        
        String path = args[0];
        String classToLookFor = args[1];
        findClass(path, classToLookFor, true);
    }
}
[ Comment  | Tags ]
3 comments  
Easy email testing with http://www.ximailstop.com