Here you can find the the following about automatic modules:
The module system also scans META-INF/services and makes the automatic module provide the services named therein. An automatic module is assumed allowed to use all services.
However, I have the following situation. I want to use log4j2 with slf4j in JPMS. In order to do it log4j-slf4j-impl-2.11.1.jar
must provide JPMS service to slf4j-api-1.8.0-beta2.jar
. The developers of log4j made log4j-slf4j-impl-2.11.1.jar
as automatic module and provided service via META-INF/services. However, it doesn't work, at it gives the following:
Exception in thread "main" java.lang.NoClassDefFoundError: org/apache/logging/log4j/Logger
at [email protected]/org.apache.logging.slf4j.SLF4JServiceProvider.initialize(SLF4JServiceProvider.java:53)
at org.slf4j/org.slf4j.LoggerFactory.bind(LoggerFactory.java:153)
at org.slf4j/org.slf4j.LoggerFactory.performInitialization(LoggerFactory.java:141)
at org.slf4j/org.slf4j.LoggerFactory.getProvider(LoggerFactory.java:419)
at org.slf4j/org.slf4j.LoggerFactory.getILoggerFactory(LoggerFactory.java:405)
at org.slf4j/org.slf4j.LoggerFactory.getLogger(LoggerFactory.java:354)
at org.slf4j/org.slf4j.LoggerFactory.getLogger(LoggerFactory.java:380)
at Log4j2Slf4jJdk11/com.temp.NewMain.<clinit>(NewMain.java:12)
Caused by: java.lang.ClassNotFoundException: org.apache.logging.log4j.Logger
at java.base/jdk.internal.loader.BuiltinClassLoader.loadClass(BuiltinClassLoader.java:582)
at java.base/jdk.internal.loader.ClassLoaders$AppClassLoader.loadClass(ClassLoaders.java:178)
at java.base/java.lang.ClassLoader.loadClass(ClassLoader.java:521)
... 8 more
I decided to add module-info to log4j-slf4j-impl-2.11.1.jar
and export service in JPMS manner via provides ... with..
. And the problem was solved - I don't get any NoClassDefFoundError
. This is link to the issue.
So my questions:
EDIT There are total 5 modules:
slf4j-api-1.8.0-beta2.jar // name: org.slf4j
log4j-slf4j18-impl-2.11.1.jar // name: org.apache.logging.log4j.slf4j
log4j-core-2.11.1.jar // name: org.apache.logging.log4j.core
log4j-api-2.11.1.jar // name: org.apache.logging.log4j
log4j2-slf4j-jdk11-1.0-SNAPSHOT.jar // name: Log4j2Slf4jJdk11
VARIANT 1 If I run --show-module-resolution when log4j-slf4j18-impl-2.11.1.jar
has META-INF/services
I get the following piece of output (I replaced full path with ...
):
...
root Log4j2Slf4jJdk11 file:.../log4j2-slf4j-jdk11-1.0-SNAPSHOT.jar
Log4j2Slf4jJdk11 requires org.slf4j file:.../slf4j-api-1.8.0-beta2.jar
jdk.compiler binds org.apache.logging.log4j.core file:.../log4j-core-2.11.1.jar automatic
org.slf4j binds org.apache.logging.log4j.slf4j file:.../log4j-slf4j18-impl-2.11.1.jar automatic
VARIANT 2 If I run --show-module-resolution when log4j-slf4j18-impl-2.11.1.jar
has module-info
I get the following piece of output:
...
root Log4j2Slf4jJdk11 file:.../log4j2-slf4j-jdk11-1.0-SNAPSHOT.jar
Log4j2Slf4jJdk11 requires org.slf4j file:.../slf4j-api-1.8.0-beta2.jar
jdk.compiler binds org.apache.logging.log4j.core file:.../log4j-core-2.11.1.jar automatic
org.slf4j binds org.apache.logging.log4j.slf4j file:.../log4j-slf4j18-impl-2.11.1.jar
org.apache.logging.log4j.slf4j requires org.slf4j file:.../slf4j-api-1.8.0-beta2.jar
org.apache.logging.log4j.slf4j requires org.apache.logging.log4j.core file:.../log4j-core-2.11.1.jar automatic
org.apache.logging.log4j.slf4j requires org.apache.logging.log4j file:.../log4j-api-2.11.1.jar
org.apache.logging.log4j binds org.apache.logging.log4j.core file:.../log4j-core-2.11.1.jar automatic
In VARIANT 1 the service from org.apache.logging.log4j.slf4j
can not load class (org.apache.logging.log4j.Logger
) from org.apache.logging.log4j.core
. In VARIANT 2 the service from org.apache.logging.log4j.slf4j
loads all classes from org.apache.logging.log4j.core
and everything is ok. We see in the ouput of VARIANT 2 that there is a line
org.apache.logging.log4j.slf4j requires org.apache.logging.log4j.core
and there is no such line in VARIANT 1. Is the problem in this? But if two modules are automatic can't they be resolved automatically?
SLF4J 1.8 requires an implementation of org.slf4j.spi.SLF4JServiceProvider as an exposed service. It is finding that in the log4j-slf4j18-impl jar. However, the Log4j SLF4J bridge requires the Log4J API (module org.apache.logging.log4j). Even though that is an explicit Java module, because it is only referenced from an automatic module it is not being loaded, resulting in the ClassNotFoundException.
The simple solution to this is to include --addmodules=org.apache.logging.log4j on the command line when starting the application.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With