EJB胖客户端在“胖JAR”中的WebSphere ClassCastException

时间:2016-04-18 19:01:16

标签: maven ejb websphere

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

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

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

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

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

<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“中?

1 个答案:

答案 0 :(得分:1)

我不知道你的意思是“将自己移植到现有的网络应用程序中”,但基于提到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客户端中运行,或者因为您在服务器中运行)并且不想回送到同一个进程。)