users@glassfish.java.net

Same class gets loaded twice by different class loaders

From: <forums_at_java.net>
Date: Wed, 22 Jun 2011 09:47:35 -0500 (CDT)

Hi,

I'm having trouble with a jee5 ear deployed in glassfish 3.1, namely: a
certain spring security core class
(org.springframework.security.core.userdetails.User) gets loaded twice in
different moments by different classloaders. I've tried to build a
clusterizable spring security SessionRegistry as a stateful 3.0 ejb which
maintains a set of SessionInformation instances wrapped into JPA entities.
Here it follows my ear structure:

my.ear
│   aa_ejb-2.4.7.jar
│   backbone-2.4.7.war

├───lib
│       aa-2.4.7.jar
│       ...
│       spring-aop-3.0.5.RELEASE.jar
│       spring-asm-3.0.5.RELEASE.jar
│       spring-beans-3.0.5.RELEASE.jar
│       spring-context-3.0.5.RELEASE.jar
│       spring-context-support-3.0.5.RELEASE.jar
│       spring-core-3.0.5.RELEASE.jar
│       spring-expression-3.0.5.RELEASE.jar
│       spring-jdbc-3.0.5.RELEASE.jar
│       spring-jms-3.0.5.RELEASE.jar
│       spring-ldap-core-1.3.1.RELEASE.jar
│       spring-security-core-3.0.5.RELEASE.jar
│       spring-security-ldap-3.0.5.RELEASE.jar
│       spring-security-web-3.0.5.RELEASE.jar
│       spring-tx-3.0.5.RELEASE.jar
│       spring-web-3.0.5.RELEASE.jar
│       ...

└───META-INF
        application.xml

There is a single JPA entity packed into the lib/aa-2.4.7.jar, which stands
for the spring security SessionInformation to be maintained across the
cluster; apart from the zero-arg constructor it also posseses a more complex
one, the one being problematic:

    public JpaSessionInformation( SessionInformation pSinfo ) throws
Exception {
        UserDetails aux = ( UserDetails ) pSinfo.getPrincipal();
        principal = serialize( aux );
        username = aux.getUsername();
        sessionId = pSinfo.getSessionId();
        lastRequest = pSinfo.getLastRequest();
        expired = pSinfo.isExpired();
    }

This JPA entity is referenced from the aa_ejb ejb-jar. The latter is in turn
referenced by the backbone-2.4.7 war, which also takes care of everything
related to spring and spring-security initialization. It's possible to deploy
the ear with no error or warning. Upon trying to access the application, the
login form is processed. I won't get long, however:

*exception*

javax.ejb.EJBException
*note* The full stack traces of the exception and its root causes are
available in the GlassFish Server Open Source Edition 3.1 logs.

server.log says:

