X
  • REST |
    Tuesday, March 17, 2015

Jersey 2.x Client on Android - Take 2

In my earlier post i described what we did in Jersey version 2.16 in order to make Jersey client work on Android.
The whole idea was that after separating JAXB providers out from the Jersey core module, things would just work out of the box.
Unfortunately, it turned out the JAXB provider separation was not enough. Basic things worked (see my original post for a test case application), but Jersey was still trying to pull in some types not available in Android environment. Harish complained this caused his application to crash.

In this post i would like to share a workaround, that should hopefully help overcome the above mentioned issue. The good thing is, you do not need to upgrade your Jersey dependency (version 2.16 and 2.17 should work just fine).
The bad thing is the workaround relies on certain Jersey implementation internals, and i can not guarantee it would keep working for future Jersey versions.

The workaround is quite simple and uses HK2 public API, that allows you to "unbind" components from the HK2 service locator on the fly. Here is the code:

public static class AndroidFriendlyFeature implements Feature{
@Overridepublic boolean configure(FeatureContext context) {
context.register(new AbstractBinder() {
@Overrideprotected void configure() {
addUnbindFilter(new Filter() {
@Overridepublic boolean matches(Descriptor d) {
String implClass = d.getImplementation();return implClass.startsWith("org.glassfish.jersey.message.internal.DataSource")
|| implClass.startsWith("org.glassfish.jersey.message.internal.RenderedImage");}});}});return true;}
}

The above feature must be registered with your Jersey client in the following way:
client = ClientBuilder.newClient().register(AndroidFriendlyFeature.class);

I have tested this only locally, so i am not sure the above workaround works for everybody, thus I would like to take this as an opportunity to ask for feedback. Please let me know if you had a chance to check this workaround out
and whether it worked for you. It would help us to come up with a final solution for Android environment in Jersey.

Join the discussion

