Chapter 9. Remote classloading facility

When a Remoting unmarshaller reads a serialized request or response from the network, it needs to have access to the classes of the objects contained in the serialized object. Frequently, these will be a simple combination of Remoting and Java objects. For example, if an application returns a java.lang.String, it will be wrapped in an org.jboss.remoting.InvocationResponse, and both classes will be known to the unmarshaller. But what if an application returns an unknown implementation of a known interface? Remoting has a remote classloading facility to support the latter scenario.

9.1. Classloading in client invokers

An instance of org.jboss.remoting.marshal.serializable.SerializableUnMarshaller, which is the default unmarshaller, or parent of the default unmarshaller, for the socket, bisocket, and http transports (marshalling and unmarshalling in the rmi transport is handled by the RMI runtime), will look for classes in the following order:

  1. try system classloader, which loads from locations specified in the classpath;

  2. try the Remoting classloader, that is, the classloader that loaded the Remoting classes (which, depending on the context, may or may not be the system classloader);

  3. try to load from Remoting's remote classloading facility;

  4. try the current thread's context classloader.

The current thread's context classloader can be moved to the front of the list to enable the alternative behavior:

  1. try the current thread's context classloader;

  2. try system classloader, which loads from locations specified in the classpath;

  3. try the Remoting classloader, that is, the classloader that loaded the Remoting classes (which, depending on the context, may or may not be the system classloader);

  4. try to load from Remoting's remote classloading facility.

This alternative behavior may be enabled either by setting the org.jboss.remoting.Remoting.CLASSLOADING_PARENT_FIRST_DELEGATION parameter (actual value "classloadingParentFirstDelegation") to "false" in the InvokerLocator or in the client invoker's configuration map (see Configuration for more on configuration options) or by setting the system property Remoting.CLASSLOADING_PARENT_FIRST_DELEGATION_PROP (actual value "org.jboss.remoting.classloadingParentFirstDelegation") to "false".

Note that the default behavior, in the absence of any explicit action to the contrary, is for a thread's context classloader to be set to the same classloader that loaded the application. However, somewhere outside of Remoting, the context classloader may be set otherwise. For example, a Remoting client invoker might be running inside an EJB3 container that maintains a classloader associated with the EJB3's EAR file and sets the thread context classloader to the EAR classloader whenever it passes control into code supplied in the EAR. This situation would arise when one EJB3 calls another EJB3, where the invocation would ultimately be made by a Remoting client invoker. Note, by the way, the implication that this discussion about classloading in client invokers applies not only to clients in the ordinary client server sense, but also to clients running inside the server.

9.2. Server side support for remote classloading

Remoting implements an optional remote classloading facility that makes it possible for a client invoker unmarshaller to request copies of classes that it encounters during the deserialization process. This facility is provided by a special org.jboss.remoting.transport.Connector that runs an org.jboss.remoting.ServerInvocationHandler designed to locate and return requested classes. This facility is enabled by configuring a Connector with the parameter org.jboss.remoting.InvokerLocator.LOADER_PORT (actual value "loaderport") set to an available port number. (See Configuration for more on configuration options.) Using the "loaderport" parameter will result in the creation of a second Connector which responds to requests to download classes.

Prior to Remoting release 2.4.0.SP1, the classloading search implemented by the classloading SerrverInvocationHandler was the following:

  1. try system classloader, which loads from locations specified in the classpath;

  2. try the Remoting classloader, that is, the classloader that loaded the Remoting classes (which, depending on the context, may or may not be the system classloader).

This original behavior is too restrictive in the context of the rich classloading options of the JBoss Application Server. As of release 2.4.0.SP1, it is possible to configure the classloading facility with a list of additional classloaders. One option is to pass a list of classloaders to a Connector programatically using the org.jboss.remoting.Remoting.Remoting.REMOTE_CLASS_LOADERS parameter (actual value "remoteClassLoaders") in either a configuration map or an org.jboss.remoting.ServerConfiguration. For example:

ServerConfiguration serverConfiguration = new ServerConfiguration("socket");
Map invokerLocatorParameters = new HashMap();
invokerLocatorParameters.put(InvokerLocator.LOADER_PORT, "5544");
serverConfiguration.setInvokerLocatorParameters(invokerLocatorParameters);
Map serverParameters = new HashMap();
ArrayList classLoaders = new ArrayList();
classLoader1 = ...
classLoader2 = ...
classLoaders.add(classLoader1);
classLoaders.add(classLoader2);
serverParameters.put(Remoting.REMOTE_CLASS_LOADERS, classLoaders);
serverConfiguration.setServerParameters(serverParameters);
connector = new Connector();
connector.setServerConfiguration(serverConfiguration);
          

An alternative in the presence of the JBoss microcontainer, e.g., in the Application Server, would be to achieve the same result declaratively. For example:

<?xml version="1.0" encoding="UTF-8"?>
<deployment xmlns="urn:jboss:bean-deployer:2.0">
             
   <bean name="remoting:serverConfiguration"
         class="org.jboss.remoting.ServerConfiguration">
      <constructor>
         <parameter>socket</parameter>
      </constructor>
      <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>ear1:classloader</value>
                   <value>ear2:classloader</value>
                 </list>
               </value>
            </entry>
         </map>
      </property>
      <property name="invocationHandlers">
      ...
      </property>
   </bean>
             
   <bean name="remoting:connector" class="org.jboss.remoting.transport.Connector">
      <property name="serverConfiguration">
         <inject bean="remoting:serverConfiguration"/>
      </property>
   </bean>
             
</deployment>