I am trying to embed OSGi in my application following Neil Bartlett's tutorial. My code is this:
public class OsgiInitServletContextListener implements ServletContextListener {
@Override
public void contextInitialized(ServletContextEvent sce) {
OsgiFramework.initOsgiFramework(sce.getServletContext());
}
@Override
public void contextDestroyed(ServletContextEvent sce) {
OsgiFramework.destroyOsgiFrameWork();
}
}
And the OsgiFramework
class is this:
public class OsgiFramework {
private static Framework osgiFramework = null;
private static BundleContext bundleContext = null;
public static Framework getOsgiFramework() {
return osgiFramework;
}
public static BundleContext getBundleContext() {
return bundleContext;
}
public static synchronized void initOsgiFramework(ServletContext servletContext) {
if (osgiFramework == null) {
Map<String, String> config = new HashMap<String, String>();
config.put(Constants.FRAMEWORK_STORAGE, "/home/tamasg/osgidata");
config.put(Constants.FRAMEWORK_STORAGE_CLEAN, "true");
config.put(
Constants.FRAMEWORK_SYSTEMPACKAGES_EXTRA,
... /*lot of packages*/);
try {
FrameworkFactory frameworkFactory = ServiceLoader.load(FrameworkFactory.class).iterator().next();
osgiFramework = frameworkFactory.newFramework(config);
osgiFramework.start();
bundleContext = osgiFramework.getBundleContext();
BundleStarter starter = new BundleStarter(servletContext, bundleContext);
starter.launch();
} catch (Exception e) {
e.printStackTrace();
}
}
}
public static synchronized void destroyOsgiFrameWork() {
if (OsgiFramework.osgiFramework != null) {
try {
OsgiFramework.osgiFramework.stop();
} catch (BundleException e) {
e.printStackTrace();
}
}
}
}
My BundleStarter
class gets all the bundles from my app's 'bundles' folder, then installs them and tries to start them.
Then I have a bundle called MailSenderService
which has an interface and and implementation for it. The manifest looks like this:
Manifest-Version: 1.0
...
Bundle-SymbolicName:
Bundle-Activator: com.rr.fr.base.mail.activator.Activator
Bundle-Version: 1.0.7.SNAPSHOT
...
Export-Package: com.rr.fr.base.mail.service;version="1.0.7"
Import-Package: com.fusionr.fnd.zeus.tools.mail,com.fusionr.fnd.zeus.tools.message,com.rr.fr.base.exception,com.rr.fr.base.messages,com.rr.fr.base.persistence,com.rr.fr.base.security,com.rr.fr.base.system,com.rr.fr.base.types,com.rr.fr.interfaces.eb.rms,fusionr.fnd.zeus.springbean,javax.activation,javax.mail;version="[1.4,2)",javax.mail.internet;version="[1.4,2)",javax.mail.util;version="[1.4,2)",org.apache.commons.logging, org.osgi.framework;version="[1.5,2)",org.springframework.context
...
And the activator is real simple:
public class Activator implements BundleActivator {
private ServiceRegistration serviceReg;
public static BundleContext bc = null;
@Override
public void start(BundleContext context) throws Exception {
bc = context;
System.out.println(bc.getBundle().getHeaders().get(
Constants.BUNDLE_NAME) + " starting...");
serviceReg = bc.registerService(
MailSenderService.class.getName(),
new FRMailSenderAPI(), null);
System.out.println("Service registered: MailSenderService");
}
@Override
public void stop(BundleContext context) throws Exception {
System.out.println(bc.getBundle().getHeaders().get(
Constants.BUNDLE_NAME) + " stopping...");
bc = null;
serviceReg.unregister();
System.out.println("Service stopped: MailSenderService");
}
}
I think so far it works well.
But then I have another bundle which tries to use the MailSenderService.
Manifest:
Manifest-Version: 1.0
...
Bundle-Activator: com.rr.fr.base.cdfunc.activator.Activator
...
Bundle-ManifestVersion: 2
...
Bundle-ClassPath: .
Import-Package: com.rr.fr.base.barcode,com.rr.fr.base.
barcode.service,com.rr.fr.base.mail.service,com.rr.fr
.base.osgi.framework,org.apache.avalon.framework.configuration,org.krys
alis.barcode4j,org.osgi.framework,org.osgi.util.track
er
The activator is this:
public class Activator implements BundleActivator {
private BundleContext bc = null;
private MailSenderServiceTracker mailSenderST = null;
@Override
public void start(BundleContext context) throws Exception {
this.bc = context;
System.out.println(bc.getBundle().getHeaders().get(Constants.BUNDLE_NAME) + " starting...");
mailSenderST = new MailSenderServiceTracker(bc);
mailSenderST.open();
System.out.println("The MailSenderServiceTracker is opened.");
}
@Override
public void stop(BundleContext context) throws Exception {
System.out.println(bc.getBundle().getHeaders().get(Constants.BUNDLE_NAME) + " stopping...");
mailSenderST.close();
mailSenderST = null;
System.out.println("The MailSenderServiceTracker is closed.");
this.bc = null;
}
}
And here comes my struggle. IF I implement the MailSenderServiceTracker
like this:
public class MailSenderServiceTracker extends ServiceTracker {
public MailSenderServiceTracker(BundleContext context) {
super(context, MailSenderService.class.getName(), null);
}
@Override
public Object addingService(ServiceReference reference) {
System.out.println("Inside MailSenderServiceTracker.addingService " + reference.getBundle());
MailSenderService mss = (MailSenderService) context.getService(reference);
mss.sendMail(parameters...);
return mss;
}
@Override
public void removedService(ServiceReference reference, Object service) {
System.out.println("Inside MailSenderServiceTracker.removedService " + reference.getBundle());
super.removedService(reference, service);
}
}
This way it works, and sends a mail. My problems are:
- This way the code runs when the bundle gets active, which I don't want.
- I also don't want to send mails with hard coded parameters.
To avoid these, I created an other class in this bundle:
public class CashDeskFunction {
public void sendCDDataInBarcodeViaEmail(Map<String, Object> data) {
MailSenderServiceTracker mailSenderST = new MailSenderServiceTracker(FrameworkUtil.
getBundle(MailSenderService.getClass()).
getBundleContext());
mailSenderST.open();
System.out.println("The MailSenderServiceTracker is opened.");
MailSenderService mailSenderService = (MailSenderService) mailSenderST.getService();
mailSenderService.sendMail(data);
mailSenderST.close();
}
}
The sendCDDataInBarcodeViaEmail
method is invoked when the user clicks on a button. If I use this class, I don't create the mailSenderST in the activator class of course.
I think that I shall only track the service I want to use, when I want to use it, but it doesn't work this way, because the FrameworkUtil.
getBundle(MailSenderService.getClass()).
getBundleContext()
line gives null
.
Any ideas on what am I doing wrong? How should a xxxServiceTracker look like? When should I create a xxxServiceTracker to track a service I want to use? How should I get the right bundle context?
Thanks in advance!