Estoy desarrollando una aplicación en Eclipse RCP. Necesito ayuda con una decisión de diseño sobre el diseño de un servicio.Arquitectura de servicios OSGi: creación de servicio a pedido del consumidor
Tengo algunos paquetes que se utilizan para proporcionar un objeto REngine
a otros módulos. El REngine
es una interfaz para un motor de cálculo, que se puede implementar de múltiples maneras. Los paquetes proporcionan instancias de REngine conectándose a un servidor remoto o iniciando una cadena de cálculo local. Algunos paquetes requieren configuración por la GUI (pero también deben estar disponibles en una plataforma sin cabeza). Un paquete de cliente puede solicitar múltiples objetos REngine
para cálculos paralelos.
Actualmente registro estos módulos para proporcionar un servicio REngine
. El servicio es creado por ServiceFactory, que lanza una instancia de cálculo local o una instancia remota (servidor). El cliente es responsable de probar todos los registros de servicio de la clase REngine
y seleccionar el correcto.
El código para hacer esto se puede resumir de la siguiente manera:
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
}
}
}
Me gustaría mantener este modelo. Es agradable que la especificación del servicio OSGi coincida con el requisito de mi empresa de que los objetos REngine sean objetos vivos que deberían liberarse cuando ya no los necesiten.
Sin embargo, un servicio registrado solo puede proporcionar una instancia de servicio por paquete. La segunda vez que se solicita el servicio, se devuelve una instancia en caché (en lugar de crear una nueva). Esto no coincide con mi requerimiento; un paquete debería ser capaz de obtener múltiples objetos REngine del mismo proveedor.
Ya he analizado otras clases de framework OSGi, pero nada parece ayudar. La alternativa es el modelo de pizarra, pero parece extraño registrar un REngineRequestService que es utilizado por el paquete REngineProvider para entregar un REngine en vivo.
¿Cómo implemento esto en OSGi? Como recordatorio, aquí está mi lista de requisitos:
- Habilitación y deshabilitación de los paquetes
REngineProvider
. El código del cliente simplemente usará otro proveedor en su lugar. - Configuración de paquetes REngineProvider.
- Múltiples
REngine
instancias por paquete de cliente. - Versión explícita de
REngine
instancias REngine
creación puede fallar. El módulo del cliente debería poder saber el motivo.
sólo para añadir la solución que he elegido como referencia en el futuro. Parece que la plataforma de Servicios OSGi no está hecha para "solicitar un servicio". Es el paquete de proveedores el que crea un servicio y el paquete de clientes que puede encontrar y usar los servicios. No es posible proporcionar una "Fábrica" automática para los servicios por solicitud del usuario.
La solución elegida implica el OSGi whiteboard model. A primera vista, esto puede parecer muy difícil de manejar, ¡pero Blueprint
puede ayudar mucho!
Plan del proveedor.archivo 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 clase REngineRequest
es una clase API compartida que permita al proveedor a la entrada de su objeto REngine, o establecer una excepción para explicar por qué la creación no funcionó.
Para el cliente, utilizando un REngine es ahora tan fácil como hacer:
REngineRequest req = new REngineRequest();
ServiceRegistration reg = bundleContext.registerService(req, REngineRequest.class.getName(), engineCreationProperties);
req.getEngine().doSomeStuff();
reg.unregister();
Hacemos el supuesto de que el proveedor no se detendrá mientras el cliente está utilizando el REngine. Si lo hace, el REngine
deja de ser válido.
¿Recomendaría BluePrint y la anotación PROTOTIPO en lugar de DS? Me cuesta mucho ver la diferencia. – parasietje
Iba a sugerir Blueprint y el alcance del prototipo. Blueprint tiene más perillas que los servicios declarativos, que pueden serle útiles aquí. –
Lamentablemente, necesito el contexto (propiedades) al crear mi objeto. Es una gran dificultad que no pueda especificar un método de fábrica simple, pero debe especificar una clase que se crea una instancia. ¡Ojalá Blueprint tenga algo así! – parasietje