findResource(“”) returning null when module-info.java is present, why is that?

  • A+
Category:Languages

I'm debugging why in the presence of module-info.java in my Spring Boot application, spring-orm throws an exception during start up time. This is the exception:

org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'entityManagerFactory' defined in class path resource [org/springframework/boot/autoconfigure/orm/jpa/HibernateJpaConfiguration.class]: Invocation of init method failed; nested exception is java.lang.NoClassDefFoundError: javax/transaction/UserTransaction     at spring.beans@5.0.8.RELEASE/org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.initializeBean(AbstractAutowireCapableBeanFactory.java:1699) ~[spring-beans-5.0.8.RELEASE.jar:na]     at spring.beans@5.0.8.RELEASE/org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:573) ~[spring-beans-5.0.8.RELEASE.jar:na]     at spring.beans@5.0.8.RELEASE/org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:495) ~[spring-beans-5.0.8.RELEASE.jar:na]     at spring.beans@5.0.8.RELEASE/org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean$0(AbstractBeanFactory.java:317) ~[spring-beans-5.0.8.RELEASE.jar:na]     at spring.beans@5.0.8.RELEASE/org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:222) ~[spring-beans-5.0.8.RELEASE.jar:na]     at spring.beans@5.0.8.RELEASE/org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:315) ~[spring-beans-5.0.8.RELEASE.jar:na]     at spring.beans@5.0.8.RELEASE/org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:199) ~[spring-beans-5.0.8.RELEASE.jar:na]     at spring.context@5.0.8.RELEASE/org.springframework.context.support.AbstractApplicationContext.getBean(AbstractApplicationContext.java:1089) ~[spring-context-5.0.8.RELEASE.jar:na]     at spring.context@5.0.8.RELEASE/org.springframework.context.support.AbstractApplicationContext.finishBeanFactoryInitialization(AbstractApplicationContext.java:859) ~[spring-context-5.0.8.RELEASE.jar:na]     at spring.context@5.0.8.RELEASE/org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:550) ~[spring-context-5.0.8.RELEASE.jar:na]     at spring.boot@2.0.4.RELEASE/org.springframework.boot.web.servlet.context.ServletWebServerApplicationContext.refresh(ServletWebServerApplicationContext.java:140) ~[spring-boot-2.0.4.RELEASE.jar:na]     at spring.boot@2.0.4.RELEASE/org.springframework.boot.SpringApplication.refresh(SpringApplication.java:762) [spring-boot-2.0.4.RELEASE.jar:na]     at spring.boot@2.0.4.RELEASE/org.springframework.boot.SpringApplication.refreshContext(SpringApplication.java:398) [spring-boot-2.0.4.RELEASE.jar:na]     at spring.boot@2.0.4.RELEASE/org.springframework.boot.SpringApplication.run(SpringApplication.java:330) [spring-boot-2.0.4.RELEASE.jar:na]     at spring.boot@2.0.4.RELEASE/org.springframework.boot.SpringApplication.run(SpringApplication.java:1258) [spring-boot-2.0.4.RELEASE.jar:na]     at spring.boot@2.0.4.RELEASE/org.springframework.boot.SpringApplication.run(SpringApplication.java:1246) [spring-boot-2.0.4.RELEASE.jar:na]     at tech.flexpoint.dashmanserver/tech.flexpoint.dashmanserver.DashmanServerApplication.main(DashmanServerApplication.java:13) [classes/:na]     at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method) ~[na:na]     at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) ~[na:na]     at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) ~[na:na]     at java.base/java.lang.reflect.Method.invoke(Method.java:564) ~[na:na]     at spring.boot.devtools@2.0.4.RELEASE/org.springframework.boot.devtools.restart.RestartLauncher.run(RestartLauncher.java:49) [spring-boot-devtools-2.0.4.RELEASE.jar:na] Caused by: java.lang.NoClassDefFoundError: javax/transaction/UserTransaction     at java.base/java.lang.Class.getDeclaredMethods0(Native Method) ~[na:na]     at java.base/java.lang.Class.privateGetDeclaredMethods(Class.java:3119) ~[na:na]     at java.base/java.lang.Class.privateGetPublicMethods(Class.java:3144) ~[na:na]     at java.base/java.lang.Class.getMethods(Class.java:1863) ~[na:na]     at hibernate.core@5.2.17.Final/org.hibernate.service.internal.AbstractServiceRegistryImpl.applyInjections(AbstractServiceRegistryImpl.java:288) ~[hibernate-core-5.2.17.Final.jar:na]     at hibernate.core@5.2.17.Final/org.hibernate.service.internal.AbstractServiceRegistryImpl.injectDependencies(AbstractServiceRegistryImpl.java:279) ~[hibernate-core-5.2.17.Final.jar:na]     at hibernate.core@5.2.17.Final/org.hibernate.service.internal.AbstractServiceRegistryImpl.initializeService(AbstractServiceRegistryImpl.java:239) ~[hibernate-core-5.2.17.Final.jar:na]     at hibernate.core@5.2.17.Final/org.hibernate.service.internal.AbstractServiceRegistryImpl.getService(AbstractServiceRegistryImpl.java:210) ~[hibernate-core-5.2.17.Final.jar:na]     at hibernate.core@5.2.17.Final/org.hibernate.service.internal.SessionFactoryServiceRegistryImpl.getService(SessionFactoryServiceRegistryImpl.java:80) ~[hibernate-core-5.2.17.Final.jar:na]     at hibernate.core@5.2.17.Final/org.hibernate.internal.SessionFactoryImpl.canAccessTransactionManager(SessionFactoryImpl.java:942) ~[hibernate-core-5.2.17.Final.jar:na]     at hibernate.core@5.2.17.Final/org.hibernate.internal.SessionFactoryImpl.buildCurrentSessionContext(SessionFactoryImpl.java:953) ~[hibernate-core-5.2.17.Final.jar:na]     at hibernate.core@5.2.17.Final/org.hibernate.internal.SessionFactoryImpl.<init>(SessionFactoryImpl.java:319) ~[hibernate-core-5.2.17.Final.jar:na]     at hibernate.core@5.2.17.Final/org.hibernate.boot.internal.SessionFactoryBuilderImpl.build(SessionFactoryBuilderImpl.java:462) ~[hibernate-core-5.2.17.Final.jar:na]     at hibernate.core@5.2.17.Final/org.hibernate.jpa.boot.internal.EntityManagerFactoryBuilderImpl.build(EntityManagerFactoryBuilderImpl.java:892) ~[hibernate-core-5.2.17.Final.jar:na]     at spring.orm@5.0.8.RELEASE/org.springframework.orm.jpa.vendor.SpringHibernateJpaPersistenceProvider.createContainerEntityManagerFactory(SpringHibernateJpaPersistenceProvider.java:57) ~[spring-orm-5.0.8.RELEASE.jar:na]     at spring.orm@5.0.8.RELEASE/org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean.createNativeEntityManagerFactory(LocalContainerEntityManagerFactoryBean.java:365) ~[spring-orm-5.0.8.RELEASE.jar:na]     at spring.orm@5.0.8.RELEASE/org.springframework.orm.jpa.AbstractEntityManagerFactoryBean.buildNativeEntityManagerFactory(AbstractEntityManagerFactoryBean.java:390) ~[spring-orm-5.0.8.RELEASE.jar:na]     at spring.orm@5.0.8.RELEASE/org.springframework.orm.jpa.AbstractEntityManagerFactoryBean.afterPropertiesSet(AbstractEntityManagerFactoryBean.java:377) ~[spring-orm-5.0.8.RELEASE.jar:na]     at spring.orm@5.0.8.RELEASE/org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean.afterPropertiesSet(LocalContainerEntityManagerFactoryBean.java:341) ~[spring-orm-5.0.8.RELEASE.jar:na]     at spring.beans@5.0.8.RELEASE/org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.invokeInitMethods(AbstractAutowireCapableBeanFactory.java:1758) ~[spring-beans-5.0.8.RELEASE.jar:na]     at spring.beans@5.0.8.RELEASE/org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.initializeBean(AbstractAutowireCapableBeanFactory.java:1695) ~[spring-beans-5.0.8.RELEASE.jar:na]     ... 21 common frames omitted Caused by: java.lang.ClassNotFoundException: javax.transaction.UserTransaction     at java.base/jdk.internal.loader.BuiltinClassLoader.loadClass(BuiltinClassLoader.java:582) ~[na:na]     at java.base/jdk.internal.loader.ClassLoaders$AppClassLoader.loadClass(ClassLoaders.java:190) ~[na:na]     at java.base/java.lang.ClassLoader.loadClass(ClassLoader.java:499) ~[na:na]     ... 42 common frames omitted 

