curso desarrollo documentation · el descriptor de la parte servidora es meta-inf/web-fragment.xml...

73
curso_desarrollo Documentation Release 1 Fernando González Jul 31, 2017 Contents 1 Publicación de versiones 1 2 Prerrequisitos 2 3 JQuery 2 4 OpenLayers 2 5 RequireJS 2 6 Arquitectura 2 6.1 Estructura del repositorio ......................................... 3 6.2 error-management ............................................. 9 6.3 context-attributes ............................................. 9 6.4 request-attributes ............................................. 9 6.5 RequireJS ................................................. 9 6.6 Patrón de diseño message-bus .................................... 13 6.7 Arquitectura servidor ........................................... 15 7 Configuración de los plugins 17 7.1 Modificación de la configuración en tiempo de ejecución ........................ 17 7.2 Modificación de la configuración por programación ........................... 17 8 Módulos importantes 18 8.1 Message-bus ............................................... 18 9 Referencia mensajes 18 9.1 modules-loaded .............................................. 18 9.2 before-adding-layers ........................................... 19 9.3 layers-loaded ............................................... 19 9.4 reset-layers ................................................ 19 9.5 ajax .................................................... 19 9.6 error .................................................... 20 9.7 info-features ................................................ 20 9.8 zoom-in .................................................. 21 9.9 zoom-out ................................................. 21 9.10 zoom-to .................................................. 21 9.11 initial-zoom ................................................ 21

Upload: others

Post on 02-Jun-2020

10 views

Category:

Documents


0 download

TRANSCRIPT

Page 1: curso desarrollo Documentation · El descriptor de la parte servidora es META-INF/web-fragment.xml y se encuentra en src/main/ resources. Sigue el estándar Servlet3 de Java y contiene

curso_desarrollo DocumentationRelease 1

Fernando González

Jul 31, 2017

Contents

1 Publicación de versiones 1

2 Prerrequisitos 2

3 JQuery 2

4 OpenLayers 2

5 RequireJS 2

6 Arquitectura 26.1 Estructura del repositorio . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 36.2 error-management . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 96.3 context-attributes . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 96.4 request-attributes . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 96.5 RequireJS . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 96.6 Patrón de diseño message-bus . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 136.7 Arquitectura servidor . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 15

7 Configuración de los plugins 177.1 Modificación de la configuración en tiempo de ejecución . . . . . . . . . . . . . . . . . . . . . . . . 177.2 Modificación de la configuración por programación . . . . . . . . . . . . . . . . . . . . . . . . . . . 17

8 Módulos importantes 188.1 Message-bus . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 18

9 Referencia mensajes 189.1 modules-loaded . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 189.2 before-adding-layers . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 199.3 layers-loaded . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 199.4 reset-layers . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 199.5 ajax . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 199.6 error . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 209.7 info-features . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 209.8 zoom-in . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 219.9 zoom-out . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 219.10 zoom-to . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 219.11 initial-zoom . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 21

Page 2: curso desarrollo Documentation · El descriptor de la parte servidora es META-INF/web-fragment.xml y se encuentra en src/main/ resources. Sigue el estándar Servlet3 de Java y contiene

9.12 set-default-exclusive-control . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 229.13 activate-default-exclusive-control . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 229.14 activate-exclusive-control . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 229.15 highlight-feature . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 229.16 clear-highlighted-features . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 239.17 add-group . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 239.18 add-layer . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 239.19 layer-visibility . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 249.20 time-slider.selection . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 249.21 layer-time-slider.selection . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 259.22 layer-timestamp-selected . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 259.23 toggle-legend . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 259.24 register-layer-action . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 259.25 register-group-action . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 269.26 show-layer-panel . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 269.27 show-info . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 269.28 show-layer-info . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 279.29 show-group-info . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 279.30 show-wait-mask . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 279.31 hide-wait-mask . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 279.32 activate-feedback . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 289.33 deactivate-feedback . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 28

10 Temas avanzados 2810.1 Secuencia de inicio de la aplicación . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 28

11 Compilación del proyecto 2811.1 Obtención de los fuentes . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 2811.2 Instalación de los fuentes en eclipse . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 2911.3 Ejecución del portal en tomcat desde Eclipse . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 3011.4 Generación del unredd-portal.war . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 3111.5 Generación del unredd-portal.war desde eclipse . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 3111.6 Particularidades de la construcción del proyecto . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 32

12 Ejecución de los tests de integración 3312.1 Servicio de base de datos . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 3312.2 Configuración del plugin Feedback . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 3312.3 Ejecución de los tests . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 34

13 Manuales 3413.1 Hello World . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 3413.2 Añadir elementos a la interfaz . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 3413.3 Añadir una botonera . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 3513.4 Manejo de eventos . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 3613.5 Posición del mapa . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 3713.6 Lectura de parámetros URL . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 3813.7 Servicio hola mundo . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 3813.8 Servicio configuración (1 de 2) . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 4013.9 Servicio configuración (2 de 2) . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 4213.10Conexión a base de datos . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 4613.11Cómo crear un nuevo plugin . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 5113.12Cómo crear una nueva aplicación . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 5613.13Cómo dar licencia libre a un plugin . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 6413.14Publicación de un plugin en un repositorio Maven . . . . . . . . . . . . . . . . . . . . . . . . . . . . 70

Page 3: curso desarrollo Documentation · El descriptor de la parte servidora es META-INF/web-fragment.xml y se encuentra en src/main/ resources. Sigue el estándar Servlet3 de Java y contiene

Contents:

Publicación de versiones

Este proceso se lanzará cada tres meses.

1. Avisar un par de semanas antes de la publicación. Aunque la fecha esté acordada es una buena práctica avisar.

2. Creación de la rama para la versión en el repositorio:

git checkout -b release-<VERSION>mvn versions:setmvn versions:commitgit commit -am "Bumped version numbers"

Donde la versión tiene la forma x.y.z-SNAPSHOT:

• x se incrementa si se rompe la API o el formato del directorio de configuración

• y se incrementa cuando se incorporan nuevas funcionalidades

• x se incrementa para parches menores

La rama se sacará de la última revisión “estable” en la rama principal.

3. Añadimos lista de mejoras a un fichero CHANGELOG. Esto se hace repasando commits desde una fecha deter-minada, poniendo especial cuidado en inventariar los cambios de API si es una versión mayor.

4. Cuando la rama está lista se elimina el SNAPSHOT de los pom:

mvn versions:setmvn versions:commitgit commit -am "Bumped version numbers"

Se genera un .war y se sube al redmine.

En la rama principal se deberá actualizar el número de versión también. De la forma habitual:

mvn versions:setmvn versions:commitgit commit -am "Bumped version numbers"

5. Se anuncia en la lista incluyendo el changelog y un link al nuevo war.

Prerrequisitos

La documentación tecnica parte de la base de que hay conocimiento sobre:

• maquetación web con HTML y CSS

• los lenguajes Java y Javascript

• desarrollo de Java Servlets

• jQuery básico

Page 4: curso desarrollo Documentation · El descriptor de la parte servidora es META-INF/web-fragment.xml y se encuentra en src/main/ resources. Sigue el estándar Servlet3 de Java y contiene

• OpenLayers

JQuery

JQuery se puede definir como una librería de utilidad que nos ayuda a realizar las siguientes tareas:

• Manipulación del modelo de objetos del documento HTML

• Communicación AJAX con el servidor

• Communicación entre módulos mediante eventos

Ejercicios

OpenLayers

OpenLayers es una librería que permite la visualizacion e interacción con mapas en navegadores web.

Ejercicios

RequireJS

RequireJS es un cargador de módulos asíncrono. En el contexto del portal se utiliza para cargar los distintos módulosque componen el mismo.

Ejercicios

Arquitectura

El portal es una aplicación cliente/servidor, en la que el cliente está programado mediante módulos Javascript que re-alizan distintas operaciones. Para realizar estas operaciones, el cliente accede a distintos servicios realizando llamadasasíncronas a servicios que devuelven documentos XML, JSON, etc. con la información necesaria.

El lado del servidor se basa en la especificación Servlets 3 de Java. A través de dicha especificación se puedenimplementar los servicios que devuelven los documentos XML, JSON, etc. que permitirán a los módulos Javascriptdesarrollar su funcionalidad.

Warning: Aunque la parte servidora se implementa en Java, desde el punto de vista técnico no es estrictamentenecesario utilizar Java para implementar los servicios que nos interesen. Los módulos Javascript consumen losservicios a través del protocolo HTTP, que es transparente a los detalles de implementación de los servicios con-sumidos. En efecto, es posible desplegar una aplicación con la parte cliente accediendo a un conjunto de serviciosen PHP, por ejemplo.

Sin embargo, aunque la parte cliente acceda a servicios implementados en otro idioma, el sistema de plugins estáimplementado en Java, como veremos a continuación. Por lo que Java sigue siendo un requisito en el sistemadonde se despliegue la aplicación.

A continuación se presenta el caso particular del portal (Estructura del repositorio). Se termina presentando la tec-nología y patrones usados en el cliente (RequireJS, Patrón de diseño message-bus) así como la especificación Servlet3 de Java usada en el cliente (Arquitectura servidor).

Page 5: curso desarrollo Documentation · El descriptor de la parte servidora es META-INF/web-fragment.xml y se encuentra en src/main/ resources. Sigue el estándar Servlet3 de Java y contiene

Estructura del repositorio

Cliente ligero

El cliente Javascript es una aplicación modular que se comunica mediante llamadas asíncronas con los servicios web.

Desde el punto de vista del navegador, la aplicación tiene la siguiente estructura:

unredd-portal|- modules/ -> RequireJS modulos y sus estilos|- jslib/ -> Librerías Javascript usadas por los módulos: OpenLayers, RequireJS,→˓etc.|- styles/ -> Hojas CSS generales (de JQuery UI, etc.)|- indicators -> Devuelve una lista con información de los indicadores de un objeto→˓en una capa|- ... -> Otros servicios\- index.html -> Documento HTML de la aplicación

Así, si tubiéramos que añadir una nueva funcionalidad en el cliente, tendríamos que meter los módulos en el directoriomodules, las hojas de estilos en styles o en modules y las librerías que se utilicen en jslib.

Sin embargo, repartir todos estos ficheros en distintos directorios complicaría el proceso de añadir y quitar plugins ala aplicación, por lo que el código fuente está organizado tratando de agrupar todos esos ficheros por funcionalidad en“plugins”.

Plugins, cargador de plugins, aplicaciones

Así, para la parte cliente agruparemos los HTML, CSS y Javascript necesarios para implementar las funcionalidadesque nos interesen en artefactos que llamaremos plugins.

Para la aplicación que queremos desarrollar, habrá que utilizar uno o más plugins, que tendrán que ser cargadosmediante el cargador de plugins.

Por último, tendremos que desplegar una aplicación, diciéndole al cargador de plugins qué plugins queremos incluiren el resultado final

Así, una aplicación incluirá uno o más plugins que serán cargados mediante el cargador de plugins.

Estructura del código fuente

¿Cómo están estructurados los plugins, el cargador de plugins y las aplicaciones en el código fuente?

La aplicación es implementada por varios proyectos Java estructurados de la siguiente manera:

portal|- core/ -> Librería que contiene el cargador de plugins y algunas→˓funcionalidades básicas como el manejo de errores.|- base/ -> Plugin que contiene la funcionalidad básica de la→˓aplicación: árbol de capas, panel de leyenda, mapa, etc.|- feedback/ -> Plugin que contiene la funcionalidad de feedback|- layer-time-sliders/ -> Plugin que muestra una barra temporal por cada capa, para→˓cambiar la instancia temporal mostrada de forma independiente.|- time-slider/ -> Plugin que muestra la barra temporal que se instala en la→˓barra de herramientas bajo el banner.|- language-buttons/ -> Plugin que muestra los botones para cambiar de idioma.|- geoexplorer-reader/ -> Plugin que lee una base de datos de GeoExplorer para→˓añadir las capas al mapa.\- demo/ -> Aplicación incluye todos los plugins anteriores.

Page 6: curso desarrollo Documentation · El descriptor de la parte servidora es META-INF/web-fragment.xml y se encuentra en src/main/ resources. Sigue el estándar Servlet3 de Java y contiene

El proyecto base contendrá los módulos RequireJS, librerías Javascript y estilos CSS necesarios para tener todas lasfuncionalidades del portal, mientras que el proyecto demo especificará de alguna manera que quiere incluir base.

Estructura por defecto Maven

Todos los proyectos del portal utilizan Maven como herramienta de compilación, adaptándose a la estructura pordefecto que por convención tienen los proyectos Maven:

• src/main/java: Código fuente Java

• src/main/resources: Recursos usados por el código Java, componentes de la parte cliente (CSS, módulos ylibrerías Javascript, etc.)

• src/test/java y src/test/resources: Tests automatizados

• pom.xml: Descriptor del proyecto Maven, donde se definen las dependencias entre proyectos.

Adicionalmente a estos directorios, las aplicaciones como demo incorporan un directorio adicional:

• src/main/webapp: Raíz de la aplicación web

Todos los recursos que se sitúen aquí se ofrecerán via HTTP en la raíz de la aplicación por lo que es el lugar idealpara los contenidos estáticos y específicos de la aplicación. Además, incluye el directorio WEB-INF específico deaplicaciones Java, con el descriptor de despliegue web.xml

Estructura proyectos plugin

Los proyectos plugin constan de los siguientes artefactos:

Desarrollos parte cliente

Si los proyectos de los que estamos hablando son Java ¿cómo se incluyen artefactos del cliente (módulos RequireJS,CSS, etc.)?

Las funcionalidades para la parte cliente se encuentran en el directorio nfms dentro de src/main/resources.Estas funcionalidades consisten en módulos RequireJS, hojas de estilo CSS, librerías Javascript, etc. organizadossiguiendo esta estructura:

nfms|- xxx-conf.json (descriptor del plugin. xxx es el nombre del plugin. E.g.: base-→˓conf.json)|- modules/ (módulos RequireJS)|- jslib/ (librerías Javascript utilizadas)\- styles/ (hojas de estilo CSS)

Este directorio se encuentra en src/main/resources porque Maven por defecto incluirá todo lo que haya ahí enel JAR que genere al empaquetar.

Por ejemplo, en el proyecto base existe el directorio src/main/resources que contiene nfms/modules/layer-list.js y nfms/jslib/OpenLayers/OpenLayers.unredd.js, entre otros. Cuando Mavengenere el JAR, el directorio nfms aparecerá en la raíz de los contenidos del JAR.

Page 7: curso desarrollo Documentation · El descriptor de la parte servidora es META-INF/web-fragment.xml y se encuentra en src/main/ resources. Sigue el estándar Servlet3 de Java y contiene

Descriptor parte cliente

Es un fichero compuesto por el nombre del plugin y “-conf.json” que reside en la raíz del directorio nfms y quecontiene información descriptiva sobre el plugin, como la configuracion por defecto de los plugins (ver Configuraciónde los plugins) o las librerías de terceros que utiliza el plugin y sus dependencias. Esta última información es necesariapara que RequireJS cargue las librerías en el orden correcto.

El formato del fichero es el siguiente:

{"default-conf" : {

"<nombre-modulo>" : <configuracion-por-defecto-modulo>...

},"requirejs": {

"paths" : {"<id-libreria>" : "<ruta relativa a 'modules'>",...

},"shim" : {

"<id-libreria>" : [ "<id-dependencia1>", "<id-dependencia2>",→˓... ],

...},

}}

Ejemplo:

{"default-conf" : {

"banner" : {"hide" : false

}},"requirejs": {

"paths" : {"jquery-ui" : "../jslib/jquery-ui-1.10.4.custom","fancy-box": "../jslib/jquery.fancybox.pack","openlayers": "../jslib/OpenLayers/OpenLayers.unredd","mustache": "../jslib/jquery.mustache"

},"shim" : {

"fancy-box": [ "jquery" ],"mustache": [ "jquery" ]

},}

}

Parte servidora

El descriptor de la parte servidora es META-INF/web-fragment.xml y se encuentra en src/main/resources. Sigue el estándar Servlet3 de Java y contiene referencia a las clases Java que implementan los serviciosen él declarados.

La implementación de los servicios estará en src/main/java.

Page 8: curso desarrollo Documentation · El descriptor de la parte servidora es META-INF/web-fragment.xml y se encuentra en src/main/ resources. Sigue el estándar Servlet3 de Java y contiene

Estructura proyectos aplicación

Los proyectos aplicación constan de los siguientes artefactos.

TODO

Cargador de plugins

Para desplegar la aplicación se genera un WAR (Web application ARchive) que contendrá los ficheros JARpertenecientes a los plugins y sus dependencias.

Cuando este WAR se despliega y se inicia la aplicación, se analizan todos los JARs existentes dentro del WAR enbusca de módulos RequireJS, estilos y librerías externas.

• los paquetes modules y styles son escaneados en busca de módulos javascript y estilos:

