2012-03-28 11 views
5

Sto sviluppando un'applicazione in Eclipse RCP. Ho bisogno di aiuto con una decisione di progettazione riguardante la progettazione di un servizio.Architettura dei servizi OSGi: creazione del servizio su richiesta del consumatore

Ho alcuni pacchetti che vengono utilizzati per fornire un oggetto REngine ad altri moduli. REngine è un'interfaccia per un motore di calcolo che può essere implementato in diversi modi. I bundle forniscono istanze di REngine connettendosi a un server remoto o avviando un thread di calcolo locale. Alcuni bundle richiedono la configurazione dalla GUI (ma devono anche essere disponibili su una piattaforma headless). Un pacchetto client può richiedere più oggetti REngine per calcoli paralleli.

Attualmente registro questi moduli per fornire un servizio REngine. Il servizio viene creato da ServiceFactory, che avvia un'istanza di calcolo locale o un'istanza remota (server). Il cliente è responsabile di provare tutte le registrazioni dei servizi della classe REngine e selezionare quella corretta.

Il codice per fare questo può essere riassunto come segue:

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 
     } 
    } 
} 

vorrei mantenere questo modello. È bello che le specifiche del servizio OSGi coincidano con le mie esigenze aziendali secondo le quali gli oggetti REngine sono oggetti viventi che dovrebbero essere rilasciati quando non sono più necessari.

Tuttavia, un servizio registrato può fornire solo un'istanza di servizio per pacchetto. La seconda volta che viene richiesto il servizio, viene restituita un'istanza memorizzata nella cache (anziché crearne una nuova). Questo non corrisponde al mio requisito; un bundle dovrebbe essere in grado di ottenere più oggetti del motore dallo stesso provider.

Ho già esaminato altre classi di framework OSGi, ma nulla sembra aiutare. L'alternativa è il modello di lavagna bianca, ma sembra strano registrare un REngineRequestService che viene utilizzato dal pacchetto REngineProvider per distribuire un motore REngine.

Come implementarlo in OSGi? Come promemoria, ecco il mio elenco di requisiti:

  1. Facile abilitazione e disabilitazione dei pacchetti REngineProvider. Il codice cliente utilizzerà invece un altro provider.
  2. Configurazione dei pacchetti REngineProvider.
  3. Più istanze REngine per pacchetto client.
  4. Rilascio esplicito di istanze REngine
  5. REngine la creazione può non riuscire. Il modulo client dovrebbe essere in grado di conoscerne il motivo.

solo per aggiungere la soluzione che ho scelto come riferimento futuro. Sembra che la piattaforma OSGi Services non sia fatta per "richiedere un servizio". È il bundle del provider che crea un servizio e il bundle del client che può trovare e utilizzare i servizi. Non è possibile fornire una "Fabbrica" ​​automatica per i servizi per richiesta dell'utente.

La soluzione scelta riguarda lo OSGi whiteboard model. A prima vista, questo può sembrare molto difficile da gestire, ma l'Blueprint può essere di grande aiuto!

Progetto fornitore.file xml:

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

La classe REngineRequest è una classe API condiviso che consente al prestatore di inserire il suo oggetto REngine, o impostare un'eccezione che spiega perché la creazione non ha funzionato.

Per il client, utilizzando un REngine ora è facile come fare:

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

Facciamo l'ipotesi che il provider non smetterà mai, mentre il client utilizza l'REngine. Se lo fa, REngine diventa non valido.

risposta

3

ComponentFactory dal Declarative Services è quello che ti serve. La maggior parte delle volte si dovrebbe usare DS invece di registrarsi manualmente e cercare i servizi.

Il lato provider deve registrare il servizio di fabbrica REngine (non è necessario implementare la fabbrica stessa, DS lo farà per te). Il curatore dovrebbe dichiarare la dipendenza uno a molti al servizio REngine. Al momento dell'esecuzione, tutte le fabbriche disponibili verranno iniettate e il consumatore potrà attraversarle per creare istanze REngine reali.

+0

Consiglieresti BluePrint e l'annotazione PROTOTYPE invece di DS? Ho difficoltà a vedere la differenza. – parasietje

+0

Stavo per suggerire Blueprint e lo scopo del prototipo. Il piano ha più manopole dei servizi dichiarativi, che potrebbero esserti utili qui. –

+0

Purtroppo, ho bisogno del contesto (proprietà) al momento della creazione del mio oggetto. È un grosso problema che non posso specificare un semplice metodo factory, ma devo specificare una classe che viene istanziata. Speriamo che Blueprint abbia qualcosa del genere! – parasietje

1

Una soluzione sarebbe registrare REngineFactory come servizio piuttosto che l'implementazione di REngine stessa e restituire la factory dal metodo getService. In questo modo i clienti possono cercare la fabbrica e, una volta trovata con successo, usarla per ottenere una nuova implementazione di REngine.

3

Due anni fa ho provato a creare le fabbriche di servizi originali che poi sono diventati servizi parametrizzati. Tuttavia, dopo l'analisi si è scoperto che non era necessario nulla, basta registrare la fabbrica come servizio.

Tuttavia.

Non so abbastanza del tuo servizio ma suona molto che potresti semplificare notevolmente le cose rimuovendo il controllo dal bundle del client, il bundle del client dovrebbe semplicemente usare qualsiasi servizio REngine disponibile nel registro del servizio, magari con un proprietà che segnalano il suo tipo di utilizzo se ci sono più bundle che richiedono REngines e non dovrebbero condividere lo stesso motore (che dovrebbe essere raramente il caso).

Se questo modello è possibile, di solito semplifica notevolmente. Generalmente utilizzo DS con le configurazioni di configurazione dell'amministratore che gestiscono le istanze (uno degli aspetti più utili di DS, vedere http://www.aqute.biz/Bnd/Components). Con l'integrazione del metatype, puoi persino ottenere un'interfaccia utente per modificare le proprietà di configurazione.

+0

Questa sarebbe probabilmente l'implementazione più semplice. Un pacchetto che ottiene un servizio innescherebbe la registrazione di un nuovo servizio. – parasietje

+0

Aha! Quindi la soluzione sarebbe quella di fornire un elenco di "Voglio che crei le seguenti istanze di REngine" utilizzando l'Admin di configurazione e quindi ottenere i servizi. Posso probabilmente comunicare eccezioni pubblicando un'eccezione anziché l'oggetto REngine stesso. L'unico problema che rimane sono i problemi di temporizzazione tra la richiesta del servizio e l'ottenimento del servizio. – parasietje

+2

Ottenere un servizio non innescherà una nuova registrazione. Ci sono due passaggi: il pacchetto client accetta tutto ciò che si trova nel registro, l'amministratore della configurazione definisce ciò che è disponibile attraverso i componenti di DS. Con i componenti di DS, la tempistica non ha alcun problema dal momento che esprimi le tue dipendenze. Non utilizzare mai ServiceReferences a meno che tu non sia uno sviluppatore middleware ... DS è incredibilmente pulito, specialmente con le annotazioni –

Problemi correlati