You are viewing a plain text version of this content. The canonical link for it is here.
Posted to user@commons.apache.org by Keith Naas <kn...@netjets.com> on 2005/11/09 15:17:25 UTC

Re: logging: more then one version of 'org.apache.commons.logging.Log'

I apologize for the length of this message.  We are encountering the
exact same problem as had been mentioned by Kurt.  However, it is
occuring within a WebLogic 8.1 SP2 container.  We know exactly what
tickles the and we have identified the code in Commons-Logging that may
be the cause.  FYI: WL uses a parent-first classloader.

Issue:

We have an enterprise application that contains many different web app
and ejb modules.  The ear contains a single commons-logging library and
a single log4j library.  It makes use of another enterprise application
that also contains many different web app and ejb modules.  We are using
distributed transactions to tie together transactions between two
different enterprise applications.  We are using Hibernate3.0.5 as our
persistence layer.  We are using commons-logging 1.0.4 and log4j 1.2.8.

A method in EAR1.EJBModuleA with a "Required" TxAttribute calls a method
in EAR2.EJBModuleB with a "Required" TxAttribute.

Just before the transaction is completed by the container, we receive a

org.apache.commons.logging.LogConfigurationException:
org.apache.commons.logging.LogConfigurationException: Invalid class
loader hierarchy.  You have more than one version of
'org.apache.commons.logging.Log' visible, which is not allowed. (Caused
by org.apache.commons.logging.LogConfigurationException: Invalid class
loader hierarchy.  You have more than one version of
'org.apache.commons.logging.Log' visible, which is not allowed.)

Bizarrely If we change the TxAttribute in EAR1.EJBModuleA to "Supports",
the issue does not occur.

We did some investigation into the hibernate & commons-logging code and
we found that the LogFactoryImpl in commons-logging is loading the Log
interface using a different classloader than the Logger implementation.
This is easy to see by adding the following code to the top of the
getLogConstructor() method.

            final ClassLoader cl1 = this.getClass().getClassLoader();
            final ClassLoader cl2 = this.getContextClassLoader();
            String logInterfaceName = LOG_INTERFACE;
            java.util.Enumeration enumeration =
cl1.getResources(logInterfaceName.replace('.', '/') + ".class");
            while (enumeration.hasMoreElements())
            {
                System.out.println(logInterfaceName + ":" +
enumeration.nextElement());
            }
            enumeration = cl2.getResources(logClassName.replace('.',
'/') + ".class");
            while (enumeration.hasMoreElements())
            {
                System.out.println(logClassName + ":" +
enumeration.nextElement());
            }  

The printout looks something like this:

org.apache.commons.logging.Log:zip:EAR2......logging/Log.class
org.apache.commons.logging.impl.Log4JLogger:zip:EAR1.......impl/Log4JLog
ger.class

Classes loaded by two different ClassLoaders are not considered
identical.  Since EAR1 and EAR2 have two different ClassLoaders, there
is no isa relationship between the EAR1 Log4JLogger implementation and
the EAR2 Log interface.

Question:

The LogFactoryImpl loads the interface on the ClassLoader from
LogFactoryImpl.getClass().getClassLoader().  However, it loads the
implementation on the Thread.currentThread().getClassLoader().  Why does
it use two different ClassLoaders instead of loading both the interface
& implementation on the same ClassLoader? 

Thanks for any help,
Keith Naas
Sr. Developer
NetJets, Inc.
898-3044 x4105



**********
This message contains information which may be confidential and privileged. Unless you are the addressee (or authorized to receive for the addressee), you may not use, copy or disclose to anyone the message or any information contained in the message. If you have received the message in error,  please advise the sender by reply e-mail and delete the message.

Re: logging: more then one version of 'org.apache.commons.logging.Log'

Posted by Simon Kitching <sk...@apache.org>.
On Wed, 2005-11-09 at 09:17 -0500, Keith Naas wrote:
> Question:
> 
> The LogFactoryImpl loads the interface on the ClassLoader from
> LogFactoryImpl.getClass().getClassLoader().  However, it loads the
> implementation on the Thread.currentThread().getClassLoader().  Why does
> it use two different ClassLoaders instead of loading both the interface
> & implementation on the same ClassLoader? 

It's quite deliberate. This is so the commons-logging.jar can be in some
"shared" classpath, as it is in tomcat for example, but the adapter
class and the concrete library can be bundled in a webapp.

If webapp X wants to use Avalon logging, it should not be necessary to
put the avalon.jar file in the server classpath; that lib should go in
WEB-INF/lib of the webapp. However in this case, the logging "adapter"
class for commons logging ("the implementation" you refer to above)
needs to be loaded via the same classloader as the underlying logging
lib in order to be able to access its classes.

Now if JCL is deployed in an ancestor classloader, then the JCL
LogFactory class is expecting to cast the adapter object to an
implementation of the Log interface - one loaded via the same
classloader as the LogFactory.

And this leads unfortunately into a very tangled web of classloader
issues.

Having the logging libs in the webapps also ensures that logging config
is separate for each webapp, by forcing separate class instances to be
used for each webapp; many libs will fail in very interesting ways when
deployed in a container classpath and used by multiple webapps
simultaneously. And of course it allows different versions of the
logging lib to be used concurrently.

May I suggest you try the current HEAD JCL version? Several of us put
quite a lot of effort into trying to resolve as many issues as possible
within the constraints of the existing design. And there has been an 
effort to avoid hard failures (throwing LogException) where there is any
kind of halfway acceptable fallback that could be made. The end solution
isn't perfect, but hopefully it's better than JCL 1.0.4. However there
are so many different containers and different uses of them out there
that only people like you can really tell us whether the changes are for
the better.

Regards,

Simon



---------------------------------------------------------------------
To unsubscribe, e-mail: commons-user-unsubscribe@jakarta.apache.org
For additional commands, e-mail: commons-user-help@jakarta.apache.org


Re: logging: more then one version of 'org.apache.commons.logging.Log'

Posted by robert burrell donkin <ro...@blueyonder.co.uk>.
On Wed, 2005-11-09 at 09:17 -0500, Keith Naas wrote:

<snip>

> Question:
> 
> The LogFactoryImpl loads the interface on the ClassLoader from
> LogFactoryImpl.getClass().getClassLoader().  However, it loads the
> implementation on the Thread.currentThread().getClassLoader().  Why does
> it use two different ClassLoaders instead of loading both the interface
> & implementation on the same ClassLoader? 

if it's education you're looking for...

for a short answer, it's because years ago this was considered the
correct way to behave according to the specifications released at that
time.

for a long answer, consult
http://jakarta.apache.org/commons/logging/tech.html and search the mail
archives.


if it's a fix you're looking for...

the trunk contains code that copes more gracefully with some common use
cases and is backwards compatible. there's a good chance that your
problem will go away if you upgrade. 

- robert


---------------------------------------------------------------------
To unsubscribe, e-mail: commons-user-unsubscribe@jakarta.apache.org
For additional commands, e-mail: commons-user-help@jakarta.apache.org