Inyección de dependencia en entornos OSGI

Primero algunos antecedentes:

Estoy trabajando en un código de prototipo de aplicación web basado en Apache Sling que se basa en OSGI y se ejecuta en Apache Felix. Todavía soy relativamente nuevo en OSGI, aunque creo que ya he comprendido la mayoría de los conceptos. Sin embargo, lo que me desconcierta es que no he podido encontrar un marco de dependency injection (DI) completo. He empleado con éxito DI rudimentaria utilizando servicios declarativos (DS). Pero mi entendimiento es que los DS están acostumbrados a hacer referencia, ¿cómo pongo esto? – Servicios y componentes registrados OSGI juntos. Y para eso funciona bien, pero yo personalmente uso marcos DI como Guice para conectar gráficos de objetos completos y colocar objetos en los ámbitos correctos (por ejemplo, @RequestScoped o @SessionScoped ). Sin embargo, ninguno de los marcos específicos de OSGI que he examinado parece apoyar este concepto.

Empecé a leer sobre los planos de OSGI y iPOJO, pero estos marcos parecen estar más preocupados por el cableado de los servicios OSGI juntos que por proporcionar una solución DI completa. Debo admitir que todavía no he realizado ninguna muestra, por lo que mi impresión podría ser incorrecta.

Siendo una extensión de Guice, experimenté con Peaberry , sin embargo, encontré la documentación muy difícil de encontrar y, aunque conseguí que la DI básica funcionara, muchas de las funciones avanzadas de guice-servlet (inyección automática en filtros, servlets, etc.) no funcionaron. t trabajo en absoluto

Entonces, mis preguntas son las siguientes:

  1. ¿Cómo se comparan los servicios declarativos con los DI “tradicionales” como Guice o Spring? ¿Resuelven el mismo problema o están orientados hacia diferentes problemas?
  2. Todas las soluciones específicas de OSGI que he visto hasta ahora carecen del concepto de ámbitos para DI. Por ejemplo, Guice + guice-servlet tiene dependencias de ámbito de solicitud que hacen que la escritura de aplicaciones web sea realmente fácil y sencilla. ¿Me perdí eso en los documentos o estas preocupaciones no están cubiertas por ninguno de estos marcos?
  3. ¿ JSR 330 y OSGI se basan en dos mundos diferentes? iPOJO, por ejemplo, trae sus propias anotaciones y Felix SCR Las anotaciones parecen ser un mundo completamente diferente.
  4. ¿Alguien tiene experiencia en la construcción de sistemas basados ​​en OSGI y DI? Tal vez incluso un código de ejemplo en github?
  5. ¿Alguien usa diferentes tecnologías como Guice y iPOJO juntas o es solo una idea loca?

Lo siento por la pregunta bastante larga.

Cualquier comentario es muy apreciado.


Actualizaciones

Inyección en el ámbito: la inyección en el ámbito es un mecanismo útil para inyectar automáticamente objetos de un ciclo de vida específico. Piense, por ejemplo, que parte de su código se basa en un objeto de sesión de Hibernate que se crea como parte de un filtro de servlet. Al marcar una dependencia, el contenedor reconstruirá automáticamente el gráfico de objetos. Tal vez hay diferentes enfoques para eso?

JSR 330 vs DS : de todas sus excelentes respuestas veo que estas son dos cosas diferentes. Eso plantea la pregunta, ¿cómo tratar con las bibliotecas y los marcos de terceros que usan las anotaciones JSR 330 cuando se usan en un contexto OSGI? ¿Qué es un buen enfoque? ¿Ejecutar un contenedor JSR 330 dentro del paquete?

Aprecio todas tus respuestas, has sido muy útil!

Enfoque global

La forma más sencilla de realizar una dependency injection con Apache Sling, y la utilizada en todo el código base, es utilizar el complemento de scr-maven .

Puede anotar sus clases de java y luego, en el momento de la creación, invocar el complemento de SCR, ya sea como complemento de Maven o como tarea de Ant.

Por ejemplo, para registrar un servlet puede hacer lo siguiente:

 @Component // signal that it's OSGI-managed @Service(Servlet.class) // register as a Servlet service public class SampleServlet implements Servlet { @Reference SlingRepository repository; // get a reference to the repository } 

Respuestas especificas

¿Cómo se comparan los servicios declarativos con los DI “tradicionales” como Guice o Spring? ¿Resuelven el mismo problema o están orientados hacia diferentes problemas?

