1. Introducción a los servidores de aplicaciones y a WildFly
1.1. ¿Qué es un servidor de aplicaciones?
Un servidor de aplicaciones es una plataforma que implementa los servicios definidos en la especificación de Java Enterprise.

Estos servicios van desde la ejecución de aplicaciones web sencillas, basadas en Servlets/JSP hasta aplicaciones distribuidas con soporte transaccional, basadas en Enterprise Java Beans (EJB) o servicios Web.
Especificación | Java EE 6 | Java EE 7 |
---|---|---|
Servlet |
3.0 |
3.1 |
JavaServer Pages (JSP) |
2.2 |
2.3 |
Unified Expression Language (EL) |
2.2 |
3.0 |
Debugging Support for Other Languages (JSR-45) |
1.0 |
1.0 |
JavaServer Pages Standard Tag Library (JSTL) |
1.2 |
1.2 |
JavaServer Faces (JSF) |
2.0 |
2.2 |
Java API for RESTful Web Services (JAX-RS) |
n/a |
2.0 |
Java API for WebSocket (WebSocket) |
n/a |
1.0 |
Java API for JSON Processing (JSON-P) |
n/a |
1.0 |
Common Annotations for the Java Platform (JSR-250) |
1.1 |
1.2 |
Enterprise JavaBeans (EJB) |
3.1 Lite |
3.2 Lite |
Java Transaction API (JTA) |
1.1 |
1.2 |
Java Persistence API (JPA) |
2.0 |
2.1 |
Bean Validation |
1.0 |
1.1 |
Managed Beans |
1.0 |
1.0 |
Interceptors |
1.1 |
1.2 |
Contexts and Dependency Injection (CDI) |
1.0 |
1.1 |
¿Se puede considerar Tomcat un servidor de aplicaciones?
Tomcat implementa un contenedor Web que en su versión 8 cumple las especificaciones Servlet 3.1, JSP 2.3, y EL 3.0 y WebSockets, pero no incluye un contenedor de EJB’s o da soporte a otras APIs como JMS, por tanto no se puede considerar como un servidor de aplicaciones completo.
|
Sí que es cierto es que de la combinación de Tomcat con Spring, junto con otras librerías de terceros resulta en algo parecido a un servidor de aplicaciones. Es más, proyectos como Spring Boot o DropWizard permiten integrar dentro de un único fichero, tanto código de aplicación como los servicios que necesita para funcionar.
La principal ventaja de un servidor de aplicaciones es que integra todo los componentes necesarios para empezar a trabajar, permitiendo al programador centrarse en lo más importante que es el desarrollo de aplicaciones. Esto no quiere decir que no podamos utilizar las librerías que creamos oportunas, pero el producto ya nos ofrece una solución válida en conjunto.
Para un administrador de sistemas también es más sencillo pues al ser un producto estándar, puede trabajar sobre el servidor sin necesidad de conocer los entresijos de las aplicaciones que ejecutan.
¿Debemos entonces debemos utilizar un servidor de aplicaciones para todo?
No necesariamente, puesto que la administración y configuración de un Tomcat, Jetty u otros servidores es más sencilla, siempre y cuando nuestra aplicación no requiera de servicios más avanzados. Por otra parte si nuestro desarrollo es muy específico puede compensar el invertir tiempo y desarrollar una solución a medida con librerías y/o frameworks alternativos.
|
Lo que nos debe llevar a pensar en utilizar un servidor de aplicaciones es considerar que cuestiones como la estandarización, la alta disponibilidad, la monitorización del rendimiento, transaccionalidad y seguridad son importantes en nuestros desarrollos así como el poder centrarnos en resolver un problema abstrayéndonos de los servicios que vamos a utilizar.
La especificación Java EE 7 se publicó en mayo de 2013 pero no es hasta finales de 2015 cuando se puede considerar como instaurada. La propia Oracle certifica su producto comercial WebLogic en Octubre de 2015 y RedHat, a fecha de redacción de estos apuntes aún no ha publicado una versión comercial certificada.

- Los más importantes son
-
-
Comerciales: Oracle WebLogic, IBM WebSphere, Red Hat JBoss EAP.
-
Open Source: Oracle GlassFish, RedHat WildFly.
-
Hace unos años la respuesta a esta pregunta era más sencilla, pero hoy en día con la cantidad de información disponible y la madurez de los productos Open Source actuales es complicado el amortizar los costes de licencias, sobre todo en el caso de nuevos desarrollos basados en Java EE 6 o 7.
1.2. Evolución de Java Enterprise
Desde sus inicios, la especificación Java Enterprise ha intentado definir y estandarizar los servicios que una aplicación de negocio necesita, y a lo largo de sus primeras versiones fue añadiendo nuevas tecnologías, si bien el desarrollo de aplicaciones era bastante complejo.
Los primeros servidores de aplicaciones de algún modo emulaban el modelo de Mainframe ya conocido en aplicaciones de grandes empresas pero especializado en la ejecución de aplicaciones java y suponían un coste elevado en hardware y licencias.

Esta situación llevó a que surgieran iniciativas como Spring que ofrecían la misma idea de estandarización pero sustentada en contenedores web ligeros, gratuita y sustentada en una gran comunidad de desarrolladores.
Proyectos como Spring sirvieron para que, primero Sun y ahora Oracle, entendiesen que el desarrollo debía evolucionar hacía una concepción más sencilla donde el desarrollo fuese lo más cercano posible a la escritura de POJO’s y los servidores de aplicaciones mucho más ligeros y optimizados. Y por qué no decirlo, se consiguió aceptando como estándar las mejores ideas de los competidores. Esto se consigue sobre todo a partir de la madurez en el mercado de Java EE 6.
1.3. Introducción a WildFly
WildFly es el nuevo nombre que recibe el servidor de aplicaciones JBoss, desarrollado por Red Hat. Frente a las versiones anteriores (JBoss AS 7.1 y EAP6) presenta las siguientes novedades:
-
Compatible y optimizado para la JVM de Java SE 7. Esto se traduce en mejoras de rendimiento en Entrada/Salida, políticas de Garbage collector y concurrencia.
-
Certificado para Java EE 7 Web y Full Profile.
-
Roles definidos dentro de los usuarios administradores. Se puede delimitar con mayor detalle las tareas que puede realizar un usuario administrador.
-
Nuevo Servidor Web. Tradicionalmente se utilizaba Tomcat, pero en esta versión se sustituye por Undertow, que proporciona un mayor rendimiento.
-
Mejoras en las herramientas de administración (Consola, línea de comandos).
-
Reducción del número de puertos de escucha a solo 2, puerto de trabajo y puerto de administración.
Si bien es interesante académicamente poder trabajar con la última especificación de Java EE, Red Hat no ofrece ninguna variante con soporte comercial, por tanto en el mundo de la empresa es más recomendable continuar con una versión anterior ya sea comercial u Open Source.
1.3.1. Instalación de WildFly
La instalación del servidor es muy sencilla. Basta con conectarse a http://www.wildfly.org y descargarse un fichero ZIP. Es recomendable establecer la variable de entorno JBOSS_HOME apuntando a la carpeta donde se vaya a descomprimir el servidor:
export JBOSS_HOME=/usr/share/wildfly-8.2.1
1.3.2. Definición de dominio
Un dominio define un conjunto de propiedades, recursos e instancias de servidores de aplicaciones. La definición de dominios permite flexibilizar y organizar la instalación de servidores de aplicaciones, ya que es posible asignar distintos dominios dentro de un mismo host a distintas organizaciones y administradores. Las aplicaciones y recursos instaladas en un dominio son independientes de los otros dominios. Para permitir que distintos dominios puedan estar en marcha al mismo tiempo, cada dominio utiliza distintos puertos de servicio y administración.

1.3.3. Modos de trabajo de WildFly
Standalone
Es el modo más sencillo y habitual. Permite ejecutar WildFly como un proceso independiente. Este modo de trabajo no es incompatible con una configuración HA, por lo que podemos tener configurados varios servidores independientes en clúster. Para iniciar WildFly en modo Standalone hay que ejecutar el script:
standalone.sh
Managed Domain
En este modo se permite iniciar uno o varios servidores de WildFly administrados de forma conjunta desde un único dominio. La forma de iniciar un dominio WildFly es a través del script:
domain.sh
Este script iniciará un proceso denominado Host Controller, que se encargará de iniciar los servidores administrados del dominio. El siguiente diagrama describe un dominio WildFly:

Los conceptos de host y servidor ya los conocemos, pero tenemos nuevos elementos:
- Host Controller
-
Al ejecutar el script domain.sh, se inicia un proceso denominado Host Controller cuya única misión es la de iniciar/parar los distintos servidores que se hayan definido en la máquina. Para ello se comunica con el proceso Domain Controller que definiremos a continuación.
- Domain Controller
-
Dentro de un dominio debe existir un proceso Host Controller configurado como el Domain Controller, es decir como almacén central de la configuración del dominio. Este proceso será el encargado de gestionar las políticas de administración, de sincronizar dichas políticas con el resto de Host Controllers del dominio
- Server Group
-
Un server group es un conjunto de instancias que deben ser administradas como si fueran una sola. En una configuración de dominio puede haber uno o varios grupos, y cada servidor de aplicaciones debe ser miembro de un grupo, aún en el caso de que sólo exista un servidor en el dominio. Es responsabilidad del Domain Controller y de los Host Controllers que todos los servidores dentro de un grupo tengan una configuración coherente y los mismos despliegues.
1.3.4. Estructura de directorios de WildFly
Las carpetas más importantes:
-
appclient: contiene ficheros de configuración y propios del contenedor de aplicaciones clientes.
-
bin: contiene varios ficheros de configuración de arranque, jboss-client.jar para acceder crear una aplicación cliente Java.
-
domain: Contiene los ficheros de configuración del modo Managed Domain y datos de ejecución del servidor en este modo.
-
standalone: Similar la anterior, pero específica del modo Standalone.
-
welcome-content: Contenido estático de la aplicación web por defecto.
1.3.5. Creación de un usuario administrador
Para poder comenzar a trabajar con WildFly es necesario crear un usuario administrador y para ello utilizaremos el script add-user.sh.

En este ejemplo, hemos creado el usuario experto como usuario administrador perteneciente al ManagementRealm. Es importante contestar SI a la última pregunta para indicar que el usuario se podrá conectar al domain controller. Se generará una password encriptada que se utilizará a la hora de configurar un dominio de servidores de aplicaciones.
1.4. Herramientas de administración de WildFly
1.4.1. Consola de administración Web
Mediante la consola de administración podemos acometer las tareas más básicas de una forma amigable. El puerto de administración habitual es el 9990, y para acceder a la consola basta con abrir la dirección:

Comparada con la consola de administración de otros servidores como WebLogic o incluso GlassFish es mas rudimentaria, si bien ha mejorado notablemente con respecto a versiones anteriores.
1.4.2. Command Line Interface (CLI)
Se trata de una herramienta de administración desde una pantalla de terminal. A esta consola se accede ejecutando el script jboss-cli.sh
Desde esta consola se puede cambiar la configuración de un servidor local o remoto. Parámetros más importantes:
$ ./jboss-cli.sh [--help] [--version] [--connect] [--controller] [--commands] [--user] [--password] [--file]
- --help
-
Muestra un listado de comandos admitidos
- --version
-
Muestra información de la versión de WildFly y de la máquina.
- --connect
-
Comando utilizado para conectarnos al servidor e iniciar su administración
- --controller
-
Host:puerto al que nos queremos conectar
- --commands
-
Permite especificar una o varias instrucciones de administración.
- --user/password
-
Credenciales para conectarse a un servidor, utilizadas normalmente al acceder a un servidor remoto.
- --file
-
Permite ejecutar un script con múltiples instrucciones de administración.
Ejemplos de uso:
#Conexión a un servidor local (localhost:9990)
./jboss-cli.sh --connect
#Conexión a un host controller remoto
./jboss-cli.sh --connect --controller=192.168.10.1 --user=admin1234 --pasword=pass1234
#Ejecutar una secuencia de comandos
./jboss-cli.sh --commands="connect,deploy prueba.jar"
#Ejecutar un script de administración
./jboss-cli.sh --file=myscript.cli
#Parar el servidor
./jboss-cli.sh --connect --command=shutdown
Ejemplo de script:
# Connect to Wildfly instance
connect
# Create Spring Batch Module
module add \
--name=org.springframework.batch \
--dependencies=javax.api,javaee.api \
--resources=${wildfly.module.classpath}
En el caso de que tengamos algún problema con la configuración, que no permita operar normalmente al servidor, podemos recurrir al siguiente comando:
./standalone.sh --admin-only
Este comando permite arrancar el servidor en modo administración, bloqueado excepto para tareas administrativas. De esta forma podremos acceder a CLI y corregir la configuración. Una vez hecho esto, podemos salir del modo administración con el siguiente comando:
[standalone@localhost:9990/]reload --admin-only=false
Una vez estemos conectados a una instancia de WildFly podemos trabajar directamente sobre la configuración del servidor como si fuera un sistema de ficheros:

Por último, si queremos una interfaz más amigable, podemos ejecutar la consola en modo gráfico:
./jboss-cli.sh --gui

1.4.3. Edición manual de los ficheros de configuración
Toda la configuración del servidor persiste en ficheros XML que se pueden editar manualmente. En la siguiente sesión veremos con más detalle la forma en la que WildFly almacena esta configuración pero hay que tener en cuenta que lo recomendable hacer cambios en la configuración mediante las herramientas de administración, sobre todo en servidores de producción.
1.5. Aplicaciones Java Enterprise
1.5.1. Empaquetado de aplicaciones
Hasta ahora hemos utilizado dos tipos de ficheros para empaquetar aplicaciones Java: ficheros JAR y ficheros WAR. Además de estos tipos hay un tercero específico de las aplicaciones Java Enterprise: el EAR.
Los ficheros JAR empaquetan clases Java. Un fichero JAR contiene clases Java compiladas (ficheros .class) junto con un descriptor de la aplicación.

Cuando un fichero JAR se añade al classpath de una JVM (Máquina Virtual Java) las clases incluidas en él se ponen a disposición de cualquier aplicación Java que ejecute la JVM. De la misma forma, cuando un fichero JAR se define en el classpath del compilador Java, las clases incluidas en él pueden utilizarse en las clases que estamos compilando.
Una idea fundamental relacionada con el empaquetado de clases es que el compilador Java y la máquina virtual Java tienen distintos classpath, esto es, resuelven las dependencias utilizando distintos ficheros. Esto trasladado a una aplicación Java Enterprise permite que para compilar una aplicación estándar, únicamente necesitemos una dependencia:
<dependencies>
<dependency>
<groupId>javax</groupId>
<artifactId>javaee-api</artifactId>
<version>7.0</version>
<scope>provided</scope>
</dependency>
</dependencies>
El servidor de aplicaciones por su parte contendrá tanto la especificación como las librerías (jars) específicas que implementen la especificación.
Las aplicaciones web empaquetadas en ficheros WAR son más complejas puesto que pueden contener recursos estáticos, clases compiladas y librerías Jar. Al desplegar una aplicación web en un servidor de aplicaciones, las librerías contenidas pasan a formar parte del classpath de la aplicación exclusivamente.
¿Qué ocurre si queremos utilizar una misma librería en distintas aplicaciones Web desplegadas en un servidor? Si las incluimos en cada aplicación y son pesadas, penalizará el arranque del servidor y elevará el consumo de recursos innecesariamente. Para evitarlo tenemos varias formas:
-
Incluir la librería directamente en el classpath del servidor (¡No recomendado!)
-
Despliegue del jar como librería compartida, en el caso concreto de WildFly sería declararla como módulo global del servidor en ficheros de configuración.
-
Si son aplicaciones relacionadas empaquetarlas dentro de un fichero EAR.
Los ficheros EAR representan una aplicación empresarial formada por distintos módulos (aplicaciones web y ficheros JAR). Los ficheros JAR pueden ser librerías o Enterprise JavaBeans (EJB) usados por las aplicaciones web y estarán disponibles para cualquier aplicación contenida.