nfms|- xxx-conf.json|- modules/ (escaneado en busca de .js y .css)|- jslib/\- styles/ (escaneado en busca de .css)

De esta manera, cualquier fichero .css existente en cualquier de los dos paquetes será importado al cargar laaplicación. Igualmente, todo fichero .js existente en modules será cargado inicialmente por RequireJS aliniciar la aplicación.

• el descriptor del plugin es analizado.

Tras este proceso, todos estos recursos encontrados serán accesibles via HTTP.

Despliegue

Como visto en el punto Cargador de plugins, todos los JARs incluídos en la aplicación son analizados en busca demódulos, librerías, estilos, etc. Así, para componer una aplicación que incluya los plugins que nos interesan basta conespecificar en el pom.xml la dependencia al proyecto del plugin.

Cuando este proyecto es incluido como dependencia en un proyecto, por ejemplo demo, aparecerá como JAR dentrodel WAR y sus contenidos serán analizados y accesibles via HTTP.

Optimización

Durante el proceso de empaquetado de una aplicación como fichero WAR se realiza un proceso de optimización de lashojas de estilos CSS y el código Javascript.

Este proceso consiste en la generación de dos recursos optimizados para estilos CSS y código Javascript en el directoriooptimized del espacio web de dicha aplicación, es decir, en src/main/webapp/.

Estos dos ficheros contienen respectivamente todos los estilos CSS y todo el código Javascript proporcionado por todoslos plugins incluidos en la aplicación. Además el contenido está comprimido para que la descarga desde el navegadorsea más ligera.

Así, cuando desplegamos el fichero WAR de la aplicación, éste contiene tanto las hojas de estilo y módulos Javascriptindividuales como los dos ficheros optimizados. Para seleccionar el modo optimizado basta con poner la variable deentorno MINIFIED_JS=true.

A continuación podemos observar lo que nos arroja el fichero index.html en cada caso. Primero sin optimizar:

Page 9: curso desarrollo Documentation · El descriptor de la parte servidora es META-INF/web-fragment.xml y se encuentra en src/main/ resources. Sigue el estándar Servlet3 de Java y contiene

<html><head><meta http-equiv="X-UA-Compatible" content="IE=EmulateIE7"><meta http-equiv="Content-Type" content="text/html; charset=utf-8">

<link rel="icon" type="image/png" href="static/img/favicon.png">

<link rel="stylesheet" href="modules/banner.css"><link rel="stylesheet" href="modules/info-dialog.css"><link rel="stylesheet" href="modules/layer-list.css"><link rel="stylesheet" href="modules/layout.css"><link rel="stylesheet" href="modules/legend-button.css"><link rel="stylesheet" href="modules/legend-panel.css"><link rel="stylesheet" href="modules/scale.css"><link rel="stylesheet" href="modules/time-slider.css"><link rel="stylesheet" href="modules/toolbar.css"><link rel="stylesheet" href="modules/zoom-bar.css"><link rel="stylesheet" href="styles/jquery-ui-1.10.3.custom.css"><link rel="stylesheet" href="styles/jquery.fancybox.css">

<script src="config.js"></script><!--<script src="js/require.js" data-main="modules/main"></script>--><script src="jslib/require.js"></script><script>

require.config({paths: {

"main": "modules/main"}

});require(["main"]);

</script>

<link rel="stylesheet" href="static/overrides.css"/></head><body></body></html>

Y ahora con la variable MINIFIED_JS = true:

<html><head><meta http-equiv="X-UA-Compatible" content="IE=EmulateIE7"><meta http-equiv="Content-Type" content="text/html; charset=utf-8">

<link rel="icon" type="image/png" href="static/img/favicon.png">

<link rel="stylesheet" href="optimized/portal-style.css">

<script src="config.js"></script><!--<script src="js/require.js" data-main="modules/main"></script>--><script src="jslib/require.js"></script><script>

require.config({paths: {

"main": "optimized/portal"}

});

Page 10: curso desarrollo Documentation · El descriptor de la parte servidora es META-INF/web-fragment.xml y se encuentra en src/main/ resources. Sigue el estándar Servlet3 de Java y contiene

require(["main"]);</script>

<link rel="stylesheet" href="static/overrides.css"/></head><body></body></html>

Podemos observar cómo en lugar de cargarse todos los CSS de forma separada, se carga un único CSS enoptimized/portal y que el modulo main se mapea a optimized/portal.js

Programación de servicios

El código en los módulos RequireJS puede realizar peticiones a los servicios de la aplicación. De igual modo que enla parte cliente, un plugin puede contribuir con servicios a la aplicación final.

La implementación de estos servicios se basa en la especificación Java Servlet 3.0 y consistirá en la implementaciónde uno o más Servlets definidos en el descriptor de despliegue. Este puede encontrarse en dos ficheros.

El primero es WEB-INF/web.xml del espacio web, es decir en src/main/webapp/WEB-INF/web.xml enla estructura por defecto de Maven. Este fichero es el descriptor de despliegue propiamente dicho, y en él se puedendefinir todos los servlets necesarios en las aplicaciones, como demo.

Sin embargo, en los plugins no es posible utilizar el descriptor de despliegue (web-xml) ya que no se genera ningúnfichero WAR sino un JAR (que se incluirá en un WAR). En este caso, la especificación Servlet 3.0 define que laslibrerías JAR usadas por una aplicación WAR pueden contribuir al descriptor de despliegue mediante un ficheroMETA-INF/web-fragment. Es el caso por ejemplo del plugin base que incluye distintos servicios para accedera indicadores sobre objetos de algunas capas del mapa:

<?xml version="1.0" encoding="UTF-8"?><web-fragment version="3.0" xmlns="http://java.sun.com/xml/ns/javaee"

xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/

→˓ns/javaee/web-fragment_3_0.xsd">

<!-- indicators --><servlet>

<servlet-name>indicator-list-servlet</servlet-name><servlet-class>org.fao.unredd.indicators.IndicatorListServlet</

→˓servlet-class></servlet><servlet-mapping>

<servlet-name>indicator-list-servlet</servlet-name><url-pattern>/indicators</url-pattern>

</servlet-mapping><servlet>

<servlet-name>indicator-data-servlet</servlet-name><servlet-class>org.fao.unredd.indicators.IndicatorDataServlet</

→˓servlet-class></servlet><servlet-mapping>

<servlet-name>indicator-data-servlet</servlet-name><url-pattern>/indicator</url-pattern>

</servlet-mapping></web-fragment>

Page 11: curso desarrollo Documentation · El descriptor de la parte servidora es META-INF/web-fragment.xml y se encuentra en src/main/ resources. Sigue el estándar Servlet3 de Java y contiene

error-management

context-attributes

request-attributes

RequireJS

RequireJS es una librería que permite encapsular en módulos nuestro código JavaScript y cargarlos bajo demanda(llamada require).

Para ello es necesario cargar la librería RequireJS en el código HTML de la página en la que se va a utilizar:

<html><head><script src="require.js" type="text/javascript"

data-main="modules/main"></script></head><body></body></html>

En el tag de carga se especifica el módulo inicial que RequireJS cargará y ejecutará. En el ejemplo “modules/main.js”.

Módulos RequireJS

Un módulo RequireJS consiste en:

• Unas dependencias

• Un código de inicialización que devuelve opcionalmente un valor y que se ejecuta cuando las dependencias hansido a su vez inicializadas

Árbol de dependencias

Típicamente los módulos consisten en una llamada a define, que toma una lista de los módulos y una función deinicialización que se ejecuta cuando las dependencias están satisfechas:

define([ "map" ], function(map) {alert("este módulo depende del módulo"

+ "map y en este punto map ha sido"+ "cargado e inicializado");

});

La función obtiene recibe como parámetros referencias a los módulos que se definen como dependencias, en el mismoorden:

define([ "layer-list", "map" ], function(layerList, map) {// layerList apunta al módulo "layer-list"// map apunta al módulo "map"

});

pero el orden de carga puede variar, ya que por ejemplo, en el caso anterior otro módulo puede haber cargado yalayer-list. RequireJS analiza las llamadas define y crea un árbol de dependencias que se cargan en cualquier

Page 12: curso desarrollo Documentation · El descriptor de la parte servidora es META-INF/web-fragment.xml y se encuentra en src/main/ resources. Sigue el estándar Servlet3 de Java y contiene

orden que garantice que las dependencias de un módulo estén todas cargadas e inicializadas cuando el módulo quedepende de ellas se inicializa.

De forma más visual:

para cargar A requireJS realiza una secuencia similar a esta:

• Se detecta la dependencia de A en B y C por lo que pasa a intentar cargarlos.

• Se intentar cargar B pero B depende de E, por lo que se intenta cargar E.

• Como E no tiene ninguna dependencia se cargar directamente y se llama a su código de inicialización.

• Ahora que B tiene todas sus dependencias satisfechas se ejecuta su código de incialización.

• RequireJS pasa a cargar C, pero detecta la dependencia en D y en E y, como E ya ha sido cargada, pasa a cargarD.

• Como D no tiene ninguna dependencia se cargar directamente y se llama a su código de inicialización.

• Ahora C tiene sus dos dependencias ya cargadas, por lo que se llama a su código de incialización.

• Con B y C ya cargados se pasa a ejecutar el código de inicialización de A.

En la secuencia anterior se ilustra por una parte el hecho de que los módulos en los que depende un módulo soncargados e inicializados previamente. Por otra parte, se muestra que el orden de carga de las dependencias de unmódulo no corresponde con el orden en el que están definidas en el código. Por ejemplo, en el caso de C primero secargó E antes que D porque la dependencia fue cargada por B previamente.

Dependencias de módulos no-RequireJS

En el caso de un módulo que dependa de librerías que no son módulos RequireJS (jquery, openlayers, etc.) es to-davía necesario especificar las dependencias, ya que de lo contrario RequireJS puede cargarlas arbitrariamente anteso después. En caso de hacerlo después, el módulo intentará utilizar una librería que no está cargada y se producirá unerror.

La forma de especificar la dependencia un modulo tal es idéntica a la de los modulos RequireJS:

define([ "jquery" ], function($) {alert("este módulo depende de jQuery"

+ "y en este punto jQuery ha sido"+ "cargado e inicializado");

});

pero ¿de dónde saca RequireJS el código de jQuery? No lo puede obtener de ningún sitio, por lo que hay queespecificárselo. El módulo main, que es el módulo de entrada, es el encargado de realizar esta configuración:

require.config({baseUrl : "modules",paths : {

Page 13: curso desarrollo Documentation · El descriptor de la parte servidora es META-INF/web-fragment.xml y se encuentra en src/main/ resources. Sigue el estándar Servlet3 de Java y contiene

"jquery" : "http://ajax.googleapis.com/ajax/libs/jquery/2.0.3/jquery.→˓min",

"jquery-ui" : "http://ajax.googleapis.com/ajax/libs/jqueryui/1.10.3/→˓jquery-ui.min",

"fancy-box" : "../js/jquery.fancybox.pack","openlayers" : "../js/OpenLayers/OpenLayers.debug"

},shim : {

"fancy-box" : [ "jquery" ]}

});

En el código de main se puede observar cómo hay una llamada al método require.config que incluye unelemento cuya propiedad paths incluye las direcciones de las cuales puede descargar RequireJS cada librería.

También se puede observar cómo en la propiedad shim se establecen las dependencias entre módulos no-RequireJS.

Valores de retorno

Se ha mostrado anteriormente cómo las dependencias de un módulo son recibidas como parámetro en la función deinicialización:

define([ "layer-list", "map" ], function(layerList, map) {// layerList apunta al módulo "layer-list"// map apunta al módulo "map"

});

pero, ¿qué contienen exactamente estas referencias?

Contienen el valor que retorna la función de inicialización del módulo dependencia. Por ejemplo, el siguiente módulomap en su función de inicialización crea un mapa OpenLayers, añade una capa y la devuelve:

define([ "openlayers" ], function() {

var mimapa = new OpenLayers.Map("map");mimapa.addLayer(new OpenLayers.Layer.WMS("OpenLayers WMS", "http://vmap0.

→˓tiles.osgeo.org/wms/vmap0?", {layers : "basic"

}));

return mimapa;});

Por tanto, cuando el siguiente módulo establezca la dependencia:

define([ "map" ], function(map) {map.setCenter(new OpenLayers.LonLat(-84, 0), 6);

});

el valor de la variable map que se pasará a la función de inicialización de éste será el mapa OpenLayers que se creóen el módulo map anterior.

Funciones públicas

A veces un módulo debe permitir que los módulos que dependen de él realicen algunas operaciones sobre el mismo.Más adelante se verá que existe otra manera que frecuentemente es mejor, pero de momento mostramos otra manera

Page 14: curso desarrollo Documentation · El descriptor de la parte servidora es META-INF/web-fragment.xml y se encuentra en src/main/ resources. Sigue el estándar Servlet3 de Java y contiene

que también puede ser útil en un momento dado.

Por ejemplo, si queremos hacer un módulo error para manejar los errores de la aplicación mostrando un diálogo alusuario y escribiendo el problema en la consola tendríamos una función como ésta:

function(errorMsg) {console.log(errorMsg);alert(errorMsg);

});

Para poder utilizar este módulo tendremos que importarlo en las dependencias de nuestro módulo y cuando se produzcala condición del error hacer una llamada a dicho módulo:

define([ "error" ], function(error) {if (condicion) {

error.showMessage("Se ha cumplido una condición de error");}

});

Pero para esto tendríamos que devolver un objeto en el módulo error que tenga una función showMessage. Estopodría hacerse teniendo un módulo error como el siguiente:

define([], function() {return {

showMessage : function(errorMsg) {console.log(errorMsg);alert(errorMsg);

}};

});

cuyo valor de retorno tiene una propiedad showMessage que es la función de error.

Funciones privadas

Es posible definir funciones privadas dentro del módulo. Para ello basta con definir las funciones antes del valor deretorno del módulo:

