2016年3月18日星期五

EAP_051:从 exclude org.apache.httpcomponents 不起作用说起

环境:OS X EI Capitan 10.11.6 + JBoss EAP 6.4.0 + Redis 3.2.3

问题重现步骤:
(1)启动 redis
(2)启动 jboss eap
(3)部署 cas.war

控制台抛出异常,web 应用发布不成功。
ERROR [org.apache.catalina.core.ContainerBase.[jboss.web].[default-host].[/cas-bad]] (ServerService Thread Pool -- 102) JBWEB000287: Exception sending context initialized event to listener instance of class org.springframework.web.context.ContextLoaderListener: org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'logoutManager' defined in ServletContext resource [/WEB-INF/spring-configuration/applicationContext.xml]: Cannot resolve reference to bean 'noRedirectHttpClient' while setting constructor argument; nested exception is org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'noRedirectHttpClient': FactoryBean threw exception on object creation; nested exception is java.lang.NoSuchMethodError: org.apache.http.impl.client.HttpClientBuilder.setSSLHostnameVerifier(Ljavax/net/ssl/HostnameVerifier;)Lorg/apache/http/impl/client/HttpClientBuilder;

经过检查,发现是 JBoss EAP 本身自带的 httpcomponents 版本太旧,没有方法 setSSLHostnameVerifier,于是导致异常。

于是在WEB-INF/lib 下增加高版本的 httpcomponents,并增加 WEB-INF/jboss-deployment-structure.xml,内容如下:
<?xml version="1.0" encoding="UTF-8"?>
<jboss-deployment-structure>
<deployment>
<exclusions>
 <module name="org.apache.httpcomponents"/>
<module name="org.apache.commons.logging" />
<module name="org.apache.log4j" />
<module name="org.slf4j" />
<module name="org.slf4j.impl" />
<module name="org.jboss.logging.jul-to-slf4j-stub" />
<module name="org.jboss.stdio" />
   <module name="org.jboss.netty"/>
</exclusions>
</deployment>
</jboss-deployment-structure>

重新打成 war 包,jar -cvf cas.war META-INF/ WEB-INF/,并重新部署。

发现问题依旧,这是怎么回事?

修改 standalone.conf,增加 -verbose:class 参数
JAVA_OPTS="-Xms1303m -Xmx1303m -XX:MaxPermSize=256m -Djava.net.preferIPv4Stack=true -verbose:class"

发现类的加载路径中,加载的是 modules 下面的httpcomponents,难道说 exclude org.apache.httpcomponents 没起作用?!

使用命令查找 modules 目录下,所有含有name="org.apache.httpcomponents" export="true"的xml文件。
grep -n -R --include="*.xml" 'name="org.apache.httpcomponents" export="true"' .
输出如下:
./system/layers/base/org/jboss/resteasy/resteasy-jaxrs/main/module.xml:47:        <module name="org.apache.httpcomponents" export="true"/>

打开 ./system/layers/base/org/jboss/resteasy/resteasy-jaxrs/main/module.xml,内容如下:
<module xmlns="urn:jboss:module:1.1" name="org.jboss.resteasy.resteasy-jaxrs">

    <resources>
        <resource-root path="async-http-servlet-3.0-2.3.10.Final-redhat-1.jar"/>
        <resource-root path="resteasy-jaxrs-2.3.10.Final-redhat-1.jar"/>
        <!-- Insert resources here -->
    </resources>

    <dependencies>
        <module name="javax.api"/>
        <module name="javax.annotation.api"/>
        <module name="javax.activation.api"/>
        <module name="javax.ws.rs.api"/>
        <module name="org.apache.commons.codec" />
        <module name="org.apache.httpcomponents" />
        <module name="org.apache.log4j"/>
        <module name="org.scannotation.scannotation" />
        <module name="org.slf4j" />
        <module name="javax.servlet.api"/>
        <module name="org.apache.commons.io"/>
        
        <!-- exported -->
        <module name="org.apache.httpcomponents" export="true"/>
        <module name="org.jboss.logging" export="true"/>
    </dependencies>
</module>

也就是说,虽然在 WEB-INF/jboss-deployment-structure.xml 中 exclude org.apache.httpcomponents,但是由于加载了 org.jboss.resteasy.resteasy-jaxrs module,而 rest easy 加载了org.apache.httpcomponents,所以最终 org.apache.httpcomponents,还是被加载了。

找到了问题根源,修改方法也简单了,
只要在 WEB-INF/jboss-deployment-structure.xml 中,再增加一行,把 rest easy 给 exclude 就好了。
<module name="org.jboss.resteasy.resteasy-jaxrs"/>

当然,你也可以把  ./system/layers/base/org/jboss/resteasy/resteasy-jaxrs/main/module.xml 中的 <module name="org.apache.httpcomponents" export="true"/> 这一行的 export="true" 改成 "false"。
但是由于 modules 下面的东西是系统自带的,在以后的升级中,可能会被改动,所以用户最好还是不要修改。

没有评论: