DZone Snippets is a public source code repository. Easily build up your personal collection of code snippets, categorize them with tags / keywords, and share them with the world

Victor has posted 1 posts at DZone. View Full User Profile

Get All Classes Within A Package

11.30.2007
| 122553 views |
  • submit to reddit
        The code below gets all classes within a given package. Notice that it should only work for classes found locally, getting really ALL classes is impossible.

    /**
     * Scans all classes accessible from the context class loader which belong to the given package and subpackages.
     *
     * @param packageName The base package
     * @return The classes
     * @throws ClassNotFoundException
     * @throws IOException
     */
    private static Class[] getClasses(String packageName)
            throws ClassNotFoundException, IOException {
        ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
        assert classLoader != null;
        String path = packageName.replace('.', '/');
        Enumeration<URL> resources = classLoader.getResources(path);
        List<File> dirs = new ArrayList<File>();
        while (resources.hasMoreElements()) {
            URL resource = resources.nextElement();
            dirs.add(new File(resource.getFile()));
        }
        ArrayList<Class> classes = new ArrayList<Class>();
        for (File directory : dirs) {
            classes.addAll(findClasses(directory, packageName));
        }
        return classes.toArray(new Class[classes.size()]);
    }

    /**
     * Recursive method used to find all classes in a given directory and subdirs.
     *
     * @param directory   The base directory
     * @param packageName The package name for classes found inside the base directory
     * @return The classes
     * @throws ClassNotFoundException
     */
    private static List<Class> findClasses(File directory, String packageName) throws ClassNotFoundException {
        List<Class> classes = new ArrayList<Class>();
        if (!directory.exists()) {
            return classes;
        }
        File[] files = directory.listFiles();
        for (File file : files) {
            if (file.isDirectory()) {
                assert !file.getName().contains(".");
                classes.addAll(findClasses(file, packageName + "." + file.getName()));
            } else if (file.getName().endsWith(".class")) {
                classes.add(Class.forName(packageName + '.' + file.getName().substring(0, file.getName().length() - 6)));
            }
        }
        return classes;
    }
    

Comments

Shubham Kumar replied on Thu, 2013/11/07 - 4:50pm

Please tell me how to give the directory and package name in the code !!


Carla Brian replied on Mon, 2012/07/16 - 6:03pm

This is really cool. This might be helpful in the future. I could use this and this would be really effective. - Mercy Ministries

Snippets Manager replied on Tue, 2010/02/16 - 4:31pm

Another way to handle spaces (or other non-alphanumeric characters) in file names is to use the URL.toURI() method: dirs.add(new File(resource.toURI())); On the downside, you now have to add URISyntaxException to the throws clause of getClasses()

Snippets Manager replied on Fri, 2012/05/04 - 5:05am

This does not work if the package is not located under the current project. How do i have to proceed to load classes from a zip file located anywhere? Would you help me please?

Vítor Souza replied on Fri, 2012/02/17 - 12:35pm

This will not work in the Activator of an OSGi bundle (at least the code from the latest comment, which I tried). Here's how I solved it: public void start(BundleContext bundleContext) throws Exception { Bundle bundle = bundleContext.getBundle(); BundleWiring wiring = bundle.adapt(BundleWiring.class); Collection resources = wiring.listResources(PACKAGE_QUALIFIED_NAME.replace('.', '/'), "*.class", 0); //$NON-NLS-1$ for (String resource : resources) { // Replace slashes back to dots and remove the trailling ".class" from the file name to form the qualified name of the class. resource = resource.replace('/', '.').substring(0, resource.length() - 6); Class clazz = bundle.loadClass(resource); // Do something with the class... } } BundleWiring is org.osgi.framework.wiring.BundleWiring, other OSGi classes are from org.osgi.framework.*. Hope it helps someone...

Snippets Manager replied on Wed, 2012/02/01 - 3:05pm

