Can't get BundleContext outside Activator class

968 views Asked by At

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:

  1. This way the code runs when the bundle gets active, which I don't want.
  2. 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!

0

There are 0 answers