Prologue

Recently we experienced a problem regarding the RMI-compability between IBM WebSphere (WAS) version 5.1 and BEA WebLogic (WLS) version 8.3 both running a version 1.4.2 JVM. The WLS is a client of the WAS server and this worked fine since both the IBM and BEA RMI-implementation is based upon Sun’s RMI-ORB. Then an application came along that needed a custom login module and we had to enable security attribute propagation in WAS. All of a sudden hell broke loose, and the WLS was not able to speak with the WAS anymore. The problem was soon identified to be the wire format of the RMI-implementation. When you enable security attribute propagation, the wire format changes and WLS didn’t understand the WAS RMI response anymore.

The solution

In one way or the other I needed the RMI-implementation from WAS to work inside WLS. This is not an easy thing to do because a RMI-implementation register itself inside the JVM in several ways. I also wanted to hide the IBM classes from WLS to make sure I didn’t get any unexpected class ambiguities.

First I needed to just be able to call a EJB on a WAS server from a remote JVM. This was pretty easy and well documented from IBM. Since I didn’t need the support for DataSources or JMS resources I opted for the WebSphere pluggable client that only support EJB invocations. The following list includes the relevant WAS libraries needed to run the pluggable client.

"ibmext.jar", "ibmorb.jar", "iwsorbutil.jar", "naming.jar", "namingclient.jar", "properties.jar", "txClientPrivate.jar", "ras.jar", "wsexception.jar", "bootstrap.jar", "ecutils.jar", "iwsorb.jar", "ffdc.jar", "idl.jar", "txClient.jar"

The solution involved emulating the environment to let the IBM RMI-implementation believe it was running in a standard client JVM without knowing about BEA RMI and vica versa. I therefore created an IsolatingClassLoader that knew how to load the IBM client jars from the filesystem and not from the classpath. The same ClassLoader also knew what packages and classes it was supposed to isolate and to do the loading itself instead of delegating it to the parent ClassLoader.

  public class IsolatingClassLoader extends URLClassLoader {
    ...
    ...
    protected synchronized Class loadClass(String name, boolean resolve) throws ClassNotFoundException {
      boolean isolated = isolatedClassNames.contains(name);
      if (!isolated) {
        for (int i = 0; i < isolatedPackagePrefixes.length; i++) {
          if (name.startsWith(isolatedPackagePrefixes[i])) {
            isolated = true;
            break;
          }
        }
      }
      if (isolated) {
        Class c = findLoadedClass(name);
        if (c == null) c = findClass(name);
        if (resolve) resolveClass(c);
        return c;
      }
    return super.loadClass(name, resolve);
  }
}

By doing this I isolated all com.ibm. packages and classes. Before doing the lookups I had to substitute the context ClassLoader of the current thread to ensure that classes was loaded with the IsolatingClassLoader and register the thread to ensure the correct UtilDelegate is used (More on that below). This was restored and unregistered in a finally clause right after doing the lookup and narrowing.

ClassLoader oldClassLoader = Thread.currentThread().getContextClassLoader();
Thread.currentThread().setContextClassLoader(isolatingClassLoader);
ThreadRegistry.getInstance().register();
try {
  InitialContext ctx = new InitialContext(jndiConfig);
  Object o = ctx.lookup(”iiop://wasserverhostname:2809/cell/clusters/mycluster/ejb/com/path/to/ejb/SomeEJBHome”);
  SomeEJBHome home = (SomeEJBHome) PortableRemoteObject.narrow(o, SomeEJBHome.class);
  return (SomeEJBRemoteHome) Proxy.newProxyInstance(getClass().getClassLoader(), new Class[] \\
     { SomeEJBRemoteHome.class }, new IBMIvocationHandler(home.create(), isolatingClassLoader));
} finally {
  ThreadRegistry.getInstance().unregister();
  Thread.currentThread().setContextClassLoader(oldClassLoader);
}

The java.naming configuration also needed modification to use IBM’s implementation and using the constructor of the InitialContext class I supplied my own naming configuration.

key value
java.naming.factory.initial com.ibm.websphere.naming.WsnInitialContextFactory
java.naming.provider.url iiop://wasserverhost:2809
java.naming.factory.url.pkgs com.ibm.ws.naming

As you can see from the lookup code I was forced to not return the actual RemoteHome object but actually a proxied version. Inside the IBMInvocationHandler I had to do the ContextClassLoader manipulation and thread registering again to ensure that IBM classes was available during the remote EJB invocation.

public class IBMIvocationHandler implements InvocationHandler {
  private ClassLoader isolatingClassLoader;
  private Object remoteStub;

  public MetroIvocationHandler(Object remoteStub, ClassLoader isolatingClassLoader) {
    this.remoteStub = remoteStub;
    this.isolatingClassLoader= isolatingClassLoader;
  }

  public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
    ClassLoader oldClassLoader = Thread.currentThread().getContextClassLoader();
    Thread.currentThread().setContextClassLoader(isolatingClassLoader);
    ThreadRegistry.getInstance().register();
    try {
      return method.invoke(remoteStub, args);
    } finally {
      ThreadRegistry.getInstance().unregister();
      Thread.currentThread().setContextClassLoader(oldClassLoader);
    }
  }
}

