Comprender los conceptos de OSGi. Trate de seguir el enfoque del rompecabezas

Publicado: 2013-04-20

OSGi se hizo muy popular hoy en día, gracias a su enfoque de modularidad y su capacidad para imponer límites lógicos entre módulos. Cuando lo descubrimos por primera vez, la pregunta es ¿por dónde empezar a entender cómo funciona?

Para comprender los conceptos de OSGi, intentaremos seguir el enfoque del rompecabezas, la idea es comenzar con la parte trivial de esta tecnología y buscar otras partes relacionadas con las encontradas. Y para armar el rompecabezas, contaremos con la asistencia de JArchitect, que será útil para detectar el diseño interno de OSGi.

Para ser más concretos, analizamos con JArchitect una aplicación que usa tecnología OSGi, se trata del famoso eclipse IDE que usa el contenedor equinoccio de OSGi.

Comencemos con la típica definición de OSGi:

OSGi reduce la complejidad al proporcionar una arquitectura modular para los sistemas distribuidos a gran escala de la actualidad, así como para las aplicaciones pequeñas e integradas. La creación de sistemas a partir de módulos internos y estándar reduce significativamente la complejidad y, por lo tanto, los gastos de desarrollo y mantenimiento. El modelo de programación OSGi cumple la promesa de los sistemas basados ​​en componentes.

La parte trivial de OSGi es la modularidad, descubramos qué son los módulos OSGi.

Los módulos OSGI se denominan paquetes y, por lo tanto, cada aplicación consta de al menos un paquete.
Estos paquetes se ejecutan dentro de un contenedor, y como cada enfoque modular donde un contenedor administra los módulos, la pregunta es ¿qué contrato debe implementar cada módulo para integrarse en el contenedor?

Tomemos como ejemplo el paquete org.eclipse.equinox.jsp.jasper y busquemos todas sus interfaces implementadas, para eso podemos ejecutar la siguiente solicitud CQLinq:

eclipse4

Se implementa la interfaz BundleActivator del paquete OSGi, esta interfaz contiene dos métodos de inicio y detención útiles para personalizar el inicio y la detención del paquete.

Otra especificidad de un paquete es su archivo de manifiesto, aquí hay una parte del archivo de manifiesto org.eclipse.equinox.jsp.jasper:

Como podemos observar, este manifiesto contiene algunas metainformaciones que necesita el contenedor, como especificar la clase de activador de paquete que implementa la interfaz BundleActivator.

El paquete representa nuestra primera pieza del rompecabezas, y aquí hay una representación simplificada de un paquete:

eclipse7

Y solo para tener una idea de todos los paquetes utilizados por eclipse, busquemos todas las clases que implementan la interfaz BundleActivator.

eclipse2

¿Quién administra el paquete e invoca los métodos BundleActivator?

Para descubrirlo, busquemos métodos que invoquen directa o indirectamente BundleActivator.start

eclipse6

El paquete se activa mediante el marco OSGi cuando se inicia. La clase de marco se inicia con el contenedor de equinoccio. Y para comprender mejor lo que sucede cuando se inicia el contenedor, aquí hay algunas acciones que se ejecutan cuando se inicia el contenedor:

eclipse31

Cuando se inicia el contenedor, inicializa el marco OSGi, el marco obtiene todos los paquetes instalados y, para cada uno, crea una instancia de la clase BundleHost y almacena en un repositorio el paquete encontrado.

La clase BundleHost implementa la interfaz Bundle, que contiene métodos como iniciar, detener, desinstalar y actualizar, estos métodos son necesarios para administrar el ciclo de vida del paquete.

Entonces, nuestra segunda pieza del rompecabezas es el contenedor OSGi, es lanzado por Equinoxlauncher, que inicializa el marco. La clase framework es responsable de cargar paquetes y activarlos.

Después de descubrir algunos conceptos básicos sobre el paquete y el contenedor, profundicemos en los paquetes y descubramos cómo funcionan internamente.

Tomemos como ejemplo el paquete org.eclipse.equinox.http.servlet y busquemos métodos invocados por el método de inicio de su clase Activator.

eclipse1

Este paquete crea un servicio y lo registra en el contenedor. Un servicio en OSGi está definido por una clase o interfaz Java estándar. Normalmente se utiliza una interfaz Java para definir la interfaz de servicio. El servicio es el método preferido que deben usar los paquetes para comunicarse entre sí.