Resuelven el mismo problema – dependency injection. Sin embargo (ver más abajo) también están diseñados para tener en cuenta los sistemas dynamics donde los servicios pueden aparecer o desaparecer en cualquier momento.

Todas las soluciones específicas de OSGI que he visto hasta ahora carecen del concepto de ámbitos para DI. Por ejemplo, Guice + guice-servlet tiene dependencias de ámbito de solicitud que hacen que la escritura de aplicaciones web sea realmente fácil y sencilla. ¿Me perdí eso en los documentos o estas preocupaciones no están cubiertas por ninguno de estos marcos?

No he visto ningún enfoque en el mundo de SCR para agregar servicios de sesión o de solicitud. Sin embargo, el SCR es un enfoque genérico, y el scope se puede manejar en una capa más específica.

Dado que está utilizando Sling, creo que no habrá necesidad de enlaces de sesión o de solicitud, ya que Sling tiene objetos incorporados para cada solicitud que se crean adecuadamente para el usuario actual.

Un buen ejemplo es la sesión de JCR. Se construye automáticamente con los privilegios correctos y, en la práctica, es un DAO de ámbito de solicitud. Lo mismo ocurre con el Sling resourceResolver.

Si necesita un trabajo por usuario, el enfoque más sencillo es tener servicios que reciban una Session JCR o un Sling ResourceResolver y usarlos para realizar el trabajo que necesita. Los resultados se ajustarán automáticamente para los privilegios del usuario actual sin ningún esfuerzo adicional.

¿JSR 330 y OSGI se basan en dos mundos diferentes? iPOJO, por ejemplo, trae sus propias anotaciones y Felix SCR Las anotaciones parecen ser un mundo completamente diferente.

Si son diferentes Debe tener en cuenta que aunque Spring y Guice son más comunes, los servicios OSGi son más complejos y admiten más casos de uso. En OSGi, los paquetes (y los servicios implícitos) son gratuitos en cualquier momento.

Esto significa que cuando tiene un componente que depende de un servicio que no está disponible, su componente está desactivado. O cuando recibe una lista de componentes (por ejemplo, implementaciones de Servlet) y uno de ellos está desactivado, se lo notificamos. Que yo sepa, ni Spring ni Guice lo admiten, ya que sus cableados son estáticos.

Eso es una gran flexibilidad que OSGi te da.

¿Alguien tiene experiencia en la construcción de sistemas basados ​​en OSGI y DI? Tal vez incluso un código de ejemplo en github?

Hay un gran número de muestras en el repository SVN de Sling Samples . Debes encontrar la mayoría de lo que necesitas allí.

¿Alguien usa diferentes tecnologías como Guice y iPOJO juntas o es solo una idea loca?

Si tiene marcos que están configurados con anotaciones JSR 330, tiene sentido configurarlos en tiempo de ejecución utilizando Guice o Spring o lo que sea que funcione para usted. Sin embargo, como Neil Bartlett ha señalado, esto no funcionará en paquetes cruzados.

Solo me gustaría agregar un poco más de información a la excelente respuesta de Robert, particularmente con respecto a JSR330 y DS.

Los servicios declarativos, Blueprint, iPOJO y los otros “modelos de componentes” de OSGi están destinados principalmente para inyectar servicios OSGi. Estos son un poco más difíciles de manejar que las dependencias regulares porque pueden aparecer y desaparecer en cualquier momento, incluso en respuesta a eventos externos (por ejemplo, red desconectada) o acciones del usuario (por ejemplo, paquete eliminado). Por lo tanto, todos estos modelos de componentes proporcionan una capa de ciclo de vida adicional sobre los marcos de dependency injection pura.

Esta es la razón principal por la que las anotaciones de DS son diferentes de las de JSR330 … las de JSR330 no proporcionan semántica suficiente para lidiar con el ciclo de vida. Por ejemplo no dicen nada sobre:

  • ¿Cuándo debe inyectarse la dependencia?
  • ¿Qué debemos hacer cuando la dependencia no está disponible actualmente (es decir, es opcional u obligatoria)?
  • ¿Qué debemos hacer cuando desaparece un servicio que estamos usando?
  • ¿Podemos cambiar dinámicamente de una instancia de un servicio a otro?
  • etc …