Comments ( 13 )
  • rdehuyss Saturday, April 4, 2015

    Hi Jakub,

    excellent work - I have the Jersey client working on Android with JSON (jackson).

    Keep up the good work guys!

    Cheers,

    Ronald


  • Jakub Podlešák Friday, April 10, 2015

    Hi Ronald,

    Thank you very much for verifying this! I am glad the thing works for you.

    Cheers,

    ~Jakub


  • Abhijit Gujar Monday, April 20, 2015

    .I referredboth of your post . Dude if you claim something please provide the link of related things such as where is the SNAPSHOT jar ? I searched every where i could not find finally i added jersey client 2.17 but its structure is different ..it cannot find

    import javax.ws.rs.client.Client;

    import javax.ws.rs.client.ClientBuilder;


  • Abhijit Gujar Monday, April 20, 2015

    I tried to load the project and jar in studio and eclipse nothing worked its not recognizing the Client . any guess what might be wrong ? i just added jersey client do i need to add other jar ?


  • guest Monday, April 20, 2015

    Hi Abhijit,

    Jersey snapshots are available at java.net snapshot repository:

    https://maven.java.net/content/repositories/snapshots/org/glassfish/jersey

    Details could be found here:

    https://jersey.java.net/documentation/latest/user-guide.html#d0e570

    javax.ws.rs.client package is part of JAX-RS jar and as such is distributed separately.

    Dependency management should take care of this. The jar is available at:

    http://search.maven.org/#artifactdetails%7Cjavax.ws.rs%7Cjavax.ws.rs-api%7C2.0.1%7Cjar

    HTH,

    ~Jakub


  • guest Monday, April 20, 2015

    Otherwise this is an excerpt from my build.gradle file related to project dependencies:

    dependencies {

    compile fileTree(dir: 'libs', include: ['*.jar'])

    compile 'com.android.support:appcompat-v7:21.0.3'

    compile 'org.glassfish.jersey.core:jersey-client:2.18-SNAPSHOT'

    }

    configurations {

    compile.exclude group: 'javax.inject', module: 'javax.inject'

    }

    repositories {

    mavenLocal()

    }


  • Abhijit Gujar Tuesday, April 21, 2015

    Hi Jakub,

    yes i figured that out found all the dependencies and managed to run the one line code as you did . I am new to REST and web services things so finding it rather difficult. i used simple android httpclient to exchange data using json. can we do same for jersey ? What i needed is to use a objectmapper to send json data. I have code that works perfectly for jersey desktop but android ....:( can you help ?


  • klutzer Tuesday, May 12, 2015

    The solution above has a problem running on Dalvik VM, so still it's not possible to have a jersey client in Android.. The stack trace:

    05-12 00:19:36.283: D/dalvikvm(2446): GC_FOR_ALLOC freed 213K, 9% free 3311K/3636K, paused 2ms, total 2ms

    05-12 00:19:36.295: I/dalvikvm(2446): Failed resolving Lorg/glassfish/jersey/internal/OsgiRegistry; interface 5592 'Lorg/osgi/framework/SynchronousBundleListener;'

    05-12 00:19:36.295: W/dalvikvm(2446): Link of class 'Lorg/glassfish/jersey/internal/OsgiRegistry;' failed

    05-12 00:19:36.295: W/dalvikvm(2446): VFY: unable to find class referenced in signature (Lorg/glassfish/jersey/internal/OsgiRegistry;)

    05-12 00:19:36.295: I/dalvikvm(2446): Failed resolving Lorg/glassfish/jersey/internal/OsgiRegistry; interface 5592 'Lorg/osgi/framework/SynchronousBundleListener;'

    05-12 00:19:36.295: W/dalvikvm(2446): Link of class 'Lorg/glassfish/jersey/internal/OsgiRegistry;' failed

    05-12 00:19:36.295: I/dalvikvm(2446): Could not find method org.glassfish.jersey.internal.OsgiRegistry.getResourceBundle, referenced from method org.glassfish.jersey.internal.l10n.Localizer.localize

    05-12 00:19:36.295: W/dalvikvm(2446): VFY: unable to resolve virtual method 32598: Lorg/glassfish/jersey/internal/OsgiRegistry;.getResourceBundle (Ljava/lang/String;)Ljava/util/ResourceBundle;

    05-12 00:19:36.295: D/dalvikvm(2446): VFY: replacing opcode 0x6e at 0x006b

    05-12 00:19:36.347: D/dalvikvm(2446): GC_FOR_ALLOC freed 252K, 10% free 3545K/3916K, paused 3ms, total 3ms

    05-12 00:19:36.351: D/dalvikvm(2446): GC_FOR_ALLOC freed 106K, 11% free 3657K/4108K, paused 3ms, total 3ms

    05-12 00:19:36.355: D/dalvikvm(2446): GC_FOR_ALLOC freed 183K, 13% free 3667K/4172K, paused 3ms, total 4ms

    05-12 00:19:36.363: D/dalvikvm(2446): GC_FOR_ALLOC freed 211K, 13% free 3707K/4248K, paused 5ms, total 5ms

    05-12 00:19:36.407: D/dalvikvm(2446): GC_FOR_ALLOC freed 291K, 10% free 3928K/4332K, paused 4ms, total 4ms

    05-12 00:19:36.515: D/dalvikvm(2446): GC_FOR_ALLOC freed 265K, 9% free 4175K/4552K, paused 4ms, total 4ms

    05-12 00:19:36.567: D/dalvikvm(2446): GC_FOR_ALLOC freed 232K, 8% free 4456K/4800K, paused 3ms, total 3ms

    05-12 00:19:36.591: W/internal(2446): Cannot find a default implementation of the HK2 ServiceLocatorGenerator

    05-12 00:19:36.591: W/dalvikvm(2446): threadid=11: thread exiting with uncaught exception (group=0xa4ceab20)

    05-12 00:19:36.591: E/AndroidRuntime(2446): FATAL EXCEPTION: AsyncTask #1

    05-12 00:19:36.591: E/AndroidRuntime(2446): Process: com.app.ese, PID: 2446

    05-12 00:19:36.591: E/AndroidRuntime(2446): java.lang.RuntimeException: An error occured while executing doInBackground()

    05-12 00:19:36.591: E/AndroidRuntime(2446):

    at android.os.AsyncTask$3.done(AsyncTask.java:300)

    05-12 00:19:36.591: E/AndroidRuntime(2446):

    at java.util.concurrent.FutureTask.finishCompletion(FutureTask.java:355)

    05-12 00:19:36.591: E/AndroidRuntime(2446):

    at java.util.concurrent.FutureTask.setException(FutureTask.java:222)

    05-12 00:19:36.591: E/AndroidRuntime(2446):

    at java.util.concurrent.FutureTask.run(FutureTask.java:242)

    05-12 00:19:36.591: E/AndroidRuntime(2446):

    at android.os.AsyncTask$SerialExecutor$1.run(AsyncTask.java:231)

    05-12 00:19:36.591: E/AndroidRuntime(2446):

    at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1112)

    05-12 00:19:36.591: E/AndroidRuntime(2446):

    at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:587)

    05-12 00:19:36.591: E/AndroidRuntime(2446):

    at java.lang.Thread.run(Thread.java:841)

    05-12 00:19:36.591: E/AndroidRuntime(2446): Caused by: java.lang.RuntimeException: java.lang.ClassNotFoundException: Provider org.glassfish.jersey.internal.RuntimeDelegateImpl could not be instantiated: java.lang.IllegalStateException: No generator was provided and there is no default generator registered

    05-12 00:19:36.591: E/AndroidRuntime(2446):

    at javax.ws.rs.ext.RuntimeDelegate.findDelegate(RuntimeDelegate.java:152)

    05-12 00:19:36.591: E/AndroidRuntime(2446):

    at javax.ws.rs.ext.RuntimeDelegate.getInstance(RuntimeDelegate.java:120)

    05-12 00:19:36.591: E/AndroidRuntime(2446):

    at javax.ws.rs.core.UriBuilder.newInstance(UriBuilder.java:95)

    05-12 00:19:36.591: E/AndroidRuntime(2446):

    at javax.ws.rs.core.UriBuilder.fromUri(UriBuilder.java:119)

    05-12 00:19:36.591: E/AndroidRuntime(2446):

    at org.glassfish.jersey.client.JerseyWebTarget.<init>(JerseyWebTarget.java:71)

    05-12 00:19:36.591: E/AndroidRuntime(2446):

    at org.glassfish.jersey.client.JerseyClient.target(JerseyClient.java:185)

    05-12 00:19:36.591: E/AndroidRuntime(2446):

    at org.glassfish.jersey.client.JerseyClient.target(JerseyClient.java:70)

    05-12 00:19:36.591: E/AndroidRuntime(2446):

    at com.app.ese.service.RESTService.target(RESTService.java:51)

    05-12 00:19:36.591: E/AndroidRuntime(2446):

    at com.app.ese.service.PerguntaService.listAsJSON(PerguntaService.java:36)

    05-12 00:19:36.591: E/AndroidRuntime(2446):

    at com.app.ese.MainActivity$2.doInBackground(MainActivity.java:119)

    05-12 00:19:36.591: E/AndroidRuntime(2446):

    at com.app.ese.MainActivity$2.doInBackground(MainActivity.java:1)

    05-12 00:19:36.591: E/AndroidRuntime(2446):

    at android.os.AsyncTask$2.call(AsyncTask.java:288)

    05-12 00:19:36.591: E/AndroidRuntime(2446):

    at java.util.concurrent.FutureTask.run(FutureTask.java:237)

    05-12 00:19:36.591: E/AndroidRuntime(2446):

    ... 4 more

    05-12 00:19:36.591: E/AndroidRuntime(2446): Caused by: java.lang.ClassNotFoundException: Provider org.glassfish.jersey.internal.RuntimeDelegateImpl could not be instantiated: java.lang.IllegalStateException: No generator was provided and there is no default generator registered

    05-12 00:19:36.591: E/AndroidRuntime(2446):

    at javax.ws.rs.ext.FactoryFinder.newInstance(FactoryFinder.java:122)

    05-12 00:19:36.591: E/AndroidRuntime(2446):

    at javax.ws.rs.ext.FactoryFinder.find(FactoryFinder.java:225)

    05-12 00:19:36.591: E/AndroidRuntime(2446):

    at javax.ws.rs.ext.RuntimeDelegate.findDelegate(RuntimeDelegate.java:135)

    05-12 00:19:36.591: E/AndroidRuntime(2446):

    ... 16 more

    05-12 00:19:36.591: E/AndroidRuntime(2446): Caused by: java.lang.IllegalStateException: No generator was provided and there is no default generator registered

    05-12 00:19:36.591: E/AndroidRuntime(2446):

    at org.glassfish.hk2.internal.ServiceLocatorFactoryImpl.internalCreate(ServiceLocatorFactoryImpl.java:266)

    05-12 00:19:36.591: E/AndroidRuntime(2446):

    at org.glassfish.hk2.internal.ServiceLocatorFactoryImpl.create(ServiceLocatorFactoryImpl.java:247)

    05-12 00:19:36.591: E/AndroidRuntime(2446):

    at org.glassfish.jersey.internal.inject.Injections._createLocator(Injections.java:138)

    05-12 00:19:36.591: E/AndroidRuntime(2446):

    at org.glassfish.jersey.internal.inject.Injections.createLocator(Injections.java:109)

    05-12 00:19:36.591: E/AndroidRuntime(2446):

    at org.glassfish.jersey.internal.RuntimeDelegateImpl.<init>(RuntimeDelegateImpl.java:63)

    05-12 00:19:36.591: E/AndroidRuntime(2446):

    at java.lang.Class.newInstanceImpl(Native Method)

    05-12 00:19:36.591: E/AndroidRuntime(2446):

    at java.lang.Class.newInstance(Class.java:1208)

    05-12 00:19:36.591: E/AndroidRuntime(2446):

    at javax.ws.rs.ext.FactoryFinder.newInstance(FactoryFinder.java:118)

    05-12 00:19:36.591: E/AndroidRuntime(2446):

    ... 18 more


  • DerManiac Wednesday, May 13, 2015

    Dear Jakub,

    Thanks a LOT for this. This really saved our project. I'm in a class at university where we are working in teams learning to develop Android applications. We have a very tight release schedule (it's also a class on agile software development) so we have very limited time. After setting up a webservice and a client library for a few days, we tried to port it to Android. It simply wouldn't work. We were desperate, wasting an entire day trying to figure it out. Tried different Android versions, different Jersey versions.. The error we got was different from what you reported, so we couldn't find anything online either. Still, I gave your solution a shot and it worked! Your AndroidFriendlyFeature really saved our butts.

    So, in case anyone encounters this error:

    "java.lang.IllegalStateException: Cannot set request property after connection is made"

    while trying to do a post request using Jersey on Android, Jakub's post is the solution! Thank you so very much!


  • Rudi Ziegaus Wednesday, May 27, 2015

    Hi Jakub,

    thanks for your posts. I am very new to REST and Jersey and I am struggling to get this thing running. I have integrated version 2.17 of Jersey and also your class for Android, unfortunately my app hangs at the second line:

    resourceTarget = target.path("auth/text");

    Response response = resourceTarget.request(MediaType.TEXT_PLAIN).get();

    Any ideas what's wrong? in plain Java everything'w working fine, but not so in Android.

    Beyond that I'd also like to incorporate SSL - also no problem in plain Java, but what do I have to do in Android?

    Thanks a lot for your thoughts,

    Rudi


  • Rudi Ziegaus Thursday, May 28, 2015

    Hi Jacub,

    thanks for your posts - I have tried your solution, but unfortunlately it does not work for me (Android 4.4). When I try to perform the request, the app seems to hang, although I have registered the AndroidFriendlyFeature class.

    How can I diagnose what's going on? Any ideas for me?

    Another question: How can I configure Jersey-Client to work with SSL in Android? That seems to be a difficult matter as I have found out so far.

    Thanks a lot,

    Rudi


  • Daniel Gisbert Monday, July 13, 2015

    Hi.

    I've been three days trying to use Jersey within our Andoid App because it seems that we can send multipart messages very easily with Jersey. But we didn't succeed.

    Surprisingly, the project you offered on your first post was working fine on Android Studio but when I adapted it to use it on Eclipse ADT, taking special care of libraries needed for dependencies, it didn't worked.

    I tried everything, but always there was an error:

    Cannot find a default implementation of the HK2 ServiceLocatorGenerator

    07-10 11:41:45.279: E/AndroidRuntime(17116): Caused by: java.lang.RuntimeException: java.lang.ClassNotFoundException: Provider org.glassfish.jersey.internal.RuntimeDelegateImpl could not be instantiated: java.lang.IllegalStateException: No generator was provided and there is no default generator registered

    07-10 11:41:45.279: E/AndroidRuntime(17116):

    at javax.ws.rs.ext.RuntimeDelegate.findDelegate(RuntimeDelegate.java:152)

    07-10 11:41:45.279: E/AndroidRuntime(17116):

    at javax.ws.rs.ext.RuntimeDelegate.getInstance(RuntimeDelegate.java:120)

    07-10 11:41:45.279: E/AndroidRuntime(17116):

    at javax.ws.rs.core.UriBuilder.newInstance(UriBuilder.java:95)

    07-10 11:41:45.279: E/AndroidRuntime(17116):

    at javax.ws.rs.core.UriBuilder.fromUri(UriBuilder.java:119)

    ...

    I'm almost sure that is the same problem described here: http://stackoverflow.com/questions/25740556/error-while-using-jersey-client-in-osgi-no-generator-was-provided

    But I can't find the solution. I am missing something ?

    The libraries included are:

    android-support-v4.jar

    aopalliance-repackaged-2.4.0-b09.jar

    hk2-api-2.4.0-b09.jar

    hk2-locator-2.4.0-b09.jar

    hk2-utils-2.4.0-b09.jar

    javassist-3.18.1-GA.jar

    javax.annotation-api-1.2.jar

    javax.inject-2.4.0-b09.jar

    javax.ws.rs-api-2.0.1.jar

    jersey-client.jar

    jersey-common.jar

    jersey-guava-2.16.jar

    org.osgi.core-4.2.0.jar

    osgi-resource-locator-1.0.1.jar


  • guest Friday, July 24, 2015

    @Daniel Gisbert

    The problem with ServiceLocatorGenerator is due to a bug in Android tools:

    https://code.google.com/p/android/issues/detail?id=59658

    You NEED to have a META-INF/services/org.glassfish.hk2.extension.ServiceLocatorGenerator entry, but the APK build tools you're using will probably INCORRECTLY (re: bug 59658) strip them out. This will break Jersey's dependency injection(s). We were able to work around this by shoving a ServiceLocatorFactoryImpl instance into its own "defaultGenerator" field via Java reflection. This is a bit of a hack but it allowed us to call web services using Jersey Client albeit very slowly. Unfortunately, we could not find a cleaner way to define the dependency via HK2 or otherwise.

    As you found, building an APK using more recent versions of Android Studio or Gradle will work because they properly handle the META-INF folder. They also produce an APK that does not suffer from the performance issue utilizing our Java reflection "hack".


Please enter your name.Please provide a valid email address.Please enter a comment.CAPTCHA challenge response provided was incorrect. Please try again.Captcha
Oracle

Integrated Cloud Applications & Platform Services