We have soon reached our goal, but one nasty obstacle is standing between ourselves and a solution. That is the javax.rmi.CORBA.Util class that holds a private reference to a singleton implementing the javax.rmi.CORBA.UtilDelegate inteface providing utility methods that can be used by stubs and ties to perform common operations. The implementation is defined by a system property (javax.rmi.CORBA.UtilClass) and is initialized during VM startup. We could simply have replaced the private field containting the UtilDelegate with IBM’s implementation (com.ibm.ws.orb.WSUtilDelegateImpl), but then BEA’s RMI-implementation would get into trouble and we would see some really unbelievable exceptions ;-). Instead I created a proxy version of the UtilDelegate that chooses between the original and IBM’s implementation depending on which RMI-implementation that was using it. The ThreadRegistry uses a WeakHashMap to store the current Thread to identify which Thread is coming through IBM code or BEA RMI code.

public class UtilDelegateInvocationHandler implements InvocationHandler {
  private UtilDelegate original;
  private UtilDelegate ibm;

  public UtilDelegateInvocationHandler(UtilDelegate original, UtilDelegate ibm) {
    this.original = original;
    this.ibm = ibm;
  }

  public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
    iif(ThreadRegistry.getInstance().isRegistered()) {
        return method.invoke(ibm, args);
      else
       return method.invoke(original, args);
  }

}

The proxied UtilDelegate was installed using the reflection API and required that Java2 Security allowed my class to modify the accessible flags.

UtilDelegate ibmUD = (UtilDelegate) \\
      isolatingClassLoader.loadClass("com.ibm.ws.orb.WSUtilDelegateImpl").newInstance();
Class c = Class.forName("javax.rmi.CORBA.Util");
Field ff = c.getDeclaredField("utilDelegate");
ff.setAccessible(true);
UtilDelegate originalUD = (UtilDelegate) ff.get(null);
UtilDelegate proxyUtilDelegate = (UtilDelegate) Proxy.newProxyInstance(getClass().getClassLoader(), \\
      new Class[] { UtilDelegate.class }, new UtilDelegateInvocationHandler(originalUD, ibmUD));
ff.set(null, proxyUtilDelegate);

Conclusion

After doing this we could successfully use both RMI-implementations interchangably inside the WLS server and finally invoke EJBs running inside a WAS cluster with the security attribute propagation enabled.

Source Code

You may download the complete source code: was-client-inside-wls-updated-src

Update: 2. May, 2008

After beeing challange by Asgeir Nilsen I came up with a much improved way of determining which UtilDelegate implementation to use. In the old version I parsed the stack trace, but in this new and improved version I register the Thread in a singleton instead. The source archive has been updated to reflect this change.

Share/Save/Bookmark

6 Responses to “Using the WebSphere pluggable client inside WebLogic Server”
  1. Pal says:

    Hahahaha. Fanbloodytastic

  2. Hans-Jacob Melby says:

    How cool is that!
    Source please ?

  3. Asgeir S. Nilsen says:

    I see your useIBM() method creates an exception and inspects the stack trace to decide whether IBM or BEA is using it. Creating stack traces are costly on 1.4 JVMs. Does there exist other options for achieving the same result?

  4. Paul René Jørgensen says:

    @Asgeir S. Nilsen
    Your reply forced me to rethink the situation, and yes, there is a better way of doing this. Since both entry and exit point to the transaction leading to the IBM RMI-implementation is through the InvocationHandler I could create a singleton registering / unregistering threads that are bound to use the IBM-implementation. And the UtilDelegateInvocationHandler could likewise check this singleton to see if the current Thread was registered and then choose the IBM implementation.

    Thank you for challenging my solution and force me come up with something even better. :)

  5. Marcus says:

    I’ve just downloaded your project. When you said “You may download the complete source code: was-client-inside-wls-updated-src”, do you mean the complete byte code? I’m not seeing any .java files here. There are only .class files.
    Can you send me a copy? I also don’t have ibmext.jar and properties.jar. Can you send me these jar as well?

    thanks and best regards.

  6. Paco G says:

    Please, as Marcus said, can you upload the sources?
    The download zip only has the .class ones.

    Thanks in advance.

    Paco García

Leave a Reply