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:
- Facile abilitazione e disabilitazione dei pacchetti
REngineProvider
. Il codice cliente utilizzerà invece un altro provider. - Configurazione dei pacchetti REngineProvider.
- Più istanze
REngine
per pacchetto client. - Rilascio esplicito di istanze
REngine
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.
Consiglieresti BluePrint e l'annotazione PROTOTYPE invece di DS? Ho difficoltà a vedere la differenza. – parasietje
Stavo per suggerire Blueprint e lo scopo del prototipo. Il piano ha più manopole dei servizi dichiarativi, che potrebbero esserti utili qui. –
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