Archive for diciembre, 2008
Gestión de servicios en OSGi
Anteriormente hemos visto en este blog la gestión de dependencias en OSGi. En esta entrada vamos a ver OSGi desde el punto de vista de un servicio.
Un servicio lo podemos declarar como un interface que debe implementar el proveedor de ese servicio y que utilizarán los consumidores del servicio. Lógicamente, ese interface debe ser accesible tanto al proveedor como al consumidor. Aquí lo voy a poner en el módulo proveedor del servicio y dicho módulo lo exportará, pero podría estar en un módulo aparte para separarlo de la implementación.
Para esta entrada utilizaré un teórico servicio proveedor de mensajería SMS, cuyo interface será:
package org.tcymu.osgi.sms.interfaces; public interface ISmsProvider { public void sendMessage(String number, String text); }
Los servicios en OSGi se registran manualmente, es decir, no se resuelven automáticamente como las dependencias. Para registrar un servicio nos valemos del interface BundleActivator
ya visto en entradas anteriores. De esta forma, al iniciarse el proveedor de un servicio lo registraremos y al pararse desharemos el registro. Vamos a ver el código:
package org.tcymu.osgi.sms; import org.osgi.framework.BundleActivator; import org.osgi.framework.BundleContext; import org.osgi.framework.ServiceRegistration; import org.tcymu.osgi.sms.interfaces.ISmsProvider; public class SmsBundleActivator implements BundleActivator { private SmsProvider smsProvider; private ServiceRegistration serviceRegistration; public void start(BundleContext bundleContext) throws Exception { System.out.println("Starting SmsProvider"); smsProvider = new SmsProvider(); serviceRegistration = bundleContext.registerService(ISmsProvider.class.getName(), smsProvider,null); } public void stop(BundleContext bundleContext) throws Exception { System.out.println("Stopping SmsProvider"); serviceRegistration.unregister(); } }
La parte importante es el registro mediante el BundleContext
que se nos pasa en el start
de nuestro servicio. Para ello llamamos al método registerService
(guardando el ServiceRegistration
que nos devuelve) con los siguientes parámetros:
String
que representa el nombre de la clase que representa el servicio.- Proveedor del servicio. En este caso una simple implementación que sólo hace un
System.out
(esta implementación va también en este módulo). - Instancia de
java.util.Dictionary
(básicamente unaHashtable
) que representa unas propiedades del servicio. En este caso no pasamos nada, pero se podría pasar una versión o cualquier otro dato que pudiera ser necesario.
También podemos apreciar que en el stop
, deshacemos el registro mediante el método unregister
del ServideRegistration
que hemos guardado.
Como hemos visto en entradas anteriores, necesitamos declarar las propiedades del módulo en el MANIFEST.MF
. No hay novedades respecto a lo ya visto en otras entradas. Las líneas más interesantes son:
Bundle-Activator: org.tcymu.osgi.sms.SmsBundleActivator Export-Package: org.tcymu.osgi.sms.interfaces;version="1.0.0"
Si creamos el jar y lo instalamos en Felix (en este caso la versión 1.4.0) recibimos el mensaje esperado.
La parte interesante viene con el comando de Felix services
, que nos lista los servicios disponibles en el contenedor. En su salida podemos ver entre algunos servicios propios de Felix, el nuestro:
Ahora es el turno de crear el cliente para nuestro servicio. Lo pongo todo en una sencilla clase:
package org.tcymu.osgi.smsclient; import org.osgi.framework.BundleActivator; import org.osgi.framework.BundleContext; import org.osgi.framework.ServiceReference; import org.tcymu.osgi.sms.interfaces.ISmsProvider; public class SmsClient implements BundleActivator { public void start(BundleContext bundleContext) throws Exception { System.out.println("Starting SmsClient"); ServiceReference smsServiceRef = bundleContext.getServiceReference(ISmsProvider.class.getName()); ISmsProvider smsProvider = (ISmsProvider)bundleContext.getService(smsServiceRef); smsProvider.sendMessage("555555555", "Tus ceros y mis unos"); } public void stop(BundleContext bundleContext) throws Exception { System.out.println("Stopping SmsClient"); } }
Como podemos ver, primero se obtiene el ServiceReference
a través del contexto de módulos (BundleContext
). Y con esa referencia ya obtenemos la implementación del servicio también a través del contexto.
Si lo empaquetamos y arrancamos vemos que efectivamente se está utilizando el servicio.
Esta no será la última entrada dedicada a OSGi en este blog, o sea que todos los interesados… ¡a suscribirse!.
Añadir jars de un directorio al classpath
Gracias a esta entrada en el blog de Torsten Curdt descubro una interesante característica que desconocía de Java, eso sí a partir de su versión 1.6.
Todos los que llevamos un tiempo en Java nos hemos encontrado alguna vez con lo tedioso que puede ser el añadir todas la dependencias (normalmente en jars) de nuestro proyecto al classpath a la hora de ejecutarlo. Bien porque son muchos, bien porque en su nombre incluyen su versión o por algún otro motivo.
En el parámetro classpath
del comando java
(o javaw
) se podían añadir directorios de clases (java -classpath classes org.tcymu.MiClase
) o un jar determinado (java -classpath lib/dependencia.jar org.tcymu.MiClase
) o una lista de ambas combinaciones separadas por «:» (dos puntos).
Pues resulta que a partir de Java 1.6 se pueden añadir comodines a dicho classpath, así para añadir todos los jars de un directorio determinado utilizaríamos:
java -classpath classes:lib/* org.tcymu.MiClase
Sin duda puede ser muy beneficioso. Eso sí, debemos cuidar aún más si cabe los jars que añadimos como dependencias, ya que perdemos control sobre el orden en que se van leyendo los jars, por lo que si existen clases duplicadas podemos tener problemas.
Espero que os sea de utilidad.
Presentando Maven. Explicación básica.
Voy a presentar una herramienta libre muy utilizada en el mundo Java: Maven. Pretendo hacer una pequeña introducción para resaltar ideas.
Básicamente Maven es una herramienta de generación y gestión de proyectos Java. Uno de sus puntos fuertes es la gestión de dependencias, y es una de las cosas que hicieron que se adoptase en muchos proyectos en lugar de Ant.
Maven promueve una estructura de proyecto determinada, ya que se basa en el principio «convención sobre configuración», es decir, que si hacemos las cosas de una forma determinada nos ahorramos configurar esas cosas. Sin duda seguir esa estructura nos facilitará las cosas.
A pesar de la creencia popular de que Maven es una especie de Ant, o un Ant ampliado, la verdad es que se parecen en poco. Ente esas similitudes destaca el detalle de que la descripción del proyecto está en un xml, que en Maven se llama pom.xml (frente al build.xml de Ant).
Otro concepto importante dentro de Maven es el de repositorios. Un repositorio es un almacén de paquetes (archivos jar habitualmente) ordenados de forma inequívoca incluyendo la versión. Maven se descarga a un repositorio local (bajo .m2 en el directorio del usuario) todas las dependencias y plugins que va necesitando. Esta descarga hace que las primeras ejecuciones puede ser bastante largas.
Pero para hacernos una primera idea de cómo funciona Maven, vamos a ver cómo sería un archivo pom.xml típico.
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd"> <modelVersion>4.0.0</modelVersion> <groupId>org.tcymu.maven</groupId> <artifactId>test1</artifactId> <name>test1</name> <version>0.0.1-SNAPSHOT</version> <dependencies> <dependency> <groupId>log4j</groupId> <artifactId>log4j</artifactId> <version>1.2.15</version> </dependency> </dependencies> </project>
Las partes importantes sería la definición de groupId
que cumple una función similar a la de un paquete Java. artifactId
sería el nombre de este artefacto (jar) en el repositorio. Como es de suponer version
define la versión del artefacto. Y bajo dependencies
aparecen las dependencias, que como se puede ver constan precisamente de un groupId
, un artifactId
y una version
.
Con esto y si hemos seguido la estructura de paquetes que sugiere Maven, compilaríamos el proyecto (incluyendo la descarga de dependencias) con una simple línea:
mvn compile
Con compile le estamos indicando una fase. Algunas de las otras fases disponibles en Maven son: clean
(limpia todo lo que ha generado), package
(genera un jar), install
(instala el jar en el repositorio local) o deploy
(instala el jar en un repositorio remoto).
Hay que señalar que al ejecutar una fase se ejecutan todas las anteriores (para hacer un package
primero hace un compile
). Además ejecutar una fase significa realmente ejecutar una serie de tareas (goals) de una serie de plugins determinados.
¿Y qué es un plugin en Maven?. Pues prácticamente todo. Todas las tareas que ejecuta Maven son plugins. Así por ejemplo el compilar es llamar a la tarea compile del plugin compiler
. Se puede llamar a una tarea de un plugin directamente mediante:
mvn compiler:compile
(notación plugin:goal)
Existe una gran cantidad de plugins para Maven. Muchos de ellos con multitud de tareas.
Con esta introducción he intentado aclarar un poco los conceptos sobre Maven. Que realmente en un principio pueden ser algo confusos. Como en otras entradas utilizaré Maven, seguro que se irán aclarando más vuestras dudas.
Comentarios recientes