2012-03-28 9 views
5

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:

  1. Habilitación y deshabilitación de los paquetes REngineProvider. El código del cliente simplemente usará otro proveedor en su lugar.
  2. Configuración de paquetes REngineProvider.
  3. Múltiples REngine instancias por paquete de cliente.
  4. Versión explícita de REngine instancias
  5. 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.

Respuesta

3

ComponentFactory del Declarative Services es lo que necesita. La mayoría de las veces debe usar DS en lugar de registrarse manualmente y buscar los servicios.

El lado del proveedor debe registrar el servicio de fábrica REngine (no tiene que implementar la fábrica en sí, DS lo hará por usted). El consmer debe declarar una dependencia de uno a muchos al servicio REngine. En el tiempo de ejecución, todas las fábricas disponibles se inyectarán y el consumidor puede atravesarlas para crear instancias REngine reales.

+0

¿Recomendaría BluePrint y la anotación PROTOTIPO en lugar de DS? Me cuesta mucho ver la diferencia. – parasietje

+0

Iba a sugerir Blueprint y el alcance del prototipo. Blueprint tiene más perillas que los servicios declarativos, que pueden serle útiles aquí. –

+0

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

1

Una solución sería registrar REngineFactory como el servicio en lugar de la implementación de REngine y devolver la fábrica del método getService. De esta forma, los clientes pueden buscar la fábrica y, al encontrarla con éxito, usarla para obtener una nueva implementación de REngine.

3

Hace dos años traté de crear Fábricas de servicios genuinos, que luego se convirtieron en Servicios parametrizados. Sin embargo, después del análisis resultó que no se necesitaba nada, solo registra la fábrica como el servicio.

Sin embargo.

No sé lo suficiente sobre su servicio, pero parece mucho que podría simplificar significativamente las cosas al eliminar el control del paquete del cliente, el paquete del cliente solo debe usar el servicio REngine disponible en el registro del servicio, tal vez con un propiedad que indica su tipo de uso si hay múltiples paquetes que necesitan REngines y no deben compartir el mismo REngine (lo que raramente debería ser el caso).

Si ese modelo es posible, generalmente se simplifica significativamente. En general, luego uso DS con configuraciones de administración de configuración que manejan las instancias (uno de los aspectos más útiles de DS, vea http://www.aqute.biz/Bnd/Components). Con la integración del metatipo, incluso obtiene una interfaz de usuario para editar sus propiedades de configuración.

+0

Esta sería probablemente la implementación más simple. Un paquete que recibe un servicio activará el registro de un nuevo servicio. – parasietje

+0

Aha! Por lo tanto, la solución sería proporcionar una lista de "Deseo que cree las siguientes instancias de REngine" utilizando Configuration Admin y luego obteniendo los servicios. Probablemente pueda comunicar excepciones publicando una excepción en lugar del objeto REngine. El único problema restante son los problemas de tiempo entre la solicitud del servicio y la obtención del servicio. – parasietje

+2

Obtener un servicio no activará un nuevo registro. Hay dos pasos: el paquete de cliente toma todo lo que está en el registro, el administrador de configuración define lo que está disponible a través de los componentes de DS. Con los componentes de DS, el tiempo no es importante ya que expresa sus dependencias. Nunca use ServiceReferences a menos que sea un desarrollador de middleware ... DS es increíblemente limpio, especialmente con las anotaciones –

Cuestiones relacionadas