Issue Details (XML | Word | Printable)

Key: GLASSFISH-11748
Type: Bug Bug
Status: Resolved Resolved
Resolution: Fixed
Priority: Critical Critical
Assignee: Martin Grebac
Reporter: Sanjeeb Sahoo
Votes: 2
Watchers: 0
Operations

If you were logged in you would be able to see more operations.
glassfish

JAXB does not work in vanilla OSGi bundle context

Created: 01/Apr/10 06:02 PM   Updated: 11/Jan/13 04:08 AM   Resolved: 17/Sep/10 03:45 AM
Component/s: web_services
Affects Version/s: V3
Fix Version/s: 3.1

Time Tracking:
Not Specified

File Attachments: 1. Zip Archive metainf-service-test.zip (15 kB) 01/Apr/10 06:04 PM - Sanjeeb Sahoo

Environment:

Operating System: All
Platform: All


Issuezilla Id: 11,748
Tags:
Participants: aaronjwhiteside, egarcia_sms, kumara, ltouve, Martin Grebac and Sanjeeb Sahoo


 Description  « Hide

I am deploying a vanilla OSGi bundle in GlassFish. This has nothing to do with
any Java EE programming model. It tries to obtain a JAXBContext to do some XML
unmarshalling, but gets NPE as the stack below shows:

java.lang.NullPointerException
at javax.xml.bind.ContextFinder.handleClassCastException(ContextFinder.java:95)
at javax.xml.bind.ContextFinder.newInstance(ContextFinder.java:204)
at javax.xml.bind.ContextFinder.find(ContextFinder.java:375)
at javax.xml.bind.JAXBContext.newInstance(JAXBContext.java:618)
at javax.xml.bind.JAXBContext.newInstance(JAXBContext.java:565)
at sahoo.metainfservicetest.Activator.start(Activator.java:9)
at
org.apache.felix.framework.util.SecureAction.startActivator(SecureAction.java:640)
at org.apache.felix.framework.Felix.activateBundle(Felix.java:1700)
at org.apache.felix.framework.Felix.startBundle(Felix.java:1622)
at org.apache.felix.framework.BundleImpl.start(BundleImpl.java:915)
at org.apache.felix.shell.impl.StartCommandImpl.execute(StartCommandImpl.java:114)
at
org.apache.felix.shell.impl.Activator$ShellServiceImpl.executeCommand(Activator.java:286)
at org.apache.felix.shell.remote.Shell.run(Shell.java:109)
at java.lang.Thread.run(Thread.java:619)

To reproduce, use the attached test case. Run it like this:
mvn package
cp target/metainfservicetest-1.0-SNAPSHOT.jar domain1/autodeploy/bundles/
See server.log



Sanjeeb Sahoo added a comment - 01/Apr/10 06:04 PM

Created an attachment (id=4291)
Test case. mvn package, cp jar to autodeploy/bundles/, see server.log


kumara added a comment - 01/Apr/10 06:22 PM

-> snajper


Martin Grebac added a comment - 20/Apr/10 03:24 AM

There is supposed to be a code in GF osgi runtime which should shield the JDK
api/impl from the runtime. It seems that code is not working properly for
non-javaee bundles?


Sanjeeb Sahoo added a comment - 20/Apr/10 03:47 AM

How will the glassfish class loader magic come into play for non-javaee bundles?
It can't and that's exactly why we have to fix JAXB and other web services
bundles that rely on the magic. Thanks a lot for looking into it.


ltouve added a comment - 04/Jun/10 10:33 AM
      • Issue 11748 has been confirmed by votes. ***

Martin Grebac added a comment - 07/Jun/10 07:03 AM

Hi Sahoo, I'd really like to implement a solution. However, yet I didn't find
any that would not require adding osgi-dependent code into JAXB api.
Remember that jaxb api integrates into JDK, thus it is not acceptable. Any
suggestions?


Sanjeeb Sahoo added a comment - 07/Jun/10 07:13 AM

Pl. look at http://wiki.glassfish.java.net/Wiki.jsp?page=JdkSpiOsgi
If you follow the proposal, JAXB code will not have any dependency on OSGi APIs.
It will get configured in runtime to look at OSGi bundles for providers.


Martin Grebac added a comment - 07/Jun/10 07:17 AM

Thanks, will look at it.


Martin Grebac added a comment - 16/Jun/10 09:16 AM

Hi Sahoo, could the ServiceLoader from http://wiki.glassfish.java.net/Wiki.jsp?page=JdkSpiOsgi contain
one more method to obtain a classloader instance? The method would return osgi-based classloader
extension (let's say e.g. OsgiClassLoader extends ClassLoader).
The issue is that in JAXB api the META-INF services provider lookup is not the only way to lookup the
implementation. There are other ways, like lookup the impl class through jaxb.properties file.
The current proposed approach would help in META-INF/services lookup, but not in the other cases.


Martin Grebac added a comment - 21/Jun/10 06:41 AM

As we discussed, because JAXB uses slightly modified service lookup mechanism, osgiutil shall provide
method which does not check isAssignable on service provider instances. I'll post the updated jaxb api
code once such method is ready.


Sanjeeb Sahoo added a comment - 24/Aug/10 10:30 AM

Martin,

Has JAXB been changed to use the new ServiceLoader? AFAIK, I added the necessary
methods to meet your requirements and I have also released osgi-resource-locator
module version 1.0.0.

I just received a request from a user who is facing this exact problem.

Sahoo


egarcia_sms added a comment - 17/Sep/10 02:45 AM

I'm the user Sahoo was referring to.

As almost all our products depends on this (parsing XML using JAXB, as we have lot of classes) it will be
nice to know the status of the bug, because I'm now experiencing side issues related to this one (not being
able to load a class inside a WAR using JAXB)

