在tomcat的jsp中调用ejb的问题。怪!

我在tomcat中用jsp调用运行在weblogic中的ejb,出现错误。我搞了半天都没有搞明白,后来我把那个jsp放在resin中运行,就一切正常。真搞不懂,同样的东西在tomcat中为什么就不好用。大家有没有遇到过这样的情况阿。

有可能是 JNDI 没配好吧,各个服务器的实现机制是不一样的。
当然会有一些不兼容的情况。

应该是你自己没有配置好。Tomcat是一个单纯的Servlet Container而Resin本身是一个支持EJB的App Server。所以Resin能够运行而Tomcat不能运行的情况很正常。

如果你想要在Tomcat中调用Weblogic中EJB的话,必须到Weblogic去找一个wlclient.jar(大概是这个名字,记不住了)把它放到你的Tomcat的web app的WEB-INF\lib目录下,然后就OK了,当然JNDI的配置也不能搞错。我曾经试过Tomcat中的Servlet调用Weblogic上的EJB,完全是可以的。只有一点要注意,就是不要用那个PortableRemoteObject.narrow(),lookup出来直接用就行了。

我一开始就已经把weblogic.jar放在tomcat/lib中了。至于为什么不能用narrow,我不大懂。

不是weblogic.jar,而是wlclient.jar,你去weblogic8.1的server\lib目录下可以找到。如果你用weblogic.jar的话,tomcat在deploy web app的时候肯定会报错的,因为里面的weblogic.jar包括了weblogic的servlet implements,和tomcat自己的servlet implements冲突了。

tomcat不支持narrow,那是因为它本身就是一个Servlet Container,不支持专门对EJB对象的方法。

我已经把narrow去掉了,但是问题依旧。

type Exception report

message

description The server encountered an internal error () that prevented it from fulfilling this request.

exception

org.apache.jasper.JasperException: weblogic/rmi/extensions/server/Stub
at org.apache.jasper.servlet.JspServletWrapper.service(JspServletWrapper.java:254)
at org.apache.jasper.servlet.JspServlet.serviceJspFile(JspServlet.java:295)
at org.apache.jasper.servlet.JspServlet.service(JspServlet.java:241)
at javax.servlet.http.HttpServlet.service(HttpServlet.java:853)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:247)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:193)
at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:256)
at org.apache.catalina.core.StandardPipeline$StandardPipelineValveContext.invokeNext(StandardPipeline.java:643)
at org.apache.catalina.core.StandardPipeline.invoke(StandardPipeline.java:480)
at org.apache.catalina.core.ContainerBase.invoke(ContainerBase.java:995)
at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:191)
at org.apache.catalina.core.StandardPipeline$StandardPipelineValveContext.invokeNext(StandardPipeline.java:643)
at org.apache.catalina.core.StandardPipeline.invoke(StandardPipeline.java:480)
at org.apache.catalina.core.ContainerBase.invoke(ContainerBase.java:995)
at org.apache.catalina.core.StandardContext.invoke(StandardContext.java:2415)
at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:180)
at org.apache.catalina.core.StandardPipeline$StandardPipelineValveContext.invokeNext(StandardPipeline.java:643)
at org.apache.catalina.valves.ErrorDispatcherValve.invoke(ErrorDispatcherValve.java:171)
at org.apache.catalina.core.StandardPipeline$StandardPipelineValveContext.invokeNext(StandardPipeline.java:641)
at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:172)
at org.apache.catalina.core.StandardPipeline$StandardPipelineValveContext.invokeNext(StandardPipeline.java:641)
at org.apache.catalina.core.StandardPipeline.invoke(StandardPipeline.java:480)
at org.apache.catalina.core.ContainerBase.invoke(ContainerBase.java:995)
at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:174)
at org.apache.catalina.core.StandardPipeline$StandardPipelineValveContext.invokeNext(StandardPipeline.java:643)
at org.apache.catalina.core.StandardPipeline.invoke(StandardPipeline.java:480)
at org.apache.catalina.core.ContainerBase.invoke(ContainerBase.java:995)
at org.apache.coyote.tomcat4.CoyoteAdapter.service(CoyoteAdapter.java:223)
at org.apache.coyote.http11.Http11Processor.process(Http11Processor.java:594)
at org.apache.coyote.http11.Http11Protocol$Http11ConnectionHandler.processConnection(Http11Protocol.java:392)
at org.apache.tomcat.util.net.TcpWorkerThread.runIt(PoolTcpEndpoint.java:565)
at org.apache.tomcat.util.threads.ThreadPool$ControlRunnable.run(ThreadPool.java:619)
at java.lang.Thread.run(Thread.java:536)


root cause