define([ "openlayers" ], function() {

function createLayer(name, url, wmsName) {return new OpenLayers.Layer.WMS(name, url, {

layers : wmsName,transparent : true

});}

var mimapa = new OpenLayers.Map("map");mimapa.addLayer(createLayer("Basic", "http://vmap0.tiles.osgeo.org/wms/vmap0?

→˓", "basic");mimapa.addLayer(createLayer("Costa", "http://vmap0.tiles.osgeo.org/wms/vmap0?

→˓", "coastline_01");

return mimapa;});

En el ejemplo anterior se define una función createLayer y a continuación se le invoca un par de veces parainstanciar las capas WMS que se añaden al mapa.

Page 15: curso desarrollo Documentation · El descriptor de la parte servidora es META-INF/web-fragment.xml y se encuentra en src/main/ resources. Sigue el estándar Servlet3 de Java y contiene

Plantilla módulo

A continuación se presenta una plantilla que puede ser útil para la creación de nuevos módulos:

define([ "dependencia1", "dependencia2" ], function(modulo1, modulo2) {

// // variables privadas// var miVariablePrivada = ...;

// // funciones privadas// function miFuncionPrivada() {// }

// // inicialización

// // valor de retorno// return {

// propiedades públicasmiPropiedadPublica : ...,

// Funciones públicasmiFuncionPublica : function() {}

};});

Patrón de diseño message-bus

El patrón de diseño Message Bus permite desacoplar los componentes que forman una aplicación. En una aplicaciónmodular, los distintos componentes necesitan interactuar entre sí. Si el acoplamiento es directo, la aplicación dejade ser modular ya que aparecen dependencias, con frecuencia recíprocas, entre los distintos módulos y no es posiblerealizar cambios a un módulo sin que otros se vean afectados.

En cambio, si los objetos se acoplan a través de un objeto intermediario (Message Bus), casi todas las dependenciasdesaparecen, dejando sólo aquellas que hay entre el Message Bus y los distintos módulos.

En el siguiente ejemplo vemos una hipotética aplicación modular que consta de tres componentes con representacióngráfica y que están dispuestos de la siguiente manera:

• Map: En la parte central (verde) hay un mapa que muestra cartografía de España.

• LayerList: En la parte izquierda (rojo) hay una lista de temas. Vemos que sólo hay un tema de catastro, que esel que se visualiza en el mapa.

• NewLayer: En la parte superior (azul) existe un control que permite añadir temas a los otros dos componentes.

Un posible diseño de dicha página consistiría en un módulo Layout que maqueta la página HTML y que inicializalos otros tres objetos. En respuesta a la acción del usuario, el objeto NewLayer mandaría un mensaje a LayerList yMap para añadir el tema en ambos componentes. De la misma manera, LayerList podría mandar un mensaje a Map encaso de que se permitiera la eliminación de capas desde aquél. El siguiente grafo muestra los mensajes que se pasaríanlos distintos objetos:

Page 16: curso desarrollo Documentation · El descriptor de la parte servidora es META-INF/web-fragment.xml y se encuentra en src/main/ resources. Sigue el estándar Servlet3 de Java y contiene

Es posible observar como en el caso de que se quisiera quitar el módulo LayerList, sería necesario modificar el objetoLayout así como el objeto NewLayer, ya que están directamente acoplados. Sin embargo, con el uso del Message Bus,sería posible hacer que los distintos objetos no se referenciaran entre sí directamente sino a través del Message Bus:

Así, el módulo NewLayer mandaría un mensaje al Message Bus con los datos de la nueva capa y Map y LayerListsímplemente escucharían el mensaje y reaccionarían convenientemente. Sería trivial quitar de la página LayerList yaque no hay ninguna referencia directa al mismo (salvo tal vez en Layout).

Y al contrario: sería posible incluir un nuevo módulo, por ejemplo un mapa adicional, y que ambos escuchasen elevento “add-layer” de forma que se añadirían los temas a ambos mapas.

De esta manera la aplicación es totalmente modular: es posible reemplazar módulos sin que los otros módulos se veanafectados, se pueden realizar contribuciones bien definidas que sólo deben entender los mensajes existentes para poderintegrarse en la aplicación, etc.

TODO referenciar los ejemplos del taller de desarrollo.

Page 17: curso desarrollo Documentation · El descriptor de la parte servidora es META-INF/web-fragment.xml y se encuentra en src/main/ resources. Sigue el estándar Servlet3 de Java y contiene

Arquitectura servidor

Introducción a servlet 3.0

La parte servidor del portal está construida sobre la especificación Servlet 3.0. En este contexto se pueden crearprincipalmente tres tipos de objetos: filtros, servlets y escuchadores de contexto o ContextListeners.

Servlets

Los servlets son classes que heredan de javax.servlet.http.HttpServlet:

package org.fao.unredd;

import javax.servlet.http.HttpServlet;

public class HolaMundoServlet extends HttpServlet {

}

El objetivo principal del servlet es proporcionar una respuesta a una petición HTTP. Si por ejemplo queremos retornaruna petición GET deberemos de implementar el método doGet:

package org.fao.unredd;

import java.io.IOException;

import javax.servlet.ServletException;import javax.servlet.http.HttpServlet;import javax.servlet.http.HttpServletRequest;import javax.servlet.http.HttpServletResponse;

public class HolaMundoServlet extends HttpServlet {

@Overrideprotected void doGet(HttpServletRequest req, HttpServletResponse resp)

throws ServletException, IOException {String nombre = req.getParameter("nombre");

resp.getWriter().println("Hola mundo, " + nombre);}

}

Podemos observar que es posible utilizar la instancia de HttpServletRequest que se pasa en el parámetro reqpara acceder a los parámetros de la petición GET, en este caso nombre; y que podemos escribir la respuesta a travésde la instancia de HttpServletResponse.

Por último se necesario especificar al sistema en qué URL se debe acceder a dicho servlet. Para ello hay que registrarloen el fichero web.xml de la siguiente manera:

<servlet><servlet-name>holamundo-servlet</servlet-name><servlet-class>org.fao.unredd.HolaMundoServlet</servlet-class>

</servlet><servlet-mapping>

<servlet-name>holamundo-servlet</servlet-name>

Page 18: curso desarrollo Documentation · El descriptor de la parte servidora es META-INF/web-fragment.xml y se encuentra en src/main/ resources. Sigue el estándar Servlet3 de Java y contiene

<url-pattern>/holamundo</url-pattern></servlet-mapping>

En el elemento servlet se le dice al sistema que hay un servlet de nombre holamundo-servlet y que es im-plementado por la clase org.fao.unredd.HolaMundoServlet (vista más arriba), mientras que en el segundoelemento se establece la URL en la que se puede acceder al servlet. Con esta configuración sería posible acceder alservlet en una URL similar a esta:

http://localhost:8080/app/holamundo

Note: La especificación Servlet 3.0 permite la inclusión de anotaciones en la clase que implementa el servlet paraespecificar la misma información que contiene el web.xml, de tal manera que el fichero no es necesario. Sin embargo,por simplicidad se evita el uso de anotaciones de este tipo en general.

Filtros

Configuración de los plugins

Con anterioridad se ha comentado que el descriptor del plugin “xxx-conf.json” incluye un elemento para la configu-ración de los distintos modulos RequireJS que forman el plugin.

La configuración que se especifica en dichos elementos queda accesible a los módulos RequireJS mediante el métodoconfig() meta-modulo module. Por ejemplo, si tuviéramos el siguiente descriptor de plugin:

{"default-conf" : {

"mi-modulo" : {"mensaje" : "hola mundo"

}}

}

el siguiente módulo, definido en “mi-modulo.js” podría acceder a su configuración así:

define([ "module" ], function(module) {alert(module.config());

});

mostrando por pantalla el valor de su configuración, es decir el mensaje “hola mundo”.

Modificación de la configuración en tiempo de ejecución

Ahora bien, esta configuración está definida en el plugin de forma fija y sólo se puede cambiar tocando el códido delportal y generando un nuevo WAR, lo cual no es nada práctico. ¿Cómo se puede cambiar la configuración de un pluginde la aplicación una vez ésta está desplegada y ejecutándose en el servidor?

La manera más sencilla consiste en modificar el fichero plugin-conf.json que se encuentra en el directorio deconfiguración del portal. Este fichero tiene la misma estructura que el descriptor del plugin con la única diferencia deque es usado sólo para sobreescribir la configuración por defecto de los distintos módulos. Así, podríamos editar elfichero para dejarlo de esta manera:

Page 19: curso desarrollo Documentation · El descriptor de la parte servidora es META-INF/web-fragment.xml y se encuentra en src/main/ resources. Sigue el estándar Servlet3 de Java y contiene

{"default-conf" : {

"mi-modulo" : {"ejemplo" : "hola a todo el mundo"

}}

}

Y al cargar el módulo mi-modulo aparecerá por la pantalla “hola a todo el mundo”, en lugar de “hola mundo”.

Modificación de la configuración por programación

En ocasiones la configuración que se quiere pasar al plugin depende de un valor de la base de datos, o de si el usuarioestá logado o, en general, de aspectos que se tienen que comprobar por programación. ¿De qué manera es posiblehacer llegar estos valores a un elemento de la interfaz de usuario? La solución son los proveedores de configuración.

Los proveedores de configuración son instancias que implementan la interfaz org.fao.unredd.portal.ModuleConfigurationProvider, que permite añadir elementos a la configuración de los módulos de la mismamanera que se haría manualmente modificando el fichero plugin-conf.json.

Para que la instancia contribuya a la configuración hay que registrarla en la instancia config, por lo que normalmentese registrará en un context listener con un código similar al siguiente:

ServletContext servletContext = sce.getServletContext();Config config = (Config) servletContext.getAttribute("config");config.addModuleConfigurationProvider(new MiConfigurationProvider());

Módulos importantes

Entre los módulos más importantes existentes en la plataforma podemos destacar:

• message-bus: Implementación del patrón message bus (Ver Patrón de diseño message-bus)

• communication: Facilita la comunicación con el servidor

• error: Gestión centralizada de los errores

• layout: Crea el layout de la página

• i18n: Contiene las cadenas de los ficheros de traducción .properties

• map: Crea el mapa principal

• layers: Lee la configuración de capas y lanza eventos add-layer y add-group

• customization: Carga el resto de módulos

Message-bus

De los módulos anteriores, el más importante es message-bus

Función principal: Ofrecer dos métodos para mandar y escuchar mensajes al/del bus. La documentación de las dosfunciones puede encontrarse en el código fuente: https://github.com/nfms4redd/nfms/blob/develop/portal/src/main/webapp/modules/message-bus.js

Valor de retorno: Un objeto con dos propiedades send y listen que permiten respectivamente enviar y escucharmensajes.

Page 20: curso desarrollo Documentation · El descriptor de la parte servidora es META-INF/web-fragment.xml y se encuentra en src/main/ resources. Sigue el estándar Servlet3 de Java y contiene

Mensajes enviados: Ninguno. El módulo se encarga de procesar y canalizar los eventos, pero no inicia ni escuchaninguno.

Mensajes escuchados: Ninguno.

Referencia mensajes

modules-loaded

Enviado una vez el módulo customization ha cargado todos los módulos especificados por el servidor.

Parámetros: Ninguno.

Ejemplo de uso: Útil para realizar acciones que requieren que todos los módulos hayan sido cargados. Por ejemplo, elenvío de mensajes de potencial interés para algún módulo ha de hacerse tras la carga de todos los módulos, es decir,una vez este mensaje ha sido enviado.

Más información:

• Secuencia de inicio de la aplicación

before-adding-layers

Enviado justo antes de que se empiecen a lanzar los eventos add-group y add-layer. Da la oportunidad a otros módulosde realizar operaciones previas a la carga de las capas.

Parámetros: Ninguno.

Más información:

• Secuencia de inicio de la aplicación

layers-loaded

Enviado una vez el módulo layers ha lanzado los eventos add-layer y add-group correspondientes a la con-figuración de capas existente en el servidor.

Parámetros: Ninguno

Ejemplo de uso: Útil para realizar acciones que requieren que las capas de información se hayan cargado, por ejemplo,para centrar el mapa.

Más información:

• Secuencia de inicio de la aplicación

• moduleconfiguration

reset-layers

Se envía para resetear la configuración de capas del portal.

Como norma general, cualquier módulo que escuche los eventos add-layer, add-group o layers-loaded también deberáescuchar el evento reset-layers, y devolver el estado interno del módulo (y del DOM) al que tenía justo antes deempezar a cargar grupos y capas.

Parámetros: Ninguno

Page 21: curso desarrollo Documentation · El descriptor de la parte servidora es META-INF/web-fragment.xml y se encuentra en src/main/ resources. Sigue el estándar Servlet3 de Java y contiene

Más información:

• add-layer

• add-group

• layers-loaded

ajax

Escuchado por el módulo communication para realizar llamadas a servicios.

Parámetros: Un objeto con las siguientes propiedades:

• url: URL a la que se quiere pedir la información

• success: función a ejecutar cuando el servidor responda satisfactoriamente

• complete: función a ejecutar cuando el servidor responda, sea satisfactoriamente o tras un error

• errorMsg: Mensaje de error

• error: función a ejecutar cuando el servidor responda con un error. Por defecto se generará un mensaje de errorcon el contenido de errorMsg.

• controlCallBack: función que recibe el objeto XMLHttpRequest que representa la petición. Este objeto tienemétodos como abort() que permiten la cancelación de la petición

Ejemplo de uso:

error

Escuchado por el módulo error, que muestra un mensaje de error al usuario:

Parametros: Mensaje con el error a mostrar

Ejemplo de uso:

bus.send("error", "Dirección de e-mail incorrecta");

info-features

Resultados de la petición de información a una única capa.

Parámetros:

• wmsLayerId: Id de la capa a la que pertenecen las features.

• features: array con las features OpenLayers. Cada feature tiene:

– Una propiedad “aliases” que es un array que contiene un objeto con propiedades “name” y “alias” paracada atributo de la feature. Por ejemplo:

[{"name" : "ident","alias" : "Id"

},{

"name" : "nprov","alias" : "Nombre"

Page 22: curso desarrollo Documentation · El descriptor de la parte servidora es META-INF/web-fragment.xml y se encuentra en src/main/ resources. Sigue el estándar Servlet3 de Java y contiene

},{

"name" : "pop96","alias" : "Población (1996)"

}]

– Una propiedad “bounds” con el bounding box de la geometría de la feature o null si el servidor no ladevolvió. Siempre en EPSG:900913.

– Una propiedad highlightGeom con la geometría de la feature o el bounding box (en caso de que así seconfigure en el layers.json) o null si el servidor no devolvió datos geométricos. Siempre en EPSG:900913.

• x: Posición X en la que se hizo click para obtener la información

• y: Posición Y en la que se hizo click para obtener la información

Ejemplo de uso:

Más información:

zoom-in

Mueve la escala al nivel inmediatamente mayor

Parámetros: Ninguno

Ejemplo de uso:

bus.send("zoom-in");

Más información:

zoom-out

Mueve la escala al nivel inmediatamente menor

Parámetros: Ninguno

Ejemplo de uso:

bus.send("zoom-out");

Más información:

zoom-to

Mueve el encuadre al objeto OpenLayers.Bounds que se pasa como parámetro. El objeto bounds debe estar en elsistema de referencia del mapa (EPSG:900913)

Parámetros: OpenLayers.Bounds con el extent deseado

Ejemplo de uso:

var bounds = new OpenLayers.Bounds();bounds.extend(new OpenLayers.LonLat(0,42));bounds.extend(new OpenLayers.LonLat(10,52));

bounds.transform( new OpenLayers.Projection("EPSG:4326"),

Page 23: curso desarrollo Documentation · El descriptor de la parte servidora es META-INF/web-fragment.xml y se encuentra en src/main/ resources. Sigue el estándar Servlet3 de Java y contiene

new OpenLayers.Projection("EPSG:900913"));

bus.send("zoom-to", bounds);

Más información:

initial-zoom

Situa el mapa en la posición inicial

Parámetros: Ninguno

Ejemplo de uso:

bus.send("initial-zoom");

Más información:

set-default-exclusive-control

Establece el control exclusivo por defecto para el mapa. Sólo un módulo exclusivo está activado en cada momento.

Parámetros: Objeto OpenLayers.Control.

Ejemplo de uso:

var control = new OpenLayers.Control.WMSGetFeatureInfo({...});bus.send("set-default-exclusive-control", [control]);

Más información:

activate-default-exclusive-control

Activar el control establecido por defecto mediante el mensaje set-default-exclusive-control

Parámetros: Ninguno

Ejemplo de uso:

bus.send("activate-default-exclusive-control");

Más información:

activate-exclusive-control

Pide la activación exclusiva del control que se pasa como parámetro y la desactivación del control exclusivo queestuviera activado en el momento de lanzar el mensaje

Parámetros: OpenLayers.Control

Ejemplo de uso:

Page 24: curso desarrollo Documentation · El descriptor de la parte servidora es META-INF/web-fragment.xml y se encuentra en src/main/ resources. Sigue el estándar Servlet3 de Java y contiene

var clickControl = new OpenLayers.Control({...});bus.send("activate-exclusive-control", [ clickControl ]);

Más información:

highlight-feature

Indica que se debe resaltar la geometría que se pasa como parámetro

Parámetros: OpenLayers.Geometry

Ejemplo de uso:

Más información:

clear-highlighted-features

Indica que se deben de eliminar todos los resaltes establecidos mediante highlight-feature

Parámetros: Ninguno.

Ejemplo de uso:

Más información:

add-group

Indica que se debe añadir un grupo al árbol de capas

Parámetros: Un objeto con las siguientes propiedades:

• id: identificador del grupo

• parentId: Opcional, para grupos dentro de otros grupos hace referencia al grupo contenedor

• name: nombre del grupo

• infoLink: Ruta de la página HTML con información sobre el grupo

Ejemplo de uso:

bus.send("add-group", [ {id:"grupo_admin",name:"Límites administrativos"

}]);

Más información:

add-layer

Indica que se debe añadir una capa a la aplicación

Parámetros: Un objeto con las siguientes propiedades:

• id: id de la capa

Page 25: curso desarrollo Documentation · El descriptor de la parte servidora es META-INF/web-fragment.xml y se encuentra en src/main/ resources. Sigue el estándar Servlet3 de Java y contiene

• groupId: id del grupo en el que se debe añadir la capa

• label: Texto con el nombre de la capa a usar en el portal

• infoLink: Ruta de la página HTML con información sobre la capa

• inlineLegendUrl: URL con una imagen pequeña que situar al lado del nombre de la capa en el árbol de capas

• queryable: Si se pretende ofrecer herramienta de información para la capa o no

• active: Si la capa está inicialmente visible o no

• wmsLayers: Array con la información de las distintas capas WMS que se accederán desde esta capa. El casomás habitual es que se acceda sólo a una, pero es posible configurar varias. Los objetos de este array tienen lasiguiente estructura:

– baseUrl: URL del servidor WMS que sirve la capa

– wmsName: Nombre de la capa en el servicio WMS

– imageFormat: Formato de imagen a utilizar en las llamadas WMS

– zIndex: Posición en la pila de dibujado

– legend: Nombre del fichero imagen con la leyenda de la capa. Estos ficheros se acceden enstatic/loc/{lang}/images

– label: Título de la leyenda

– sourceLink: URL del proveedor de los datos

– sourceLabel: Texto con el que presentar el enlace especificado en sourceLink

– timestamps: Array con los instantes de tiempo en ISO8601 para los que la capa tiene información

Ejemplo de uso:

bus.send("add-layer", {"id" : "meteo-eeuu","groupId" : "landcover","label" : "Radar EEUU","active" : "true","wmsLayers" : [ {

"baseUrl" : "http://mesonet.agron.iastate.edu/cgi-bin/wms/nexrad/n0r-→˓t.cgi",

"wmsName" : "nexrad-n0r-wmst"} ]

});

Más información:

layer-visibility

Cambia la visibilidad de una capa

Parámetros:

• id de la capa portal

• valor de visibilidad

Ejemplo de uso:

Page 26: curso desarrollo Documentation · El descriptor de la parte servidora es META-INF/web-fragment.xml y se encuentra en src/main/ resources. Sigue el estándar Servlet3 de Java y contiene

bus.send("layer-visibility", ["provincias", false]);

Más información:

time-slider.selection

Lanzado cuando el usuario selecciona un instante temporal global distinto al actual. Generalmente se actualiza elmapa con la información de esa fecha.

Parámetros: objeto Date con el instante temporal seleccionado

Ejemplo de uso:

var d = new Date();bus.send("time-slider.selection", d);

Más información:

layer-time-slider.selection

Lanzado cuando el usuario selecciona un instante temporal específico para una capa (a diferencia del time-slider.selection cuyo instante es global para todas las capas).

Parámetros:

• id de la portalLayer que ha determinado su instante temporal.

• objeto Date con el instante temporal seleccionado

Ejemplo de uso:

var d = new Date();bus.send("layer-time-slider.selection", ["mi-portal-layer", date]);

Más información:

layer-timestamp-selected

Una capa ha escuchado uno de los eventos de selección temporal y ha determinado qué instancia temporal es la quemás se ajusta a esa. La capa selecciona la última instancia temporal que es menor o igual al instante seleccionado o laprimera instancia si el instante seleccionado es anterior a todas sus instancias.

Parámetros:

• id de la portalLayer que ha determinado su instante temporal.

• objeto Date con el instante temporal seleccionado

• cadena de carácteres con el nombre del estilo que se debe usar para esta instancia temporal. Puede ser nulo si lacapa no requiere un estilo distinto por instante.

Más información:

Page 27: curso desarrollo Documentation · El descriptor de la parte servidora es META-INF/web-fragment.xml y se encuentra en src/main/ resources. Sigue el estándar Servlet3 de Java y contiene

toggle-legend

Escuchado por el módulo legend-panel para mostrar u ocultar el panel con la leyenda.

Parámetros: Ninguno

Ejemplo de uso:

bus.send("toggle-legend");

Más información:

register-layer-action

Escuchado por la lista de capas. Instala un botón a la derecha de las capas que realizará una acción al ser pulsado.

Parámetros: Función que devuelve el objeto jQuery que se mostará a modo de botón. Esta función toma comoparámetro el mismo objeto que se lanza en el evento add-layer.

Ejemplo de uso (botón de información):

bus.listen("before-adding-layers", function() {

var showInfoAction = function(portalLayer) {if (portalLayer.hasOwnProperty("infoFile")) {

aLink = $("<a/>").attr("href", portalLayer.infoFile);aLink.addClass("layer_info_button");aLink.fancybox({

"closeBtn" : "true","openEffect" : "elastic","closeEffect" : "elastic","type" : "iframe","overlayOpacity" : 0.5

});return aLink;

} else {return null;

}};

bus.send("register-layer-action", showInfoAction);

});

Más información:

register-group-action

Igual que register-layer-action pero para grupos.

show-layer-panel

Activa el panel de capas indicado.

Parámetros: identificador del panel a activar. La lista de paneles puede variar en función de los plugins que hayaactivados. La lista completa de ids es:

Page 28: curso desarrollo Documentation · El descriptor de la parte servidora es META-INF/web-fragment.xml y se encuentra en src/main/ resources. Sigue el estándar Servlet3 de Java y contiene

• all_layers_selector

• layers_transparency_selector

• layer_slider_selector (sólo con el plugin layer-time-sliders)

Ejemplo de uso:

bus.send("show-layer-panel", [ "layers_transparency_selector" ]);

Más información:

show-info

Muestra una ventana emergente con determinada información que se pasa como parámetro.

Parámetros:

• title: Título de la ventana

• link: Bien una url que apunta a la página que se pretende mostrar, o un objeto jquery que será mostrado en laventana

• eventOptions: Opcional. Elemento con las opciones para la personalización de la ventana. Actualmente seutiliza FancyBox por lo que se puede añadir cualquier opción válida de este framework.

Ejemplo de uso:

bus.send("show-info", [ "Mi info", "http://ambiente.gob.am/portal/static/loc/es/html/→˓doc.html" ]);

Más información:

show-layer-info

Muestra la información asociada a una capa con su atributo infoLink o infoFile.

Parámetros: identificador de la capa.

Ejemplo de uso:

bus.send("show-layer-info", [ "provincias" ]);

show-group-info

Muestra la información asociada a un grupo con su atributo infoLink o infoFile.

Parámetros: identificador del grupo

Ejemplo de uso:

bus.send("show-group-info", [ "base" ]);

Page 29: curso desarrollo Documentation · El descriptor de la parte servidora es META-INF/web-fragment.xml y se encuentra en src/main/ resources. Sigue el estándar Servlet3 de Java y contiene

show-wait-mask

Muestra un indicador de que el sistema está ocupado y el usuario debe esperar

Parámetros: Texto informativo

Ejemplo de uso:

bus.send("show-wait-mask", "Enviando información al servidor...");

hide-wait-mask

Oculta el indicador mostrado por show-wait-mask

Parámetros: Ninguno

Ejemplo de uso:

bus.send("hide-wait-mask");

activate-feedback

Activa el modo feedback mostrando la ventana y seleccionando la herramienta para el dibujado del polígono sobre elque se da el feedback.

Parámetros: ninguno

Ejemplo de uso:

bus.send("activate-feedback");

deactivate-feedback

Desactiva el modo feedback, ocultando la ventana y volviendo a la herramienta por defecto (navegación).

Parámetros: ninguno

Ejemplo de uso:

bus.send("deactivate-feedback");

Temas avanzados

Secuencia de inicio de la aplicación

La aplicación se inicia generando el fichero index.html mediante un motor de plantillas. Dicho motor rellena:

• Los tags style con las referencias a los ficheros .css del directorio de módulos.

• Una llamada al servicio config.js con el parámetro lang establecido al valor con el que se accedió aindex.html (si se accedió con index.html?lang=es se generará la carga de config.js?lang=es).

Page 30: curso desarrollo Documentation · El descriptor de la parte servidora es META-INF/web-fragment.xml y se encuentra en src/main/ resources. Sigue el estándar Servlet3 de Java y contiene

La llamada al servicio config.js devuelve un fichero javascript con la configuración de los distintos módulos. Laconfiguración relevante para el inicio de la aplicación es la del módulo customization, que incluye la lista demódulos existente en el fichero portal.properties en la propiedad client.modules.

El módulo customization obtiene la lista de módulos de la configuración y se realiza la siguiente secuencia:

1. customization hace una llamada a require para cargar los módulos.

2. una vez cargados, se lanza el mensaje modules-loaded

3. el evento es escuchado por el módulo layers que fue cargado en el primer paso. layers lanza el eventobefore-adding-layers y a continuación procesa el árbol de capas y lanza los mensajes add-group y add-layercorrespondientes.

4. layers lanza el mensaje layers-loaded

Compilación del proyecto

Obtención de los fuentes

El código del portal se encuentra alojado en el siguiente repositorio de GitHub: https://github.com/nfms4redd/portal.

GitHub proporciona un servicio de GIT, un conocido sistema de control de versiones. Para aquellos que tenganconocimientos de uso de GIT, la URL del servicio es [email protected]:nfms4redd/portal.git. Bastaría con ejecutar lasiguiente instrucción para tener una copia de los fuentes instalada en local:

$ git clone [email protected]:nfms4redd/portal.git

En el caso de que no se quiera utilizar GIT, es posible descargarse un fichero zip con el estado actual del repositoriopulsando el botón “Download ZIP” que ofrece GitHub en la página del repositorio.

Hay que tener en cuenta que la rama por defecto, llamada “develop”, es en la que se realizan los desarrollos y esposible que sea algo más inestable. En el combo “branch:” es posible elegir otras versiones del código fuente, comopor ejemplo las ramas que comienzan por “release_” que son más estables.

Page 31: curso desarrollo Documentation · El descriptor de la parte servidora es META-INF/web-fragment.xml y se encuentra en src/main/ resources. Sigue el estándar Servlet3 de Java y contiene

Instalación de los fuentes en eclipse

Una vez el código fuente ha sido descargado, es necesario abrir Eclipse e importar un proyecto Maven existentemediante un clic con el botón derecho en el Project Explorer > Import > Import...

En el diálogo que aparece hay que decirle a Eclipse que el proyecto que queremos importar es un proyecto Maven.Así, habrá que seleccionar Maven > Existing Maven Projects

A continuación hay que seleccionar el directorio en el que hemos puesto el portal y darle a siguiente.

Tras este paso, nos aparecerá una ventana en la que podremos seleccionar los proyectos dentro del repositorio delportal que queremos importar:

Page 32: curso desarrollo Documentation · El descriptor de la parte servidora es META-INF/web-fragment.xml y se encuentra en src/main/ resources. Sigue el estándar Servlet3 de Java y contiene

Como se ve en la imagen, seleccionaremos todos menos el proyecto raíz y haremos clic en el botón de finalizar. Esposible que Eclipse instale algunos plugins tras esta acción.

Warning: El repositorio del portal contiene varios proyectos correspondientes a la aplicación del portal, plugins,herramientas, etc. Para más información sobre cómo están organizados los proyectos dentro del repositorio verEstructura del repositorio

Ejecución del portal en tomcat desde Eclipse

Para arrancar un Tomcat que contenga el portal, es necesario hacer clic derecho en uno de los proyectos aplicación(ver Estructura del repositorio), como demo, y seleccionar en el menú contextual que aparece: Debug As > Debug onserver.

Page 33: curso desarrollo Documentation · El descriptor de la parte servidora es META-INF/web-fragment.xml y se encuentra en src/main/ resources. Sigue el estándar Servlet3 de Java y contiene

En el caso de no tener un servidor instalado aparecerá un diálogo que nos permitirá definir un nuevo servidor. Selec-cionaremos “Tomcat v7.0 Server” y en la siguiente pantalla seleccionaremos el directorio donde se encuentra nuestroTomcat. Obviamente, es necesario haber descargado e instalado previamente un servidor Apache Tomcat 7.0.

Tras esta operación podremos aceptar el diálogo, tras lo cual el servidor se ejecutará y se abrirá una ventana dentro deeclipse con el portal. También debe ser posible acceder al mismo mediante la siguiente URL:

http://localhost:8080/unredd-portal/

desde cualquier navegador web.

Generación del unredd-portal.war

Si lo que se pretende es exclusivamente obtener el fichero .war, es posible obviar Eclipse y utilizar directamente Mavendesde línea de comandos:

$ mvn package

tras el cual el fichero demo/target/unredd-portal.war habrá aparecido. Dicho fichero se puede desplegar en el directoriowebapps de Tomcat para poder usar la aplicación recién compilada.

Generación del unredd-portal.war desde eclipse

Desde eclipse, se podría generar el war clicando con el botón derecho del ratón en el proyecto portal -> Run as ->Maven Build.

En la ventana que aparece, escribir “package” en el texto Goals:

Page 34: curso desarrollo Documentation · El descriptor de la parte servidora es META-INF/web-fragment.xml y se encuentra en src/main/ resources. Sigue el estándar Servlet3 de Java y contiene

y seleccionar la casilla “Skip Tests”. Tras pinchar en el botón Run se iniciará la ejecución. Si hacia el final aparece eltexto:

[INFO] ------------------------------------------------------------------------[INFO] BUILD SUCCESS[INFO] ------------------------------------------------------------------------

todo estará correcto y aparecerá un fichero unredd-portal.war en el directorio target.

Particularidades de la construcción del proyecto

Maven tiene un método estandarizado para la construcción de proyectos en Java. Este método sigue una secuenciade actividades o ciclo de vida (explicado aquí) que define que primero se realiza la compilación, luego un testeo, elempaquetado, etc. y que es común para todos los proyectos Maven.

Sin embargo, a la hora de construir el portal, hay algunas particularidades que hay que tener en cuenta.

Optimización

Por una parte, durante la construcción del proyecto se realizan una optimización de los recursos del cliente (verOptimización) que puede tomar bastante tiempo. En algunos casos es posible que esta optimización no sea necesaria.

Page 35: curso desarrollo Documentation · El descriptor de la parte servidora es META-INF/web-fragment.xml y se encuentra en src/main/ resources. Sigue el estándar Servlet3 de Java y contiene

Para solucionar esto, Maven proporciona perfiles, que son configuraciones que se pueden activar y desactivar. Así,la optimización está configurada en un perfil “optimize” que está activo por defecto pero se puede desactivar con elparámetro -P, seguida del nombre del perfil con un signo de exclamación delante (para desactivar):

mvn -P \!optimize package

Tests de integración

Por otra parte, una vez se construye el WAR en la fase package de Maven, se llega a la fase integration-test.En el caso del portal, se testean las conexiones a bases de datos, etc. por lo que se requiere levantar determinadosservicios externos para que la fase se pase con éxito. Para más información ver Ejecución de los tests de integración

Ejecución de los tests de integración

El proyecto que contiene los tests automatizados de integración es integration-tests. En él hay una serie detests que se hacen al WAR de demo y en el que se comprueban los servicios de los plugins que demo incluye. Comoestos servicios hacen uso de bases de datos externas, es necesario realizar algún tipo de configuración previa.

Servicio de base de datos

Lo primero es levantar un servicio de base de datos que pueda ser utilizado por los distintos plugins. Una vez el servicioestá levantado, hay que configurar en el fichero integration-tests/src/main/resources/org/fao/unredd/functional/functional-test.properties los parámetros de dicha conexión. Por ejemplo:

db-url=jdbc:postgresql://192.168.2.107:5432/geoserverdatadb-user=geoserverdb-password=unr3dddb-test-schema=integration_tests

Actualmente esta configuración está apuntando al servidor local ya que así lo requiere el servidor de integracióncontinua Travis.

Configuración del plugin Feedback

En el fichero portal.properties es necesario especificar los parámetros necesarios para que el plugin de feed-back pueda encontrar la base de datos:

feedback-db-table=integration_tests.comments

y pueda enviar emails:

feedback-mail-host=smtp.gmail.comfeedback-mail-port=587feedback-mail-username=miusuario@gmail.comfeedback-mail-password=mipasswordfeedback-mail-title=Comentario en portal UNREDDfeedback-mail-text=Por favor, visite $url para confirmar el envío.

Este fichero portal.properties se encuentra en un directorio de configuración propio de los tests de integración, enintegration-tests/test_config. Como para enviar correos es necesario darle al sistema el passwordde [email protected] y no queremos guardarlo en un repositorio de código público, en este portal.properties la

Page 36: curso desarrollo Documentation · El descriptor de la parte servidora es META-INF/web-fragment.xml y se encuentra en src/main/ resources. Sigue el estándar Servlet3 de Java y contiene

propiedad feedback-mail-password tiene el valor $password, que se reemplazará por el valor de la variable deentorno ONUREDDMAILPASSWORD antes de ejecutar los tests.

Ejecución de los tests

Una vez los componentes externos están levantados y el portal configurado, sólo queda ejecutar los tests de integración:

mvn verify

Sin embargo, pasaremos por la fase package que incluye una operación de optimización. Éstas se pueden evitardesactivando el perfil optimize:

mvn -P \!optimize verify

Para más información sobre el uso de Maven ver Particularidades de la construcción del proyecto.

Manuales

Las siguientes secciones incluyen manuales para la creación de las tareas de desarrollo sobre el portal más comunes.

Hello World

Lo primero es crear un nuevo fichero hola-mundo.js en el directorio de módulos. Como contenido podemos definirsímplemente:

alert("hola mundo");

A continuación registramos el módulo en el fichero portal.properties. En su propiedad client.modules añadimosel nombre de nuestro módulo, que no es otro que el nombre del fichero sin la extensión .js:

client.modules=hola-mundo,layers,communication,iso8601,error-management,map,banner,→˓toolbar,time-slider,layer-list,info-control,info-dialog,center,zoom-bar,layer-list-→˓selector,active-layer-list,legend-button,legend-panel

Al recargar el portal nos encontraremos con el mensaje “hola mundo” nada más empezar.

Añadir elementos a la interfaz

Se crea el módulo de la forma habitual, creando el fichero en el directorio de módulos y añadiendo el módulo comodependencia al pom.xml del proyecto.

A continuación se debe elegir en qué punto de la página se quiere añadir el botón. En este caso queremos añadirlo a labarra de herramientas principal. Para ello tenemos que obtener el objeto div de dicha barra, el cual nos lo proporcionael módulo toolbar, que importaremos como dependencia:

define([ "toolbar" ], function(toolbar) {

});

Si observamos el módulo toolbar, podemos ver que devuelve un objeto jQuery con el div y que sólo tenemos queacceder al objeto toolbar para acceder al div:

Page 37: curso desarrollo Documentation · El descriptor de la parte servidora es META-INF/web-fragment.xml y se encuentra en src/main/ resources. Sigue el estándar Servlet3 de Java y contiene

return divToolbar

En este punto podríamos realizar una prueba para comprobar que tenemos una referencia valida al div. El siguientecódigo hace invisible el div de la barra de herramientas:

define([ "toolbar" ], function(toolbar) {toolbar.hide();

});

Si hemos hecho todos los pasos correctamente, veremos que la barra de herramientas no aparece, ya que la hemosescondido en nuestro módulo.

Lo único que queda por hacer es reemplazar el código de prueba anterior por otro que cree un botón. Esto lo podemoshacer creando un tag <button> con jQuery:

define([ "toolbar", "jquery" ], function(toolbar, $) {var aButton = $("<button/>").attr("id", "miboton").addClass("blue_button").

→˓html("púlsame");aButton.appendTo(toolbar);aButton.click(function() {

alert("boton pulsado");});

});

Nótese que, como queremos utilizar jQuery, tenemos que declararla como un módulo y definirla como parámetro $ enla función de inicialización.

Por último, podemos añadir un fichero CSS para estilar dicho botón y añadirle un margen, por ejemplo:

#miboton {margin: 12px;

}

