OSGi 应用程序内的 REST 客户端

Posted

技术标签:

【中文标题】OSGi 应用程序内的 REST 客户端【英文标题】:REST client inside of OSGi application 【发布时间】:2012-08-28 06:46:24 【问题描述】:

我需要将 REST 客户端集成到使用 Apache Felix 实现的现有 OSGi 应用程序中。 REST 服务基于 JAX-RS 的 RESTeasy 实现(版本 2.3.2.Final)。我创建了一个包含客户端依赖项的单独包,导出所需的 RESTeasy 包并将它们导入到使用客户端的包中,但不幸的是我无法让它在 OSGi 上下文中工作。

我尝试了两种不同的方法。第一个使用通用 ClientRequest:

ClientRequest request = new ClientRequest(MyService.URL_TEST+"/stats");
request.body(javax.ws.rs.core.MediaType.APPLICATION_XML, stats);
ClientResponse<String> response = request.post(String.class);

在这种情况下我得到的错误很奇怪:

[java] java.lang.RuntimeException: java.lang.ClassCastException:
org.jboss.resteasy.client.core.executors.ApacheHttpClient4Executor cannot be cast to 
org.jboss.resteasy.client.ClientExecutor

我确信 ApacheHttpClient4Executor 实现了 ClientExecutor 接口。

当我尝试像这样围绕 RESTeasy 使用自己的 REST 客户端包装器时:

MyService myService = MyServiceClient.getInstance();
myService.saveStatistics(stats);

我得到一个不同的例外:

[java] java.lang.LinkageError: ClassCastException: attempting to
castjar:file:/D:/Development/Eclipses/eclipse_4.2_j2ee_x64/lib/jaxrs-api-2.3.2.Final.jar
!/javax/ws/rs/ext/RuntimeDelegate.classtobundle:
//78.0:1/javax/ws/rs/ext/RuntimeDelegate.class

据我了解,LinkageError 很可能与 RESTeasy 使用一些类加载器技巧初始化 RuntimeDelegate 的方式有关,这可能属于 OSGi 框架的限制。我怀疑首先提到的 java.lang.ClassCastException 具有相同的来源。

有什么方法可以让 RESTeasy 在 OSGi 中工作?

PS:关于 RESTeasy 的类似问题的讨论,但在 OSGi 之外:java.lang.LinkageError: ClassCastException

更新: 这些是包含在 restclient 包中的: 激活-1.1.jar commons-codec-1.2.jar commons-httpclient-3.1.jar commons-io-2.1.jar commons-logging-1.0.4.jar flexjson-2.1.jar httpclient-4.1.2.jar httpcore-4.1 .2.jar javassist-3.12.1.GA.jar jaxb-api-2.2.3.jar jaxb-impl-2.2.4.jar jaxrs-api-2.3.2.Final.jar jcip-annotations-1.0.jar 抛弃-1.3.1.jar jsr250-api-1.0.jar junit-4.10.jar log4j-1.2.14.jar resteasy-jaxb-provider-2.3.2.Final.jar resteasy-jaxrs-2.3.2.Final.jar resteasy -jettison-provider-2.3.2.Final.jar scannotation-1.0.3.jar slf4j-api-1.6.4.jar slf4j-log4j12-1.6.4.jar myservice-common-0.1.0.3.jar my-service- client-0.1.0.3-SNAPSHOT.jar stax-api-1.0-2.jar xmlpull-1.1.3.1.jar xpp3_min-1.1.4c.jar xstream-1.4.2.jar

这些是来自 restclient 包的 exports:javax.ws.rs、javax.ws.rs.ext、javax.ws.rs.core、org.jboss.resteasy.client、org .jboss.resteasy.client.cache, org.jboss.resteasy.client.extractors, org.jboss.resteasy.client.marshallers, org.jboss.resteasy.client.core.executors, javax.xml.bind.annotation, org .jboss.resteasy.plugins.providers、org.jboss.resteasy.plugins.providers.jaxb、org.jboss.resteasy.spi

【问题讨论】:

您的客户端捆绑包中包含哪些 jar? 到目前为止:myservice-client-0.1.0.3-SNAPSHOT.jar myservice-common-0.1.0.3.jar junit-4.10.jar javassist-3.12.1.GA.jar jcip-annotations- 1.0.jar jsr250-api-1.0.jar resteasy-jaxrs-2.3.2.Final.jar jaxrs-api-2.3.2.Final.jar 激活-1.1.jar commons-httpclient-3.1.jar 在我的 restclient 包中,我尝试导出 javax.ws.rs、javax.ws.rs.ext、javax.ws.rs.core、org.jboss.resteasy.client、org. jboss.resteasy.client.core.executors 除了我自己的 myservice-common 类 大概那些 resteasy jars 也存在于其他一些包或 jvm 的其他地方? 嗯...是的,我也觉得这可能是原因。我刚刚完成了对项目内部任何出现的 javax.ws.* RESTeasy 的广泛搜索,并且只找到了包含在我的 resteasy 包中并由我自己使用的那些。除了 restclient 之外,我还提取了所有最终捆绑的 jar 并在那里搜索 - 什么也没找到。我什至尝试在 Eclipse 之外运行以排除任何类路径魔法的可能性 - 得到了相同的结果。 【参考方案1】:

