6

I need to integrate a REST client into an existing OSGi application implemented using Apache Felix. The REST service is based on RESTeasy implementation (version 2.3.2.Final) of JAX-RS. I created a separate bundle with clients' dependencies, exporting required RESTeasy packages and importing them in the bundle where the client is used, but unfortunately I cannot get it working inside of the OSGi context.

I tried two different approaches. First one using the generic 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);

The error that I get in this case is pretty weird:

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

where I it is known for sure that ApacheHttpClient4Executor implements the ClientExecutor interface.

When I try to use my own REST client wrapper around RESTeasy like this:

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

I get a different exception:

[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

As far as I understand, the LinkageError most probably has to do with the way RESTeasy initializes the RuntimeDelegate using some classloader tricks, which probably fall under the restrictions of OSGi framework. I get the suspicion that the java.lang.ClassCastException mentioned first has the same source.

Is there any way to get RESTeasy working inside of OSGi?

PS: discussion about a similar issue with RESTeasy, but outside of OSGi: java.lang.LinkageError: ClassCastException

Update: these are the libraries included into restclient bundle: activation-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 jettison-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

These are the exports from the restclient bundle: 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

Community
  • 1
  • 1
Alex Fedulov
  • 1,442
  • 17
  • 26
  • what jars are included in your client bundle? – jtahlborn Sep 03 '12 at 14:33
  • those so far: 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 activation-1.1.jar commons-httpclient-3.1.jar – Alex Fedulov Sep 03 '12 at 14:40
  • And inside of my restclient bundle I have tried exporting javax.ws.rs, javax.ws.rs.ext, javax.ws.rs.core, org.jboss.resteasy.client, org.jboss.resteasy.client.core.executors apart from my own classes from myservice-common – Alex Fedulov Sep 03 '12 at 14:46
  • presumably those resteasy jars also exist in some other bundle or elsewhere in the jvm? – jtahlborn Sep 03 '12 at 14:51
  • hmmm...yes, I also got the feeling that this might be the reason. I have just finished extensively searching for any occurrences of RESTeasy of javax.ws.* inside of the project and only found those contained in my resteasy bundle and used by myself. I have also extraced all of the final bundled jars apart from restclient and searched there - found nothing. I have even tried running outside of Eclipse to exclude the possibility of any classpath magic - got the same result. – Alex Fedulov Sep 03 '12 at 15:38
  • In those issue that I referenced above, the problem was that the RuntimeDelegate was loaded twice by two difference classloaders - once by the normal one and once by that of a testing framework. I am wondering what might cause similar effects in OSGi. – Alex Fedulov Sep 03 '12 at 15:42
  • @AlexFedulov: This can be caused by different things, but it usually happens when some component bypasses the OSGi-classloading mechanism by doing something like `getSystemClassloader`. Btw, you should add all the extra information you have posted in the comments to your question. – Björn Pollex Sep 04 '12 at 06:48
  • Thanks for the hint, Björn. I think this is exactly what happens in the RuntimeDelegate findDelegate() method: [LINK](http://grepcode.com/file/repository.jboss.org/nexus/content/repositories/releases/org.jboss.resteasy/jaxrs-api/2.2.2.GA/javax/ws/rs/ext/RuntimeDelegate.java/#109). The question is: is there any way around it? – Alex Fedulov Sep 04 '12 at 08:45

4 Answers4

0

Have a look at the SpringSource Bundle Repo, it's got some very useful pre-built bundles of common libraries including the Apache HTTP Client which we are using (in conjunction with gson) to do our RESTful comms.

James Carr
  • 797
  • 2
  • 10
  • 21
  • Thanks for the tip. I have been there at the time of asking the question and checked again just now - unfortunately there is no RESTeasy bundle. And the actual problem is in fact that RESTeasy uses some class loading by name at runtime when using Proxies. This results in two versions of the same class - one loaded by the OSGi context and the other by RESTeasy itself. – Alex Fedulov Mar 15 '13 at 11:53
0

(unfortunately a legacy module of my project still uses OSGi, but using RESTeasy 3.0.16 now)

When I need to OSGify a dependency my preferred solution now is to wrap it using the excellent Apache Ops4j Pax Tipi project.

The project provides a preconfigured Maven setup (parent POM handles the bundling) and you just have to adapt the GAV coordinates of the original project in a Tipi sub module with a org.apache.ops4j.pax.tipi prefix and build the new bundle project which draws in the original dependency, unpacks and wraps it as OSGi bundle.

You can start from an existing Tipi sub project that best matches your project setup (dependencies, etc.) and adapt any OSGi imports/exports missing (most often, these are created automatically by the maven-bundle-plugin anyway).

This worked quite well for me as long as the original project did not contain too many exotic or malformed dependencies.

However you may run into snags like transitive dependencies using the root package, as I currently experience, which can be a real show stopper (finding out which library is a real nightmare).

Unfortunately, RESTeasy seems to be affected by this, as I get exactly the same error (default package , even after declaring non-test and non-provided dependencies as optional:

The default package '.' is not permitted by the Import-Package syntax.

Upgrading the maven-bundle-plugin to the latest release 3.0.1 yields a different error (even less helpful):

[ERROR] Bundle org.ops4j.pax.tipi:org.ops4j.pax.tipi.resteasy-jaxrs:bundle:3.0.16.Final.1 : Can not parse name from bundle native code header: [ERROR] Error(s) found in bundle configuration

Update seems to be solved by upping Tipi version in POM to 1.4.0, testing...

Gregor
  • 1,297
  • 1
  • 19
  • 31
0

Is RESTEasy mandatory ? I personally use jersey in OSGi and it is working perfectly, both as client and server.

Alexandre Cartapanis
  • 1,513
  • 3
  • 15
  • 19
0

This problem isn't limited to RESTeasy. It also occurs with Jersey.

It is occurring because you have two copies of the JAX-RS classes on the classpath.
You can see this in the LinkageError:

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

i.e. one copy is coming from:

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

and the other from the OSGI bundle.

This causes problems for the RuntimeDelegate class, which by default uses the system class loader to create the RuntimeDelegate implementation (see javax.ws.rs.ext.FactoryFinder).

The problem can also occur if the same jar is loaded via two different class loaders.

There are a couple of workarounds:

  • remove the jaxrs-api-2.3.2.Final.jar from the system class path
  • set the thread context class loader to that of your bundle, prior to making any JAX-RS calls.
    The FactoryFinder will use this to load the RuntimeDelegate.
    To avoid polluting your code with calls to Thread.currentThread().setContextClassLoader(myBundleClassLoader), you can wrap your JAX-RS client using a Proxy. e.g. see the Thread context classloader section of https://puredanger.github.io/tech.puredanger.com/2007/06/15/classloaders/
tanderson
  • 1,169
  • 12
  • 29