I agree, this saved some time for me. Have to make one more tweak though. In the case of jar/zip files, you are properly searching them but not properly filtering by the original package name. if (className.startsWith(packageName) && (regex == null || regex.matcher(className).matches())) classes.add(className); I also added a simple regex filter, useful as well. /** * Scans all classes accessible from the context class loader which belong to the given package and subpackages. * Adapted from http://snippets.dzone.com/posts/show/4831 and extended to support use of JAR files * * @param packageName The base package * @param regexFilter an optional class name pattern. * @return The classes */ public static Class[] getClassesInPackage(String packageName, String regexFilter) { Pattern regex = null; if (regexFilter != null) regex = Pattern.compile(regexFilter); try { ClassLoader classLoader = Thread.currentThread().getContextClassLoader(); assert classLoader != null; String path = packageName.replace('.', '/'); Enumeration resources = classLoader.getResources(path); List dirs = new ArrayList(); while (resources.hasMoreElements()) { URL resource = resources.nextElement(); dirs.add(resource.getFile()); } TreeSet classes = new TreeSet(); for (String directory : dirs) { classes.addAll(findClasses(directory, packageName, regex)); } ArrayList classList = new ArrayList(); for (String clazz : classes) { classList.add(Class.forName(clazz)); } return classList.toArray(new Class[classes.size()]); } catch (Exception e) { e.printStackTrace(); return null; } } /** * Recursive method used to find all classes in a given path (directory or zip file url). Directories * are searched recursively. (zip files are * Adapted from http://snippets.dzone.com/posts/show/4831 and extended to support use of JAR files * * @param path The base directory or url from which to search. * @param packageName The package name for classes found inside the base directory * @param regex an optional class name pattern. e.g. .*Test * @return The classes */ private static TreeSet findClasses(String path, String packageName, Pattern regex) throws Exception { TreeSet classes = new TreeSet(); if (path.startsWith("file:") && path.contains("!")) { String[] split = path.split("!"); URL jar = new URL(split[0]); ZipInputStream zip = new ZipInputStream(jar.openStream()); ZipEntry entry; while ((entry = zip.getNextEntry()) != null) { if (entry.getName().endsWith(".class")) { String className = entry.getName().replaceAll("[$].*", "").replaceAll("[.]class", "").replace('/', '.'); if (className.startsWith(packageName) && (regex == null || regex.matcher(className).matches())) classes.add(className); } } } File dir = new File(path); if (!dir.exists()) { return classes; } File[] files = dir.listFiles(); for (File file : files) { if (file.isDirectory()) { assert !file.getName().contains("."); classes.addAll(findClasses(file.getAbsolutePath(), packageName + "." + file.getName(), regex)); } else if (file.getName().endsWith(".class")) { String className = packageName + '.' + file.getName().substring(0, file.getName().length() - 6); if (regex == null || regex.matcher(className).matches()) classes.add(className); } } return classes; }

Snippets Manager replied on Mon, 2012/01/23 - 4:07pm

The following code was adapted from above, but will work if the package is inside a jar, making it useful for distribution. It will load the class names from the executing Jar as well as all supporting Jars in the classpath. It uses a treeset with the classnames to avoid duplicate classnames and also to naturally order them. /** * Scans all classes accessible from the context class loader which belong to the given package and subpackages. * Adapted from http://snippets.dzone.com/posts/show/4831 and extended to support use of JAR files * @param packageName The base package * @return The classes * @throws ClassNotFoundException * @throws IOException */ public static Class[] getClasses(String packageName) { try { ClassLoader classLoader = Thread.currentThread().getContextClassLoader(); assert classLoader != null; String path = packageName.replace('.', '/'); Enumeration resources = classLoader.getResources(path); List dirs = new ArrayList(); while (resources.hasMoreElements()) { URL resource = resources.nextElement(); dirs.add(resource.getFile()); } TreeSet classes = new TreeSet(); for (String directory : dirs) { classes.addAll(findClasses(directory, packageName)); } ArrayList classList = new ArrayList(); for (String clazz : classes) { classList.add(Class.forName(clazz)); } return classList.toArray(new Class[classes.size()]); } catch (Exception e) { e.printStackTrace(); return null; } } /** * Recursive method used to find all classes in a given directory and subdirs. * Adapted from http://snippets.dzone.com/posts/show/4831 and extended to support use of JAR files * @param directory The base directory * @param packageName The package name for classes found inside the base directory * @return The classes * @throws ClassNotFoundException */ private static TreeSet findClasses(String directory, String packageName) throws Exception { TreeSet classes = new TreeSet(); if (directory.startsWith("file:") && directory.contains("!")) { String [] split = directory.split("!"); URL jar = new URL(split[0]); ZipInputStream zip = new ZipInputStream(jar.openStream()); ZipEntry entry = null; while ((entry = zip.getNextEntry()) != null) { if (entry.getName().endsWith(".class")) { String className = entry.getName().replaceAll("[$].*", "").replaceAll("[.]class", "").replace('/', '.'); classes.add(className); } } } File dir = new File(directory); if (!dir.exists()) { return classes; } File[] files = dir.listFiles(); for (File file : files) { if (file.isDirectory()) { assert !file.getName().contains("."); classes.addAll(findClasses(file.getAbsolutePath(), packageName + "." + file.getName())); } else if (file.getName().endsWith(".class")) { classes.add(packageName + '.' + file.getName().substring(0, file.getName().length() - 6)); } } return classes; }

