import java.io.File; import java.io.FilenameFilter; import java.io.Serializable; import java.net.MalformedURLException; import java.net.URL; import java.util.ArrayList; import java.util.Collection; import java.util.Enumeration; import java.util.List; import java.util.jar.JarEntry; import java.util.jar.JarFile; /** * Find classes in the classpath (reads JARs and classpath folders). * * @author Pål Brattberg, brattberg@gmail.com * @see http://gist.github.com/pal */ @SuppressWarnings("unchecked") public class ClasspathInspector { static boolean DEBUG = false; public static List getAllKnownClassNames() { List classNames = new ArrayList(); List classLocations = getClassLocationsForCurrentClasspath(); for (File file : classLocations) { classNames.addAll(getClassNamesFromPath(file)); } return classNames; } public static List getAllKnownClasses() { List classFiles = new ArrayList(); List classLocations = getClassLocationsForCurrentClasspath(); for (File file : classLocations) { classFiles.addAll(getClassesFromPath(file)); } return classFiles; } public static List getMatchingClasses(Class interfaceOrSuperclass) { List matchingClasses = new ArrayList(); List classes = getAllKnownClasses(); log("checking %s classes", classes.size()); for (Class clazz : classes) { if (interfaceOrSuperclass.isAssignableFrom(clazz)) { matchingClasses.add(clazz); log("class %s is assignable from %s", interfaceOrSuperclass, clazz); } } return matchingClasses; } public static List getMatchingClasses(String validPackagePrefix, Class interfaceOrSuperclass) { throw new IllegalStateException("Not yet implemented!"); } public static List getMatchingClasses(String validPackagePrefix) { throw new IllegalStateException("Not yet implemented!"); } private static Collection getClassesFromPath(File path) { if (path.isDirectory()) { return getClassesFromDirectory(path); } else { return getClassesFromJarFile(path); } } private static Collection getClassNamesFromPath(File path) { if (path.isDirectory()) { return getClassNamesFromDirectory(path); } else { return getClassNamesFromJarFile(path); } } private static String fromFileToClassName(final String fileName) { return fileName.substring(0, fileName.length() - 6).replaceAll("/|\\\\", "\\."); } private static List getClassesFromJarFile(File path) { List classes = new ArrayList(); log("getClassesFromJarFile: Getting classes for " + path); try { if (path.canRead()) { JarFile jar = new JarFile(path); Enumeration en = jar.entries(); while (en.hasMoreElements()) { JarEntry entry = en.nextElement(); if (entry.getName().endsWith("class")) { String className = fromFileToClassName(entry.getName()); log("\tgetClassesFromJarFile: found " + className); Class claz = Class.forName(className); classes.add(claz); } } } } catch (Exception e) { throw new RuntimeException("Failed to read classes from jar file: " + path, e); } return classes; } private static List getClassNamesFromJarFile(File path) { List classes = new ArrayList<>(); log("getClassNamesFromJarFile: Getting classes for " + path); try { if (path.canRead()) { JarFile jar = new JarFile(path); Enumeration en = jar.entries(); while (en.hasMoreElements()) { JarEntry entry = en.nextElement(); if (entry.getName().endsWith("class")) { String className = fromFileToClassName(entry.getName()); classes.add(className); log("\tgetClassesFromJarFile: found " + className); } } } } catch (Exception e) { throw new RuntimeException("Failed to read classnames from jar file: " + path, e); } return classes; } private static List getClassesFromDirectory(File path) { List classes = new ArrayList(); log("getClassesFromDirectory: Getting classes for " + path); // get jar files from top-level directory List jarFiles = listFiles(path, new FilenameFilter() { @Override public boolean accept(File dir, String name) { return name.endsWith(".jar"); } }, false); for (File file : jarFiles) { classes.addAll(getClassesFromJarFile(file)); } // get all class-files List classFiles = listFiles(path, new FilenameFilter() { @Override public boolean accept(File dir, String name) { return name.endsWith(".class"); } }, true); // List urlList = new ArrayList(); // List classNameList = new ArrayList(); int substringBeginIndex = path.getAbsolutePath().length() + 1; for (File classfile : classFiles) { String className = classfile.getAbsolutePath().substring(substringBeginIndex); className = fromFileToClassName(className); log("Found class %s in path %s: ", className, path); try { classes.add(Class.forName(className)); } catch (Throwable e) { log("Couldn't create class %s. %s: ", className, e); } } return classes; } private static List getClassNamesFromDirectory(File path) { List classes = new ArrayList<>(); log("getClassNamesFromDirectory: Getting classes for " + path); // get jar files from top-level directory List jarFiles = listFiles(path, new FilenameFilter() { @Override public boolean accept(File dir, String name) { return name.endsWith(".jar"); } }, false); for (File file : jarFiles) { classes.addAll(getClassNamesFromJarFile(file)); } // get all class-files List classFiles = listFiles(path, new FilenameFilter() { @Override public boolean accept(File dir, String name) { return name.endsWith(".class"); } }, true); // List urlList = new ArrayList(); // List classNameList = new ArrayList(); int substringBeginIndex = path.getAbsolutePath().length() + 1; for (File classfile : classFiles) { String className = classfile.getAbsolutePath().substring(substringBeginIndex); className = fromFileToClassName(className); log("Found class %s in path %s: ", className, path); classes.add(className); } return classes; } private static List listFiles(File directory, FilenameFilter filter, boolean recurse) { List files = new ArrayList(); File[] entries = directory.listFiles(); // Go over entries for (File entry : entries) { // If there is no filter or the filter accepts the // file / directory, add it to the list if (filter == null || filter.accept(directory, entry.getName())) { files.add(entry); } // If the file is a directory and the recurse flag // is set, recurse into the directory if (recurse && entry.isDirectory()) { files.addAll(listFiles(entry, filter, recurse)); } } // Return collection of files return files; } public static List getClassLocationsForCurrentClasspath() { List urls = new ArrayList(); String javaClassPath = System.getProperty("java.class.path"); if (javaClassPath != null) { for (String path : javaClassPath.split(File.pathSeparator)) { urls.add(new File(path)); } } return urls; } // todo: this is only partial, probably public static URL normalize(URL url) throws MalformedURLException { String spec = url.getFile(); // get url base - remove everything after ".jar!/??" , if exists final int i = spec.indexOf("!/"); if (i != -1) { spec = spec.substring(0, spec.indexOf("!/")); } // uppercase windows drive url = new URL(url, spec); final String file = url.getFile(); final int i1 = file.indexOf(':'); if (i1 != -1) { String drive = file.substring(i1 - 1, 2).toUpperCase(); url = new URL(url, file.substring(0, i1 - 1) + drive + file.substring(i1)); } return url; } private static void log(String pattern, final Object... args) { if (DEBUG) System.out.printf(pattern + "\n", args); } public static void main(String[] args) { // find all classes in classpath List allClasses = ClasspathInspector.getAllKnownClasses(); System.out.printf("There are %s classes available in the classpath\n", allClasses.size()); // find all classes that implement/subclass an interface/superclass List serializableClasses = ClasspathInspector.getMatchingClasses(Serializable.class); for (Class clazz : serializableClasses) { System.out.printf("%s is Serializable\n", clazz); } } }