Does JPMS support automatic module services from META-INF/services?
Here you can find the following information 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 and slf4j in JPMS. In order to do this, log4j-slf4j-impl-2.11.1.jar JPMS services must be provided to slf4j-api-1.8.0-beta2.jar.
The developers of log4j made
log4j-slf4j-impl-2.11.1.jar
as an automatic module and served it through META-INF/services. However, it doesn’t work, 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 the module information to log4j-slf4j-impl-2.11.1.jar
and via provide... with..
Export services in JPMS. This is
- Does JPMS support services for services/automodules in META-INF?
- If so, then how to explain this behavior?
Edit
There are 5 modules in total:
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 AT LOG4J-SLF4J18-IMPL-2.11.1.jar |
WITH META-INF/SERVICES
I GET THE FOLLOWING OUTPUT (I REPLACED THE 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 at log4j-slf4j18-impl-2.11.1.jar
I get module-info
| I get the following 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, services from org.apache.logging.log4j.slf4j
cannot load classes from org.apache.logging.log4j.Logger (org.apache.logging.log4j.core
).
, the service from org.apache.logging.log4j.slf4j loads all classes from
org.apache.logging.log4j.core
and everything is fine.
WE SEE A LINE IN THE OUTPUT OF VARIANT 2
org.apache.logging.log4j.slf4j requires org.apache.logging.log4j.core
AND THERE IS NO SUCH LINE IN VARIANT 1, IS THIS THE PROBLEM? But if both modules are automatic, can’t they be parsed automatically?
Solution
SLF4J 1.8 requires the implementation of org.slf4j.spi.SLF4JServiceProvider to be exposed as a service. It is found in log4j-slf4j18-impl jar. However, the Log4j SLF4J bridge requires the Log4J API (module org.apache.logging.log4j). Although this is an explicit Java module, it is not loaded because it is referenced only from the automatic module, resulting in a ClassNotFoundException.
A simple solution to this is to include –addmodules=org.apache.logging.log4j on the command line when starting the application.