[jboss-user] [JBoss Remoting] - Re: Dynamic classloading issue

Ron Sigal do-not-reply at jboss.com
Wed Sep 29 07:11:36 EDT 2010


Ron Sigal [http://community.jboss.org/people/ron.sigal%40jboss.com] created the discussion

"Re: Dynamic classloading issue"

To view the discussion, visit: http://community.jboss.org/message/563767#563767

--------------------------------------------------------------
> 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 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=" mailto:ron.sigal at jboss.com ron.sigal at 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.
--------------------------------------------------------------

Reply to this message by going to Community
[http://community.jboss.org/message/563767#563767]

Start a new discussion in JBoss Remoting at Community
[http://community.jboss.org/choose-container!input.jspa?contentType=1&containerType=14&container=2050]

-------------- next part --------------
An HTML attachment was scrubbed...
URL: http://lists.jboss.org/pipermail/jboss-user/attachments/20100929/ce32e0f3/attachment-0001.html 


More information about the jboss-user mailing list