OSGi erwacht zum Leben

Das Provider-Bundle muss beim Launch der OSGi-Umgebung automatisch gestartet werden, damit die Services über den Activator registriert werden. Consumer Bundles können anhand des Interfacenamens in der Registry nach Services suchen, um sie dann zu verwenden. Das Bundle com.zuehlke.contacts.ui tut genau das. Der Code, der in Listing 2 dargestellt ist, ist im Activator des Consumers angesiedelt.

Listing 2
public void start(BundleContext bundleContext) throws Exception {
  private Map> serviceReferenceMap = new HashMap>();

public  T getService(Class serviceClass) {
      String serviceName = serviceClass.getName();
      BundleContext bundleContext = getBundle().getBundleContext();
      ServiceReference serviceReference = null;
      if (!serviceReferenceMap.containsKey(serviceName)) {
        serviceReference = (ServiceReference) bundleContext
            .getServiceReference(serviceName);
        if (serviceReference != null) {
          serviceReferenceMap.put(serviceName, serviceReference);
        }
      } else {
        serviceReference = (ServiceReference) serviceReferenceMap
            .get(serviceName);
      }
      T service = null;
      if (serviceReference != null) {
        service = bundleContext.getService(serviceReference);
      }
      return service;
}

Die Methode ist generisch, sodass sie sowohl für den CustomerService als auch für den ContactService genutzt werden kann. Die ServiceReference-Objekte werden in einer Map abgelegt, um zum einen Wiederverwendung zu ermöglichen; zum anderen müssen sie beim Stoppen des Bundles bei der OSGi Service Registry wieder abgemeldet werden.

Optionale Abhängigkeiten

Interessant wird die OSGi Service Registry im Zusammenhang mit optionalen Abhängigkeiten. Das wird notwendig, wenn ein Service (Schnittstelle und Implementierung) zur Laufzeit potenziell nicht vorhanden sein kann.

Zunächst muss in einem solchen Fall die Bundle-Abhängigkeit im Manifest als optional gekennzeichnet werden. Der Zugriff auf den Service sollte dann programmatisch erfolgen. Denn so kann geprüft werden, ob der Service vorhanden ist, ohne dass auch nur die Interface-Klasse geladen werden müsste. Die im Abschnitt „Abhängen via OSGi Services“ vorgestellte Zugriffsmethode getService()kann wiederverwendet werden, jedoch kann auf Generics verzichtet werden. Anstelle der konkreten Interface-Klasse muss der vollqualifizierte Name verwendet werden.

public Object getService(String serviceName) {
.
serviceReference = bundleContext.getServiceReference(serviceName);
.
return serviceObject;
}
So eignet sich die Methode auch für das Laden optionaler Services. Die Verwendung sieht dann wie folgt aus:

.
Object serviceObject =
Activator.getDefault().getService("com.zuehlke.contacts.service.ContactService");
if (serviceObject != null) {
com.zuehlke.contacts.service.ContactService service =
(com.zuehlke.contacts.service.ContactService) serviceObject;
// use available service
}
else {
  // handle missing dependency
}
.

Wichtig ist, dass auch der vollqualifizierte Klassenname nur inline und nicht in einer Importanweisung verwendet wird. Denn sonst würde zur Laufzeit ggf. auch die Klasse, in der sich der dargestellte Code befindet, nicht geladen werden können.

Der Umgang mit optionalen Services ist umständlich. Eine deutliche Vereinfachung könnte bereits erreicht werden, wenn sichergestellt wäre, dass das API immer verfügbar ist. Hierfür würde sich der im letzten Artikel vorgestellte Ansatz „Stand-alone API“ anbieten.

Kommentare

Schreibe einen Kommentar

Deine E-Mail-Adresse wird nicht veröffentlicht.