findResource("") returning null when module-info.java is present, why is that?
One thing I notice is that your application (assuming that it's packaged in tech.flexpoint.dashman
) does not seem to be opened up to Spring in any way, which will surely result in failed class loading/illegal access.
I would expect to see something like this in module-info.java
(depending on your Spring dependencies):
opens tech.flexpoint.dashman to spring.core, spring.beans, spring.context;
The exception is a NoClassDefFoundError
, which is thrown at runtime when the class definition of a class that was known at compile time cannot be resolved, in this case the interface javax.transaction.UserTransaction
, which is part of the Java Transaction API (JTA).
As others have pointed out, JTA is not bundled with the JDK, and needs to be added as a compile dependency. However, the class that needs to load the UserTransaction
class definition comes from the spring-boot-autoconfigure
artifact, which is responsible for its own dependencies (spring-boot-autoconfigure@2.4.0.RELEASE
🡒 jboss-transaction-spi@7.6.0.Final
🡒 jboss-transaction-api_1.2_spec@1.1.1.Final
), so you should not need to add JTA as a dependency.
However, because you want to package your own app as a Java 9 module, it needs to explicitly state its dependencies. spring-boot-autoconfigure
is not yet a modularized Java 9 library, and does not do this for you (i.e. transitively). The automatic module name for JTA is java.transaction
, so you need to add the requirement in module-info.java
:
requires java.transaction;
I got your example running and did indeed get NoClassDefFoundError
s when running from IntelliJ IDEA. The stacktrace pointed back to a ClassNotFoundException
, which indicates classpath problems. Since IDEA calculates the classpath when launching the application from there, I wanted to see if I could reproduce the error when using the spring-boot-maven-plugin
to run the application.
I copied the IDEA run configuration to the spring-boot-maven-plugin
configuration, as shown below:
<plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> <configuration> <mainClass>tech.flexpoint.demo.DemoApplication</mainClass> <jvmArguments>--show-module-resolution --add-opens=java.base/java.lang=spring.core --add-opens=java.base/java.io=tomcat.embed.core --add-opens=java.base/java.lang=ALL-UNNAMED --add-opens=java.rmi/sun.rmi.transport=ALL-UNNAMED</jvmArguments> <workingDirectory>${project.basedir}</workingDirectory> </configuration></plugin>
Then I invoked mvn spring-boot:run
and voila, the application booted successfully without errors.I can only conclude that this is an issue with the classpath calculated by IntelliJ.
Assuming you have declared the dependency:
<dependency> <groupId>javax.transaction</groupId> <artifactId>javax.transaction-api</artifactId> <version>1.3</version></dependency>
Include the following in module-info.java
:
requires java.transaction;
Version 1.3 declares the automatic module name, while version 1.2 doesn't.
The latter requires javax.transaction.api;
. Source
As you have mentioned in your original problem, the code works without module-info.java but not with the module-info.java. I can see that you have done all this hard work in explaining the problem, creating a minimal project and so on to drill down to the problem.
Looking at your problem it is obvious that one of the modules is causing the URLClassLoader.findResource("")
returning null
. It might be one of the modules down the list is overriding this class method or have an ambiguous implementation.
Why don't you start with an empty module-info.java for the minimal example and keep adding 1 module at a time till we see the error? I believe this will help us find the culprit.