Curso: “OSGi: Open Services Gateway Interface”

Anuncio
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
dipina@eside.deusto.es
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
Descargar