简体   繁体   中英

How to use an OSGi service from a web application?

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.

With the environment felix-server and on jetty running webservices you can easily use any OSGi-services within any webservices.

First you have to inject the ServletContext in your webservice so that you can access the OSGi-context by calling servletContext.getAttribute("osgi-bundlecontext"). The result is your OSGi-bundle context.

Please find the full example on http://blog.meyerdaniel.ch/2012/08/accessing-osgi-services-from-servlets.html

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.

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.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM