Hello,
I encountered a class loading problem using CXF as the JAX-RS implementation in
Equinox OSGi container. It seems that CXF, deep in its core, triggers
unfortunately some Equinox-specific stuff that results later in the problems. I
have no idea how to solve it and I would be thankful for any help. But let's
start from the beginning.
We want to create a RESTful service based on Apache Olingo library, which uses
JAX-RS, and we want to run the service in Equinox OSGi container with Jetty.
Although we plan to move to another container in the future, we can't do that
right now, so we are stuck with Equinox at this moment. For completeness our
sample RESTful service is based on a sample from Olingo, we use Equinox 3.8.0
with Apache Aries 1.1.0 (for Blueprint) and CXF 2.7.8, Olingo 1.2.0 and Jetty
8.1.5. I can provide the complete bundle list if necessary, but I'm omitting it
for brevity. Anyway, when the container starts, all bundles are resolved (or
active), no mandatory dependencies are missing.
When I access our sample service, I get following stack trace:
java.lang.RuntimeException: org.apache.cxf.interceptor.Fault
at
org.apache.cxf.interceptor.AbstractFaultChainInitiatorObserver.onMessage(AbstractFaultChainInitiatorObserver.java:116)
at
org.apache.cxf.phase.PhaseInterceptorChain.doIntercept(PhaseInterceptorChain.java:333)
at
org.apache.cxf.transport.ChainInitiationObserver.onMessage(ChainInitiationObserver.java:121)
at
org.apache.cxf.transport.http.AbstractHTTPDestination.invoke(AbstractHTTPDestination.java:239)
at
org.apache.cxf.transport.servlet.ServletController.invokeDestination(ServletController.java:248)
at
org.apache.cxf.transport.servlet.ServletController.invoke(ServletController.java:222)
at
org.apache.cxf.transport.servlet.ServletController.invoke(ServletController.java:153)
at
org.apache.cxf.transport.servlet.CXFNonSpringServlet.invoke(CXFNonSpringServlet.java:167)
at
org.apache.cxf.transport.servlet.AbstractHTTPServlet.handleRequest(AbstractHTTPServlet.java:286)
at
org.apache.cxf.transport.servlet.AbstractHTTPServlet.doGet(AbstractHTTPServlet.java:211)
at javax.servlet.http.HttpServlet.service(HttpServlet.java:735)
at
org.apache.cxf.transport.servlet.AbstractHTTPServlet.service(AbstractHTTPServlet.java:262)
at
org.eclipse.jetty.servlet.ServletHolder.handle(ServletHolder.java:643)
at
org.eclipse.jetty.servlet.ServletHandler$CachedChain.doFilter(ServletHandler.java:1331)
at
org.apache.olingo.sample.annotation.filter.ServiceFactoryFilter.doFilter(ServiceFactoryFilter.java:24)
at
org.eclipse.jetty.servlet.ServletHandler$CachedChain.doFilter(ServletHandler.java:1302)
at
org.eclipse.jetty.servlet.ServletHandler.doHandle(ServletHandler.java:448)
at
org.eclipse.jetty.server.handler.ScopedHandler.handle(ScopedHandler.java:131)
at
org.eclipse.jetty.security.SecurityHandler.handle(SecurityHandler.java:524)
at
org.eclipse.jetty.server.session.SessionHandler.doHandle(SessionHandler.java:231)
at
org.eclipse.jetty.server.handler.ContextHandler.doHandle(ContextHandler.java:1067)
at
org.eclipse.jetty.servlet.ServletHandler.doScope(ServletHandler.java:377)
at
org.eclipse.jetty.server.session.SessionHandler.doScope(SessionHandler.java:192)
at
org.eclipse.jetty.server.handler.ContextHandler.doScope(ContextHandler.java:1001)
at
org.eclipse.jetty.server.handler.ScopedHandler.handle(ScopedHandler.java:129)
at
org.eclipse.jetty.server.handler.ContextHandlerCollection.handle(ContextHandlerCollection.java:250)
at
org.eclipse.jetty.server.handler.HandlerCollection.handle(HandlerCollection.java:149)
at
org.eclipse.jetty.server.handler.HandlerWrapper.handle(HandlerWrapper.java:111)
at org.eclipse.jetty.server.Server.handle(Server.java:356)
at
org.eclipse.jetty.server.AbstractHttpConnection.handleRequest(AbstractHttpConnection.java:454)
at
org.eclipse.jetty.server.AbstractHttpConnection.headerComplete(AbstractHttpConnection.java:890)
at
org.eclipse.jetty.server.AbstractHttpConnection$RequestHandler.headerComplete(AbstractHttpConnection.java:944)
at org.eclipse.jetty.http.HttpParser.parseNext(HttpParser.java:630)
at org.eclipse.jetty.http.HttpParser.parseAvailable(HttpParser.java:230)
at
org.eclipse.jetty.server.AsyncHttpConnection.handle(AsyncHttpConnection.java:77)
at
org.eclipse.jetty.io.nio.SelectChannelEndPoint.handle(SelectChannelEndPoint.java:622)
at
org.eclipse.jetty.io.nio.SelectChannelEndPoint$1.run(SelectChannelEndPoint.java:46)
at
org.eclipse.jetty.util.thread.QueuedThreadPool.runJob(QueuedThreadPool.java:603)
at
org.eclipse.jetty.util.thread.QueuedThreadPool$3.run(QueuedThreadPool.java:538)
at java.lang.Thread.run(Unknown Source)
Caused by: org.apache.cxf.interceptor.Fault
at
org.apache.cxf.service.invoker.AbstractInvoker.createFault(AbstractInvoker.java:162)
at
org.apache.cxf.service.invoker.AbstractInvoker.invoke(AbstractInvoker.java:128)
at org.apache.cxf.jaxrs.JAXRSInvoker.invoke(JAXRSInvoker.java:205)
at org.apache.cxf.jaxrs.JAXRSInvoker.invoke(JAXRSInvoker.java:269)
at org.apache.cxf.jaxrs.JAXRSInvoker.invoke(JAXRSInvoker.java:102)
at
org.apache.cxf.interceptor.ServiceInvokerInterceptor$1.run(ServiceInvokerInterceptor.java:58)
at
org.apache.cxf.interceptor.ServiceInvokerInterceptor.handleMessage(ServiceInvokerInterceptor.java:94)
at
org.apache.cxf.phase.PhaseInterceptorChain.doIntercept(PhaseInterceptorChain.java:272)
... 38 more
Caused by: java.lang.ExceptionInInitializerError
at
org.apache.olingo.sample.annotation.processor.AnnotationSampleServiceFactory.createService(AnnotationSampleServiceFactory.java:67)
at
org.apache.olingo.odata2.core.rest.ODataSubLocator.handle(ODataSubLocator.java:146)
at
org.apache.olingo.odata2.core.rest.ODataSubLocator.handleGet(ODataSubLocator.java:56)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(Unknown Source)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source)
at java.lang.reflect.Method.invoke(Unknown Source)
at
org.apache.cxf.service.invoker.AbstractInvoker.performInvocation(AbstractInvoker.java:180)
at
org.apache.cxf.service.invoker.AbstractInvoker.invoke(AbstractInvoker.java:96)
... 44 more
Caused by: java.lang.IllegalArgumentException: No folder to scan found for
package 'org.apache.olingo.sample.annotation.model'.
at
org.apache.olingo.odata2.annotation.processor.core.util.ClassHelper.loadClasses(ClassHelper.java:71)
at
org.apache.olingo.odata2.annotation.processor.core.util.ClassHelper.loadClasses(ClassHelper.java:62)
at
org.apache.olingo.odata2.annotation.processor.core.edm.AnnotationEdmProvider.<init>(AnnotationEdmProvider.java:97)
at
org.apache.olingo.odata2.annotation.processor.core.rt.AnnotationServiceFactoryImpl.createAnnotationService(AnnotationServiceFactoryImpl.java:42)
at
org.apache.olingo.odata2.annotation.processor.api.AnnotationServiceFactory.createAnnotationService(AnnotationServiceFactory.java:103)
at
org.apache.olingo.sample.annotation.processor.AnnotationSampleServiceFactory$AnnotationInstances.<clinit>(AnnotationSampleServiceFactory.java:54)
... 53 more
The failure occurs within AnnotationServiceFactory.createAnnotationService()
call, where Olingo attempts to discover and load classes with entity
definitions for our sample service and it fails. The service factory uses TCCL
(thread context class loader) for loading the classes. The notable thing is
that this class loader at this point *is not* the webapp's class loader that
was set by Jetty, it is an instance of
org.eclipse.core.runtime.internal.adaptor.ContextFinder! The ContextFinder is
Equinox's speciality, I suppose, and probably somewhat flawed anyway:
http://blog.bjhargrave.com/2007/07/contextfinder-in-eclipse-is-broken.html
How the ContextFinder was set? Further exploration leads to the finding that
the TCCL is set by org.apache.cxf.transport.servlet.CXFNonSpringServlet in
invoke() method.
- The 'loader' field is filled in CXFNonSpringServlet.init(): loader =
bus.getExtension(ClassLoader.class);
- The 'bus' is org.apache.cxf.bus.CXFBusImpl and the getExtension() method uses
org.apache.cxf.bus.osgi.OSGiBeanLocator as the ConfiguredBeanLocator.
OSGiBeanLocator.getBeansFromOsgiService() asks the OSGi container for
java.lang.ClassLoader service... and well, Equinox provides such a service! The
returned reference displays:
{java.lang.ClassLoader}={service.ranking=2147483647,
service.pid=0.org.eclipse.core.runtime.internal.adaptor.ContextFinder,
service.vendor=Eclipse.org - Equinox,
equinox.classloader.type=contextClassLoader, service.id=5}
So, the summary:
- The CXF servlet creates a CXF bus.
- The CXF bus binds an OSGiBeanLocator during initialization.
- Equinox, unfortunately, provides an OSGi service for java.lang.ClassLoader
type (the ContextFinder).
- The CXF servlet sets this OSGi service/class loader as TCCL when invoked,
overriding the class loader for the webapp provided by Jetty.
- Well, later the ContextFinder is used by Olingo instead of the desired class
loader.
Any ideas how to fix the TCCL override performed by the CXF servlet?
Thanks for help in advance,
Petr