I tracked down the problem to to URLClassLoader.findResource("") returning null if module-info.java is present but "file:/C:/Users/pupeno/Documents/Dashman/code/dashmanserver/target/classes/" if it's not.

I need findResource("") to return "file:/C:/Users/pupeno/Documents/Dashman/code/dashmanserver/target/classes/" so that spring-orm can work.

findResource("") looks like this:

public URL findResource(final String name) {     /*      * The same restriction to finding classes applies to resources      */     URL url = AccessController.doPrivileged(         new PrivilegedAction<>() {             public URL run() {                 return ucp.findResource(name, true);             }         }, acc);      return url != null ? URLClassPath.checkURL(url) : null; } 

So I can see there's some access going on that's ok without using the module system but it's prevented by Java's module system when a module-infe.java is present. My problem is that I don't see how to make it work, what should export or open for it to work?

The way Spring Boot is causing the call of that method is through RestartClassLoader, a subclass of URLClassLoader, specifically, line 124 which calls super.findResource(name) in:

@Override public URL findResource(String name) {     final ClassLoaderFile file = this.updatedFiles.getFile(name);     if (file == null) {         return super.findResource(name);     }     if (file.getKind() == Kind.DELETED) {         return null;     }     return AccessController             .doPrivileged((PrivilegedAction<URL>) () -> createFileUrl(name, file)); } 

