javawebsphereejbxdoclet

XDoclet-based Stateless Session Bean Compatibility Issue: ClassCastException in WebSphere 9.0.5


I have a legacy EJB application that was originally developed in Java 1.6 and deployed on WebSphere 8.5 more than a decade ago. Recently, I upgraded the Java version to 1.8 and the WebSphere version to 9.0.5. The application works fine.

However, I'm encountering a peculiar issue related to a specific XDoclet-based stateless session bean. This bean is annotated with @ejb.util generate = "physical".

The issue occurs when the batch job starts first and creates the bean, and then the application attempts to use the bean. I get the following exception:

Caused by: java.lang.ClassCastException: org.omg.stub.javax.ejb._EJBHome_Stub incompatible with com.xxx.common.reference.interfaces.ReferenceDataBeanHome

If the application creates the bean first and then the batch job uses it, everything works as expected.

XDoclet Generated Code:
The narrowing code is already generated by the XDoclet, we can't edit the code

 private static Object lookupHome(java.util.Hashtable environment, String jndiName, Class narrowTo) throws javax.naming.NamingException {
      // Obtain initial context
      javax.naming.InitialContext initialContext = new javax.naming.InitialContext(environment);
      try {
         Object objRef = initialContext.lookup(jndiName);
         // only narrow if necessary
         if (narrowTo.isInstance(java.rmi.Remote.class))
            return javax.rmi.PortableRemoteObject.narrow(objRef, narrowTo);
         else
            return objRef;
      } finally {
         initialContext.close();
      }
   }

What I tried:

I have changed the @ejb.util generate = "logical". based on the IBM WebSphere suggestion but the Batch job starts to fail if starts after the application creates the Bean

Environment:

Specifics:

Question:


Solution

  • The code that is failing with the ClassCastException is not calling PortableRemoteObject.narrow(). Anytime a remote EJB is looked up in naming it should perform a PortableRemoteObject.narrow.

    Often, code will work without the narrow because the ORB will attempt to narrow automatically, but this does not always work, depending on the classpath of the client at the time of the lookup. If the first lookup does not have the stub on the classpath, then you will get the EJBHome stub. Unfortuntely, this can then end up cached in Naming, which may effect other clients as well. Always performing the narrow avoids this issue.

    Since the JNDI lookup is returning an _EJBHome_Stub, this means the JNDI name is for a remote home and that the thread context classloader does not have access to the target class, ReferenceDataBeanHome. Since the ORB is unable to load _ReferenceDataBeanHome_Stub, then it will return _EJBHome_Stub instead, which must be narrowed.

    What is especially curious with this scenario is that the code does contain a call to narrow, but it is not being called, suggesting that narrowTo.isInstance(java.rmi.Remote.class) is returning false. Since the home being looked up is a remote home, the narrowTo class should implement javax.ejb.EJBHome, which extends java.rmi.Remote and therefore this method should return true and the narrow should be performed.

    I recommend checking the following:

    Determining why isInstance is returning false is the best solution. Alternatively, you could make sure the _ReferenceDataBeanHome_Stub class is packaged with the batch application, and that may allow the ORB to proactively narrow; however this seems like a workaround.