[#|2011-06-22T16:16:59.940+0200|WARNING|glassfish3.1|javax.enterprise.system.container.ejb.com.sun.ejb.containers|_ThreadID=95;_ThreadName=Thread-1;|A
system exception occurred during an invocation on EJB JpaSessionRegistryBean
method public void
com.ptb.clerk.aa.JpaSessionRegistryBean.registerNewSession(java.lang.String,java.lang.Object)
javax.ejb.EJBException
    at
com.sun.ejb.containers.BaseContainer.processSystemException(BaseContainer.java:5194)
    at
com.sun.ejb.containers.BaseContainer.completeNewTx(BaseContainer.java:5092)
    at
com.sun.ejb.containers.BaseContainer.postInvokeTx(BaseContainer.java:4894)
    at
com.sun.ejb.containers.BaseContainer.postInvoke(BaseContainer.java:2039)
    at
com.sun.ejb.containers.BaseContainer.postInvoke(BaseContainer.java:1990)
    at
com.sun.ejb.containers.EJBObjectInvocationHandler.invoke(EJBObjectInvocationHandler.java:213)
    at
com.sun.ejb.containers.EJBObjectInvocationHandlerDelegate.invoke(EJBObjectInvocationHandlerDelegate.java:79)
    at $Proxy196.registerNewSession(Unknown Source)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at
sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
    at
sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
    at java.lang.reflect.Method.invoke(Method.java:597)
    at
com.sun.corba.ee.impl.presentation.rmi.StubInvocationHandlerImpl.privateInvoke(StubInvocationHandlerImpl.java:241)
    at
com.sun.corba.ee.impl.presentation.rmi.StubInvocationHandlerImpl.invoke(StubInvocationHandlerImpl.java:152)
    at
com.sun.corba.ee.impl.presentation.rmi.codegen.CodegenStubBase.invoke(CodegenStubBase.java:227)
    at
com.ptb.clerk.aa.__JpaSessionRegistry_Remote_DynamicStub.registerNewSession(com/ptb/clerk/aa/__JpaSessionRegistry_Remote_DynamicStub.java)
    at
com.ptb.clerk.aa._JpaSessionRegistry_Wrapper.registerNewSession(com/ptb/clerk/aa/_JpaSessionRegistry_Wrapper.java)
    at
com.ptb.rt.web.JpaSessionRegistryWrapper.registerNewSession(JpaSessionRegistryWrapper.java:58)
    at
com.ptb.rt.web.ClerkSessionRegistry.registerNewSession(ClerkSessionRegistry.java:88)
    at
com.ptb.clerk.aa.ClerkConcurrentSessionControlStrategy.onAuthentication(ClerkConcurrentSessionControlStrategy.java:70)
    at
org.springframework.security.web.authentication.AbstractAuthenticationProcessingFilter.doFilter(AbstractAuthenticationProcessingFilter.java:204)
    at
com.ptb.clerk.aa.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:524)
    at
org.springframework.security.web.authentication.logout.LogoutFilter.doFilter(LogoutFilter.java:105)
...
Caused by: java.lang.RuntimeException: java.lang.ClassCastException:
org.springframework.security.core.userdetails.User cannot be cast to
org.springframework.security.core.userdetails.UserDetails
    at
com.ptb.clerk.aa.JpaSessionRegistryBean.registerNewSession(JpaSessionRegistryBean.java:64)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at
sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
    at
sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
    at java.lang.reflect.Method.invoke(Method.java:597)
    at
org.glassfish.ejb.security.application.EJBSecurityManager.runMethod(EJBSecurityManager.java:1052)
    at
org.glassfish.ejb.security.application.EJBSecurityManager.invoke(EJBSecurityManager.java:1124)
    at
com.sun.ejb.containers.BaseContainer.invokeBeanMethod(BaseContainer.java:5367)
...
Caused by: java.lang.ClassCastException:
org.springframework.security.core.userdetails.User cannot be cast to
org.springframework.security.core.userdetails.UserDetails
    at
com.ptb.clerk.aa.JpaSessionInformation.<init>(JpaSessionInformation.java:50)
    at
com.ptb.clerk.aa.JpaSessionRegistryBean.registerNewSession(JpaSessionRegistryBean.java:62)
    ... 76 more

This obviously makes no sense (User *does* implemente UserDetails) unless the
class the passed User instance belongs to has been loaded by a classloader
other than the one that loaded JpaSessionInformation. By debugging I get the
following:


The spring security User class "seen" by JpaSessionInformation has been
loaded by the EarLibClassLoader and is thus different from the User class
"seen" by the provided SessionInformation instance, which has been loaded by
WebAppClassLoader:


Any suggestion as to how to overcome this?

With kind regards

 

César Varona


--
[Message sent by forum member 'cesar.varona']
View Post: http://forums.java.net/node/814689




User.class_.getClassLoader.png
(image/png attachment: User.class_.getClassLoader.png)

sinfo.getPrincipal.getClass.getClassLoader.png
(image/png attachment: sinfo.getPrincipal.getClass.getClassLoader.png)