Snippets Manager replied on Thu, 2009/11/26 - 12:35pm

Nice snippet, helped me a lot. Below is the same snippet with the following changes: - problems with URL-encoded file paths are fixed (the same problem, as reported by davidparks21) - skipped inner, private and anonyme classes (which appear with $ in the class name) - added check for the ExceptionInInitializerError on calls to Class.forName(), in which case Class.forName() variant without initialization is used to get the class /** * Scans all classes accessible from the context class loader which belong to the given package and subpackages. * * @param packageName The base package * @return The classes * @throws ClassNotFoundException * @throws IOException */ @SuppressWarnings("unchecked") private static List getClasses(String packageName) throws ClassNotFoundException, IOException { ClassLoader classLoader = Thread.currentThread().getContextClassLoader(); assert classLoader != null; String path = packageName.replace('.', '/'); Enumeration resources = classLoader.getResources(path); List dirs = new ArrayList(); while (resources.hasMoreElements()) { URL resource = resources.nextElement(); String fileName = resource.getFile(); String fileNameDecoded = URLDecoder.decode(fileName, "UTF-8"); dirs.add(new File(fileNameDecoded)); } ArrayList classes = new ArrayList(); for (File directory : dirs) { classes.addAll(findClasses(directory, packageName)); } return classes; } /** * Recursive method used to find all classes in a given directory and subdirs. * * @param directory The base directory * @param packageName The package name for classes found inside the base directory * @return The classes * @throws ClassNotFoundException */ @SuppressWarnings("unchecked") private static List findClasses(File directory, String packageName) throws ClassNotFoundException { List classes = new ArrayList(); if (!directory.exists()) { return classes; } File[] files = directory.listFiles(); for (File file : files) { String fileName = file.getName(); if (file.isDirectory()) { assert !fileName.contains("."); classes.addAll(findClasses(file, packageName + "." + fileName)); } else if (fileName.endsWith(".class") && !fileName.contains("$")) { Class _class; try { _class = Class.forName(packageName + '.' + fileName.substring(0, fileName.length() - 6)); } catch (ExceptionInInitializerError e) { // happen, for example, in classes, which depend on // Spring to inject some beans, and which fail, // if dependency is not fulfilled _class = Class.forName(packageName + '.' + fileName.substring(0, fileName.length() - 6), false, Thread.currentThread().getContextClassLoader()); } classes.add(_class); } } return classes; }

David Parks replied on Tue, 2009/03/24 - 1:45pm

Great snippet of code, thanks. However, one change is required: This code does not work for directories with a space in the name (the use of URL passes back a URL encoded file name which isn't found by File.exists()) Quick fix (to handle file names with spaces): Replace the line shown below with the 2nd one shown: dirs.add(new File(resource.getFile())); dirs.add(new File(resource.getFile().replace("%20", " ")));

Jon Antoine replied on Sun, 2009/01/04 - 3:35pm

hello, This may not work if the package is inside a jar...