views:

209

answers:

2

I'm trying to develop a web application that is going to be launched from a HTTP OSGi service, this application needs to use other OSGi service (db4o OSGi), for what I need a reference to a BundleContext. I have tried two different approaches to get the OSGi context in the web application:

  1. Store the BundleContext of the Activator in a static field of a class that the web service can import and use.
  2. Use FrameworkUtil.getBundle(this.getClass()).getBundleContext() (being this an instance of MainPage, a class of the web application).

I think that first option is completely wrong, but anyway I'm having problems with the class loaders in both options. In the second one it raises a LinkageError:

java.lang.LinkageError: loader constraint violation: loader (instance of org/apache/felix/framework/ModuleImpl$ModuleClassLoader) previously initiated loading for a different type with name "com/db4o/ObjectContainer"

Also tried with Equinox and I have a similar error:

java.lang.LinkageError: loader constraint violation: loader (instance of org/eclipse/osgi/internal/baseadaptor/DefaultClassLoader) previously initiated loading for a different type with name "com/db4o/ObjectContainer"

The code that provokes the exception is:

ServiceReference reference = context.getServiceReference(Db4oService.class.getName());
Db4oService service = (Db4oService)context.getService(reference);
database = service.openFile("foo.db");

The exception is raised in the last line, database class is ObjectContainer, if I change the type of this variable to Object the exception is not raised, but it's not useful as an Object :)

Update: I have tried to use other services instead of db4o and they worked as expected. Maybe db4o OSGi bundle does something strange when loading its own classes, or maybe I'm not using it correctly. It also works if I use it from a non-web bundle.

A: 

I'm not 100% sure this will help you, but you could try setting the thread's context class loader before trying to access the class in the other bundle:

Thread currentThread = Thread.currentThread ();
ClassLoader origLoader = currentThread.getContextClassLoader ();

currentThread.setContextClassLoader (Db4oService.class.getClassLoader ());

ServiceReference reference = context.getServiceReference(Db4oService.class.getName());
Db4oService service = (Db4oService)context.getService(reference);
database = service.openFile("foo.db");

currentThread.setContextClassLoader (origLoader);

It looks like OSGi is detecting that the an already-loaded class from another bundle (Db4oService) will be loaded by this class loader.

Matthew Phillips
I tried your solution but I still have the same issue. Thanks.
Jaime Soriano
A: 

Why not passing the BundleContext in the constructor of the servlet class? That class can safely store the context since the service is stopped when the bundle is stopped (and the BundleContext becomes invalid).

I recommend to avoid using classloaders in OSGi at all, since a) the OSGi framework does a lot of classloader magic in order to separate the bundles from each others, and b) you might run in a lot of problems when OSGi and Java 2 security is enabled. This will most probably reduce the reusability of your bundle.

akr
It wouldn't be the same that storing the context in a static field as I did in my first approach?About playing with Classloaders, yes, I think the same. Thanks.
Jaime Soriano
Yes, it is basically the same, just another approach. You should only pass the bundle context to objects from the same bundle.
akr