2012-03-28 3 views
5

Ich entwickle eine Anwendung in Eclipse RCP. Ich brauche Hilfe bei einer Designentscheidung über die Gestaltung eines Service.OSGi-Services-Architektur: Erstellung von Service auf Anfrage des Verbrauchers

Ich habe einige Bündel, die verwendet werden, um ein REngine Objekt für andere Module bereitzustellen. Die REngine ist eine Schnittstelle zu einer Berechnungsmaschine, die auf mehrere Arten implementiert werden kann. Die Bundles stellen Instanzen von REngine bereit, indem sie eine Verbindung zu einem Remote-Server herstellen oder einen lokalen Berechnungsthread starten. Einige Bundles erfordern eine Konfiguration durch die GUI (müssen aber auch auf einer kopflosen Plattform verfügbar sein). Ein Client-Paket kann mehrere REngine Objekte für parallele Berechnungen anfordern.

Ich registriere diese Module derzeit, um einen REngine Service zur Verfügung zu stellen. Der Service wird von einer ServiceFactory erstellt, die entweder eine lokale Berechnungsinstanz oder eine Remote-Instanz (Server) startet. Der Kunde ist dafür verantwortlich, alle Service-Registrierungen der Klasse REngine auszuprobieren und die richtige auszuwählen.

Der Code, dies zu tun, kann wie folgt zusammengefasst werden:

class API.REngine { ... } 

class REngineProvider.Activator { 
    public void start(BundleContext ctx) { 
     ctx.registerService(REngine.class.getName(), new REngineFactory(), null); 
    } 
} 
class REngineProvider.REngineFactory implements ServiceFactory { 
    public Object getService(Bundle bundle, ServiceReference reference) { 
     return new MyREngineImplementation(); 
    } 
    public void ungetService(REngine service) { 
     service.releaseAssociatedResources(); 
    } 
} 

class RConsumer.Class { 
    REngine getREngine() { 
     ServiceReference[] references = bundleContext.getAllServiceReferences(REngine.class.getName(), null); 
     for(ServiceReference ref: references) { 
      try { 
      return bundleContext.getService(ref); 
      } catch (Exception e) {} // too bad, try the next one 
     } 
    } 
} 

ich dieses Modell halten möchten. Es ist schön, dass die OSGi-Dienstespezifikation mit meiner Geschäftsanforderung übereinstimmt, dass REngine-Objekte lebende Objekte sind, die freigegeben werden sollten, wenn sie nicht mehr benötigt werden.

Ein registrierter Dienst kann jedoch nur eine Serviceinstanz pro Bündel bereitstellen. Wenn der Dienst das zweite Mal angefordert wird, wird eine zwischengespeicherte Instanz zurückgegeben (anstatt eine neue Instanz zu erstellen). Dies entspricht nicht meiner Anforderung; Ein Bundle sollte mehrere REngine-Objekte vom selben Provider erhalten können.

Ich habe bereits andere OSGi-Framework-Klassen betrachtet, aber nichts scheint zu helfen. Die Alternative ist das Whiteboard-Modell, aber es scheint seltsam, einen REngineRequestService zu registrieren, der vom REngineProvider-Bundle verwendet wird, um eine Live-REngine auszugeben.

Wie implementiere ich das in OSGi? Zur Erinnerung, hier ist meine Liste der Anforderungen:

  1. Einfache Aktivierung und Deaktivierung von REngineProvider Bundles. Client-Code verwendet stattdessen nur einen anderen Anbieter.
  2. Konfiguration der REngineProvider-Bundles.
  3. Mehrere REngine Instanzen pro Client-Bundle.
  4. Explizite Freigabe von REngine Instanzen
  5. REngine Erstellung kann fehlschlagen. Das Client-Modul sollte den Grund dafür kennen.

einfach die Lösung, die wir als zukünftige Referenz gewählt haben, hinzuzufügen. Es scheint, dass die OSGi Services-Plattform nicht dafür ausgelegt ist, "einen Dienst anzufordern". Es ist das Provider-Paket, das einen Service erstellt, und das Client-Paket, das die Services finden und verwenden kann. Es ist nicht möglich, eine automatische "Factory" für Dienste pro Benutzeranforderung bereitzustellen.

Die gewählte Lösung umfasst die OSGi whiteboard model. Auf den ersten Blick scheint dies sehr schwierig zu sein, aber Blueprint kann viel helfen!

Anbieter Blaupause.XML-Datei:

<reference-list interface="org.application.REngineRequest" 
      availability="optional"> 
    <reference-listener 
      bind-method="bind" unbind-method="unbind"> 
     <bean class="org.provider.REngineProvider"/>   
    </reference-listener> 

Die Klasse REngineRequest ist eine gemeinsame API-Klasse ermöglicht den Provider zur Eingabe seines REngine Objekt oder eine Ausnahme zu erklären, warum die Schöpfung nicht funktioniert hat.

Für den Kunden ist es, ein REngine mit jetzt so einfach wie zu tun:

REngineRequest req = new REngineRequest(); 
ServiceRegistration reg = bundleContext.registerService(req, REngineRequest.class.getName(), engineCreationProperties); 
req.getEngine().doSomeStuff(); 
reg.unregister(); 

Wir davon ausgehen, dass sich der Anbieter wird nie aufhören, während der Client die REngine verwendet. Wenn dies der Fall ist, wird REngine ungültig.

Antwort

3

ComponentFactory von der Declarative Services ist, was Sie brauchen. Meistens sollten Sie lieber DS verwenden, anstatt die Dienste manuell zu registrieren und nachzuschlagen.

Die Provider-Seite sollte den REngine Factory Service registrieren (Sie müssen die Fabrik nicht selbst implementieren, DS wird das für Sie tun). Der Konsument sollte eine Eins-zu-Viele-Abhängigkeit gegenüber dem REngine-Service deklarieren. Zur Laufzeit werden alle verfügbaren Factories injiziert und der Konsument kann sie durchlaufen, um tatsächliche REngine-Instanzen zu erstellen.

+0

Würden Sie BluePrint und die PROTOTYPE-Anmerkung anstelle von DS empfehlen? Es fällt mir schwer, den Unterschied zu sehen. – parasietje

+0

Ich wollte Blueprint und den Prototypbereich vorschlagen. Blueprint hat mehr Knöpfe als deklarative Dienste, die Ihnen hier nützlich sein können. –

+0

Leider brauche ich den Kontext (Eigenschaften) bei der Erstellung meines Objekts. Es ist ein großer Fehler, dass ich keine einfache Factory-Methode angeben kann, sondern eine Klasse angeben muss, die instanziiert wird. Hoffen wir, dass Blueprint so etwas hat! – parasietje

1

Eine Lösung wäre, die REngineFactory als Dienst und nicht die REngine-Implementierung selbst zu registrieren und die Factory von der getService-Methode zurückzugeben. Auf diese Weise können die Kunden die Fabrik nachschlagen und nach erfolgreicher Suche eine neue REngine-Implementierung erhalten.

3

Vor zwei Jahren versuchte ich, echte Service Factories zu schaffen, was später zu parametrisierten Diensten wurde. Nach der Analyse stellte sich jedoch heraus, dass nichts nötig war, einfach die Fabrik als Service registrieren.

Jedoch.

Ich weiß nicht genug über Ihren Dienst, aber es klingt sehr, dass Sie Dinge erheblich vereinfachen könnten, indem Sie die Kontrolle aus dem Client-Bundle entfernen, das Client-Bundle sollte einfach jeden verfügbaren REngine-Dienst in der Service-Registry verwenden Eigenschaft, die ihren Benutzertyp signalisiert, wenn mehrere Bundles REngines benötigen und sie nicht die gleiche REngine teilen sollten (was selten der Fall sein sollte).

Wenn dieses Modell möglich ist, wird es in der Regel erheblich vereinfacht. In der Regel verwende ich dann DS mit Configuration Admin-Konfigurationen, die die Instanzen steuern (einer der nützlichsten Aspekte von DS, siehe http://www.aqute.biz/Bnd/Components). Mit der Metatype-Integration erhalten Sie sogar eine Benutzeroberfläche zum Bearbeiten Ihrer Konfigurationseigenschaften.

+0

Dies wäre wahrscheinlich die einfachste Implementierung. Ein Bündel, das einen Dienst erhält, würde die Registrierung eines neuen Dienstes auslösen. – parasietje

+0

Aha! Die Lösung wäre also, eine Liste von "Ich möchte, dass Sie die folgenden REngine-Instanzen erstellen" mit dem Konfigurations-Admin bereitzustellen und dann die Dienste zu beziehen. Ich kann wahrscheinlich Ausnahmen mitteilen, indem ich eine Ausnahme anstelle des REngine-Objekts selbst veröffentliche. Das einzige verbleibende Problem sind Zeitprobleme zwischen dem Anfordern des Dienstes und dem Abrufen des Dienstes. – parasietje

+2

Das Abrufen eines Dienstes löst keine neue Registrierung aus. Es gibt zwei Schritte: Das Client-Paket übernimmt das, was in der Registrierung vorhanden ist. Der Konfigurationsadministrator definiert, was über DS-Komponenten verfügbar ist. Bei DS-Komponenten spielt das Timing keine Rolle, da Sie Ihre Abhängigkeiten ausdrücken. Verwenden Sie ServiceReferences niemals, es sei denn, Sie sind ein Middleware-Entwickler ... DS ist unglaublich sauber, besonders mit den Anmerkungen –