Thank you in advance.

�ngel Eduardo


Martin Grebac added a comment - 17/Sep/10 03:45 AM

The OSGi based loading of JAXB Context depending on Sahoo's utility classes has been integrated to GF so
marking this as fixed.


aaronjwhiteside added a comment - 10/Jan/13 10:00 PM

I am still seeing this error with Glassfish 3.1.2 and an OSGi bundle..

The bundle is Apache Camel, and the corresponding code is, it seems that GlassFish fails to find the ContextFactory if a ClassLoader is supplied. This same code works under other OSGi containers Servicemix and JBoss 7..

// must use classloader from CamelContext to have JAXB working
            jaxbContext = JAXBContext.newInstance(Constants.JAXB_CONTEXT_PACKAGES, CamelContext.class.getClassLoader());

Stacktrace below...

[#|2013-01-10T16:51:01.480-0500|INFO|glassfish3.1.2|javax.enterprise.system.std.com.sun.enterprise.server.logging|_ThreadID=224;_ThreadName=Thread-2;|ERROR [EclipseGeminiBlueprintExtenderThread-42] com.mm.gateway.messaging.routing.RouteDeployer - An error occurred while trying to load the routing file: /tmp/routes/hello.xml
javax.xml.bind.JAXBException: Provider com.sun.xml.internal.bind.v2.ContextFactory not found
	at javax.xml.bind.ContextFinder.newInstance(ContextFinder.java:148) ~[jaxb-api-osgi.jar:na]
	at javax.xml.bind.ContextFinder.find(ContextFinder.java:361) ~[jaxb-api-osgi.jar:na]
	at javax.xml.bind.JAXBContext.newInstance(JAXBContext.java:446) ~[jaxb-api-osgi.jar:na]
	at javax.xml.bind.JAXBContext.newInstance(JAXBContext.java:409) ~[jaxb-api-osgi.jar:na]
	at com.mm.gateway.messaging.routing.RouteLoader.load(RouteLoader.java:61) ~[routing-engine-1.0-SNAPSHOT.jar:1.0-SNAPSHOT]
	at com.mm.gateway.messaging.routing.RouteDeployer.loadRoutes(RouteDeployer.java:266) [routing-engine-1.0-SNAPSHOT.jar:1.0-SNAPSHOT]
	at com.mm.gateway.messaging.routing.RouteDeployer.loadExistingRoutes(RouteDeployer.java:106) [routing-engine-1.0-SNAPSHOT.jar:1.0-SNAPSHOT]
	at com.mm.gateway.messaging.routing.RouteDeployer.start(RouteDeployer.java:79) [routing-engine-1.0-SNAPSHOT.jar:1.0-SNAPSHOT]
	at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) ~[na:1.7.0_09]
	at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57) ~[na:1.7.0_09]
	at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) ~[na:1.7.0_09]
	at java.lang.reflect.Method.invoke(Method.java:601) ~[na:1.7.0_09]
	at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.invokeCustomInitMethod(AbstractAutowireCapableBeanFactory.java:1612) [org.springframework.beans-3.2.0.RELEASE.jar:3.2.0.RELEASE]
	at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.invokeInitMethods(AbstractAutowireCapableBeanFactory.java:1553) [org.springframework.beans-3.2.0.RELEASE.jar:3.2.0.RELEASE]
	at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.initializeBean(AbstractAutowireCapableBeanFactory.java:1483) [org.springframework.beans-3.2.0.RELEASE.jar:3.2.0.RELEASE]
	at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:524) [org.springframework.beans-3.2.0.RELEASE.jar:3.2.0.RELEASE]
	at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:461) [org.springframework.beans-3.2.0.RELEASE.jar:3.2.0.RELEASE]
	at org.springframework.beans.factory.support.AbstractBeanFactory$1.getObject(AbstractBeanFactory.java:295) [org.springframework.beans-3.2.0.RELEASE.jar:3.2.0.RELEASE]
	at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:223) [org.springframework.beans-3.2.0.RELEASE.jar:3.2.0.RELEASE]
	at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:292) [org.springframework.beans-3.2.0.RELEASE.jar:3.2.0.RELEASE]
	at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:194) [org.springframework.beans-3.2.0.RELEASE.jar:3.2.0.RELEASE]
	at org.springframework.beans.factory.support.DefaultListableBeanFactory.preInstantiateSingletons(DefaultListableBeanFactory.java:607) [org.springframework.beans-3.2.0.RELEASE.jar:3.2.0.RELEASE]
	at org.springframework.context.support.AbstractApplicationContext.finishBeanFactoryInitialization(AbstractApplicationContext.java:932) [org.springframework.context-3.2.0.RELEASE.jar:3.2.0.RELEASE]
	at org.eclipse.gemini.blueprint.context.support.AbstractDelegatedExecutionApplicationContext.access$1600(AbstractDelegatedExecutionApplicationContext.java:60) [gemini-blueprint-core-1.0.2.RELEASE.jar:1.0.2.RELEASE]
	at org.eclipse.gemini.blueprint.context.support.AbstractDelegatedExecutionApplicationContext$4.run(AbstractDelegatedExecutionApplicationContext.java:325) [gemini-blueprint-core-1.0.2.RELEASE.jar:1.0.2.RELEASE]
	at org.eclipse.gemini.blueprint.util.internal.PrivilegedUtils.executeWithCustomTCCL(PrivilegedUtils.java:85) [gemini-blueprint-core-1.0.2.RELEASE.jar:1.0.2.RELEASE]
	at org.eclipse.gemini.blueprint.context.support.AbstractDelegatedExecutionApplicationContext.completeRefresh(AbstractDelegatedExecutionApplicationContext.java:290) [gemini-blueprint-core-1.0.2.RELEASE.jar:1.0.2.RELEASE]
	at org.eclipse.gemini.blueprint.extender.internal.dependencies.startup.DependencyWaiterApplicationContextExecutor$CompleteRefreshTask.run(DependencyWaiterApplicationContextExecutor.java:137) [gemini-blueprint-extender-1.0.2.RELEASE.jar:1.0.2.RELEASE]
	at java.lang.Thread.run(Thread.java:722) [na:1.7.0_09]
