Java ServiceLoaders、Tomcat、Apache CXF 和 Metro

Java ServiceLoaders, Tomcat, Apache CXF and Metro

我必须使用部署在 Tomcat 上的 Web 应用程序从外部 SOAP Web 服务获取一些信息。此外部 Web 服务需要具有相互身份验证的 TLS。

作为起点,我在我的台式电脑上安装了 Tomcat 7.0.96,并使用外部 Web 服务 wsdl 文件,我建立了一个虚拟的 Web 服务,模仿真实的服务,查询时返回一些硬编码对象。我用过 Apache CXF 3.3.8 .

我还有一个 Web 应用程序,它充当另一个 Web 应用程序的代理,以及外部(测试和生产)和虚拟(本地)Web 服务的客户端。

代理网络应用程序从虚拟网络服务请求对象。使用 CXF 3.3.8 样本中存在的密钥库确保相互 TLS 身份验证。 在我的台式电脑上,一切正常,我的虚拟网络服务按预期提供对象。

当我要求在我无法控制的测试服务器上部署代理 Web 应用程序时出现问题。此服务器在 CentOS 7 上运行 Tomcat 7.0.76。当代理 Web 应用程序尝试创建外部 Web 服务客户端时,抛出异常:

java.lang.ClassCastException: com.sun.xml.ws.client.sei.SEIStub 无法转换为 org.apache.cxf.frontend.ClientProxy

我搜索了有关 Java 代理和服务加载程序的文档。测试服务器在 /tomcat/lib 文件夹中有三个 jaxws-xxx.jars。据我所知,这些 jar 是 Metro 网络服务 API 和实现。我在本地 tomcat/lib 文件夹中有这些副本,我可以重现该问题。

我在我的代理 Web 应用程序中包含了一个 Context.xml 文件。根据我在 Context.xml 中包含一个 Loader 元素,将 Loader 属性委托设置为 true 或 false,我能够重现或解决问题。

但是当我在测试服务器中部署代理网络应用程序时,包括 Context.xml 所解释的,上述异常总是出现。

我在代理 Web 应用程序打印中包含了一些行,以记录在 ServiceLoader Java class 迭代器中显示的实现 classes。在我的本地 PC 上,com.sun.xml.ws.spi.Providerimpl 出现在 org.apache.cxf.jaxws.spi.ProviderImpl 之前或之后,这取决于加载程序的“委托”属性值,正如预期的那样。

但是使用相同的 Context.xml webapp 文件,在测试服务器上,sun 的 spi 总是出现在 cxf 之前。

有人遇到过这个问题吗?有什么建议么?提前致谢

你应该问外部 Tomcat 管理员,他为什么要在公共类加载器中包含 JAX-WS RI 库。恕我直言,他们不应该在那里,但在使用它的应用程序中。

然而,您的应用程序类加载器应该选择 JAX-WS 的 CXF 实现,除非:

  • <Host> 容器有 deployXML="false",它会忽略应用程序中的 META-INF/context.xml 文件。如果是这种情况,您可以手动将其复制到 $CATALINA_BASE/conf/<enginename>/<hostname>/<contextname>.xml
  • $CATALINA_BASE/conf/context.xml 有一个 <Loader> 组件 delegate="true"