Make Retrofit ready for usage in OSGi

Retrofit is a really great library for addressing  REST APIs. It is often used for Android apps, because it is really lightweight and easy to use.

I’d also love to use this library for my Eclipse 4 RCP applications, so let’s make use of retrofit also here.

So download the retrofit artefacts and make use of it. But wait..! For Eclipse applications we need OSGi bundles rather than usual Java artefacts. When looking at the MANIFEST.MF file of the retrofit jar archive there isn’t any OSGi bundle meta data.

Fortunately there are many tools out there to convert plain Java artefacts into OSGi bundles, e.g., p2-maven-plugin(Maven) or bnd-platform(Gradle).

Since I am involved in the Buildship development (Gradle tooling for Eclipse) and we now also offer Gradle trainings besides our Maven trainings, I chose the bnd-platform plugin for Gradle.

The build.gradle file then looks like this:

buildscript {
	repositories {
		mavenCentral()
	}
	dependencies {
		classpath 'org.standardout:bnd-platform:1.2.0'
	}
}

apply plugin: 'org.standardout.bnd-platform'

repositories {
	mavenCentral()
}

platform {
	bundle 'com.squareup.retrofit:retrofit:2.0.0-beta2'

	bundle 'com.squareup.retrofit:converter-gson:2.0.0-beta2'
}

When Gradle has been setup properly, the desired bundles can be converted with the bundles task from the org.standardout.bnd-platform plugin:

/retrofit-osgi-convert$ ./gradlew bundles

By running the bundles task retrofit, a json converter, in this case GSON, and the transitive dependencies are available as converted OSGi bundles in the /retrofit-osgi-convert/build/plugins folder.

See Gradle tutorial for further information.

When adding these converted bundles to the target platform of a Eclipse RCP application it should usually work out of the box.

But …! After adding the retrofit and gson converter bundles as dependencies to my plugin’s MANIFEST.MF file I still get compile errors. 🙁

So what went wrong? Basically 2 things! The first problem is obvious, because when looking into the generated MANIFEST.MF meta data of retrofit there is an import for the android.os package. This import was added automatically during the conversion. The readme of the bnd-platform plugin explains how to configure the imports.

The second thing is that the retrofit and its converter bundles have split packages, which is fine for plain Java projects, but not for OSGi bundles. So the split package problem has also to be resolved. See https://github.com/SimonScholz/retrofit-osgi#make-use-of-retrofit-in-osgi

Fortunately this can also be configured in the build.gradle file:

platform {

	// Convert the retrofit artifact to OSGi, make android.os optional and handle the split package problems in OSGi
	bundle('com.squareup.retrofit:retrofit:2.0.0-beta2'){
		bnd {
			optionalImport 'android.os'
			instruction 'Export-Package', 'retrofit;com.squareup.retrofit=split;mandatory:=com.squareup.retrofit, retrofit.http'
	    	}
	}

	// Convert the retrofit gson converter artifact to OSGi and handle the split package problems in OSGi
	bundle('com.squareup.retrofit:converter-gson:2.0.0-beta2') {
		bnd{
			instruction 'Require-Bundle', 'com.squareup.retrofit'
			instruction 'Export-Package', 'retrofit;com.squareup.retrofit.converter-gson=split;mandatory:=com.squareup.retrofit.converter-gson'
		}
	}

	// You can add other converters similar to the gson converter above...
}

The actual build.gradle file can be found on Github.

After resolving these problems, no compile errors are left. 🙂 But when running the application an java.lang.IllegalAccessError: tried to access class retrofit.Utils from class retrofit.GsonResponseBodyConverter error occured.

The cause for this are the modifiers used in the retrofit.Utils class, which itself is package private and also its closeQuietly method is package private. So even with these split package rules being applied this package private access rules prohibit the usage of the closeQuietly method from the converters bundles (gson, jackson etc.).

Now comes the part, why I love open source that much. I checked out the retrofit sources made some changes, build retrofit locally, tried my local OSGi version with my changes and finally provided a fix for this. See https://github.com/square/retrofit/pull/1266. Thanks a lot @JakeWharton for merging my pull request that fast.

Retrofit and its GSON converter can already be obtained from bintray as p2 update site: https://dl.bintray.com/simon-scholz/retrofit-osgi/

For further information and a complete example please refer https://github.com/SimonScholz/retrofit-osgi

This repository contains the conversion script for making OSGi bundles from retrofit artifacts and  a sample application, which shows how to make use of retrofit in an Eclipse 4 RCP application. Just clone the repository in an Eclipse workspace, activate the target platform from the retrofit-osgi-target project and start the product in the de.simonscholz.retrofit.product project.

Retrofit and Eclipse 4

Retrofit and Eclipse 4

Feedback is highly appreciated.

Happy retrofitting in your OSGi applications 😉

This entry was posted in Eclipse, Java, OSGi, Simon Scholz. Bookmark the permalink.