OSGi Service Trackers
Requirement is to have a dynamic mapping with an interface implementation provided by the main OSGi bundle. At a given time there could only be a default and a single custom implementation. For this purpose I'm using OSGi ServiceTracker to dynamically assign the implementation.
Use case:
This interface "org.siriwardana.sample.core.MessageHandlerFactory" will be exported by the core bundle and will be implemented by the default bundle and a custom implementation.
Following is the org.siriwardana.sample.core.MessageHandler interface which is also exported by the core bundle.
Default service bundle and a custom implementation service bundle will be available at runtime and the custom implementation will get the priority by the consumer bundle.
Solution:
ServiceTracker (org.osgi.util.tracker.ServiceTracker) and ServiceTrackerCustomizer (org.osgi.util.tracker.ServiceTrackerCustomizer) will be used to dynamically used by the consumer bundle.
Following is the custom implementation bundle which will register it's service for the MessageHandlerFactory interface.
When every an Interface implementation is available, ServiceTracker will update the consumer bundle.
Use case:
This interface "org.siriwardana.sample.core.MessageHandlerFactory" will be exported by the core bundle and will be implemented by the default bundle and a custom implementation.
/** * * Interface for message handler factory. Custom deployments should implement this interface */ public interface MessageHandlerFactory { MessageHandler getHandler(String messageType); }
Following is the org.siriwardana.sample.core.MessageHandler interface which is also exported by the core bundle.
/** * * Custom message handler which should be implemented by the custom deployment bundle to handle */ public interface MessageHandler { /** *Create the message with a custom implementation. * @return String */ String createReqMsg(); /** * * Handle response of the request as per the custom implementation. */ void handleResponse (String response); /** * * Handle error as per the custom implementation */ void onError(Exception e); }
Default service bundle and a custom implementation service bundle will be available at runtime and the custom implementation will get the priority by the consumer bundle.
Solution:
ServiceTracker (org.osgi.util.tracker.ServiceTracker) and ServiceTrackerCustomizer (org.osgi.util.tracker.ServiceTrackerCustomizer) will be used to dynamically used by the consumer bundle.
Following bundle activator implementation will demonstrates the solution.
/** * @scr.component name="org.siriwardana.sample.consumer" immediate="true" */ public class ServiceComponent { private static Log LOGGER = LogFactory.getLog(ServiceComponent.class); private static final String MESSAGE_HANDLER_DEFAULT = "default"; private ServiceTracker serviceTracker; private BundleContext bundleContext; private ServiceRegistration defaultHandlerRef; @SuppressWarnings("unchecked") protected void activate(ComponentContext context) { bundleContext = context.getBundleContext(); Dictionary<String, String> props = new Hashtable<>(); props.put(Constants.MESSAGE_HANDLER_KEY, MESSAGE_HANDLER_DEFAULT); if (bundleContext != null) { ServiceTrackerCustomizer trackerCustomizer = new Customizer(); serviceTracker = new ServiceTracker(bundleContext, MessageHandlerFactory.class.getName(), trackerCustomizer); serviceTracker.open(); LOGGER.debug("ServiceTracker initialized"); } else { LOGGER.error("BundleContext cannot be null"); } } protected void deactivate(ComponentContext context) { defaultHandlerRef.unregister(); serviceTracker.close(); serviceTracker = null; LOGGER.debug("ServiceTracker stopped. Cloud Default handler bundle deactivated."); } private void setMessageHandlerFactory(ServiceReference<?> reference) { MessageHandlerFactory handlerFactory = (MessageHandlerFactory) bundleContext.getService(reference); LOGGER.debug("MessageHandlerFactory is acquired"); ServiceDataHolder.getInstance().setHandlerFactory(handlerFactory); } private void unsetMessageHandlerFactory(MessageHandlerFactory handlerFactory) { LOGGER.debug("MessageHandlerFactory is released"); ServiceDataHolder.getInstance().setHandlerFactory(null); } /** * * Service tracker for Message handler factory implementation */ private class Customizer implements ServiceTrackerCustomizer { @SuppressWarnings("unchecked") public Object addingService(ServiceReference serviceReference) { LOGGER.debug("ServiceTracker: service added event invoked"); ServiceReference serviceRef = updateMessageHandlerService(); return bundleContext.getService(serviceRef); } public void modifiedService(ServiceReference reference, Object service) { LOGGER.debug("ServiceTracker: modified service event invoked"); updateMessageHandlerService(); } @SuppressWarnings("unchecked") public void removedService(ServiceReference reference, Object service) { if (reference != null) { MessageHandlerFactory handlerFactory = (MessageHandlerFactory) bundleContext.getService(reference); unsetMessageHandlerFactory(handlerFactory); LOGGER.debug("ServiceTracker: removed service event invoked"); updateMessageHandlerService(); } } private ServiceReference updateMessageHandlerService() { ServiceReference serviceRef = null; try { ServiceReference<?>[] references = bundleContext .getAllServiceReferences(MessageHandlerFactory.class.getName(), null); for(ServiceReference<?> reference : references) { serviceRef = reference; if (!MESSAGE_HANDLER_DEFAULT .equalsIgnoreCase((String) reference.getProperty(Constants.MESSAGE_HANDLER_KEY))) { break; } } if (serviceRef != null) { LOGGER.debug("ServiceTracker: HandlerFactory updated. Service reference: " + serviceRef); setMessageHandlerFactory(serviceRef); } else { LOGGER.debug("ServiceTracker: HandlerFactory not updated: Service reference is null"); } } catch (InvalidSyntaxException e) { LOGGER.error("ServiceTracker: Error while updating the MessageHandlers. ", e); } return serviceRef; } } }
Following is the custom implementation bundle which will register it's service for the MessageHandlerFactory interface.
/** * @scr.component name="org.siriwardana.custom" immediate="true" */ public class CustomMessageHandlerFactoryComponent { private static final String MESSAGE_HANDLER = "CUSTOM"; private static Log LOGGER = LogFactory.getLog(CustomMessageHandlerFactoryComponent.class); private ServiceRegistration serviceRef; protected void activate(ComponentContext context) { BundleContext bundleContext = context.getBundleContext(); Dictionary<String, String> props = new Hashtable<>(); props.put(Constants.MESSAGE_HANDLER_KEY, MESSAGE_HANDLER); serviceRef = bundleContext.registerService(MessageHandlerFactory.class, new CustomMessageHandlerFactory(), props); LOGGER.debug("Custom Message handler impl bundle activated "); } protected void deactivate(ComponentContext context) { serviceRef.unregister(); LOGGER.debug("Custom Message handler impl bundle deactivated "); } }
When every an Interface implementation is available, ServiceTracker will update the consumer bundle.
Comments
Post a Comment