看看SpringSource Bundle Repo,它有一些非常有用的预构建的常用库包,包括我们正在使用的Apache HTTP Client(与gson一起)来执行我们的RESTful通信。

【讨论】:

感谢您的提示。我在问这个问题的时候一直在那里,刚才又检查了一遍——不幸的是,没有 RESTeasy 捆绑包。而实际问题实际上是 RESTeasy 在使用 Proxies 时在运行时使用了一些按名称加载的类。这会产生同一个类的两个版本——一个由 OSGi 上下文加载,另一个由 RESTeasy 本身加载。【参考方案2】:

(不幸的是,我项目的一个遗留模块仍然使用 OSGi,但现在使用的是 RESTeasy 3.0.16)

当我需要对依赖项进行 OSG 化时,我现在首选的解决方案是使用出色的 Apache Ops4j Pax Tipi project 包装它。

该项目提供了预配置的 Maven 设置(父 POM 处理捆绑),您只需在带有 org.apache.ops4j.pax.tipi 前缀的 Tipi 子模块中调整原始项目的 GAV 坐标并构建新的捆绑项目原来的依赖,解压并打包成 OSGi 包。

您可以从与您的项目设置(依赖项等)最匹配的现有 Tipi 子项目开始,并调整任何缺少的 OSGi 导入/导出(大多数情况下,这些都是由 maven-bundle-plugin 自动创建的)。

只要原始项目不包含太多外来或格式错误的依赖项,这对我来说效果很好。

但是,根据我目前的经验,您可能会遇到像 transitive dependencies using the root package 这样的障碍,这可能是一个真正的阻碍(找出哪个库是一场真正的噩梦)。

不幸的是,RESTeasy 似乎受此影响,因为我得到完全相同的错误(默认包,即使在声明非测试和非提供的依赖项为可选之后:

默认包'.' Import-Package 语法不允许。

将 maven-bundle-plugin 升级到最新版本 3.0.1 会产生不同的错误(甚至不太有用):

[错误] 捆绑包 org.ops4j.pax.tipi:org.ops4j.pax.tipi.resteasy-jaxrs:bundle:3.0.16.Final.1 :无法从捆绑包本机代码头解析名称: [错误] 在捆绑配置中发现错误

更新似乎可以通过将 POM 中的 Tipi 版本升级到 1.4.0 来解决,正在测试...

【讨论】:

【参考方案3】:

RESTEasy 是强制性的吗? 我个人在 OSGi 中使用jersey,它作为客户端和服务器都运行良好。

【讨论】:

【参考方案4】:

这个问题不仅限于 RESTeasy。泽西岛也会出现这种情况。

这是因为您在类路径上有两个 JAX-RS 类副本。 您可以在 LinkageError 中看到这一点:

[java] java.lang.LinkageError: ClassCastException: 试图转换 jar:file:/D:/Development/Eclipses/eclipse_4.2_j2ee_x64/lib/jaxrs-api-2.3.2.Final.jar!/javax/ ws/rs/ext/RuntimeDelegate.class 到 bundle://78.0:1/javax/ws/rs/ext/RuntimeDelegate.class

即一份来自:

D:/Development/Eclipses/eclipse_4.2_j2ee_x64/lib/jaxrs-api-2.3.2.Final.jar

另一个来自 OSGI 包。

这会导致 RuntimeDelegate 类出现问题,默认情况下它使用系统类加载器来创建 RuntimeDelegate 实现(请参阅 javax.ws.rs.ext.FactoryFinder)。

如果通过两个不同的类加载器加载同一个 jar,也会出现此问题。

有几种解决方法:

从系统类路径中移除 jaxrs-api-2.3.2.Final.jar 在进行任何 JAX-RS 调用之前,将线程上下文类加载器设置为您的包的加载器。 FactoryFinder 将使用它来加载 RuntimeDelegate。 为避免调用 Thread.currentThread().setContextClassLoader(myBundleClassLoader) 污染您的代码,您可以使用代理包装您的 JAX-RS 客户端。例如请参阅https://puredanger.github.io/tech.puredanger.com/2007/06/15/classloaders/ 的线程上下文类加载器部分

【讨论】:

以上是关于OSGi 应用程序内的 REST 客户端的主要内容,如果未能解决你的问题,请参考以下文章

使用在后台运行的 REST Web 服务的 OSGi 网站

使用 REST 的用户身份验证

如何管理 OSGi 构建依赖项?

websphere OSGi应用环境下服务调用saaj包加载问题分析报告

OSGi:以编程方式决定是不是可以启动捆绑包

覆盖 CORS 配置