reflections icon indicating copy to clipboard operation
reflections copied to clipboard

getAllTypes does not return all classes in a package

Open hayos opened this issue 5 years ago • 2 comments

Reflections.getAllTypes() only returns a subset of the types in the package. This is also the case for Store.getAll, Store.get and Store.getAllIncluding.

This is our test class:

@Test
public void testFindAllClassesInPackage() {
    String packageName = "com.acme.globe.compliancetest.util.findallclassesinpackage.test.model.classes";
    Set<Class<?>> expectedClasses = new HashSet<>();
    expectedClasses.add(ExtendedAndInterfacedExternallyTestClass.class);
    expectedClasses.add(ExtendedAndInterfacedInternallyTestClass.class);
    expectedClasses.add(ExtendedTestClass.class);
    expectedClasses.add(InterfacedTestClass.class);
    expectedClasses.add(RawTestClass.class);
    expectedClasses.add(InternalTestInterface.class);
    expectedClasses.add(SubPackageTestClass.class);
    //
    Set<Class<?>> classes = ComplianceTestUtils.findAllClassesInPackage(packageName);
    assertEquals(expectedClasses.size(), classes.size());
    expectedClasses.removeAll(classes);
    assertEquals("Expected classes not found: " + expectedClasses.toString(), expectedClasses.size(), 0);
}

The classes and interfaces are defined as follows:

package com.acme.globe.compliancetest.util.findallclassesinpackage.test.model.classes;
public class ExtendedAndInterfacedExternallyTestClass extends BaseAbstractTestClass implements ExternalTestInterface {

package com.acme.globe.compliancetest.util.findallclassesinpackage.test.model.classes;
public class ExtendedAndInterfacedInternallyTestClass extends BaseAbstractTestClass implements InternalTestInterface {

package com.acme.globe.compliancetest.util.findallclassesinpackage.test.model.classes;
public class ExtendedTestClass extends BaseAbstractTestClass {

package com.acme.globe.compliancetest.util.findallclassesinpackage.test.model.classes;
public class InterfacedTestClass implements ExternalTestInterface {

package com.acme.globe.compliancetest.util.findallclassesinpackage.test.model.classes;
public class InterfacedTestClass implements ExternalTestInterface {

package com.acme.globe.compliancetest.util.findallclassesinpackage.test.model.classes;
public class RawTestClass {

package com.acme.globe.compliancetest.util.findallclassesinpackage.test.model.classes.interfaces;
public interface InternalTestInterface {

package com.acme.globe.compliancetest.util.findallclassesinpackage.test.model.classes.sub;
public class SubPackageTestClass {

The ExternalTestInterface resides outside the package:

package com.acme.globe.compliancetest.util.findallclassesinpackage.test.model.interfaces;
public interface ExternalTestInterface {

Two classes are not discovered.

java.lang.AssertionError: Expected classes not found: [class com.acme.globe.compliancetest.util.findallclassesinpackage.test.model.classes.ExtendedTestClass, class com.acme.globe.compliancetest.util.findallclassesinpackage.test.model.classes.ExtendedAndInterfacedExternallyTestClass] expected:<2> but was:<0> at org.junit.Assert.fail(Assert.java:88)

Here is the implementation of the method under test, with a few alternative implementations:

    private static Set<String> getAllClassNamesFromReflections(final Reflections reflections) {
        // a) previous implementation - method getStoreMap was removed before 0.9.11:
        //        Map<String, Multimap<String, String>> storeMap = reflections.getStore().getStoreMap();
        //
        //        Set<String> classes = new HashSet<>();
        //        for (Entry<String, Multimap<String, String>> entry : storeMap.entrySet()) {
        //            classes.addAll(entry.getValue().values());
        //        }
        //        return classes;

        // b) expected implementation - does not return all results (this is a showstopper defect in the library)
        return reflections.getAllTypes();

        // c) workaround implementation for 0.9.12 - no way:
        //        Set<String> keys = reflections.getStore().keySet();
        //        //        return reflections.getStore().get(reflections.getConfiguration().getScanners().iterator().next().getClass(), keys);
        //
        //        Set<String> b = reflections.getStore().get(reflections.getConfiguration().getScanners().iterator().next().getClass(), "java.lang.Object");
        //        
        //        Set<String> c = reflections.getStore().getAllIncluding(reflections.getConfiguration().getScanners().iterator().next().getClass(), Collections.singletonList("java.lang.Object"));
        //        return null;

        // d) workaround with 0.9.11 - actually works well, but due to incompatibilities with Columbo 0.9.11 cannot be used. In 0.9.12 the method "get" is not visible.
        //        Multimap<String, String> storeMap = reflections.getStore().get(reflections.getConfiguration().getScanners().iterator().next().getClass().getSimpleName());
        //        Set<String> classes = new HashSet<>();
        //        for (Entry<String, String> entry : storeMap.entries()) {
        //            classes.add(entry.getValue());
        //        }
        //        return classes;
    }

As you see, for 0.9.12 there is no workaround left, because of your downward-incompatibility policy for the API. To me it seems the functionality even did not work in 0.9.8. Otherwise the developer of a) would not have created this strange code to exctract the classes from the MultiMap.

You may consider to add a similar test to your library. The test should be extended with other types like Enums and cover any possible combination.

I do not need any consultation, we will remove the library. Anyhow, other consumers of the library must get correct results. In the current state the library should be withdrawn IMHO.

hayos avatar Jul 15 '20 17:07 hayos

Environment: Java 8 (jdk-1.8.0-202-win_x64).

hayos avatar Jul 15 '20 17:07 hayos

I can't find the function findAllClassesInPackage() in the source code of reflections, did you wrote this function yourself?

dota17 avatar Jul 27 '20 12:07 dota17