Otra característica de los EAR es que permite el control unificado (arranque, despliegue, parada…) del conjunto de las aplicaciones contenidas.
Físicamente, un fichero EAR es un fichero comprimido con el mismo formato que los ficheros JAR. Todos los comandos que se pueden utilizar con los ficheros JAR (jar -tvf mi-fichero.jar, etc.) sirven para los ficheros EAR. Los ficheros EAR llevan la extensión .ear. La estructura de un fichero EAR es sencilla, contiene un conjunto de ficheros WAR, un conjunto de ficheros JAR y el directorio META-INF, en el que se encuentra los distintos ficheros de configuración necesarios. Opcionalmente puede contener el descriptor de despliegue application.xml en el que se identifican los módulos que se incluyen en él.
1.5.2. Despliegue de aplicaciones en WildFly
WildFly nos ofrece tres posibilidades:
-
Despliegue desde sistema de ficheros (sólo en modalidad standalone).
cp example.war /usr/local/wildfly-8.2.1.Final/standalone/deployments
WildFly distingue entre aplicaciones empaquetadas (Jar,War,Ear) o aplicaciones descomprimidas (exploded). Por defecto el autodeploy está habilitado para las aplicaciones empaquetadas pero no para las exploded. En el caso de que el despliegue sea manual, se controla mediante ficheros auxiliares llamados markers. Estos ficheros ficheros también aparecen cuando falla un despliegue por autodeploy (fichero application.FAILED). Si lo eliminamos, el escáner intentará deplegar la aplicación de nuevo.
La configuración del escáner de despliegues se realiza sobre el fichero standalone.xml o a través de CLI:
<deployment-scanner scan-interval="5000" relative-to="jboss.server.base.dir"
path="deployments" auto-deploy-zipped="true" auto-deploy-exploded="false"/>
Para más detalles podéis consultar la documentación de WildFly: https://docs.jboss.org/author/display/WFLY8/Application+deployment
-
Mediante las herramientas de administración
[standalone@localhost:9990/]deploy example.war

-
Uso de herramientas de terceros como por ejemplo wildfly-maven-plugin
<plugin>
<groupId>org.wildfly.plugins</groupId>
<artifactId>wildfly-maven-plugin</artifactId>
<version>1.0.2.Final</version>
<executions>
<execution>
<phase>install</phase>
<goals>
<goal>deploy</goal>
</goals>
</execution>
</executions>
</plugin>
[standalone@localhost:9990/]mvn wildfly:deploy
[standalone@localhost:9990/]mvn wildfly:redeploy
[standalone@localhost:9990/]mvn wildfly:undeploy
Ejecutar aplicacion sin instalación previa de WildFly wildfly:run
Es una función interesente del plugin de maven de cara a realizar pruebas o ejecutar la aplicación en una máquina donde no tenemos configurada ninguna instancia de WildFly. Este comando Maven descarga automáticamente un servidor WildFly en modo standalone y despliega en el la aplicación compilada.
|
1.6. Arquitecturas basadas en microservicios
Una arquitectura basada en microservicios es una forma de entender las aplicaciones como un conjunto de pequeños servicios, cada uno ejecutándose como proceso independiente y comunicándose con el resto mediante mecanismos sencillos y "agnósticos", a menudo HTTP. Estos servicios se diseñan sobre funciones de negocio y siguen estrategias de de despliegue independientes. Aunque se trate de servicios independientes suele existir una pequeña lógica de administración común para estos servicios, que pueden estar construidos usando distintos lenguajes de programación y diferentes tecnologías de almacenamiento.
Martin Fowler (padre del término microservicio)

Este tipo de arquitectura nace en contraposición a las grandes aplicaciones Monolíticas. En el mundo Java, una aplicación monolítica podría ser un EAR con una serie de módulos y librerías dependientes que se gestiona como una unidad. En si mismo ésto no tiene porqué ser un problema (sigue siendo un modelo válido) hasta que la propia envergadura del proyecto o la dificultad para alcanzar requerimientos de escalabilidad o separación de responsabilidades evidencíen dificultades de gestión. En cierta medida microservicios trata más de organización del trabajo que de tecnologías concretas.
También hay que destacar que el término de "micro" no se refiere al tamaño del servicio si no a la funcionalidad que desempeña. Para definir correctamente un microservicio hay que apoyarse en buenas prácticas de programación ya establecidas como la alta cohesión y bajo acoplamiento o el Principio de Responsabilidad Única (Un subsistema, módulo o incluso clase sólo debe tener un motivo para cambiar).
Se podría entender como un SOA 2.0 (Arquitectura Orientada a Servicios) o un SOA pragmático donde se sustituyen protocolos pesados como SOAP y elementos como ESB’s donde se ejecutaba gran cantidad de lógica por servicios donde la inteligencia está en el propio servicio y no existe un control y persistencia centralizados.
1.6.1. Ventajas de una arquitectura basada en Microservicios sobre una monolítica
- Facilidad para escalar el desarrollo
-
Permite dividir el trabajo entre equipos de trabajo reducidos, con conocimientos completos del stack de desarrollo y con una responsabilidad funcional acotada. (Two Pizza teams)
- Aislamiento ante fallos
-
Si un servicio tiene un fallo, es factible corregir dicho servicio y desplegarlo individualmente sin afectar al resto de la aplicación.
- Despliegues más rápidos
-
Poder desplegar por partes facilita el poder realizar despliegues a producción más frecuentes y con un riesgo menor.
- Elimina la dependencia a largo plazo de una tecnología
-
El utilizar mecanismo de comunicación agnósticos, permite que podamos utilizar la tecnología más adecuada para cada servicio, en contraposición al modelo de EAR donde se elige un único "stack" para toda la aplicación. Si en un momento decidimos cambiar de tecnología, podemos ir reemplazando paulatinamente los servicios afectados.
1.6.2. Desventajas
- Complejidad de un sistema distribuido
-
Servicios independientes requieren una monitorización independiente, recursos de ejecución adicionales, herramientas de descubrimiento, gestión de logs… en definitiva costes y complejidad adicionales a las de un proyecto monolítico.

