106

I need to read classes contained in a Java package. Those classes are in classpath. I need to do this task from a Java program directly. Do you know a simple way to do?

List<Class> classes = readClassesFrom("my.package")
Jørgen R
  • 10,568
  • 7
  • 42
  • 59
  • 7
    Put simply, no, you can't do that easily. There are some extremely lengthy tricks that work in some situations, but I strongly suggest a different design. – skaffman Sep 21 '09 at 21:19
  • 5
    http://stackoverflow.com/questions/749533/how-to-walk-through-java-class-resources http://stackoverflow.com/questions/435890/find-java-classes-implementing-an-interface http://stackoverflow.com/questions/251336/is-something-similar-to-serviceloader-in-java-1-5 http://stackoverflow.com/questions/1429172/list-files-inside-a-jar http://stackoverflow.com/questions/205573/java-at-runtime-find-all-classes-in-app-that-extend-a-base-class http://stackoverflow.com/questions/347248/how-can-i-get-a-list-of-all-the-implementations-of-an-interface-programmatically – erickson Sep 21 '09 at 21:33
  • The solution might be found in the [Weld](http://seamframework.org/Weld) project. – Ondra Žižka Jul 22 '11 at 01:17
  • Refer this link for answer: http://stackoverflow.com/questions/176527/how-can-i-enumerate-all-classes-in-a-package-and-add-them-to-a-list – Karthik E Apr 01 '12 at 18:43
  • possible duplicate of [Can you find all classes in a package using reflection?](http://stackoverflow.com/questions/520328/can-you-find-all-classes-in-a-package-using-reflection) – rgettman May 22 '13 at 18:27
  • @Bachi has found [a solution](http://stackoverflow.com/a/14171791/1651697) that works for my purposes. – c0nstruct0r May 15 '14 at 09:48
  • The most robust mechanism for scanning all classes in a package is currently ClassGraph (I am the author): https://github.com/classgraph/classgraph -- see my longer answer below. – Luke Hutchison Oct 05 '19 at 10:19

15 Answers15

55

If you have Spring in you classpath then the following will do it.

Find all classes in a package that are annotated with XmlRootElement:

private List<Class> findMyTypes(String basePackage) throws IOException, ClassNotFoundException
{
    ResourcePatternResolver resourcePatternResolver = new PathMatchingResourcePatternResolver();
    MetadataReaderFactory metadataReaderFactory = new CachingMetadataReaderFactory(resourcePatternResolver);

    List<Class> candidates = new ArrayList<Class>();
    String packageSearchPath = ResourcePatternResolver.CLASSPATH_ALL_URL_PREFIX +
                               resolveBasePackage(basePackage) + "/" + "**/*.class";
    Resource[] resources = resourcePatternResolver.getResources(packageSearchPath);
    for (Resource resource : resources) {
        if (resource.isReadable()) {
            MetadataReader metadataReader = metadataReaderFactory.getMetadataReader(resource);
            if (isCandidate(metadataReader)) {
                candidates.add(Class.forName(metadataReader.getClassMetadata().getClassName()));
            }
        }
    }
    return candidates;
}

private String resolveBasePackage(String basePackage) {
    return ClassUtils.convertClassNameToResourcePath(SystemPropertyUtils.resolvePlaceholders(basePackage));
}

private boolean isCandidate(MetadataReader metadataReader) throws ClassNotFoundException
{
    try {
        Class c = Class.forName(metadataReader.getClassMetadata().getClassName());
        if (c.getAnnotation(XmlRootElement.class) != null) {
            return true;
        }
    }
    catch(Throwable e){
    }
    return false;
}
Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
Shalom938
  • 909
  • 2
  • 10
  • 24
  • Thanks for code snippet. :) If you want to avoid Class duplication in a candidates list, change collection type from List to Set. And use hasEnclosingClass() and getEnclosingClassName() of classMetaData. [Use getEnclosingClass method about a underlying class](https://github.com/spring-projects/spring-framework/blob/master/spring-core/src/main/java/org/springframework/core/type/ClassMetadata.java#L73) – traeper Mar 22 '17 at 07:18
32

You could use the Reflections Project described here

It's quite complete and easy to use.

Brief description from the above website:

Reflections scans your classpath, indexes the metadata, allows you to query it on runtime and may save and collect that information for many modules within your project.

Example:

Reflections reflections = new Reflections(
    new ConfigurationBuilder()
        .setUrls(ClasspathHelper.forJavaClassPath())
);
Set<Class<?>> types = reflections.getTypesAnnotatedWith(Scannable.class);
Apurv
  • 3,723
  • 3
  • 30
  • 51
Renato
  • 12,940
  • 3
  • 54
  • 85
  • Does is work in Equinox? And if so, can it do so without activating plugins? – Tag Jun 06 '12 at 08:50
  • works for equinox. on older versions of Reflections, add bundle jar vfsType. see [here](http://stackoverflow.com/questions/8339845/reflections-library-not-working-when-used-in-an-eclipse-plug-in) – zapp Mar 16 '13 at 13:09
31

I use this one, it works with files or jar archives

public static ArrayList<String>getClassNamesFromPackage(String packageName) throws IOException{
    ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
    URL packageURL;
    ArrayList<String> names = new ArrayList<String>();;

    packageName = packageName.replace(".", "/");
    packageURL = classLoader.getResource(packageName);

    if(packageURL.getProtocol().equals("jar")){
        String jarFileName;
        JarFile jf ;
        Enumeration<JarEntry> jarEntries;
        String entryName;

        // build jar file name, then loop through zipped entries
        jarFileName = URLDecoder.decode(packageURL.getFile(), "UTF-8");
        jarFileName = jarFileName.substring(5,jarFileName.indexOf("!"));
        System.out.println(">"+jarFileName);
        jf = new JarFile(jarFileName);
        jarEntries = jf.entries();
        while(jarEntries.hasMoreElements()){
            entryName = jarEntries.nextElement().getName();
            if(entryName.startsWith(packageName) && entryName.length()>packageName.length()+5){
                entryName = entryName.substring(packageName.length(),entryName.lastIndexOf('.'));
                names.add(entryName);
            }
        }

    // loop through files in classpath
    }else{
    URI uri = new URI(packageURL.toString());
    File folder = new File(uri.getPath());
        // won't work with path which contains blank (%20)
        // File folder = new File(packageURL.getFile()); 
        File[] contenuti = folder.listFiles();
        String entryName;
        for(File actual: contenuti){
            entryName = actual.getName();
            entryName = entryName.substring(0, entryName.lastIndexOf('.'));
            names.add(entryName);
        }
    }
    return names;
}
Chris
  • 1,119
  • 1
  • 8
  • 26
Edoardo Panfili
  • 322
  • 3
  • 8
  • 1
    this works if you take out the reference to File.Separator and just use '/' – Will Glass Nov 21 '11 at 09:44
  • 1
    One more change is required for this to work with jar files when the path contains spaces. You must decode the jar file path. Change: jarFileName= packageURL.getFile(); to: jarFileName= URLDecoder.decode(packageURL.getFile()); – Will Glass Nov 22 '11 at 20:06
  • i get only the package name in jar..not getting class name – AutoMEta Sep 16 '13 at 10:42
  • new File(uri) will fix your issue with spaces. – Hakanai Oct 25 '13 at 01:09
  • entryName.lastIndexOf('.') would be -1 – marstone Jan 31 '14 at 13:27
  • Thanks for the code! I created a reusable "library" from it https://github.com/terraframe/Runway-SDK/blob/dev/runwaysdk-common/src/main/java/com/runwaysdk/ClasspathResource.java – Ring Feb 17 '18 at 01:43
12

Spring has implemented an excellent classpath search function in the PathMatchingResourcePatternResolver. If you use the classpath*: prefix, you can find all the resources, including classes in a given hierarchy, and even filter them if you want. Then you can use the children of AbstractTypeHierarchyTraversingFilter, AnnotationTypeFilter and AssignableTypeFilter to filter those resources either on class level annotations or on interfaces they implement.

Apurv
  • 3,723
  • 3
  • 30
  • 51
John Ellinwood
  • 14,291
  • 7
  • 38
  • 48
7

The most robust mechanism for listing all classes in a given package is currently ClassGraph, because it handles the widest possible array of classpath specification mechanisms, including the new JPMS module system. (I am the author.)

List<String> classNames;
try (ScanResult scanResult = new ClassGraph().whitelistPackages("my.package")
        .enableClassInfo().scan()) {
    classNames = scanResult.getAllClasses().getNames();
}
Luke Hutchison
  • 8,186
  • 2
  • 45
  • 40
  • I just came across this a couple years later. This is a great tool! It works much faster than reflection and I like that it does not invoke static initializers. Just what I needed to solve a problem we had. – akagixxer Sep 07 '17 at 15:25
6

Java 1.6.0_24:

public static File[] getPackageContent(String packageName) throws IOException{
    ArrayList<File> list = new ArrayList<File>();
    Enumeration<URL> urls = Thread.currentThread().getContextClassLoader()
                            .getResources(packageName);
    while (urls.hasMoreElements()) {
        URL url = urls.nextElement();
        File dir = new File(url.getFile());
        for (File f : dir.listFiles()) {
            list.add(f);
        }
    }
    return list.toArray(new File[]{});
}

This solution was tested within the EJB environment.

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
Paul Kuit
  • 101
  • 1
  • 2
6

Scannotation and Reflections use class path scanning approach:

Reflections reflections = new Reflections("my.package");
Set<Class<? extends Object>> classes = reflections.getSubTypesOf(Object.class);

Another approach is to use Java Pluggable Annotation Processing API to write annotation processor which will collect all annotated classes at compile time and build the index file for runtime use. This mechanism is implemented in ClassIndex library:

Iterable<Class> classes = ClassIndex.getPackageClasses("my.package");
Ferrybig
  • 18,194
  • 6
  • 57
  • 79
Sławek
  • 1,450
  • 14
  • 14
2

That functionality is still suspiciously missing from the Java reflection API as far as I know. You can get a package object by just doing this:

Package packageObj = Package.getPackage("my.package");

But as you probably noticed, that won't let you list the classes in that package. As of right now, you have to take sort of a more filesystem-oriented approach.

I found some sample implementations in this post

I'm not 100% sure these methods will work when your classes are buried in JAR files, but I hope one of those does it for you.

I agree with @skaffman...if you have another way of going about this, I'd recommend doing that instead.

Apurv
  • 3,723
  • 3
  • 30
  • 51
Brent Writes Code
  • 19,075
  • 7
  • 52
  • 56
  • 4
    It's not suspicious, it just doesn't work that way. Classes don't "belong" to packages, they have references to them. The association doesn't point in the other direction. – skaffman Sep 21 '09 at 21:34
  • 1
    @skaffman Very interesting point. Never thought about it that way. So as long as we're wandering down that trail of thought, why is the association not bi-directional (this is more for my own curiosity now)? – Brent Writes Code Sep 21 '09 at 21:54
2
  1. Bill Burke has written a (nice article about class scanning] and then he wrote Scannotation.

  2. Hibernate has this already written:

    • org.hibernate.ejb.packaging.Scanner
    • org.hibernate.ejb.packaging.NativeScanner
  3. CDI might solve this, but don't know - haven't investigated fully yet

.

@Inject Instance< MyClass> x;
...
x.iterator() 

Also for annotations:

abstract class MyAnnotationQualifier
extends AnnotationLiteral<Entity> implements Entity {}
Apurv
  • 3,723
  • 3
  • 30
  • 51
Ondra Žižka
  • 43,948
  • 41
  • 217
  • 277
2

eXtcos looks promising. Imagine you want to find all the classes that:

  1. Extend from class "Component", and store them
  2. Are annotated with "MyComponent", and
  3. Are in the “common” package.

With eXtcos this is as simple as

ClasspathScanner scanner = new ClasspathScanner();
final Set<Class> classStore = new ArraySet<Class>();

Set<Class> classes = scanner.getClasses(new ClassQuery() {
    protected void query() {
        select().
        from(“common”).
        andStore(thoseExtending(Component.class).into(classStore)).
        returning(allAnnotatedWith(MyComponent.class));
    }
});
noelob
  • 460
  • 5
  • 14
1

I happen to have implemented it, and it works in most cases. Since it is long, I put it in a file here.

The idea is to find the location of the class source file which is available in most cases (a known exception are JVM class files -- as far as I've tested). If the code is in a directory, scan through all files and only spot class files. If the code is in a JAR file, scan all entries.

This method can only be used when:

  1. You have a class that is in the same package you want to discover, This class is called a SeedClass. For example, if you want to list all classes in 'java.io', the seed class may be java.io.File.

  2. Your classes are in a directory or in a JAR file it has source file information (not source code file, but just source file). As far as I've tried, it work almost 100% except the JVM class (those classes come with the JVM).

  3. Your program must have permission to access ProtectionDomain of those classes. If your program is loaded locally, there should be no problem.

I've tested the program only for my regular usage, so it may still have problem.

I hope this helps.

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
NawaMan
  • 25,129
  • 10
  • 51
  • 77
  • 1
    it appear to be a very interesting stuff! i will try to use it. If i found helpful for me, can i use your code in an open source project? –  Nov 04 '09 at 07:05
  • 1
    I am preparing to open-source it too. So go ahead :D – NawaMan Nov 04 '09 at 11:24
  • @NawaMan: Did you opensource it, finally? If so: where can we find the last version? Thanks! – Joanis Jan 05 '12 at 04:41
1

Here is another option, slight modification to another answer in above/below:

Reflections reflections = new Reflections("com.example.project.package", 
    new SubTypesScanner(false));
Set<Class<? extends Object>> allClasses = 
    reflections.getSubTypesOf(Object.class);
Alex Rashkov
  • 9,833
  • 3
  • 32
  • 58
0

Back when applets were common place, one might have a URL on the classpath. When the classloader required a class, it would search all the locations on the classpath, including http resources. Because you can have things like URLs and directories on the classpath, there is no easy way to get a definitive list of the classes.

However, you can get pretty close. Some of the Spring libraries are doing this now. You can get all the jar's on the classpath, and open them up like files. You can then take this list of files, and create a data structure containing your classes.

brianegge
  • 29,240
  • 13
  • 74
  • 99
0

use dependency maven:

groupId: net.sf.extcos
artifactId: extcos
version: 0.4b

then use this code :

ComponentScanner scanner = new ComponentScanner();
        Set classes = scanner.getClasses(new ComponentQuery() {
            @Override
            protected void query() {
                select().from("com.leyton").returning(allExtending(DynamicForm.class));
            }
        });
Lucas Zamboulis
  • 2,494
  • 5
  • 24
  • 27
Yalami
  • 63
  • 1
  • 3
  • 11
-2

Brent - the reason the association is one way has to do with the fact that any class on any component of your CLASSPATH can declare itself in any package (except for java/javax). Thus there just is no mapping of ALL the classes in a given "package" because nobody knows nor can know. You could update a jar file tomorrow and remove or add classes. It's like trying to get a list of all people named John/Jon/Johan in all the countries of the world - none of us is omniscient therefore none of us will ever have the correct answer.

idarwin
  • 607
  • 4
  • 19