Apr 10, 2009
Hacking Jersey/JAX-RS to run RESTful web services on Google AppEngine/Java
Update: this article is about Jersey 1.0.2, i’ll update the modifications i made for the 1.1.0-ea version soon.
Jersey doesn’t run out of the box on GAE, and since i can’t wait for a new release to try RESTful services on AppEngine/Java, let’s modify its servlet container so it will at least run basic samples.
I really find Jersey/JAX-RS compelling, while others think it could be the ultimate framework, and with all the recent buzz about the AppEngine platform, i wanted to try and make the two meet, grab a little dinner, a glass of wine or two, and make sweet web service babies. That didn’t go as easily as I planned, but after a little hacking i was able to run basic examples, which is probably enough for now, until a later release fixes the issues.
In this post i’ll describe what i did to make Jersey’s basic servlet example work (ie. what i broke) and provide you with the code (code + binary here) so you can try it on your own.
AppEngine runs a tight ship with strict security policies, which caused most of the errors you get using Jersey: classes you can’t access inside their sandbox. I’ve done my best bypassing those errors, and in doing so i actually removed and broke features, some of which i know about, the others, well, i don’t (:
The people following my twitter stream know i learned the hard way that the dev server google provides with its eclipse plugin doesn’t enforce the same security policies as their appspot servers (why is that is a question to which i don’t have the answer), and the kicker is that Jersey runs fine in their dev server….
The first major error you encounter is related to javax.naming.InitialContext, the second one to JAXB binding for WADL generation. I removed both the WADL pregeneration and root resources registration in the JNDI context. I don’t care much for WADL so it’s not a big loss anyway, however i’m not totally sure how the JNDI context is used inside Jersey. This might break something more useful.
A couple more classloader errors, and other tiny issues, and we’re good to go.
Here’s how to make the Jersey basic servlet sample (simple-servlet) run on AppEngine (which you’ll be able to see here for a little while, if you want to test it. It comes with a little ajax ui changing Accept headers, choosing resources to test, etc)
Create a GAE using the Google plugin for Eclipse, add the jersey jars into the war/WEB-INF/lib directory (i used the jersey archive found here to get Jersey, its dependencies and the samples), while you’re at it also add the modified jersey servlet container there, jersey-appengine-container_0.1.jar found in the linked zip file.
Add the modified servlet (org.hybird.appengine.jersey.container.ServletContainer) to web.xml:
<servlet> <servlet-name>Jersey Web Application</servlet-name> <servlet-class>org.hybird.appengine.jersey.container.ServletContainer</servlet-class> <init-param> <param-name>com.sun.jersey.config.property.packages</param-name> <param-value>com.sun.jersey.samples.servlet.resources</param-value> </init-param> <load-on-startup>1</load-on-startup> </servlet> <servlet-mapping> <servlet-name>Jersey Web Application</servlet-name> <url-pattern>/resources/*</url-pattern> </servlet-mapping>
Now you can take the simple-servlet sample, copy its index.html in the war folder, and java source files inside your src. And if i remember correctly, that should be it ^^ and you’ll get the same app as i have.
Since Jersey has so many features (a lot more than the JAX-RS spec would let you believe), i haven’t had the time to test a lot of them yet, and i think other security exceptions are probably waiting for us.
Great! thanks for trying this out.
The next release (after 1.0.3) will include changes so that Jersey will work gracefully with GAE, but with limited functionality as some packages like that for JAXB are not available for use at runtime, which is the reason why WADL support causes things to fail (JAXB classes are visible to the class loader but JAXBContext.newInstance fails with a java.lang.NoClassDefFoundError).
It looks like it’s not populating the ${it} instance, when you return a new Viewable(“index”, this). If I don’t have access to it from my jsp, then it’s pretty much useless for me. Have you run into this problem?
Thanks!
No, not yet, i’ll see if i can do anything about that. But most likely it’s because of the classes that are unavailable in AppEngine.
Thanks for posting this article. I needed to deploy a simple RESTful web service example for my team to view (http://brucerestfulwsexample.appspot.com/) on Google AppEngine/Java.
Initially, I had a problem using your jersey-appengine-container_0.1.jar with my project. Apparently, your jar doesn’t work with the 1.1.0-ea version of the Jersey jars that I was using. I had to replace that version with 1.0.2 Jersey jar files and then your jersey-appengine-container_0.1.jar worked fine.
Bruce
Thanks for the comment Bruce, that’s nice to hear !
I’ve updated the article to specifiy it’s a modified Jersey 1.0.2. I’ll look at updating it for 1.1.0-ea as soon as i can.
First of all, thanks for the patch.
Unfortunately I am still getting errors when deploying to my appengine account.
(I am using your patch with Jersey 1.0.2)
This is the exception I am seeing. Any ideas?
thanks,
com.sun.jersey.core.spi.component.ProviderFactory _getComponentProvider: The provider class, class com.sun.jersey.core.impl.provider.entity.XMLListElementProvider$App, could not be instantiated
java.lang.SecurityException: Unable to get members for class com.sun.jersey.core.impl.provider.entity.XMLListElementProvider$App
at com.google.apphosting.runtime.security.shared.intercept.java.lang.Class_$10.run(Class_.java:357)
at com.google.apphosting.runtime.security.shared.intercept.java.lang.Class_$10.run(Class_.java:347)
at java.security.AccessController.doPrivileged(Native Method)
at com.google.apphosting.runtime.security.shared.intercept.java.lang.Class_.getMembers(Class_.java:347)
at com.google.apphosting.runtime.security.shared.intercept.java.lang.Class_.getMethods(Class_.java:170)
at com.sun.jersey.core.reflection.MethodList.(MethodList.java:57)
at com.sun.jersey.core.spi.component.ComponentConstructor.getPostConstructMethod(ComponentConstructor.java:124)
at com.sun.jersey.core.spi.component.ComponentConstructor.(ComponentConstructor.java:118)
at com.sun.jersey.core.spi.component.ProviderFactory.getInstance(ProviderFactory.java:205)
at com.sun.jersey.core.spi.component.ProviderFactory._getComponentProvider(ProviderFactory.java:133)
at com.sun.jersey.core.spi.component.ProviderFactory.getComponentProvider(ProviderFactory.java:126)
at com.sun.jersey.core.spi.component.ProviderServices.getComponent(ProviderServices.java:168)
at com.sun.jersey.core.spi.component.ProviderServices.getProvidersAndServices(ProviderServices.java:120)
at com.sun.jersey.core.spi.factory.MessageBodyFactory.getProviderMap(MessageBodyFactory.java:136)
at com.sun.jersey.core.spi.factory.MessageBodyFactory.initReaders(MessageBodyFactory.java:110)
at com.sun.jersey.core.spi.factory.MessageBodyFactory.init(MessageBodyFactory.java:105)
at org.hybird.appengine.jersey.container.WebApplicationImpl.initiate(WebApplicationImpl.java:463)
at org.hybird.appengine.jersey.container.WebApplicationImpl.initiate(WebApplicationImpl.java:320)
at org.hybird.appengine.jersey.container.WebComponent.initiate(WebComponent.java:424)
at org.hybird.appengine.jersey.container.WebComponent.load(WebComponent.java:435)
at org.hybird.appengine.jersey.container.WebComponent.init(WebComponent.java:168)
at org.hybird.appengine.jersey.container.ServletContainer.init(ServletContainer.java:198)
Hi,
Which appengine SDK version your container work with? I’m trying 1.2.1 and having problem.
Jun 23, 2009 1:07:37 AM org.hybird.appengine.jersey.container.WebApplicationImpl processRootResources
SEVERE: The ResourceConfig instance does not contain any root resource classes.
Jun 23, 2009 1:07:37 AM com.google.apphosting.utils.jetty.JettyLogger warn
WARNING: failed Jersey Web Application
javax.servlet.ServletException: com.sun.jersey.api.container.ContainerException: The ResourceConfig instance does not contain any root resource classes.
Hi,
It looks like taking time for the first connection.
Uncaught exception from servlet
com.google.apphosting.runtime.HardDeadlineExceededError
(…)
But after 2-3 minuts, it’s ok. I have a lot of javadoc files in the webapp and it’s possible that everything is parsed at the first connection to look for @Path classes.
I was using 1.2.0.
At first i thought 1.2.1 wouldn’t be so different. But I’ve actually run into an almost unsurmountable amount of weird exceptions with Jersey 1.1.0-ea and sdk 1.2.1. We shouldn’t have to do this, at this point it doesn’t seem GAE’s VM is really usable with real world libs and frameworks, without heavy work on our part.
Has anyone got Jersey 1.1.4 and GAE 1.2.6 (latest versions as of writing) to work together?
Ideally I would like a JAX-RS implementation to not load any providers/modules for XML and JSON as I want to drop in providers that get around App Engine’s restrictions.
Restlet seems to work fine. But it appears to not implement all parts of the JAX-RS specification (eg. all @Context type injections).
I don’t think so, however Paul Sandoz recently said they would look at it for the next release, so it shouldn’t be too long.
Restlet DOES provide Jax-rs functionnalities with an extension:
http://wiki.restlet.org/docs_2.0/13-restlet/28-restlet/57-restlet.html