【问题标题】:Correct way to lookup local EJB in websphere - Getting ClassCastException在 websphere 中查找本地 EJB 的正确方法 - 获取 ClassCastException
【发布时间】:2015-09-16 08:55:50
【问题描述】:

我有一个由本地和远程接口公开的 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 {



}

我的应用容器是 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

所以基本一样。

【问题讨论】:

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


    【解决方案1】:

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

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

    或者,只需使用SamEnqueueRemote 接口。

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

    【讨论】:

    • 在我从 lib 中删除了存根,然后将 Class loader policyMultiple 设置为 Single 之后,我得到了这个工作。顺便说一句,清单条目引用另一只耳朵的语法是什么?
    • 事实证明,我必须使用隔离的类加载器,因为在同一台服务器(遗留代码)上还有其他耳朵,不同耳朵有不同版本的同一类。因此,通过在清单文件中指定 Class-Path: 来尝试引用它的第二个选项。我尝试了一些东西,但没有成功。你能举个例子吗?
    【解决方案2】:

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

    EJB 规范只要求支持本地客户端视图 对于打包在同一应用程序中的 EJB。这包括本地 家庭、本地业务接口和无接口视图。 WebSphere® Application Server 允许访问本地客户端视图以 EJB 打包在一个单独的应用程序中,但有一些限制

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

    ...

    【讨论】:

    • 好的.. 所以我卸载了包含目标会话 bean 及其接口的 EAR。我创建了一个独立的共享库,将我的 jar 的子库放在该库中,并将其添加到源 EAR 的类路径中。问题是lib中的会话bean没有初始化,我得到javax.naming.NameNotFoundException: Name "com.sam.enqueue.SamEnqueueLocal" not found in context "ejblocal:".
    【解决方案3】:

    来自bkail's answer 中提供的链接,这些是我为使其工作而遵循的步骤。

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

    this链接中提到的一种方法是类似的。

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

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

    使其工作的第二种方法是将调用者和被调用者使用的类放在“WAS 共享库”中,并将两个应用程序配置为使用该共享库。共享库的主题以及如何配置它们在 WAS 信息中心文档中进行了描述...搜索“共享库”。

    使其工作的第 3 种方法(这三种方法中最不可取的)是将 WAS 服务器类加载器策略更改为“每个服务器一个类加载器”。正如我在顶部提到的,默认设置是“每个应用程序(EAR 文件)一个类加载器”。更改为每台服务器一个类加载器可确保所有内容都由同一个类加载器加载,因此是类型兼容的,但会剥夺您的应用程序在其自己的类加载器中拥有每个应用程序所带来的隔离/安全优势。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2015-01-25
      • 1970-01-01
      • 2023-04-04
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多