ClassCastException with PortableRemoteObject.narrow() EJB2/EJB3

After switching to JBoss 5 and recreating my EAR project inside eclipse, I started getting ClassCastException:
java.lang.ClassCastException
at com.sun.corba.se.impl.javax.rmi.PortableRemoteObject.narrow(PortableRemoteObject.java:229)
at javax.rmi.PortableRemoteObject.narrow(PortableRemoteObject.java:137)
Object obj = initialContext.lookup(ejbReferenceComponent);
MyEJBRemoteHome home = javax.rmi.PortableRemoteObject.narrow(obj, MyEJBRemoteHome.class);

After looking around, the cause of it is how the app server determines whether to give you a EJBHome instance which you have to use .narrow() then .create() to get a reference to the bean for EJB2 vs returning a proxy to the bean itself for EJB3. JBoss has some information on referencing EJB3 from EJB2 environments and vice-versa.

The summary generally is if the application.xml has the EJB2 header then it gives you an EJBHome interface.
If the ear's ejb-jar has the EJB3 header then it gives you a proxy to the bean itself (no need for narrow/create).

The lack of a more intelligent error from PortableRemoteObject is a bit annoying. However here is a generic solution for cases when your switching between environments:
public <T, S extends EJBHome> T lookupEJB(String ejbReferenceComponent, Class<S> homeClass, Class<T> ejbClass)
throws NamingException, IllegalArgumentException, SecurityException, IllegalAccessException, InvocationTargetException,
NoSuchMethodException
{
log.debug("{}", initialContext.getEnvironment());
java.lang.Object remote = initialContext.lookup(ejbReferenceComponent);

if (ejbClass.isInstance(remote))
return (T) remote;

S home = (S) PortableRemoteObject.narrow(remote, homeClass);

return (T) home.getClass().getMethod("create", (Class[]) null).invoke(home, (Object[]) null);
}
Of course the simplest approach is to convert it all to EJB3 and you won't have to worry :)

Comments