Friday, February 12, 2010

E.T. phone home

Using some extra facilities it should be possible to call EJBs deployed on JBoss 4 from JBoss 5 (or calling 5 from 6, and even calling WebSphere from JBoss). The end result should be:
InitialContext ctx = new InitialContext();
GreeterRemote bean = (GreeterRemote) ctx.lookup("java:externalContext/Greeter");
String result = bean.sayHi("testPositive");
Now the first step should be to be able to have complete isolation of the outgoing calls, so that any existing class in the application server doesn’t collide with any client class.

// we want the client classes of the receiving application server
URL clientUrl = new URL(jbossHomeDir.toURI().toURL(), "client/jbossall-client.jar");
// don't set a parent, so we run in complete isolation.
URLClassLoader urlCl = new URLClassLoader(urls, null);
// since we're running in isolation my own interface needs to be added.
ClassLoader cl = new AluniteClassLoader(urlCl, ClassLoader.getSystemClassLoader());
ClassLoader previous = Thread.currentThread().getContextClassLoader();
Thread.currentThread().setContextClassLoader(cl);
try
{
InitialContext ctx = new InitialContext();
GreeterRemote bean = (GreeterRemote) ctx.lookup("Greeter");
String result = bean.sayHi("testPositive");
assertEquals("Hi testPositive", result);
}
finally
{
Thread.currentThread().setContextClassLoader(previous);
}
The most important bit is the AluniteClassLoader which has no parent class loader and delegates to the given class loaders in turn. Thus can the above code work out.

As Jaikiran correctly pointed out, the current bean proxy needs to be called within the correct context class loader. So we would also need a specialized external context which return wrappers to allow setting the thread context class loader at the right moment.

Lastly it needs code to correctly setup and install the external context in the local JNDI.

The code so far can be found here.

5 comments:

  1. Hi Carlo,

    I have tried the Alunite class loader for remote call from JBoss 5.1 to JBoss 7.

    I can successfully instantiate the InitialContext but when I lookup the remote ConnectionFactory (a HornetQJMSConnectionFactory) I get a ClassCastException :

    connectionFactory = (ConnectionFactory) as7InitialContext.lookup(JBossAs7JNDIClient.JMS_REMOTE_CONNECTION_FACTORY);

    java.lang.ClassCastException: org.hornetq.jms.client.HornetQJMSConnectionFactory cannot be cast to javax.jms.ConnectionFactory

    Is it possible to cast to a class contained in the Alunite loaded jar ?

    Best regards,

    ReplyDelete
  2. Try to find out which class loader picks up which ConnectionFactory.

    System.out.println(ConnectionFactory.class.getClassLoader());
    Object connectionFactory = as7InitialContext.lookup(JBossAs7JNDIClient.JMS_REMOTE_CONNECTION_FACTORY);
    for (Class intf : connectionFactory.getClass().getInterfaces()) {
    System.out.println(intf + " => " + intf.getClassLoader());
    }

    The ConnectionFactory should only be on your app class path, not the urlCl.

    ReplyDelete
  3. Hi,
    I'm trying to connect EJB deployed on JBOSS 4.3 EAP from JBOSS 7.1 with Alunite ClassLoader, but i obtain :

    Caused by: java.rmi.UnmarshalException: error unmarshalling arguments; nested exception is:
    java.net.MalformedURLException: unknown protocol: vfs
    at sun.rmi.server.UnicastServerRef.dispatch(UnicastServerRef.java:282)
    at sun.rmi.transport.Transport$1.run(Transport.java:153)

    Do you have nay idea?

    ReplyDelete