javax.servlet.ServletException: weblogic/rmi/extensions/server/Stub
at org.apache.jasper.runtime.PageContextImpl.handlePageException(PageContextImpl.java:536)
at org.apache.jsp.ejbtest_jsp._jspService(ejbtest_jsp.java:75)
at org.apache.jasper.runtime.HttpJspBase.service(HttpJspBase.java:137)
at javax.servlet.http.HttpServlet.service(HttpServlet.java:853)
at org.apache.jasper.servlet.JspServletWrapper.service(JspServletWrapper.java:210)
at org.apache.jasper.servlet.JspServlet.serviceJspFile(JspServlet.java:295)
at org.apache.jasper.servlet.JspServlet.service(JspServlet.java:241)
at javax.servlet.http.HttpServlet.service(HttpServlet.java:853)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:247)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:193)
at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:256)
at org.apache.catalina.core.StandardPipeline$StandardPipelineValveContext.invokeNext(StandardPipeline.java:643)
at org.apache.catalina.core.StandardPipeline.invoke(StandardPipeline.java:480)
at org.apache.catalina.core.ContainerBase.invoke(ContainerBase.java:995)
at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:191)
at org.apache.catalina.core.StandardPipeline$StandardPipelineValveContext.invokeNext(StandardPipeline.java:643)
at org.apache.catalina.core.StandardPipeline.invoke(StandardPipeline.java:480)
at org.apache.catalina.core.ContainerBase.invoke(ContainerBase.java:995)
at org.apache.catalina.core.StandardContext.invoke(StandardContext.java:2415)
at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:180)
at org.apache.catalina.core.StandardPipeline$StandardPipelineValveContext.invokeNext(StandardPipeline.java:643)
at org.apache.catalina.valves.ErrorDispatcherValve.invoke(ErrorDispatcherValve.java:171)
at org.apache.catalina.core.StandardPipeline$StandardPipelineValveContext.invokeNext(StandardPipeline.java:641)
at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:172)
at org.apache.catalina.core.StandardPipeline$StandardPipelineValveContext.invokeNext(StandardPipeline.java:641)
at org.apache.catalina.core.StandardPipeline.invoke(StandardPipeline.java:480)
at org.apache.catalina.core.ContainerBase.invoke(ContainerBase.java:995)
at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:174)
at org.apache.catalina.core.StandardPipeline$StandardPipelineValveContext.invokeNext(StandardPipeline.java:643)
at org.apache.catalina.core.StandardPipeline.invoke(StandardPipeline.java:480)
at org.apache.catalina.core.ContainerBase.invoke(ContainerBase.java:995)
at org.apache.coyote.tomcat4.CoyoteAdapter.service(CoyoteAdapter.java:223)
at org.apache.coyote.http11.Http11Processor.process(Http11Processor.java:594)
at org.apache.coyote.http11.Http11Protocol$Http11ConnectionHandler.processConnection(Http11Protocol.java:392)
at org.apache.tomcat.util.net.TcpWorkerThread.runIt(PoolTcpEndpoint.java:565)
at org.apache.tomcat.util.threads.ThreadPool$ControlRunnable.run(ThreadPool.java:619)
at java.lang.Thread.run(Thread.java:536)

非常感谢你,我明白了。

TO:Robbin

我对您所说的不应该使用 narrow 一说有些不解。

我印象中,narrow 方法是由 JDK 提供的,和 Tomcat 是不是 Servlet Container 好像没有关系~~!!!

按道理说, 纯 RMI 走的是 JRMP 协议,可以直接用动态造型(加"("和")")将对象从 java.lang.Object 造型回原来的对象。
而如果是走的 RMI-IIOP 的话,则应该使用 narrow 方法来处理。

J2EE 中 RMI 规范里实际上是建议尽量使用 narrow 方法的。这也是 EJB 技术发展的需求所致。

难道在 Weblogic 中使用了该方法会有什么奇怪的现象发生? 我没有这方面的经验,不知道 Robbin 能不能 share 一下你的经验呢?

使用narrow是RMI规范所要求的,可以保证在不同的App Server之间移植的兼容性,正常情况下当然应该用。但是Weblogic的情况有点不同。

weblogic的RMI协议采用的是自己的t3协议,而不是标准的IIOP,据说性能好很多,但是在跨App Server调用的时候容易出现不兼容的问题。因为我对Weblogic的t3协议也不大懂,所以也说不出所以然来。

虽说javax.rmi.PortableRemoteObject.narrow()方法是JDK提供的,但实际上你打开源码看看,就可以发现narrow方法实际是在调用javax.rmi.CORBA.PortableRemoteObjectDelegate.narrow()方法的,而PortableRemoteObjectDelegate只是一个interface而已,还是要靠具体的App Server来提供该接口的实现类,所以绕了一圈narrow还是Weblogic实现的,而Weblogic的narrow是不做任何事情直接返回,所以在Weblogic来说,用不用narrow都一样。因此在Tomcat里面去lookup Weblogic上的EJB,找到的Home stub你不用narrow也完全没有问题。