Añadir una botonera

Este manual pretende añadir un botón al portal creando un módulo botonera que facilite la tarea en lo sucesivo.

Así pues, el proceso será similar, con la diferencia que en lugar de añadir un botón, vamos a añadir un objeto div:

define([ "jquery", "layout" ], function($, layout) {var botonera = $("<div/>").attr("style", "position:absolute;top:6px; left:7em;

→˓ z-index:2000");botonera.appendTo(layout["map"]);

});

En el código anterior podemos observar cómo se crea un objeto div, se estila y se añade al espacio reservado para elmapa por el módulo layout. Esto último quiere decir que la botonera estará sobre el mapa.

A continuación debemos de dar la posibilidad a otros módulos para que añadan elementos a dicha botonera. Existendos maneras: escuchando un mensaje o devolviendo un objeto con un método que añada el botón cuando es invocado.En este manual veremos esta última posibilidad.

Para ello el módulo deberá devolver un objeto {} con una propiedad que sea una función:

define([ "jquery", "layout" ], function($, layout) {var botonera = $("<div/>").attr("style", "position:absolute;top:6px; left:8em;

→˓ z-index:2000");

Page 38: curso desarrollo Documentation · El descriptor de la parte servidora es META-INF/web-fragment.xml y se encuentra en src/main/ resources. Sigue el estándar Servlet3 de Java y contiene

botonera.appendTo(layout["map"]);return {

newButton : function(text, callback) {// Añade el botón

}};

});

Para utilizar esta funcionalidad, es necesario crear un nuevo módulo que importe el módulo botonera:

define([ "botonera" ], function(botonera) {

});

En este código, la variable botonera recibida en la función de inicialización del módulo es el valor de retorno dela inicialización del módulo botonera, por lo que es posible hacer llamadas a la propiedad newButton de estavariable:

define([ "botonera" ], function(botonera) {botonera.newButton("hola mundo", function() {

alert('hola mundo');});

});

Por último, queda implementar el código de la función newButton, que debe tomar al menos un texto y una funcióncallback que se invocará cuando se pinche en el botón:

define([ "jquery", "layout" ], function($, layout) {var botonera = $("<div/>").attr("style", "position:absolute;top:6px; left:7em;

→˓ z-index:2000");botonera.appendTo(layout["map"]);return {

newButton : function(text, callback) {var aButton = $("<button/>").html(text);aButton.appendTo(botonera);aButton.click(callback);

}};

});

Manejo de eventos

El siguiente tutorial muestra cómo a través de los eventos existentes es posible interactuar con la plataforma. Paraello crearemos un módulo consistente en poner a invisible todas las capas (checkbox desactivado) mediante dos pasostípicos:

1. Captura de eventos y recogida de información

Para poner invisible las capas se utilizará el evento “layer-visibility”, al que se pasa el identificador de la capa y elvalor de visibilidad. El valor de visibilidad es siempre falso, pero además se necesitará la lista de identificadores detodas las capas. Es en este punto es necesario escuchar el evento “add-layer” y guardar la información relevante, losidentificadores de las capas:

Page 39: curso desarrollo Documentation · El descriptor de la parte servidora es META-INF/web-fragment.xml y se encuentra en src/main/ resources. Sigue el estándar Servlet3 de Java y contiene

define([ "message-bus" ], function(bus) {

var layerIds = [];

bus.listen("add-layer", function(event, layerInfo) {layerIds.push(layerInfo.id);

});});

2. Ejecución del módulo

Una vez recopilados todos los ids, sólo queda lanzar el mensaje para cada una de las capas. Esto se hará en respuestaa la pulsación en un botón, utilizando el módulo botonera creado en manuales anteriores:

define([ "message-bus", "botonera" ], function(bus, botonera) {

var layerIds = [];

bus.listen("add-layer", function(event, layerInfo) {layerIds.push(layerInfo.id);

});

botonera.newButton("todas invisibles", function() {for (var i = 0; i < layerIds.length; i++) {

bus.send("layer-visibility", [layerIds[i], false]);}

});});

Patrón habitual

El presente manual es un ejemplo sencillo de un patrón que se repite una y otra vez a lo largo del portal:

1.- Escuchado de eventos y recogida de información 2.- Realización de una acción con la información obtenida de loseventos

Posición del mapa

Cada vez que se quiere añadir un mapa OpenLayers en una página web se comienza escribiendo código en el quecreamos el mapa:

var map = new OpenLayers.Map("map", {theme : null,projection : new OpenLayers.Projection("EPSG:4326"),units : "m",allOverlays : true,controls : []

});

para luego interactuar con él añadiéndole capas, instalando controles, etc.:

map.addControl(new OpenLayers.Control.Navigation({documentDrag : true,zoomWheelEnabled : true

Page 40: curso desarrollo Documentation · El descriptor de la parte servidora es META-INF/web-fragment.xml y se encuentra en src/main/ resources. Sigue el estándar Servlet3 de Java y contiene

}));map.addControl(new OpenLayers.Control.MousePosition({

prefix : '<a target="_blank" ' +'href="http://spatialreference.org/ref/epsg/4326/">' + 'EPSG:4326</a>

→˓coordinates: ',separator : ' | ',numDigits : 2,emptyString : 'Mouse is not over map.'

}));

Sin embargo, en el portal de diseminación ya existe un mapa creado. ¿Cuál es la manera de proceder para obtener unareferencia a dicho mapa y, por ejemplo, mostrar información sobre las coordenadas que se están navegando?

La respuesta es sencilla, símplemente hay que importar el módulo map y obtener la referencia en la función deinicialización:

define([ "map", "layout" ], function(map, layout) {

var divMap = layout["map"];var divCoor = $("<div/>").attr("id", "coor");divMap.append(divCoor);var control = new OpenLayers.Control.MousePosition({

prefix : '<a target="_blank" ' + 'href="http://spatialreference.org/→˓ref/epsg/4326/">' + 'EPSG:4326</a> coordinates: ',

div : divCoor.get(0),separator : ' | ',numDigits : 2,emptyString : 'Mouse is not over map.'

});map.addControl(control);

});

Lectura de parámetros URL

Los parámetros de la URL son procesados por el servidor y retornados como configuración del módulourl-parameters.

A su vez, el módulo url-parameters ofrece una función get en su valor de retorno que permite obtener losvalores de los parámetros.

Así pues, para obtener el valor del parámetro lang podemos crear el siguiente módulo:

define([ "url-parameters" ], function(urlParams) {alert(urlParams.get("lang"));

});

Como se puede observar, se importa el módulo url-parameters en la variable urlParams y posteriormentese llama a la función urlParams.get pasando como parámetro el nombre del parámetro de la URL cuyo valorqueremos obtener. Esta función retornará null en caso de que el parámetro no exista.

Servicio hola mundo

El presente manual muestra cómo crear un nuevo servicio que nos devuelve un mensaje “hola mundo” en texto planoo XML, en función de un parámetro.

Page 41: curso desarrollo Documentation · El descriptor de la parte servidora es META-INF/web-fragment.xml y se encuentra en src/main/ resources. Sigue el estándar Servlet3 de Java y contiene

La primera tarea consiste en modificar el fichero web.xml para añadir un nuevo Servlet:

<servlet><servlet-name>holamundo-servlet</servlet-name><servlet-class>org.fao.unredd.portal.HolaMundoServlet</servlet-class>

</servlet><servlet-mapping>

<servlet-name>holamundo-servlet</servlet-name><url-pattern>/holamundo</url-pattern>

</servlet-mapping>

El código anterior asocia el servlet holamundo-servlet con la URL /holamundo y lo implementa con la claseorg.fao.unredd.portal.HolaMundoServlet. Ahora sólo es necesario implementar dicha clase:

package org.fao.unredd.portal;

import javax.servlet.http.HttpServlet;

public class HolaMundoServlet extends HttpServlet{

private static final long serialVersionUID = 1L;

}

La única particularidad del código anterior es que el servlet debe extender a javax.servlet.http.HttpServlet.

El atributo estático serialVersionUID no tiene otro objeto que evitar un warning y es totalmente irrelevante parael portal.

Si hemos hecho todo correctamente será posible, previo reinicio del servidor, acceder a la URL http://localhost:8080/unredd-portal/holamundo y obtener un error 405: método no permitido. Nótese que elmensaje es distinto si accedemos a una URL inexistente, como http://localhost:8080/unredd-portal/holamundonoexiste, donde obtenemos un 404: no encontrado.

Esto quiere decir que el servlet está bien instalado. Sólo hace falta implementar el método GET, que es el que se estápidiendo el navegador:

package org.fao.unredd.portal;

import java.io.IOException;

import javax.servlet.ServletException;import javax.servlet.http.HttpServlet;import javax.servlet.http.HttpServletRequest;import javax.servlet.http.HttpServletResponse;

public class HolaMundoServlet extends HttpServlet{

private static final long serialVersionUID = 1L;

@Overrideprotected void doGet(HttpServletRequest req, HttpServletResponse resp)

throws ServletException, IOException {}

}

Page 42: curso desarrollo Documentation · El descriptor de la parte servidora es META-INF/web-fragment.xml y se encuentra en src/main/ resources. Sigue el estándar Servlet3 de Java y contiene

Ahora, el servidor debe devolver una página en blanco, pero no debe dar un error. Se llega así al punto en el queleeremos el parámetro y en función de este devolveremos un XML o texto plano:

package org.fao.unredd.portal;

import java.io.IOException;

import javax.servlet.ServletException;import javax.servlet.http.HttpServlet;import javax.servlet.http.HttpServletRequest;import javax.servlet.http.HttpServletResponse;

public class HolaMundoServlet extends HttpServlet {

private static final long serialVersionUID = 1L;

@Overrideprotected void doGet(HttpServletRequest req, HttpServletResponse resp)

throws ServletException, IOException {String outputformat = req.getParameter("outputformat");resp.setCharacterEncoding("utf-8");if ("xml".equals(outputformat)) {

resp.setContentType("application/xml");resp.getWriter().write("<response>hola mundo</response>");

} else {resp.setContentType("text/plain");resp.getWriter().write("hola mundo");

}}

}

Servicio configuración (1 de 2)

En este manual se presenta la forma de implementar un servicio para visualizar algunos aspectos de la configuración, enconcreto la lista de módulos que componen el cliente. En una segunda parte se mostrará cómo realizar modificacionesa la configuración.

Como se prentende mostrar un servicio, es necesario crear un servlet modificando el web.xml:

<servlet><servlet-name>module-list-servlet</servlet-name><servlet-class>org.fao.unredd.portal.ModuleListServlet</servlet-class>

</servlet><servlet-mapping>

<servlet-name>module-list-servlet</servlet-name><url-pattern>/moduleList</url-pattern>

</servlet-mapping>

y creando el fichero Java correspondiente:

package org.fao.unredd.portal;

import java.io.IOException;

import javax.servlet.ServletException;import javax.servlet.http.HttpServlet;

Page 43: curso desarrollo Documentation · El descriptor de la parte servidora es META-INF/web-fragment.xml y se encuentra en src/main/ resources. Sigue el estándar Servlet3 de Java y contiene

import javax.servlet.http.HttpServletRequest;import javax.servlet.http.HttpServletResponse;

public class ModuleListServlet extends HttpServlet {private static final long serialVersionUID = 1L;

@Overrideprotected void doGet(HttpServletRequest req, HttpServletResponse resp)

throws ServletException, IOException {}

}

En este caso, el servlet debe acceder a la propiedad client.modules de portal.properties:

[...]info.layerUrl=http://demo1.geo-solutions.it/diss_geoserver/gwc/service/wmsclient.modules=layers,communication,iso8601,error-management,map,banner,toolbar,time-→˓slider,layer-list,info-control,info-dialog,center,zoom-bar,layer-list-selector,→˓active-layer-list,legend-button,legend-panelmap.centerLonLat=24, -4[...]

Dicha propiedad se puede obtener directamenten via el método getPropertyAsArray de la clase org.fao.unredd.portal.Config. Una instancia de esta clase se puede encontrar en el ServletContext y se puede recu-perar así:

Config config = (Config) getServletContext().getAttribute("config");

Utilizando la librería net.sf.json se puede codificar la lista de módulos como JSON y devolver el resultado:

package org.fao.unredd.portal;

import java.io.IOException;import java.io.PrintWriter;

import javax.servlet.ServletException;import javax.servlet.http.HttpServlet;import javax.servlet.http.HttpServletRequest;import javax.servlet.http.HttpServletResponse;

import net.sf.json.JSONArray;

public class ModuleListServlet extends HttpServlet {private static final long serialVersionUID = 1L;

@Overrideprotected void doGet(HttpServletRequest req, HttpServletResponse resp)

throws ServletException, IOException {Config config = (Config) getServletContext().getAttribute("config");

JSONArray json = new JSONArray();String[] modules = config.getPropertyAsArray("client.modules");for (String moduleName : modules) {

json.add(moduleName);}resp.setContentType("application/json");resp.setCharacterEncoding("utf8");

Page 44: curso desarrollo Documentation · El descriptor de la parte servidora es META-INF/web-fragment.xml y se encuentra en src/main/ resources. Sigue el estándar Servlet3 de Java y contiene

PrintWriter writer = resp.getWriter();writer.write(json.toString());

}

}

Servicio configuración (2 de 2)

En el manual anterior se muestra un servicio que devuelve un array JSON con los módulos activos en el cliente. Eneste manual se dará la opción de modificar esta lista de módulos eliminando un módulo mediante una petición web.

De nuevo se crea un servlet modificando el web.xml:

<servlet><servlet-name>module-removal-servlet</servlet-name><servlet-class>org.fao.unredd.portal.ModuleRemovalServlet</servlet-class>

</servlet><servlet-mapping>

<servlet-name>module-removal-servlet</servlet-name><url-pattern>/moduleRemoval</url-pattern>

</servlet-mapping>

y creando el fichero Java correspondiente:

package org.fao.unredd.portal;

import java.io.FileOutputStream;import java.io.IOException;import java.util.Properties;

import javax.servlet.ServletException;import javax.servlet.http.HttpServlet;import javax.servlet.http.HttpServletRequest;import javax.servlet.http.HttpServletResponse;

public class ModuleRemovalServlet extends HttpServlet {private static final long serialVersionUID = 1L;

@Overrideprotected void doGet(HttpServletRequest req, HttpServletResponse resp)

throws ServletException, IOException {}

}

En este caso, en lugar de obtener la propiedad client.modules del fichero portal.properties es necesariomodificarla, lo cual se consigue fácilmente reescribiendo el fichero completo:

[...]info.layerUrl=http://demo1.geo-solutions.it/diss_geoserver/gwc/service/wmsclient.modules=layers,communication,iso8601,error-management,map,banner,toolbar,time-→˓slider,layer-list,info-control,info-dialog,center,zoom-bar,layer-list-selector,→˓active-layer-list,legend-button,legend-panelmap.centerLonLat=24, -4[...]

Para ello son de utilidad:

Page 45: curso desarrollo Documentation · El descriptor de la parte servidora es META-INF/web-fragment.xml y se encuentra en src/main/ resources. Sigue el estándar Servlet3 de Java y contiene

• la clase java.util.Properties, capaz de leer y escribir ficheros de propiedades

• el método getPortalPropertiesFile de la clase org.fao.unredd.portal.Config, que de-vuelve la ubicación del fichero.

Para la lectura de un fichero de propiedades es necesario crear un InputStream que acceda al fichero:

Properties properties = new Properties();FileInputStream inputStream = new FileInputStream(

config.getPortalPropertiesFile());properties.load(inputStream);inputStream.close();

De forma análoga, la escritura requiere de un OutputStream:

FileOutputStream outputStream = new FileOutputStream(config.getPortalPropertiesFile());

properties.store(outputStream, null);outputStream.close();

Para la eliminación del módulo, se procederá a convertirlo en el un ArrayList, de fácil modificación, para luegoregenerar la lista de elementos:

String modules = properties.getProperty("client.modules");String[] moduleArray = modules.split(",");ArrayList<String> moduleList = new ArrayList<String>();Collections.addAll(moduleList, moduleArray);moduleList.remove(moduleName);properties.put("client.modules", StringUtils.join(moduleList, ','));

Finalmente el servlet quedaría así:

package org.fao.unredd.portal;

import java.io.FileInputStream;import java.io.FileOutputStream;import java.io.IOException;import java.util.ArrayList;import java.util.Collections;import java.util.Properties;

import javax.servlet.ServletException;import javax.servlet.http.HttpServlet;import javax.servlet.http.HttpServletRequest;import javax.servlet.http.HttpServletResponse;

import org.apache.commons.lang.StringUtils;

public class ModuleRemovalServlet extends HttpServlet {private static final long serialVersionUID = 1L;

@Overrideprotected void doGet(HttpServletRequest req, HttpServletResponse resp)

throws ServletException, IOException {Config config = (Config) getServletContext().getAttribute("config");

String moduleName = req.getParameter("moduleName");

Properties properties = new Properties();

Page 46: curso desarrollo Documentation · El descriptor de la parte servidora es META-INF/web-fragment.xml y se encuentra en src/main/ resources. Sigue el estándar Servlet3 de Java y contiene

// Lectura del ficheroFileInputStream inputStream = new FileInputStream(

config.getPortalPropertiesFile());properties.load(inputStream);inputStream.close();

// Eliminación del móduloString modules = properties.getProperty("client.modules");String[] moduleArray = modules.split(",");ArrayList<String> moduleList = new ArrayList<String>();Collections.addAll(moduleList, moduleArray);moduleList.remove(moduleName);properties.put("client.modules", StringUtils.join(moduleList, ','));

// Escritura del ficheroFileOutputStream outputStream = new FileOutputStream(

config.getPortalPropertiesFile());properties.store(outputStream, null);outputStream.close();

}

}

Nótese que no se devuelve ningún contenido pero que en cualquier caso, cuando el código del servlet se ejecuta sinerror, al cliente le llegará un código HTML “200 OK” indicando que la operación fue satisfactoria.

Comunicación con el cliente

El servlet anterior parte de la base de que las peticiones que se hagan van a ser satisfactorias, se va a eliminar elmódulo, etc. Pero en la realidad esto no es la norma general. ¿Qué sucede si la petición no incluye el parámetromoduleName? ¿Y si el valor no se corresponde con ninguno de los módulos existentes? ¿Qué pasa si el ficheroportal.properties ha sido eliminado?

El estándar HTML define una serie de códigos que pueden ayudar en la comunicación de estas condiciones excep-cionales:

• Ok (200): Ejecución satisfactoria.

• Bad Request (400): La petición no pudo ser entendida por el servidor. Aquí se puede indicar que el nombre delmódulo no se encontró o que no fue especificado el parámetro. Es posible acompañar el código con un mensajedescriptivo.

• Internal server error (500): Adecuado para indicar errores graves, irrecuperables, como un bug en el código oque el fichero portal.properties no existe!

La clase org.fao.unredd.portal.ErrorServlet es la encargada de gestionar los errores que se producenen el sistema. La única característica especial que tiene es que está implementada de tal manera que si se lanza unaexcepción org.fao.unredd.portal.StatusServletException, el código que se pasa como parámetroserá el código que se le devuelva al cliente. Además, es posible especificarle a esta instrucción el mensaje que seenviará al cliente.

Por ejemplo, en caso de que se desee enviar un código 400 cuando el parámetro moduleName no esté presente seprocedería así:

if (moduleName == null) {throw new StatusServletException(400, "El parámetro moduleName es obligatorio

→˓");

Page 47: curso desarrollo Documentation · El descriptor de la parte servidora es META-INF/web-fragment.xml y se encuentra en src/main/ resources. Sigue el estándar Servlet3 de Java y contiene

}

El segundo parámetro se enviaría codificado en un documento JSON, para que el cliente que realice la llamada puedaleerlo y presentarlo al usuario convenientemente. Así pues, si se accede a la URL http://localhost:8080/unredd-portal/moduleRemoval (sin el parámetro) se obtendrá como resultado un código 400 y el siguientedocumento:

{"message": "El parámetro moduleName es obligatorio"

}

Teniendo esto en cuenta, el servlet anterior se podría escribir así:

package org.fao.unredd.portal;

import java.io.FileInputStream;import java.io.FileOutputStream;import java.io.IOException;import java.util.ArrayList;import java.util.Collections;import java.util.Properties;

import javax.servlet.ServletException;import javax.servlet.http.HttpServlet;import javax.servlet.http.HttpServletRequest;import javax.servlet.http.HttpServletResponse;

import org.apache.commons.lang.StringUtils;

public class ModuleRemovalServlet extends HttpServlet {private static final long serialVersionUID = 1L;

@Overrideprotected void doGet(HttpServletRequest req, HttpServletResponse resp)

throws ServletException, IOException {Config config = (Config) getServletContext().getAttribute("config");

String moduleName = req.getParameter("moduleName");if (moduleName == null) {

throw new StatusServletException(400,"El parámetro moduleName es obligatorio");

}

Properties properties = new Properties();

// Lectura del ficherotry {

FileInputStream inputStream = new FileInputStream(config.getPortalPropertiesFile());

properties.load(inputStream);inputStream.close();

} catch (IOException e) {throw new StatusServletException(500,

"Error grave en el servidor. Contacte al→˓administrador");

}

// Eliminación del módulo

Page 48: curso desarrollo Documentation · El descriptor de la parte servidora es META-INF/web-fragment.xml y se encuentra en src/main/ resources. Sigue el estándar Servlet3 de Java y contiene

String modules = properties.getProperty("client.modules");String[] moduleArray = modules.split(",");ArrayList<String> moduleList = new ArrayList<String>();Collections.addAll(moduleList, moduleArray);if (!moduleList.remove(moduleName)) {

throw new StatusServletException(400,"El módulo especificado no existe");

}properties.put("client.modules", StringUtils.join(moduleList, ','));

// Escritura del ficherotry {

FileOutputStream outputStream = new FileOutputStream(config.getPortalPropertiesFile());

properties.store(outputStream, null);outputStream.close();

} catch (IOException e) {throw new StatusServletException(500,

"Error grave en el servidor. Contacte al→˓administrador");

}}

}

Decodificación en el cliente

Por último, cabe destacar que el módulo communication.js escucha un evento ajax que permite realizar lla-madas a nuestro servidor y que en caso de error leería el atributo message del documento JSON generado y lomostraría al usuario.

El siguiente módulo hace la petición para eliminar el módulo banner cuando se pulsa un botón:

define([ "message-bus", "botonera" ], function(bus, botonera) {

botonera.newButton("remove banner", function() {bus.send("ajax", {

url : "moduleRemoval?moduleName=banner",success : function(indicators, textStatus, jqXHR) {

alert("módulo eliminado con éxito");},errorMsg : "No se pudo eliminar el módulo"

});});

});

La primera vez debe funcionar correctamente, pero la segunda debe fallar porque el módulo banner ya no está presente.

Como la comunicación se realiza via el módulo communication con el evento ajax, en caso de error el propiomódulo lee el mensaje y lo muestra al usuario.

Conexión a base de datos

Page 49: curso desarrollo Documentation · El descriptor de la parte servidora es META-INF/web-fragment.xml y se encuentra en src/main/ resources. Sigue el estándar Servlet3 de Java y contiene

Conexión a base de datos en Java

Cuando queremos conectar a la base de datos desde un servicio Java tenemos que utilizar la API JDBC (Java DataBaseConnectivity) de Java.

En general, el código para conectar a una base de datos en Java es el siguiente:

Class.forName("org.postgresql.Driver");Connection connection = DriverManager.getConnection(

"jdbc:postgresql://hostname:port/dbname","username", "password");...connection.close();

Warning: En el código anterior estamos conectando a una base de datos PostgreSQL, para lo cual in-stanciamos el driver org.postgresql.Driver y conectamos usando la URL propia de PostgreSQLjdbc:postgresql://hostname:port. Estos dos aspectos cambiarán en función del tipo de base de datosa la que estemos conectando.

Primero, instanciamos el driver para que se autoregistre en el DriverManager y poder invocar después el métodogetConnection para obtener la conexión. Por último, es necesario cerrar la conexión.

Por medio del objeto de tipo Connection podremos obtener instancias de Statement, con las que se puedenenviar instrucciones SQL al servidor de base de datos.

Conexión a base de datos desde una aplicación web

Partimos de un servicio configurado de esta manera en el descriptor de despliegue:

<!-- database example --><servlet>

<servlet-name>example-db</servlet-name><servlet-class>org.fao.unredd.portal.ExampleDBServlet</servlet-class>

</servlet><servlet-mapping>

<servlet-name>example-db</servlet-name><url-pattern>/example-db</url-pattern>

</servlet-mapping>

y que implementará su funcionalidad en el método GET:

package org.fao.unredd.portal;

import java.io.IOException;

import javax.servlet.ServletException;import javax.servlet.http.HttpServlet;import javax.servlet.http.HttpServletRequest;import javax.servlet.http.HttpServletResponse;

public class ExampleDBServlet extends HttpServlet {private static final long serialVersionUID = 1L;

@Overrideprotected void doGet(HttpServletRequest req, HttpServletResponse resp)

throws ServletException, IOException {

Page 50: curso desarrollo Documentation · El descriptor de la parte servidora es META-INF/web-fragment.xml y se encuentra en src/main/ resources. Sigue el estándar Servlet3 de Java y contiene

//...}

}

Aunque sería posible poner el código anterior en el método doGet, no es recomendable debido a que la creación deuna conexión es siempre un proceso costoso.

Configuración conexión en el descriptor de despliegue

Para evitar crear una conexión cada vez, tendremos que configurar el contenedor de aplicaciones, Tomcat en esteejemplo, para que gestione las conexiones por nosotros. Para ello, tenemos que darle a Tomcat la información necesariapara conectar modificando dos ficheros.

El primero es el fichero context.xml que existe en el directorio de configuración del servidor conf. Ahí declararemosun recurso llamado “jdbc/mis-conexiones” que incluirá todos los datos necesarios para conectar: url, usuario, etc.:

<Resource name="jdbc/mis-conexiones" auth="Container" type="javax.sql.DataSource"driverClassName="org.postgresql.Driver" url="jdbc:postgresql://192.168.0.

→˓18:5432/geoserverdata"username="nfms" password="unr3dd" maxActive="20" maxIdle="10"maxWait="-1" />

El otro fichero a modificar es el descriptor de despliegue web-fragment.xml del plugin que estamos desarrollando(ver Estructura proyectos plugin), donde añadiremos una referencia al recurso anterior, “jdbc/mis-conexiones”:

<resource-ref><description>Application database</description><res-ref-name>jdbc/mis-conexiones</res-ref-name><res-type>javax.sql.DataSource</res-type><res-auth>Container</res-auth>

</resource-ref>

Note: Al ejecutar una aplicación en Tomcat desde Eclipse se crea un proyecto “Servers”, que contiene una entradapara el servidor que estamos utilizando y que en el caso de Tomcat incluye los ficheros de configuración, entre otros elfichero context.xml donde configuramos el recurso. La manera más sencilla de acceder al fichero context delservidor es a través de dicho proyecto.

Una vez estos ficheros han sido modificados ya no tenemos que preocuparnos de realizar la conexión porque Tomcatlas gestiona por nosotros. Pero, ¿cómo podemos obtener una de estas conexiones gestionadas por Tomcat?

El código Java cambia ligeramente, ya que ahora se obtiene un objeto de tipo java.sql.DataSource que es elque nos proporciona las conexiones:

InitialContext context;DataSource dataSource;try {

context = new InitialContext();dataSource = (DataSource) context

.lookup("java:/comp/env/jdbc/mis-conexiones");} catch (NamingException e) {

throw new ServletException("Problema en la configuración");}try {

Connection connection = dataSource.getConnection();

Page 51: curso desarrollo Documentation · El descriptor de la parte servidora es META-INF/web-fragment.xml y se encuentra en src/main/ resources. Sigue el estándar Servlet3 de Java y contiene

// ...connection.close();

} catch (SQLException e) {throw new ServletException("No se pudo obtener una conexión");

}

try {context.close();

} catch (NamingException e) {// ignore

}

Si sutituímos la línea que contiene los puntos suspensivos por código que haga algo más interesante con la conexión,podemos devolver un JSON con el array de nombres que haya en una tabla:

package org.fao.unredd.portal;

import java.io.IOException;import java.sql.Connection;import java.sql.ResultSet;import java.sql.SQLException;import java.sql.Statement;import java.util.ArrayList;

import javax.naming.InitialContext;import javax.naming.NamingException;import javax.servlet.ServletException;import javax.servlet.http.HttpServlet;import javax.servlet.http.HttpServletRequest;import javax.servlet.http.HttpServletResponse;import javax.sql.DataSource;

import net.sf.json.JSONSerializer;

public class ExampleDBServlet extends HttpServlet {private static final long serialVersionUID = 1L;

@Overrideprotected void doGet(HttpServletRequest req, HttpServletResponse resp)

throws ServletException, IOException {InitialContext context;DataSource dataSource;try {

context = new InitialContext();dataSource = (DataSource) context

.lookup("java:/comp/env/jdbc/mis-conexiones");} catch (NamingException e) {

throw new ServletException("Problema en la configuración");}

ArrayList<String> provincias = new ArrayList<String>();try {

Connection connection = dataSource.getConnection();Statement statement = connection.createStatement();ResultSet result = statement

.executeQuery("SELECT name_1 FROM gis.arg_adm1→˓");

while (result.next()) {

Page 52: curso desarrollo Documentation · El descriptor de la parte servidora es META-INF/web-fragment.xml y se encuentra en src/main/ resources. Sigue el estándar Servlet3 de Java y contiene

provincias.add(result.getString("name_1"));}

resp.setContentType("application/json");JSONSerializer.toJSON(provincias).write(resp.getWriter());

connection.close();} catch (SQLException e) {

throw new ServletException("No se pudo obtener una conexión",→˓e);

}

try {context.close();

} catch (NamingException e) {throw new ServletException("No se pudo liberar el recurso");

}}

}

La clase DBUtils

Conexiones existentes

Como se puede ver en http://nfms4redd.org/tmp/ref/install/portal.html, el portal incorpora ya una conexión a una basede datos que se deberá configurar a nivel del contenedor de aplicaciones (Tomcat).

La referencia a esa conexión está configurada en el web-fragment.xml de core, que todo plugin debe incluircomo dependencia (y por tanto, todo plugin puede utilizar):

<resource-ref><description>Application database</description><res-ref-name>jdbc/unredd-portal</res-ref-name><res-type>javax.sql.DataSource</res-type><res-auth>Container</res-auth>

</resource-ref>

Como se puede observar, el nombre es “jdbc/unredd-portal” por lo que con esta información, y usando la clase DBUtilsvista anteriormente, sería posible reescribir el servlet anterior de la siguiente manera y sin tocar ningún fichero deconfiguración:

package org.fao.unredd.portal;

import java.io.IOException;import java.sql.Connection;import java.sql.ResultSet;import java.sql.SQLException;import java.sql.Statement;import java.util.ArrayList;

import javax.naming.InitialContext;import javax.naming.NamingException;import javax.servlet.ServletException;import javax.servlet.http.HttpServlet;import javax.servlet.http.HttpServletRequest;

Page 53: curso desarrollo Documentation · El descriptor de la parte servidora es META-INF/web-fragment.xml y se encuentra en src/main/ resources. Sigue el estándar Servlet3 de Java y contiene

import javax.servlet.http.HttpServletResponse;import javax.sql.DataSource;

import net.sf.json.JSONSerializer;

public class ExampleDBServlet extends HttpServlet {private static final long serialVersionUID = 1L;

@Overrideprotected void doGet(HttpServletRequest req, HttpServletResponse resp)

throws ServletException, IOException {

final ArrayList<String> provincias = new ArrayList<String>();

try {DBUtils.processConnection("unredd-portal", new DBUtils.

→˓DBProcessor() {

@Overridepublic void process(Connection connection) throws

→˓SQLException {Statement statement = connection.

→˓createStatement();ResultSet result = statement

.executeQuery("SELECT name_1→˓FROM gis.arg_adm1");

while (result.next()) {provincias.add(result.getString("name_

→˓1"));}

}});

} catch (PersistenceException e) {throw new ServletException("No se pudo obtener una conexión",

→˓e);}

resp.setContentType("application/json");JSONSerializer.toJSON(provincias).write(resp.getWriter());

}

}

Cómo crear un nuevo plugin

Cuando queremos realizar una implementación que se pueda utilizar fácilmente en portales existentes hay que empa-quetar las funcionalidades en cuestión en un plugin.

Creación del proyecto para el plugin

Para ello hay que crear un nuevo proyecto con Maven. Escribiendo desde la línea de comandos:

$ mvn archetype:generate

Page 54: curso desarrollo Documentation · El descriptor de la parte servidora es META-INF/web-fragment.xml y se encuentra en src/main/ resources. Sigue el estándar Servlet3 de Java y contiene

Dicho comando inicia un asistente que nos permitirá crear un proyecto fácilmente. Primero nos preguntará por el tipode proyecto y versión del plugin de Maven. Nos valen los valores por defecto por lo que sólo pulsaremos INTRO unpar de veces:

Choose a number or apply filter (format: [groupId:]artifactId, case sensitive→˓contains): 497: INTRO

Choose org.apache.maven.archetypes:maven-archetype-quickstart version:1: 1.0-alpha-12: 1.0-alpha-23: 1.0-alpha-34: 1.0-alpha-45: 1.06: 1.1Choose a number: 6: INTRO

A continuación nos preguntará por el groupId y artifactId, que no es otra cosa que una manera de identificar el plugin.En este ejemplo usaremos respectivamente “org.fao.unredd” y “holamundo”:

Define value for property 'groupId': : org.fao.unreddDefine value for property 'artifactId': : holamundo

Luego nos preguntará versión y paquete de Java que queremos crear. Podemos aceptar las opciones por defectopulsando INTRO en cada caso:

Define value for property 'version': 1.0-SNAPSHOT: : INTRODefine value for property 'package': org.fao.unredd: : INTRO

Por último nos pedirá confirmación. Pulsaremos INTRO de nuevo:

Confirm properties configuration:groupId: org.fao.unreddartifactId: holamundoversion: 1.0-SNAPSHOTpackage: org.fao.unreddY: :

[INFO] ----------------------------------------------------------------------------[INFO] Using following parameters for creating project from Old (1.x) Archetype:→˓maven-archetype-quickstart:1.1[INFO] ----------------------------------------------------------------------------[INFO] Parameter: groupId, Value: org.fao.unredd[INFO] Parameter: packageName, Value: org.fao.unredd[INFO] Parameter: package, Value: org.fao.unredd[INFO] Parameter: artifactId, Value: holamundo[INFO] Parameter: basedir, Value: /tmp[INFO] Parameter: version, Value: 1.0-SNAPSHOT[INFO] project created from Old (1.x) Archetype in dir: /tmp/holamundo[INFO] ------------------------------------------------------------------------[INFO] BUILD SUCCESS[INFO] ------------------------------------------------------------------------[INFO] Total time: 4:16.288s[INFO] Finished at: Wed Nov 05 10:10:44 CET 2014[INFO] Final Memory: 15M/108M[INFO] ------------------------------------------------------------------------

Tras este proceso, Maven nos reporta BUILD SUCCESS, que quiere decir que el proyecto fue creado con éxito y estáen un directorio con el mismo nombre que el artifactId, es decir, “holamundo”.

Page 55: curso desarrollo Documentation · El descriptor de la parte servidora es META-INF/web-fragment.xml y se encuentra en src/main/ resources. Sigue el estándar Servlet3 de Java y contiene

Configuración en Eclipse

Para empezar a escribir código, tendremos que importar el proyecto en algún entorno de desarrollo, como Eclipse.Para ello hacemos clic con el botón derecho en el Project Explorer > Import > Import...

En el diálogo que aparece hay que decirle a Eclipse que el proyecto que queremos importar es un proyecto Maven.Así, habrá que seleccionar Maven > Existing Maven Projects

A continuación hay que seleccionar el directorio que Maven acaba de crear y finalizar el asistente.

Para finalizar la importación en eclipse tendremos que crear el directorio src/main/resources que no vienecreado por defecto pero que es donde se incluirán todos los desarrollos para la parte cliente (ver Estructura proyectosplugin). Una vez creado habrá que hacer clic derecho en el proyecto y seleccionar Maven > Update project, tras locual src/main/resources aparecerá como un directorio de código:

Page 56: curso desarrollo Documentation · El descriptor de la parte servidora es META-INF/web-fragment.xml y se encuentra en src/main/ resources. Sigue el estándar Servlet3 de Java y contiene

Desarrollo de un módulo

Ahora que tenemos el proyecto en Eclipse podemos crear un módulo hola mundo. Los módulos tienen que estar en eldirectorio nfms/modules en src/main/resources por lo que tendremos que crear ese directorio. Dentro deese directorio podemos crear el módulo, como se ve en la imagen:

Reutilización del módulo

Por último, queremos que nuestro plugin se incluya en alguna aplicación, por ejemplo demo. Esto es tan fácil comoincluir nuestro plugin como dependencia de demo. Para ello abrimos el pom.xml de demo e incluimos una sección<dependency> adicional con los datos que introdujimos en nuestro plugin al inicio del manual:

Page 57: curso desarrollo Documentation · El descriptor de la parte servidora es META-INF/web-fragment.xml y se encuentra en src/main/ resources. Sigue el estándar Servlet3 de Java y contiene

Ahora sólo queda ejecutar demo en un servidor Tomcat y ver el resultado:

Page 58: curso desarrollo Documentation · El descriptor de la parte servidora es META-INF/web-fragment.xml y se encuentra en src/main/ resources. Sigue el estándar Servlet3 de Java y contiene

Warning: Es posible que el plugin no aparezca inicialmente por problemas de refresco, se recomienda clicar conel botón derecho uno de los proyectos y seleccionar Maven > Update project seleccionando en el diálogo queaparece todos los proyectos implicados (plugins y aplicación).

Cómo crear una nueva aplicación

Creación del proyecto

Cuando el objetivo es crear una aplicación que agrupe uno o más plugins existentes tenemos que crear un tipo deproyecto distinto (ver Estructura proyectos aplicación para información sobre los artefactos que tiene que tener unproyecto tal).

Al igual que en el caso de crear un nuevo plugin, lo primero es crear un nuevo proyecto maven. El proceso es idénticoa Creación del proyecto para el plugin sólo que utilizaremos “mi-app” como artifactId.

La configuración de dicho proyecto en Eclipse es también realizada de forma idéntica al caso de los plugins: Configu-ración en Eclipse.

Page 59: curso desarrollo Documentation · El descriptor de la parte servidora es META-INF/web-fragment.xml y se encuentra en src/main/ resources. Sigue el estándar Servlet3 de Java y contiene

Selección de los plugins que componen la aplicación

Una vez el proyecto está creado, es necesario especificar los plugins que van a utilizarse en la aplicación. Para ellohabrá que modificar el fichero pom.xml incluyendo los elementos <dependency> de core, el cargador de plugins,y de base, plugin principal:

<dependency><groupId>org.fao.unredd</groupId><artifactId>core</artifactId><version>3.1-SNAPSHOT</version>

</dependency><dependency>

<groupId>org.fao.unredd</groupId><artifactId>base</artifactId><version>3.1-SNAPSHOT</version>

</dependency>

Además, para que Maven pueda descargar estas dependencias, hay que especificar algunos repositorios de librerías:

<pluginRepositories><pluginRepository>

<id>nfms4redd</id><name>nfms4redd maven repository</name><url>http://maven.nfms4redd.org/</url>

</pluginRepository></pluginRepositories><repositories>

<repository><id>osgeo</id><name>Open Source Geospatial Foundation Repository</name><url>http://download.osgeo.org/webdav/geotools/</url>

</repository><repository>

<id>nfms4redd</id><name>nfms4redd maven repository</name><url>http://maven.nfms4redd.org/</url>

</repository><repository>

<id>EclipseLink</id><url>http://download.eclipse.org/rt/eclipselink/maven.repo</url>

</repository><repository>

<id>geosolutions</id><name>GeoSolutions public maven repository</name><url>http://maven.geo-solutions.it/</url>

</repository></repositories>

El fichero pom.xml quedaría de la siguiente manera:

<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>org.fao.unredd</groupId><artifactId>mi-app</artifactId>

Page 60: curso desarrollo Documentation · El descriptor de la parte servidora es META-INF/web-fragment.xml y se encuentra en src/main/ resources. Sigue el estándar Servlet3 de Java y contiene

<version>1.0-SNAPSHOT</version><packaging>war</packaging>

<name>mi-app</name><url>http://maven.apache.org</url>

<properties><project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>

</properties><pluginRepositories>

<pluginRepository><id>nfms4redd</id><name>nfms4redd maven repository</name><url>http://maven.nfms4redd.org/</url>

</pluginRepository></pluginRepositories><repositories>

<repository><id>osgeo</id><name>Open Source Geospatial Foundation Repository</name><url>http://download.osgeo.org/webdav/geotools/</url>

</repository><repository>

<id>nfms4redd</id><name>nfms4redd maven repository</name><url>http://maven.nfms4redd.org/</url>

</repository><repository>

<id>EclipseLink</id><url>http://download.eclipse.org/rt/eclipselink/maven.repo</

→˓url></repository><repository>

<id>geosolutions</id><name>GeoSolutions public maven repository</name><url>http://maven.geo-solutions.it/</url>

</repository></repositories>

<dependencies><dependency>

<groupId>junit</groupId><artifactId>junit</artifactId><version>3.8.1</version><scope>test</scope>

</dependency><dependency>

<groupId>org.fao.unredd</groupId><artifactId>core</artifactId><version>3.1-SNAPSHOT</version>

</dependency><dependency>

<groupId>org.fao.unredd</groupId><artifactId>base</artifactId><version>3.1-SNAPSHOT</version>

</dependency></dependencies>

</project>

Page 61: curso desarrollo Documentation · El descriptor de la parte servidora es META-INF/web-fragment.xml y se encuentra en src/main/ resources. Sigue el estándar Servlet3 de Java y contiene

Ejecución de la aplicación desde eclipse

Para la ejecución del proyecto como aplicación web dentro de Eclipse tenemos que realizar dos configuraciones adi-cionales.

Lo primero es configurar el proyecto para que Eclipse entienda que es una aplicación web. Para ello hay que modificarel elemento packaging del fichero mi-app/pom.xml como se puede ver en el listado anterior correspondiente alpom.xml, estableciendo el valor a “war”. Tras editar el fichero habrá que clicar en el proyecto con botón derecho yseleccionar Maven > Update project.

A continuación es necesario proporcionar a la aplicación un directorio de configuración, que proporciona a la apli-cación información sobre las capas del mapa, etc. Podemos tomar el que hay en demo/src/main/webapp/WEB-INF/default_config y copiarlo en mi-app/src/main/webapp/WEB-INF/default_config.

Por último, para ejecutar la aplicación tendremos que operar como se muestra con demo en el punto Ejecución delportal en tomcat desde Eclipse, pero con el proyecto mi-app que acabamos de crear.

Empaquetado

Warning: Para que el proceso funcione es necesario que exista el descriptor de despliegue de aplicaciones JEE, elfichero src/main/webapp/WEB-INF/web.xml. Bastaría con crear ese fichero con el siguiente contenido:

<?xml version="1.0" encoding="UTF-8"?><web-app version="3.0" xmlns="http://java.sun.com/xml/ns/javaee"

xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/

→˓xml/ns/javaee/web-app_3_0.xsd">

</web-app>

Para realizar el empaquetado tenemos que ejecutar el comando mvn package en el directorio mi-app. Esto tam-bién se puede hacer desde Eclipse haciendo clic con el botón derecho en el proyecto mi-app y seleccionando RunAs > Maven Build. En la ventana que aparece hay que especificar “package” en “Goals”, como se puede ver en lasiguiente imagen:

Page 62: curso desarrollo Documentation · El descriptor de la parte servidora es META-INF/web-fragment.xml y se encuentra en src/main/ resources. Sigue el estándar Servlet3 de Java y contiene

Al pinchar en el botón Run, Maven se ejecutará y mostrará por la consola el resultado. Cuando el proceso se terminacon éxito se obtiene el fichero .war en el directorio target del proyecto y un mensaje similar a éste:

[INFO][INFO] --- maven-war-plugin:2.2:war (default-war) @ mi-app ---[INFO] Packaging webapp[INFO] Assembling webapp [mi-app] in [/home/fergonco/temp/howtoworkspace/mi-app/→˓target/mi-app-1.0-SNAPSHOT]

Page 63: curso desarrollo Documentation · El descriptor de la parte servidora es META-INF/web-fragment.xml y se encuentra en src/main/ resources. Sigue el estándar Servlet3 de Java y contiene

[INFO] Processing war project[INFO] Copying webapp resources [/home/fergonco/temp/howtoworkspace/mi-app/src/main/→˓webapp][INFO] Webapp assembled in [172 msecs][INFO] Building war: /home/fergonco/temp/howtoworkspace/mi-app/target/mi-app-1.0-→˓SNAPSHOT.war[INFO] WEB-INF/web.xml already added, skipping[INFO] ------------------------------------------------------------------------[INFO] BUILD SUCCESS[INFO] ------------------------------------------------------------------------[INFO] Total time: 3.299s[INFO] Finished at: Thu Nov 06 11:40:09 CET 2014[INFO] Final Memory: 12M/172M[INFO] ------------------------------------------------------------------------

Warning: Si la aplicación tiene como dependencia un plugin que hemos desarrollado nosotros, es necesario quedicho plugin esté disponible para Maven, lo cual se consigue ejecutando el goal “install” en dicho plugin.

Empaquetado con optimización

Cuando una aplicación tiene muchos módulos y librerías Javascript, hojas de estilo CSS, etc. la carga puede ser unpoco lenta. Para acelerar esto se puede configurar Maven para que realice un proceso de optimización y combine todosestos ficheros en uno sólo.

Primero, hay que introducir la siguiente sección en el pom.xml de mi-app tras la sección <dependencies></dependencies>:

<build><plugins>

<plugin><groupId>org.apache.maven.plugins</groupId><artifactId>maven-dependency-plugin</artifactId><version>2.8</version><executions>

<execution><id>unpack-dependencies</id><phase>prepare-package</phase><goals>

<goal>unpack-dependencies</goal></goals><configuration>

<outputDirectory>${project.build.→˓directory}/requirejs</outputDirectory>

</configuration></execution>

</executions></plugin><plugin>

<groupId>org.fao.unredd</groupId><artifactId>jwebclient-analyzer-maven-plugin</artifactId><version>4.0.1</version><executions>

<execution><id>generate-buildconfig</id><phase>prepare-package</phase>

Page 64: curso desarrollo Documentation · El descriptor de la parte servidora es META-INF/web-fragment.xml y se encuentra en src/main/ resources. Sigue el estándar Servlet3 de Java y contiene

<goals><goal>generate-buildconfig</goal>

</goals><configuration>

<mainTemplate>${project.build.→˓directory}/requirejs/main.js</mainTemplate>

<webClientFolder>${project.build.→˓directory}/requirejs</webClientFolder>

<buildconfigOutputPath>${project.→˓build.directory}/buildconfig.js</buildconfigOutputPath>

<mainOutputPath>${project.build.→˓directory}/requirejs/nfms/modules/main.js</mainOutputPath>

</configuration></execution>

</executions></plugin><plugin>

<groupId>ro.isdc.wro4j</groupId><artifactId>wro4j-maven-plugin</artifactId><version>1.7.6</version><executions>

<execution><phase>prepare-package</phase><goals>

<goal>run</goal></goals>

</execution></executions><configuration>

<wroManagerFactory>ro.isdc.wro.maven.plugin.manager.→˓factory.ConfigurableWroManagerFactory</wroManagerFactory>

<extraConfigFile>${basedir}/src/main/config/wro.→˓properties</extraConfigFile>

<targetGroups>portal-style</targetGroups><minimize>true</minimize><contextFolder>${basedir}/target/requirejs/nfms/</

→˓contextFolder><destinationFolder>${basedir}/src/main/webapp/

→˓optimized/</destinationFolder><wroFile>${basedir}/src/main/config/wro.xml</wroFile>

</configuration></plugin><plugin>

<groupId>com.github.bringking</groupId><artifactId>requirejs-maven-plugin</artifactId><version>2.0.4</version><executions>

<execution><phase>prepare-package</phase><goals>

<goal>optimize</goal></goals>

</execution></executions><configuration>

<!-- optional path to a nodejs executable --><!--<nodeExecutable> --><!--/opt/nodejs/node -->

Page 65: curso desarrollo Documentation · El descriptor de la parte servidora es META-INF/web-fragment.xml y se encuentra en src/main/ resources. Sigue el estándar Servlet3 de Java y contiene

<!--</nodeExecutable> --><!-- path to optimizer json config file --><configFile>${project.build.directory}/buildconfig.js

→˓</configFile><fillDepsFromFolder>${project.build.directory}/

→˓requirejs/nfms/modules</fillDepsFromFolder><!-- optional path to optimizer executable --><!--<optimizerFile> --><!--${basedir}/src/main/scripts/r.js --><!--</optimizerFile> --><!-- optional parameters to optimizer executable --><optimizerParameters>

<parameter>optimize=uglify</parameter><!--<parameter>baseUrl=${baseDir}</parameter>

→˓--></optimizerParameters><!-- Whether or not to process configFile with maven

→˓filters. If youuse this option, some options in your

→˓configFile must resolve to absolutepaths (see below) -->

<filterConfig>true

</filterConfig><!-- Skip requirejs optimization if true --><skip>

false</skip>

</configuration></plugin>

</plugins></build>

Esta configuración hace referencia a dos ficheros existentes en el directorio src/main/config, wro.properties y wro.xml. El contenido de wro.properties será:

preProcessors=cssDataUri,cssImport,semicolonAppender,cssMinJawrpostProcessors=

Mientras que para wro.xml pondremos:

<?xml version="1.0" encoding="UTF-8"?><groups xmlns="http://www.isdc.ro/wro"

xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://www.isdc.ro/wro wro.xsd">

<group name="portal-style"><css>/modules/**.css</css><css>/styles/**.css</css>

</group>

</groups>

Una vez realizada esta configuración, podemos generar el WAR de nuevo. Aparentemente este WAR es igual que el an-terior, pero a diferencia de aquél, justo antes de empaquetar se habrán generado dos ficheros: src/main/webapp/optimized/portal.js y src/main/webapp/optimized/portal-style.css, que incluyen respecti-vamente todo el código Javascript y todos los estilos de los plugins usados por la aplicación.

Page 66: curso desarrollo Documentation · El descriptor de la parte servidora es META-INF/web-fragment.xml y se encuentra en src/main/ resources. Sigue el estándar Servlet3 de Java y contiene

Cuando despleguemos tal WAR, podremos seleccionar poniendo la variable de entorno MINIFIED_JS a “true” elmodo optimizado, que cargará el portal bastante más rápido.

Cómo dar licencia libre a un plugin

Una vez hemos realizado nuestro plugin y tenemos claro que queremos publicarlo con una licencia para que la gentelo reutilice de forma libre, tenemos que seguir los siguientes pasos:

1. Elegir una licencia libre

2. Aplicar la licencia a nuestro proyecto

Elegir una licencia libre

Existen muchísimas licencias de software libre por lo que para este tutorial nos limitaremos al ámbito de las másutilizadas. Además, para facilitar la aplicación de la licencia nos limitaremos también a las soportadas por elmaven-license-plugin.

Para obtener un listado de las licencias basta con ejecutar el objetivo license-list del plugin Maven license:

$ mvn license:license-list[INFO] Scanning for projects...[INFO][INFO] ------------------------------------------------------------------------[INFO] Building Maven Stub Project (No POM) 1[INFO] ------------------------------------------------------------------------[INFO][INFO] --- license-maven-plugin:1.8:license-list (default-cli) @ standalone-pom ---[WARNING] File encoding has not been set, using platform encoding UTF-8, i.e. build→˓is platform dependent![INFO] Available licenses :

* agpl_v3 : GNU Affero General Public License (AGPL) version 3.0

* apache_v2 : Apache License version 2.0

* bsd_2 : BSD 2-Clause License

* bsd_3 : BSD 3-Clause License

* cddl_v1 : COMMON DEVELOPMENT AND DISTRIBUTION LICENSE (CDDL) Version 1.0

* epl_only_v1 : Eclipse Public License - v 1.0

* epl_v1 : Eclipse Public + Distribution License - v 1.0

* eupl_v1_1 : European Union Public License v1.1

* fdl_v1_3 : GNU Free Documentation License (FDL) version 1.3

* gpl_v1 : GNU General Public License (GPL) version 1.0

* gpl_v2 : GNU General Public License (GPL) version 2.0

* gpl_v3 : GNU General Public License (GPL) version 3.0

* lgpl_v2_1 : GNU General Lesser Public License (LGPL) version 2.1

* lgpl_v3 : GNU General Lesser Public License (LGPL) version 3.0

* mit : MIT-License

[INFO] ------------------------------------------------------------------------[INFO] BUILD SUCCESS[INFO] ------------------------------------------------------------------------[INFO] Total time: 2.228s[INFO] Finished at: Thu Mar 05 06:46:17 CET 2015[INFO] Final Memory: 10M/144M[INFO] ------------------------------------------------------------------------

Page 67: curso desarrollo Documentation · El descriptor de la parte servidora es META-INF/web-fragment.xml y se encuentra en src/main/ resources. Sigue el estándar Servlet3 de Java y contiene

Cada licencia tiene unas particularidades, pero está fuera del ámbito de este tutorial analizar estas diferencias. Para elsiguiente punto, aplicaremos a nuestro proyecto la licencia GPLv3 (GNU General Public License (GPL) version 3.0),que es la misma que tiene el portal de FAO.

Aplicar la licencia a nuestro proyecto

Ahora que tenemos nuestro proyecto y sabemos la licencia que queremos que tenga, GPLv3, ¿cómo se la aplicamos?

Para aplicar esta licencia tenemos que incluir en la raíz de nuestro proyecto un fichero LICENSE.txt con el texto de lalicencia y en cada fichero de código, una cabecera similar a la siguiente:

/** NOMBRE DEL PROYECTO

** Copyright (C) AÑO ORGANIZACIÓN

** This program is free software: you can redistribute it and/or modify

* it under the terms of the GNU General Public License as

* published by the Free Software Foundation, either version 3 of the

* License, or (at your option) any later version.

** This program is distributed in the hope that it will be useful,

* but WITHOUT ANY WARRANTY; without even the implied warranty of

* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the

* GNU General Public License for more details.

** You should have received a copy of the GNU General Public

* License along with this program. If not, see

* <http://www.gnu.org/licenses/gpl-3.0.html>.

**/

Esto, que sería bastante tedioso de realizar a mano, lo hace de forma automática el plugin license de Maven, peropara que pueda llevar estas acciones a cabo es necesario configurarlo en el pom.xml con los datos propios de nuestroproyecto, que se usarán para personalizar el texto de la cabecera.

Vamos a suponer que partimos de un pom.xml tan sencillo como éste:

<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>mi.organizacion</groupId><artifactId>pluginParaX</artifactId><version>1.0-SNAPSHOT</version><packaging>jar</packaging>

<name>PluginParaX</name><url>http://maven.apache.org</url>

<properties><project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>

</properties>

<dependencies><dependency>

Page 68: curso desarrollo Documentation · El descriptor de la parte servidora es META-INF/web-fragment.xml y se encuentra en src/main/ resources. Sigue el estándar Servlet3 de Java y contiene

<groupId>junit</groupId><artifactId>junit</artifactId><version>3.8.1</version><scope>test</scope>

</dependency></dependencies>

</project>

Para configurar nuestro plugin tendremos que añadir una sección build/plugins, dentro de la cual pondremos laconfiguración del plugin license:

<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>mi.organizacion</groupId><artifactId>pluginParaX</artifactId><version>1.0-SNAPSHOT</version><packaging>jar</packaging>

<name>PluginParaX</name><url>http://maven.apache.org</url>

<properties><project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>

</properties>

<dependencies><dependency>

<groupId>junit</groupId><artifactId>junit</artifactId><version>3.8.1</version><scope>test</scope>

</dependency></dependencies><build><plugins>

<!-- Aquí va la configuración del plugin license --></plugins>

</build></project>

Para la configuración del plugin usaremos algo similar a este elemento plugin. Nótese que en un fichero XML, lossímbolos <!-- y --> sirven para abrir y cerrar comentarios:

<plugin><!-- Identificación del plugin license --><groupId>org.codehaus.mojo</groupId><artifactId>license-maven-plugin</artifactId><!-- Configuración para el plugin anterior --><configuration><!-- Año en el que se publica el plugin --><inceptionYear>2015</inceptionYear><!-- Organización que publica el código --><organizationName>FAO</organizationName><!-- Nombre del proyecto como aparecerá en la licencia -->

Page 69: curso desarrollo Documentation · El descriptor de la parte servidora es META-INF/web-fragment.xml y se encuentra en src/main/ resources. Sigue el estándar Servlet3 de Java y contiene

<projectName>PluginParaX</projectName><!-- licencia escogida de la lista del punto anterior --><licenseName>gpl_v3</licenseName><!--directorios donde se encuentran los ficheros de códigocuya cabecera queremos editar--><roots>

<root>src/main/java</root><root>src/main/resources/nfms/modules</root><root>src/main/resources/nfms/styles</root>

</roots><!--Patrones que indentifican los ficheros cuya cabeceraqueremos editar en los directorios especificados por"roots"--><includes>

<include>*.java</include><include>*.js</include><include>*.css</include>

</includes></configuration>

</plugin>

Finalmente, insertando el elemento plugin anterior en el pom.xml, el fichero quedaría de la siguiente manera:

<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>mi.organizacion</groupId><artifactId>pluginParaX</artifactId><version>1.0-SNAPSHOT</version><packaging>jar</packaging>

<name>PluginParaX</name><url>http://maven.apache.org</url>

<properties><project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>

</properties>

<dependencies><dependency>

<groupId>junit</groupId><artifactId>junit</artifactId><version>3.8.1</version><scope>test</scope>

</dependency></dependencies><build><plugins>

<plugin><!-- Identificación del plugin license --><groupId>org.codehaus.mojo</groupId>

Page 70: curso desarrollo Documentation · El descriptor de la parte servidora es META-INF/web-fragment.xml y se encuentra en src/main/ resources. Sigue el estándar Servlet3 de Java y contiene

<artifactId>license-maven-plugin</artifactId><!-- Configuración para el plugin anterior --><configuration><!-- Año en el que se publica el plugin --><inceptionYear>2015</inceptionYear><!-- Organización que publica el código --><organizationName>FAO</organizationName><!-- Nombre del proyecto como aparecerá en la licencia --><projectName>PluginParaX</projectName><!-- licencia escogida de la lista del punto anterior --><licenseName>gpl_v3</licenseName><!--directorios donde se encuentran los ficheros de códigocuya cabecera queremos editar. Especificaremos losdirectorios donde se encuentran los ficheros Java ylos módulos Javascript.--><roots>

<root>src/main/java</root><root>src/main/resources/nfms/modules</root><root>src/main/resources/nfms/styles</root>

</roots><!--Patrones que indentifican los ficheros cuya cabeceraqueremos editar en los directorios especificados por"roots". Especificaremos ficheros Java, Javascript ylas hojas de estilo CSS.--><includes><include>*.java</include><include>*.js</include><include>*.css</include>

</includes></configuration>

</plugin></plugins>

</build></project>

Una vez la configuración está realizada, ya sólo queda realizar las dos acciones necesarias: añadir el texto de la licenciay las cabeceras. Para añadir la licencia podemos ejecutar el objetivo update-project-license:

$ mvn license:update-project-license[INFO] Scanning for projects...[WARNING][WARNING] Some problems were encountered while building the effective model for mi.→˓organizacion:pluginParaX:jar:1.0-SNAPSHOT[WARNING] 'build.plugins.plugin.version' for org.codehaus.mojo:license-maven-plugin→˓is missing. @ line 27, column 15[WARNING][WARNING] It is highly recommended to fix these problems because they threaten the→˓stability of your build.[WARNING][WARNING] For this reason, future Maven versions might no longer support building→˓such malformed projects.[WARNING][INFO][INFO] ------------------------------------------------------------------------

Page 71: curso desarrollo Documentation · El descriptor de la parte servidora es META-INF/web-fragment.xml y se encuentra en src/main/ resources. Sigue el estándar Servlet3 de Java y contiene

[INFO] Building PluginParaX 1.0-SNAPSHOT[INFO] ------------------------------------------------------------------------[INFO][INFO] --- license-maven-plugin:1.8:update-project-license (default-cli) @→˓PluginParaX ---[INFO] Will create or update license file [gpl_v3] to /tmp/pluginParaX/LICENSE.txt[INFO] ------------------------------------------------------------------------[INFO] BUILD SUCCESS[INFO] ------------------------------------------------------------------------[INFO] Total time: 1.738s[INFO] Finished at: Thu Mar 05 08:48:02 CET 2015[INFO] Final Memory: 9M/144M[INFO] ------------------------------------------------------------------------

que añadirá el fichero LICENSE.txt en la raíz del proyecto.

Y por último, para añadir las cabeceras, usaremos el objetivo update-file-header:

$ mvn license:update-file-header[INFO] Scanning for projects...[WARNING][WARNING] Some problems were encountered while building the effective model for mi.→˓organizacion:PluginParaX:jar:1.0-SNAPSHOT[WARNING] 'build.plugins.plugin.version' for org.codehaus.mojo:license-maven-plugin→˓is missing. @ line 27, column 15[WARNING][WARNING] It is highly recommended to fix these problems because they threaten the→˓stability of your build.[WARNING][WARNING] For this reason, future Maven versions might no longer support building→˓such malformed projects.[WARNING][INFO][INFO] ------------------------------------------------------------------------[INFO] Building PluginParaX 1.0-SNAPSHOT[INFO] ------------------------------------------------------------------------[INFO][INFO] --- license-maven-plugin:1.8:update-file-header (default-cli) @ PluginParaX ---[INFO] Will search files to update from root /tmp/pluginParaX/src/main/java[INFO] Will search files to update from root /tmp/pluginParaX/src/main/resources/nfms/→˓modules[INFO] Scan 4 files header done in 27.798ms.[INFO]

* add header on 4 files.[INFO] ------------------------------------------------------------------------[INFO] BUILD SUCCESS[INFO] ------------------------------------------------------------------------[INFO] Total time: 1.874s[INFO] Finished at: Thu Mar 05 08:50:36 CET 2015[INFO] Final Memory: 10M/144M[INFO] ------------------------------------------------------------------------

Por último, sólo nos queda poner nuestro plugin en un lugar accesible para que lo puedan encontrar otros desarrol-ladores.

Page 72: curso desarrollo Documentation · El descriptor de la parte servidora es META-INF/web-fragment.xml y se encuentra en src/main/ resources. Sigue el estándar Servlet3 de Java y contiene

Publicación de un plugin en un repositorio Maven

Para que los plugins desarrollados puedan ser reutilizados en otros desarrollos, ya sean propios o de terceras partes, esnecesario subirlos a un repositorio Maven. Para ello modificaremos dos ficheros:

• El pom.xml de nuestro plugin

• El fichero de configuración settings.xml de Maven

Un repositorio Maven es una estructura de directorios que contiene los plugins y sus dependencias organizados porgroupId y artifactId. El repositorio principal de Maven se encuentra en http://central.maven.org/maven2/ y contienelas librerías de uso general. Para los plugins y las librerías más específicas del portal REDD, FAO pone a disposiciónel servidor maven.nfms4redd.org. Es este servidor el que podemos utilizar para subir nuestro plugin a un sitioaccesibile.

Si hemos desarrollado un plugin, seguro que tenemos Maven ya configurado para que se descargue las dependenciasdel portal del repositorio de FAO. Pero ahora habrá que configurarlo para que, además de descargar los plugins, puedasubirlos.

Para subir los plugins al repositorio de FAO hay que utilizar el servicio ftp://maven.nfms4redd.org/repo.Esto se configura en el pom.xml de nuestro plugin mediante dos elementos. El primero es un elemento dentro de<build> que dice a Maven que vamos a acceder por FTP:

<build><extensions>

<extension><groupId>org.apache.maven.wagon</groupId><artifactId>wagon-ftp</artifactId><version>2.3</version>

</extension></extensions>

</build>

El segundo le dice a Maven la URL del servidor:

<distributionManagement><repository>

<id>nfms4redd</id><url>ftp://maven.nfms4redd.org/repo</url><uniqueVersion>false</uniqueVersion>

</repository></distributionManagement>

Como no queremos compartir el usuario y contraseña para acceder al FTP, lo que hacemos es especificar sólo un identi-ficador, en este caso nfms4redd, al que haremos referencia desde el fichero de configuración de Maven settings.xml para asignar a ese id el usuario y contraseña. El fichero settings.xml se encuentra en el directorio .m2 del“HOME” del usuario:

Page 73: curso desarrollo Documentation · El descriptor de la parte servidora es META-INF/web-fragment.xml y se encuentra en src/main/ resources. Sigue el estándar Servlet3 de Java y contiene

En dicho fichero, crearemos un elemento server con el mismo identificador que el especificado en el pom.xml delplugin. Y en ese elemento pondremos el usuario y contraseña.

<settings xmlns="http://maven.apache.org/SETTINGS/1.0.0"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://maven.apache.org/SETTINGS/1.0.0

http://maven.apache.org/xsd/settings-1.0.0.xsd"><servers>

<server><id>nfms4redd</id><username>XXXXX</username><password>XXXXX</password>

</server></servers>

</settings>

Una vez la configuración está terminada, es necesario pedir a Maven que ejecute la tarea (goal) deploy.

Warning: Es posible que la ejecución de Maven falle si se ejecuta desde Eclipse y tenemos configurado sólo unJRE y no un JDK. En tal caso la solución es simple: instalar un JDK y utilizarlo por defecto.