PDA

View Full Version : Running sample bundle on Equinox - NPE at BundleLoader.createBCL


regunath
Jun 5th, 2008, 05:07 AM
Hi,

I am experimenting with Spring-DM to serve as a container for my Spring beans exposed as services. Contrary to the samples provided, I would like to deploy and invoke a bundle standalone and actually outside of Eclipse.

As per the documentation, I built the bundle for the "simple-service" sample. Again, in order to access the service, I created another Spring bean XML with relevant contents like

<osgi:reference id="service" interface="org.springframework.osgi.samples.simpleservice.MyS ervice"/>

<bean id="consumerBean" class="org.springframework.osgi.samples.simpleservice.Ser viceConsumer">
<property name="service" ref="service"/>
</bean>

I then wrote a sample class like this:

package org.springframework.osgi.samples.simpleservice;

public class OSGiTest {
private static final String[] paths = new String[] {
"D:/WorkTools/spring-osgi-1.1.0-m2/lib/log4j.osgi-1.2.15-SNAPSHOT.jar",
"D:/WorkTools/spring-osgi-1.1.0-m2/lib/commons-logging_all-2.0.0.jar",
"D:/WorkTools/spring-osgi-1.1.0-m2/lib/aopalliance.osgi-1.0-SNAPSHOT.jar",
"D:/WorkTools/spring-osgi-1.1.0-m2/lib/spring-core-2.5.4.jar",
"D:/WorkTools/spring-osgi-1.1.0-m2/lib/spring-beans-2.5.4.jar",
"D:/WorkTools/spring-osgi-1.1.0-m2/lib/spring-context-2.5.4.jar",
"D:/WorkTools/spring-osgi-1.1.0-m2/lib/spring-aop-2.5.4.jar",
"D:/WorkTools/spring-osgi-1.1.0-m2/dist/spring-osgi-io-1.1.0-m2.jar",
"D:/WorkTools/spring-osgi-1.1.0-m2/dist/spring-osgi-core-1.1.0-m2.jar",
"D:/WorkTools/spring-osgi-1.1.0-m2/dist/spring-osgi-extender-1.1.0-m2.jar",
};

public static void main(String args[]) throws Exception {

EquinoxPlatform runtime = new EquinoxPlatform();
runtime.start();
BundleContext context = runtime.getBundleContext();

for (int i = 0; i < paths.length; i++) {
FileInputStream fis = new FileInputStream(paths[i]);
Bundle bundle = context.installBundle("Test " + i,fis);
bundle.start();
}

FileInputStream fis = new FileInputStream("D:/WorkTools/spring-osgi-1.1.0-m2/src/samples/simple-service/simple-service-bundle/target/simple-service-bundle-1.1.0-m2.jar");
Bundle bundle = context.installBundle("Simple-Service-Sample",fis);
bundle.start();

Bundle[] bundles = context.getBundles();
for (int j = 0; j < bundles.length; j++) {
ServiceReference[] sr = bundles[j].getRegisteredServices();
if (sr != null) {
for (int i = 0; i < sr.length; i++) {
System.out.println("Service Reference ---> " + sr[i]);
}
}
}
OsgiBundleXmlApplicationContext applicationContext = new OsgiBundleXmlApplicationContext(
new String[] {"file:///D:/WorkTools/spring-osgi-1.1.0-m2/src/samples/simple-service/simple-service-bundle/testbean.xml"});
applicationContext.setBundleContext(context);
applicationContext.refresh();

((ServiceConsumer)applicationContext.getBean("consumerBean")).foo();

bundle.stop();
//runtime.stop();
}

}
}

I was able to see a ServiceReference for the simple-service bundle. I however get an error trace like below :