但如果Tomcat自己提供了PortableRemoteObjectDelegate接口的实现类,或者Tomcat自己没有提供实现类同时Tomcat也找不到该接口的weblogic实现类,你还硬要用narrow()的话,肯定会报错。我推测Tomcat应该是后者的情况,因为如果是前者的话,应该抛出一个协议异常之类的错误,但实践证明是Tomcat中使用了narrow以后会报NullPointerException异常,这说明是Tomcat找不到接口的实现类,接口工厂创建了一个Null对象返回造成的。

Resin也比较特殊,在很早的版本就可以作为Weblogic的Web层来用了。可能Resin专门下过功夫针对Weblogic的EJB调用调整过代码也说不定。

我记得 Weblogic 好像是没有 RMI-IIOP 协议的。
就 JRMP 来说,直接返回对象是对的,这也是确保 narrow 方法能正确运行的一种实现方式。

就你说的Tomcat会产生NullPointerException错误,我同意你的说法,估计还是 weblogic 相关的 class 文件没有完全 import 到 client 中去而造成的。

等下我去装个 Weblogic 来试试,验证一下,你使用的是哪一款的 Weblogic? 我将使用 wls7.0 做个试验

建议你用Weblogic8.1,我当时用的也是8.1。7.0的包没有分离出来一个比较完整的client包,比较麻烦,把整个weblogic.jar都import,会冲突。

我实验过了,是可以的。

先说说我的实验,

[1] 我使用最简单的 HelloWorld EJB 来做测试。
EJB的 coding 和配置就省去了,第一步的结果是 EJB 正常 deploy 到了 weblogic 中。

[2] 我在 tomcat 中写了一个 test.jsp


<%@page import="java.util.*"%>
<%@page import=
"javax.naming.*"%>
<%@page import=
"javax.rmi.*"%>
<%@page import=
"demo.*"%>
<%
Hashtable env = new Hashtable(5);
env.put(Context.INITIAL_CONTEXT_FACTORY,
"weblogic.jndi.WLInitialContextFactory");
env.put(Context.PROVIDER_URL,
"t3://test-server:7001");
Context ctx = new InitialContext(env);
HelloHome helloHome = (HelloHome)PortableRemoteObject.narrow(ctx.lookup(
"Hello"),HelloHome.class);
Hello hello = helloHome.create();
out.println(
"REMOTE_RESULT:"+hello.sayHello());
%>

[3] 第三步,我在 $TOMCAT_HOME/webapps/ROOT/WEB-INF 下建了个 lib 目录,将生成的 EJB 的 Remote 和 Home 接口打成包,放入其中。

[4] 第四步,将 $WLS_HOME/weblogic700/server/lib/weblogic.jar copy 到 第三步的 lib 中。

[5] 正如 Robbin 所说的,Weblogic.jar 中有 servlet 相关的类,与Tomcat 的冲突,这会导致 weblogic.jar 整个 jar 都不能放入 WebClassLoader 中,最终导致 test.jsp 运行失败。因为连 javax.ejb 都找不到,当然不能运行。所以,要对 weblogic.jar 做适当的调整,我用 winrar 打开 weblogic.jar 然后去掉了 javax.servlet 这个包里的所有文件。

[6] 启动 Tomcat,一切正常,测试通过

说明:
那个 NullPointerException 错误我的理解是:因为缺少相应的 Weblogic 类造成的。

分析是这样的:
如果不定指javax.rmi.CORBA.PortableRemoteObjectClass 的实现类时,JDK会使用 com.sun.corba.se.internal.javax.rmi.PortableRemoteObject 来做为 defaul 实现。
但是因为 Weblogic 使用的协议是 t3 协议,SUN 的 JDK 没法认识与它交互的数据是什么东西,于是只好返回一个 Null Object.

我觉得 weblogic 这一点做得没有 JBoss 好,它的 weblogic.jar 太大了,不适于在第三方ServletContainer 上运行。看起来像是 BEA 没想过让别人在 Weblogic 以外调用 EJB :( .

>>我用 winrar 打开 weblogic.jar 然后去掉了 javax.servlet 这个包里的所有文件

这个办法好毒啊,不过很好,很方便,哈哈!但我估计瘦身的不彻底会有比较大的隐忧,像weblogic.servlet这样的包也必须删除。

我@段rg也遇到了同拥},今天看到@N子才解Q了}.非常感x.

本来我是在chinajavaworld上看到有人问这个问题,我觉得奇怪,然后就试了一下,然。我奇怪为什么要放weblogic.jar到tomcat,而不是wlclient.jar,然后我打开wlclient.jar发现没有报错找不到的那个Stub,
于是我也没有试wlclient.jar。
后来我就到网上搜,搜来搜去,就搜到了J道上面的这个帖子,呵呵我也去掉了weblogic.jar中的servlet包,并没有成功,而这里的竟然有人说成功了。我很奇怪,试了又试,就是不能得到Context,就别提narrow了。
最后,我打电话到bea,得到的答案是将wlclient.jar放到tomcat。
wlclient.jar只有200多k,而weblogic.jar有几十M。
发此回复,以免还有人要往tomcat里放weblogic.jar.