Caused by: java.lang.ClassNotFoundException: com.sun.xml.internal.bind.v2.ContextFactory not found by org.apache.camel.camel-core [286]
	at org.apache.felix.framework.BundleWiringImpl.findClassOrResourceByDelegation(BundleWiringImpl.java:1460) ~[na:na]
	at org.apache.felix.framework.BundleWiringImpl.access$400(BundleWiringImpl.java:72) ~[na:na]
	at org.apache.felix.framework.BundleWiringImpl$BundleClassLoader.loadClass(BundleWiringImpl.java:1843) ~[na:na]
	at java.lang.ClassLoader.loadClass(ClassLoader.java:356) ~[na:1.7.0_09]
	at javax.xml.bind.ContextFinder.safeLoadClass(ContextFinder.java:573) ~[jaxb-api-osgi.jar:na]
	at javax.xml.bind.ContextFinder.newInstance(ContextFinder.java:145) ~[jaxb-api-osgi.jar:na]
	... 28 common frames omitted
|#]

aaronjwhiteside added a comment - 10/Jan/13 10:27 PM

So after more digging it turns out that it will load correctly when using the current threads context ClassLoader but not the actual bundle's class loader (where the classes are actually located).

The bundle ClassLoader should be able to find the ContextFactory too.


Sanjeeb Sahoo added a comment - 11/Jan/13 04:04 AM - edited

There are three things I would like to highlight:
1. It seems you are passing your bundle class loader to JAXBContext.newInstance.
In such a case JAXB seems to be searching available service provider using that class loader only.
Since your bundle does not have a dependency on a JAXB implementation bundle,
JAXB API is not able to locate a provider.

I am assuming you are not trying to locate a specific JAXB provider. Any provider is good for for you.
In that case, why do you pass a classloader argument? If you leave it unspecified, I am sure
JAXB can locate the default provider. So, I think you should change your code to leave it unspecified.

2. Coming to the issue of JAXB not being able to locate the default behavior.
I am hesitant to call it a bug, but I definitely call it a poor implementation.
I can't call it a bug because the javadocs for JAXBContext [1] only recommends the API to locate the default provider.
The wordings from javadocs [1] is produced below verbatim:

"Finally, if all the steps above fail, then the rest of the look up is unspecified.
That said, the recommended behavior is to simply look for some hard-coded platform default JAXB implementation.
This phase of the look up is so that JavaSE can have its own JAXB implementation as the last resort."

It does not mandate as you can see from the above wording. By no means, I am defending their behavior.

3. Finally coming to the issue of why JAXBContext.newInstance succeeds when you pass it a classloader that's
same as Thread's context classloader. Without knowing what's set as Thread's context classloader, it is difficult
to say why it works. There is one thing I would like to highlight is the following code in FactoryFinder:

if (getContextClassLoader() == classLoader) {
Class factory = lookupUsingOSGiServiceLoader("javax.xml.bind.JAXBContext");
if (factory != null) { logger.fine("OSGi environment detected"); return newInstance(contextPath, factory, classLoader, properties); }
}

I definitely don't endorse the "if (getContextClassLoader() == classLoader)" check here.
This may explain why you code behaved differently when you passed TCL as the argument.
I am not the author of this class and my experience says that it will take a long time
to get this fixed. So, the simple thing to do would be to do what I asked you to do in #1.

Thanks,
Sahoo

[1] http://docs.oracle.com/javaee/6/api/javax/xml/bind/JAXBContext.html