- Requieren cultura DevOps
-
Las iniciativas DevOps permiten automatizar los procesos de validación y despliegue de los nuevos servicios y allanar las dificultades anteriores. Comentaremos con más detalle qué es DevOps en sesiones posteriores.
- Falta de herramientas
-
Las herramientas tradicionales están orientados a desarrollos monolíticos y no tanto a proyectos complejos distribuidos.
- Comunicación y coordinación entre equipos
-
Si bien gestionar un equipo grande es complejo, el coordinar y compartir conocimiento entre equipos independientes también plantea retos.
1.6.3. Cuándo utilizar microservicios
Según Martin Fowler no se debería considerar una arquitectura basada en microservicios hasta tener un sistema monolítico que sea demasiado complejo de mantener. Nosotros añadiríamos otro motivo: cuando las necesidades de escalabilidad del trabajo lo justifiquen o se necesite utilizar distintas tecnologías en distintas funciones de negocio.

Para un desarrollo ya existente, un enfoque muy aceptado es asegurar en primer lugar una que tenemos una aplicación monolítica correcta en términos de arquitectura:
-
Separación de funciones.
-
Alta cohesión y bajo acoplamiento utilizando APIs bien definidas.
-
Sin redundancias.
-
Diseño dirigido por el Dominio (DDD).
-
Lo demás "sobra".
De forma progresiva, se puede ir identificando las distintas funciones de negocio y separarlas en serivicios siguiendo el principio de responsabilidad única (SPR). Una vez definidos los servicios más básicos, las aplicaciones harán uso de ellos combinándolos según diferentes patrones (Ver referencias).
Una vez definidos los servicios básicos las aplicaciones harán uso de ellos combinándolos siguiendo distintos patrones:
1.6.4. Java EE Y los microservicios
El estado de evolución actual de la especificación Java EE permite afrontar el desarrollo de aplicaciones basados en nuevas arquitecturas. Hoy en día, es razonable utilizar un servidor de aplicaciones para desplegar un único servicio basado en Java EE sin incurrir en un consumo excesivo de recursos y beneficiándonos de años de estandarización y una amplia gama de servicios.
En cifras, usar un servidor de aplicaciones completo hoy en día puede suponer un sobrecoste de unos 30-50Mb de RAM sobre una aplicación java standalone , así como unos 100Mb de espacio en disco y el tiempo de arranque puede ser de 1-3 segundos de tiempo. En una aplicación empresarial con un número de servicios limitado esto no supone un problema. De hecho, resulta curioso ver cómo se ha ido reduciendo el consumo de recursos en la parte servidora y sin embargo hemos sobrepasado el consumo de las aplicaciones de escritorio pesadas en el lado del cliente (Vease Chrome, por ejemplo).
Sólo en el caso de aplicaciones altamente escalables y orientadas a internet (al nivel de Google, Netflix, etc.), merece la pena invertir esfuerzos en reducir al máximo las librerías necesarias para que un servicio funcione. Aún así puede ser más ventajoso aprovechar la modularidad de los servidores de aplicaciones que construir una solución desde cero.
Hay ponentes como Adam Bien que plantean un modelo minimalista de aplicación Java EE con las siguientes características:
- 1 servidor de aplicaciones, 1 dominio y 1 WAR
-
El concepto de servicio quedaría constituido por un servidor de aplicaciones que ofrezca todo el API Java EE (Full Profile) a una aplicación desplegada como WAR que implemente un servicio.
- WARs ligeras
-
El tamaño de un fichero WAR y su complejidad interna influye negativamente en los tiempos de despliegue y arranque de una aplicación. Para conseguir mejores resultados Adam Bien propone los siguientes cambios sobre el modelo de desarrollo de aplicaciones Web:
-
Utilizar preferentemente las librerías que proporciona el servidor de aplicaciones, en lugar de incluirlas en la aplicación. Esto ya puede suponer pasar de trabajar con un fichero de varios megabytes a unos pocos Kilobytes.
-
Aplicaciones Autocontenidas: toda la lógica del servicio debe estar incluida dentro del War.
-
Reducir o eliminar los módulos JAR internos. Ya hemos comentado que es habitual trabajar con aplicaciones WAR que contienen a su vez librerías JAR con clases diversas y EJB’s. Este esquema ralentiza las compilaciones y despliegues y en realidad la aplicación siempre se compila o despliega como un todo, por lo que la división interna en librerías no aporta ventajas en muchos casos.
-
- Estructura del código
-
-
No utilizar interfaces. Únicamente los utilizaremos cuando sepamos que vamos a tener diferentes implementaciones para una misma funcionalidad, pero hasta entonces no complicar el código innecesariamente.
-
Centrarnos en el código de negocio. Java EE permite que las aplicaciones estén compuestas básicamente por POJOS y permite abstraer la complejidad de los servicios. El trabajar con objetos pojo tiene el beneficio adicional de facilitar las pruebas unitarias
-
Pocas anotaciones (Convención sobre declaración): Evitar declarar propiedades que ya tengan el mismo valor por defecto dentro de su especificación.
-
Ejemplo de microservicio minimalista:
@Path("cursos")
public class CursosResource {
@GET
@Produces("application/json")
public JsonObject all(){
return Json.createObjectBuilder().add("cursos","JavaEE, JavaScript, MongoDB").build();
}
}
1.6.5. Docker