Desafortunadamente, debido a que los modelos de componentes se centran principalmente en los servicios, es decir, los vínculos entre los paquetes, son comparativamente sencillos con respecto al cableado de las dependencias dentro del paquete (aunque Blueprint sí ofrece cierto soporte para esto).

No debería haber ningún problema al utilizar un marco DI existente para conectar las dependencias dentro del paquete. Por ejemplo, tuve un cliente que usó Guice para cablear las piezas internas de algunos componentes de los Servicios declarativos. Sin embargo, tiendo a cuestionar el valor de hacer esto, porque si necesita DI dentro de su paquete, sugiere que su paquete puede ser demasiado grande e incoherente.

Tenga en cuenta que es muy importante NO utilizar un marco de DI tradicional para conectar componentes entre paquetes. Si el marco DI necesita acceder a una clase de otro paquete, entonces ese otro paquete debe exponer sus detalles de implementación, lo que rompe la encapsulación que buscamos en OSGi.

Tengo algo de experiencia en la creación de aplicaciones utilizando Aries Blueprint. Tiene algunas características muy buenas con respecto a los servicios OSGi y al soporte de configuración de administrador.

Si busca algunos ejemplos excelentes, eche un vistazo al código de Apache Karaf, que utiliza un plano para todo su cableado. Ver http://svn.apache.org/repos/asf/karaf/

También tengo algunos tutoriales para Blueprint y Apache Karaf en mi sitio web: http://www.liquid-reality.de/display/liquid/Karaf+Tutorials

En su entorno con el felix incorporado será un poco diferente ya que no tiene las funciones de administración de Karaf, pero simplemente necesita instalar los mismos paquetes y debería funcionar bien.

Estoy usando osgi y DI para mi proyecto actual, elegí el modelo Gemini porque es la segunda versión de SPRING DYNAMIC MODULES . Basándome en esta información, le sugiero que lea Spring Spring Modules en acción . Este libro te ayudará a entender algunas partes y puntos de cómo construir architecture y por qué es bueno 🙂

Puedo recomendar Bnd y si usa Eclipse IDE sepcially Bndtools también. Con eso puedes evitar describir DS en XML y usar anotaciones en su lugar. Hay una anotación de Reference especial para DI. Este también tiene un filtro donde puede hacer referencia solo a un subconjunto especial de servicios.

Nos encontramos con un problema de architecture similar aquí, como Robert mencionó anteriormente en su respuesta:

Si necesita un trabajo por usuario, el enfoque más sencillo es tener servicios que reciban una sesión de JCR o un Sling ResourceResolver y usarlos para realizar el trabajo que necesita. Los resultados se ajustarán automáticamente para los privilegios del usuario actual sin ningún esfuerzo adicional.

Extrapolando de esto (y lo que actualmente estoy codificando), un enfoque sería agregar @param resourceResolver a cualquier método @Service para que pueda pasar el objeto de ámbito de solicitud apropiado para ser usado en la cadena de ejecución.

Específicamente tenemos una capa XXXXService / XXXXDao , llamada desde XXXXServlet / XXXXViewHelper / JSP equivalentes. Entonces, al administrar todos estos componentes a través de las anotaciones OSGI @Service , podemos conectar fácilmente toda la stack.

La desventaja aquí es que necesita ensuciar el diseño de su interfaz con los parámetros ResourceResolver o Sessions .

Originalmente intentamos inyectar ResourceResolverFactory en la capa DAO, de modo que pudiéramos acceder fácilmente a la sesión a través de la fábrica. Sin embargo, estamos interactuando con la sesión en varios puntos de la jerarquía y varias veces por solicitud. Esto dio lugar a excepciones de sesión cerrada.

¿Hay alguna forma de obtener ese ResourceResolver solicitud de forma confiable sin tener que pasarlo a cada método de servicio?

Con la inyección en el ámbito de la solicitud en las capas de Servicio, en su lugar, podría pasar el ResourceResolver como argumento de constructor y usar una variable de instancia en su lugar. Por supuesto, el inconveniente aquí es que tendría que pensar en el código de servicio de solicitud de scope en comparación con el de prototipo de scope y separarse en consecuencia.

Esto parece que sería un problema común en el que desea separar las preocupaciones en el código de servicio / dao, dejando las interacciones de JCR en el DAO, de forma análoga a Hibernate. ¿Cómo puede obtener fácilmente en la Session por solicitud para realizar operaciones de repo?