package chiralsoftware.findjar; import java.io.File; import java.io.FileFilter; import java.io.PrintStream; import java.util.ArrayList; import java.util.Arrays; import java.util.Enumeration; import java.util.jar.JarEntry; import java.util.jar.JarFile; /** * A simple utility to look through a file tree and * find any jar which contains the requested class. * This utility handles exploded jar files correctly. * Future enhancements could include looking * for jars inside war / ear files. * * @author Eric Hollander */ public final class Main { private static final PrintStream out = System.out; private static void usage() { out.println("Usage: findjar class-name path"); } public static void main(String[] args) throws Exception { if(args.length < 1) { usage(); return; } if(args.length > 2) { usage(); return; } final String path = args.length == 1 ? "." : args[1]; final String className = args[0]; findJar(className, path); } /** Given a name of a class file, as it would appear * in a jar file, turn it into a proper Java * class anme * @param s a jar file name like package/MyClass.class * @return a proper Java class name, like package.MyClass */ private static String nameToClassName(String s) { if(s == null) return null; if(s.lastIndexOf(".") == -1) return s; return s.substring(0, s.lastIndexOf(".")).replace("/", "."); } /** Scan a jar file, which could be either a file or * an exploded jar (directory) for the given class name */ private static void oneJarFile(String className, File file) throws Exception { // the filename of the desired class, as it would appear // in a jar file final String matchName = className.toLowerCase().replace(".", "/") + ".class"; final boolean partial = className.indexOf(".") == -1; if(file.isDirectory()) { // exploded jar // recurse through the entire thing // looking for this particular file name final ArrayList list = new ArrayList(); list.addAll(Arrays.asList(file.listFiles(explodedJarFilter))); final String jarRoot = file.getCanonicalPath(); while(!list.isEmpty()) { final File first = list.remove(0); // this is either a directory or a class file if(first.isDirectory()) { list.addAll(0, Arrays.asList(first.listFiles(explodedJarFilter))); continue; } // otherwise let's see if this is a match if(partial) { if(first.getName().toLowerCase().endsWith(matchName)) { // we need some trickier logic to find // out the class name of this partial match // in an exploded jar final String showName = first.getCanonicalPath().substring(jarRoot.length() + 1); showEntry(file, nameToClassName(showName)); } } else { //out.println("looking for a match between " + // first.getCanonicalPath() + " and " + // jarRoot + File.separatorChar + matchName); if(first.getCanonicalPath().equalsIgnoreCase(jarRoot + File.separatorChar + matchName)) { final String showName = first.getCanonicalPath().substring(jarRoot.length() + 1); showEntry(file, nameToClassName(showName)); } } } } else { // plain jar file final JarFile jarFile = new JarFile(file); final Enumeration entries = jarFile.entries(); while(entries.hasMoreElements()) { final JarEntry entry = entries.nextElement(); final String name = entry.getName(); if(partial) { if(name.toLowerCase().endsWith(matchName)) showEntry(file, nameToClassName(name)); } else { if(name.equalsIgnoreCase(matchName)) showEntry(file, nameToClassName(name)); } } } } /** When a match is found, print out the file * it was found in, and the class name, in a nice * display format */ private static void showEntry(File file, String className) throws Exception { final StringBuilder sb = new StringBuilder(file.getCanonicalPath()); if(file.isDirectory()) sb.append(File.separatorChar); sb.append(" "); sb.append(className); out.println(sb); } private static void findJar(String className, String path) throws Exception { final ArrayList list = new ArrayList(); // let's get some files! File dir = new File(path); if(!dir.exists()) return; if(dir.isFile()) { // we're looking in only one jar file! oneJarFile(className, dir); return; } // otherwise let's recurse along here list.addAll(Arrays.asList(dir.listFiles(myFilter))); while(!list.isEmpty()) { final File first = list.remove(0); if(first.getName().endsWith(".jar")) { oneJarFile(className, first); continue; } if(first.isDirectory()) { list.addAll(0, Arrays.asList(first.listFiles(myFilter))); continue; } } } private static final FileFilter myFilter = new FileFilter() { public boolean accept(File file) { if(file == null) return false; if(!file.canRead()) return false; if(!file.exists()) return false; if(file.isDirectory()) return true; if(file.getName().endsWith(".jar")) return true; return false; } }; private static final FileFilter explodedJarFilter = new FileFilter() { public boolean accept(File pathname) { if(!pathname.canRead()) return false; if(pathname.isDirectory()) return true; if(pathname.getName().endsWith(".class")) return true; return false; } }; }