Estos son algunos escenarios útiles de uso de servicios:

  • Exporte la funcionalidad de un paquete a otros paquetes.
  • Importar funcionalidad de otros paquetes.
  • Registre oyentes para eventos de otros paquetes.

Otro comentario del gráfico de dependencia anterior es que se utiliza una fábrica de servicios para crear la instancia de servicio.

Nuestra tercera pieza del rompecabezas es la capa de servicios OSGi, cada paquete puede usar o declarar algunos servicios, lo que hace cumplir el enfoque de diseño de componentes, aquí está la nueva representación del paquete OSGi.

eclipse8

Si el paquete usa servicios para comunicarse con otros paquetes, ¿cómo se comunica con otros jars?

Si desarrollamos un paquete e intentamos usar una clase de otro jar, podemos sorprendernos de que no funcione como se esperaba, la razón es que el ClassLoader está enganchado al contenedor OSGi, para comprobar que vamos a buscar qué método invocar java. lang.Thread.setContextClassLoader.

eclipse9

Muchos métodos lo invocan, incluido EquinoxLauncher. por lo tanto, cada vez que el paquete intente crear una instancia de clase, el contenedor OSGi verificará si el código puede realizar esta acción o no, y aquí viene el rol del paquete importado y exportado en el archivo de manifiesto.

El paquete declara explícitamente el paquete exportado e importado, y para verificar eso, busquemos los paquetes usados ​​por el paquete org.eclipse.equinox.http.servlet y verifiquemos si usa solo el paquete importado.

eclipse10

Como podemos observar, todos los paquetes utilizados se especifican en el archivo de manifiesto, en la sección de paquetes de importación.
Sin embargo, el paquete exportado representa el paquete que se puede usar desde otros paquetes. está representado por la interfaz ExportedPackage, y podemos buscar las clases de contenedores usando esta interfaz.

eclipse12

Lo interesante de esta nueva capacidad es que el paquete tendrá un límite bien definido, y lo que usa y lo que expone como servicios está muy bien especificado.

Podemos hacer cumplir la verificación del uso de paquetes importados usando otras herramientas, por ejemplo, con CQLinq podemos escribir algunas reglas que advierten cada vez que un proyecto usa paquetes distintos a los especificados, pero es mejor tener esta verificación en el entorno de ejecución, por lo que el desarrollador no puede romper estas reglas.

Esta capacidad para tratar paquetes importados y exportados es administrada por la capa de módulos OSGi y fue nuestra cuarta pieza del rompecabezas.

Volvamos al contenedor OSGi y descubramos qué servicios proporciona.

Servicios de contenedores

Como descubrimos antes, el contenedor es lanzado por la clase EquinoxLauncher y la clase del marco inicializa y lanza los paquetes, para detectar qué servicios se proporcionan, busquemos todas las clases utilizadas en el método de inicialización del marco.

eclipse11

Algunas clases ya se han descubierto antes, como BundleRepository,BundleHost,PackageAdminImpl y ServiceRegistry.

¿Qué pasa con las otras clases:

  • Administrador de niveles de inicio:
    Cada paquete OSGi está asociado con un nivel de inicio que permite al servidor controlar el orden relativo de inicio y finalización de los paquetes. Solo deben estar activos los paquetes que tengan un nivel de inicio menor o igual que el nivel de inicio activo del marco del servidor. Por lo general, un paquete con un nivel de inicio más pequeño tiende a iniciarse antes.
  • Administrador de seguridad:
    La capa de seguridad maneja los aspectos de seguridad al limitar la funcionalidad del paquete a capacidades predefinidas.
  • Administrador de evento:
    El servicio de administración de eventos proporciona un modelo de publicación y suscripción para manejar eventos. Se realiza de acuerdo con la especificación del servicio de administración de eventos OSGi. El servicio de administración de eventos envía eventos entre los publicadores de eventos y los suscriptores de eventos (controladores de eventos) mediante la interposición de un canal de eventos. Los editores publican eventos en el canal y el canal de eventos define qué controladores deben recibir notificaciones. Por lo tanto, los editores y los controladores no tienen conocimiento directo entre sí, lo que simplifica la gestión de eventos.

La imagen completa de OSGi

Ensamblaremos todas las piezas del rompecabezas descritas anteriormente y tendremos la siguiente imagen OSGi:
capas-osgi

Esta arquitectura tiene los siguientes beneficios interesantes:

  • Sencillo.
  • Complejidad reducida.
  • Fácil implementación.
  • Seguro.

Lo que lo hace muy atractivo y digno de un desvío, y no perderá su tiempo si lo estudia en profundidad.