5 Replies Latest reply on Sep 27, 2010 10:32 AM by Ron Sigal

    Dynamic classloading issue

    v a Newbie

      I'm using Remoting 2.5.2.SP3 with JBoss 6.0.M3. Remote class loading works only for some common classes but not for my beans. I have a simple bean with remote interface. When I try accessing it by InitialContext.lookup the exception occurs:

       

      org.jboss.ejb3.proxy.impl.objectfactory.session.stateless.StatelessSessionProxyObjectFactory.getProxy(StatelessSessionProxyObjectFactory.java:79)
          at org.jboss.ejb3.proxy.impl.objectfactory.ProxyObjectFactory.getObjectInstance(ProxyObjectFactory.java:161)
          at javax.naming.spi.NamingManager.getObjectInstance(NamingManager.java:304)
          at org.jnp.interfaces.NamingContext.getObjectInstance(NamingContext.java:1483)
          at org.jnp.interfaces.NamingContext.getObjectInstanceWrapFailure(NamingContext.java:1500)
          ... 4 more
      Caused by: java.lang.ClassNotFoundException: *** my bean remote interface ***
          at org.jboss.remoting.serialization.ClassLoaderUtility.loadClass(ClassLoaderUtility.java:103)
          at org.jboss.remoting.loading.RemotingClassLoader.loadClass(RemotingClassLoader.java:93)
          at java.lang.Class.forName0(Native Method)
          at java.lang.Class.forName(Class.java:247)
          at org.jboss.remoting.loading.ObjectInputStreamWithClassLoader.resolveProxyClass(ObjectInputStreamWithClassLoader.java:253)
          at java.io.ObjectInputStream.readProxyDesc(ObjectInputStream.java:1531)
          at java.io.ObjectInputStream.readClassDesc(ObjectInputStream.java:1493)
          at java.io.ObjectInputStream.readOrdinaryObject(ObjectInputStream.java:1732)
          at java.io.ObjectInputStream.readObject0(ObjectInputStream.java:1329)
          at java.io.ObjectInputStream.readObject(ObjectInputStream.java:351)
          at org.jboss.aop.joinpoint.InvocationResponse.readExternal(InvocationResponse.java:119)
          at java.io.ObjectInputStream.readExternalData(ObjectInputStream.java:1792)
          at java.io.ObjectInputStream.readOrdinaryObject(ObjectInputStream.java:1751)
          at java.io.ObjectInputStream.readObject0(ObjectInputStream.java:1329)
          at java.io.ObjectInputStream.defaultReadFields(ObjectInputStream.java:1947)
          at java.io.ObjectInputStream.readSerialData(ObjectInputStream.java:1871)
          at java.io.ObjectInputStream.readOrdinaryObject(ObjectInputStream.java:1753)
          at java.io.ObjectInputStream.readObject0(ObjectInputStream.java:1329)
          at java.io.ObjectInputStream.readObject(ObjectInputStream.java:351)
          at org.jboss.remoting.serialization.impl.java.JavaSerializationManager.receiveObjectVersion2_2(JavaSerializationManager.java:238)
          at org.jboss.remoting.serialization.impl.java.JavaSerializationManager.receiveObject(JavaSerializationManager.java:138)
          at org.jboss.remoting.marshal.serializable.SerializableUnMarshaller.read(SerializableUnMarshaller.java:123)
          at org.jboss.remoting.transport.socket.MicroSocketClientInvoker.versionedRead(MicroSocketClientInvoker.java:1299)
          at org.jboss.remoting.transport.socket.MicroSocketClientInvoker.transport(MicroSocketClientInvoker.java:907)
          at org.jboss.remoting.MicroRemoteClientInvoker.invoke(MicroRemoteClientInvoker.java:167)
          at org.jboss.remoting.Client.invoke(Client.java:1927)
          at org.jboss.remoting.Client.invoke(Client.java:770)
          at org.jboss.aspects.remoting.InvokeRemoteInterceptor.invoke(InvokeRemoteInterceptor.java:60)
          at org.jboss.aop.joinpoint.MethodInvocation.invokeNext(MethodInvocation.java:102)
          at org.jboss.ejb3.proxy.impl.remoting.IsLocalProxyFactoryInterceptor.invoke(IsLocalProxyFactoryInterceptor.java:104)
          at org.jboss.aop.joinpoint.MethodInvocation.invokeNext(MethodInvocation.java:102)
          at org.jboss.aspects.remoting.PojiProxy.invoke(PojiProxy.java:62)
          at $Proxy0.createProxyBusiness(Unknown Source)
          at org.jboss.ejb3.proxy.impl.objectfactory.session.SessionProxyObjectFactory.createProxy(SessionProxyObjectFactory.java:129)
          at org.jboss.ejb3.proxy.impl.objectfactory.session.stateless.StatelessSessionProxyObjectFactory.getProxy(StatelessSessionProxyObjectFactory.java:79)
          at org.jboss.ejb3.proxy.impl.objectfactory.ProxyObjectFactory.getObjectInstance(ProxyObjectFactory.java:161)
          at javax.naming.spi.NamingManager.getObjectInstance(NamingManager.java:304)
          at org.jnp.interfaces.NamingContext.getObjectInstance(NamingContext.java:1483)
          at org.jnp.interfaces.NamingContext.getObjectInstanceWrapFailure(NamingContext.java:1500)
          at org.jnp.interfaces.NamingContext.lookup(NamingContext.java:824)
          at org.jnp.interfaces.NamingContext.lookup(NamingContext.java:688)
          at javax.naming.InitialContext.lookup(InitialContext.java:392)
      ...

       

      In the server log there are some odd things:

       

      13:36:31,971 DEBUG [org.jboss.remoting.marshal.MarshallerLoaderHandler] MarshallerLoaderHandler: loading class: *** my bean remote interface ***
      13:36:31,978 TRACE [org.jboss.remoting.loading.ClassUtil] trying to load class: *** my bean remote interface *** from path: *** my bean remote interface class ***
      13:36:31,980 TRACE [org.jboss.remoting.loading.ClassByteClassLoader] getResourceAsStream =>*** my bean remote interface *** = null
      13:36:31,982 DEBUG [org.jboss.remoting.marshal.MarshallerLoaderHandler] unable to load class *** my bean remote interface ***
      13:36:31,983 DEBUG [org.jboss.remoting.marshal.MarshallerLoaderHandler] MarshallerLoaderHandler: returning class: *** my bean remote interface ***: ClassBytes [class=*** my bean remote interface ***,value=null]
      13:36:31,983 TRACE [org.jboss.remoting.transport.socket.ServerThread] SocketServerInvoker[ip:4873].invoke() returned ClassBytes [class=*** my bean remote interface ***,value=null]
      13:36:31,983 TRACE [org.jboss.remoting.transport.socket.ServerThread] creating response instance
      

       

      I also checked that the remote class loading works at all (I removed some client jars from my client, the missing classes were successfully loaded):

       

      13:43:36,402 DEBUG [org.jboss.remoting.marshal.MarshallerLoaderHandler] MarshallerLoaderHandler: loading class: org.jboss.ejb3.security.client.SecurityClientInterceptor
      13:43:36,402 TRACE [org.jboss.remoting.loading.ClassUtil] trying to load class: org.jboss.ejb3.security.client.SecurityClientInterceptor from path: org/jboss/ejb3/security/client/SecurityClientInterceptor.class
      13:43:36,403 TRACE [org.jboss.remoting.loading.ClassByteClassLoader] getResourceAsStream =>org.jboss.ejb3.security.client.SecurityClientInterceptor = null
      13:43:36,403 TRACE [org.jboss.remoting.loading.ClassUtil] looking for classes at: ClassByteClassLoader [{}]
      13:43:36,403 TRACE [org.jboss.remoting.loading.ClassUtil] found class at classloader: ClassByteClassLoader [{}]
      13:43:36,403 DEBUG [org.jboss.remoting.marshal.MarshallerLoaderHandler] MarshallerLoaderHandler: returning class: org.jboss.ejb3.security.client.SecurityClientInterceptor: ClassBytes [class=org.jboss.ejb3.security.client.SecurityClientInterceptor,value=[B@90f268]
      13:43:36,403 TRACE [org.jboss.remoting.transport.socket.ServerThread] SocketServerInvoker[ip:4873].invoke() returned ClassBytes [class=org.jboss.ejb3.security.client.SecurityClientInterceptor,value=[B@90f268]
      13:43:36,404 TRACE [org.jboss.remoting.transport.socket.ServerThread] creating response instance
      

       

      Then I tried to get rid of an isolated class loader for my EAR but the following exception occurs in that case:

      ...
      Caused by: java.lang.RuntimeException: Can not find interface declared by Proxy in our CL + sun.misc.Launcher$AppClassLoader@1a16869
          at org.jboss.ejb3.proxy.impl.objectfactory.ProxyObjectFactory.redefineProxyInTcl(ProxyObjectFactory.java:410)
          at org.jboss.ejb3.proxy.impl.objectfactory.session.SessionProxyObjectFactory.createProxy(SessionProxyObjectFactory.java:134)
          at org.jboss.ejb3.proxy.impl.objectfactory.session.stateless.StatelessSessionProxyObjectFactory.getProxy(StatelessSessionProxyObjectFactory.java:79)
          at org.jboss.ejb3.proxy.impl.objectfactory.ProxyObjectFactory.getObjectInstance(ProxyObjectFactory.java:161)
          at javax.naming.spi.NamingManager.getObjectInstance(NamingManager.java:304)
          at org.jnp.interfaces.NamingContext.getObjectInstance(NamingContext.java:1483)
          at org.jnp.interfaces.NamingContext.getObjectInstanceWrapFailure(NamingContext.java:1500)
          ... 4 more
      Caused by: java.lang.ClassNotFoundException: *** my bean remote interface ***
      ...

       

      Any suggestions?

        • 1. Re: Dynamic classloading issue
          Ron Sigal Master

          Based on your examples, it looks like JBoss files are found but your own files are not.  Is that true?

           

          Have you read Section 9.  "Remote classloading facility" of the Remoting Guide (http://docs.jboss.org/jbossremoting/2.5.2.SP3/html/) carefully?

           

          You might need to configure the Remoting EJB3 connector with the classloader associated with your EAR.

           

          -Ron

          • 2. Re: Dynamic classloading issue
            v a Newbie

            Yes, I have read it. I have this line in jboss-app.xml:

             

            <loader-repository>org.test:loader=jboss-test.ear</loader-repository>

             

            When I added just this part from the documentation to my deploy/ejb3-connectors-jboss-beans.xml:

             <bean name="remoting:serverConfiguration"
                     class="org.jboss.remoting.ServerConfiguration">
                  <property name="invokerLocatorParameters">
                     <map keyClass="java.lang.String" valueClass="java.lang.String">
                        <entry>
                           <key>loaderport</key>
                           <value>5544</value>
                        </entry>
                     </map>
                  </property>
                  <property name="serverParameters">
                     <map keyClass="java.lang.String" valueClass="java.lang.Object">
                        <entry>
                           <key>remoteClassLoaders</key>
                           <value>
                             <list elementClass="java.lang.ClassLoader">
                               <value>jboss-test.ear:classloader</value>
                             </list>
                           </value>
                        </entry>
                     </map>
                  </property>
                  ...
               </bean>

             

             

            nothing happened. The only lines in log were:

             

            13:28:59,808 TRACE [org.jboss.remoting.serialization.impl.java.JavaSerializationManager] Creating ObjectOutputStream
            13:28:59,809 TRACE [org.jboss.remoting.serialization.impl.java.JavaSerializationManager] Creating ObjectInputStreamWithClassLoader
            13:28:59,809 TRACE [org.jboss.remoting.transport.socket.SocketServerInvoker] passed through ServerSocketRefresh.release()
            13:28:59,825 TRACE [org.jboss.remoting.transport.socket.ClientSocketWrapper] reset timeout: 60000
            13:28:59,884 TRACE [org.jboss.remoting.transport.socket.ServerSocketWrapper] ServerSocketWrapper[Socket[addr=/ip,port=40042,localport=3873].bc9678] wrote CLOSING
            13:28:59,885 TRACE [org.jboss.remoting.transport.socket.SocketWrapper] ServerSocketWrapper[Socket[addr=/ip,port=40042,localport=3873].bc9678] closing socket
            13:28:59,885 TRACE [org.jboss.remoting.transport.socket.SocketWrapper] ServerSocketWrapper[Socket[addr=/ip,port=40042,localport=3873].bc9678] closed socket
            13:28:59,885 DEBUG [org.jboss.remoting.transport.socket.ServerThread] WorkerThread#20[10.0.4.75:40042] closed socketWrapper: ServerSocketWrapper[Socket[addr=/ip,port=40042,localport=3873].bc9678]

             

            I also needed to add this (documentation has nothing about it):

            <bean name="org.jboss.ejb3.RemotingConnector"
                class="org.jboss.remoting.transport.Connector">

                <property name="invokerLocator">

                  <value-factory bean="ServiceBindingManager"
                    method="getStringBinding">

                    ...
                    <parameter>socket://${jboss.bind.address}:${port}?loaderport=4873</parameter>

                    ...
                  </value-factory>

                </property>
                <property name="serverConfiguration">
                  <inject bean="ServerConfiguration" />
                </property>
              </bean>

            Then log becames:

            13:37:22,331 DEBUG [org.jboss.remoting.marshal.MarshallerLoaderHandler] MarshallerLoaderHandler received invocation with param of load_class and data type of null
            13:37:22,331 DEBUG [org.jboss.remoting.marshal.MarshallerLoaderHandler] MarshallerLoaderHandler: loading class: *my interface*
            13:37:22,331 TRACE [org.jboss.remoting.loading.ClassUtil] trying to load class: *my interface* from path: *my interface class file*
            13:37:22,332 TRACE [org.jboss.remoting.loading.ClassByteClassLoader] getResourceAsStream =>*my interface* = null
            13:37:22,332 DEBUG [org.jboss.remoting.marshal.MarshallerLoaderHandler] unable to load class *my interface*
            13:37:22,332 DEBUG [org.jboss.remoting.marshal.MarshallerLoaderHandler] MarshallerLoaderHandler: returning class: *my interface*: ClassBytes [class=*my interface*,value=null]

            Still confused why there is no any example of remote class loading in the documentation.

            • 3. Re: Dynamic classloading issue
              v a Newbie

              I have some progress. I removed <property name="invokerLocator"...> from <bean name="org.jboss.ejb3.RemotingConnector"...>. Now it seems that almost all works. I have this exception:

               

              15:11:40,092 TRACE [org.jboss.remoting.loading.ClassUtil] trying to load class: *my interface* from path: *my interface class file*
              15:11:40,094 TRACE [org.jboss.remoting.loading.ClassByteClassLoader] getResourceAsStream =>*my interface* = null
              15:11:40,095 TRACE [org.jboss.remoting.transport.socket.ServerThread] SocketServerInvoker[10.0.4.75:5544].invoke() call failed: java.lang.ClassCastException: java.lang.String cannot be cast to java.lang.ClassLoader
                      at org.jboss.remoting.marshal.MarshallerLoaderHandler.loadClassBytes(MarshallerLoaderHandler.java:177) [:6.0.0.20100721-M4]
                      at org.jboss.remoting.marshal.MarshallerLoaderHandler.invoke(MarshallerLoaderHandler.java:119) [:6.0.0.20100721-M4]
                      at org.jboss.remoting.ServerInvoker.invoke(ServerInvoker.java:898) [:6.0.0.20100721-M4]
                      at org.jboss.remoting.transport.socket.ServerThread.completeInvocation(ServerThread.java:791) [:6.0.0.20100721-M4]
                      at org.jboss.remoting.transport.socket.ServerThread.processInvocation(ServerThread.java:744) [:6.0.0.20100721-M4]
                      at org.jboss.remoting.transport.socket.ServerThread.dorun(ServerThread.java:548) [:6.0.0.20100721-M4]
                      at org.jboss.remoting.transport.socket.ServerThread.run(ServerThread.java:234) [:6.0.0.20100721-M4]

              I think the problem is in the remoteClassLoaders property. What should I use as values of the list? Now it looks like:

               

              <entry>
                <key>remoteClassLoaders</key>
                <value>
                  <list elementClass="java.lang.ClassLoader">
                    <value>jboss-test.ear:classloader</value>
                  </list>
                </value>
              </entry>

               

              Then I tried this:

               

              <classloader name="jboss-test-classloader" xmlns="urn:jboss:classloader:1.0" export-all="NON_EMPTY" import-all="true">
                  <root>*my jar*</root>
              </classloader>

               

              ...

              <entry>

                <key>remoteClassLoaders</key>

                <value>

                  <list elementClass="java.lang.ClassLoader">

                    <inject bean="jboss-test-classloader:0.0.0" />

                  </list>

                </value>

              </entry>

               

              Now the server side works well. I have a not-null classes on client, but I returned to the very beginning:

               

              Exception in thread "main" javax.naming.NamingException: Could not dereference object [Root exception is java.lang.RuntimeException: Can not find interface declared by Proxy in our CL + sun.misc.Launcher$AppClassLoader@1a16869]
                  at org.jnp.interfaces.NamingContext.getObjectInstanceWrapFailure(NamingContext.java:1508)
                  at org.jnp.interfaces.NamingContext.lookup(NamingContext.java:824)
                  at org.jnp.interfaces.NamingContext.lookup(NamingContext.java:688)
                  at javax.naming.InitialContext.lookup(InitialContext.java:392)
                  at org.test.jboss.client.Client.main(Client.java:22)
              Caused by: java.lang.RuntimeException: Can not find interface declared by Proxy in our CL + sun.misc.Launcher$AppClassLoader@1a16869
                  at org.jboss.ejb3.proxy.impl.objectfactory.ProxyObjectFactory.redefineProxyInTcl(ProxyObjectFactory.java:413)
                  at org.jboss.ejb3.proxy.impl.objectfactory.session.SessionProxyObjectFactory.createProxy(SessionProxyObjectFactory.java:134)
                  at org.jboss.ejb3.proxy.impl.objectfactory.session.stateless.StatelessSessionProxyObjectFactory.getProxy(StatelessSessionProxyObjectFactory.java:92)
                  at org.jboss.ejb3.proxy.impl.objectfactory.ProxyObjectFactory.getObjectInstance(ProxyObjectFactory.java:161)
                  at javax.naming.spi.NamingManager.getObjectInstance(NamingManager.java:304)
                  at org.jnp.interfaces.NamingContext.getObjectInstance(NamingContext.java:1483)
                  at org.jnp.interfaces.NamingContext.getObjectInstanceWrapFailure(NamingContext.java:1500)
                  ... 4 more
              Caused by: java.lang.ClassNotFoundException: org.test.ejb.ITest
                  at java.net.URLClassLoader$1.run(URLClassLoader.java:202)
                  at java.security.AccessController.doPrivileged(Native Method)
                  at java.net.URLClassLoader.findClass(URLClassLoader.java:190)
                  at java.lang.ClassLoader.loadClass(ClassLoader.java:307)
                  at sun.misc.Launcher$AppClassLoader.loadClass(Launcher.java:301)
                  at java.lang.ClassLoader.loadClass(ClassLoader.java:248)
                  at java.lang.Class.forName0(Native Method)
                  at java.lang.Class.forName(Class.java:247)
                  at org.jboss.ejb3.proxy.impl.objectfactory.ProxyObjectFactory.redefineProxyInTcl(ProxyObjectFactory.java:406)
                  ... 10 more

               

              The client code is very simple:

               

              public static void main(String[] args) throws Exception {
                  Properties props = new Properties();
                  props.setProperty(Context.INITIAL_CONTEXT_FACTORY, "org.jnp.interfaces.NamingContextFactory");
                  props.setProperty(Context.URL_PKG_PREFIXES, "org.jboss.naming:org.jnp.interfaces");
                  props.setProperty(Context.PROVIDER_URL, "jnp://localhost");

               

                  InitialContext ctx = new InitialContext(props);
                  Object test = ctx.lookup("jboss-test/Test/remote");

               

                  System.out.println(test);
              }

              And it works if I have server classes in the client classpath. So does dynamic classloading really work?

              • 4. Re: Dynamic classloading issue
                v a Newbie

                Finally, got it. It's not a Remoting problem. I wrote a simple client that connects on the loaderport and successfully loads the class. So the remoting classloading really works (sorry for wasting your time). But when I tried to load something through InitialContext.lookup the weird exception (see above) occurred. However, I still have no idea how to deal with the isolated classloader because the example from the documentation does not work.

                • 5. Re: Dynamic classloading issue
                  Ron Sigal Master

                  v a wrote:

                   

                  But when I tried to load something through InitialContext.lookup the weird exception (see above) occurred.

                   

                  The JBoss JNDI implementation is completely separate from Remoting.  It is based on RMI and can support remote classloading to some extent.  The MBean "jboss:service=WebService" in $JBOSS_HOME/server/$CONFIG/conf/jboss-service.xml is the special purpose web server that returns classes from the server.  Note, however, that it can't process jars inside of ears.

                   

                  v a wrote:

                   

                  However, I still have no idea how to deal with the isolated classloader because the example from the documentation does not work.

                   

                  I have recently learned that it's not so easy to access classloaders by name in AS 5.  I have a partial solution which involves adding an object that collects references to all classloaders used in the AS and which can be used to inject classloaders into a Remoting connector.  The class is called ClassloaderCatcher:

                   

                  /*
                  * JBoss, Home of Professional Open Source.
                  * Copyright 2010, Red Hat Middleware LLC, and individual contributors
                  * as indicated by the @author tags. See the copyright.txt file in the
                  * distribution for a full listing of individual contributors.
                    *
                  * This is free software; you can redistribute it and/or modify it
                  * under the terms of the GNU Lesser General Public License as
                  * published by the Free Software Foundation; either version 2.1 of
                  * the License, or (at your option) any later version.
                  *
                  * This software is distributed in the hope that it will be useful,
                  * but WITHOUT ANY WARRANTY; without even the implied warranty of
                  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
                  * Lesser General Public License for more details.
                  *
                  * You should have received a copy of the GNU Lesser General Public
                  * License along with this software; if not, write to the Free
                  * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
                  * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
                  */
                  package org.jboss.deployers.classloadercatcher;

                   

                  import java.io.IOException;
                  import java.io.InputStream;
                  import java.net.URL;
                  import java.util.ArrayList;
                  import java.util.Collections;
                  import java.util.Enumeration;
                  import java.util.Map;
                  import java.util.concurrent.ConcurrentHashMap;

                   

                  import org.jboss.deployers.spi.DeploymentException;
                  import org.jboss.deployers.spi.deployer.helpers.AbstractDeployer;
                  import org.jboss.deployers.structure.spi.DeploymentUnit;
                  import org.jboss.logging.Logger;
                  import org.jboss.metadata.ejb.jboss.JBossMetaData;

                   

                  /**
                  * A ClassLoaderCatcher.
                  *
                  * @author <a href="ron.sigal@jboss.com">Ron Sigal</a>
                  * @version $Revision: 1.1 $
                  *
                  * Copyright Aug 9, 2010
                  */
                  public class ClassLoaderCatcher extends AbstractDeployer
                  {

                   

                     // ------------------------------------------------------------------------------||
                     // Class Members ----------------------------------------------------------------||
                     // ------------------------------------------------------------------------------||

                   

                     public ClassLoader getClassLoader(String deploymentUnitName) {
                        return new ClassLoaderWrapper(deploymentUnitName);
                     }
                    
                     private static ClassLoader getActualClassLoader(String deploymentUnitName) {
                        return map.get(deploymentUnitName);
                     }
                        
                  //   public static
                     private static final Logger log = Logger.getLogger(ClassLoaderCatcher.class);
                     private static final Map<String, ClassLoader> map = new ConcurrentHashMap<String, ClassLoader>();
                    
                     // ------------------------------------------------------------------------------||
                     // Instance Members -------------------------------------------------------------||,
                     // ------------------------------------------------------------------------------||

                   

                     // ------------------------------------------------------------------------------||
                     // Constructor ------------------------------------------------------------------||
                     // ------------------------------------------------------------------------------||

                   

                     public ClassLoaderCatcher() {
                        log.debug(this + " created");
                     }

                   

                     // ------------------------------------------------------------------------------||
                     // Required Implementations -----------------------------------------------------||
                     // ------------------------------------------------------------------------------||

                   

                     /**
                      * {@inheritDoc}
                      * @see org.jboss.deployers.spi.deployer.Deployer#deploy(org.jboss.deployers.structure.spi.DeploymentUnit)
                      */
                     public void deploy(final DeploymentUnit unit) throws DeploymentException
                     {
                        log.trace(this + " gets DeploymentUnit: " + unit);
                  //      if (!isEjb3Deployment(unit)) {
                  //          log.trace(this + " not an EJB3");
                  ////          return;
                  //      }
                       
                        log.trace(this + " name: " + unit.getName());
                         log.trace(this + " topLevel name: " + unit.getTopLevel().getName());
                         log.trace(this + " topLevel simple name: " + unit.getTopLevel().getSimpleName());
                        log.trace(this + " classloader: " + unit.getTopLevel().getClassLoader());
                       
                         DeploymentUnit topLevel = unit.getTopLevel();
                        map.put(topLevel.getSimpleName(), topLevel.getClassLoader());
                        log.trace(this + " map: " + map);
                     }

                   

                     // ------------------------------------------------------------------------------||
                     // Helper Methods ---------------------------------------------------------------||
                     // ------------------------------------------------------------------------------||

                   

                     /*
                      * These may be overridden for testing purposes
                      */

                   

                     /**
                      * Returns whether this is an EJB3 Deployment, determining if we should take action
                      * @param unit
                      * @return
                      */
                     boolean isEjb3Deployment(final DeploymentUnit unit)
                     {
                        // Obtain the Merged Metadata
                        final JBossMetaData md = unit.getAttachment(JBossMetaData.class);

                   

                        // If metadata's not present as an attachment, return
                        if (md == null)
                        {
                           return false;
                        }

                   

                        // If this is not an EJB3 Deployment, return
                        if (!md.isEJB3x())
                        {
                           return false;
                        }

                   

                        // Meets conditions
                        return true;
                     }
                    
                     static class ClassLoaderWrapper extends ClassLoader {
                        private static Enumeration<URL> emptyEnumeration = Collections.enumeration(new ArrayList<URL>());
                        private String topLevelName;
                        private ClassLoader delegate;

                   

                        public ClassLoaderWrapper(String topLevelName) {
                           this.topLevelName = topLevelName;
                        }

                   

                        public Class<?> loadClass(String name) throws ClassNotFoundException {
                           checkClassLoader();
                           if (delegate == null) {
                              throw new ClassNotFoundException("top level name not found: " + topLevelName);
                           }
                           return delegate.loadClass(name);
                        }
                       
                        public URL getResource(String name) {
                           checkClassLoader();
                           if (delegate == null) {
                              return null;
                           }
                           return delegate.getResource(name);
                        }
                       
                        public Enumeration<URL> getResources(String name) throws IOException {
                           checkClassLoader();
                           if (delegate == null) {
                              return emptyEnumeration;
                           }
                           return delegate.getResources(name);
                        }
                       
                        public InputStream getResourceAsStream(String name) {
                           checkClassLoader();
                           if (delegate == null) {
                              return null;
                           }
                           return delegate.getResourceAsStream(name);
                        }
                       
                        public synchronized void setDefaultAssertionStatus(boolean enabled) {
                           checkClassLoader();
                           if (delegate == null) {
                              return;
                           }
                           delegate.setDefaultAssertionStatus(enabled);
                        }
                       
                        public synchronized void setPackageAssertionStatus(String packageName, boolean enabled) {
                           checkClassLoader();
                           if (delegate == null) {
                              return;
                           }
                           delegate.setPackageAssertionStatus(packageName, enabled);
                        }
                       
                        public synchronized void setClassAssertionStatus(String className, boolean enabled) {
                           checkClassLoader();
                           if (delegate == null) {
                              return;
                           }
                           delegate.setClassAssertionStatus(className, enabled);
                        }
                       
                        public synchronized void clearAssertionStatus() {
                           checkClassLoader();
                           if (delegate == null) {
                              return;
                           }
                           delegate.clearAssertionStatus();
                          
                        }
                    
                        private void checkClassLoader() {
                  //         if (delegate == null) {
                  //            delegate = TestDeployer.getActualClassLoader(topLevelName);
                  //         }
                           delegate = ClassLoaderCatcher.getActualClassLoader(topLevelName);
                        }
                     }

                   

                  }

                   

                  It is added to the AS by putting it in a jar and adding the following directory to $JBOSS_HOME/server/$CONFIG/deployers:

                   

                  classloader-catcher.deployer

                    classloader-catcher.jar

                    META-INF

                      classloader-catcher-jboss-beans.xml

                   

                  where classloader-catcher-jboss-beans.xml is

                   

                  <?xml version="1.0" encoding="UTF-8"?>
                  <deployment xmlns="urn:jboss:bean-deployer:2.0">

                   

                     <bean name="classloaderCatcher" class="org.jboss.deployers.classloadercatcher.ClassLoaderCatcher">
                     </bean>
                       
                  </deployment>

                   

                  You then can add the following to $JBOSS_HOME/server/$CONFIG/deploy/ejb3-connectors-jboss-beans.xml:

                   

                  <property name="serverParameters">
                        <map keyClass="java.lang.String" valueClass="java.lang.Object">
                          <entry>
                            <key>remoteClassLoaders</key>
                            <value>
                              <list elementClass="java.lang.ClassLoader">
                                <value-factory bean="classloaderCatcher" method="getClassLoader">
                                   <parameter>Simple21EJB.ear</parameter>
                                </value-factory>
                              </list>
                            </value>
                          </entry>
                        </map>
                      </property>

                   

                  where, in this case, Simple21EJB.ear is the ear with the classes I want to load.

                   

                  It's not a foolproof solution, since there's no way to distinguish two distinct classes with the same name, but it's a step in the right direction.