Docker ha supuesto el salto a la fama del concepto de contenerización de servicios y viene a ser una mezcla de máquina virtual con control de versiones tipo Subversion. Permite desplegar todo el stack de una aplicación o servicio en una máquina con un simple comando pull como si fuera un único paquete. Se apoya los conceptos básicos de imagen y contenedor:
Una imagen se puede considerar como un sistema de archivos en el que podemos añadir lo que la aplicación necesite para funcionar, como una máquina virtual de Java o la instalación de un servidor de aplicaciones. Una característica fundamental es que está formado por capas, que funcionan como los commit de Git o SVN. Cada vez que se añade una capa, se superponen nuevos ficheros sobre la imagen inicial. En este caso es fundamental dejar en la última capa el despliegue de la aplicación para que cuando ésta se modifique, la imagen conserve todas sus capas y sustituya únicamente la última (que tendrá el tamaño del WAR desplegado). Aquí tenemos una importante mejor en el caso de las aplicaciones Java ligeras que proponemos, especialmente si la comparamos con las basadas en Java StandAlone (o FAT Jars).
Llamamos contenedor a la instancia en ejecución de una imagen. Apoyándose en los mecanismos de seguridad de Linux, el container se ejecuta en un espacio de memoria separado, con lo que es tarea del programador el definir la comunicación entre los procesos del container y el resto de la máquina (gestionando los puertos o montando directorios físicos fundamentalmente). Hay que tener siempre presente que cada vez que se inicia un contenedor, se parte de la imagen definida por lo que cualquier cambio que se realice sobre el sistema de ficheros propio del contenedor NO se almacenará en la imagen y por tanto se perderá.
Entender Docker es mucho más sencillo en la práctica, así que en los ejercicios veremos un pequeño ejemplo.
1.7. Referencias
-
Información sobre Roles Java EE http://docs.oracle.com/javaee/6/tutorial/doc/bnaca.html .
-
Wiki con los distintos servidores de aplicaciones y sus especificaciones http://en.wikipedia.org/wiki/Java_Platform,_Enterprise_Edition .
-
Información sobre WildFly http://wildfly.org/about/ .
-
Guia de iniciación https://docs.jboss.org/author/display/WFLY8/Getting+Started+Guide .
-
Guía completa de administración https://docs.jboss.org/author/display/WFLY8/Admin+Guide#AdminGuide-Examplesinthisguide .
-
Despliegues desde sistema de ficheros https://docs.jboss.org/author/display/WFLY8/Application+deployment
-
WildFly Maven Plugin https://docs.jboss.org/wildfly/plugins/maven/latest/index.html
-
Java EE 7 Examples https://github.com/javaee-samples/javaee7-samples
-
Spring Boot http://projects.spring.io/spring-boot/
-
Dropwizard http://www.dropwizard.io/
-
WildFly Swarm http://wildfly.org/swarm/
-
WAS Liberty https://developer.ibm.com/wasdev/
-
Desarrollo con WildFly y Docker http://tools.jboss.org/blog/2015-03-03-docker-and-wildfly-2.html
-
Introducción a los microservicios http://microservices.io/patterns/microservices.html
-
Patrones de diseño para microservicios http://blog.arungupta.me/microservice-design-patterns/
-
Docker https://www.docker.com/
-
Simulador online de Docker http://dockersim.com/
-
Docker Maven plugin https://github.com/spotify/docker-maven-plugin/tree/master
-
ECB Model http://www.cs.sjsu.edu/~pearce/modules/patterns/enterprise/ecb/ecb.htm
1.8. Ejercicios de Introducción a los servidores de aplicaciones y a WildFly
Al ser los primero ejercicios del módulo de PaaS debéis hacer un fork del repositorio ejercicios_paas. En el debéis almacenar todos los ejercicios del módulo Servidores Web y PaaS independientemente de que luego en IntelliJ se gestionen en proyectos separados. De hecho, en las sesiones OpenShift trabajaremos con repositorios Git independientes por aplicación.
Además de este repositorio, tenéis los ejemplos de cada sesión dentro de la carpeta ejemplos del mismo repositorio, por si queréis probarlos con más detalle.
1.8.1. Desarrollo de un microservicio (0.3 Puntos)
Partiendo de que ya habéis trabajado con WildFly en sesiones anteriores vamos a centrarnos en el desarrollo de una aplicación Java EE, de nombre cursos, que sirva para practicar los distintos tipos de despliegue que admite el servidor tanto como instalación local como virtualizada a partir de una imagen de Docker.
Para crear el proyecto utilizaremos Maven y un arquetipo elaborado por Adam Bien, que ofrece una aplicación web similar al arquetipo estándar webapp-javaee7 pero con una única dependencia que permite referenciar todo el API de Java EE. Básicamente es un proyecto web minimalista con todo lo necesario para desarrollar.
GroupID | Artefacto | Versión |
---|---|---|
com.airhacks |
javaee7-essentials-archetype |
1.3 |
Pasos a seguir
1.- Crearemos un nuevo módulo Maven dentro del proyecto de ejercicios a partir de este arquetipo y lo denominaremos cursos. Las propiedades completas del nuevo artefacto serán:
GroupID | Artefacto | Versión |
---|---|---|
org.expertojava.paas |
cursos |
1.0 |
Por último indicaremos que el nuevo módulo dentro de IntelliJ se llamará cursos así como el directorio dentro del proyecto principal.
2.- Renombraremos el paquete com.airhacks del proyecto generado a es.ua.expertojava.paas.cursos mediante la herramienta Refactor de IntelliJ. Tambien eliminaremos el paquete com que queda vacío tras el proceso anterior.
3.- Dentro del repositorio de ejercicios hay una carpeta sesion1 con subcarpetas de nombre: boundary, control y entity. Debéis copiar las tres carpetas a dentro de la carpeta es/ua/expertojava/paas/cursos/ del proyecto. Al hacerlo IntelliJ automáticamente importará las clases Java contenidas en dichas carpetas. La estructura del proyecto debe quedar de la siguiente forma:

Lo que estamos haciendo es dividir una funcion de negocio "cursos" en tres paquetes distintos con diferentes cometidoas:
- Boundary
-
Contiene las clases que definen la interfaz entre la función de negocio y el exterior.
- Control
-
Son las clases que definen la ejecución del servicio e interactuan con las otras dos capas.
- Entity
-
Son las clases que gestionan elementos de información persistentes, de mayor o menor granularidad pero con significado propio.
Esta forma de trabajar se llama patrón Entity-Control-Boundary y es una variación del MVC orientado a servicios.
4.- Añadir la unidad de persistencia al proyecto. Para ello basta con copiar la carpeta META-INF del directorio sesion1 a la ruta /src/main/resources del proyecto.
5.- Crear la configuración de despliegue en IntelliJ para poder desplegar la aplicación en nuestra instancia de WildFly.
Si la configuración de ejecución os muestra un mensaje de error indicando que no está definida la versión del JDK a utilizar, ésta se puede establecer desde File-→ Project Structure -→ Project JDK. En nuestro caso trabajaremos con JDK 1.8. |
6.- Una vez desplegada la aplicación comprobar que funciona correctamente mediante la herramienta curl:
#Dar de alta un curso
curl -H "Content-Type: application/json" -X POST -d '{"nombre":"Curso de prueba"}' http://localhost:8080/cursos/resources/cursos
#Obtener la lista de curso almacenada
curl -X GET http://localhost:8080/cursos/resources/cursos
7.- Realizar el undeploy desde IntelliJ

1.8.2. Otras formas de despliegue (0.3 Puntos)
-
Indicar en el fichero soluciones.txt, dentro de vuestro proyecto de ejercicios_paas, los pasos a seguir para realizar las siguientes tareas:
-
Desplegar la aplicación mediante Autodeploy.
-
Desplegar la aplicación mediante CLI.
-
-
Crear un script de CLI, de nombre script.cli, que liste las aplicaciones desplegadas, elimine la aplicación cursos.war y que pare el servidor.
1.8.3. Virtualización con Docker (0.6 Puntos)
Nota
Este ejercicio será una demostración guiada para que os familiaricéis con Docker, y sentar las bases para quien quiera profundizar más en el tema.
|
La principal diferencia entre una aplicación desplegada en una máquina virtual con respecto a Docker es que el conjunto de capas sobre las que se apoya la aplicación es más reducido:

Los contenedores se ejecutan en el espacio de memoria del Sistema operativo Host (debidamente aislados) y no requieren disponer de un sistema de archivos tan complejo como el de un sistema operativo, si no que puede apoyarse en las herramientas específicas necesarias. El resultado es que trabajamos con imágenes más pequeñas y tiempos de arranque mucho más rápidos. También hay que recordar que las imágenes están compuestas por capas apiladas por lo que un cambio en la aplicación desplegada puede suponer sustituir una sola capa, si la imagen está bien estructurada (si se tiene que modificar una capa intermedia, se reemplazan todas las capas dependientes).

La composición en capas de una imagen se define paso a paso en un fichero denominado Dockerfile. Un ejemplo:
FROM jboss/wildfly:8.2.1.Final
MAINTAINER José Luis Zamora Sánchez joseluiszamora@jlz.gmail.com
EXPOSE 8080 9990
RUN /opt/jboss/wildfly/bin/add-user.sh expertojava expertojava --silent
COPY target/cursos.war /opt/jboss/wildfly/standalone/deployments/
CMD ["/opt/jboss/wildfly/bin/standalone.sh", "-b", "0.0.0.0", "-bmanagement","0.0.0.0"]
Lo primero que se indica es la capa base que vamos a utilizar, y para ello podemos utilizar una imagen que ya tengamos instalada, o bien una imagen que nos descarguemos de un repositorio. El repositorio por defecto de Docker es Docker-Hub, con un planteamiento muy similar a Git Hub. En este repositorio es donde se suelen publicar las imágenes oficiales de todos los proyectos Open Source.
También debemos indicar quien será el responsable mantener esta imagen y cómo contactar con él.
La comunicación entre el contenedor y el sistema operativo host se realiza mediante conexiones de red, de forma muy similar a otras soluciones como VirtualBox, si bien la definición se realiza en dos partes:
-
Desde el fichero Dockerfile donde se indica que puertos serán accesibles desde el exterior con el comando EXPOSE.
-
Al iniciar el contenedor donde podemos mapear puertos del Host con puertos del contenedor.
A continuación veréis una serie de comandos que realizan operaciones sobre el sistema de archivos como RUN, COPY que realmente se ejecutan en el momento de generar la imagen, no al iniciarse. Es importante indicar que cada línea del fichero Dockerfile supone una capa, por lo que si agrupamos varios comandos en una línea, físicamente se agruparán en una capa. Si os fijáis, con el comando RUN estamos definiciendo quien será el usuario administrador de la instancia de WildFly.
Esto es muy útil, por ejemplo, si copiamos un fichero ZIP con un servidor de aplicaciones, lo descomprimimos y lo eliminamos en la misma fila. Si lo hacemos así, sólo se almacenarán las diferencias, es decir la nueva carpeta descomprimida del servidor. Si lo hacemos paso a paso, tendremos una capa intermedia con el ZIP completo, que tendremos que descargar para luego eliminarlo en capas posteriores.
Por último, con el comando CMD estamos indicando qué se tiene que ejecutar por defecto al iniciar un contenedor.
Cliente docker
Para trabajar con imágenes y contenedores, necesitamos una herramienta cliente, y en nuestro caso es el comando docker. En este enlace tenéis una "chuleta" con comandos útiles para trabajar con imágenes y con contenedores:
Objetivos
Vamos a crear una imagen independiente que despliegue nuestro servicio, para ello seguiremos los siguientes pasos:
-
Abrir una cuenta gratuita en Docker Hub
La idea es que vuestra imagen quede publicada y que nosotros podamos validarla directamente a partir del repositorio, con lo que debéis acceder a la página de Docker Hub y registraros como usuarios. Posteriormente configuraréis vuestro cliente de Docker para que se conecte a Docker Hub utilizando vuestra cuenta:
expertojava@expertojava:~/IdeaProjects/cursos$ docker login Username: <vuestro usuario> Password: Email: <vuestro correo> WARNING: login credentials saved in /home/expertojava/.docker/config.json Login Succeeded
-
Simular cómo se actualizaría y desplegaría un servicio en producción:
docker run -it --name=miservicio -p 8080:8080 -p 9990:9990 djbyte1977/wildfly # Para crear un nuevo contenedor docker pull djbyte1977/wildfly # Para actualizar la imagen
Con estos comandos os descargaréis la última versión de la imagen wildfly de ejemplo que hemos preparado con la aplicación cursos.war ya desplegada y preparada para funcionar. También estamos mapeando los puertos 8080 y 9090 con sus equivalentes en el contenedor. El fichero Dockerfile utilizado para crear esta imagen es el que tenéis de ejemplo, por lo que si se modifica la aplicación cursos.war no tendreis que volver a descargar las capas anteriores (WildFly base + creación del usuario administrador).
El comando run -it crea un contenedo con el servidor de aplicaciones en modo interactivo, por lo que será análogo a lanzar un servidor directamente desde un CMD. Alternativamente sería posible lanzarlo en background con el comando:
docker run -d --name=miservicio -p 8080:8080 -p 9990:9990 djbyte1977/wildfly
El comando run realmente crea y ejecuta un contenedor por lo que si se ha usado previamente y se ha especificado un nombre, en la siguiente ejecución nos dará un error indicando que ya existe un contenedor con ese nombre .Una vez ya se ha asignado un nombre al contenedor se puede ejecutar simplemente:
docker start miservicio #-i modo interactivo
Para enumerar los contenedores existentes tenemos el comando:
docker ps # Lista los controles en ejecución docker ps -a # Lista todos los controles
Una vez hemos lanzado un contenedor en background, lo podemos gestionar mediante los comandos de docker específicos. Ejemplo:
# Conectarse a la salida estándar del contenedor docker attach --sig-proxy=false miservicio # Parar el contenedor docker stop miservicio # Matar el contenedor vía SIGKILL docker kill miservicio
-
Empaquetar el WAR cursos en una imagen Docker
Lo primero que tenéis que hacer es crear un fichero Dockerfile con los mismos pasos indicados en el ejemplo y dejarlo en la carpeta donde tengáis vuestro proyecto:
expertojava@expertojava:~/IdeaProjects/cursos$ ls cursos.iml Dockerfile pom.xml README.md src target WEB-INF expertojava@expertojava:~/IdeaProjects/cursos$
Con la aplicación ya compilada, ejecutaréis el siguiente comando:
docker build -t <usuario>/wildfly .
Con este comando habreis creado una nueva imagen asociada a vuestro usuario, pero que únicamente existe en el repositorio local. Si queremos compartirla, la debemos subir a Docker Hub, para ello ejecutaréis:
docker push <usuario>/wildfly
-
Repetir el primer paso pero indicando la imagen que habéis creado y dándole un nombre distinto al del ejemplo. Si lo ejecutáis desde vuestra máquina no os descargaréis ninguna capa del repositorio, sin embargo si el servicio estuviera desplegado en otra máquina, al ejecutar un pull se descargaría la capa correspondiente al nuevo WAR.
Importante
La entrega final de este ejercicio constará del código fuente del proyecto, incluyendo el nuevo Dockerfile, y el nombre del repositorio asociado en Docker Hub para que podamos descargar y probar el servicio. El nombre del repositorio se indicará en el fichero soluciones.txt
|
1.8.4. Entrega
En esta sesión debeis entregar el código fuente del proyecto cursos, el fichero script.cli, y el fichero soluciones.txt actualizado con la información solicitada en los ejercicios.