在websphere中查找本地EJB的正确方法 - 获取ClassCastException

时间:2015-09-16 08:55:51

标签: java java-ee websphere jndi ejb-3.1

我有一个由本地和远程接口公开的EJB

package com.sam.enqueue;

import javax.ejb.Local;
import javax.ejb.Remote;
import javax.ejb.Singleton;

@Singleton
@Local(SamEnqueueLocal.class)
@Remote(SamEnqueueRemote.class)
public class SamEnqueue implements SamEnqueueRemote, SamEnqueueLocal {


}


// remote interface
package com.sam.enqueue;

import javax.ejb.Remote;

@Remote
public interface SamEnqueueRemote {


}

// local interface
package com.sam.enqueue;

@Local
public interface SamEnqueueLocal {



}

我的app容器是websphere 8.0,我没有覆盖服务器分配的默认JNDI名称。 在服务器启动期间,我在日志中获得以下默认绑定:

CNTR0167I: The server is binding the com.sam.enqueue.SamEnqueueRemote interface of the SamEnqueue enterprise bean in the SAM_ENQUEUE.jar module of the SAM_ENQUEUE application.  The binding location is: ejb/SAM_ENQUEUE/SAM_ENQUEUE.jar/SamEnqueue#com.sam.enqueue.SamEnqueueRemote
CNTR0167I: The server is binding the com.sam.enqueue.SamEnqueueRemote interface of the SamEnqueue enterprise bean in the SAM_ENQUEUE.jar module of the SAM_ENQUEUE application.  The binding location is: com.sam.enqueue.SamEnqueueRemote
CNTR0167I: The server is binding the com.sam.enqueue.SamEnqueueRemote interface of the SamEnqueue enterprise bean in the SAM_ENQUEUE.jar module of the SAM_ENQUEUE application.  The binding location is: java:global/SAM_ENQUEUE/SamEnqueue!com.sam.enqueue.SamEnqueueRemote
CNTR0167I: The server is binding the com.sam.enqueue.SamEnqueueLocal interface of the SamEnqueue enterprise bean in the SAM_ENQUEUE.jar module of the SAM_ENQUEUE application.  The binding location is: ejblocal:SAM_ENQUEUE/SAM_ENQUEUE.jar/SamEnqueue#com.sam.enqueue.SamEnqueueLocal
CNTR0167I: The server is binding the com.sam.enqueue.SamEnqueueLocal interface of the SamEnqueue enterprise bean in the SAM_ENQUEUE.jar module of the SAM_ENQUEUE application.  The binding location is: ejblocal:com.sam.enqueue.SamEnqueueLocal
CNTR0167I: The server is binding the com.sam.enqueue.SamEnqueueLocal interface of the SamEnqueue enterprise bean in the SAM_ENQUEUE.jar module of the SAM_ENQUEUE application.  The binding location is: java:global/SAM_ENQUEUE/SamEnqueue!com.sam.enqueue.SamEnqueueLocal

查找类只是同一服务器中不同EAR中的一个简单java类,代码如下:

Context ctx = new InitialContext();
Object local = ctx.lookup("java:global/SAM_ENQUEUE/SamEnqueue!com.sam.enqueue.SamEnqueueLocal");

SamEnqueueLocal samEnqueue = (SamEnqueueLocal) local;

查找正在使用本地的三个JNDI名称中的任何一个,但它没有被强制转换为SamEnqueueLocal。异常跟踪是:

SystemErr     R java.lang.ClassCastException: com.sam.enqueue.EJSLocal0SGSamEnqueue_cf56ba6f incompatible with com.sam.enqueue.SamEnqueueLocal

... rest ommited

我创建了一个共享库,并将目标EAR的存根放入其中。该库是具有Classes loaded with local class loader first (parent last)策略的源查找EAR的类路径。图书馆不是孤立的。如果删除存根,我会按预期得到java.lang.ClassNotFoundException: com.sam.enqueue.SamEnqueueLocal

更新

使用依赖注入时:

