blob: d3410b00876e9467ca3ccc3d0adda670de5cc44e [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2009 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.e4.internal.core.services.osgi;
import java.util.*;
import org.eclipse.e4.core.services.IDisposable;
import org.eclipse.e4.core.services.context.IEclipseContext;
import org.eclipse.e4.core.services.context.spi.ILookupStrategy;
import org.osgi.framework.*;
import org.osgi.util.tracker.ServiceTracker;
import org.osgi.util.tracker.ServiceTrackerCustomizer;
/**
* A context strategy that provides access to OSGi services.
* <p>
* OSGi services are looked up by service class name.
*/
public class OSGiContextStrategy implements ILookupStrategy, IDisposable, ServiceTrackerCustomizer {
class ServiceData {
//the service name
String name;
ServiceTracker tracker;
//the contexts using this service (IEclipseContext -> null)
final Map users = new WeakHashMap();
ServiceData(String name) {
this.name = name;
}
public void addContext(IEclipseContext originatingContext) {
users.put(originatingContext, null);
}
}
private final BundleContext bundleContext;
/**
* Map of String (service name) -> ServiceData
*/
private Map services = Collections.synchronizedMap(new HashMap());
public OSGiContextStrategy(BundleContext bc) {
super();
this.bundleContext = bc;
}
public Object addingService(ServiceReference reference) {
String name = serviceName(reference);
Object newValue = bundleContext.getService(reference);
if (newValue == null)
return null;
//for performance we store the concrete service object with each context that requested it
ServiceData data = (ServiceData) services.get(name);
for (Iterator it = data.users.keySet().iterator(); it.hasNext();)
((IEclipseContext) it.next()).set(name, newValue);
return newValue;
}
/**
* Discards any services that are no longer used by any strongly
* reachable contexts.
*/
private void cleanReferences() {
synchronized (services) {
for (Iterator it = services.values().iterator(); it.hasNext();) {
ServiceData data = (ServiceData) it.next();
//if there are no more references, discard the service
if (data.users.isEmpty()) {
data.tracker.close();
it.remove();
}
}
}
}
public boolean containsKey(String name, IEclipseContext context) {
cleanReferences();
return bundleContext.getServiceReference(name) != null;
}
public void dispose() {
synchronized (services) {
for (Iterator it = services.values().iterator(); it.hasNext();)
((ServiceData) it.next()).tracker.close();
services.clear();
}
}
public Object lookup(String name, IEclipseContext originatingContext) {
cleanReferences();
ServiceData data = (ServiceData) services.get(name);
if (data == null) {
data = new ServiceData(name);
data.tracker = new ServiceTracker(bundleContext, name, this);
services.put(name, data);
//just opening a tracker will cause values to be set by the tracker callback methods
data.tracker.open();
}
data.addContext(originatingContext);
return data.tracker.getService();
}
public void modifiedService(ServiceReference reference, Object service) {
String name = serviceName(reference);
ServiceData data = (ServiceData) services.get(name);
for (Iterator it = data.users.keySet().iterator(); it.hasNext();)
((IEclipseContext) it.next()).set(name, service);
}
public void removedService(ServiceReference reference, Object service) {
String name = serviceName(reference);
//must set to null rather than removing so injection continues to work
ServiceData data = (ServiceData) services.get(name);
for (Iterator it = data.users.keySet().iterator(); it.hasNext();)
((IEclipseContext) it.next()).set(name, null);
bundleContext.ungetService(reference);
}
/**
* Returns the service name for a service reference
*/
private String serviceName(ServiceReference reference) {
return ((String[]) reference.getProperty(Constants.OBJECTCLASS))[0];
}
}