Service Reference ---> {org.springframework.osgi.samples.simpleservice.My Service}={org.springframework.osgi.bean.name=simpl eService, Bundle-SymbolicName=org.springframework.osgi.samples.simp leservice, Bundle-Version=1.0, service.id=21}
Service Reference ---> {org.springframework.osgi.context.DelegatedExecuti onOsgiBundleApplicationContext, org.springframework.osgi.context.ConfigurableOsgiB undleApplicationContext, org.springframework.context.ConfigurableApplicatio nContext, org.springframework.context.ApplicationContext, org.springframework.context.Lifecycle,
........< other interfaces listed here >
org.springframework.beans.factory.DisposableBean}= {org.springframework.context.service.name=org.spri ngframework.osgi.samples.simpleservice, Bundle-SymbolicName=org.springframework.osgi.samples.simp leservice, Bundle-Version=1.0.0, service.id=22}
2008-06-05 14:27:17,031 [main] INFO [factory.xml.XmlBeanDefinitionReader] - Loading XML bean definitions from OSGi resource[file:///D:/WorkTools/spring-osgi-1.1.0-m2/src/samples/simple-service/simple-service-bundle/testbean.xml|bnd.id=0|bnd.sym=system.bundle]
Exception in thread "main" org.springframework.beans.factory.BeanDefinitionSt oreException: Unexpected exception parsing XML document from OSGi resource[file:///D:/WorkTools/spring-osgi-1.1.0-m2/src/samples/simple-service/simple-service-bundle/testbean.xml|bnd.id=0|bnd.sym=system.bundle]; nested exception is java.lang.NullPointerException
at org.springframework.beans.factory.xml.XmlBeanDefin itionReader.doLoadBeanDefinitions(XmlBeanDefinitio nReader.java:420)
...... <stack trace from Spring framework followed by>
Caused by: java.lang.NullPointerException
at org.eclipse.osgi.framework.internal.core.BundleLoa der.createBCL(BundleLoader.java:664)
at org.eclipse.osgi.framework.internal.core.BundleLoa der.createBCLPrevileged(BundleLoader.java:639)
at org.eclipse.osgi.framework.internal.core.BundleLoa der.createClassLoader(BundleLoader.java:313)
at org.eclipse.osgi.framework.internal.core.BundleLoa der.getResources(BundleLoader.java:304)
at org.eclipse.osgi.framework.internal.core.BundleHos t.getResources(BundleHost.java:270)
at org.springframework.osgi.util.BundleDelegatingClas sLoader.findResources(BundleDelegatingClassLoader. java:137)
at java.lang.ClassLoader.getResources(ClassLoader.jav a:1015)
at org.springframework.core.io.support.PropertiesLoad erUtils.loadAllProperties(PropertiesLoaderUtils.ja va:103)
at org.springframework.beans.factory.xml.PluggableSch emaResolver.getSchemaMapping(PluggableSchemaResolv er.java:128)
at org.springframework.beans.factory.xml.PluggableSch emaResolver.resolveEntity(PluggableSchemaResolver. java:107)
at org.springframework.beans.factory.xml.DelegatingEn tityResolver.resolveEntity(DelegatingEntityResolve r.java:85)
at org.springframework.osgi.context.support.Delegated EntityResolver.resolveEntity(DelegatedEntityResolv er.java:59)
at com.sun.org.apache.xerces.internal.util.EntityReso lverWrapper.resolveEntity(EntityResolverWrapper.ja va:148)
at com.sun.org.apache.xerces.internal.impl.XMLEntityM anager.resolveEntity(XMLEntityManager.java:701)
at com.sun.org.apache.xerces.internal.impl.xs.XMLSche maLoader.resolveDocument(XMLSchemaLoader.java:599)
at com.sun.org.apache.xerces.internal.impl.xs.XMLSche maValidator.findSchemaGrammar(XMLSchemaValidator.j ava:2454)
at com.sun.org.apache.xerces.internal.impl.xs.XMLSche maValidator.handleStartElement(XMLSchemaValidator. java:1807)
at com.sun.org.apache.xerces.internal.impl.xs.XMLSche maValidator.startElement(XMLSchemaValidator.java:7 05)
at com.sun.org.apache.xerces.internal.impl.XMLNSDocum entScannerImpl.scanStartElement(XMLNSDocumentScann erImpl.java:324)
at com.sun.org.apache.xerces.internal.impl.XMLNSDocum entScannerImpl$NSContentDispatcher.scanRootElement Hook(XMLNSDocumentScannerImpl.java:773)
at com.sun.org.apache.xerces.internal.impl.XMLDocumen tFragmentScannerImpl$FragmentContentDispatcher.dis patch(XMLDocumentFragmentScannerImpl.java:1794)
at com.sun.org.apache.xerces.internal.impl.XMLDocumen tFragmentScannerImpl.scanDocument(XMLDocumentFragm entScannerImpl.java:368)
at com.sun.org.apache.xerces.internal.parsers.XML11Co nfiguration.parse(XML11Configuration.java:834)
at com.sun.org.apache.xerces.internal.parsers.XML11Co nfiguration.parse(XML11Configuration.java:764)
at com.sun.org.apache.xerces.internal.parsers.XMLPars er.parse(XMLParser.java:148)
at com.sun.org.apache.xerces.internal.parsers.DOMPars er.parse(DOMParser.java:250)
at com.sun.org.apache.xerces.internal.jaxp.DocumentBu ilderImpl.parse(DocumentBuilderImpl.java:292)
at org.springframework.beans.factory.xml.DefaultDocum entLoader.loadDocument(DefaultDocumentLoader.java: 75)
at org.springframework.beans.factory.xml.XmlBeanDefin itionReader.doLoadBeanDefinitions(XmlBeanDefinitio nReader.java:396)
... 14 more

I wonder why I get a NullPointerException in the Eclipse BundleLoader.createBCL() method. Any pointers or help is much appreciated.

Costin Leau
Jun 5th, 2008, 01:34 PM
This is indeed odd. What version of Equinox are you using? Try using another version and see if that helps. Inside Spring-DM we're testing against Equinox 3.2.2 for example.
For the record this how the code looks like in Equinox 3.2.2:


BundleClassLoader createBCL(final BundleProtectionDomain pd, final String[] cp) {
BundleClassLoader bcl = bundle.getBundleData().createClassLoader(BundleLoa der.this, pd, cp);
// attach existing fragments to classloader
org.osgi.framework.Bundle[] fragments = bundle.getFragments();
if (fragments != null)
for (int i = 0; i < fragments.length; i++) {
AbstractBundle fragment = (AbstractBundle) fragments[i];
try {
bcl.attachFragment(fragment.getBundleData(), fragment.domain, fragment.getBundleData().getClassPath());
} catch (BundleException be) {
bundle.framework.publishFrameworkEvent(FrameworkEv ent.ERROR, bundle, be);
}
}

// finish the initialization of the classloader.
bcl.initialize(); <-- NPE occurs
return bcl;
}


If you're using this version then it's likely a nasty bug (maybe caused by threading if the error doesn't occur repeatedly).

regunath
Jun 6th, 2008, 01:26 AM
Thanks for the response. I use the Equinox version that came bundled with "spring-osgi-1.1.0-m2" distribution and the jar says "org.eclipse.osgi-3.2.2.jar". Btw, I use JDK 1.5.

The classpath entries for this project in my Eclipse environment(v 3.1.0) looks like this:

<?xml version="1.0" encoding="UTF-8"?>
<classpath>
<classpathentry kind="src" path="simple-service-bundle/src/main/java"/>
<classpathentry kind="src" path="simple-service-bundle/src/test/java"/>
<classpathentry kind="src" path="simple-service-integration-test/src/test/java"/>
<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER"/>
<classpathentry kind="lib" path="D:/WorkTools/spring-osgi-1.1.0-m2/lib/spring-context-2.5.4.jar"/>
<classpathentry kind="lib" path="D:/WorkTools/spring-osgi-1.1.0-m2/lib/spring-core-2.5.4.jar"/>
<classpathentry kind="lib" path="D:/WorkTools/spring-osgi-1.1.0-m2/lib/spring-test-2.5.4.jar"/>
<classpathentry kind="lib" path="D:/WorkTools/spring-osgi-1.1.0-m2/lib/spring-beans-2.5.4.jar"/>
<classpathentry exported="true" kind="lib" path="D:/WorkTools/spring-osgi-1.1.0-m2/lib/org.eclipse.osgi-3.2.2.jar"/>
<classpathentry kind="lib" path="D:/WorkTools/spring-osgi-1.1.0-m2/lib/junit.osgi-3.8.2-SNAPSHOT.jar"/>
<classpathentry kind="lib" path="D:/projects/momentum/buildscripts/lib/build/log4j-1.2.14.jar"/>
<classpathentry kind="lib" path="D:/projects/momentum/buildscripts/lib/build/commons-logging-1.0.4.jar"/>
<classpathentry kind="lib" path="D:/WorkTools/spring-osgi-1.1.0-m2/lib/slf4j-log4j12-1.4.3.jar"/>
<classpathentry kind="lib" path="D:/WorkTools/spring-osgi-1.1.0-m2/lib/slf4j-api-1.4.3.jar"/>
<classpathentry kind="lib" path="D:/WorkTools/spring-osgi-1.1.0-m2/dist/spring-osgi-web-extender-1.1.0-m2.jar"/>
<classpathentry kind="lib" path="D:/WorkTools/spring-osgi-1.1.0-m2/dist/spring-osgi-core-1.1.0-m2.jar"/>
<classpathentry kind="lib" path="D:/WorkTools/spring-osgi-1.1.0-m2/dist/spring-osgi-extender-1.1.0-m2.jar"/>
<classpathentry kind="lib" path="D:/WorkTools/spring-osgi-1.1.0-m2/dist/spring-osgi-io-1.1.0-m2.jar"/>
<classpathentry kind="lib" path="D:/WorkTools/spring-osgi-1.1.0-m2/dist/spring-osgi-mock-1.1.0-m2.jar"/>
<classpathentry kind="lib" path="D:/WorkTools/spring-osgi-1.1.0-m2/dist/spring-osgi-test-1.1.0-m2.jar"/>
<classpathentry kind="lib" path="D:/WorkTools/spring-osgi-1.1.0-m2/dist/spring-osgi-web-1.1.0-m2.jar"/>
<classpathentry kind="lib" path="D:/WorkTools/spring-osgi-1.1.0-m2/lib/spring-aop-2.5.4.jar"/>
<classpathentry kind="lib" path="D:/WorkTools/spring-osgi-1.1.0-m2/lib/aopalliance.osgi-1.0-SNAPSHOT.jar"/>
<classpathentry kind="lib" path="D:/WorkTools/spring-osgi-1.1.0-m2/lib/org.apache.felix.main-1.0.4.jar"/>
<classpathentry kind="output" path="simple-service-bundle/target/test-classes"/>
</classpath>

I was not sure if the way I have used the Spring-DM platform is OK. My need is to invoke an OSGi service (Spring bean exposed as service) from a non-OSGi environment.

I even tried looking up the service directly like:

ServiceReference sr =context.getServiceReference(MyService.class.getNa me());
System.out.println(((MyService)context.getService( sr)).stringValue());

I got a ClassCastException on the MyService interface implementation. I guess that was because the bundle and the test class load the MyService type using different classloaders. How can I share classes between OSGi bundles and non OSGi code like this class that wants to invoke an OSGi service?

I also noticed a threading issue. The above code fails at the ServiceReference lookup with a NPE. I suspected a threading issue and added a line like

Thread.sleep(3000);

before the lookup and it came through.
Finally, these are the issues:

Potential threading issue. Dirty hack was to call Thread.sleep(). Need a better option
ClassCastException if the service is looked up using BundleContext.getService(ServiceReference). Need a solution to share classes easily between an OSGi bundle and a non OSGi piece of code that invokes the bundle. Are fragment packages/extensions the solution? Does Spring DM have any support to create these?
The NPE in createBCL() method of Equinox BundleLoader class when the Spring DM OsgiBundleXmlApplicationContext is refreshed


Appreciate your response and any help in resolving the above issues.

Costin Leau
Jun 6th, 2008, 02:25 AM
If you need to share information between OSGi and non-OSGi the consider either doing some serialization/deserialization or delegating the common classes to a common/root classloader (boot path delegation).

For your case, Spring-DM could simply be installed as a bundle rather then creating it from outside OSGi.

The synchronization issue appears because the context is created in a asynch manner so you have to wait for the application context to published as a service if you want to be sure that everything is in place.
As for the NPE, as I said, it's a strange case which indicates a bug - you are probably using the OSGi platform in the wrong way but still you should get a validation error rather then a cryptic NPE.

regunath
Jun 9th, 2008, 06:02 AM
Costin,

Thanks for the tip around the boot path delegation. I am now able to access an OSGi service(exposed via Spring DM) from a non OSGi class.
The NPE with the BundleLoader class still exists though.

I do a direct ServiceReference lookup on the BundleContext and then get the Service object from it. This works.
Creating an extension to the system bundle (fragment) works. The manifest of my extension looks like this:

Manifest-Version: 1.0
Archiver-Version: Plexus Archiver
Created-By: Apache Maven
Built-By: m1000350
Build-Jdk: 1.5.0
Bundle-Version: 1.0
Bundle-SymbolicName: org.springframework.osgi.samples.simpleservice.boo t
Bundle-Name: Simple-Service-Sample-boot
Bundle-Vendor: Spring Framework
Fragment-Host: system.bundle
Export-Package: org.springframework.osgi.samples.simpleservice
Bundle-ManifestVersion: 2

An in the simple service sample's manifest , I import the package like:

Manifest-Version: 1.0
Archiver-Version: Plexus Archiver
Created-By: Apache Maven
Built-By: m1000350
Build-Jdk: 1.5.0
Bundle-Version: 1.0
Spring-Context: *;create-asynchronously=false
Bundle-SymbolicName: org.springframework.osgi.samples.simpleservice
Bundle-Name: Simple-Service-Sample
Bundle-Vendor: Spring Framework
Bundle-ManifestVersion: 2
Import-Package: org.springframework.osgi.samples.simpleservice

It would have been good if I was able to access the service via the OsgiBundleXmlApplicationContext though.

Costin Leau
Jun 20th, 2008, 07:25 AM
Spring-DM app ctx (as the rest of the other OSGi entities) can access the services available in the OSGi service registry. So as long as the service is available it can be properly accessed however, my understanding is that due to your configuration of accessing the service from outside OSGi, you might be running into class cast exceptions.
Basically, as long as your code simply interacts with the OSGi service registry, Spring-DM can do the same.

regunath
Jun 20th, 2008, 08:09 AM
Costin,

Thanks for the response. Your suggestion to use fragment bundles to get around the ClassCastException works. But as highlighted in my post, I was able to access the service via the OSGi interfaces of ServiceReference and Service and not via the OsgiBundleXmlApplicationContext due to the NPE as shown in the stack trace.

Costin Leau
Jun 20th, 2008, 08:33 AM
Right - have you tried using a different Equinox version or reporting the problem? I assume the problem might be caused by the fact that you are attaching a fragment to the system bundle which might be handled differently.

regunath
Jun 20th, 2008, 08:51 AM
Costin,

You might have noticed from the classpath details in my post that I use Equinox from "spring-osgi-1.1.0-m2/lib/org.eclipse.osgi-3.2.2.jar".
FYI, I got the NPE error even when I didnot have the fragment bundle. The error comes when I do a App context refresh() after creating it. As you can see, this is before I do an appContext.getBean()


OsgiBundleXmlApplicationContext applicationContext = new OsgiBundleXmlApplicationContext(new String[] {"file:///D:/WorkTools/spring-osgi-1.1.0-m2/src/samples/simple-service/simple-service-bundle/testbean.xml"});

applicationContext.setBundleContext(context);

applicationContext.refresh(); <--- get NPE when this is called

((ServiceConsumer)applicationContext.getBean("consumerBean")).foo(); <-- ClassCastException can occur only after i.e here