5

How do i get all classes within a package?

5 Answers5

10

You can't. Classes can come in via many different class loaders, including remote ones.

ChssPly76
  • 99,456
  • 24
  • 206
  • 195
7

Here's a more complete way to solve this for jars, based on the idea posted by JG.

/**
 * Scans all classloaders for the current thread for loaded jars, and then scans
 * each jar for the package name in question, listing all classes directly under
 * the package name in question. Assumes directory structure in jar file and class
 * package naming follow java conventions (i.e. com.example.test.MyTest would be in
 * /com/example/test/MyTest.class)
 */
public Collection<Class> getClassesForPackage(String packageName) throws Exception {
  String packagePath = packageName.replace(".", "/");
  ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
  Set<URL> jarUrls = new HashSet<URL>();

  while (classLoader != null) {
    if (classLoader instanceof URLClassLoader)
      for (URL url : ((URLClassLoader) classLoader).getURLs())
        if (url.getFile().endsWith(".jar")  // may want better way to detect jar files
          jarUrls.add(url);

    classLoader = classLoader.getParent();
  }

  Set<Class> classes = new HashSet<Class>();

  for (URL url : jarUrls) {
    JarInputStream stream = new JarInputStream(url.openStream()); // may want better way to open url connections
    JarEntry entry = stream.getNextJarEntry();

    while (entry != null) {
      String name = entry.getName();
      int i = name.lastIndexOf("/");

      if (i > 0 && name.endsWith(".class") && name.substring(0, i).equals(packagePath)) 
        classes.add(Class.forName(name.substring(0, name.length() - 6).replace("/", ".")));

      entry = stream.getNextJarEntry();
    }

    stream.close();
  }

  return classes;
}
toluju
  • 4,097
  • 2
  • 23
  • 27
4

There's a snippet from here that does exactly what you want, assuming the classes can be found locally:

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()]);
}

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;
}
João Silva
  • 89,303
  • 29
  • 152
  • 158
  • Does this work with jar'ed classes? – Thorbjørn Ravn Andersen Jul 20 '09 at 23:50
  • As far as I can tell this will fail if you have a jar (-1) but is a decent routine for looking for .class files for discovery (+1) so you break even :) – Bill K Jul 20 '09 at 23:52
  • The above code not only assumes that classes from the given package are local, but also that they have all been loaded by the same class loader AND they haven't been packaged. – ChssPly76 Jul 20 '09 at 23:53
  • This is still a useful start for a system like Eclipse where a jar is dropped into a directory and discovered on the next boot. A reasonable solution, but very limited (and since it doesn't currently support Jars, not that useful as-is) – Bill K Jul 20 '09 at 23:58
1

There is no global way to do that. That being said, if you know where your classes are coming from, you can walk the directory of a jar file or the file system.

Yishai
  • 90,445
  • 31
  • 189
  • 263
1

Java doesn't have discovery.

Most products that have the ability to add (discover) new classes either have a text file describing "Program Extensions" or a specific directory where you can place either classes or jars that uses a trick like @JG described. (This is what eclipse does and is recommended for any solution where the user may add the new module by hand)

Bill K
  • 62,186
  • 18
  • 105
  • 157
  • 1
    Don't forget the SPI APIs for plugins. These provide a reasonably elegant solution for plugin frameworks. – jsight Jul 21 '09 at 03:07