0
votes

Background: in karaf, I have two features that each use a different version of Jersey (1.17 and 2.0).

They are separated from each other and there are no bundles that import both of them.

The package names between 1.17 and 2.0 of Jersey have changed (com.sun.jersey vs. org.glassfish.jersey).

Anyway, the bundles that use jersey 1.17 work fine.

To make 2.0 work (presumably), I read somewhere that I can specify FQN of providers for message readers in a file in a directory called "services" in my META-INF dir in my jar/bundle. (as per http://docs.oracle.com/javase/6/docs/technotes/guides/jar/jar.html#Service%20Provider )

That looks like this:

META-INF/services/javax.ws.rs.ext.MessageBodyReader:

org.glassfish.jersey.message.internal.XmlRootObjectJaxbProvider.App
org.glassfish.jersey.message.internal.SourceProvider.StreamSourceReader
org.glassfish.jersey.message.internal.SourceProvider.SaxSourceReader
org.glassfish.jersey.message.internal.SourceProvider.DomSourceReader
org.glassfish.jersey.message.internal.XmlRootObjectJaxbProvider.Text
org.glassfish.jersey.message.internal.XmlRootObjectJaxbProvider.General

It seems to be working, because my org.glassfish.jersey.core.jersey-common 2.0 bundle attempts to load the first class. But it throws a ClassNotFoundException on it.

2013-07-29 14:36:45,334 | WARN  | Executor: 2      | OsgiRegistry                     | egistry$BundleSpiProvidersLoader  222 | 207 - org.glassfish.jersey.core.jersey-common - 2.0.0 | [] | Exception caught while loading SPI providers.
java.lang.ClassNotFoundException: org.glassfish.jersey.message.internal.XmlRootObjectJaxbProvider.App
    at org.eclipse.osgi.internal.loader.BundleLoader.findClassInternal(BundleLoader.java:501)[osgi-3.8.0.v20120529-1548.jar:]
    at org.eclipse.osgi.internal.loader.BundleLoader.findClass(BundleLoader.java:421)[osgi-3.8.0.v20120529-1548.jar:]
    at org.eclipse.osgi.internal.loader.BundleLoader.findClass(BundleLoader.java:412)[osgi-3.8.0.v20120529-1548.jar:]
    at org.eclipse.osgi.internal.baseadaptor.DefaultClassLoader.loadClass(DefaultClassLoader.java:107)[osgi-3.8.0.v20120529-1548.jar:]
    at java.lang.ClassLoader.loadClass(ClassLoader.java:356)[:1.7.0_21]
    at org.eclipse.osgi.internal.loader.BundleLoader.loadClass(BundleLoader.java:340)[osgi-3.8.0.v20120529-1548.jar:]
    at org.eclipse.osgi.framework.internal.core.BundleHost.loadClass(BundleHost.java:229)[osgi-3.8.0.v20120529-1548.jar:]
    at org.eclipse.osgi.framework.internal.core.AbstractBundle.loadClass(AbstractBundle.java:1212)[osgi-3.8.0.v20120529-1548.jar:]
    at org.glassfish.jersey.internal.OsgiRegistry$BundleSpiProvidersLoader.call(OsgiRegistry.java:217)[207:org.glassfish.jersey.core.jersey-common:2.0.0]
    at org.glassfish.jersey.internal.OsgiRegistry$BundleSpiProvidersLoader.call(OsgiRegistry.java:189)[207:org.glassfish.jersey.core.jersey-common:2.0.0]
    at org.glassfish.jersey.internal.OsgiRegistry.locateAllProviders(OsgiRegistry.java:468)[207:org.glassfish.jersey.core.jersey-common:2.0.0]

Note this is happening in bundle 207 jersey-common 2.0

if I run

karaf@root> osgi:find-class XmlRootObjectJaxbProvider

jersey-core-common (207)
org/glassfish/jersey/message/internal/XmlRootObjectJaxbProvider$App.class
org/glassfish/jersey/message/internal/XmlRootObjectJaxbProvider$General.class
org/glassfish/jersey/message/internal/XmlRootObjectJaxbProvider$Text.class
org/glassfish/jersey/message/internal/XmlRootObjectJaxbProvider.class

The class is right there, in the same bundle! It cannot find a class in its own bundle.

This makes no sense to me, unless glassfish is using some other class loader somehow. Can anyone shed any light on this?

Thanks.

update I found this bug:

https://java.net/jira/browse/GLASSFISH-16970

which lead me to this wiki on the matter:

https://wikis.oracle.com/display/GlassFish/JdkSpiOsgi

I don't see how I could implement their work-around without hacking up the jersey-core-common jar.

1

1 Answers

2
votes

Oh, what tangled webs we weave when we want to avoid using services :-( This utter mess is caused by Java's lack of a service registry.

I assume your problem is that jersey tries to load from YOUR bundle since it finds the services directory in that bundle. Since you don't have those classes, Jersey balks correctly.

The original solution is to place those files in the services directory in the jersey bundle, requiring you to open up that bundle. If the jersey bundle exports the implementation packages (the anathema of modularity but often done in code that does not understand OSGi) you could import those packages in your bundle even though you do not use them directly.

Another alternative, which I have not checked out, is to use an OSGi hack: fragments. You can make a fragment to the jersey bundle that contains the services directory. However, not sure if this works.

Last but not least, you could include the jersey code inside your own bundle privately. With bnd, this is quite easy to do.