Posts tagged ‘OSGi’
Gestión de dependencias en OSGi (y 2)
En la primera parte de esta entrada vimos cómo exportar e importar paquetes, y ahora vamos a ver el funcionamiento con varias versiones de dichos paquetes.
Para empezar, es el momento de mejorar nuestro Logger:
package org.tcymu.osgi.log.logger; import java.text.SimpleDateFormat; import java.util.Date; public class Logger { private SimpleDateFormat dateFormat = new SimpleDateFormat("dd-MM-yyyy HH:mm:ss.S"); private Date date = new Date(); public void log(String msg) { date.setTime(System.currentTimeMillis()); System.out.println(dateFormat.format(date) + ": " + msg); } }
Y también actualizamos las siguientes líneas del MANIFEST para cambiar su versión:
Bundle-Version: 1.0.1 Export-Package: org.tcymu.osgi.log.logger;version="1.0.1"
Si empezamos a instalar de nuevo los módulos (hemos generado una nueva versión logbundle-1.0.1.jar) en Felix, comprobamos que podemos perfectamente tener dos versiones de una librería (en este caso nuestro Logger) instaladas, y que cada aplicación según su configuración utilizará una u otra. Si hacemos la prueba vemos que si tenemos las dos versiones del Logger va a utilizar la versión 1.0.1. ¿Por qué?. La respuesta es que version="1.0.0"
se interpreta como version >= 1.0.0 y el contenedor selecciona la mayor. ¿Existe otra manera de definir las versiones?. Por supuesto. Los intervalos:
version="1.0.0"
se interpreta como version >= 1.0.0
version="[1.0.0,1.1.0)"
se interpreta como 1.0.0 <= version < 1.1.0
version="(1.0.0,1.1.0]"
se interpreta como 1.0.0 < version <= 1.1.0
NOTA: Nótese la notación estilo matemático para definir los intervalos abiertos o cerrados.
Por lo tanto si modificamos el MANIFEST de nuestro módulo de aplicación, en concreto la siguiente línea:
Import-Package: org.osgi.framework,org.tcymu.osgi.log.logger;version="[1.0.0,1.0.0]"
comprobamos que ahora hemos forzado a que utilice la versión antigua.
También debemos conocer que en el caso de que tengamos módulos que exportan un mismo paquete con una misma versión, todavía podemos diferenciarlos y forzar la carga de uno determinado mediante la utilización de atributos. Por ejemplo, si tenemos exportado nuestro Logger de esta manera:
Export-Package: org.tcymu.osgi.log.logger;version="1.0.1";provider="tcymu"
Y existiera otro módulo que exportara el mismo paquete, se podría forzar a utilizar nuestro paquete mediante:
Import-Package: org.tcymu.osgi.log.logger;version="1.0.1";provider="tcymu"
Existen más posibilidades que dejaremos en el tintero de momento, como por ejemplo la directiva resolution, el atributo use, la fragmentación de módulos o la dependencia de módulos (Require-Bundle). Se puede ver todo esto en la especificación descargable (previo registro) en la web de OSGi.
Y esto es todo de momento. Próximamente veremos la utilización de servicios en OSGi.
Gestión de dependencias en OSGi
Anteriormente en este blog hemos dado unos primeros pasos con OSGi, que os recomiendo leer si todavía no lo habéis hecho. Desde esas entradas he actualizado la versión de Apache Felix hasta la 1.2.1.
Ahora llega el momento de echar un vistazo a una de las características más interesantes de Osgi, como es la gestión de dependencias.
Lo primero que debemos conocer es que en OSGi permite exportar e importar paquetes de forma que es el contenedor OSGi el que se encarga de realizar y validar esas dependencias como paso previo a la ejecución de un módulo. Si no se pueden resolver dichas dependencias no se podrá ejecutar el módulo. Para jugar un poco con el tema, he creado dos pequeños módulos:
- Uno que exporta un Logger con funcionalidad avanzada (bueno, no muy avanzada, pero es que es un ejemplo) cuyo código vemos a continuación. Este módulo también tiene un BundleActivator que saca una traza tanto en el arranque como en la parada.:
package org.tcymu.osgi.log.logger; public class Logger { public void log(String msg) { System.out.println(System.currentTimeMillis() + ": " + msg); } }
- Otro que importa dicho Logger para hacer uso de él:
package org.tcymu.osgi.app; import org.osgi.framework.BundleActivator; import org.osgi.framework.BundleContext; import org.tcymu.osgi.log.logger.Logger; public class AppBundle implements BundleActivator { Logger logger = new Logger(); public void start(BundleContext arg0) throws Exception { logger.log("Arrancando AppBundle"); } public void stop(BundleContext arg0) throws Exception { logger.log("Parando AppBundle"); } }
Tanto la exportación como la importación de los paquetes se hace a través de los descriptores. Las líneas importantes son:
Bundle-SymbolicName: org.tcymu.osgi.log.LogBundle Bundle-Version: 1.0.0 Bundle-Activator: org.tcymu.osgi.log.LogBundle Import-Package: org.osgi.framework Export-Package: org.tcymu.osgi.log.logger;version="1.0.0"
y en la importación:
Bundle-SymbolicName: org.tcymu.osgi.app.AppBundle Bundle-Version: 1.0.0 Bundle-Activator: org.tcymu.osgi.app.AppBundle Import-Package: org.osgi.framework,org.tcymu.osgi.log.logger;version="1.0.0"
Empaquetaremos ambos módulos tal y como vimos en la anterior entrada. Ahora vamos a probar la instalación. Primero en el orden correcto. Primero instalo el módulo de log y después la aplicación:
install file:////d:/felix-1.2.1/bundle/org.tcymu.osgi.log.logbundle-1.0.0.jar Bundle ID: 16 install file:////d:/felix-1.2.1/bundle/org.tcymu.osgi.app.appbundle-1.0.0.jar Bundle ID: 17 start 16 start 17
Todo funciona correctamente. Ahora podemos hasta desinstalar el módulo de log, que una vez exportado, el contenedor mantiene una copia de esas clases para ofrecerlas a quien las necesite. Ahora vamos a parar y desinstalar nuestros módulos. También paro y reinicio Felix, ya que quiero comprobar qué ocurre si no tenemos una dependencia. Si instalo sólo el módulo de aplicación e intento arrancar, me lanza el siguiente error:
org.osgi.framework.BundleException: Unresolved constraint in bundle 24: package; (&(package=org.tcymu.osgi.log.logger)(version>=1.0.0))
Voy a dejar para una segunda parte de esta entrada el crear nuevas versiones de los módulos y comprobar cómo ser gestionan en el contenedor.
Actualización: Segunda parte
OSGi: Primeros pasos (y 2)
Bien, continuamos avanzando en el camino que empezamos en la primera entrada. Para empezar, os quería comentar mi opinión respecto a OSGi, basada en este pequeño estudio que he hecho. Esta tecnología favorece el diseñar e implementar aplicaciones en forma de módulos, lo que ayuda de cara a la reutilización y encapsulación de servicios. Al igual que la inyección de dependencias favorece la buena práctica de utilizar interfaces, OSGi favorece la buena práctica de modularizar. Hecho este pequeño inciso, pasamos al punto donde lo dejamos. Crear nuestro primer módulo. El código de mi primer módulo será:
package org.tcymu.osgi; import java.util.logging.Logger; import org.osgi.framework.BundleActivator; import org.osgi.framework.BundleContext; public class FirstBundle implements BundleActivator { Logger logger = Logger.getLogger(this.getClass().getName()); public void start(BundleContext arg0) throws Exception { logger.info("Arrancando FirstBundle"); } public void stop(BundleContext arg0) throws Exception { logger.info("Parando FirstBundle"); } }
Como vemos basta con implementar BundleActivator
, con sus métodos start
y stop
. En nuestro caso sacamos una traza de log en cada caso. Seguidamente necesitamos crear un descriptor para el jar
de nuestro primer módulo. El contenido de nuestro MANIFEST.MF
es:
Manifest-Version: 1.0 Bundle-SymbolicName: org.tcymu.osgi.FirstBundle Bundle-Version: 1.0.0 Bundle-Activator: org.tcymu.osgi.FirstBundle Import-Package: org.osgi.framework
Le damos a nuestro módulo un nombre y una versión, le señalamos la clase activadora mediante Bundle-Activator
y le indicamos los paquetes requeridos mediante Import-Package
Bien, con esto ya podemos empaquetar nuestro módulo en un jar
de la manera que más rabia nos dé. En mi caso lo he empaquetado mediante el export de Eclipse.
Ahora ya podemos instalar el módulo en Felix, para lo que usamos install
y que nos devuelve el identificador de nuestro módulo dentro del contenedor.
Con ese identificador podemos arrancar el módulo mediante start
, pararlo mediante stop
o desinstalarlo mediante uninstall
.
Bien, pues ya hemos visto cómo crear un módulo. Dejaré para más adelante el crear módulos dependientes de otros módulos y otras acciones más avanzadas. Por cierto, para salir de Felix, si no lo habéis descubierto todavía, se debe ejecutar shutdown
.
Comentarios recientes