Monday 14 March 2016

JDK6和JBoss 5.0.0.GA的不兼容问题

  今天研究EJB3的web service过程中,出现一些问题,解决后觉得挺有心得,所以记录下来,已供将来参考。

环境:
  JDK6, JBoss 5.0.0.GA,Eclipse Helios。JBoss整合在Eclipse中。

问题:
  客户端访问部署在JBoss中的web service时,出现以下异常
java.lang.UnsupportedOperationException: setProperty must be overridden by all subclasses of SOAPMessage
 at javax.xml.soap.SOAPMessage.setProperty(SOAPMessage.java:445)
......

分析:
  JDK6中提供了web service相关的jar包,但和JBoss并不兼容。从这个例子中来看,就是JDK提供了一个SOAPMessage的类,同时JBoss也提供了同名的类(显然,内容是不一样的)。JBoss本意是要加载自己的类,但JVM classloading的顺序就是先加载JDK自己的核心类,再加载server的类。没办法,就这样被JDK的类捷足先登了。所以解决思路就2个,一个就是去除JDK6的SOAPMessage的类,如果只剩下一个类,那JVM也无从选择了。另一个思路就是设法使JBoss的SOAPMessage的类加载在先。

解决方案1:
  为了去除JDK6的SOAPMessage的类,不可能说去把这个jar包删掉,可行的方法就是放弃JDK6,而使用JDK5,JDK5中是没有这个类的。但这样我们就无法使用JDK6中的新功能了,以这样的代价去解决这个问题并不能令人满意。

解决方案2:
  在JBoss的路径中,找出包含SOAPMessage的jar包——jbossws-native-saaj.jar,放入<JAVA_HOME>\jre\lib\endorsed目录中。(如没有这个目录,则自己建立)

  为什么要放在endorsed目录下呢?原来放在这个目录下的class是优先于JDK核心类先被加载的。举例来说,大家都知道String类是final,所以无法继承。但你如果就是铁了心了要改写这个String类,也是有办法的,就是把这个新写的类打包成jar文件后放入endorsed目录下,这样JVM就会优先加载你这个String类了。

  所以这样一来,JBoss的SOAPMessage类就先被加载,这样就解决了问题。

  但这种解决方案也是有问题的,人家JDK6开发出SOAPMessage这个包终归是有用意的,它或许跟JBoss不兼容,但可能就跟Glassfish兼容的很好,如果以后要在这个机器上跑2个服务器,该怎么办呢?

  以我的知识所及,本来是想不出第三种,也就是最好的解决方案。但我在论坛中,看到有些人在同样的环境下运行的很好,完全没有发生错误。这使我很疑惑,究竟我跟他们之间有什么区别呢?

  抱着试试看的态度,我将JBoss的启动改为Commond Line方式启动,也即是双击bin目录下的run.bat。然后调用web service客户端,居然毫无问题。经过反复试验后,证明JBoss在Eclipse中运行就会出错,而在Commond Line下启动就没问题。这不得不使我去探索这两种启动方法到底有什么区别,也终于使我研究出了——

解决方案3:
  在Eclipse中,修改JBoss启动的VM Arguments,默认的是-Dprogram.name=run.bat -server -Xms128m -Xmx512m -XX:MaxPermSize=256m,在下面再加一句话

  -Djava.endorsed.dirs="D:\jboss-5.0.0.GA\lib\endorsed" (假设JBOSS_HOME=D:\jboss-5.0.0.GA)

  这句话的意思就是告诉JVM,这次启动,你要把D:\jboss-5.0.0.GA\lib\endorsed当作你的endorsed目录来启动。在这里你完全可以猜出D:\jboss-5.0.0.GA\lib\endorsed目录下一定有jbossws-native-saaj.jar。

  这么做的好处就是让这个配置只应用于JBoss的启动上,甚至说是只限于这个JBoss Server的instance。我完全可以再建立一个JBoss server,不加上那个参数,那么新的JBoss server还是只会以默认的方式启动。

  说实话,为了找出两种JBoss启动的不同,费了我可大的劲了。最后我是把run.bat中的@echo off去掉,让它把执行的话一句一句打印出来,最后才找到了这句参数。而一旦找到了这句话,一瞬间我就豁然开朗了。

总结:
  通过这次问题处理,一是巩固了对endorsed目录的作用的理解(原先只是模模糊糊大概知道那么回事),二是知道了-Djava.endorsed.dirs=...的用法,三是对类加载的顺序有了进一步的了解。

No comments:

Post a Comment