【问题标题】:WebSphere ClassCastException with EJB client in a "fat JAR"带有 EJB 客户端的 WebSphere ClassCastException 在“胖 JAR”中
【发布时间】:2023-04-04 14:33:01
【问题描述】:

我在同一个 WAS 8.5 JVM 上有两个应用程序。 “服务器应用程序”包含一个 @Remote bean,“客户端应用程序”调用它。

“服务器应用程序”被打包为 EAR 文件。 “客户端应用程序”是一个 JAR 文件,可将自身移植到现有的 Web 应用程序中。

当我构建一个“EJB 接口 JAR”并将它包含在两个应用程序中时,一切都很好。

但是,我在一个深奥的环境中工作,这使我无法在生产中这样做。相反,我可以将远程 EJB 接口包含在“客户端应用程序”的“胖 JAR”化身中。

我正在使用 Maven 来组装这个“胖客户端应用程序 JAR”。我有一个构建上面提到的“EJB 接口 JAR”的 Maven 模块。我将此模块声明为“客户端应用程序”模块的依赖项。然后我这样做:

<plugin>
            <groupId>org.apache.maven.plugins</groupId>
            <artifactId>maven-assembly-plugin</artifactId>
            <version>2.6</version>
            <configuration>
                <descriptorRefs>
                    <descriptorRef>jar-with-dependencies</descriptorRef>
                </descriptorRefs>

            </configuration>
            <executions>
                <execution>
                    <id>assemble-all</id>
                    <phase>package</phase>
                    <goals>
                        <goal>single</goal>
                    </goals>
                </execution>
            </executions>
        </plugin>

生成的“jar-with-dependencies”确实包含 EJB 接口。但是,将 bean 转换为所述接口时出现错误。

        Hashtable<String, String> environment = new Hashtable<String, String>();
        environment.put(javax.naming.Context.INITIAL_CONTEXT_FACTORY, "com.ibm.websphere.naming.WsnInitialContextFactory");
        environment.put(javax.naming.Context.PROVIDER_URL, "corbaloc:iiop:localhost:2810");

        javax.naming.Context ctx = new InitialContext(environment);
        engine = (UXServerRemote) ctx.lookup("java:global/myapp/my-ejb/UXServerBean!com.myapp.client.UXServerRemote");

错误:

java.lang.ClassCastException:com.myapp.client._UXServerRemote_Stub 与 com.myapp.client.UXServerRemote 不兼容

显然,WAS 在投射对象时非常谨慎。如何以 WAS 接受的与其“EJB 接口 JAR”副本相同的方式将我的 EJB 接口放在“客户端应用程序 JAR”中?

【问题讨论】:

  • 请分享您正在使用的常量INITIAL_CONTEXT_FACTORY、PROVIDER_URL、UX_SERVER_BEAN的值。
  • 用实际值替换了常量。
  • 我认为您使用的 JNDI 名称不正确。这是通过 JBoss 中的 JNDI 读取 (java:global) EJB 的方式。在 websphere 中,您需要使用 java:comp/env/something ,其中 something = 您在 web.xml 中定义为 的内容
  • @Sampada JNDI 名称看起来不错。 java:global 自 EE 6 起有效。
  • @FredericFortier java:global 查找应该自动缩小 _UXServerRemote_Stub 以便它在客户端中可用。如果在ctx.lookup 之前打印UXServerRemote.class.getClassLoader()Thread.currentThread().getContextClassLoader().loadClass(UXServerRemote.class).getClassLoader() 的值,它们的值如何比较?

标签: maven ejb websphere


【解决方案1】:

我不知道您所说的“将自身移植到现有的 Web 应用程序中”是什么意思,但基于 java.net.FactoryURLClassLoader 的提及,我猜您正在使用 URLClassLoader.newInstance 以某种方式在外部加载此 JAR正常的 EE 类加载。在这种情况下,我会建议这样的事情:

Thread thread = Thread.currentThread();
ClassLoader saveContextClassLoader = thread.getContextClassLoader();

thread.setContextClassLoader(getClass().getClassLoader());
try {
    javax.naming.Context ctx = new InitialContext();
    engine = (UXServerRemote) ctx.lookup("java:global/myapp/my-ejb/UXServerBean!com.myapp.client.UXServerRemote");
} finally {
    thread.setContextClassLoader(saveContextClassLoader);
}

通过在调用查找周围将上下文类加载器交换到您的自定义类加载器,您允许java:global 查找内部查找只有您的自定义类加载器才能访问的 UXServerRemote 类。查找基本上尝试相同的Thread.currentThread().getContextClassLoader().loadClass(UXServerRemote.class.getName()) 调用;如果找不到该类,java:global 查找内部将返回来自“服务器应用程序”的存根,而不是因 ClassNotFoundException 而失败。如果您正在编写一个实际上不需要直接调用返回对象上的方法的客户端(例如,客户端可能只是一个代理,它将传递对象),这将非常有用。在您的情况下,该“服务器应用程序”存根将与您的“客户端应用程序”自定义类加载器不兼容,这就是您看到 ClassCastException 的原因。

注意:当您的代码在托管服务器中运行时,您不需要指定 InitialContext 构造函数属性。仅当您在独立 Java SE 客户端中运行时才需要 INITIAL_CONTEXT_FACTORY,并且仅当您正在连接到另一个进程时才需要 PROVIDER_URL(因为您在独立 Java SE 客户端中运行或因为您在服务器中运行并且不想循环到同一个进程)。

【讨论】:

  • 现有的“网络应用程序”是一个具有特殊特性的专有应用程序。它接受“插件”,因此是“客户端应用程序 JAR”的由来。上述应用程序将在“正常 EE 类加载之外”加载这些库是有道理的。我会尝试建议的解决方法。感谢您对初始上下文工厂和提供程序 url 的注释!
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2014-06-04
  • 2014-01-01
  • 2019-08-10
  • 2018-06-28
  • 2011-02-13
  • 2010-12-18
相关资源
最近更新 更多