/******************************************************************************* * Copyright (c) 2008 IBM Corporation and others. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v10.html * * Contributors: * IBM Corporation - initial API and implementation *******************************************************************************/ package org.eclipse.osgi.internal.composite; import java.util.*; import org.eclipse.osgi.util.ManifestElement; import org.osgi.framework.*; import org.osgi.util.tracker.ServiceTracker; import org.osgi.util.tracker.ServiceTrackerCustomizer; class CompositeServiceTracker implements ServiceTrackerCustomizer { final BundleContext sourceContext; final BundleContext targetContext; final ServiceTracker[] trackers; final String[] filters; /* @GuardedBy("serviceComposites") */ final HashMap serviceComposites = new HashMap(); public CompositeServiceTracker(BundleContext sourceContext, BundleContext targetContext, String serviceFilters) { this.sourceContext = sourceContext; this.targetContext = targetContext; filters = ManifestElement.getArrayFromList(serviceFilters, ","); //$NON-NLS-1$ trackers = new ServiceTracker[filters.length]; } synchronized void open() { for (int i = 0; i < trackers.length; i++) { try { trackers[i] = new ServiceTracker(sourceContext, sourceContext.createFilter(filters[i]), this); trackers[i].open(); } catch (InvalidSyntaxException e) { // TODO log // we will skip this filter; note that trackers may have null entries } } } synchronized void close() { for (int i = 0; i < trackers.length; i++) { if (trackers[i] != null) trackers[i].close(); } } public Object addingService(ServiceReference reference) { ServiceLink serviceLink; int useCount; synchronized (serviceComposites) { serviceLink = (ServiceLink) serviceComposites.get(reference); if (serviceLink == null) { serviceLink = new ServiceLink(reference); serviceComposites.put(reference, serviceLink); } useCount = serviceLink.incrementUse(); } // register service outside of the sync block if (useCount == 1) serviceLink.register(); return serviceLink; } public void modifiedService(ServiceReference reference, Object service) { ServiceLink serviceLink = (ServiceLink) service; Dictionary serviceProps = null; synchronized (serviceComposites) { serviceProps = serviceLink.getRefreshProperties(); } // set service properties out side the sync block if (serviceProps != null) ((ServiceLink) service).setServiceProperties(serviceProps); } public void removedService(ServiceReference reference, Object service) { int useCount; synchronized (serviceComposites) { useCount = ((ServiceLink) service).decrementUse(); if (useCount == 0) serviceComposites.remove(reference); } // unregister outside the sync block if (useCount == 0) ((ServiceLink) service).unregister(); } class ServiceLink implements ServiceFactory { private final ServiceReference reference; private volatile ServiceRegistration registration; /* @GuardedBy("this") */ private Object service; /* @GuardedBy("serviceLinks") */ private int useCount; ServiceLink(ServiceReference reference) { this.reference = reference; } /* @GuardedBy("serviceLinks") */ Dictionary getRefreshProperties() { Dictionary result = getServiceProperties(); if (useCount <= 1) return result; // need to do an expensive properties check to avoid multiple registration property changes String[] originalKeys = registration.getReference().getPropertyKeys(); for (int i = 0; i < originalKeys.length; i++) { if (!Constants.OBJECTCLASS.equals(originalKeys[i]) && !Constants.SERVICE_ID.equals(originalKeys[i])) // identity compare is done on purpose here to catch any kind of change if (registration.getReference().getProperty(originalKeys[i]) != result.get(originalKeys[i])) return result; } for (Enumeration eKeys = result.keys(); eKeys.hasMoreElements();) { String key = (String) eKeys.nextElement(); if (!Constants.OBJECTCLASS.equals(key) && !Constants.SERVICE_ID.equals(key)) // identity compare is done on purpose here to catch any kind of change if (result.get(key) != registration.getReference().getProperty(key)) return result; } return null; } /* @GuardedBy("serviceLinks") */ int decrementUse() { return --useCount; } /* @GuardedBy("serviceLinks") */ int incrementUse() { return ++useCount; } /* @GuardedBy("serviceLinks") */ int getUse() { return useCount; } void setServiceProperties(Dictionary props) { ServiceRegistration current = registration; if (current != null) current.setProperties(props); } void register() { Dictionary props = getServiceProperties(); registration = targetContext.registerService((String[]) props.get(Constants.OBJECTCLASS), this, props); } void unregister() { ServiceRegistration current = registration; if (current != null) current.unregister(); } private Dictionary getServiceProperties() { String[] keys = reference.getPropertyKeys(); Hashtable serviceProps = new Hashtable(keys.length); for (int i = 0; i < keys.length; i++) serviceProps.put(keys[i], reference.getProperty(keys[i])); return serviceProps; } public synchronized Object getService(Bundle bundle, ServiceRegistration reg) { if (service == null) service = sourceContext.getService(reference); return service; } public void ungetService(Bundle bundle, ServiceRegistration reg, Object serv) { // nothing } } }