Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Should ClassPath.getTopLevelClasses() return `java.*` packages?

I'm using guava's ClassPath to retrieve available classes. With Java 8, this worked well, but I'm migrating to Java 10, and now it doesn't work. I've tried both guava 24.1 (the latest jarfile available on Maven Central) and the latest sources on github (as of April 27th 2018) -- presumably more or less guava 25.0, given they released it yesterday.

I can retrieve some classes, but anything in any java.* packages fail to appear. Is this is actually a bug, or am I using Java 10 wrong (i.e., does modularity mean I need to do something special)? I see the same symptom in Java 9. I've tried building and running with Java 10 (or 9) as well as building with Java 8 and running with Java 10 (or 9), the behaviour is the same either way.

I've set up a dummy example with two classes in packages com.just.me and com.just.another:

com.just.me.Main.java

package com.just.me;

import com.google.common.reflect.ClassPath;
import com.just.another.Dummy;
import java.lang.String;
import java.util.Collections;
import java.util.List;

import java.io.IOException;

public class Main {

    public static void main(String[] args) {
        List<String> dummyList = Collections.emptyList();
        printClassCount("com.just.me", Main.class);
        printClassCount("com.just.another", Main.class);
        printClassCount("com.just", Main.class);
        printClassCount("com.google", Main.class);
        printClassCount("java.lang", Main.class);
        printClassCount("java.util", Main.class);
        printClassCount("java", Main.class);
    }

    private static void printClassCount(String packageName, Class classForClassLoader) {
        System.out.println("Number of toplevel classes in " + packageName + ": " + countTopleveClassesInPackage(packageName, classForClassLoader));
    }

    private static int countTopleveClassesInPackage(String packageName, Class clazz) {
        try {
            ClassPath classPath = ClassPath.from(clazz.getClassLoader());
            return classPath.getTopLevelClassesRecursive(packageName).size();
        } catch (IOException e) {
            return 0;
        }
    }
}

com.just.another.Dummy.java

package com.just.another;

public class Dummy {
    private String s;
    Dummy(String s) {
        this.s = s;
    }

    @Override
    public String toString() {
        return s;
    }
}

Output (Java 8)

Number of toplevel classes in com.just.me: 1
Number of toplevel classes in com.just.another: 1
Number of toplevel classes in com.just: 2
Number of toplevel classes in com.google: 569
Number of toplevel classes in java.lang: 232
Number of toplevel classes in java.util: 367
Number of toplevel classes in java: 1878

Output (Java 9/10)

Number of toplevel classes in com.just.me: 1
Number of toplevel classes in com.just.another: 1
Number of toplevel classes in com.just: 2
Number of toplevel classes in com.google: 569
Number of toplevel classes in java.lang: 0
Number of toplevel classes in java.util: 0
Number of toplevel classes in java: 0
like image 523
rainbowgoblin Avatar asked Apr 27 '18 14:04

rainbowgoblin


1 Answers

I think, there is no bug in ClassPath. It represents a classpath but system classes are not on the classpath in Java 10:

// doesn't print rt.jar because there is no rt.jar anymore
System.out.println(StandardSystemProperty.JAVA_CLASS_PATH.value());

So this is correct behavior that getTopLevelClasses does not return any classes for the java.* package.

If you still want to list top-level system classes, you can do something like that (not exact code, just a sketch):

FileSystem fs = FileSystems.getFileSystem(URI.create("jrt:/"));

Files
    .list(fs.getPath("modules", "java.base", "java/lang"))
    .map(Path::getFileName)
    .filter(p -> p.toString().endsWith(".class") && !p.toString().contains("$"))
    .forEach(System.out::println);
like image 183
ZhekaKozlov Avatar answered Nov 01 '22 23:11

ZhekaKozlov