背景

某项目接口采用jersey开发,部署在weblogic上,升级JDK至jdk1.7.0_191后所有接口无法访问,返回404 Not Found。

测试

首先怀疑是JDK升级后部分java类初始化失败导致404,但重启服务器,重启应用,重新部署应用都未报错,说明应用启动是成功的,一开始也没怀疑到jersey框架上,因此走了很多弯路,这里就不赘述。到后面所有的猜测都验证后,还是没有解决,于是决定本地用jersey写一个最简单的接口部署上去看下结果。

  • 接口部署到线上weblogic访问404
  • 接口部署到本地tomcat,访问正常
  • 接口部署到其他weblogic环境,访问正常,jdk版本为jdk1.7.0_80
  • 正常的环境将jdk切换至jdk1.8.0_171,访问404

基于以上实验可以看出,**jersey在weblogic环境下,高版本的jdk会报404,tomcat没有这问题**,确定了问题后排查方向就比较清楚,就是搞清楚为什么高版本的jdk会报错。

解决

以下是jersey的web.xml配置

<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"
         version="2.5" xmlns="http://java.sun.com/xml/ns/javaee">
    <servlet>
        <servlet-name>jersey</servlet-name>
        <servlet-class>org.glassfish.jersey.servlet.ServletContainer</servlet-class>
        <init-param>
            <param-name>jersey.config.server.provider.packages</param-name>
            <param-value>com.xx.poc</param-value>
        </init-param>
        <load-on-startup>1</load-on-startup>
    </servlet>
    <servlet-mapping>
        <servlet-name>jersey</servlet-name>
        <url-pattern>/*</url-pattern>
    </servlet-mapping>
</web-app>

jersey工作原理很简单,所有的请求(/*)都由统一的servlet(org.glassfish.jersey.servlet.ServletContainer)进行处理,jersey会扫描jersey.config.server.provider.packages下所有的类,ServletContainer根据url进行分发请求处理。所以首先从ServletContainer入手,通过查看jersey源码找到以下代码(位于org.glassfish.jersey.server.ServerRuntime.process)

final Ref<Endpoint> endpointRef = Refs.emptyRef();
final RequestProcessingContext data = Stages.process(context, requestProcessingRoot, endpointRef);
final Endpoint endpoint = endpointRef.get();
if (endpoint == null) {
    // not found
    throw new NotFoundException();
}

看来就是在这里导致了404,进一步追踪,以下代码set了一个空,也就是Stages.extractInflector(lastStage)返回了空

inflectorRef.set(Stages.extractInflector(lastStage));

使用arthas进行调试证实了猜想

watch org.glassfish.jersey.process.internal.Stages extractInflector "{returnObj}" -x 2
Press Q or Ctrl+C to abort.
Affect(class-cnt:1 , method-cnt:1) cost in 69 ms.
ts=2020-11-21 13:14:36; [cost=0.652066ms] result=@ArrayList[
    null,
]

原因应该是web.xml中jersey.config.server.provider.packages不生效,rest相关类没有被扫描到,这里也找到了扫描的代码位于org.glassfish.jersey.server.ResourceConfig.scanClasses

final String[] classNames = parsePropertyValue(ServerProperties.PROVIDER_CLASSNAMES);
if (classNames != null) {
    for (final String className : classNames) {
        try {
            result.add(_state.getClassLoader().loadClass(className));
        } catch (final ClassNotFoundException e) {
            LOGGER.log(Level.CONFIG, LocalizationMessages.UNABLE_TO_LOAD_CLASS(className));
        }
    }
}
final String[] packageNames = parsePropertyValue(ServerProperties.PROVIDER_PACKAGES);
if (packageNames != null) {
    final Object p = getProperty(ServerProperties.PROVIDER_SCANNING_RECURSIVE);
    final boolean recursive = p == null || PropertiesHelper.isProperty(p);
    rfs.add(new PackageNamesScanner(packageNames, recursive));
}

从代码上看,jersey支持两种定义扫描类方式

  • 直接定义类名
  • 定义包路径,扫描该路径下所有类

包路径扫描由org.glassfish.jersey.server.internal.scanning.PackageNamesScanner负责,遗憾的是最终还是未找到未扫描到的原因,因为扫描过程程序启动时就完成了,很难使用arthas进行跟踪调试。但我们也从代码中发现了另外一种方法,就是直接指定rest类。修改web.xml添加ServerProperties.PROVIDER_CLASSNAMES配置,也就是jersey.config.server.provider.classnames

<init-param>
     <param-name>jersey.config.server.provider.classnames</param-name>
     <param-value>com.fuyao.poc.JerseyService</param-value>
 </init-param>

重新部署,问题解决

## 总结

对jersey的印象一直就不大好,兼容性差,依赖多,很容易出问题,这次更是验证了jersey确实不是一个成熟的框架,最终也没找到根本原因算是一个遗憾,另一方面,weblogic的兼容性也是出名的差,很多应用在tomcat上跑的好好的,在weblogic就是有问题,还要依靠各种奇技淫巧解决,比如spring boot。很大一部分原因是weblogic并不像tomcat那样只提供环境,weblogic本身自带另很多库,导致和应用的jar包冲突,所以在部署weblogic应用时,最好在weblogic.xml上加上以下配置,减少冲突。

<?xml version='1.0' encoding='UTF-8'?>
<wls:weblogic-web-app
        xmlns:wls="http://xmlns.oracle.com/weblogic/weblogic-web-app"
        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
        xsi:schemaLocation="http://java.sun.com/xml/ns/javaee
                http://java.sun.com/xml/ns/javaee/ejb-jar_3_0.xsd
                http://xmlns.oracle.com/weblogic/weblogic-web-app
                http://xmlns.oracle.com/weblogic/weblogic-web-app/1.4/weblogic-web-app.xsd">
    <wls:container-descriptor>
       <wls:prefer-web-inf-classes>true</wls:prefer-web-inf-classes>
       <wls:show-archived-real-path-enabled>true</wls:show-archived-real-path-enabled>
    </wls:container-descriptor>
    <wls:context-root>appContextRoot</wls:context-root>
</wls:weblogic-web-app>
Trackback

no comment untill now

Add your comment now