Curso: “OSGi: Open Services Gateway Interface” Dr. Diego Lz. de Ipiña Gz. de Artaza http://paginaspesonales.deusto.es/dipina http://www.morelab.deusto.es http://www.ctme.deusto.es [email protected] Contenido 1. Introducción ¿Qué proporciona? ¿Dónde se aplica? c. Especificaciones y Herramientas OSGi 2. a. b. 2. Arquitectura OSGi JVM, modularidad, gestión de ciclo de vida, seguridad Registro de Servicios c. Concepto de Bundle d. Servicios Estándar OSGi a. b. 3. Programando OSGi con Knopplerfish a. b. c. d. e. f. g. h. i. Instalación y configuración de plug-in para Eclipse Usando Ant para compilar código OSGi Desarrollando un primer bundle Fichero de manifesto Creación de la clase Activator Compilando e instalando un bundle Creando interfaces de servicio Usando el registro de servicios Usando los servicios avanzados de servicios: ServiceListener y ServiceTracker 2/142 1 Contenido 4. Desarrollo de un ejemplo complejo en OSGi Diseño de un Servicio de Descubrimiento de Servicios distribuidos b. Usando el LogService c. Implementando el servicio, bundle y activator d. Creación de Servicios Distribuidos descubribles mediante el Distributed Bundle Discoverer a. 5. Servicios Avanzados en OSGi Declarative Service Configuration Service c. Event Admin Service d. Wire Admin Service e. R-OSGi a. b. 6. Conclusión 3/142 OSGi en Pocas Palabras Una framework Java para el desarrollo de aplicaciones desplegables remotamente, que requieren: Robustez Elevada distribución Heterogeneidad (diferentes dispositivos) Colaboraciones Resuelve el problema de desplegar muchos programas independientes en grandes sistemas distribuidos, proveyendo: Un sistema operativo para programas Un formato para la descarga de código ejecutable Un mecanismo para descubrir otros programas Estandarización de APIs para promocionar la reutilización Gestión eficiente del ciclo de vida de las aplicaciones descargadas 4/142 2 OSGi (Open Services Gateway Initiative) Define una Arquitectura Centralizada Orientada a Servicios (SOA) dentro de una máquina virtual (JVM) ¿Por qué se creo? Necesidad de crear herramientas que estandaricen los aspectos de integración del software de tal manera que la reutilización de componentes software sea más sencilla, fiable, robusta y de bajo coste. ¿Qué aporta? OSGi aporta modularidad dinámica a Java y permite estandarizar la integración del software 5/142 OSGi (Open Services Gateway Initiative) OSGi define un entorno de ejecución estandarizado orientado a componentes que es la base de una arquitectura SOA. Proporciona una pequeña capa de software que permite la cooperación entre componentes Java en una única JVM. Gestiona dinámica del ciclo de vida de los componentes gestionados (instalación, actualización o eliminación) sin reinicio del dispositivo Múltiples dominios de aplicación: Pasarelas residenciales Dispositivos móviles de última generación Industria del automóvil Aplicaciones de sobremesa o Dispositivos electrónicos (Philips iPronto, Nokia N800 y E70 o NSLU2) Realmente una especificación, no un producto, en R4: r4.core.pdf (2.01 MB) -- R4 Core Specification r4.cmpn.pdf (4.55 MB) -- R4 Service Compendium 6/142 3 OSGi Alliance OSGi es gestionado por la OSGi Alliance: http://www.osgi.org/, actualmente más de 30 empresas: Alpine Electronics Europe Gmbh , Aplix Corporation , BMW Group , Computer Associates , Deutsche Telekom AG , Echelon Corporation , Electricité de France (EDF) , Ericsson Mobile Platforms AB , Esmertec , Espial Group, Inc. , ETRI Electronics and Telecommunications Research Institute , France Telecom , Gatespace Telematics AB , Gemplus , Harman/Becker Automotive Systems GmbH , Hitachi, Ltd. , IBM Corporation , Industrial Technology Research Institute , Insignia Solutions , Intel Corporation , KDDI R&D Laboratories, Inc. , KT Corporation , Mitsubishi Electric Corporation , Motorola, Inc. , NEC Corporation , Nokia Corporation , NTT , Oracle Corporation , Panasonic Technologies, Inc. , ProSyst Software GmbH , Robert Bosch Gmbh , Samsung Electronics Co., Ltd. , SavaJe Technologies, Inc. , Sharp Corporation , Siemens AG , Sun Microsystems, Inc. , Telcordia Technologies, Inc. , Telefonica I+D , TeliaSonera , Vodafone Group Services Limited 7/142 Características Principales de OSGi 1. Gestión de componentes software 2. Gestión de Componentes Remota 3. Formato de empaquetamiento para aplicaciones (bundle) Instalar/Arrancar/Parar/Actualizar/Desinstalar un bundle Capacidad de recibir actualizaciones futuras de productos Provee una API de gestión a utilizar por un bundle de gestión que mapea un protocolo a un conjunto de llamadas Hace frente a aspectos de heterogeneidad Cooperación entre Aplicaciones Los bundles pueden contribuir tanto con código como servicios al entorno Contenedor abierto donde las aplicaciones no se ejecutan aisladas, comparten librerías El Registro de Servicios de OSGi ofrece modelo ligero para publicar, encontrar y asociar servicios dentro de una JVM 4. 5. Incluye un servicio de notificación para generar eventos de ciclo de vida Naturaleza Dinámica La actualización de componentes no requiere el reinicio Otras propiedades Despliegue simplificado Separación estricta de interfaz e implementación (enfoque SOA) Entorno de Ejecución Seguro Componentes Comerciales pueden Desplegarse Fácilmente 8/142 4 ¿Qué proporciona OSGi? Framework de componentes software estándar y abierto para productores de dispositivos, proveedores de servicios y desarrolladores Modelo de coexistencia para permitir diferentes componentes dentro de una JVM Modelo cooperativo donde las aplicaciones pueden descubrir y usar servicios provistos por otras aplicaciones en la misma instancia de OSGi Arquitectura flexible de gestión remota: API de despliegue que controla ciclo de vida de aplicaciones: bundle + ciclo de vida de bundle Conjunto de servicios opcionales como HTTP, Wiring, IO o eventos Entorno de ejecución seguro 9/142 Complejidad del Software Productivity Service Oriented Programming Structured Programming Assembly Complexity and Size 10/142 5 ¿Dónde se aplica? Pasarelas residenciales: Aplicaciones de dektop: Los fabricantes de dispositivos móviles requieren una plataforma para el despliegue continuo de servicios escalable, flexible y de pequeño tamaño basada en OSGi y Java (JSR 232 – http://jcp.org/en/jsr/detail?id=232 y http://gceclub.sun.com.cn/java_one_online/2006/TS-3757/TS-3757.pdf) Automoción: El popular entorno de desarrollo Eclipse está basado en OSGi (http://www.eclipse.org/osgi/) Dispositivos móviles de nueva generación: Empresas como Siemens producen dispositivos para la automatización del hogar y sus componentes conectados por PLC o UPnP. Hace controlables remotamente a esos dispositivos. Series 5 y 7 BMW incorporan OSGi para su plataforma de información y entretenimiento (AMI-C – http://www.ami-c.org/) Próximamente en Servidores de Aplicaciones Empresariales Interface21, creadores de Spring, definiendo la nueva generación de servidor EE en OSGi (http://www.springframework.org/osgi/specification) Ultima versión de IBM WebSphere basada en OSGi 11/142 Ejemplo de Despliegue OSGi en el Hogar Access from any Web Terminal (1) Aggregation & Management Platform Notification Server HomeControl Subscriber, Service and Device Management e.g. access from office or via PDA/WAP phone Home Office Internet Security Different Networks Webpad Entertainment/ Gaming Secure Tunnel Family Portal Access Gateway OSGi Service Gateway Firewall Service Offering Internet Services Services Provider 12/142 6 Ejemplo de Despliegue OSGi en el Hogar (2) 13/142 Implementaciones OSGi R4 Comerciales Existen varias implementaciones de OSGi comerciales certificadas para la versión 4: Makewave Knopflerfish Pro 2.0 (www.makewave.com) comercial/libre ProSyst Software mBedded Server 6.0 (www.prosyst.com) Samsung OSGi R4 Solution (www.samsung.com) KT OSGi Service Platform (KOSP) 1.0 (http://www.kt.co.kr/) HitachiSoft SuperJ Engine Framework (http://hitachisoft.jp/) 14/142 7 Implementaciones OSGi R4 Libres Además, las siguientes implementaciones libres de R4 OSGi también existen: Eclipse Equinox (http://www.eclipse.org/equinox/) – framework OSGi usada en Eclipse Makewave Knopflerfish (http://www.knopflerfish.org/) – más usada y mejor documentada (elegida para este curso) Apache Felix (http://felix.apache.org/site/index.html) OSCAR 15/142 Aplicaciones Reales usando OSGi X-ray measurement Systems BMW 7 series Eclipse Siemens Medical Nokia E70 Siemens Gigaset SX765 Espial Set-top box VDO on-board computer Prosyst mBedded Server, Remote Manager and Builder Bosch and Siemens Home Appliances Philips iPronto Telefónica ehogar Websphere application server 16/142 8 Características Esenciales de OSGi Gestión de componentes software Gestión remota de componentes Cooperación entre componentes/aplicaciones Provisión de entorno de ejecución seguro 17/142 Gestión de Componentes Software OSGi provee las siguientes funciones para la gestión del ciclo de vida de aplicaciones: Bundles: formato de empaquetamiento para aplicaciones. Es un simple JAR compatible con mecanismos de compresión ZIP. Gestión de ciclo de vida de bundles: Instalar un bundle Arrancar/parar un bundle Actualizar un bundle Desinstalar un bundle Java VM OS CPU IO 18/142 9 Configuración Remota de Componentes OSGi está concebido para dispositivos que operan desatendidos o controlados por un operador de plataforma que requiera gestión remota El ciclo de vida del software no para cuando un dispositivo abandona la fábrica Es conveniente poder actualizar el software instalado una vez desplegado Para permitir gestión remota OSGi proporciona una API de gestión de bundles, donde algunos bundles autorizados actúan de puente entre cualquier protocolo y las llamadas de la API. 19/142 Cooperación entre Aplicaciones (1) OSGi es el único modelo de servidor de aplicaciones Java donde éstas pueden compartir código entre ellas y no se ejecutan aisladas unas de otras: A diferencia de MIDP, Java EE (librerías pero mucha replicación) OSGi aporta un modelo de servicios ligero que permite publicar, encontrar y asociar servicios dentro de una JVM a través del Registro de Servicios Servicio OSGi = objeto de un bundle disponible para su uso por otros bundles 20/142 10 Cooperación entre Aplicaciones (2) Arquitectura Orientada a Servicios (SOA): Separa el contrato de la implementación Permite implementaciones alternativas Descubre y asocia dinámicamente las aplicaciones Asociación basada en contratos (definiciones de interfaz) Reutilización de componentes Componentes desligados a los detalles de implementación de otros componentes, solamente sus interfaces tienen que conocerse Service Contract provides Component uses 21/142 Entorno de Ejecución Seguro OSGi ofrece un modelo de seguridad dividido en 4 niveles: Mecanismo de seguridad de la JVM: previene operaciones peligrosas como manipulación de punteros o acceso no restringido a arrays Seguridad del lenguaje Java: modificadores de acceso (“public”, “private”, “protected”) Seguridad proporcionada por la plataforma Java SE (http://java.sun.com/javase/technologies/security/ y java.security.permission.*) OSGi separa unos bundles de otros y comprueba que un bundle tiene permisos para interactuar con otro 22/142 11 Arquitectura OSGi (1) OSGi proporciona un entorno de computación para bundles que se ejecutan conjuntamente en una JVM. 23/142 Arquitectura OSGi (2) 24/142 12 Arquitectura OSGi (3) Permite a las aplicaciones compartir una única JVM. Gestiona la carga de clases en una manera mejor definida y eficiente que el estándar Java Soporta incluso versiones Provee aislamiento/seguridad entre aplicaciones Media permitiendo comunicación y colaboración entre aplicaciones Provee gestión del ciclo de vida (instalar, empezar, parar, actualizar) Libre de políticas Éstas son impuestas por los bundles 25/142 Capas de la Framework OSGi (1) Life Cycle Security Applications Services Module Execution Environment 26/142 13 Capas de la Framework OSGi (2) Entorno de ejecución Módulos Instalación, comienzo, parada, actualización y desinstalación dinámica de Bundles Mecanismos de dependencia para asegurar operación correcta del entorno Servicios: Provee capacidades de carga y empaquetamiento de clases Facilita el lincado entre módulos Ciclo de Vida: Provee un contexto de ejecución bien definido a las aplicaciones, tal como J2SE, CDC, CLDC, MIDP Modelo de cooperación entre bundles que tiene en cuenta el dinamismo Completo modelo para compartir objetos entre bundles. Provisión de un mecanismo de eventos para gestionar el dinamismo Seguridad Imbuida en todas las capas basada en Java y en el modelo de seguridad de Java 2 Añade gestión dinámica de permisos 27/142 Capas de la Framework OSGi: Entorno de Ejecución OSGi requiere un entorno de computación seguro, abierto, robusto, bien documentado, maduro, rico y portable Inspirado en Java porque en 1999 cuando se creó OSGi, Java ya contaba con uno Podrían realizarse implementaciones alternativas de OSGi en .NET Las APIs de OSGi utilizan un subconjunto de las clases definidas por Java SE o Java ME CDC/CLDC CLDC/ MIDP OSGi Min. CDC/FP J2SE 28/142 14 Capas de la Framework OSGi: Modularidad (1) Un bundle es la unidad de ejecución en OSGi Compuesto principalmente de clases Java, librerías, recursos y manifest.mf Mejora la modularidad de las aplicaciones Java: Elimina dependencias en Classpath Protege quién accede a qué Soporta versiones 29/142 Capas de la Framework OSGi: Modularidad (2) Características: Compartición: OSGi promueve compartir las clases entre bundles Un bundle puede proveer librerías utilizadas por otros bundles reducción en necesidades de memoria Cada bundle puede exportar e importar paquetes (conjuntos de clases): Si múltiples bundles exportan el mismo paquete (con una versión diferente), la framework ha de seleccionar una versión apropiada por cada bundle Tras desinstalar un bundle los importadores son reiniciados para que se asocien a otro nuevo exportador de paquetes Gestión de interdependencias: los bundles pueden depender de funcionalidad ya alojada en un entorno Lazy loading: un bundle puede tener dependencias en bundles todavía no instalados Versionamiento: Diferentes bundles pueden utilizar diferentes versiones de la misma clase 30/142 15 Capas de la Framework OSGi: Modularidad (3) 31/142 Capas de la Framework OSGi: Ciclo de Vida (1) El bundle del sistema representa a la framework OSGi. State (active or not) Ofrece una API para gestionar bundles: Instalarlos Resolverlos Arrancar Parar Refrescar Actualizar Desinstalar Manages System bundle Bundle M Bundle X-v2 X Bundle A Bundle B 32/142 16 Capas de la Framework OSGi: Ciclo de Vida (2) Un bundle es iniciado por la clase BundleActivator Una cabecera en el fichero JAR que define un bundle hace referencia a esta clase La interfaz BundleActivator tiene dos métodos: Start() Start(): () inicializa y retorna inmediatamente Stop(): limpia el bundle El BundleActivator recibe un BundleContext que provee acceso a las funciones de la framework OSGi La Framework provee el servicio Start Level que controla el inicio/parada de grupos de aplicaciones INSTALLED STARTING start RESOLVED ACTIVE stop UNINSTALLED STOPPING 33/142 Capas de la Framework OSGi: Servicios(1) Provee un modelo de servicios dentro de una JVM Descubrimiento (y notificación) de servicios basada en interfaces y propiedades, no requiere ningún protocolo Asociación a uno o varios servicios mediante: Aclaración sobre Service Oriented Architectures (SOA) Control de programa Siguiendo ciertas reglas de la framework Configuración de despliegue Los Servicios Web se asocian y descubren en la red En la plataforma OSGi lo hacen dentro de una Java VM La OSGi Alliance define un gran conjunto de servicios 34/142 17 Capas de la Framework OSGi: Servicios(2) 35/142 Capas de la Framework OSGi: Seguridad Basado en Seguridad de Java 2 Permisos Firma de bundles 36/142 18 Beneficios de la Plataforma OSGi Los componentes son pequeños Los componentes están totalmente desacoplados unos de otros Permite la reutilización de componentes para diferentes aplicaciones Gran aceptación Aporta reusabilidad Modelo colaborativo Fáciles de hacer Mercado grande, muchos componentes existentes Modelo dinámico para las personalización y variación continua de aplicaciones en los dispositivos actuales 37/142 Bundles: Infraestructura de Despliegue en OSGi Un bundle es una aplicación autocontenida ejecutable en diferentes implementaciones de OSGi Como si fuera un fichero EXE en Windows Contiene programas y recursos Registra cero o más servicios Un mecanismo de búsqueda puede ser utilizado para encontrar servicios registrados por otros bundles Un servicio se especifica como una interfaz Java y puede ser implementado por varios bundles Lenguaje de consultas (filtros) Recordemos que: La propia framework es un bundle (System Bundle) La especificación de OSGi define un conjunto de servicios estándar que las aplicaciones pueden cargar y utilizar Bundle Repository – http://www2.osgi.org/Repository/HomePage Oscar Bundel Repository: http://oscar-osgi.sourceforge.net/ 38/142 19 Bundles: Contenido interno Un bundle es un fichero JAR que contiene: El fichero manifest.mf con los metadatos del bundle: Bundle A {…} Código (las clases en paquetes) Recursos (otros ficheros dentro del .JAR) Durante la instalación, la framework: Algunas cabeceras predefinidas por el formato JAR Otras definidas por la OSGi Alliance Lee el manifesto del fichero Instala el código y los recursos Resuelve las dependencias Inicia el control del ciclo de vida del bundle Durante la ejecución, la framework: Invoca BundleActivator para iniciar gestión del ciclo de vida Gestiona el CLASSPATH del bundle como una red de classloaders Gestiona las dependencias entre servicios Invoca a BundleActivator para parar el bundle Elimina los recursos utilizados por el bundle cuando acaba 39/142 Modelo de Colaboración en OSGi OSGi es más que un entorno de ejecución de Applet, MIDlet o Xlet Los bundles, unidades de ejecución en OSGi, colaboran mediante: Objetos de servicios Compartiendo paquetes Un Registro de Servicios dinámico permite a un bundle encontrar y seguir el rastro de otros objetos de servicio La framework efectúa una gestión eficiente de esta colaboración Dependencias, seguridad 40/142 20 Modelo de Colaboración en OSGi: Servicios = service, defined by java interface OSGi Framework = bundle Bundle A {…} Bundle B {…} Bundle C {…} 41/142 Modelo de Colaboración en OSGi: Dependencias La especificacion OSGi soporta la declaración de dependencias mediante las cabeceras: RequireRequire-Bundle e ImportImport-Package Require-Bundle crea una dependencia en un bundle completo q r Los bundles solamente importan lo que necesitan Se recomienda usar Import-Package porque facilita el despliegue y la gestión de versiones r s Import-Package crea una dependencia en un único paquete Muy fácil de usar Importa paquetes que no son utilizados Import-Package p Require-Bundle r 42/142 21 Modelo de Colaboración en OSGi: Servicios OSGi define una plataforma orientada a servicios con tres actores: Proveedores de Servicios, publican descripciones de servicios Consumidores de Servicios, descubren los servicios y se asocian a sus proveedores Registro de Servicios, permite a los consumidores de servicios descubrir otros servicios mediante consultas en sintaxis LDAP: Pueden recibir notificaciones sobre cambios en el estado de servicios "(&(" + Constants.OBJECTCLASS + "=Person)(|(sn=Jensen)(cn=Babs J*)))" En OSGi, un servicio está compuesto de: Interfaz de Servicio, una clase o interfaz Java Propiedades del servicio, pares nombre-valor Los servicios se implementan como objetos dentro del bundle 43/142 Modelo de Colaboración en OSGi: Servicios 44/142 22 Modelo de Colaboración en OSGi: Servicios 45/142 Modelo de Colaboración en OSGi: Servicios Java Application Manager No management bundles Midlet, Xlet, or Applet Service registry packages packages Midlet, Xlet, or Applet No collaboration No package management (versions!) JAVA Operating System No native code Hardware 46/142 23 Ciclo de Vida de un Bundle en Detalle (1) Antes de poder arrancar un bundle instalado es necesario resolver sus dependencias Todo bundle arrancable debe implementar la interfaz BundleActivator con los métodos: a) start(BundleContext) y b) stop(BundleContext) Durante la vida de un bundle varios eventos pueden ocurrir en la framework: La cabecera del manifesto Bundle-Activator indica la clase que habrá que instanciar para arrancar o parar un bundle Otros bundles son parados, actualizados, desinstalados, instalados, empezados, etc. Si se desinstala un bundle en el que otro tiene dependencias, el último accederá a los paquetes exportados anteriormente mientras no haya alternativas a esos paquetes exportados adoptados mediante un refresco del bundle El comienzo y fin de un bundle es grabado permanentemente, además existen start-levels que indican el orden de arranque de los bundles en la framework 47/142 Ciclo de Vida de un Bundle en Detalle (2) El objeto BundleContext provee una API para la instalación de bundles y su registro 48/142 24 Registro de Servicios en Detalle (1) La característica diferencial de OSGi frente a otros entornos Java es su naturaleza dinámica: Teléfono móvil es asociado a un nuevo dispositivo Pasarela residencial detecta nueva electrodoméstico conectado Coche detecta la presencia de un teléfono móvil a su alrededor El Registro de Servicios permite a los desarrolladores de aplicaciones construir pequeños y desacoplados componentes que pueden adaptarse al entorno cambiante en tiempo real Estos componentes pueden ser combinados para crear complejas aplicaciones 49/142 Registro de Servicios en Detalle (2) Un servicio se registra bajo un nombre de interfaz y un conjunto de propiedades Un lenguaje de filtrado sencillo basado en LDAP es utilizado para seleccionar los servicios necesarios. (&(sn=Ipiña)(cn=Diego)) La interfaz ServiceListener y la clase ServiceTracker permiten recibir notificaciones de eventos de ciclo de vida de servicios de interés La interfaz BundleContext permite la interacción con el Registro de Servicios: Registrar objetos (BundleContext.registerService()) Buscar otros objetos en el registro (BundleContext.getServiceReference()) Recibir notificaciones cuando servicios de interés son registrados o eliminados (BundleContext.addServiceListener()) 50/142 25 Detalles sobre Servicios listen Un servicio es un objeto registrado con el Framework por un bundle para su uso por otros bundles La semántica de un servicio es dada por su interfaz Java Un bundle puede registrar un servicio Un bundle puede usar un servicio (bind) con cierta cardinalidad 1..1, 0..1, 0..n Un servicio puede ser descubierto dinámicamente Búsqueda activa con filtro de consulta Interfaz listener ¡Los servicios pueden acabar en cualquier momento! ¡Todo es muy dinámico! bind register service package org.osgi.service.log; import org.osgi.framework.ServiceReference; public interface LogService { static final intLOG_ERROR= 1; static final intLOG_WARNING= 2; static final intLOG_INFO= 3; static final intLOG_DEBUG= 4; void log(int level, String message); void log(int level, String message, Throwable exception); void log(ServiceReference sr,int level, String message); void log(ServiceReference sr, int level, String message, Throwable exception); } 51/142 Manipulación de Servicios La interfaz BundleContext facilita los métodos para manipular el registro de servicios Los registros de servicio son gestionados por objetos de tipo ServiceRegistration Pueden utilizarse para desregistrar un servicio o modificar sus propiedades Los objetos ServiceReference dan acceso al servicio así como a sus propiedades El acceso a un servicio es mediante el método getService Los servicios se devuelven con el método ungetService. ungetService ServiceRegistration registerService( String clss, Object srvc, Dictionary prprts) ServiceReference[] getServiceReferences( String clss, String fltr) Object getService( ServiceReference reference) boolean ungetService( ServiceReference rfrnc); 52/142 26 Seguridad en Detalle OSGi ejecuta aplicaciones de una variedad de fuentes bajo control estricto de un sistema de gestión: Seguridad de Código de Java 2: java.security.Permission y sus subclases (FilePermission, SocketPermission) protegen el acceso a recursos del sistema Exposición de contenido de bundles mínima mediante modificadores de acceso de las clases Java o haciendo que los paquetes sean visibles sólo dentro de un bundle Permisos de paquete OSGi limitan quién puede importar o exportar paquetes o mediante org.osgi.framework.ServicePermission 53/142 Servicios Estándar en OSGi (1) Servicios del Framework: Permission Admin: Admin los permisos de bundles actuales o futuros pueden ser manipulados a través de este servicio Conditional Permission Admin: Admin extiende el Permission Admin con permisos que son aplicados cuando ciertas condiciones son cumplidas Package Admin: provee información sobre el estado compartido por un paquete y permite refrescarlos Start Level: conjunto de bundles que deben ejecutarse conjuntamente precediendo o siguiendo a otros niveles de comienzo URL Handlers: permite a los bundles contribuir dinámicamente con nuevos gestores de contenidos a la clase URL 54/142 27 Servicios Estándar en OSGi (2) Servicios del Sistema: Log Service: trazado de información, advertencias, información de debug o errores es redireccionado a bundles subscritos con él Configuration Admin: modelo flexible y dinámico para obtener información de configuración Event Admin: mecanismo general y flexible de publicar y subscribirse a eventos Device Access: mecanismo para asociar un driver a un nuevo dispositivo y descargar automáticamente un bundle User Admin: BBDD con información de usuario con propósito de autenticación y autorización IO Conector: permite a los bundles proveer nuevos y alternativos protocolos para javax.microedition.io Preferences Service: provee acceso a base de datos jerárquica de propiedades. Similar al registro de Windows. 55/142 Servicios Estándar en OSGi (3) Servicios del Sistema: Servicio HTTP: este servicio ejecuta un contenedor de servlets, los bundles pueden proveer servlets que son accesibles mediante HTTP. Servicio UPnP: mapea dispositivos en una red UPnP al Registro de Servicios. Wire Admin: permite la composición de diferentes servicios de acuerdo a una conexión. 56/142 28 Servicios Estándar en OSGi (4) Soporte a la Programación: Service Tracker: simplifica la gestión de servicios proveyendo una clase que sigue la pista de los servicios de una aplicación Declarative Services: permite leer una configuración XML de un bundle con registro de servicios y dependencias. Será iniciado solamente cuando los servicios declarados sean realmente necesarios por otros bundles. 57/142 Knopflerfish Varias opciones de implementaciones open source Makewave Knopflerfish – http://www.knopflerfish.org/ Equinox – implementación de OSGi usada por Eclipse (http://www.eclipse.org/equinox/) Apache Felix (http://felix.apache.org) Knopflerfish ofrece la mejor documentación y acabado Muy sencilla de utilizar Producida por Makewave (antes Gatespace Telematics) Provee herramienta de gestión de bundles fantástica Una de las implementaciones de pago más utilizadas Para una comparativa entre las ventajas y desventajas de las diferentes implementaciones open source de OSGi visitar: http://www.pierocampanelli.info/articles/2007/01/22/status-of-opensourceosgi-containers 58/142 29 Instalación de Knoplerfish Prerrequisito: tener una versión de Java 1.2.2 o superior en tu máquina Descargar knopflerfish_osgi_2.0.1.jar (11 MB) Framework completa, incluyendo código fuente, documentación, ejecutable autoextraíble: Instalar Knoplerfish con el siguiente comando: http://www.knopflerfish.org/download.html#2.0.1 java -jar knopflerfish_osgi_<version>.jar Ejecutar Knoplerfish mediante: cd %KNOPFLERFISH_INTALL_DIR%/knopflerfish.org/osgi java –jar framework.jar Abrirá el Knopplerfish OSGi Desktop 59/142 Knoplerfish OSGi Desktop 60/142 30 Instalación Plug-in Eclipse OSGi Pasos necesarios para su instalación: 1. 2. 3. Seleccionar la opción de menú en Eclipse HelpSoftware UpdatesFind and Install.... Seleccionar Search for new features to install e introducir los detalles de un nuevo site remoto: Knopflerfish update site, y la URL http://www.knopflerfish.org/eclipse-update/. Hacer clic sobre la caja Knopflerfish update site y pulsar Finish. Documentado en: http://www.knopflerfish.org/eclipse_install.html Pasos necesarios para su configuración: Seleccionar opción de menú WindowPreferences y en Framework añadir el directorio de instalación de Knopflerfish. Seleccionar Default JRE como el entorno de ejecución de bundles. Podrían seleccionarse otras opciones como CLDC u OSGi Minimum Asociar un Bunde repository donde Eclipse pueda resolver dependencias durante la compilación y ejecución de los bundles. Documentado en: http://www.knopflerfish.org/eclipse_preferences.html 61/142 Configuración de un Proyecto Knopflerfish en Eclipse 1. 2. 3. Seleccionar la opción de menú FileNewProject... Seleccionar la opción Bundle Project e introducir la información del proyecto. Editar y compilar el proyecto creado. La primera vez que se vaya a compilar y ejecutar el proyecto habrá que introducir una nueva configuración de lanzamiento, yendo a la opción del menú Run: OSGiKnopflerfish. Documentación adicional en: http://www.knopflerfish.org/eclipse_plugin.html 62/142 31 Usando Ant para Compilar Bundles Knopflerfish viene con un fichero build.xml preconfigurado que permite la generación y compilación de bundles en OSGi Pasos para configurar el proyecto: 1. 2. 3. 4. 5. Crear un nuevo directorio donde colocar el código del nuevo bundle. Por ejemplo, holamundobundle. Copiar el fichero knopflerfish_osgi_2.0.1\knopflerfish.org\ant\bui ld_example.xml a holamundobundle\build.xml. Configurar la plantilla de fichero build.xml a tu proyecto. Entre otras cosas deberás configurar las propiedades impl.pattern y api.pattern. Invocar ant. Instalar el bundle, bien usando la interfaz gráfica o bien los comandos de línea de comando provistos por Knopflerfish. 63/142 Nuestro Propio Fichero Ant Nosotros vamos a utilizar una versión simplificada del build.xml, no autogenera manifest.mf, pero sí compila (compile), crea el .jar (jar) correspondiente al bundle y lo despliega en Knopflerfish (deploy) IMPORTANTE: hay que crear la variable de entorno OSGI_HOME para que apunte al directorio de instalación de Knopflerfish, por ejemplo: c:\knopflerfish_osgi_2.0.1\knopflerfish.org 64/142 32 Nuestro Propio Fichero Ant <?xml version="1.0"?> <project name="nombre-bundle" default="all"> <property name="app.name" value="nombre-bundle"/> <property name="output.home" value="./build"/> <property environment="env"/> <property name="osgi.deploy" value="${env.OSGI_HOME}/jars"/> <path id="lib.class.path"> <fileset dir="${env.OSGI_HOME}"> <include name="**/*.jar"/> </fileset> </path> <target name="all" depends="init,compile,jar,deploy"/> <target name="init"> <mkdir dir="./classes"/><mkdir dir="./build"/> </target> <target name="compile"> name="compile"> <javac destdir = "./classes "./classes" classes" debug = "on "on"> on"> <src path= path= "./src "./src"/>< src"/><classpath "/><classpath refid=" refid="lib.class.path ="lib.class.path"/> lib.class.path"/> </javac </javac> javac> </target </target> target> 65/142 Nuestro Propio Fichero Ant <target name=" name="jar ="jar"> jar"> <jar basedir = "./classes" jarfile = "./build/${app.name}.jar" compress = "true" includes = "**/*" manifest = "./meta-inf/MANIFEST.MF"/> </target> <target name=" name="clean ="clean"> clean"> <delete dir = "./classes"/><delete dir = "./build"/> </target> <target name=" name="deploy ="deploy" deploy" depends=" depends="jar ="jar"> jar"> <delete> <fileset dir="${osgi.deploy}"> <include name="${app.name}.jar"/> </fileset> </delete> <copy todir="${osgi.deploy}"> <fileset dir="${output.home}" includes="${app.name}.jar"/> </copy> </target> </project> 66/142 33 Creando nuestro primer bundle Nuestro primer bundle contendrá un hilo que imprime el mensaje “¡Hola Mundo!” cada cinco segundos. Pasos para implementar un bundle autónomo que no exporta servicios a otros, pero sí puede consumirlos: Creación del fichero manifest.mf. Edición de una clase que implemente la interfaz BundleActivator. Edición de la clase del bundle, compilación y despliegue del bundle. 67/142 Pasos en detalle para crear nuestro primer bundle 1. Creación de un directorio para el bundle 1. 2. 3. 4. 5. 2. cd %KNOPFLERFISH_INSTALL%/knopflerfish.org/osgi mkdir bundles mkdir simplebundle mkdir simplebundle/src mkdir simplebundle/META-INF Creación del fichero simplebundle/META-INF/manifest.mf Manifest-Version: 1.0 Bundle-Name: simplebundle Bundle-SymbolicName: simplebundle Bundle-Version: 1.0.0 Bundle-Description: Demo Bundle Bundle-Vendor: Universidad de Deusto-ko Unibertsitatea BundleBundle-Activator: Activator: es.deusto.simplebundle.impl.Activator Bundle-Category: example ImportImport-Package: Package: org.osgi.framework 68/142 34 Pasos en detalle para crear nuestro primer bundle manifest.mf Fichero existente en todo archivo .jar Extendido por la especificación de OSGi para añadir información adicional sobre un bundle. Indica los servicios ofrecidos por un bundle a otros bundles. Expresa las interdependencias existentes entre el bundle configurado y otros - Un bundle no arranca hasta que todas sus dependencias han sido resueltas. En OSGi al fin y al cabo todo es un bundle, incluso la propia framework Su contenido documentado en página 3.2.1 Bundle Manifest Headers (página 36) de la Especificación de OSGi Propiedad BundleBundle-Activator indica a la framework qué clase es la Activator clase principal a invocar por la framework cuando cargue el bundle ImportImport-Package indica al framework que nuestro bundle requiere acceso a todas las clases en el paquete org.osgi.framework 69/142 Pasos en detalle para crear nuestro primer bundle 3. Create simplebundle/src/es/deusto/simplebundle/impl/Activator.java package es.deusto.simplebundle.impl; import org.osgi.framework.BundleActivator; import org.osgi.framework.BundleContext; public class Activator implements BundleActivator { public static BundleContext bc = null; private HelloWorldThread thread = null; public void start(BundleContext bc) bc) throws Exception { System.out.println("SimpleBundle starting..."); Activator.bc = bc; this.thread = new HelloWorldThread(); HelloWorldThread this.thread.start(); } public void stop(BundleContext bc) bc) throws Exception { System.out.println("SimpleBundle stopping..."); this.thread.stopThread(); this.thread.join(); Activator.bc = null; } } 70/142 35 Pasos en detalle para crear nuestro primer bundle Casi todos los bundles tienen una clase Activator Los Bundles que exportan funcionalidad pero no son ejecutados carecen de esta clase Clase que implementa la interfaz org.osgi.framework.BundleActivator Los métodos start() y stop() permiten gestionar el ciclo de vida de un bundle La clase BundleContext permite a un bundle interactuar con la framework OSGi: Subscribirse a eventos publicados por el framework (addXXXListener addXXXListener) addXXXListener Registrar servicios con el Registro de Servicios de la framework OSGi registerService) (registerService registerService Recuperar ServiceReferences del Registro de Servicios (getServiceReferences getServiceReferences) getServiceReferences Obtener y liberar objetos de servicios (getService getService y ungetService) ungetService Instalar nuevos bundles (installBundle installBundle) installBundle Obtener la lista de bundles disponibles (getBundles()) Obtener el objeto Bundle asociado a una implementación de un bundle (getBundle getBundle) getBundle Crear ficheros permanentes para el uso del bundle (getDataFile getDataFile) getDataFile 71/142 Pasos en detalle para crear nuestro primer bundle 4. Create simplebundle/src/es/deusto/simplebundle/impl/HelloWorldThread.j ava package es.deusto.simplebundle.impl; public class HelloWorldThread extends Thread { private boolean running = true; public HelloWorldThread() { } public void run() { while (running) { System.out.println("Hello World!"); try { Thread.sleep(5000); } catch (InterruptedException e) { System.out.println("HelloWorldThread ERROR: " + e); } } } public void stopThread() { this.running = false; } } 72/142 36 Pasos en detalle para crear nuestro primer bundle 5. 6. Compilarlo con fichero Ant provisto: ant Importarlo usando el Knopplerfish OSGi Desktop 73/142 Ejecución del Bundle 74/142 37 Ciclo de Desarrollo de un Bundle en OSGi Los pasos a seguir son: 1. 2. 3. 4. 5. Creación del fichero manifest.mf Definición de una interfaz Java con los métodos provistos por el servicio a ofrecer. Implementación de tal interfaz en una clase Java. Activación de la clase creada mediante un BundleActivator Compilación y despliegue del bundle Los ponemos en práctica mediante el desarrollo de la aplicación datebundle Ofrece como servicio formatear una fecha como un string Principal novedad del fichero manifest.mf es la cabecera ExportExport-Package: es.deusto.firstservice 75/142 Creando tu primer bundle ofreciendo un servicio 1. Definimos el fichero manifest.mf Manifest-Version: 1.0 Bundle-Name: firstservice Bundle-SymbolicName: firstservice Bundle-Version: 1.0.0 Bundle-Description: Demo Bundle Bundle-Vendor: Universidad de Duesto BundleBundle-Activator: es.deusto.firstservice.impl.Activator Bundle-Category: example ImportImport-Package: org.osgi.framework ExportExport-Package: es.deusto.firstservice 76/142 38 Creando tu primer bundle ofreciendo un servicio 2. Creamos la interfaz que define el servicio src/es/deusto/dateservice/DateService.java package es.deusto.dateservice; import java.util.Date; public interface DateService { public String getFormattedDate(Date date); } 3. Creamos la implementación del servicio en src/es/deusto/dateservice/impl/DateServiceImpl.java package es.deusto.dateservice.impl; import java.text.DateFormat; import java.util.Date; import es.deusto.dateservice.DateService; public class DateServiceImpl implements DateService { public String getFormattedDate(Date date) { return DateFormat.getDateInstance(DateFormat.SHORT).format(date); } } 77/142 Creando tu primer bundle ofreciendo un servicio 4. Creación de la clase que activa (Activator) el bundle en OSGi y exporta el servicio: src/es/deusto/dateservice/impl/Activator.java: package es.deusto.dateservice.impl; import java.util.Hashtable; import org.osgi.framework.BundleActivator; import org.osgi.framework.BundleContext; import org.osgi.framework.Constants; import org.osgi.framework.ServiceRegistration; import es.deusto.dateservice.DateService; public class Activator implements BundleActivator { public static BundleContext bc = null; public void start(BundleContext bc) bc) throws Exception { System.out.println("Empezando " + bc.getBundle().getHeaders().get(Constants.BUNDLE_NAME) ) + " ..."); bc.getBundle().getHeaders().get(Constants.BUNDLE_NAME Activator.bc = bc; DateService service = new DateServiceImpl(); ServiceRegistration registration = bc.registerService(DateService.class.getName(), bc.registerService(DateService.class.getName(), service, new Hashtable()); Hashtable()); System.out.println("Servicio registrado: dateservice"); } public void stop(BundleContext bc) bc) throws Exception { System.out.println("Parando " + bc.getBundle().getHeaders().get(Constants.BUNDLE_NAME) + " ..."); Activator.bc = null; } } 5. Compilar y ejecutar la aplicación 78/142 39 Registro de Servicios en OSGi El método registerService de BundleContext permite el registro de un servicio con el Registro de Servicios de OSGI. Recibe tres parámetros: Nombre de la interfaz del servicio Implementación del servicio Información adicional (opcional) sobre el servicio en formato de pares clave/value. Ejemplo de registro de servicio: Long I = new Long(20); Hashtable props = new Hashtable(); Hashtable(); props.put("description", props.put("description", "This an long value"); bc.registerService(Long.class.getName(), bc.registerService(Long.class.getName(), i, props); props); 79/142 Más detalles sobre el Activator El método getBundle() de BundleContext devuelve un objeto de tipo org.osgi.framework.Bundle asociado con tal contexto. El objeto Bundle permite recuperar todo tipo de metadatos: Su identificador (getBundleId getBundleId() getBundleId()) () Cabeceras de su fichero manifest.mf asociado (getHeaders getHeaders() getHeaders()) () Recursos asociados al bundle (getResources getResources() getResources()). () La interfaz Constants define nombres estándar para propiedades del entorno OSGi, sus servicios y las cabeceras del fichero Manifest (e.g. BUNDLE_NAME, BUNDLE_ACTIVATOR). 80/142 40 Consumiendo un Servicio en OSGi Creamos un nuevo proyecto denominado DateBundleUser con los ficheros manifest.mf y Activator.java Manifest.mf: Manifest-Version: 1.0 Bundle-Name: dateserviceuser Bundle-SymbolicName: dateserviceuser Bundle-Version: 1.0.0 Bundle-Description: Bundle that consumes dateservice exported by another bundle Bundle-Vendor: Revista Sólo Programadores BundleBundle-Activator: Activator: es.deusto.dateserviceuser.impl. es.deusto.dateserviceuser.impl. ActivatorWithoutCheckingReference ImportImport-Package: Package: org.osgi.framework,es.deusto.dateservice 81/142 Consuming a Service in OSGi Creamos la clase datebundleuser/src/es/deusto/dateserviceuser/impl/ActivatorWithoutChecking Reference.java, que obtiene una referencia al servicio DateService de manera incorrecta: Para dateservice y luego ejecuta dateserviceuser package es.deusto.dateserviceuser.impl; import import import import import import java.util.Date; org.osgi.framework.BundleActivator; org.osgi.framework.BundleContext; org.osgi.framework.Constants; org.osgi.framework.ServiceReference; es.deusto.dateservice.DateService; public class ActivatorWithoutCheckingReference implements BundleActivator { public static BundleContext bc = null; public void start(BundleContext bc) throws Exception { System.out.println("Empezando" + bc.getBundle().getHeaders().get(Constants.BUNDLE_NAME) + " ..."); ActivatorWithoutCheckingReference.bc = bc; ServiceReference reference = bc.getServiceReference(DateService.class.getName()); bc.getServiceReference(DateService.class.getName()); DateService service = (DateService)bc.getService(reference (DateService)bc.getService(reference); DateService)bc.getService(reference); System.out.println("Usando DateService: formatting date: " + service.getFormattedDate(new Date())); bc.ungetService(reference); bc.ungetService(reference); } public void stop(BundleContext bc) throws Exception { System.out.println("Parando " + bc.getBundle().getHeaders().get(Constants.BUNDLE_NAME) + " ..."); ActivatorWithoutCheckingReference.bc = null; } } 82/142 41 Consumiendo Servicios en OSGi proporcionados por ServiceFactory Una factoría de servicios permite la personalización del objeto de servicio que es devuelto cuando un bundle invoca a BundleContext.getService(ServiceReference) BundleContext.getService(ServiceReference). Normalmente, el objeto de servicio registrado por el bundle es devuelto Si el objeto de servicio implementa la interfaz org.osgi.framework.ServiceFactory, la Framework invoca el método ServiceFactory.getService para crear un instancia única no compartida del objeto de servicio por cada bundle que quiere obtener el servicio. La framework cachea el objeto devuelto hasta que el bundle lo libere. 83/142 Declarando, Instanciando y Registrando un Service Factory Definición del ServiceFactory: import org.osgi.framework.ServiceFactory; public class DisplayServiceFactory implements ServiceFactory { public Object getService(Bundle bundle, ServiceRegistration registration) { Display newDisplay = new Display(Long.toString(bundle.getBundleId())); return newDisplay; } public void ungetService(Bundle bundle, ServiceRegistration registration,Object service) { //Nothing needed here in this case } } Creando y registrando el ServiceFactory: //Create the ServiceFactory DisplayServiceFactory factory = new DisplayServiceFactory(); DisplayServiceFactory(); //Create the properties of the service. Not mandatory, but used to show //how properties work Hashtable properties = new Hashtable(); Hashtable(); properties.put("service.description", properties.put("service.description", "Factory of Display Services"); properties.put("service.type", properties.put("service.type", "Factory"); //Register the ServiceFactory for the type IDisplay ServiceRegistration registration = context.registerService(IDisplay.class.getName(), context.registerService(IDisplay.class.getName(), factory, properties); 84/142 42 Usando un Servicio OSGi que Implementa ServiceFactory //Get and Use one Factory of DisplayService using a filter String filter = "(service.type "(service.type=Factory)"; service.type=Factory)"; ServiceReference[] ServiceReference[] serviceRef = context.getServiceReferences(IDisplay.class.getName(),filter); context.getServiceReferences(IDisplay.class.getName(),filter); //Here we now there should be only one IDisplay display = (IDisplay)context.getService(serviceRef[0]); display.showMessage("Using Display service from Display Consumer Search with a filter"); //Release the service reference. This MUST be done when the // service is no longer going to be used by the consumer bundle context.ungetService(serviceRef[0]); 85/142 Consumiendo Servicios en OSGi OSGi es un lugar muy dinámico donde los servicios aparecerán y desaparecerán continuamente Necesidad de comprobar que lo que se recupera del Registro de Servicios de OSGi es realmente una implementación de servicio válida y no null Después de haber utilizado el servicio es recomendable realizar un unget Recuperación de una instancia de un servicio es un doble paso: 1. 2. Recuperación de una referencia al servicio mediante el método getServiceReference de BundleContext y Recuperación de un una instancia del servicio mediante el método getService de BundleContext 86/142 43 Consumiendo Servicios en OSGi Mejora 1 Comprobar que el servicio es disponible antes de utilizarlo: ServiceReference reference = bc.getServiceReference(DateService.class.getName( bc.getServiceReference(DateService.class.getName( )); if (reference != null) null) { DateService service = (DateService)bc.getService(reference); System.out.println("Usando DateService: formateando fecha: " +service.getFormattedDate(new Date())); bc.ungetService(reference); } else { System.out.println("¡Servicio no disponible!"); } 87/142 Consumiendo Servicios en OSGi Mejora 2 Usar ServiceListener public class ActivatorWithServiceListener implements BundleActivator, BundleActivator, ServiceListener { public static BundleContext bc = null; private ServiceUserThread thread = null; private DateService service = null; public void start(BundleContext bc) bc) throws Exception { System.out.println("Empezando " + getClass().getName()); ActivatorWithServiceListener.bc = bc; String filter = "(objectclass "(objectclass=" objectclass=" + DateService.class.getName() DateService.class.getName() + ")"; bc.addServiceListener(this, bc.addServiceListener(this, filter); ServiceReference references[] = bc.getServiceReferences(null, filter); for (int i = 0; references != null && i < references.length; i++) { this.serviceChanged(new ServiceEvent(ServiceEvent.REGISTERED, references[i])); } } public void serviceChanged(ServiceEvent event) { … } 88/142 44 Consumiendo Servicios en OSGi Mejora 3 Usar ServiceTracker (BestActivator.java) public class BestActivator implements BundleActivator { public static BundleContext bc = null; private ServiceTracker tracker = null; public void start(BundleContext bc) bc) throws Exception { System.out.println("Arrancando " + bc.getBundle().getHeaders().get(Constants.BUNDLE_NAME) + " ..."); BestActivator.bc = bc; MyServiceTrackerCustomizer customizer = new MyServiceTrackerCustomizer(bc); MyServiceTrackerCustomizer(bc); tracker = new ServiceTracker(bc, ServiceTracker(bc, DateService.class.getName(), DateService.class.getName(), customizer); customizer); tracker.open(); tracker.open(); } public void stop(BundleContext bc) throws Exception { System.out.println("Parando " + bc.getBundle().getHeaders().get(Constants.BUNDLE_NAME) + " ..."); tracker.close(); tracker.close(); BestActivator.bc = null; } } 89/142 Consumiendo Servicios en OSGi Mejora 3 Usando ServiceTracker (MyServiceTrackerCustomizer.java) import org.osgi.util.tracker.ServiceTrackerCustomizer; import es.deusto.dateservice.DateService; public class MyServiceTrackerCustomizer implements ServiceTrackerCustomizer { private ServiceUserThread thread = null; private BundleContext bc; public MyServiceTrackerCustomizer(BundleContext bc) { this.bc = bc; } public Object addingService(ServiceReference reference) { DateService service = (DateService) bc.getService(reference); if (this.thread == null) { this.thread = new ServiceUserThread(service); this.thread.start(); return service; } else return service; } public void modifiedService(ServiceReference reference, Object serviceObject) serviceObject) { this.thread.stopThread(); try { this.thread.join(); } catch (InterruptedException e) { e.printStackTrace(); } DateService service = (DateService) bc.getService(reference); this.thread = new ServiceUserThread(service); this.thread.start(); public void removedService(ServiceReference reference, Object serviceObject) serviceObject) { this.thread.stopThread(); try { this.thread.join(); } catch (InterruptedException e) { e.printStackTrace(); } this.thread = null; } } 90/142 45 Comentarios Mejoras Activators Comparar si la referencia a un servicio no es null resuelve el problema del NullPointerException PROBLEMA: si datebundle no es disponible cuando datebundleuser es arrancado ya no puede usarlo BundleContext permite registrar un ServiceListener con la framework indicándose un objeto de filtrado adicional sobre los eventos de servicios (registro, desregistro) de interés. String en formato LDAP especifica filtro: "(&(" + Constants.OBJECTCLASS + "=Person)(|(sn=Ipiña)(cn=Diego I*)))“ Ahora no obtenemos una referencia directamente a DateService en el método start(). De manera indirecta con la ayuda del método serviceChanged que debe implementar el Activator 91/142 Comentarios Mejoras Activators La clase de utilidad ServiceTracker automáticamente monitoriza los ServiceEvents asociados a un servicio dando al usuario la posibilidad de personalizar qué realizar cada vez que un servicio aparezca o desaparezca. Requiere implementar la interfaz ServiceTrackerCustomizer y proveer una clase instanciando tal interfaz como parámetro al objeto ServiceTracker. Los métodos addingService, modifiedService y removedService serán invocados por la clase ServiceTracker, cada vez que un nuevo servicio sea añadido, modificado o borrado. Solventa el problema de la interfaz ServiceListener que sólo es capaz de darse cuenta de la aparición de nuevos servicios o de la desaparición de antiguos. Truco: generar en start() eventos ServiceEvent.REGISTERED a partir de una búsqueda Registro Servicios 92/142 46 Resumen Desarrollo de Servicios en OSGi 5 pasos requeridos por un bundle exportador de servicios: Implementación del fichero manifest.mf Definición de una interfaz con el servicio a exportar por el bundle 3. Implementación de tal servicio 4. Implementación de la clase principal del bundle (BundleActivator BundleActivator) BundleActivator y 5. Compilación y despliegue del bundle en el servidor OSGi 1. 2. OSGi es un entorno muy dinámico donde aparecen y desaparecen servicios continuamente, donde la interfaz ServiceListener y la clase ServiceTracker nos pueden ofrecer ayuda. 93/142 El Servicio Estándar LogService OSGi LogService permite la creación de trazas de depuración o información durante la ejecución de un bundle La interfaz org.osgi.service.log.LogService define su funcionalidad Tipos de logeo: LOG_ERROR, LOG_WARNING, LOG_INFO y LOG_DEBUG Método log() para logearlos La interfaz org.osgi.service.log.LogReaderService permite recuperar objetos de tipo org.osgi.service.log.LogEntry del log registrando un org.osgi.service.log.LogListener Normalmente, en la clase Activator crearemos una variable estática donde guardaremos la referencia a LogService Para cambiar el nivel de traceo en Knopflerfish hay que cambiar el fichero props.xargs modificando sus propiedades: Dorg.knopflerfish.log.out a true y -Dorg.knopflerfish.log.level a (debug, info, warning o error) 94/142 47 El Servicio Estándar HttpService Puerta de entrada HTTP a la funcionalidad de la plataforma OSGi Definido en la interfaz org.osgi.service.http.HttpService Permite registrar servlets (registerServlet()) y recursos (registerResources()) en la propia plataforma para que sean accedidos desde el exterior, para: Control remoto Interfaces remotas Ventaja principal: Permite acceder a OSGi mediante peticiones HTTP-GET y POST y de ahí redireccionar a servicios internos 95/142 Poniendo en Práctica el LogService y el HttpService Objetivo: crear un cliente web del servicio dateservice a través del servicio estándar de OSGi HttpService Pasos para su uso: La cabecera Import-Package del manifest.mf debe incluir los siguientes elementos javax.servlet, javax.servlet.http, org.osgi.service.http La clase Activator en su método start() obtiene una referencia al servicio HttpService de OSGi y registra en él un servlet Implementamos una clase que herede de javax.servlet.http.HttpServlet 96/142 48 Dateservicewebuser – Activator.java import org.osgi.service.http.HttpService org.osgi.service.http.HttpService; ; import org.osgi.service.log.LogService; org.osgi.service.log.LogService; public class Activator implements BundleActivator { public static BundleContext bc = null; public static LogService log = null; public void start(BundleContext context) throws Exception { Activator.bc = context; ServiceReference refLog = Activator.bc.getServiceReference(LogService.class.getName()); ()); Activator.bc.getServiceReference(LogService.class.getName Activator.log = (LogService (LogService) LogService) Activator.bc.getService(refLog); Activator.bc.getService(refLog); Activator.log.log(LogService.LOG_INFO, Activator.log.log(LogService.LOG_INFO, "Empezando " + Activator.bc.getBundle(). Activator.bc.getBundle().getHeaders ().getHeaders(). getHeaders().get(Constants.BUNDLE_NAME ().get(Constants.BUNDLE_NAME) get(Constants.BUNDLE_NAME) + " ..."); Activator.bc = context; //get a reference to the http service ServiceReference ref = context.getServiceReference(HttpService.class.getName()); ()); context.getServiceReference(HttpService.class.getName HttpService http = (HttpService (HttpService) HttpService) context.getService(ref); context.getService(ref); //register //register the capabilities servlet http.registerServlet("/ http.registerServlet("/example ("/example", example", new DateServlet(context), DateServlet(context),new ),new Properties(), Properties(),null (),null); null); } public void stop(BundleContext context) throws Exception {} } 97/142 Dateservicewebuser – DateServlet.java public class DateServlet extends HttpServlet { BundleContext context; public DateServlet(BundleContext context) { this.context = context; } protected void doGet(HttpServletRequest request, request, HttpServletResponse response) throws ServletException, IOException { //strip the service id from the url String req = request.getRequestURI(); String serviceId = req.substring(req.lastIndexOf('/')+1); //find a service that matches up ServiceReference[] refs = null; try { refs = context.getServiceReferences(DateService.class.getName(),null); } catch (InvalidSyntaxException e) { new IOException().initCause(e); } for (int i = 0; i < refs.length; i++) { DateService service = (DateService) context.getService(refs[i]); //perform the id match Activator.log.log(LogService.LOG_INFO, "Comparando con clase " + DateService.class.getName()); if (serviceId.equals(DateService.class.getName())) { //do it response.getOutputStream().write(("Usando DateService: formateando fecha: " + service.getFormattedDate(new Date())).getBytes()); context.ungetService(refs[i]); return; } } response.getOutputStream().write(("No pudo encontrar servicio con id:"+ serviceId).getBytes()); } } 98/142 49 Desarrollo de un Servicio Avanzado en OSGi: BundleDiscoverer Tradicionalmente, los entornos OSGi básicos requieren que el administrador del entorno instale los bundles o drivers que hablan con los nuevos dispositivos desplegados o con aquellos que potencialmente vayan a desplegarse. Queremos añadir extensibilidad dinámica a nuestro entorno mediante un servicio de descubrimiento y despliegue automático de bundles disponibles en un entorno LAN dentro de un hogar, oficina o empresa Es un bundle que descubre, descarga e instala bundles anunciados por dispositivos conectados a un canal multicast bien conocido Su protocolo sencillo consiste de 5 mensajes: BUNDLE_SEARCH BUNDLE_RESPONSE uuid ipAddress port BUNDLE_ANNOUNCE uuid ipAddress port BUNDLE_GET_METADATA uuid BUNDLE_GET_JAR uuid 99/142 BundleDiscoverer 100/142 50 BundleDiscoverer 101/142 BundleDiscoverer: Parte Servidora 102/142 51 BundleDiscoverer: Parte Cliente 103/142 Constantes del Protocolo package es.deusto.bundlediscoverer; public class BundleDiscoveryConstants { public final static int DISCOVERY_PORT = 4445; public final static String DISCOVERY_IP = "230.0.0.1"; public final static int MAX_BLOCK_SIZE = 1024; public final static int BUNDLE_SEARCH_PERIOD = 5000; public final static int BUNDLE_HEARTBEAT_PERIOD = BUNDLE_SEARCH_PERIOD * 4; public final static String public final static String public final static String public final static String "BUNDLE_GET_METADATA"; public final static String "BUNDLE_GET_BUNDLE_JAR"; BUNDLE_SEARCH = "BUNDLE_SEARCH"; BUNDLE_ANNOUNCE = "BUNDLE_ANNOUNCE"; BUNDLE_RESPONSE = "BUNDLE_RESPONSE"; BUNDLE_GET_METADATA = BUNDLE_GET_BUNDLE_JAR = } 104/142 52 Parte Servidora BundleDiscoverer El servicio que exporta define un único método que devuelve un listado con los bundles descubiertos: package es.deusto.bundlediscoverer; public interface BundleDiscoverer { public BundleMetadata[] getAvailableBundles(); } Fichero con metadatos del bundle: package es.deusto.bundlediscoverer; import java.io.Serializable; import org.osgi.framework.Bundle; public class BundleMetadata implements Serializable { public String uuid; public String ipAddress; public int port; public long timestamp; public Bundle bundle; public BundleMetadata(String uuid, uuid, String ipAddress, ipAddress, int port, port, Bundle bundle) bundle) { this.uuid = uuid; this.ipAddress = ipAddress; this.port = port; this.timestamp = System.currentTimeMillis(); this.bundle = bundle; } } 105/142 BundleDiscoverImpl La clase BundleDiscovererImpl aparte de implementar la interfaz BundleDiscoverer, también hereda de la clase Thread Implementa 2 timers: Primero es responsable de enviar regularmente el mensaje BUNDLE_SEARCH a través de un canal multicast con el objeto de descubrir los servicios provistos por dispositivos en el entorno controlado Segundo comprueba que los proveedores de servicios (dispositivos) antes descubiertos siguen todavía activos, para en caso contrario proceder a la eliminación de los metadatos de los servicios descubiertos y desinstalar los bundles que actúan como proxies controladores de los dispositivos remotos Subscripción y recepción de datos en canal multicast: this.socket = new MulticastSocket(BundleDiscoveryConstants.DISCOVERY_PORT); this.addressGroup = InetAddress.getByName(BundleDiscoveryConstants.DISCOVERY_IP); this.socket.joinGroup(addressGroup); … byte[] buf = new byte[BundleDiscoveryConstants.MAX_BLOCK_SIZE]; DatagramPacket packet = new DatagramPacket(buf, buf.length); socket.receive(packet); 106/142 53 BundleDiscovererImpl.run() public void run() { this.initPeriodicSearchMessageDelivery(); this.initPeriodicRegistryCleanUp(); while (continueDiscovering) { try { String msgReceived = ""; do { msgReceived = this.receiveMulticastMessage(); } while (msgReceived.startsWith(BundleDiscoveryConstants.BUNDLE_SEARCH)); if ((msgReceived.startsWith(BundleDiscoveryConstants.BUNDLE_ANNOUNCE)) || (msgReceived.startsWith(BundleDiscoveryConstants.BUNDLE_RESPONSE))) { // Process BUNDLE_RESPONSE uuid serviceIP servicePort StringTokenizer st = new StringTokenizer(msgReceived); if (st.countTokens() == 4) { String cmdMsg = st.nextToken(); String uuid = st.nextToken(); String ipAddress = st.nextToken(); String port = st.nextToken(); if (!this.bundlesAvailableMap.containsKey(uuid) ) { new RetrieveRegisterBundleThread(this, RetrieveRegisterBundleThread(this, uuid, uuid, ipAddress, ipAddress, Integer.parseInt(port)).start(); Integer.parseInt(port)).start(); } else { synchronized (this.bundlesAvailableMap) { Bundle oldBundle = this.bundlesAvailableMap.get(uuid).bundle; int bundleState = oldBundle.getState(); if (bundleState == Bundle.UNINSTALLED) { this.bundlesAvailableMap.remove(uuid); } else if (bundleState == Bundle.ACTIVE) { this.bundlesAvailableMap.get(uuid).timestamp = System.currentTimeMillis(); } } } } } else { Activator.log.log(LogService.LOG_DEBUG, "BundleDiscovererImpl.run(): Ignored message: " + msgReceived); } } catch (IOException ioe) { Activator.log.log(LogService.LOG_ERROR, "BundleDiscovererImpl.run(): " + ioe.getMessage()); } } } 107/142 org.osgi.framework.Bundle Esta clase es el punto de acceso para controlar el ciclo de vida de un bundle ya instalado en la framework. getBundleId() – para obtener el identificador único de un bundle start() para arrancarlo stop() para pararlo update() para actualizarlo uninstall() para desinstalarlo getState() para obtener su estado actual (UNINSTALLED, INSTALLED, RESOLVED, STARTING, STOPPING o ACTIVE) o getHeaders() para obtener un Dictionary con las cabeceras y valores declaradas en el manifiesto del bundle. 108/142 54 Eliminación Selectiva de Bundles private void initPeriodicRegistryCleanUp() initPeriodicRegistryCleanUp { int delay = 0; // delay for 0 sec. this.bundleGarbageCollectionTimer = new Timer(); this.bundleGarbageCollectionTimer.scheduleAtFixedRate(new TimerTask() { public void run() { long currentTime = System.currentTimeMillis(); synchronized (bundlesAvailableMap) { for (BundleMetadata BundleMetadata service: bundlesAvailableMap.values() bundlesAvailableMap.values()) () { if (service.timestamp+BundleDiscoveryConstants.BUNDLE_HEARTBEAT_PERIOD < currentTime) { try { service.bundle.stop(); service.bundle.stop(); service.bundle.uninstall(); service.bundle.uninstall(); bundlesAvailableMap.remove(service.uuid); } catch (org.osgi.framework.BundleException be) { Activator.log.log(LogService.LOG_ERROR, "BundleDiscovererImpl.initPeriodicRegistryCleanUp(): " + be.getMessage()); } } } } } }, delay, BundleDiscoveryConstants.BUNDLE_HEARTBEAT_PERIOD); } 109/142 Recuperando e Instalando los .jar de los bundles ByteArrayOutputStream jarFile = new ByteArrayOutputStream(); do { bytesRead = in.read(buf, 0, 1024); jarFile.write(buf, 0, bytesRead); } while (bytesRead == 1024); String uploadedBundleDirectoryPath = System.getProperty("user.dir") + System.getProperty("file.separator") + "uploadedBundles"; String jarFileName = uploadedBundleDirectoryPath + System.getProperty("file.separator") + this.uuid+".jar"; FileOutputStream fos = new FileOutputStream(new File(jarFileName)); jarFile.writeTo(fos); fos.close(); jarFile.close(); String bundleUrl = "file:///" + jarFileName; try { Bundle bundleInstalado = Activator.bc.installBundle(bundleUrl); Activator.bc.installBundle(bundleUrl); bundleInstalado.start(); bundleInstalado.start(); this.parent.updateBundlesAvailable(this.uuid, new BundleMetadata(this.uuid, this.ipAddress, this.port, bundleInstalado)); } catch (BundleException be) { Activator.log.log(LogService.LOG_ERROR, " RetrieveRegisterBundleThread.run(): " + be.getMessage()); } 110/142 55 Servidor de Sockets para Proporcionar .jar en Cliente package es.solop.bundlediscoverer.impl.client; public class UnicastListener extends Thread { private ServerSocket server; ... public UnicastListener(BundleDiscovererClient parent) throws IOException { this.server = new ServerSocket (0); this.parent = parent; } public void run() { try { while (continueProcessingRequests) { Socket socket = server.accept(); BundleDiscoveryHandler handler = new BundleDiscoveryHandler(socket, this.parent); handler.start(); } } catch (IOException ioe) { ioe.printStackTrace(); } } ... } 111/142 Aplicación de Ejemplo Usando BundleDiscoverer La clase WeatherForecastServer exporte dos bundles que ofrecen el pronóstico del tiempo en un conjunto de ciudades españolas: WeatherForecastBunde – se comunica con la estación metereológica remota (WeatherForecastServer) para obtener el pronóstico del tiempo en formato HTML. WeatherForecastWebBundle – requiere la previa instalación de WeatherForecastBundle en el entorno en que se despliegue y ofrece una interfaz web para seleccionar la ciudad de la que se quiere obtener el pronóstico del tiempo y así obtenerlo con la ayuda de WeatherForecastBundle 112/142 56 BundleDiscoverer 113/142 BundleDiscoverer 114/142 57 Patrón de Diseño Whiteboard El modelo tradicional de subscribir los consumidores a los proveedores (patrón observer) requiere demasiadas clases y por tanto carga en el entorno que lo implementa para gestionar la notificación de eventos y no se ajusta al entorno dinámico de OSGi (establece demasiadas dependencias entre productores y consumidores) En el patrón de diseño Whiteboard: Cuando un consumidor quiere consumir se registra con el registro de servicios El productor escucha al registro de servicios de manera que cuando tenga que realizar una notificación contiene la lista actual activa de consumidores Delega la responsabilidad de la gestión de consumidores al Service Registry Se utiliza en la mayoría de servicios, excepto el LogService y el HttpService que vienen de la versión R1. Para más info: http://www.osgi.org/documents/osgi_technology/whiteboard.pdf 115/142 Declarative Services El modelo programático de activación bundles en OSGi se basa en la publicación, búsqueda y enlazado de servicios Problemático cuando el tamaño del sistema empieza a crecer: Tiempo de arranque: cada bundle tiene que registrar de forma activa sus servicios, requiriendo que todas sus referencias estén presentes de antemano. En sistemas grandes esto puede provocar retrasos de inicialización demasiado elevados. Tamaño en memoria: Cuando se registra un servicio, éste es cargado en memoria. Si no se utiliza es un espacio que se está malgastando, aparte de la sobrecarga que supone tener que crear el class loader correspondiente. Complejidad: en un entorno donde los servicios se consideran dinámicos (pueden aparecer y desaparecer en cualquier momento) el modelo de programación en sistemas complejos puede resultar difícil de mantener. 116/142 58 Declarative Services Para resolver esta situación OSGi propone un modelo declarativo, basado en el concepto de componente: Un componente es una clase Java con ciertas características especiales: Descripción XML que indica cómo debe gestionarse en OSGi Registro de servicios, comprobación de dependencias, registro de eventos se hace por nosotros Principales diferencias con el modelo tradicional: Se elimina el concepto de BundleActivator, el framework leerá el XML con los componentes que estén definidos y los creará en función de la política definida: Immediate, se crea el componente nada más resolver todas sus dependencias Delayed, el servicio es registrado, pero no se crea la instancia hasta que alguien lo solicita Factory, se registra una factoría para crear las diferentes instancias. Cada componente debe implementar dos métodos, uno de activación (activate activate() deactivate() activate()) () y otro de desactivación (deactivate deactivate()), () que se invocarán durante su ciclo de vida. Las referencias a otros servicios se declaran en el XML con ciertas propiedades (si son obligatorias, opcionales o la cardinalidad permitida) y es el framework el que se asegura de que estén correctamente enlazadas antes de activar el componente. La gestión de las referencias a servicios que desaparecen mientras el componente está activo también se pueden gestionar automáticamente sin necesidad de registrarse al framework para escuchar los eventos. Excelente alternativa para el desarrollo de componentes complejos en OSGi, muchas de las tareas a realizar se automatizan en el XML 117/142 Formato bundle.manifest en un Bundle definido con el Declarative Services ServiceService-Component: Component: OSGIOSGIINF/es.deusto.tecnologico.osgi.declarative.impl.Display.xml INF/es.deusto.tecnologico.osgi.declarative.impl.Display.xml Private-Package: es.deusto.tecnologico.osgi.declarative.impl Bundle-Version: 1.0.0 Bundle-Name: declarative_example Bundle-ManifestVersion: 2 Bundle-SymbolicName: declarative_example ImportImport-Package: Package: org.osgi.service.component, org.osgi.service.component, org.osgi.service.log 118/142 59 Fichero XML de definición de un bundle <?xml version='1.0' encoding='utf-8'?> <component name=' name='es.deusto.tecnologico.osgi.declarative.impl.Display ='es.deusto.tecnologico.osgi.declarative.impl.Display'> es.deusto.tecnologico.osgi.declarative.impl.Display'> <implementation implementation class='es.deusto.tecnologico.osgi.declarative.impl.Display' class /> <reference reference name='log' interface='org.osgi.service.log.LogService' bind='setLog' interface bind unbind='unsetLog'/> unbind </component </component> component> 119/142 Implementación del Bundle import org.osgi.service.component.ComponentContext; ... public class Display extends Thread { LogService log; private boolean quit = false; protected void activate(ComponentContext context) { log.log(LogService.LOG_INFO, "Display component activated"); this.start(); } protected void deactivate(ComponentContext context) { log.log(LogService.LOG_INFO, "Display component deactivated"); quit = true; } public synchronized void run() { while (!quit) try { Date now = new Date(); log.log(LogService.LOG_INFO, now.toString()); wait(5000); } catch (InterruptedException ie) { /*will recheck quit*/ } } public void setLog(LogService log) { this.log = log; log.log(LogService.LOG_INFO, "LogService bounded to Display"); } public void unsetLog(LogService log) { log.log(LogService.LOG_INFO, "LogService unbounded to Display"); this.log = null; } } 120/142 60 Event Admin Service Canal de eventos con opciones de filtrado y clasificado de eventos sencillo y homogéneo dentro de la plataforma OSGi, ofreciendo un mecanismo de comunicación entre bundles basado en paradigma publish/subscribe Los elementos que intervienen en el esquema del Event Admin son: Evento (Event Event): Event situación que un bundle quiere notificar, compuesto de: Un tipo se utilizada para categorizar los eventos, su nombre es un espacio de nombres jerárquico que se utiliza como un primer mecanismo de filtrado para saber cómo despacharlos a los diferentes consumidores. Sintáxis: fully/qualified/package/ClassName/ACTION Un conjunto de propiedades, o pares atributo valor que proporcionan más información sobre el evento. El atributo debe ser de tipo String, pero el valor puede ser cualquier objeto Java primitivo o String. Event Handler, Handler interfaz bajo el cual debe registrarse un servicio que quiera escuchar eventos del EventAdmin. Se configura con dos propiedades: topic-scope: eventos a los que se suscribe event-filter: filtro en sintaxis LDAP que hay que aplicar Ejemplo: “Recibir evento LOG_WARNING de cualquier bundle cuyo nombre simbólico empieza por com.acne” topic-scope org/osgi/service/log/LogEntry/LOG_WARNING event-filter (bundle.symbolicName=com.acme.*) Event Publisher – publicadores de eventos que no tienen ninguna característica especial. Simplemente necesitan una referencia al servicio EventAdmin donde publicar eventos. EventAdmin – servicio que implementa el canal de eventos, ofreciendo dos tipos de envío: síncrono y asíncrono 121/142 bundle.manifest de bundle utilizando servicio EventAdmin Manifest-Version: 1.0 Bundle-Version: 1.0.0 Bundle-Name: event_admin_example Bundle-ManifestVersion: 2 Bundle-SymbolicName: event_admin_example ImportImport-Package: Package: org.osgi.service.component, org.osgi.service.component, org.osgi.service.event, org.osgi.service.event, org.osgi.service.log Service-Component: OSGI-INF/Display.xml,OSGIINF/Clock.xml 122/142 61 Ficheros de configuración de los componentes productor y consumidor de eventos Productor: Clock.xml <?xml version='1.0' encoding='utf-8'?> <component name='es.deusto.tecnologico.osgi.eventadmin.impl.Clock'> <implementation class='es.deusto.tecnologico.osgi.eventadmin.impl.Clock'/> <reference name='log' interface='org.osgi.service.log.LogService' bind='setLog' unbind='unsetLog'/> <reference name=' name='eventAdmin ='eventAdmin' eventAdmin' interface=' interface='org.osgi.service.event.EventAdmin ='org.osgi.service.event.EventAdmin' org.osgi.service.event.EventAdmin' bind=' bind='setEventAdmin ='setEventAdmin' setEventAdmin' unbind=' unbind='unsetEventAdmin ='unsetEventAdmin'/> unsetEventAdmin'/> </component> Consumidor: Display.xml <?xml version="1.0" encoding="utf-8"?> <component name="es.deusto.tecnologico.osgi.eventadmin.impl.Display" immediate=" immediate="true ="true" true"> <implementation class="es.deusto.tecnologico.osgi.eventadmin.impl.Display"/> <property name=" name="event.topics tecnologico/osgi/ osgi/eventadmin/ eventadmin/Clock/ Clock/NEW_DATE</ NEW_DATE</p ="event.topics">es/ event.topics">es/deusto ">es/deusto/ deusto/tecnologico/ </p roperty> roperty> <service> service> <provide interface=" interface="org.osgi.service.event.EventHandler ="org.osgi.service.event.EventHandler" org.osgi.service.event.EventHandler" /> <provide interface=" interface="es.deusto.tecnologico.osgi.eventadmin.IDisplay ="es.deusto.tecnologico.osgi.eventadmin.IDisplay" es.deusto.tecnologico.osgi.eventadmin.IDisplay" /> </service </service> service> <reference name="log" interface="org.osgi.service.log.LogService" bind="setLog" unbind="unsetLog"/> </component> 123/142 Productor de Eventos: Clock import org.osgi.service.component.ComponentContext; import org.osgi.service.event.Event; import org.osgi.service.event.EventAdmin; public class Clock extends Thread { LogService log = null; EventAdmin eventAdmin = null; boolean quit = false; protected void activate(ComponentContext context) { log.log(LogService.LOG_INFO, "Clock component activated"); this.start(); } protected void deactivate(ComponentContext context) { log.log(LogService.LOG_INFO, "Clock component deactivated"); quit = true; } public synchronized void run() { while (!quit) { try { Date now = new Date(); String topic = "es/deusto/tecnologico/osgi/eventadmin/Clock/NEW_DATE "es/deusto/tecnologico/osgi/eventadmin/Clock/NEW_DATE"; es/deusto/tecnologico/osgi/eventadmin/Clock/NEW_DATE"; Hashtable properties = new Hashtable(); Hashtable(); properties.put("currentTime", properties.put("currentTime", now.toString() now.toString() ); Event newEvent = new Event(topic,properties); Event(topic,properties); eventAdmin.sendEvent(newEvent); eventAdmin.sendEvent(newEvent); // send it synchronously wait(3000); } catch (InterruptedException ie) {} } } 124/142 62 Productor de Eventos: Clock public void setLog(LogService log) { this.log = log; log.log(LogService.LOG_INFO, "LogService bounded to Display"); } public void unsetLog(LogService log) { log.log(LogService.LOG_INFO, "LogService unbounded to Display"); this.log = null; } public void setEventAdmin(EventAdmin eventAdmin) { eventAdmin) this.eventAdmin = eventAdmin; } public void unsetEventAdmin(EventAdmin eventAdmin) { eventAdmin) this.eventAdmin = null; } } 125/142 Consumidor de Eventos: IDisplay package es.deusto.tecnologico.osgi.eventadmin; public interface IDisplay { public String getLastMessage(); getLastMessage(); } 126/142 63 Consumidor de Eventos: Display import org.osgi.service.component.ComponentContext; import org.osgi.service.event.Event; import org.osgi.service.event.EventHandler; ... public class Display implements IDisplay, IDisplay, EventHandler { LogService log; private String message; final static String [] topics = new String[] {"es/deusto/tecnologico/osgi/eventadmin/Clock/NEW_DATE" }; protected void activate(ComponentContext context) { log.log(LogService.LOG_INFO, "Display component activated"); } protected void deactivate(ComponentContext context) { log.log(LogService.LOG_INFO, "Display component deactivated"); } public void setLog(LogService log) { this.log = log; log.log(LogService.LOG_INFO, "LogService bounded to Display"); } public void unsetLog(LogService log) { log.log(LogService.LOG_INFO, "LogService unbounded to Display"); this.log = null; } public synchronized void handleEvent(Event newEvent) newEvent) { message = (String)newEvent.getProperty("currentTime"); } public String getLastMessage() { return message; } } 127/142 Configuration Admin Facilita las labores de configuración de las propiedades de los diferentes bundles del sistema Dos alternativas: ManagedService – para servicios con un único conjunto de propiedades ManagedServiceFactory – para cuando se generan varias instancias de un servicio y cada una necesitas sus propiedades El índice a las propiedades de un servicio es su PID Cuando un bundle necesita información de configuración debe registrar uno o más objetos ManagedService El ConfigurationAdmin es capaz de enviar eventos cuando la configuración cambia (CM_UPDATED) o es eliminada (CM_DELETED) Consiste de un conjunto de interfaces que los servicios han de implementar para ser avisados cuando sus configuraciones cambian Hay que registrarse previamente bajo el interfaz ConfigurationListener En la cabecera Import-Package hay que usar org.osgi.framework, org.osgi.service.cm 128/142 64 Obteniendo, creando o actualizando un objeto de Configuración //Search the reference to the ConfigurationAdmin ServiceReference configAdminReference = context.getServiceReference(ConfigurationAdmin.class.getName()); context.getServiceReference(ConfigurationAdmin.class.getName()); ConfigurationAdmin configAdmin = (ConfigurationAdmin)context.getService(configAdminReference); ConfigurationAdmin)context.getService(configAdminReference); if (configAdmin == null) System.out.println("Configuration Admin Service not found."); //Create configuration for bundle configuration_admin_example //This returns a new Configuration object. //This new object is bound to the location and the properties set to null. //If the location parameter is null, it will be set when a Managed Service // with the corresponding PID is registered for the first time. Configuration config = configAdmin.getConfiguration("es.deusto.tecnologico.osgi.GetConfiguration configAdmin.getConfiguration("es.deusto.tecnologico.osgi.GetConfiguration ", null ); //As this is the first time the properties must be created first Hashtable properties = new Hashtable(); Hashtable(); properties.put("port", properties.put("port", new Integer(6023)); properties.put("motto", properties.put("motto", "Message set with the configuration admin"); //Update the properties config.update(properties); config.update(properties); 129/142 Usando un Objeto de Configuración package es.deusto.tecnologico.osgi.configurationadmin; public class GetConfiguration implements ManagedService { … public synchronized void init(BundleContext context) { //Set default properties, SERVICE_PID is mandatory properties = new Hashtable(); properties.put(Constants.SERVICE_PID , "es.deusto.tecnologico.osgi.GetConfiguration"); properties.put("port", new Integer(6123)); properties.put("motto", "First message of the day"); //Register the service as a ManagedService interface registration = context.registerService(ManagedService.class.getName(), this, properties); } public synchronized void updated(Dictionary properties) throws ConfigurationException { if (properties != null) { try { message = (String) properties.get("motto"); port = Integer.parseInt((String) properties.get("port")); //It is advisable but not mandatory to update the properties of the service registration.setProperties(properties); } catch (Exception e) {// Ignore, use defaults} } if (motd != null) motd.kill(); //Restart the Motto of the Day server with the new configuration motd = new MottoOfTheDay(); motd.setPort(port == 0 ? port = 6123 : port); motd.setMotto(message == null ? "No motto" : message); motd.start(); } } } 130/142 65 Wire Admin Service Objetivo: productor y el consumidor de datos o eventos conozcan lo mínimo posible el uno del otro para que los cambios en cada uno no perjudiquen al otro. En los entornos orientados a servicios existen varias alternativas a la hora de enlazar unos servicios con otros: Permitir al consumidor que elija al productor, siguiendo el tradicional patrón Observer Productor localiza a sus consumidores mediante el llamado patrón whiteboard Wire Admin Service – desligar completamente el productor y el consumidor Un Wire asocia a dos servicios identificados por su PID Modelo de funcionamiento: productores (interfaz Producer) y consumidores (interfaz Consumer) se asocian mediante la creación de objetos Wire Son gestionados por el WireAdmin que notifica a producers y consumers cuando los enlaces son activados Cuando un Producer tenga datos que notificar, actualizará el correspondiente Wire Se permite la definición de filtros Los datos que se envían entre el Consumer y el Producer deben ser compatibles: concepto de flavors Soporta el mecanismo Push y el Pull Proporciona un conjunto de eventos para gestionar el ciclo de vida de los Wires La cabecera Import-Package: org.osgi.framework, org.osgi.service.wireadmin 131/142 Definiendo un Consumidor import org.osgi.service.wireadmin.Consumer; … public class Tv implements Consumer { Wire[] producers; public Tv (BundleContext context) { Hashtable ht = new Hashtable(); ht.put( Constants.SERVICE_PID, "es.deusto.Tv" ); ht.put( WireConstants.WIREADMIN_CONSUMER_FLAVORS, new Class[] { String.class, Date.class } ); context.registerService( Consumer.class.getName(), this, ht ); } public void producersConnected(Wire[] producers) { this.producers = producers; } public void updated(Wire arg0, Object in) { if ( in instanceof Date ) { System.out.println("[Tv] - New time shown as Date: " + in.toString()); } else if ( in instanceof String ) { System.out.println("[Tv] - New time shown as String: " + in); } else { System.out.println("[Tv] Error: object type not supported."); } } … } 132/142 66 Definiendo un Productor import org.osgi.service.wireadmin.Producer; org.osgi.service.wireadmin.Producer; … public class Clock extends Thread implements Producer { Wire wires[]; BundleContext context; boolean quit = false; Clock(BundleContext context) { this.context = context; registerProducer(); } private void registerProducer() { Hashtable p = new Hashtable(); Hashtable(); p.put(org.osgi.service.wireadmin.WireConstants.WIREADMIN_PRODUCER_FLAVORS R_FLAVORS, p.put(org.osgi.service.wireadmin.WireConstants.WIREADMIN_PRODUCE R_FLAVORS, new Class[] { Date.class, Date.class, String.class }); p.put(org.osgi.framework.Constants.SERVICE_PID, p.put(org.osgi.framework.Constants.SERVICE_PID, "es.deusto.Clock "es.deusto.Clock"); es.deusto.Clock"); context.registerService(Producer.class.getName(), context.registerService(Producer.class.getName(), this, p); } public synchronized void run() { while (!quit) try { Date now = new Date(); for (int i = 0; wires != null && i < wires.length; i++) { wires[i].update(now); wires[i].update(now); wires[i].update(now.toString()); wires[i].update(now.toString()); } wait(3000); } catch (InterruptedException ie) {} } 133/142 Definiendo un Productor public synchronized void consumersConnected(Wire[] consumersConnected(Wire[] wires) { this.wires = wires; } public synchronized Object polled(Wire wire) { Class clazzes[] = wire.getFlavors(); for ( int i=0; i<clazzes.length; i++ ) { Class clazz = clazzes[i]; if ( clazz.isAssignableFrom( Date.class ) ) return new Date(); else if (clazz.isAssignableFrom(String.class)) return new Date().toString(); } return null; } } 134/142 67 Artículos de Investigación Usando OSGi Challenges in building service-oriented applications for OSGi, Hall, R.S. Cervantes, H. Lab. LSR IMAG, Domaine Univ., Grenoble, France, IEEE Communications Magazine, May 2004 Device and service discovery in home networks with OSGi, Dobrev, P. Famolari, D. Kurzke, C. Miller, B.A; IEEE Communications Magazine, Aug 2002 Enabling smart spaces with OSGi, The Gator Tech Smart House: a programmable pervasive space Choonhwa Lee Nordstedt, D. Helal, S. University of Florida, Jul-Sept 2003 Helal, S. Mann, W. El-Zabadani, H. King, J. Kaddoura, Y. Jansen, E. Dept. of Comput. & Inf. Sci. & Eng., Florida Univ., FL, USA, IEEE Computer, March 2005 Toward an OSGi-based infrastructure for context-aware applications Gu, T. Pung, H.K. Zhang, D.Q. Sch. of Comput., Nat. Univ. of Singapore, Singapore, Pervasive Computing, IEEE, Oct-Dec 2004 Research and implementation of the context-aware middleware for controlling home appliances, Jonghwa Choi Dongkyoo Shin Dongil Shin, Dept. of Comput. Sci. & Eng., Sejong Univ., Seoul, South Korea;IEEE Transactions on Consumer Electronics, Feb 2005 135/142 Características Avanzadas de OSGi Integración con Servicios Web The Knopflerfish Axis port Provee acceso SOAP/Web service a cualquier bundle OSGi bien sea para exportar un servicio OSGi como Servicio Web o para importar los servicios web en una framework OSGi https://www.knopflerfish.org/svn/knopflerfish.org/trunk/osgi/bundles_opt/s oap/axis.html Service Binder Simplifica el desarrollo de bundles OSGi automatizando la gestión de dependencias de servicios. Elimina la lógica de gestión de dependencias de servicios de los bundles http://gravity.sourceforge.net/servicebinder/ Bnd plugin de aqute que genera el manifest.mf por ti Wire Admin Service Permite enlazar dinámicamente servicios que producen datos con servicios que los consumen. ¿Podríamos enlazar los servicios automáticamente si tuviéramos descripciones de servicios mejoradas con semántica? tema de investigación 136/142 68 R-OSGi R--OSGi permite el descubrimiento e invocación de servicios provistos en otras implementaciones OSGi Utiliza SLP para el descubrimiento de servicios Los proveedores de servicios tienen que registrar un servicio para acceso remoto. Los consumidores en otras instancias de OSGi lo pueden descubrir con la infraestructura R-OSGi y recuperar el servicio (sus propiedades y su interfaz) La framework cliente construirá un proxy bundle sobre la marcha y lo registrará en la framework local Los servicios locales podrán ahora acceder el servciio remoto de manera transparente Desde la versión 0.5 es posible transferir el bundle completo al cliente Url: http://r-osgi.sourceforge.net/ 137/142 Conclusion La plataforma OSGi es algo así como un sistema operativo Java para componentes desplegados a través de la red, ofreciendo: Su misión es simplificar y hacer más eficiente incluso para entornos empotrados: Modelo de ejecución y programación orientada a servicios simple Despliegue dinámico Capa de módulos Capa de servicios Seguridad integral Problemas de despliegue La composición de servicios La gestión vitalicia de los componentes Implementado por varios fabricantes y comunidades open source, gran apoyo de la industria 138/142 69 References OSGi Tutorials OSGi Tutorial -- A Step by Step Introduction to OSGi Programming Based on the Open Source Knopflerfish OSGi Framework Develop OSGi bundles http://www.knopflerfish.org/osgi_service_tutorial.html OSGi and Gravity Service Binder Tutorial http://www.knopflerfish.org/programming.html OSGi Service tutorial http://www.knopflerfish.org/tutorials/osgi_tutorial.pdf http://oscar-osgi.sourceforge.net/tutorial/ Bundle Repository http://www.knopflerfish.org/repo/index.html 139/142 Apéndices 70 OSGi vs. UPnP There is no "versus" here. OSGi is fully complimentary to UPnP. No overlap. UPnP = set of protocols to discover and communicate with networked devices UPnP Implementations could use OSGi as execution environment like they could use Windows, Linux or QNX operating systems OSGi = environment for Java programs to execute in a well defined and managed environment OSGi implementations could use UPnP (or Jini, or SLP, or Salutation) to discover and use networked devices 141/142 OSGi & UPnP: Comparison OSGi Java Executing code Behaviour (Code) Program-Program oriented Standardizing Java interfaces Service is local and fast UPnP XML DTD Communications Declarative User oriented Standardizing XML templates Service is remote and slow to execute 142/142 71