Eclipse Equinox einfache (programmatische) Verwendung von OSGi Services

Thomas Darimont

Erfahrenes Mitglied
Hallo,

die OSGi Plattform bietet mit den OSGi Services eine Möglichkeit Service Komponenten dynamisch abfragen und registrieren zu können.
Dies kann entweder programmatisch (mit BundleContext.registerService, ServiceReference, ServiceTracker) oder deklarativ
über sogenannte Declarative Services geschehen.

Will man über den programmatischen Weg einen Service binden so ist das wegen der dynamischen Natur der OSGi Umgebung
(Bundles die Services bereitstellen können im laufenden Betrieb gestoppt / neugestartet werden) etwas aufwendiger siehe:
http://www.tutorials.de/forum/java/...-von-osgi-services-auf-basis-von-equinox.html
http://www.tutorials.de/forum/java/...um-eventadmin-service-unter-osgi-equinox.html

Hier eine Liste zu Standard OSGi Services (für Equinox):
http://www.eclipse.org/equinox/bundles/index.php#bundleprojects

Hier mal ein kleines Beispiel wie man mit dynamischen Services in einer OSGi Umgebung arbeiten kann.

Dabei wird für die Arbeit mit dem Service ein ServiceCallback definiert. Über den generischen Typ des ServiceCallbacks
wird dann die entsprechende Service ermittelt welcher für den Aufruf verwendet werden soll.

Die wichtigsten Methoden des ServiceCallback sind runWith(...) und runWithServiceUnavailable().
runWith(...) wird Aufgerufen wenn der verlangte Service zur Verfügung steht andernfalls wird
runWithServiceUnvailable() aufgerufen.

Java:
package de.tutorials.osgi.training;

import org.osgi.framework.BundleContext;

public abstract class ServiceCallback<TService> {
	
	abstract void runWith(TService service);
	
	protected void runWithServiceUnavailable(){
		//default noop
	}
	
	protected BundleContext getBundleContext(){
		return null;
	}
	
	protected boolean shouldUngetServiceAfterUse(){
		return false;
	}
}

Zu Demonstrationszwecken nehmen wir hier als Beispiel-Service den EventAdmin-Service:

Java:
package de.tutorials.osgi.training;

import org.osgi.framework.BundleActivator;
import org.osgi.framework.BundleContext;
import org.osgi.service.event.EventAdmin;

public class Activator implements BundleActivator {

	private ServiceTrackers serviceTrackers;

	public void start(final BundleContext context) throws Exception {
		serviceTrackers = new ServiceTrackers(context);

		log("starting " + context.getBundle().getSymbolicName());

		serviceTrackers.execute(new ServiceCallback<EventAdmin>() {
			protected void runWith(EventAdmin service) {
				log("Service is available: " + service);
			}

			protected void runWithServiceUnavailable() {
				log("Service is unavailable: let's do something else...");
			}
		});
	}

	public void log(final String message) {
		System.out.println("Log: " + message);
	}

	public void stop(BundleContext context) throws Exception {
		log("stopping " + context.getBundle().getSymbolicName());
		serviceTrackers.close();
		serviceTrackers = null;
	}
}

Unser "Workhorse" ServiceTrackers erledigt nun das verwalten der Service Referenzen / Trackers umd kümmert sich um den Aufruf
der passenden Methode am ServiceCallback.

Java:
package de.tutorials.osgi.training;

import java.lang.reflect.ParameterizedType;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;

import org.osgi.framework.BundleContext;
import org.osgi.util.tracker.ServiceTracker;

public class ServiceTrackers {

	private BundleContext context;
	private ConcurrentMap<String, ServiceTracker> cachedTrackers;

	public ServiceTrackers(BundleContext context) {
		this.context = context;
		this.cachedTrackers = new ConcurrentHashMap<String, ServiceTracker>();
	}

	public <TService> void execute(ServiceCallback<TService> serviceCallback) {
		BundleContext bundleContext = extractBundleContext(serviceCallback);
		
		Class<?> serviceClass = extractServiceClass(serviceCallback);
		String serviceName = serviceClass.getName();

		ServiceTracker tracker = getCachedTracker(bundleContext, serviceName);

		@SuppressWarnings("unchecked")
		TService service = (TService) tracker.getService();
		if (service != null) {
			serviceCallback.runWith(service);
		} else {
			serviceCallback.runWithServiceUnavailable();
		}

		if (serviceCallback.shouldUngetServiceAfterUse()) {
			tracker = cachedTrackers.remove(serviceName);
			tracker.close();
		}
	}
	
	protected <TService> BundleContext extractBundleContext(ServiceCallback<TService> serviceCallback){
		BundleContext bundleContext = serviceCallback.getBundleContext();

		if (bundleContext == null) {
			bundleContext = this.context;
		}
		
		return bundleContext;
	}

	protected ServiceTracker getCachedTracker(BundleContext bundleContext, String serviceName) {
		ServiceTracker tracker = null;
		if (cachedTrackers.containsKey(serviceName)) {
			tracker = cachedTrackers.get(serviceName);
		} else {
			tracker = new ServiceTracker(bundleContext, serviceName, null);
			ServiceTracker existingTracker = cachedTrackers.putIfAbsent(serviceName, tracker);
			if (existingTracker != null) {
				tracker = existingTracker;
			}
			tracker.open();
		}
		return tracker;
	}

	protected Class<?> extractServiceClass(ServiceCallback<?> serviceCallback) {
		ParameterizedType parameterizedType = (ParameterizedType) serviceCallback.getClass().getGenericSuperclass();
		Class<?> typeArgument = (Class<?>) parameterizedType.getActualTypeArguments()[0];
		return typeArgument;
	}

	public void close() {
		Set<Map.Entry<String, ServiceTracker>> trackers = new HashSet<Map.Entry<String, ServiceTracker>>(cachedTrackers.entrySet());
		for (Map.Entry<String, ServiceTracker> serviceNameTracker : trackers) {
			String serviceName = serviceNameTracker.getKey();
			ServiceTracker tracker = serviceNameTracker.getValue();
			
			//log debug:
			//closing tracker for serviceName in bundleContext
			
			tracker.close();
		}
		
		cachedTrackers.clear();
		cachedTrackers = null;
	}
}

Hier mal eine Beispiel-Session:
Code:
osgi> Log: starting de.tutorials.osgi.training
Log: Service is available: org.eclipse.equinox.internal.event.EventComponent@3b75078b


osgi> ss

Framework is launched.

id	State       Bundle
0	ACTIVE      org.eclipse.osgi_3.6.0.v20100128-1430
2	ACTIVE      org.eclipse.osgi.util_3.2.100.v20100108
3	ACTIVE      org.eclipse.osgi.services_3.2.100.v20100108
5	ACTIVE      org.eclipse.equinox.event_1.2.0.v20100108
8	ACTIVE      de.tutorials.osgi.training_1.0.0.qualifier

osgi> stop 5

osgi> refresh 8

osgi> Log: stopping de.tutorials.osgi.training
Log: starting de.tutorials.osgi.training
Log: Service is unavailable: let's do something else...


osgi> start 5

osgi> refresh 8

osgi> Log: stopping de.tutorials.osgi.training
Log: starting de.tutorials.osgi.training
Log: Service is available: org.eclipse.equinox.internal.event.EventComponent@56609959


Gruß Tom
 
Zurück