HERRAMIENTAS DE PRODUCTIVIDAD SOFIA2 SDK JULIO 2014 Versión 3 1 INDICE 1 INDICE ............................................................................................................................................ 2 2 INTRODUCCIÓN .............................................................................................................................. 4 3 2.1 OBJETIVOS Y ALCANCE DEL PRESENTE DOCUMENTO .................................................................... 4 2.2 INSTALACIÓN DEL SDK DE SOFIA2 (SOFIA2-SDK) ................................................................... 4 2.3 CONSOLA DEL SDK DE SOFIA2 (SOFIA2-CONSOLE) ................................................................. 5 SOFIA2-CONSOLE: CREACIÓN DE KPS ............................................................................................. 7 3.1 COMANDO SOFIA2 CREARKP .................................................................................................. 7 3.2 COMANDO PARA GENERAR KP JAVA ........................................................................................ 8 3.2.1 3.3 COMANDO PARA GENERAR KP JAVASCRIPT ............................................................................. 12 3.3.1 3.4 Ejemplo de uso ....................................................................................................... 14 KPS JAVA: ESTRUCTURA Y CONFIGURACIÓN ................................................................................ 18 4.1 ESTRUCTURA DEL KP JAVA ................................................................................................... 18 4.1.1 Maven para la gestión de las dependencias .......................................................... 19 4.1.2 Spring como framework de soporte base .............................................................. 20 4.1.3 Soporte JMX ........................................................................................................... 21 4.2 5 Ejemplo de uso ....................................................................................................... 12 COMANDO PARA GENERAR KP ARDUINO ............................................................................... 14 3.4.1 4 Ejemplo de uso ......................................................................................................... 9 CONFIGURAR NUESTRO KP .................................................................................................. 26 4.2.1 Creación de proyecto Eclipse de nuestro KP.......................................................... 26 4.2.2 Soporte persistencia local para nuestro KP ........................................................... 26 4.2.3 Soporte manejo JSON en nuestro KP .................................................................... 31 KPS JAVA GESTIONADOS .............................................................................................................. 35 5.1 CONCEPTOS ...................................................................................................................... 35 5.2 LÓGICA DE FUNCIONAMIENTO .............................................................................................. 35 5.3 LANZADOR KPS .................................................................................................................. 36 Herramientas de Productividad SOFIA SDK Página 2/76 5.3.1 5.4 6 El Lanzador de KP´s en Ejecución. .......................................................................... 38 APP MODELO (KP MODELO) .............................................................................................. 38 5.4.1 Eventos ................................................................................................................... 40 5.4.2 Workers .................................................................................................................. 41 5.4.1 Suscripción ............................................................................................................. 47 5.4.2 Monitorización JMX ............................................................................................... 49 5.4.3 Ejemplo de Uso ...................................................................................................... 49 ANEXOS ........................................................................................................................................ 51 6.1 UN POCO DE SPRING .......................................................................................................... 51 6.1.1 Inyección de dependencias .................................................................................... 53 6.1.2 Ejemplos: ................................................................................................................ 54 6.1.3 Ámbito de un Bean................................................................................................. 64 6.1.4 Aplicando aspectos ................................................................................................ 65 6.1.5 Ecosistema/Portfolio Spring ................................................................................... 66 6.1.6 Enlaces de referencia ............................................................................................. 67 6.2 UN POCO DE MAVEN .......................................................................................................... 67 6.2.1 Conceptos básicos .................................................................................................. 67 6.2.2 Ciclo de vida ........................................................................................................... 68 6.2.3 Características ........................................................................................................ 68 6.2.4 Ejemplo de uso ....................................................................................................... 71 6.2.5 Enlaces de referencia: ............................................................................................ 73 6.3 UN POCO DE JUNIT ............................................................................................................ 73 6.3.1 Ejemplo .................................................................................................................. 75 6.3.2 Enlaces de referencia ............................................................................................. 76 Herramientas de Productividad SOFIA SDK Página 3/76 2 INTRODUCCIÓN 2.1 Objetivos y alcance del presente documento El presente documento constituye la referencia para conocer el funcionamiento de las herramientas que incluye el SDK de SOFIA2 y que nos ayudan a crear de forma más sencilla aplicaciones cliente (KPs) de SOFIA2. 2.2 Instalación del SDK de SOFIA2 (SOFIA2-SDK) Antes de seguir esta guía se recomienda leer la guía SOFIA2-Conceptos SOFIA2.doc Para poder desarrollar aplicaciones SOFIA2 es necesario tener instalado el SDK de SOFIA2, para ello: Acceder http://scfront.cloudapp.net/desarrollador.html y en la sección de Descargas, descargar el SDK de SOFIA2 Se trata de un fichero comprimido, que una vez descomprimido creará la siguiente estructura de directorios: Herramientas de Productividad SOFIA SDK Página 4/76 En el directorio SOFIA-RUNTIME tenemos un conjunto de herramientas para el desarrollo de KPs (jdk, navegador) así como un KP Java y otro Javascript de ejemplo. En el directorio SOFIA-SDK tenemos un conjunto de herramientas para el desarrollo de KPs (IDEs, APIs, Repositorio Maven con librerías de dependencias de proyectos java…) 2.3 Consola del SDK de SOFIA2 (SOFIA2-Console) La Consola de SOFIA2 es la pieza central de las herramientas de productividad de SOFIA2SDK. Su nombre técnico es SOFIA2-Console. Esta herramienta permite a través de una consola de comandos con ayuda contextual: Crear clientes SOFIA2 (KPs) para diferentes lenguajes Configurar capacidades sobre estos clientes La forma de ejecutar la consola es: 1. Una vez instalado SOFIA2-SDK desde el directorio en el que lo he instalado ejecuto SOFIA-START.bat 2. Sobre esta línea de comandos ejecuto el comando S:\>arqspring Herramientas de Productividad SOFIA SDK Página 5/76 Este comando nos abre la consola de SOFIA2 en otra ventana independiente: 3. Desde esta consola ya puedo ejecutar los comandos que se muestran en los siguientes capítulos del documento Herramientas de Productividad SOFIA SDK Página 6/76 3 SOFIA2-CONSOLE: CREACIÓN DE KPS Dentro de la consola de SOFIA2 el comando principal es el que nos permite crear KPs. 3.1 Comando sofia2 crearKp El comando sofia2 crearKp permite generar esqueletos de KPs. Se pueden generar tres tipos de KPs: Java, Javascript y Arduino. sofia2 crearKp --nombreKp [NOMBREKP] --tipoKp [tipo] --token [TOKEN] --instancia [INSTANCIA] --ip [IP] --puerto [PUERTO] --paquetebase [PAQUETE] Parámetros obligatorios: --nombreKp : nombre que tendrá el KP (obligatorio) --tipoKP: tipo del KP a generar: JAVA, JAVASCRIPT o ARDUINO (obligatorio) --token: token que junto con la instancia del KP permite conectar con el SIB (obligatorio) --instancia: instancia del KP y permite junto con el token conectar con el SIB (obligatorio) Parámetros opcionales: El resto de atributos dependerán del tipo de KP a generar. Son los siguientes: --ip: IP del SIB al que se va a conectar al usar tipoKP = JAVA. El formato para especificar la ip, se debe especificar separada por puntos, por ejemplo 192.112.12.121 --puerto: puerto del SIB al que se va a conectar al usar tipoKP = JAVA --server: IP del servidor para el tipo Arduino (se debe especificar separada por puntos, por ejemplo 192.112.12.121) y URL del servidor para el tipo de KP JavaScript (debe ser una url completa, por ejemplo http://localhost:8080) --mac: mac para establecer la conexión en un KP Arduino. El formato para especificar la MAC debe seguir el patón 0xXX, por ejemplo 0x00.0xAA.0xBB.0xCC.0xDE.0x01. --paquetebase: paquete base del proyecto Java, a utilizar con el tipoKP = JAVA. Si no se indica por defecto e paquetebase será “src.sources” Herramientas de Productividad SOFIA SDK Página 7/76 NOTA Se recomienda lanzar el comando sobre un directorio vacío, y que la ruta al directorio no contenga espacios en blanco Ejemplo de ruta válida R:\Desarrollo\MiKP\ 3.2 Comando para generar KP Java Para generar un proyecto KP de tipo Java será necesario indicar el nombre del KP, token e instancia de conexión, ip y puerto para conectar vía MQTT con el SIB. El comando quedará definido del siguiente modo: sofia2 crearKp --nombreKp [NOMBREKP] --tipoKp JAVA --token [TOKEN] --instancia [INSTANCIA] --ip [IP] --puerto [PUERTO] --paquetebase [PAQUETE] Al lanzar el comando se generará un proyecto web con las clases y recursos necesarios para desarrollar un KP. La estructura incluye: Fichero pom.xml de Maven con las dependencias necesarias para compilar nuestro proyecto KP: <dependency> <groupId>com.indra.jee.arq.spring.sofia2.ssap</groupId> <artifactId>ssap-core</artifactId> <version>2.2.0</version> </dependency> Estructura de Paquetes con conjunto de clases de utilidad o Excepciones: 2 clases de excepción que nos ayudarán en el manejo de la excepciones de nuestro proyecto de KP: ExcepcionGateway.java SensorExisteExcepcion.java o ClienteSibServer.java. Clase que hace de proxy al SIB. o GatewayLauncher.java. Clase que crea la conexión física con el SIB a través de MQTT. o ControladorLauncher.java. Para controlar la UI. Ficheros de configuración: Herramientas de Productividad SOFIA SDK Página 8/76 o monitorizacion.properties. Para monitorizar el KP vía JMX UI Web básica que permite conectar y desconectar al KP del SIB 3.2.1 Ejemplo de uso Veamos un ejemplo para crear un KP JAVA: Creamos un directorio, “KPAnuncios”. Abrimos la consola en ese directorio con \ArqSpring.bat Lanzamos el comando: sofia2 crearKp --nombreKp KPpubliAnuncios --tipoKp JAVA --token d9e77d01d3c84f96994bfdcd428faa97 --instancia KPpubliAnuncios:KPpubliAnuncios01 -ip localhost --puerto 1883 --paqueteBase com.sources Herramientas de Productividad SOFIA SDK Página 9/76 NOTA Es posible que en consola salga la siguiente traza, aunque no afecta al funcionamiento del comando.En el caso de que salga, es necesario salir de la consola (con exit) y volver a entrar (con arqspring). Importamos el proyecto en el IDE donde podemos ver que se nos ha generado la siguiente estructura: Herramientas de Productividad SOFIA SDK Página 10/76 Si desplegamos la aplicación en un servidor de aplicaciones como Jetty y accedemos a la url, podremos ver una UI como la siguiente: Si la url y puerto para conectar por MQTT y el token e instancia son correctos podremos conectar con el SIB. A partir de esta estructura podemos extender nuestro KP. Herramientas de Productividad SOFIA SDK Página 11/76 3.3 Comando para generar KP Javascript Para generar un KP de tipo JavaScript será necesario indicar el nombre del KP, token e instancia de conexión, ip del servidor: sofia2 crearKp --nombreKp [NOMBREKP] --tipoKp JAVASCRIPT --token [TOKEN] -instancia [INSTANCIA] --server [URL] Al lanzar el comando se generarán los recursos necesarios para desarrollar un KP JavaScript. La estructura incluye: Directorio img: que contendrá las imágenes del KP Directorio styles: que contendrá las hojas css con los estilos a utilizar en el KP. Librerías JavaScript: o kp-core : API Javascript que nos permitirá desarrollar nuestro KP o jquery.min: o dygraph-combined o base64 o XXTEA KP-javascript-[nombreKP].html: Que contiene esqueletos con las funciones javascript y una vista para conectar y desconectar el KP. 3.3.1 Ejemplo de uso Veamos un ejemplo para crear un KP JAVASCRIPT: Creamos un directorio, “KPAnunciosJS”. Abrimos la consola en ese directorio con \ArqSpring.bat Lanzamos el comando: sofia2 crearKp --nombreKp KPpubliAnuncios --tipoKp JAVASCRIPT --token d9e77d01d3c84f96994bfdcd428faa97 --instancia KPpubliAnuncios:KPpubliAnuncios01 -server http:localhost:8082 Si accedemos al directorio vemos que nos ha generado lo siguiente: Herramientas de Productividad SOFIA SDK Página 12/76 Si abrimos el .html generado vemos que en la parte inicial se cargan las librerías. En la imagen de arriba podemos ver que se han incuido varias líneas que carga DWR y las librerías asociadas en DWR y que deben estar en el servidor. A continuación se incluyen varias funciones Javascript para la conexión, desconexión, suscripción, etc. Herramientas de Productividad SOFIA SDK Página 13/76 3.4 Comando para generar KP Arduino Para generar un KP de tipo Arduino será necesario indicar el nombre del KP, token e instancia de conexión, ip, mac e ip del servidor sofia2 crearKp --nombreKp [NOMBREKP] --tipoKp ARDUINO --token [TOKEN] --instancia [INSTANCIA] --ip [IP] --server [IPSERVER] --mac [MAC] Al lanzar el comando se generará dentro del directorio que hemos creado un fichero arduino_[nombreKP].ino con la operativa. 3.4.1 Ejemplo de uso Veamos un ejemplo para crear un KP ARDUINO: Herramientas de Productividad SOFIA SDK Página 14/76 Creamos un directorio, “KPArduino”. Abrimos la consola en ese directorio con \ArqSpring.bat Lanzamos el comando: sofia2 crearKp --nombreKp KPArduino d9e77d01d3c84f96994bfdcd428faa97 --instancia --tipoKp ARDUINO --token KPArduino:KPArduino01 --ip 192.168.10.129 --server 192.168.10.128 --mac 0x00.0xAA.0xBB.0xCC.0xDE.0x01 Si abrimos el fichero con extensión .ino, podemos ver que se han incluido las librerías para poder desarrollar KPs Arduino. También se han incluido los datos de conexión Definición de variables para la configuración del Arduino, para el envío de mensajes SSAP y para la conexión física con el SIB a través de MQTT. Herramientas de Productividad SOFIA SDK Página 15/76 Se incluye también los esqueletos con la operativa: Herramientas de Productividad SOFIA SDK Página 16/76 Herramientas de Productividad SOFIA SDK Página 17/76 4 KPs JAVA: ESTRUCTURA Y CONFIGURACIÓN En el punto 3.2 hemos hecho referencia al comando para crear KPs Java. En este punto explicaremos con mayor detalle la estructura del código Java generado, dependencias introducidas, frameworks propuestos,… 4.1 Estructura del KP Java Al ejecutar el comando sofia2 crearKp con tipoKP JAVA se habrá generado en el directorio desde el que lo ejecutemos esta estructura. En esta estructura podemos observar: Fichero pom.xml de Maven para gestión de las dependencias Proyecto Eclipse (.project y .classpath) Herramientas de Productividad SOFIA SDK Página 18/76 Ficheros de configuración de mi aplicación: Aplicación Spring como contenedor IoC Aplicación Web Soporte JMX 4.1.1 Maven para la gestión de las dependencias Los KPs Java generados usan Maven para la gestión de las dependencias. Maven es básicamente una herramienta para la gestión y construcción de proyectos y puede verse como la evolución de Ant. Maven está basado en el concepto de un modelo de objetos del proyecto POM (Project Object Model) en el que todos los productos (artifacts) generados por Maven son el resultado de consultar un modelo de proyecto bien definido. Herramientas de Productividad SOFIA SDK Página 19/76 Maven intenta hacer la vida del desarrollador sencilla proporcionando una estructura de proyecto bien definida, unos procesos de desarrollo bien definidos a seguir, y una documentación coherente que mantiene a los desarrolladores y clientes informados sobre lo que ocurre en el proyecto. Para más información sobre Maven puede consultarse el anexo Un poco de Maven. 4.1.2 Spring como framework de soporte base Los KPs Java generados usan el framework Spring como tecnología base para el desarrollo y ejecución. Es importante por tanto conocer qué ofrece Spring. Spring es un framework ligero que ayuda en la estructuración de las aplicaciones permitiendo usar de forma coherente diferentes productos, frameworks y soluciones. La idea es que Spring se encarga de manejar la infraestructura, para que el desarrollador centre su atención en el desarrollo del resto de la aplicación. Spring permite construir aplicaciones a partir de POJOs “plain old java objects”, y dotándolas de características propias de aplicaciones J2EE. Spring permite hacer esto de una manera no intrusiva. Los puntos principales que caracterizan a Spring son: Es un estándar de facto en el mundo Java/JEE Actúa como pegamento entre todas las Capas y componentes Simplifica el desarrollo de aplicaciones JEE (Java) promoviendo buenas prácticas Es portable entre Servidores de aplicaciones, en este contexto a Spring se le conoce como un framework ligero puesto que no usa EJBS para la implementación de Servicios. No reinventa la rueda, reutiliza frameworks existentes y de amplio uso Herramientas de Productividad SOFIA SDK Página 20/76 Proporciona al programador un “modo de trabajo” Facilita la “colaboración”, puesto que estandariza el código, a nivel de componentes, permitiendo ahorrar tiempo e incrementando la calidad del software. Para más información sobre Spring puede consultarse el anexo Un poco de Spring. 4.1.3 Soporte JMX Los proyectos KPs JAVA creados ya vienen configurados para permitir la monitorización a través de JMX. Java Management Extensions (JMX) es la tecnología Java para administrar y/o supervisar aplicaciones, objetos del sistema, dispositivos y redes orientadas al servicio. Equivalente a SNMP. La entidad administrada/supervisada se representa mediante objetos llamados MBean (del inglés "Managed Bean", bean administrado). A través de JMX se puede interactuar con los componentes administrados (MBeans) de una aplicación, bien de forma local o remota. El soporte incluido en los KPs Java simplifica la creación de MBeans a través de anotaciones Java que permiten: Obtener el valor actual de los atributos de los objetos administrables que representan parámetros de monitorización o configuración Modificar el valor de los atributos de los objetos administrables que representan parámetros de configuración. Invocar operaciones de administración sobre objetos administrables, para realizar otro tipo de tareas de administración. 4.1.3.1 Anotaciones JMX Las anotaciones incluidas son: @ManagedResource para definir un MBean. Este MBean debe ser un JavaBean @ManagedAttribute para definir un atributo administrable en un MBean. Debe ser un atributo de un JavaBean, con su get y su set. @ManagedOperation para definir una operación administrable en un MBean Conviene que los atributos y operaciones administrables sean de tipo primitivo, así como arrays y collections de estos tipos. Estas pautas evitarán problemas de presentación en las consolas de administración JMX. Herramientas de Productividad SOFIA SDK Página 21/76 Definición de un Bean Monitorizable @Component @RooJavaBean @ManagedResource(objectName = "KPAnuncios:type=Monitorization,name=Connection", description = "Connection Service") public class ServiceImpl { El parámetro objectName permite especificar el nombre jerárquico que tendrá el objeto. Cada uno de los MBeans deberá disponer de un nombre jerárquico que deberá ser único entre todos los objetos administrables que se ejecuten en la máquina virtual java. [dominio]:[parámetro=valor], [parámetro=valor], … El dominio típicamente identifica a la organización que especifica los objetos de configuración, así como la categoría a la que pertenezcan los objetos de configuración, aunque pueden usarse otros convenios. Los parámetros y sus valores permiten dar más información acerca de los objetos de configuración, facilitando búsquedas. Cuando la aplicación se encuentre en ejecución y se acceda a estos objetos mediante JMX, se mostrará un árbol con todos los objetos disponibles, organizados por su nombre. Definición de un método get o set Monitorizable @ManagedAttribute(description = "Active Connection") public boolean getActiveConnection() { return connection; } Los métodos de acceso a los atributos deben anotarse de la siguiente forma, bien en los métodos get o bien en los set: Definición de una operación Monitorizable @ManagedOperation(description = "Añade un nuevo dato") @ManagedOperationParameters({ Herramientas de Productividad SOFIA SDK Página 22/76 @ManagedOperationParameter(name = "data", description = "Data") }) public void addData(String data) 4.1.3.2 Acceso a MBeans a través de JConsole Una vez en ejecución, los objetos de configuración o monitorización de la aplicación podrán ser observados y administrados a través de JMX, tanto de forma local como de forma remota. Para la administración local se puede hacer uso de la herramienta jconsole.exe que incluye el JDK de SOFIA-RUNTIME) y que permite operar sobre los objetos administrables: El primer paso será elegir el PID del proceso asociado a la máquina virtual java que se desea administrar: Una vez elegida la máquina virtual en la que se ejecuta la aplicación, pueden observarse todos los objetos de administración disponibles, clasificados según su nombre. Herramientas de Productividad SOFIA SDK Página 23/76 El elemento Attributes contiene el valor actual de los diferentes atributos del objeto administrable: El elemento Operations muestra las operaciones que es posible invocar sobre el objeto de administración para modificar la configuración: Herramientas de Productividad SOFIA SDK Página 24/76 4.1.3.3 Acceso a MBeans vía web: El KP JAVA también soporta el acceso a los MBeans a través de una consola Web que adapta el protecolo JMX a HTTP. Por defecto este acceso no está habilitado, para habilitarlo es preciso modificar el fichero monitorizacion.properties de nuestra aplicación, para elegir la dirección y puerto concretos sobre los que se desea que se lance: Tras esto arrancaré la aplicación y colocaré un navegador sobre la url en la que configuré el adaptador JMX-HTTP: Seleccionando el MBean concreto podré administrarlo: Herramientas de Productividad SOFIA SDK Página 25/76 4.2 Configurar nuestro KP 4.2.1 Creación de proyecto Eclipse de nuestro KP El SDK de SOFIA2 incluye una versión de Eclipse personalizada para el desarrollo de KPs. Para poder cargar, depurar y ejecutar un proyecto generado con el comando crearKp se puede ejecutar desde la consola de SOFIA2 el comando >perform eclipse Que genera el proyecto de Eclipse. 4.2.2 Soporte persistencia local para nuestro KP En muchas ocasiones el KP a desarrollar necesitará almacenar en local información, normalmente si se pierde temporalmente la conexión con el nodo central. Desde la consola de comandos se puede añadir fácilmente a nuestro KP la capacidad de almacenar información. El comando a ejecutar desde la consola es: >sofia2 configurarKp persistencia Este comando debe ejecutarse sobre un proyecto de tipo KP JAVA, como resultado de su ejecución se incluirá: Herramientas de Productividad SOFIA SDK Página 26/76 Dependencias necesarias en el pom.xml (H2 y ORMLite) Generar un Test y clases necesarias para simplificar el uso 4.2.2.1 Tecnologías involucradas: H2 y ORMLite El soporte a la persistencia local a los KPs Java se basa en estas tecnologías: H2 como base de datos local funcionando en modo embebido (se lanza desde el propio KP) ORMLite como motor de mapeo objeto-relacional para simplificar el uso de la base de datos Spring como framework de “glue” que simplifica el uso de estas tecnologías bajo su framework IoC Base de datos SQL Java H2: Ofrece funcionalidades similares a HSQLDB (pero con más rendimiento) Es una base de datos muy rápida y adecuada a entornos embebidos Open Source Ofrece diversos modos de funcionamiento: en memoria, embebido y Servidor Tamaño pequeño: 1 JAR de 1 Mb ORMLite como motor java de mapeo objeto-relacional Más ligero que JPA (sin dependencias) y adecuado a entornos embebidos Autoconfigurable Se compone sólo de 2 jars Configuración de clases con anotaciones Java simples Soporte DAO QueryBuilder para queries simples y complejas Soporta varias bases de datos: MySQL, Postgres, H2, Derby, etc. Soporte básico para transacciones Soporte configuración sobre Spring Soporte nativo Android SQLite API Herramientas de Productividad SOFIA SDK Página 27/76 4.2.2.2 Ejemplo de Uso Sobre el proyecto del KP Java Anuncios creado en el punto 3.2.1 lanzamos el comando que configurará la persistencia: >sofia2 configurarKp persistencia Tras ejecutar el comando se habrá generado: La clase TablaKp corresponde con la clase Java anotada que representa la tabla y campos de la tabla. Se ha creado a modo de ejemplo e incluye como campos un id, fecha y datos a insertar. Herramientas de Productividad SOFIA SDK Página 28/76 La clase FillTableKp que muestra cómo manejar el soporte de persistencia: La clase TestPersistenciaKP: en el test se valida que la tabla de base de datos contiene datos tras ejecutarse las operacions de FillTableKp. Herramientas de Productividad SOFIA SDK Página 29/76 El fichero applicationContext-Persistencia.xml que contiene toda la configuración de la base de datos se encuentra en el fichero. Veamos con más detalle que incluye este fichero. Los beans para la definición del transaction manager para H2 y ORMLite. Herramientas de Productividad SOFIA SDK Página 30/76 Bean para la creación de los Daos asociados a la clase Java que representa la tabla de base de datos. Bean para la creación automática de la tabla. Bean que representa nuestro DAO de ejemplo. Fichero database.properties con los datos de conexión: 4.2.3 Soporte manejo JSON en nuestro KP Al crear un proyecto KP Java automáticamente ya se incluye la dependencia a la librería FlexJson para manejar JSON desde Java. Además de esto la consola ofrece un comando que simplifica el uso de esta librería permitiendo de forma sencilla mapear Java-JSON y JSON-Java. El comando que permite esta funcionalidad es el siguiente: >sofia2 configurarKp mapperJSON No requiere de ningún parámetro adicional. Se debe lanzar sobre un proyecto sobre el que se haya creado previamente un KP Java. Como resultado se incluirán las dependencias necesarias, un JSON de ejemplo y se generará un test que muestra cómo utilizar esta funcionalidad. 4.2.3.1 Clase JsonToJAva Existe una clase JsonToJava.java que incluye un método jsonToJava que permite generar las clases Java a partir del Json. Herramientas de Productividad SOFIA SDK Página 31/76 public final packageName, static String void jsonToJava(String fileJson, String outputdirectory, className)throws String IOException, ClassNotFoundException, JsonParseException,JsonMappingException Como parámetros de entrada: - String outputdirectory: Ruta del proyecto donde se van a generar las clases - String packageName: Paquete para la generación de las clases - String fileJson: Ruta donde se encuentra el JSON - String className: Nombre de la clase Java. 4.2.3.2 Ejemplo de Uso Sobre el proyecto del KP Java Anuncios lanzamos el comando que configurará el mapperJSON >sofia2 configurarKp mapperJSON Que genera: La clase de Test TestJson que muestra cómo a partir del JSON de ejemplo incluido (SensorHumedad.json) se obtienen las clases Java asociadas a él y cómo mapear el json al objeto Java generado. NOTA: Para evitar problemas de compilación hasta que se generen las clases Java, se ha dejado comentado el segundo método de test. Herramientas de Productividad SOFIA SDK Página 32/76 Al ejecutar el test (hacemos refresh sobre el proyecto) vemos que se han generado la clase Java MyOntology y una serie de clases Java que representan el JSON. Si ahora descomentamos el método JavaToJson y lo lanzamos vemos que es posible mappear el Json al objeto Java MyOntology, haciendo uso de ObjectMapper. Herramientas de Productividad SOFIA SDK Página 33/76 Herramientas de Productividad SOFIA SDK Página 34/76 5 KPS JAVA GESTIONADOS Aparte del soporte para la creación de KPs en Java, Javascript y Arduino dentro de las herramientas de productividad se ofrece soporte para la creación de KPs Java gestionados, esto significa que siguen un patrón de desarrollo y que ofrecen funcionalidades avanzadas como la capacidad de autodespliegue y autoconfiguración. 5.1 Conceptos Lanzador KPs: Es la plataforma de ejecución de los KP´s Autogestionados, incluye un Servidor Web Jetty Embebido que ejecuta las diferentes AppModelo, gestiona su configuración y actualización en función de los requisitos indicados desde el SIB. AppModelo = KP Modelo: Son los KP desarrollados siguiendo la metodología y requisitos establecidos por el Lanzador KPs, abstrae al desarrollador de la lógica de actualización, configuración y perdida de conectividad. Evento: Son las diferentes acciones que un KP puede ejecutar (Captación sensórica, transformación de la información, envío y recepción de datos a y desde el SIB ) Worker: Son la tareas que interceptan los diferentes eventos que son generados por los KP. 5.2 Lógica de Funcionamiento El lanzador se conecta con el SIB y recupera. La información proporcionada por el SIB con el listado de los KP que debe tener desplegado y su configuración. Se comprueba si la versión de SW o Configuración es la indicada por el SIB Herramientas de Productividad SOFIA SDK Página 35/76 Se realiza copia de seguridad de la configuración y SW de los KP que han de ser actualizados. Se actualiza la Configuración y/o SW de los KP cuya versión difiere de la notificada por el SIB, tanto si es una versión superior como inferior. El lanzador arranca cada uno de los KP que tiene configurados. Los KP funcionan de forma autónoma accediendo a la infraestructura gestionada por el Lanzador de KP y que les proporcionan capacidades de comunicación con el SIB, monitorización, persistencia… El Lanzador interroga al SIB cada x tiempo por la configuración de los KP que debe tener desplegados, si el SIB le notifica una nueva versión de Software o Configuración, se detiene la aplicación y es actualizada. 5.3 Lanzador Kps Como hemos dicho el Lanzador KPs es el encargado de la ejecución y gestión de los KPs de la plataforma SOFIA2. Establece un marco de ejecución con un estricto ciclo de vida que facilita el desarrollo de clientes (APPModelo). La finalidad es permitir el mantenimiento remoto de las AppModelo, centralizando su configuración y gestión de versiones en el SIB, facilitar el desarrollo de clientes aportando mecanismos para el control de errores, persistencia, monitorización, . El Lanzador de KP´s es un JAR que levanta un contenedor Web donde despliega las AppModelo con la configuración que el SIB le notifica. Es un contenedor que permite ejecutar simultáneamente distintas implementaciones de KP sobre la misma máquina virtual. Utiliza un modelo de Workers para ofrecer un marco de funcionalidades básicas comunes a los KPs de SOFIA (herramientas de conexión, envío y recepción de mensajes, suscripciones, monitorización, persistencia local de mensajes, reintento de envío de mensajes fallidos, actualización/versionado automático de KPs,…) Requiere de una configuración básica para su funcionamiento, el fichero CONF_BASE.properties PROPIEDADES BASICAS DEL LANZADOR DE KP´s TIMEOUT TimeOut para comunicaciones con el SIB TIME_4_NEWVERSION Indica en minutos cada cuanto tiempo se comprueba la configuración de las AppModelo que han de estar desplegadas en el KPModelo. Herramientas de Productividad SOFIA SDK Página 36/76 SW_RUTA Ruta donde se almacenarán los almacenará el WAR del las properties de AppModelo CONF_RUTA Ruta donde se configuración de las AppModelo TMP_RUTA Ruta donde se almacenarán las copias de seguridad SIB IP : PUERTO de comunicación MQTT con el SIB PORT Puerto donde se levanta el servidor Web del KPModelo KP KP utilizado para comunicarse con el SIB INSTANCIA_KP Instancia KP utilizada para comunicarse con el SIB TOKEN Token utilizado para comunicarse con el SIB Estas propiedades son las mínimas necesarias para la ejecución del Lanzador de KP´s, y son accesibles desde las AppModelo, siempre y cuando estas últimas no las sobrescriban en su fichero de configuración. Su estructura es la siguiente LIB Donde se almacenan las dependencias para la ejecución del KPMODELO SW Directorio donde se almacenan los WAR APPModelo CFG Directorio donde se almacena la configuración de cada APPModelo TMP Directorio donde se almacena la copia de seguridad de las APPModelo que van ha ser actualizadas. El Lanzador de KP´s ha de ser ejecutado con el parámetro -DCONF_BASE= [DIRECTORIO DONDE SE UBICA CONF_BASE.properties] El script arranca la aplicación. Si no arrancamos el Lanzador de KP´s con esta configuración buscará el fichero CONF_BASE.properties en el directorio usr. Herramientas de Productividad SOFIA SDK Página 37/76 5.3.1 El Lanzador de KP´s en Ejecución. Cuando se arranca el Lanzador de KP´s a través del comando START-KP-MODELO. Lo primero que hace es conectarse al SIB con la configuración establecida en el fichero CONF_BASE.properties, solicitando la configuración asociada a él. A nivel de KP y de Instancia de KP. El SIB devolverá por un lado la configuración global del KP. Configuración de Ontologías Configuración de Assets El listado de las aplicaciones que ha de desplegar con su información a nivel de. Firmware Configuración Cualquiera de estos datos está versionado, por lo que si el Firmware o la Configuración tienen una versión diferente (Inferior o Superior) a la desplegada se actualizará en el Lanzador de KP´s. Previo a realizar una actualización de firmware o configuración se guarda una copia en el directorio indicado en el parámetro de configuración TMP_RUTA. Una vez actualizado el APPModelo se despliega en el Lanzador de KP´s. IMPORTANTE Las aplicaciones ya desplegadas en el Lanzador de KP´s y que no son notificadas por el SIB son eliminadas. Si se encuentran en ejecución serán detenidas y eliminadas. 5.4 APP Modelo (KP Modelo) Las aplicaciones que se despliegan en el Lanzador de KP´s reciben el nombre de APPModelo, estas aplicaciones son creadas con el comando sofia2 crearAppModelo --id [NOMBRE APPMODELO] –paquetebase [PAQUETE APLICACION], La Herramienta de Productividad crea una estructura y las implementaciones por defecto para empezar a desarrollar el negocio específico. Herramientas de Productividad SOFIA SDK Página 38/76 El proyecto Maven generado tiene las dependencias necesarias para compilar las AppModelo. <dependency> <groupId>com.indra.sofia2</groupId> <artifactId>launcher</artifactId> <version>1.0</version> <scope>provided</scope> </dependency> <dependency> <groupId>com.mycila</groupId> <artifactId>mycila-pubsub</artifactId> <version>5.0.ga</version> <scope>provided</scope> </dependency> <dependency> <groupId>com.indra.sofia2</groupId> <artifactId>ssap</artifactId> <version>2.5.0</version> <scope>provided</scope> </dependency> <dependency> <groupId>com.jsontojava</groupId> <artifactId>jsontojava</artifactId> <version>1.0</version> <scope>provided</scope> </dependency> Todas estas dependencias tiene scope provided, pues en tiempo de ejecución será el Lanzador de KP´s el que nos proporcione las versiones adecuadas de todas las librerias necesarias para la ejecución de las diferentes APPModelo. Herramientas de Productividad SOFIA SDK Página 39/76 IMPORTANTE Cuando se despliegan multiples wars es necesario que estos dispongan del identificador como una propiedad context-param en el web.xml, para diferenciar unívocamente cada aplicacion <context-param> <param-name>webAppRootKey</param-name> <param-value>NOMBREAPP MODELO</param-value> </context-param> A partir de la versión 2.8.0 del SDK cuando se crea una nueva AppModelo esta propiedad es creada de forma automática. 5.4.1 Eventos Los KPs gestionados funcionan en base a unos Eventos, estos Eventos se producen cuando se desencadena alguna opción. Los siguientes eventos están predefinidos en la infraestructura del Lanzador de KP´s. START_APP Evento que se genera cuando se arranca la aplicación. STOP_APP Evento que se genera cuando se solicita la detención de la aplicación. APP_STOPED Evento que se genera cuando la aplicación ha sido detenida. PREPARE_TO_RECEIVED Evento que se genera una vez que se ha arrancado la aplicación, se han lanzado los Workers asociados al evento START_APP. NEW_DATA_RECEIVED Evento que se genera cada vez que se recibe datos desde un sensor. DATA_TO_SEND Evento que se genera cuando los datos recibidos por el sensor son convertidos a un mensaje SSAP para ser enviados al SIB CONNECTION_TO_SIB Evento que se genera cuando se existe conectividad con el SIB. NO_CONNECTION_TO_SIB Evento que se genera cuando se pierde la conectividad con el SIB. DATA_SEND_TO_SIB Evento que se genera cuando se ha enviado un mensaje SSAP al SIB. ERROR_ON_SENT_TO_SIB Evento que se genera cuando se produce un error al enviar un SSAP al SIB. Herramientas de Productividad SOFIA SDK Página 40/76 Además el desarrollador puede registrar nuevos eventos de esta forma: Los eventos deben ser registrados a través del Bean KPWorkerCfg, para poder registrar nuevos eventos deberemos obtener una referencia a este objeto @Autowired protected KPWorkerCfg kPWorkerCfg; e invocar el método public void addEvent(String event, Class message, Subscriber worker) al cual debemos informarle el EVENTO que no es más que un String que lo identifica, la clase del objeto que recibe el Worker que intercepta ese evento y el propio Worker, que es la implementación de la interface Subscriber que define lo que haremos cada vez que se genere ese evento. public class StartAppWorker extends Subscriber<LifeCicleMessage> { public void onEvent(Event<LifeCicleMessage> event) throws Exception{ Para una implementación como la anterior, la invocación al método addEvent quedaría de la siguiente forma. kPWorkerCfg.addEvent(“MI_EVENTO”, LifeCicleMessage.class, new StartAppWorker()) 5.4.2 Workers La forma de trabajo está basada en Workers a traves de un modelo Pub/Subcribe a nivel de JVM. Estos Workers definen el siguiente ciclo de vida. Herramientas de Productividad SOFIA SDK Página 41/76 5.4.2.1 PrepareToReceived. @Component public class PrepareToReceived extends PrepareToReceivedWorkerImpl { @Override public SensorMessage reveivedDataSensor (LifeCicleMessage lifeCicleMessage) { /* * METODO QUE ES EJECUTADO DE FORMA CICLICA ES EL ENCARGADO DE LEER LA INFORMACION * SENSORICA HA DE DEVOLVER UN OBJETO SensorMessage CON LA INFORMACIN DE LOS * SONSORES. */ //TODO return new SensorMessage(); } } Este Worker es ejecutado de forma automática cuando arranca la APPModelo, en su interior ejecuta un Bucle que envía constantemente la información recogida en el método reveivedDataSensor al evento NEW_DATA_RECEIVED. Herramientas de Productividad SOFIA SDK Página 42/76 5.4.2.2 NewDataReceived. @Component public class NewDataReceived extends NewDataReceivedWorkerImpl { @Override public SSAPMessage generateSSAPMessage(SensorMessage sensorData) { /* * METODO QUE ES EJECUTADO CUANDO EL CAPTADOR DE DATOS SENSORICOS LOS NOTIFICA * ESTE METODO HA DE TRANFORMAR LOS DATOS CAPTADOS DE LOS SENSORES EN UN SSAP VALIDO * SI ESTE METODO DEVUELVE UN OBJETO DISTINTO DE NULO LO ENVIA AUTOMATICAMENTE A LA CLASE * DataToSend EL SESSION KEY NO ES NECESARIO PUES LA PLATAFORMA LO RELLENARA AUTOMATICAMENTE */ //TODO return new SSAPMessage(); } } Es el Worker que debemos Invocar cuando recibimos datos del sensor, su finalidad es convertir el SensorMessage que contiene la información que hemos recibido de los sensores en un SSAPMessage. Cuando retornamos el SSAPMessage la clase abstracta de la que extendemos se encargará de enviar el mensaje al siguiente Worker. public void onEvent(Event<SensorMessage> event) throws Exception{ SSAPMessage ssapMessage = generateSSAPMessage(event.getSource()); if (ssapMessage!=null){ kPWorkerCfg.publish(SensorEvents.DATA_TO_SEND.name(), ssapMessage); } } IMPORTANTE Para que los datos del sensor lleguen al Worker NewDataReceived, debemos notificarlo con el siguiente código. kPWorkerCfg.publish(SensorEvents.NEW_DATA_RECEIVED.name(), sensorMessage); El objeto kpWorkerCfg lo recuperaremos del contexto de Spring, la manera mas sencilla será con un Autowired en un Bean de Spring. @Autowired protected KPWorkerCfg kPWorkerCfg; 5.4.2.3 DataToSend. @Component public class DataToSend extends DataToSendWorkerImpl { @Override public SSAPMessage preProcessSSAPMessage(SSAPMessage requestMessage) { /* * METODO QUE ES EJECUTADO JUSTO ANTES DE ENVIAR EL SSAP CREADO EN NewDataReceived AL SIB * EL MENSAJE ENVIADO SERA EL QUE DEVUELVA ESTE METODO PARA ENVIAR EL MENSAJE GENERADO PREVIAMENTE * DEVOLVER EL OBJETO DE ENTRADA requestMessage SIN MODIFICAR */ //TODO return requestMessage; Herramientas de Productividad SOFIA SDK Página 43/76 } @Override public void postProcessSSAPMessage(SSAPMessage requestMessage, SSAPMessage responseMessage) { /* * METODO QUE ES EJECUTADO DESPUES DE ENVIAR EL SSAP AL SIB LOS PARAMETROS QUE SON EL requestMessage * MENSAJE ENVIADO Y EL requestMessage MENSAJE DE RESPUESTA DEL SIB */ //TODO } } Es el Worker encargado de enviar el SSAP al SIB, tiene dos métodos en los que implementar el código de pre preProcessSSAPMessage y post postProcessSSAPMessage procesamiento del envío. La clase abstracta de la que extendemos se encargará de enviar el mensaje al siguiente Worker. public void onEvent(Event<SSAPMessage> event) throws Exception{ SSAPMessage requestMessage = preProcessSSAPMessage(event.getSource()); SSAPMessage responseMessage = sib.send(requestMessage); if (requestMessage!=null){ postProcessSSAPMessage(requestMessage, responseMessage); } } 5.4.2.4 DataSendToSib. @Component public class DataSendToSib extends DataSendToSIBWorkerImpl { @Override public void sended(SibMessage message) { /* * METODO QUE ES EJECUTADO CUANDO SE HAN ENVIADO DATOS AL SIB DE FORMA CORRECTA SI EL MESAJE * ENVIADO ESTA EN LA BASE DE DATOS DE ERRORES LO BORRA DE ESTA */ //TODO } } Es el Worker notificado cuando un envío al SIB se ha procesado correctamente, El método que debemos implementar, recibe un SibMessage que tiene el mensaje enviado y la respuesta del SIB. La clase abstracta de la que extendemos se encargará de comprobar si el mensaje enviado está en la tabla de fallidos de la base de datos y en de ser así de borrarlo. public void onEvent(final Event<SibMessage> event) throws Exception{ try{ persistence.getTransactionManager().callInTransaction(new Callable<Void>() { public Void call() throws Exception { Table table = persistence.findById(event.getSource().getRequest().toJson()); if (table!=null){ persistence.delete(table); } return null; } }); }catch (Exception e){ e.printStackTrace(); } sended(event.getSource()); Herramientas de Productividad SOFIA SDK Página 44/76 } 5.4.2.5 Connection. @Override public void connected(SibMessage connected) { /* * METODO QUE ES EJECUTADO CUANDO SE HA REALIZADO UNA CONEXION CON EL SIB PREVIO A ESTE METODO * LA CLASE COMPRUEBA SI EXISTEN SSAP NO ENVIADOS O CON ERRORES EN LA BASE DE DATOS LOS VUELVE * A ENVIAR Y LOS BORRA DE LA BASE DE DATOS */ //TODO } Es el Worker notificado cuando se tiene conectividad con el SIB. La clase abstracta de la que extendemos se encargará de recuperar todos los mensajes que han fallado al ser enviados y volver a intentar su envío. public void onEvent(final Event<SibMessage> event) throws Exception{ try{ persistence.getTransactionManager().callInTransaction(new Callable<Void>() { public Void call() throws Exception { List<Table> tables = persistence.findAll(); for (Table table : tables){ kPWorkerCfg.publish(SensorEvents.DATA_TO_SEND.name(), SSAPMessage.fromJsonToSSAPMessage(table.getSsapMesssage())); } return null; } }); }catch (Exception e){ e.printStackTrace(); } connected(event.getSource()); } 5.4.2.6 NoConnection. @Component public class NoConnection extends NoConnectionToSIBWorkerImpl { @Override public void noConnected(ErrorMessage error) { /* * METODO QUE ES EJECUTADO CUANDO NO DE PUEDE CONECTAR CON EL SIB */ //TODO } } Es el Worker notificado cuando no se tiene Conectividad con el SIB, este evento provoca también ErrorSendToSib. 5.4.2.7 ErrorSendToSib. @Component public class ErrorSendToSib extends ErrorSendToSIBWorkerImpl { @Override public MonitoringMessage toMonitoring(ErrorMessage error) { /* * METODO QUE ES EJECUTADO CUANDO SE HAN ENVIADO DATOS AL SIB Y SE PRODUCE UN ERROR EN EL ENVIO * ESTE METODO TRANSFORMA EL MENSAJE ERRORMESSAGE error EN UN MENSAJE Herramientas de Productividad SOFIA SDK Página 45/76 MonitoringMessage QUE SERA * INTERCEPTADO POR LA CLASE Monitoring * * SI EL MENSAJE DEVUELTO ES DISTINTO DE NULL PUBLICA publish(InfraestructureEvents.MONITORING.name(), monitoringMessage); */ //TODO return new MonitoringMessage(error); } @Override public void onError(ErrorMessage error) { /* * METODO QUE ES EJECUTADO CUANDO SE HAN ENVIADO DATOS AL SIB Y SE PRODUCE UN ERROR EN EL ENVIO * ANTES DE EJECUTAR ESTE METODO DE FORMA AUTOMATICA SE ALMACENA EL SSAP ENVIADO EN LA BASE DE DATOS * CONFIGURADA PARA SU POSTERIOR REENVIO * * ESTE METODO PODRIA A PARTE DE ESCRIBIR EL LOG ADECUADO ELIMINAR DE LA BASE DE DATOS EL SSAP SI SE DETECTA * QUE LO HA PROVOCADO UN ERROR SEMANTICO O SINTACTICO */ //TODO } } Es el Worker notificado cuando no se produce un error en el envío de un SSAP al SIB, los métodos que debemos implementar reciben parámetros de tipo ErrorMessage. public class ErrorMessage extends Exception{ /** * */ private static final long serialVersionUID = -8835699096763715136L; private SSAPMessage ssapRequestMessage; private SSAPMessage ssapResponseMessage; private Throwable exception; Este tipo de objetos pueden contienen el mensaje enviado al SIB, la respuesta del SIB y la excepción originada. IMPORTANTE No todos los atributos tiene por que estar informados, si se produce un error antes de preparar el mensaje a enviar únicamente tendremos la excepción que lo ha originado. La clase abstracta de la que extendemos se encargará de almacenar el Mensaje SSAP en la base de datos para su posterior envío al SIB. public void onEvent(final Event<ErrorMessage> event) throws Exception{ persistence.getTransactionManager().callInTransaction(new Callable<Void>() { public Void call() throws Exception { Table table = new Table(event.getSource().getSsapRequestMessage().toJson()); persistence.create(table); return null; } }); onError(event.getSource()); MonitoringMessage monitoringMessage = toMonitoring(event.getSource()); if (monitoringMessage!=null){ kPWorkerCfg.publish(InfraestructureEvents.MONITORING.name(), Herramientas de Productividad SOFIA SDK Página 46/76 monitoringMessage); } } 5.4.2.8 StopApp. @Component public class StopApp extends StopAppWorkerImpl { @Autowired private KPWorkerCfg cfg; @Autowired private PropertyPlaceHolder property; @Override public void stopApp(LifeCicleMessage message) { /* * METODO QUE ES EJECUTADO CUANDO SE SOLICITA LA DETENCION DE LA APLICACION MODELO * EN ESTE METODO SE DEBERIA DE DETENER LA LECTURA SENSORICA Y REALIZAR LOS PROCESOS * ADECUADOS PARA DETENER DE FORMA SEGURA LA APLICACION */ //TODO } } Es un Worker notificado por el KPModelo cuando se solicita la detención de una aplicación, en el método que debemos implementar hemos de añadir las medidas necesarias para una detención inminente de la aplicación. La clase abstracta de la que heredamos se encarga de notificar al KPModelo que puede detener la aplicación. public void onEvent(final Event<ErrorMessage> event) throws Exception{ persistence.getTransactionManager().callInTransaction(new Callable<Void>() { public Void call() throws Exception { Table table = new Table(event.getSource().getSsapRequestMessage().toJson()); persistence.create(table); return null; } }); onError(event.getSource()); MonitoringMessage monitoringMessage = toMonitoring(event.getSource()); if (monitoringMessage!=null){ kPWorkerCfg.publish(InfraestructureEvents.MONITORING.name(), monitoringMessage); } } IMPORTANTE El resto de Clases pueden ser implementadas sin necesidad de heredar de la clase abstracta, haciendo uso de las interfaces, Para este Worker es obligatorio que se implemente una clase que herede de ErrorSendToSibWorkerImpl, en caso de no existir, se utiliza la implementación por defecto del KPModelo, que directamente notifica a este la posibilidad de detención de la aplicación. 5.4.1 Suscripción La suscripción a eventos del SIB notificados por el SIB se realiza a través de un encapsulamiento del mecanismo de Listener que proporciona el API Java de SOFIA2. Herramientas de Productividad SOFIA SDK Página 47/76 Los Subscriptores serán los encargados de atender las notificaciones de la subscripción a la que están asociados, para crear un subscriptor, debemos extender de la clase com.indra.sofia2.kpmodelo.infraestructure.subscription.Subscriber. @Component public class SubscriptionListener extends Subscriber { @Override @PostConstruct public void init() throws SubscriptionException { /* * METODO EN EL QUE ESTABLECEMOS QUE SUSCRIPCION ATENDERA ESTE LISTENER subscribe(ontology, query, SSAPQueryType); */ //TODO } @Override public void onEvent(SSAPMessage arg0) { /* * METODO QUE ES EJECUTADO CUANDO SE NOTIFICA LA INFORMACION A LA QUE NOS HEMOS * SUSCRITO EN EL INIT * DE LA CLASE */ //TODO } } Definiremos la clase como un bean de Spring anotándola con @Component y el método init lo anotaremos con @PostConstruct, con lo que nos aseguramos que se ejecutará cuando se registre el Bean. Al extender de la clase Subscriber tenemos disponibles los métodos. void subscribe(String ontology, String query, SSAPQueryType queryType) throws SubscriptionException void unsubscribe() throws SubscriptionException que nos permiten subscribirnos a una ontología y anular esa subscripción. Cuando el SIB notifique la información referente a nuestra subscripción el método void onEvent(SSAPMessage arg0) recibirá el mensaje SSAP enviado por el SIB y podremos manipularlo, adicionalmente podremos obtener la información relativa a la subscripción con el método. SubscriptionData getSubscriptionData() Que dispone de la siguiente estructura de información. private private private private String subscriptionId; String ontology; SSAPQueryType queryType; String query; Herramientas de Productividad SOFIA SDK Página 48/76 5.4.2 Monitorización JMX Al igual que pasa con la Suscripción, la monitorización a través de JMX implica el registro de los MBean en el servidor de MBeans, para ello la plataforma identifica todos los Beans que implementan la interface JmxSelfNaming y los registra como MBeans para poder explotar su información debemos anotar los Beans con las anotaciones de Mycila JMX que se encarga de registrar la información según nuestra parametrización en el servidor MBean. http://code.mycila.com/jmx/#documentation @JmxBean("com.company:type=MyService,name=main") public final class MyService { private String name; @JmxField private int internalField = 10; @JmxProperty public String getName() { return name; } public void setName(String name) { this.name = name; } @JmxMethod(parameters = {@JmxParam(value = "number", description = "put a big number please !")}) void increment(int n) { internalField += n; } } 5.4.3 Ejemplo de Uso El Comando >sofia2 crearAppModelo crea la estructura de aplicación AppModelo con la configuración básica y los Workers predefinidos para ser modificados por los desarrolladores. >sofia2 crearAppModelo --id appEjemplo --paquetebase com.ejemplo Que genera el proyecto directamente importable en eclipse. Herramientas de Productividad SOFIA SDK Página 49/76 IMPORTANTE En el fichero applicationContext.xml <bean id="properyPlaceHolder" class="com.indra.sofia2.kpmodelo.infraestructure.loader.PropertyPlaceHolder"> <constructor-arg value="APPEJEMPLO"/> </bean> En el bean properyPlaceHolder viene indicado el Identificador de la aplicación, este valor no puede ser modificado pues es el contrato entre el KPModelo y AppModelo. El nombre del WAR y el fichero de configuración han de ser denominados con el valor de este identificador. Recordar que el identificador de la aplicación es necesario en el fichero web.xml como propiedad context-param cuando se despliegan múltiples aplicaciones Herramientas de Productividad SOFIA SDK Página 50/76 6 ANEXOS 6.1 Un poco de Spring Spring es un Framework Open Source, creado con la finalidad de facilitar el desarrollo de aplicaciones empresariales. Spring es un contenedor ligero con soporte para aspectos y la inyección de dependencias. Las principales características de Spring son: Inyección de dependencias: Spring consigue un débil acoplamiento gracias a la inyección de dependencias (DI). El contenedor inyecta las dependencias durante la instanciación de los objetos que gestiona, de ésta forma, éstos no tienen que buscar las referencias de los otros objetos que usen, por lo que se reduce el acoplamiento, facilitando el mantenimiento y las pruebas. Orientación a aspectos: La AOP nos permite separar la lógica de negocio de los servicios de sistema transversales, tales como la auditoría, el logging y la gestión de transacciones. De esta manera, los objetos de aplicación únicamente contienen lógica de negocio, y mediante aspectos, definimos de manera externa los servicios transversales. Contenedor: Spring es un contenedor puesto que contiene, gestiona el ciclo de vida y gestiona las configuraciones de los objetos de aplicación. Permite declarar cómo será la instanciación de un objeto (singleton, un nuevo objeto por cada llamada, etc.), la configuración de los mismos (propiedades) así como la asociación existente entre los objetos. Framework: Spring es un Framework compuesto por diversos módulos permitiendo así la creación de aplicaciones empresariales. No reinventar la rueda: reutilizando librerías y productos existentes. Spring Framework está compuesto por un conjunto de 17 módulos: Herramientas de Productividad SOFIA SDK Página 51/76 Core Container Se encuentra compuesto por los módulos Core, Beans, Context y Expression Language. o o o Core y Beans: son las piezas fundamentales del Framework, ya que garantizan el IoC y la inyección de dependencias. Context: este módulo añade soporte para la internacionalización, propagación de eventos, carga de recursos estáticos y la creación transparente de contextos. Expression Language: permite acceder/modificar valores de propiedades, invocar métodos, acceder al contexto de los arrays, colecciones e indexadores, operadores lógicos y aritméticos… Data Access/Integration Se encuentra compuesto por los módulos JDBC, ORM, OXM, JMS y Transaction. o JDBC: facilita una capa de abstracción eliminando la necesidad de escribir y parsear códigos de error específicos por proveedor de base de datos. o ORM: Spring ofrece un amplio soporte para el trabajo con diferentes motores de persistencia (Hibernate, TopLink, IBatis) y también para trabajar con JPA permitiendo configurar estos contenedores y su transaccionalidad desde el contexto de Spring. o OXM: capa abstracta que facilita el mapeo entre objetos y XML usando JAXB, Castor, XMLBeans, JiBX y XStream. o JMS: éste módulo permite las funcionalidades de envío y recepción de mensajes de Queues y Topics. o Transaction: el módulo soporta la gestión de transacciones de manera tanto programática como declarativa (en base a configuración o anotaciones). Puede ser usado en combinación con ORM y con JDBC. Web: Ofrece funcionalidades web tales como la subida de archivos multiparte desde formularios, la inicialización del contenedor IoC usando servlet listeners… Contiene Herramientas de Productividad SOFIA SDK Página 52/76 también las partes relacionadas con el soporte de invocación remota de los servicios basados en web. AOP El módulo de Spring AOP ofrece una implementación de programación orientada a aspectos (AOP Alliance-compliant), también ofrece soporte para AspectJ. Aspects: El módulo de Spring Aspects, proporciona integración con AspectJ Instrumentation: da soporte al Api de JMX Test: Ofrece soporte para testar los componentes de Spring con JUnit y TestNG. También ofrece mock objects para poder probar el código de manera aislada. 6.1.1 Inyección de dependencias Spring es un contenedor que basa su modelo de programación en el Patrón Inversion of Control (Ioc), también llamado Inyección de Dependencias. Este patrón permite un menor acoplamiento entre componentes de una aplicación y fomenta así la reutilización de los mismos. Muchas veces, un componente tiene dependencias de servicios u otros componentes, cuyos tipos concretos son especificados en tiempo de diseño. La solución del Patrón delega la selección de una implementación concreta de las dependencias a un componente externo. Esto implica: El control de cómo un objeto A obtiene la referencia de un objeto B es invertido. El objeto A no es responsable de obtener una referencia al objeto B sino que es el Componente Externo el responsable de esto. Esta es la base del patrón IoC. Herramientas de Productividad SOFIA SDK Página 53/76 Una dependencia entre un componente y otro, puede establecerse estáticamente (en tiempo de compilación), o bien dinámicamente (en tiempo de ejecución). Es en éste último escenario es donde cabe el concepto de inyección, y para que esto fuera posible, debemos referenciar interfaces y no implementaciones directas. En general, las dependencias son expresadas en términos de interfaces en lugar de clases concretas. Esto permite un rápido reemplazo de las implementaciones dependientes sin modificar el código fuente de la clase. Lo que propone entonces la inyección de dependencias es no instanciar las dependencias explícitamente en su clase, sino expresarlas declarativamente en la definición de la clase. La esencia de la inyección de dependencias es contar con un componente capaz de obtener instancias válidas de las dependencias de un componente, e inyectárselas durante su creación o inicialización, para que éste pueda utilizarlas. 6.1.2 Ejemplos: 6.1.2.1 Ejemplo básico La configuración de Spring consiste en la definición de uno o más ficheros XML que serán administrados por el contenedor. En nuestro ejemplo, construiremos un objeto e inyectaremos dependencias sobre él usando Spring. El aspecto inicial del fichero de configuración “applicationContext.xml”, aún sin contenido, será el siguiente: Definición inicial de fichero applicationContext.xml <?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" Herramientas de Productividad SOFIA SDK Página 54/76 xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springfrmework.org/schema/beans/spring-beans3.0.xsd"> </beans> A continuación crearemos la clase del componente que queremos definir. En este caso se creará una clase llamada Connection, que será un JavaBean con tres atributos: user, password y connectionManager. La clase queda de la siguiente manera: Clase Connection package springtest; public class Connection { private String user; private String password; private ConnectionManager manager; public String getPassword() { return password; } public void setPassword(String password) { this.password = password; } public String getUser() { return user; } public void setUser(String user) { this.user = user; } public void setConnectionManager( ConnectionManager connectionManager) { this.connectionManager = manager; } public ConnectionManager getConnectionManager() { Herramientas de Productividad SOFIA SDK Página 55/76 return connectionManager; } } También debemos definir el administrador de conexiones al que hace referencia esta clase. Se trata de un JavaBean con dos atributos: host y port. Clase ConnectionManager package springtest; public class ConnectionManager { private String host; private int port; public String getHost() { return host; } public void setHost(String host) { this.host = host; } public int getPort() { return port; } public void setPort(int port) { this.port = port; } public String toString() { return "ConnectionManager [host=" + host + ", port=" + port + "]"; } } Herramientas de Productividad SOFIA SDK Página 56/76 Ahora que ya disponemos de las clases, completaremos el fichero de configuración declarándolas como componentes, y definiendo sus dependencias. Lo primero que hay que hacer es definir nuestros objetos como beans: Definición de fichero applicationContext.xml <?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springfrmework.org/schema/beans/spring-beans-3.0.xsd"> <bean id="connection" class="springtest.Connection"> </bean> <bean id="manager" class="springtest.ConnectionManager"> </bean> </beans> Para ello definimos en el fichero de configuración dos elementos <bean>, indicando los atributos id y class. El atributo id servirá para definir el nombre del componente, el cual deberá ser único. El atributo class indica el nombre cualificado de la clase A continuación definiremos el valor de las propiedades de cada componente, creándose de esta forma la estructura de dependencias necesaria para nuestro ejemplo. Definición de fichero applicationContext.xml <?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springfrmework.org/schema/beans/spring-beans-3.0.xsd"> <bean id="connection" class="springtest.Connection"> <property name="user" value="Test"/> <property name="password" value="password123"/> <property name="connectionManager" ref="manager"/> Herramientas de Productividad SOFIA SDK Página 57/76 </bean> <bean id="manager" class="springtest.ConnectionManager"> <property name="host" value="localhost"/> <property name="port" value="8080"/> </bean> </beans> Mediante el uso de elementos <property>, definimos el valor de las propiedades de cada uno de nuestros componentes. El atributo name indica el nombre de la propiedad. El atributo value indica el valor de la propiedad. El atributo ref indica una referencia a otro componente, mediante el nombre de éste. Una vez definido el fichero de configuración, se debe utilizar una implementación de contenedor Spring para crear la estructura de componentes. Existen varias implementaciones disponibles. Una de ellas es org.springframework.beans.factory.BeanFactory, que proporciona un mecanismo de configuración avanzada capaz de manejar cualquier tipo de objeto. Otra posibilidad es org.springframework.context.ApplicationContext, la cual agrega una integración más fácil con algunas de las características más interesantes de Spring, como su módulo de Programación Orientada a Aspectos, manejo de recursos de mensajes (para la internacionalización), publicación de eventos, y contextos específicos para ciertas capas de aplicaciones (como aplicaciones web), entre otras. En este ejemplo usaremos ClassPathXmlApplicationContext. En este caso, el constructor recibe una cadena (o cadenas) indicando la ubicación de este archivo (o archivos): Inicialización de contenedor Spring ApplicationContext context = new ClassPathXmlApplicationContext("/springtest/applicationContext.xml"); Con este ApplicationContext creado, y que representa el contenedor de componentes, ahora podemos recuperar el componente que necesitemos, usando el método getBean: Obtención de beans del contenedor Connection connection = context.getBean(Connection.class); Herramientas de Productividad SOFIA SDK Página 58/76 Y teniendo la instancia de Connection, proporcionada por el contenedor, se puede hacer uso normal de sus métodos: Utilización de beans obtenidos del contenedor System.out.println("El nombre de usuario es: " + connection.getUser()); A continuación se muestra la clase que instancia el contenedor Spring y que muestra los datos en consola para comprobar que efectivamente nuestro objeto ha sido inyectado con sus dependencias: Ejemplo completo package springtest; import org.springframework.context.ApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext; public class Main { public static void main(String[] args) { // crea el contenedor ApplicationContext context = new ClassPathXmlApplicationContext( "/springtest/applicationContext.xml"); // obtiene el bean con sus dependecias ya inyectadas Connection connection = context.getBean(Connection.class); // muestra en consola los datos System.out.println("El nombre de usuario es : " + connection.getUser()); System.out.println("El password es: " + connection.getPassword()); System.out.println("El manager es: " + connection.getConnectionManager()); } } Herramientas de Productividad SOFIA SDK Página 59/76 Ahora si ejecutamos el proyecto podremos ver en consola los resultados: Resultado de ejecución El nombre de usuario es: Test El password es: password123 El manager es: ConnectionManager[host=localhost, port=8080] 6.1.2.2 Ejemplo basado en anotaciones Spring también permite definir componentes y dependencias mediante el uso de anotaciones. En el próximo ejemplo definiremos un componente que realizará la función de observable, así como otros dos componentes que actuarán como observadores. En primer lugar, debemos habilitar el uso de anotaciones en el fichero XML de definición de contexto, applicationContext.xml: Definición de fichero applicationContext.xml <?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:context="http://www.springframework.org/schema/context" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springfrmework.org/schema/beans/spring-beans-3.0.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.0.xsd"> <context:component-scan base-package="springtest"/> </beans> Mediante el elemento <context:component-scan> indicaremos a Spring que, en tiempo de creación del contenedor, busque componentes escaneando las clases del classpath de la aplicación, que estén ubicadas bajo el paquete indicado en el atributo base-package. La manera en que Spring identifica que debe instanciar un componente es mediante la anotación @Component en la clase Java que implementa éste. Herramientas de Productividad SOFIA SDK Página 60/76 De esta forma, no tendremos que definir nuestros componentes en el fichero applicationContext.xml, aunque si queremos hacerlo, también es posible combinar las dos formas de definir componentes. Una vez habilitada la definición de componentes mediante anotaciones, vamos a definir el código de las clases que necesitamos para continuar con nuestro ejemplo. En primer lugar definiremos una clase que modelará un Evento, que el Observador enviará a cada uno de los Observables: Evento.java package springtest; import java.util.Date; public class Evento { public Date fecha; public Object datos; public Evento(Object datos) { this.fecha = new Date(); this.datos = datos; } public Date getFecha() { return fecha; } public Object getDatos() { return datos; } @Override public String toString() { return "Evento [fecha=" + fecha + ", datos=" + datos + "]"; } } A continuación crearemos un interfaz que nos permitirá definir distintos Observadores: Observador.java package springtest; public interface Observador { public void notificar(Evento e); Herramientas de Productividad SOFIA SDK Página 61/76 } El paso siguiente será definir el Observable: Observable.java package springtest; import java.util.List; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; @Component public class Observable { @Autowired private List<Observador> observadores; public void notificar(Evento e) { if ( observadores == null ) return; for ( Observador observador : observadores ) { observador.notificar(e); } } } Observamos que la clase está decorada con la anotación @Component. Cuando se inicie nuestro contenedor Spring, se creará una instancia de esta clase y se definirá como componente. El identificador de este componente será observable (generado automáticamente a partir del nombre de la clase), aunque también podemos definir el identificador especificándolo en la anotación. Como podemos observar, la clase dispone de un atributo decorado con @Autowired, y ya no dispone de métodos get y set, puesto que trabajando con anotaciones ya no son imprescindibles. La anotación @Autowired sirve para indicar a Spring que el valor del atributo es una dependencia que Spring deberá resolver automáticamente. En nuestro caso, el atributo es una lista de Observadores, por lo que Spring deberá buscar todos los componentes que implementen este interfaz, insertarlos en una lista, e inyectar dicha lista sobre el atributo. El método notificar recibirá un evento que será entregado a todos y cada uno de los observadores que se encuentren en la lista. Herramientas de Productividad SOFIA SDK Página 62/76 Cuando Spring resuelve dependencias automáticamente, puede seguir varias estrategias. Una de ellas consiste en buscar componentes cuyo nombre sea igual al nombre del atributo decorado con @Autowired. Otra de ellas es buscar componentes cuyo tipo de datos sea asignable al tipo de datos del atributo. Si no se encuentra ningún componente para una dependencia, Spring lanzará una excepción. A continuación definiremos dos implementaciones de Observador: ObservadorSystemOut y ObservadorSystemErr. El primero de ellos escribirá el evento recibido en la salida estándar. El segundo, lo escribirá en la salida de error. ObservadorSystemOut.java package springtest; import org.springframework.stereotype.Component; @Component public class ObservadorSystemOut implements Observador { @Override public void notificar(Evento e) { System.out.println(e); } } ObservadorSystemErr.java package springtest; import org.springframework.stereotype.Component; @Component public class ObservadorSystemErr implements Observador { @Override public void notificar(Evento e) { System.err.println(e); } } Ambas clases están decoradas con la anotación @Component, con lo que se definirá un componente de cada una de las dos clases en tiempo de arranque del contenedor. Herramientas de Productividad SOFIA SDK Página 63/76 Finalmente ya solo restaría arrancar el contenedor, obtener el componente observable e invocar su método notificar, entregando como argumento un determinado evento. Al hacerlo, se escribirá tanto por la salida estándar como por la salida de error el resultado de invocar el método toString del evento entregado. 6.1.3 Ámbito de un Bean Por defecto, los beans definidos en Spring son Singleton, es decir que siempre devuelven la misma instancia del bean. Sin embargo, Spring define otros posibles ámbitos: Ámbito Qué hace Singleton Extiende la definición del bean a una única instancia por contexto de Spring Prototype Permite a un bean ser instanciado un número de veces N (una por uso) request Extiende la definición del bean a un http request (sólo válido para contexto web de Spring como Spring MVC) Session Extiende la definición de un bean a una sesión HTTP (sólo válido para contexto web de Spring como Spring MVC) Global-session Extiende la definición de un bean a una sesión HTTP global (sólo válido cuando se usa un contexto de portlets). Para especificar el ámbito en la definición del componente mediante fichero XML, se ha de utilizar el atributo scope: Definición del ámbito de un componente en XML <bean id="miBean1" class="com.bbva.arqspring.ejemplos.MiBean1" scope="prototype"/> Para especificar el ámbito en la definición del componente mediante anotaciones, se ha de utilizar la anotación @Scope. El ámbito singleton es el ámbito por defecto, de modo que si no se indica esta anotación, nuestro componente será un singleton: Herramientas de Productividad SOFIA SDK Página 64/76 Definición del ámbito de un componente mediante anotaciones @Component @Scope("session") public class Componente { } 6.1.4 Aplicando aspectos La Inyección de dependencias permite descoplar el interfaz de la implementación. La Programación orientada a aspectos se define a menudo como una técnica de programación que promueve la separación de conceptos dentro de un sistema de software. Los sistemas están compuestos de varios componentes, cada uno responsable de una parte específica de la funcionalidad. A menudo estos componentes también tienen la responsabilidad adicional más allá de su núcleo funcionalidad. Sistema de servicios transversales tales como el logging, la gestión de transacciones y la seguridad a menudo están distribuidos por los componentes. AOP permite modularizar estos servicios y declarar a que componentes que afectar. En Spring se define de esta forma: En la configuración Spring añado el soporte: Declaro en la clase que es una Aspecto con: Herramientas de Productividad SOFIA SDK Página 65/76 Declaro un PointCut: es decir qué métodos voy a interceptar Un Aspecto interesante podría ser este, que detectaría en qué capa estamos: 6.1.5 Ecosistema/Portfolio Spring Spring ofrece un completo ecosistema de soluciones. Este portfolio abarca varias frameworks y bibliotecas que se basan en el núcleo de Spring Framework. En conjunto, el ecosistema de Spring constituye un marco para casi todas las facetas del desarrollo Java. Spring Web Flow: permite modelar flujos de navegación en aplicaciones Web. Herramientas de Productividad SOFIA SDK Página 66/76 Spring Security: es un proveedor de seguridad que permite la autenticación y la autorización de los usuarios, es independiente del Servidor de aplicaciones, fácilmente ampliable y con soporte para CAS, OpenID, certificados… Spring Batch: es el framework para procesamiento de lotes, provee servicios comunes para los programas Batch. Spring Integration: es una extensión para soportar los Patrones de Integración Corporativos Permite integrar con sistemas externos a través de adaptadores. Spring Social: facilita el desarrollo de aplicaciones con soporte para las redes sociales, con Twitter y Facebook, utilizando OAuth (con Spring Security Oauth). Spring Mobile: es una extensión de Spring MVC, para desarrollar aplicaciones web para móviles. Spring AMQP, Spring Data, Spring GemFire, Spring .Net,… 6.1.6 Enlaces de referencia Sitio oficial: http://www.springsource.org/ Documentación de Referencia: http://www.springsource.org/documentation 6.2 Un poco de Maven Maven es básicamente una herramienta para la gestión y construcción de proyectos y puede verse como la evolución de Ant. 6.2.1 Conceptos básicos Un artefacto es un componente que se puede incluir en un proyecto como dependencia, pero también se le llama por el mismo nombre al componente generado por esta herramienta (jar, war, ear). Un grupo es un conjunto de artefactos. Los grupos se usan para organizar los diferentes artefactos disponibles. El scope indica el alcance de la dependencia, y puede ser de los siguientes tipos: o Compile: La dependencia es necesaria a la hora de compilar y ésta se propaga a los proyectos dependientes. Esta opción es la que viene por defecto si no se especifica el scope. o Provided: Esta dependencia no es transitiva y es necesaria para compilar, pero en este caso se espera que en el proyecto ya esté el JDK o el contenedor que provea la dependencia. Esta opción es aconsejable en proyectos en los que se usen muchas librerías. o Runtime: La dependencia no es necesaria a la hora de compilar, pero sí lo es en tiempo de ejecución. o Test: La dependencia no es necesaria para el uso normal de la aplicación, pero si lo es para las fases de compilación y ejecución de los tests. o System: Este scope es similar al de provided, excepto porque se tiene que proveer el contenedor que contenga explícitamente. El artefacto siempre está disponible y no se busca en el repositorio. Un archetype (arquetipo) es una plantilla que es necesaria especificar cuando se crea un proyecto. Este arquetipo crea la estructura del proyecto. Herramientas de Productividad SOFIA SDK Página 67/76 6.2.2 Ciclo de vida Las partes del ciclo de vida principal del proyecto Maven son: compile test package install deploy La idea es que, para cualquier meta, todas las metas anteriores han sido completadas satisfactoriamente Por ejemplo, cuando se ejecuta mvn install Maven verificará si mvn package ha sido ejecutado exitosamente (el archivo jar existe en target/), en cuyo caso no será ejecutado otra vez. También existen algunas metas que están fuera del ciclo de vida que pueden ser llamadas, pero Maven asume que estas metas no son parte del ciclo de vida por defecto (no tienen que ser siempre realizadas). Estas metas son: 1. clean 2. assembly:assembly 3. site 4. site-deploy 5. etc. Pero estas metas pueden ser añadidas al ciclo de vida a través del Project Object Model (POM). 6.2.3 Características El modelo de objetos del proyecto POM (pom.xml) es la base de cómo Maven trabaja. El desarrollo y gestión del modelo está controlado desde el modelo del proyecto. Herramientas de Productividad SOFIA SDK Página 68/76 En el pom.xml se define toda la metainformación necesaria para el proyecto (nombre del artefacto a generar, rutas fuentes, JDK de compilación, dependencias,…): o Nombre y versión de artefacto a generar o Dependencias: o Versión JDK compilación: Maven tiene un funcionamiento basado en Repositorios. Los Repositorios son Servidores (en general Servidores Web) en los que están accesibles las diferentes versiones de las librerías organizadas jerárquicamente. Ejemplo: Maven contempla 2 tipos de Repositorios: Herramientas de Productividad SOFIA SDK Página 69/76 o Remotos: existen repositorios públicos de Maven (como http://maven.springframework.org/release) que contienen las diferentes versiones de las librerías. o Local: contiene una copia de la parte del Repositorio remoto necesaria para trabajar con las aplicaciones en una estructura local (en la Arquitectura en w:\SOFTWARE\Maven\M2_REPO\) Los Repositorios remotos que se usan en una aplicación se definen en el pom.xml del proyecto. A la hora de realizar una tarea sobre un proyecto (compilar, ejecutar tests,…) se busca en estos repositorios de forma secuencial. Al realizar una tarea de Maven (mvn install por ejemplo) se descargan al repositorio local las dependencias necesarias desde los repositorios remotos para funcionar. Ejemplo: Estructura estándar de todos los proyectos, por ejemplo, para una estructura de un módulo WAR, la configuración sería: Está listo para ser utilizado en red. Uso de repositorios centrales de librerías, utilizando un mecanismo que permite descargar automáticamente aquellas necesarias en el proyecto, lo que permite a los usuarios de Maven reutilizar librerías entre proyectos y facilita la comunicación entre proyectos para asegurar que la compatibilidad entre distintas versiones es correctamente tratada. Maven provee soporte no sólo para obtener archivos de su repositorio, sino también para subir artefactos al repositorio al final de la construcción de la aplicación, dejándola Herramientas de Productividad SOFIA SDK Página 70/76 al acceso de todos los usuarios. Una caché local de artefactos actúa como la primera fuente para sincronizar la salida de los proyectos a un sistema local. Maven está construido usando una arquitectura basada en plugins que permite que utilice cualquier aplicación controlable a través de la entrada estándar. En teoría, esto podría permitir a cualquiera escribir plugins para su interfaz con herramientas como compiladores, herramientas de pruebas unitarias, etcétera, para cualquier otro lenguaje 6.2.4 Ejemplo de uso Vamos a crear un proyecto Java, compilarlo, chequearlo, empaquetarlo y subirlo al repositorio. Dicho proyecto va a ser creado con el siguiente arquetipo en la línea de comandos: mvn archetype:generate DarchetypeGroupId=org.apache.maven.archetypes DgroupId=com.mycompany.app -DartifactId=my-app Éste es un comando interactivo, por lo que Maven nos pedirá cierta información antes de crear la estructura del proyecto. A continuación se muestran las trazas generadas: ... Choose a number: 112: 112 Choose version: 1: 1.0-alpha-1 2: 1.0-alpha-2 3: 1.0-alpha-3 4: 1.0-alpha-4 5: 1.0 6: 1.1 Choose a number: 6: 6 [INFO] Using property: groupId = com.mycompany.app [INFO] Using property: artifactId = my-app Define value for property 'version': 1.0-SNAPSHOT: [INFO] Using property: package = com.mycompany.app Confirm properties configuration: groupId: com.mycompany.app artifactId: my-app version: 1.0-SNAPSHOT package: com.mycompany.app Y: Y [INFO] ------------------------------------------------------------------------[INFO] Using following parameters for creating project from Old (1.x) Archetype: maven-archetype-quickstart:1.1 [INFO] ------------------------------------------------------------------------[INFO] Parameter: groupId, Value: com.mycompany.app [INFO] Parameter: packageName, Value: com.mycompany.app [INFO] Parameter: package, Value: com.mycompany.app [INFO] Parameter: artifactId, Value: my-app [INFO] Parameter: basedir, Value: W:\DESARROLLO\prueba [INFO] Parameter: version, Value: 1.0-SNAPSHOT [INFO] ********************* End of debug info from resources from generated POM *********************** [INFO] project created from Old (1.x) Archetype in dir: W:\DESARROLLO\MavenDemo\my-app [INFO] -----------------------------------------------------------------------[INFO] BUILD SUCCESSFUL [INFO] -----------------------------------------------------------------------[INFO] Total time: 1 minute 46 seconds Herramientas de Productividad SOFIA SDK Página 71/76 [INFO] Finished at: Wed Jul 27 10:33:36 CEST 2011 [INFO] Final Memory: 11M/28M [INFO] ------------------------------------------------------------------------ Maven habrá creado un directorio my-app, y este directorio contendrá un fichero pom.xml: pom.xml <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <groupId>com.mycompany.app</groupId> <artifactId>my-app</artifactId> <version>1.0-SNAPSHOT</version> <packaging>jar</packaging> <name>my-app</name> <url>http://maven.apache.org</url> <properties> <project.build.sourceEncoding>UTF8</project.build.sourceEncoding> </properties> <dependencies> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>3.8.1</version> <scope>test</scope> </dependency> </dependencies> </project> La estructura de ficheros generada será la siguiente: my-app |-- pom.xml `-- src |-- main | `-- java | `-- com | `-- mycompany | `-- app | `-- App.java `-- test `-- java `-- com `-- mycompany `-- app `-- AppTest.java Herramientas de Productividad SOFIA SDK Página 72/76 En la cual se nos habrán generado dos clases: Una principal: App.java Una de prueba de JUnit: AppTest.java Una vez que se hayan modificado las clases (en este caso maven ha creado un ejemplo básico, por lo que no es necesario modificar el código), se pueden compilar y ejecutar las pruebas con el siguiente comando desde el directorio my-app que ha creado: mvn test-compile Y posteriormente empaquetarlo en un jar y subirlo al repositorio: mvn package mvn install El comando “mvn install” instala el artefacto creado en el repositorio local de la máquina. El comando copia tanto el jar como el pom a nuestro repositorio. Al instalar el artefacto en el repositorio local, este proyecto está disponible para incluirlo como dependencia en otro proyecto maven, mejorando así la portabilidad y modularidad de los proyectos creados. 6.2.5 Enlaces de referencia: 6.3 Sitio oficial: http://maven.apache.org Tutorial en Castellano: http://login.osirislms.com/offline/maven Tutorial en castellano: http://www.juntadeandalucia.es/xwiki/bin/view/MADEJA/Maven2 Un poco de JUnit JUnit es un Framework de pruebas que usa anotaciones para identificar los métodos que contienen pruebas. Para escribir un test con JUnit deberemos anotar un método con la anotación @Test y deberemos usar un método proporcionado por JUnit para poder evaluar si el funcionamiento de cada uno de los métodos de la clase se comporta como se espera. En función de algún valor de entrada se evalúa el valor de retorno esperado; si la clase cumple con la especificación, entonces JUnit devolverá que el método de la clase pasó exitosamente la prueba; en caso de que el valor esperado sea diferente Herramientas de Productividad SOFIA SDK Página 73/76 al que regresó el método durante la ejecución, JUnit devolverá un fallo en el método correspondiente. Permite la ejecución de dos o más test que pueden ejecutarse sobre el mismo o similar conjunto de datos. Permita la ejecución de test y recolectar sus resultados. JUnit es también un medio de controlar las pruebas de regresión, necesarias cuando una parte del código ha sido modificado y se desea ver que el nuevo código cumple con los requerimientos anteriores y que no se ha alterado su funcionalidad después de la nueva modificación. Las siguientes anotaciones están presentes en JUnit 4.x: Anotacion Descripción @Test public void method() La annotation @Test identifica que este método es un método de prueba. @Before public void method() Ejecutara el método antes de cada prueba. @After public void method() Ejecutara el método después de cada prueba @BeforeClass public void method() Ejecutará el método antes del comienzo de todas las pruebas @AfterClass public void method() Ejecutará el método después de haber ejecutado todas las pruebas. @Ignore Se ignorará este método en la ejecución de las pruebas @Test(expected=IllegalArgumentException.class) Comprueba si el método lanza la excepción especificada. @Test(timeout=100) Falla si el método tarda más de los milisegundos indicados. Aquí pasamos a resumir algunos métodos de pruebas: Anotacion Herramientas de Productividad SOFIA SDK Descripción Página 74/76 fail(String) La comprobación falla con el texto de salida indicado. assertTrue(true); True assertsEquals([String message], expected, Comprueba si los valores son los mismos. actual) assertsEquals([String message], expected, Mismo que el anterior pero para los tipos float actual, tolerance) y double. assertNull([message], object) Comprueba si el objeto es null. assertNotNull([message], object) Comprueba si el objeto no es null. assertSame([String], expected, actual) Comprueba si ambas variables se refieren al mismo objeto. assertNotSame([String], expected, actual) Comprueba si ambas variables no se refieren al mismo objeto. assertTrue([message], boolean condition) Comprueba si la condición es verdadera. Las herramientas de desarrollo como Eclipse cuentan con plug-ins que permiten ejecutar de forma gráfica test unitarios. 6.3.1 Ejemplo Suponiendo que nos hemos creado una clase que contiene un método que se encarga de multiplicar dos números: package com.bbva.ejemplos; public class MyClass { public int multiply(int x, int y) { return x / y; } } Para comprobar que realmente el método multiplicador realiza correctamente la operación, vamos a ejecutar una prueba, en la cual vamos a multiplicar 10 x 5 y vamos a evaluar que el resultado sea 50. Dicha clase de prueba con JUnit sería la siguiente: Herramientas de Productividad SOFIA SDK Página 75/76 package com.bbva.ejemplos; import org.junit.Test; import static org.junit.Assert.assertEquals; public class MyClassTest { @Test public void testMultiply() { MyClass tester = new MyClass(); assertEquals("Result", 50, tester.multiply(10, 5)); } } Alrededor de JUnit han surgido una serie de librerías especializadas en un tipo de Test especiales: DBUnit: permite realizar tests sobre bases de datos, insertando datos antes de la ejecución de los tests y comprobando los datos tras ella. También permite importar y exportar los datos desde y hacia ficheros xml o xls (Excel). JMock: Librería que permite realizar tests utilizando objetos simulados (mock objects) dinámicos. El objetivo es aislar los objetos que se testean sustituyendo los objetos con los que se relacionan por objetos simulados en los que podemos definir su estado y los resultados de los métodos. 6.3.2 Enlaces de referencia Sitio oficial: http://www.junit.org/ Tutorial en Ingles: http://www.vogella.de/articles/JUnit/article.html Libro: http://java.sun.com/developer/Books/javaprogramming/ant/ant_chap04.pdf Sitio oficial DBUnit: http://www.dbunit.org/ Tutorial http://www.oreillynet.com/onjava/blog/2005/10/dbunit_made_easy.html Sitio oficial JMock: http://www.jmock.org/ Tutorial JMock: http://www.jmock.org/cookbook.html Herramientas de Productividad SOFIA SDK Página 76/76 DBUnit: