[
https://issues.apache.org/jira/browse/LOGGING-192?page=com.atlassian.jira.plugin.system.issuetabpanels:comment-tabpanel&focusedCommentId=17873545#comment-17873545
]
Piotr Karwasz commented on LOGGING-192:
---------------------------------------
[~vampire],
Let's call the two classloaders _custom_ and _web_ for simplicity. What is
happening is:
# The {{LogFactory}} class from _custom_ looks for Log4j API.
# Log4j API is available in _web_.
# The {{LogFactory}} tries to instantiate {{Log4jApiLogFactory}} from _web_
and fails (no such class).
# {{LogFactory.newFactory}} falls back to instantiating {{Log4jApiLogFactory}}
from _custom_ and fails (missing Log4j API).
The fallback breaks the selection logic and prevents {{LogFactory}} from trying
alternative factory implementations.
Before fixing it, we should probably decide what the selection logic should be:
we have 3 {{LogFactory}} (Log4j API, SLF4J and Legacy) implementations that we
can try loading from 2 different classloaders (TCCL and the one that loaded
{{LogFactory}}). What should be the correct selection order:
# Should try to load all 3 implementation from the TCCL and only if it fails
try the other classloader?
# Should we first try Log4j API from both classloaders, then SLF4J from both
classloaders and finally the Legacy implementation?
The first option makes more sense, since it allows a single {{commons-logging}}
copy to drive different logging backends for different web applications (that
is assuming each web app bundles {{commons-logging-adapters}} instead of the
whole library).
> NoClassDefFoundError: org/apache/logging/log4j/spi/LoggerAdapter when using
> custom classloader
> ----------------------------------------------------------------------------------------------
>
> Key: LOGGING-192
> URL: https://issues.apache.org/jira/browse/LOGGING-192
> Project: Commons Logging
> Issue Type: Bug
> Affects Versions: 1.3.0, 1.3.1, 1.3.2
> Environment: This behavior was observed while running Adopt Open JDK
> 11 and the latest version of Tomcat 9. The behavior can be reproduced
> outside of tomcat (see attached reproduction case).
> Reporter: Dave Dority
> Priority: Major
> Attachments: commons-logging-classloading-issue.zip
>
>
> If you have:
> * A web application running in Tomcat which contains commons-logging:1.2
> * That web application contains a custom classloader for loading a
> seperately distributed software component (whose dependencies will conflict
> with the dependencies of the web application).
> * The software component uses commons-logging:1.3.2
> When the web application attempts use software component, the code
> [here|https://github.com/apache/commons-logging/blob/rel/commons-logging-1.3.2/src/main/java/org/apache/commons/logging/LogFactory.java#L918-L938]
> looks for the presence of different logging implementation classes on the
> thread context classloader's (TCCL) classpath to select an optimal
> implementation. It seems like what is happening is that the LogFactory class
> looking for implementation class on the TCCL's classpath and the trying to
> load the selected factory from the web application's custom classloader (the
> loader for the instance of LogFactory that is running). This is the result:
> {code:java}
> Exception in thread "main" java.lang.NoClassDefFoundError:
> org/apache/logging/log4j/spi/LoggerAdapter
> at java.base/java.lang.Class.forName0(Native Method)
> at java.base/java.lang.Class.forName(Class.java:315)
> at
> org.apache.commons.logging.LogFactory.createFactory(LogFactory.java:419)
> at
> org.apache.commons.logging.LogFactory.lambda$newFactory$3(LogFactory.java:1431)
> at java.base/java.security.AccessController.doPrivileged(Native
> Method)
> at
> org.apache.commons.logging.LogFactory.newFactory(LogFactory.java:1431)
> at
> org.apache.commons.logging.LogFactory.getFactory(LogFactory.java:928)
> at org.apache.commons.logging.LogFactory.getLog(LogFactory.java:987)
> at
> org.component.ClassLoadedComponent.<clinit>(ClassLoadedComponent.java:7)
> at
> java.base/jdk.internal.reflect.NativeConstructorAccessorImpl.newInstance0(Native
> Method)
> at
> java.base/jdk.internal.reflect.NativeConstructorAccessorImpl.newInstance(NativeConstructorAccessorImpl.java:62)
> at
> java.base/jdk.internal.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:45)
> at
> java.base/java.lang.reflect.Constructor.newInstance(Constructor.java:490)
> at java.base/java.lang.Class.newInstance(Class.java:584){code}
> This occurs when the web application has commons-logging:1.2 and the software
> component has commons-logging:1.3.x. This does not occur when both are using
> version 1.2.
> Unfortunately, changing the web application's version of commons-logging is
> outside is not something I can influence.
> An isolated reproduction case is attached. It requires Java 11. To run it:
> * Unzip it to a directory.
> * Run
> {code:java}
> ./gradlew reproduceIssue{code}
>
--
This message was sent by Atlassian Jira
(v8.20.10#820010)