Я пытаюсь внедрить 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);
}
}
Таким образом, он работает и отправляет почту. Мои проблемы:
- Таким образом, код запускается, когда пакет становится активным, чего я не хочу.
- Я также не хочу отправлять письма с жестко заданными параметрами.
Чтобы избежать этого, я создал другой класс в этом комплекте:
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 для отслеживания службы, которую я хочу использовать? Как мне получить правильный контекст пакета?
Заранее спасибо!