Thursday, January 19, 2012

Use Java reflection to find classes that implement an interface from a package

How do you find a set of Classes that implement and interface? You do this (well, I did this).
final List<Class<?>> processorCandidates = ReflectionHelper.findClassesImpmenenting(HostDataProcessor.class, HostDataProcessor.class.getPackage());
And use this helper class:
public class ReflectionHelper {

    public static List<Class<?>> findClassesImpmenenting(final Class<?> interfaceClass, final Package fromPackage) {

        if (interfaceClass == null) {
            Debug.println("Unknown subclass.");
            return null;
        }

        if (fromPackage == null) {
            Debug.println("Unknown package.");
            return null;
        }

        final List<Class<?>> rVal = new ArrayList<Class<?>>();
        try {
            final Class<?>[] targets = getAllClassesFromPackage(fromPackage.getName());
            if (targets != null) {
                for (Class<?> aTarget : targets) {
                    if (aTarget == null) {
                        continue;
                    }
                    else if (aTarget.equals(interfaceClass)) {
                        Debug.println("Found the interface definition.");
                        continue;
                    }
                    else if (!interfaceClass.isAssignableFrom(aTarget)) {
                        Debug.println("Class '" + aTarget.getName() + "' is not a " + interfaceClass.getName());
                        continue;
                    }
                    else {
                        rVal.add(aTarget);
                    }
                }
            }
        }
        catch (ClassNotFoundException e) {
            Debug.println("Error reading package name.");
            Debug.printStackTrace(e, Debug.LOW_LEVEL);
        }
        catch (IOException e) {
            Debug.println("Error reading classes in package.");
            Debug.printStackTrace(e, Debug.LOW_LEVEL);
        }

        return rVal;
    }

    /**
     * Load all classes from a package.
     * 
     * @param packageName
     * @return
     * @throws ClassNotFoundException
     * @throws IOException
     */
    public static Class[] getAllClassesFromPackage(final 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()]);
    }

    /**
     * Find file in package.
     * 
     * @param directory
     * @param packageName
     * @return
     * @throws ClassNotFoundException
     */
    public 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;
    }
}
which uses the two methods found here

1 comment:

Unknown said...

can you provide some imports too?