Hello all-
Has anybody actually tried to use the restlet Jax-RS extension in an OSGi
environment?
My guess is that no-one has, because it doesn't work :-)
By "doesn't work", I mean that trying to run a JaxRspplication in an OSGI
container will result in extremely odd and unclear runtime exceptions,
involving, among other things, ClassNotFoundException's for classes not provided
by the ext jar at all (com.sun.ws.rs.ext.RuntimeDelegateImpl).
The problem is this:
The Jax-RS API, (shipped as "lib/javax.ws.rs_1.0/javax.ws.rs.jar" in the restlet
distribution) does some pretty funky stuff. In addition to the painful but by
now somewhat expected use of the ServiceLoader pattern (where the API jar
inspects the classpath for resources named
"META-INF/services/javax.ws.rs.ext.RuntimeDelegate" and looks inside those files
for an implementation class name), the jax-rs API jar executes this code
_lazily_, from within a static method on it's RuntimeDelegate class. This means
that the API jar will look for a provided implementation only when some bit of
code first tries to access any of the jax-rs API classes which happen to make
use, internally, of this static method.
The ServiceLoader code will fail to find any implementation in an OSGI
environment, and will then try, as a last resort, to find it's fallback
implementation: "com.sun.ws.rs.ext.RuntimeDelegateImpl", which also doesn't
exist, and for which it then reports a ClassNotFound exception, which is very
misleading.
The good news is that there are several solutions and work-arounds.
As a SHORT-TERM fix, any client code which wants to create a JaxRsApplication
can do one of two things that will allow the Jax-RS API to find the restlet
jax-rs extension implementation:
1) set the thread's context classloader (remember to restore it after):
Classloader originalCL = Thread.currentThread().getContextClassLoader();
ClassLoader jaxrsExtCL = JaxRsApplication.class.getClassLoader();
Thread.currentThread().setContextClassLoader(jaxrsExtCL);
JaxRsApplication jaxrsApp = new JaxRsApplication();
jaxrsApp.add(new MyApplication()); // <- failure here without the above
Thread.currentThread().setContextClassLoader(originalCL);
OR
2) set the RuntimeDelegate instance manually:
import javax.ws.rs.ext.RuntimeDelegate;
import org.restlet.ext.jaxrs.internal.spi.RuntimeDelegateImpl;
....
// this is the critical line
RuntimeDelegate.setInstance(new RuntimeDelegateImpl());
JaxRsApplication jaxrsApp = new JaxRsApplication();
jaxrsApp.add(new MyApplication());
However, I think it would be preferable if client code didn't have to know
anything about the internal workings of the JAX-RS SPI layer. So, I think there
are two possible LONG-TERM solutions:
1) the Restlet jax-rs extension could call RuntimeDelegate.setInstance() itself,
from within a static initializer block of code in, e.g. JaxRsRestlet.java. This
is what I have running locally, and it works fine.
OR
2) The Restlet jax-rs extension could ship with the JAX-RS API classes rolled up
into it's bundle archive, either as a wholly-contained jar within the bundle or
as "exploded" contents added to the extension jar. Then the JAX-RS API code
would exist within the same bundle classloader, and therefor the normal service
discovery process would work fine.
I think that solution #2 is preferable.
In particular, the whole thing is a little ridiculous- unlike, for instance,
Restlet's use of the service loader pattern, the JAX-RS API, as it's designed
now, can only ever support EXACTLY ONE provider implementation, regardless
whether it's used in a standard or OSGi environment. Therefore, it certainly
adds no value whatsoever to distribut it as a freestanding jar, since it MUST be
used in conjunction with exactly one provider. Additionally, it doesn't seem
like it should use the ServiceLoader pattern at all- why not just have a static
dependency on a RuntimeDelegateImpl implementation class, and skip all the
complicated and fragile discovery stuff? The jax-rs API design in particularly
loathsome in an OSGi environment, where, if not for the discovery processs, it
would be perfectly reasonable to have more than one jax-rs provider!
Anyhow, sorry for the rant (I've just spent the better part of a day struggling
with this stuff- ugh).
-Dave Fogel
p.s. should this be posted in the restlet.code list?
------------------------------------------------------
http://restlet.tigris.org/ds/viewMessage.do?dsForumId=4447&dsMessageId=1350550