0

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!

Tamas G.
  • 677
  • 3
  • 21
  • 38
  • Is the CashDeskFunction loaded by OSGi, or are you kind of bolting OSGi onto an existing application? – Brett Kail May 14 '15 at 14:03
  • The second one. The point would be that I could call from the non-OSGi part of my application into an active OSGi bundle which then communicates with another bundles with OSGi services. Is it possible? – Tamas G. May 14 '15 at 14:06
  • 1
    Not easily. There are a few challenges: (1) you either need to ensure the MailSenderService interface is loaded by your application and the OSGi framework delegates to it, and (2) you need to somehow pass a reference to an OSGi object to CashDeskFunction (either the osgiFramework or some object derived from it, like a specific Bundle/BundleContext). – Brett Kail May 14 '15 at 14:17
  • Well, these are bad news for me. Thanks anyway! – Tamas G. May 14 '15 at 14:36
  • In this SO post Neil's answer seems interesting. I'll give it a shot. I mean this part: "The trick to creating visibility between OSGi bundles and the objects "outside" OSGi is to publish and/or consume services using the system bundle's BundleContext." – Tamas G. May 14 '15 at 14:55
  • And of course I forgot the link: http://stackoverflow.com/questions/24411334/how-do-you-call-an-osgi-application-from-a-non-osgi-application-and-vice-versa – Tamas G. May 14 '15 at 16:15

0 Answers0