package com.github.dynamicextensionsalfresco.proxy; import com.github.dynamicextensionsalfresco.osgi.FrameworkManager; import com.github.dynamicextensionsalfresco.osgi.FrameworkService; import org.osgi.framework.Filter; import org.osgi.framework.ServiceReference; import org.osgi.util.tracker.ServiceTracker; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * Tracks services within the embedded OSGi context using interface matching or a custom {@link Filter}. * When {@link #invokeUsing(ServiceInvoker)} is called, the {@link ServiceInvoker} receives the sole matching instance. * This pattern is used so that we can avoid keeping a reference to the service. (dynamic nature of service discovery) * * @author Laurent Van der Linden */ public class Tracker { private final static Logger logger = LoggerFactory.getLogger(Tracker.class); private final FilterModel filterModel; private final FrameworkService frameworkService; private volatile ServiceTracker<Object,Object> serviceTracker; private volatile FrameworkManager frameworkManager; public Tracker(FilterModel filterModel, FrameworkService frameworkService) { this.filterModel = filterModel; this.frameworkService = frameworkService; } /** * Perform an action on a service. * @param serviceInvoker action to receive a reference to the service * @param <T> the target service type * @return result from the service invocation * @throws IllegalStateException if an exact service match is not possible */ @SuppressWarnings("unchecked") public <T> Object invokeUsing(ServiceInvoker<T> serviceInvoker) throws Throwable { try { if (frameworkManager != null && frameworkManager != frameworkService.getFrameworkManager()) { // the OSGi context was restarted, discard any references closeServiceTracker(); frameworkManager = null; } if (serviceTracker == null) { synchronized (this) { if (serviceTracker == null) { if (frameworkManager == null) { frameworkManager = frameworkService.getFrameworkManager(); } serviceTracker = new ServiceTracker<Object, Object>( frameworkManager.getFramework().getBundleContext(), filterModel.getServiceFilter(), null) ; serviceTracker.open(true); logger.debug("Opened servicetracker using filter {}.", filterModel.getServiceFilter()); } } } } catch (IllegalStateException e) { throw new IllegalStateException("OSGi Framework is not ready yet.", e); } final ServiceReference<Object>[] serviceReferences = serviceTracker.getServiceReferences(); if (serviceReferences == null) { throw new IllegalStateException( String.format("No Dynamic service found using filter %s.", filterModel.getServiceFilter()) ); } if (serviceReferences.length != 1) { throw new IllegalStateException( String.format("More then 1 service found using filter %s.", filterModel.getServiceFilter()) ); } final ServiceReference<Object> soleReference = serviceReferences[0]; final Object service = serviceTracker.getService(soleReference); try { return serviceInvoker.invokeService((T) service); } finally { serviceTracker.removedService(soleReference, service); } } private synchronized void closeServiceTracker() { if (serviceTracker != null) { serviceTracker.close(); serviceTracker = null; logger.debug("Closed servicetracker using filter {}.", filterModel.getServiceFilter()); } } public FilterModel getFilterModel() { // add a hook to reset the ServiceTracker return new FilterModel() { @Override public Filter getServiceFilter() { return filterModel.getServiceFilter(); } @Override public void setServiceFilter(Filter serviceFilter) { filterModel.setServiceFilter(serviceFilter); closeServiceTracker(); } @Override public void setServiceFilterString(String ldapFilter) { filterModel.setServiceFilterString(ldapFilter); closeServiceTracker(); } }; } }