WAS上部署Jmx应用中包冲突解决过程

 WAS上部署Jmx应用中包冲突解决过程

近日,在was6.0上面部署基于JMX的一个应用,出现一个JMX类的JMXConnectorFactory.resolveClassLoader()MethodNotFound的错误,但是明明JMX的类也就是jmx-remote.jar包里面的类有该类的方法,如图: 

WAS上部署Jmx应用中包冲突解决过程_第1张图片

 

  在开发机器上的tomcat上跑没有错,为什么布署在was上就有问题,肯定是环境出了问题,幸好有点was部署经验,出现此类java ERROR级别的错误,绝大多数是jar包冲突的的问题,于是打开was控制台,利用was的类装入器查看器发现在was的一个名为management.jar包里面也有同为JMXConnectorFactory的类,于是抓下来一看究竟。


WAS上部署Jmx应用中包冲突解决过程_第2张图片
 

 果然不出所料,没有resolveClassLoader方法,然而management的类加载层次肯定是比jmx-remote.jar的层次高,默认的类加载策略是先加载父类,理说当然,jmx-remote.jar包中的类不可能被加载。

   说起为什么冲突,还得说说历史。Jmxjdk1.4时代是作为sun的一个扩展包存在的,并不是标准的jdk的包,于是乎ibm也有他的jmx的实现,而且包名和sun的一样,要是不一样就不会有冲突了。

  好吧,能不能不使用基于JMX的包里面的实现,用management里面的实现呢,结果很失望,managementRmiConnectorServer根本没有实现。自己照着jmx-remote.jar里面的实现重写?实践了才发现不可能,重写一个得重写几十个类,还不能保证运行正常。

  由于jmxjar包在was上面部署的时候是采用共享包的方式部署的,我首先考虑的是能否考虑这个共享库的加载方式,改成后加载父类。为了便于理解

我们假设javaboot classloader叫做Awas的系统jarclassloader叫做B

共享库的classloader加载C,应用程序的classLoader叫做D。他们的层次如图:


WAS上部署Jmx应用中包冲突解决过程_第3张图片
 

其中C加载器加载jmx-remote.jar,如果能改变C的加载策略,改变为后加载父类,那么就可以避免加载management里面的类了。

 解决方案有两种:

  • 基于 jvm 层次的,在 jvm 上新建一个 classloader ,该 classloader 加载 jmx 共享库,方式为后加载父类。
  •   基于应用层次的,把 jmx 相关的包和应用程序放一起,放到 /WEB-INF/lib 下,改变 web 模块的加载为后加载父类。(由于该应用还有其他的共享包,如果是改变应用程序的加载顺序的话改变的是共享库的加载策略),不明白的话可以用 was 试试,然后看看类装入器就一目了然了。

 

  还有一种可能想到的解决方案,就是能不能在AB之间有一个classload E,让E去加载jmx相关的类。这样要找的肯定是E加载器中的jmx相关的类了。理想是好了,而且我们确实可以把E加载器放在AB之间。

  可以设置-Djava.ext.dirs变量或者设置-Djava.class.path变量,多个目录用”:”隔开,这种设置的前提是要知道变量的值是多少,否则就破坏系统的设置了。

一切看起来完美,布署运行,出现以下错误:

 

Caused by: java.lang.LinkageError: Class javax/management/remote/JMXServiceURL violates loader constraints:  parent and child already loaded different classes

         at java.lang.ClassLoader.defineClass0(Native Method)

         at java.lang.ClassLoader.defineClass(ClassLoader.java(Compiled Code))

         at java.security.SecureClassLoader.defineClass(SecureClassLoader.java(Compiled Code))

         at java.net.URLClassLoader.defineClass(URLClassLoader.java(Compiled Code))

         at java.net.URLClassLoader.access$500(URLClassLoader.java(Inlined Compiled Code))

         at java.net.URLClassLoader$ClassFinder.run(URLClassLoader.java(Compiled Code))

         at java.security.AccessController.doPrivileged1(Native Method)

         at java.security.AccessController.doPrivileged(AccessController.java(Compiled Code))

         at java.net.URLClassLoader.findClass(URLClassLoader.java(Compiled Code))

         at java.lang.ClassLoader.loadClass(ClassLoader.java(Compiled Code))

         at java.lang.ClassLoader.loadClass(ClassLoader.java(Compiled Code))

         at javax.management.remote.JMXConnectorServerFactory.newJMXConnectorServer(JMXConnectorServerFactory.java:275)

 

灰常底层的错误啊!googlebaidu之。原因是这样的

Classloader D 调用一个方法JMXConnectorServerFactory. newJMXConnectorServer(JMXServiceURL serviceURL,Map environment,

                MBeanServer mbeanServer)来新建一个ConnectorServer

这个方法可以从spring的源码中看到。先看看目前的类加载器:

 


WAS上部署Jmx应用中包冲突解决过程_第4张图片
 

D会去找JMXConnectorServerFactory类,由于B中没有所以找到了E中的JMXConnectorServerFactory,同时也会去找JMXServiceURL,找到B的时候,这里是关键,由于Classloader B (WAS引导程序扩展类装入器)出于保护was类的原因,其策略是后加载父类(parent_last),所以找到了B中的JMXServiceURL。然而实际上,JMXConnectorServerFactory.newJMXConnectorServer方法需要的是E中的JMXServiceURL类,并且E中的JMXServiceURLB中的JMXServiceURL字节码不一样,所以会出现violates loader constraints:  parent and child already loaded different classes这种错误,也就是类装入器约束违反。

如果还不清楚的话,可以参考:

 

写道
http://www.ibm.com/developerworks/cn/java/j-dclp4/

 

既然绕不过B,还是只能有上述两种解决方案了。两种比较而言方案二更安全点,毕竟jvm上面跑的不只是这一个应用,动配置的事情能影响小就影响小点吧。

 

 

 

 

你可能感兴趣的:(Java编程)