The specific RestartClassLoader instance being used is a member of ClassPathResource and it's defined this way:

this.classLoader = (classLoader != null ? classLoader : ClassUtils.getDefaultClassLoader()); 

in the constructor, line 85.

Lastly, getDefaultClassLoader() looks like this:

/**  * Return the default ClassLoader to use: typically the thread context  * ClassLoader, if available; the ClassLoader that loaded the ClassUtils  * class will be used as fallback.  * <p>Call this method if you intend to use the thread context ClassLoader  * in a scenario where you clearly prefer a non-null ClassLoader reference:  * for example, for class path resource loading (but not necessarily for  * {@code Class.forName}, which accepts a {@code null} ClassLoader  * reference as well).  * @return the default ClassLoader (only {@code null} if even the system  * ClassLoader isn't accessible)  * @see Thread#getContextClassLoader()  * @see ClassLoader#getSystemClassLoader()  */ @Nullable public static ClassLoader getDefaultClassLoader() {     ClassLoader cl = null;     try {         cl = Thread.currentThread().getContextClassLoader();     }     catch (Throwable ex) {         // Cannot access thread context ClassLoader - falling back...     }     if (cl == null) {         // No thread context class loader -> use class loader of this class.         cl = ClassUtils.class.getClassLoader();         if (cl == null) {             // getClassLoader() returning null indicates the bootstrap ClassLoader             try {                 cl = ClassLoader.getSystemClassLoader();             }             catch (Throwable ex) {                 // Cannot access system ClassLoader - oh well, maybe the caller can live with null...             }         }     }     return cl; } 

My module-info.java contains:

module tech.flexpoint.dashman {     exports tech.flexpoint.dashman to com.fasterxml.jackson.databind;     exports tech.flexpoint.dashman.controllers.configurator to javafx.fxml;      opens tech.flexpoint.dashman to javafx.graphics, jna;     opens tech.flexpoint.dashman.controllers.common to javafx.fxml;     opens tech.flexpoint.dashman.controllers.configurator to javafx.fxml;     opens tech.flexpoint.dashman.models to org.hibernate.validator, tech.flexpoint.dashmancommon, javafx.base;      opens common;     opens configurator;     opens displayer;     opens winscreensaver;      requires appdirs;     requires org.bouncycastle.provider;     requires com.fasterxml.jackson.core;     requires com.fasterxml.jackson.databind;     requires com.fasterxml.jackson.datatype.jdk8;     requires io.sentry;     requires jackson.annotations;     requires java.desktop;     requires java.sql;     requires java.validation;     requires javafx.controls;     requires javafx.fxml;     requires javafx.graphics;     requires javafx.media;     requires javafx.web;     requires jna;     requires jna.platform;     requires org.apache.commons.lang3;     requires org.kordamp.ikonli.javafx;     requires org.kordamp.ikonli.fontawesome5;     requires spring.core;     requires spring.retry;     requires spring.web;     requires tech.flexpoint.dashmancommon; } 

 


I don't see any dependency related to javax.transaction in your module-info.java config. May be it's hidden somewhere in your submodule?

One more thing worth to check is the java.transaction is not resolved by default

You might want to include the javax.transaction module instead:

requires javax.transaction.api; 

Comment

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen: