Не удается получить BundleContext вне класса Activator

Я пытаюсь внедрить OSGi в свое приложение, следуя руководству Нила Бартлетта. Мой код таков:

public class OsgiInitServletContextListener implements ServletContextListener {

        @Override
        public void contextInitialized(ServletContextEvent sce) {

            OsgiFramework.initOsgiFramework(sce.getServletContext());

        }

        @Override
        public void contextDestroyed(ServletContextEvent sce) {

            OsgiFramework.destroyOsgiFrameWork();

        }

}

А класс OsgiFramework таков:

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();
            }
        }
    }
}

Мой класс BundleStarter получает все пакеты из папки "bundles" моего приложения, затем устанавливает их и пытается запустить.

Затем у меня есть пакет под названием MailSenderService, который имеет интерфейс и реализацию для него. Манифест выглядит так:

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
...

А активатор очень простой:

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");
    }
}

Я думаю, что пока это работает хорошо.

Но затем у меня есть еще один пакет, который пытается использовать MailSenderService.

Манифест:

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

Активатор такой:

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;

    }

}

И вот идет моя борьба. ЕСЛИ я реализую MailSenderServiceTracker следующим образом:

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);
    }
}

Таким образом, он работает и отправляет почту. Мои проблемы:

  1. Таким образом, код запускается, когда пакет становится активным, чего я не хочу.
  2. Я также не хочу отправлять письма с жестко заданными параметрами.

Чтобы избежать этого, я создал другой класс в этом комплекте:

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();
    }
}

Метод sendCDDataInBarcodeViaEmail вызывается, когда пользователь нажимает кнопку. Если я использую этот класс, я, конечно, не создаю mailSenderST в классе активатора.

Я думаю, что я буду отслеживать сервис, который хочу использовать, только тогда, когда я хочу его использовать, но это не работает, потому что строка FrameworkUtil. getBundle(MailSenderService.getClass()). getBundleContext() дает null.

Любые идеи о том, что я делаю неправильно? Как должен выглядеть xxxServiceTracker? Когда мне следует создать xxxServiceTracker для отслеживания службы, которую я хочу использовать? Как мне получить правильный контекст пакета?

Заранее спасибо!


person Tamas G.    schedule 14.05.2015    source источник
comment
Загружается ли CashDeskFunction из OSGi, или вы как бы прикручиваете OSGi к существующему приложению?   -  person Brett Kail    schedule 14.05.2015
comment
Второй. Дело в том, что я мог бы вызвать из не-OSGi части моего приложения активный пакет OSGi, который затем взаимодействует с другими пакетами с услугами OSGi. Является ли это возможным?   -  person Tamas G.    schedule 14.05.2015
comment
Не легко. Есть несколько проблем: (1) вам либо нужно убедиться, что интерфейс MailSenderService загружается вашим приложением, и структура OSGi делегирует ему, и (2) вам нужно каким-то образом передать ссылку на объект OSGi в CashDeskFunction (либо osgiFramework или какой-либо производный от него объект, например определенный Bundle/BundleContext).   -  person Brett Kail    schedule 14.05.2015
comment
Что ж, это плохие новости для меня. Спасибо, в любом случае!   -  person Tamas G.    schedule 14.05.2015
comment
В этом сообщении SO ответ Нила кажется интересным. Я попробую. Я имею в виду эту часть: Хитрость в создании видимости между пакетами OSGi и объектами вне OSGi заключается в публикации и/или использовании сервисов с использованием BundleContext системного пакета.   -  person Tamas G.    schedule 14.05.2015
comment
И, конечно же, я забыл ссылку: stackoverflow.com/questions/24411334/   -  person Tamas G.    schedule 14.05.2015