Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Classpath scanning does not work in tests using the module path [SPR-16977] #21515

Open
spring-issuemaster opened this issue Jun 26, 2018 · 8 comments

Comments

@spring-issuemaster
Copy link
Collaborator

@spring-issuemaster spring-issuemaster commented Jun 26, 2018

Andy Wilkinson opened SPR-16977 and commented

It appears the classpath scanning doesn't work when Surefire launches the JVM configured to use the module path. target/classes is placed on the module path and target/test-classes is patched into this module but classpath scanning only finds classes in target/test-classes.

I have attached a minimal sample that should reproduce the problem when built (mvn test) with Java 10. The sysout from the test should show that only the test class has been found:

[INFO] Running com.example.ScanningTest
[file [/Users/awilkinson/dev/temp/module-path-scanning/target/test-classes/com/example/ScanningTest.class]]

To my rather untrained eye, building with -X and examining the arguments that Surefire uses to launch the forked JVM (in target/surefire) suggests that Surefire's configuration of the JVM is correct.

When the sample is modified to work with Java 8 (remove module-info.java and change the compiler plugin configuration to remove <release>10</release>) the class in target/classes is also found:

[INFO] Running com.example.ScanningTest
[file [/Users/awilkinson/dev/temp/module-path-scanning/target/test-classes/com/example/ScanningTest.class], file [/Users/awilkinson/dev/temp/module-path-scanning/target/classes/com/example/One.class]]

Affects: 5.0.7

Reference URL: spring-projects/spring-boot#13581

Attachments:

Issue Links:

  • #20937 Compatibility with JDK 11

2 votes, 7 watchers

@spring-issuemaster

This comment has been minimized.

Copy link
Collaborator Author

@spring-issuemaster spring-issuemaster commented Jun 26, 2018

Juergen Hoeller commented

This all depends on ClassLoader.getResources results for the given base package. The module system possibly only exposes the root URL for the patched part there? In any case, debugging ClassLoader.getResources results in both scenarios would help here...

@spring-issuemaster

This comment has been minimized.

Copy link
Collaborator Author

@spring-issuemaster spring-issuemaster commented Jun 27, 2018

Andy Wilkinson commented

On closer inspection, this appears to be a bug in the JDK. With the test changed to the following:

@Test
public void scanningTest() throws Exception {
    Enumeration<URL> resourceUrls = getClass().getClassLoader().getResources("com/example");
    while (resourceUrls.hasMoreElements()) {
        System.out.println(resourceUrls.nextElement());
    }
    PathMatchingResourcePatternResolver resolver = new PathMatchingResourcePatternResolver();
        System.out.println(Arrays.toString(resolver.getResources("classpath*:com/example/**/*.class")));
}

It produces output similar to this:

[INFO] Running com.example.ScanningTest
file:/Users/awilkinson/dev/temp/module-path-scanning/target/test-classes/com/example/
file:/Users/awilkinson/dev/temp/module-path-scanning/target/test-classes/com/example
[file [/Users/awilkinson/dev/temp/module-path-scanning/target/test-classes/com/example/ScanningTest.class]]
[INFO] Tests run: 1, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 0.094 s - in com.example.ScanningTest

Note the two (slightly different) URLs that are both pointing to target/test-classes. This explains why no classes in target/classes are found.

@spring-issuemaster

This comment has been minimized.

Copy link
Collaborator Author

@spring-issuemaster spring-issuemaster commented Jun 27, 2018

Juergen Hoeller commented

Looks like a bug in the ClassLoader implementation indeed. They might not have fully tested this in module patch scenarios...

Probably worth reporting to OpenJDK. Aside from a proper fix, maybe there's a workaround to discover in time for our GA still.

@spring-issuemaster

This comment has been minimized.

Copy link
Collaborator Author

@spring-issuemaster spring-issuemaster commented Jun 27, 2018

Andy Wilkinson commented

I've reported a bug. It's in internal review at the moment (ID 9055803). I'll comment again here if it makes it out the other end and becomes public. For reference, this is the test case that I provided with the bug report. Unlike the sample attached to this issue, it does not use Spring Framework.

@spring-issuemaster

This comment has been minimized.

Copy link
Collaborator Author

@spring-issuemaster spring-issuemaster commented Jul 3, 2018

Andy Wilkinson commented

The bug is now public but has been closed as not an issue. The situation's now being discussed in this thread on the core libs dev OpenJDK mailing list.

@spring-issuemaster

This comment has been minimized.

Copy link
Collaborator Author

@spring-issuemaster spring-issuemaster commented Jul 3, 2018

Andy Wilkinson commented

The thread on the mailing list seems to have reached a conclusion. The following recommendation came from Alan Bateman:

With modules then it should be looking at the modules in the boot layer and using ModuleReader to get the contents. It can use the value of java.class.path to scan the class path.

@spring-issuemaster

This comment has been minimized.

Copy link
Collaborator Author

@spring-issuemaster spring-issuemaster commented Dec 16, 2018

Thomas Kratz commented

Are there any plans to resolve this like recommended? 

Would doFindAllClassPathResources(String path) be the point to put that? I don't clearly understand that suggestion, but maybe I could invest some time over the holidays.

I would still think this is a JDK issue.

 

@wimdeblauwe

This comment has been minimized.

Copy link

@wimdeblauwe wimdeblauwe commented Sep 9, 2019

Is there a workaround for this? I have a JavaFx project that I added Spring Boot to which works really great, but I am unable to run my @JsonTest.

As a workaround, I need to manually specify the application class + the json deserializers I want to test:

@JsonTest
class MyJsonDeserializerTest {
   ...
}

needs to become:

@JsonTest
@ContextConfiguration(classes = {MyApplication.class, MyJsonDeserializerTest.TestConfig.class})
class MyJsonDeserializerTest {
   ...

    @TestConfiguration
    static class TestConfig {
        @Bean
        public MyJsonDeserializer deserializer() {
            return new MyJsonDeserializer();
        }
    }

}

(Note: It seems I only need to do this for Maven, IntelliJ seems to run the test fine, but I have to assume that IntelliJ is somehow "cheating" given this issue and the related spring-projects/spring-boot#13581 )

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
2 participants
You can’t perform that action at this time.