@EJB(lookup="ejblocal:com.sam.enqueue.SamEnqueueLocal")
private SamEnqueueLocal samEnqueueLocal;

我得到的错误是:

javax.ejb.EJBException: Injection failure; nested exception is: java.lang.IllegalArgumentException: Can not set com.sam.enqueue.SamEnqueueLocal field com.some.SomeBean.samEnqueueLocal to com.sam.enqueue.EJSLocal0SGSamEnqueue_cf56ba6f
Caused by: java.lang.IllegalArgumentException: Can not set com.sam.enqueue.SamEnqueueLocal field com.some.SomeBean.samEnqueueLocal to com.sam.enqueue.EJSLocal0SGSamEnqueue_cf56ba6f

所以它基本相同。

3 个答案:

答案 0 :(得分:1)

您正在获取java.lang.ClassCastException,因为您正在检索对EJB中的引用,该EJB存在于与尝试注入它的部署单元(ejb-jar,war等)不同的类加载器中。

如果可能的话,在应用程序之间使用本地EJB引用是依赖于供应商的。您可以将SamEnqueue bean部署在单独的EJB模块中,并尝试通过每个应用程序的清单Class-Path:条目引用它。确保EAR文件中没有SamEnqueueLocal的副本。

或者,只需使用SamEnqueueRemote界面。

有关详细信息,请参阅Java EE specification的第8章。

答案 1 :(得分:1)

请参阅知识中心EJB modules主题的“本地客户端视图”部分:

  

EJB规范仅要求支持本地客户端视图   适用于在同一应用程序中打包的EJB。这包括当地的   家庭,本地业务接口和无接口视图。   WebSphere®ApplicationServer允许访问本地客户端视图   EJB打包在具有一些限制的单独应用程序中

     
      
  • 本地接口使用的本地接口和所有参数,返回和异常类型必须对调用应用程序和目标EJB应用程序的类加载器可见。您可以通过使用与服务器类加载器关联的共享库或使用与这两个应用程序关联的隔离共享库来确保这一点。有关更多信息,请阅读创建共享库主题。
  •   
     

...

答案 2 :(得分:1)

通过bkail's answer中提供的链接,以下是我遵循的步骤。

  1. 从我的源EJB jar和包中取出本地远程接口,即SamEnqueueRemoteSamEnqueueLocal,然后转换为单独的jar文件。虽然只是取出Local界面也可以。
  2. 创建一个共享库并将此jar放入其中。必须隔离共享库,以便调用者和被调用者加载相同版本的类。
  3. 在调用者EAR中,使用查找或注入来获取对本地接口的引用。
  4. 将调用者和被调用者都部署到服务器,并确保在两个EAR的类路径中添加共享库。
  5. this链接中提到的方法之一是类似的。

      

    防止这种情况的一种方法是使用远程接口。正如Paul所提到的那样,当调用者和被调用者在同一个JVM中时会发生优化,因此它并不像在单独的JVM中那样昂贵。 ORB具有类加载器机制,可确保使用与调用的每一侧兼容的类加载器加载调用者和被调用者的类。

         

    选项2,包括新耳中的ejb jar,不会解决问题。尽管这两个类在两个类加载器中都可用,但是从被调用者传递给调用者的对象仍将使用其他应用程序的类加载器进行实例化,并且不能是类型可分配的。与选项3相同。

         

    使其工作的第二种方法是将调用者和被调用者使用的类放在" WAS共享库中#34;并配置两个应用程序以使用该共享库。 WAS InfoCenter文档中描述了共享库的主题以及如何配置它们...搜索"共享库。"

         

    使其工作的第三种方法,即三者中最不可取的方法,是将WAS服务器类加载器策略更改为每个服务器一个类加载器。"正如我在顶部提到的那样,默认值是每个应用程序(EAR文件)的一个类加载器。"更改为每个服务器的一个类加载器可确保所有内容都由同一个类加载器加载,因此类型兼容,但会剥夺应用程序在每个应用程序都在自己的类加载器中所带来的隔离/安全性好处。