blob: a9a449b404437dfa6fd8c8ac0dee2554ccb6ff6f [file] [log] [blame]
/*******************************************************************************
* 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
}
}
}