Uploaded by elisardo.a.m

RED HAT TRAINING Capacitacion integral y

advertisement
RED HAT®
TRAINING
Capacitación integral y práctica que resuelve los problemas del mundo
real
Red Hat Application
Development I:
Programming in Java EE
Manual del alumno (ROLE)
© 2018 Red Hat, Inc.
JB183-EAP7.0-es-2-20180124
RED HAT
APPLICATION
DEVELOPMENT I:
PROGRAMMING
IN JAVA EE
Red Hat Application Development I: Programming in Java EE
EAP 7.0 JB183
Red Hat Application Development I: Programming in Java EE
Edición 2 20180124 20180124
Autores:
Editor:
Jim Rigsbee, Richard Allred, Zachary Gutterman, Nancy K.A.N
Seth Kenlon
Copyright © 2018 Red Hat, Inc.
The contents of this course and all its modules and related materials, including handouts
to audience members, are Copyright © 2018 Red Hat, Inc.
No part of this publication may be stored in a retrieval system, transmitted or
reproduced in any way, including, but not limited to, photocopy, photograph, magnetic,
electronic or other record, without the prior written permission of Red Hat, Inc.
This instructional program, including all material provided herein, is supplied without
any guarantees from Red Hat, Inc. Red Hat, Inc. assumes no liability for damages or legal
action arising from the use or misuse of contents or details contained herein.
If you believe Red Hat training materials are being used, copied, or otherwise improperly
distributed please e-mail training@redhat.com or phone toll-free (USA) +1 (866) 626-2994
or +1 (919) 754-3700.
Red Hat, Red Hat Enterprise Linux, the Shadowman logo, JBoss, Hibernate, Fedora, the
Infinity Logo, and RHCE are trademarks of Red Hat, Inc., registered in the United States
and other countries.
Linux® is the registered trademark of Linus Torvalds in the United States and other
countries.
Java® is a registered trademark of Oracle and/or its affiliates.
XFS® is a registered trademark of Silicon Graphics International Corp. or its subsidiaries
in the United States and/or other countries.
The OpenStack® Word Mark and OpenStack Logo are either registered trademarks/
service marks or trademarks/service marks of the OpenStack Foundation, in the United
States and other countries and are used with the OpenStack Foundation's permission.
We are not affiliated with, endorsed or sponsored by the OpenStack Foundation, or the
OpenStack community.
All other trademarks are the property of their respective owners.
Colaboradores: Rob Locke, Bowe Strickland, Scott McBrien
Convenciones del documento
ix
Notas y advertencias .............................................................................................. ix
Introducción
xi
Red Hat Application Development I: Programming in Java EE ..................................... xi
Orientación sobre el entorno del aula .................................................................... xii
Internacionalización .............................................................................................. xiv
1. Transición a aplicaciones con varios niveles
1
Descripción de las aplicaciones empresariales .......................................................... 2
Cuestionario: Descripción de las aplicaciones empresariales ...................................... 4
Comparación de características de Java EE y Java SE .................................................. 8
Cuestionario: Comparación de Java EE y Java SE ...................................................... 13
Descripción del Proceso de la comunidad Java ........................................................ 17
Cuestionario: Descripción del Proceso de la comunidad Java (JCP) ............................ 20
Descripción de la arquitectura de aplicaciones con varios niveles ............................. 24
Cuestionario: Arquitectura de aplicaciones con varios niveles ................................... 29
Instalación de las herramientas de desarrollo de Java .............................................. 33
Ejercicio guiado: Ejecución de la aplicación To Do List .............................................. 38
Resumen .............................................................................................................. 45
2. Empaquetado e implementación de una aplicación de Java EE
Descripción de un servidor de aplicaciones ............................................................
Cuestionario: Descripción de un servidor de aplicaciones ........................................
Identificación de recursos JNDI ..............................................................................
Ejercicio guiado: Identificación de recursos JNDI ......................................................
Empaquetado e implementación de una aplicación de Java EE .................................
Ejercicio guiado: Empaquetado e implementación de una aplicación de Java EE .........
Trabajo de laboratorio: Empaquetado e implementación de aplicaciones en un
servidor de aplicaciones ........................................................................................
Resumen ..............................................................................................................
47
48
52
56
59
62
66
75
83
3. Creación de Enterprise Java Beans
85
Conversión de un POJO en un EJB ......................................................................... 86
Ejercicio guiado: Creación de un EJB sin estado ....................................................... 99
Acceso local y remoto a un EJB ............................................................................ 106
Ejercicio guiado: Acceso remoto a un EJB .............................................................. 110
Descripción del ciclo de vida de un EJB ................................................................. 117
Cuestionario: El ciclo de vida de un EJB ................................................................ 121
Delimitación de transacciones implícitas y explícitas .............................................. 125
Ejercicio guiado: Delimitación de transacciones ..................................................... 131
Trabajo de laboratorio: Creación de Enterprise Java Beans ..................................... 137
Resumen ............................................................................................................. 145
4. Gestión de la persistencia
Descripción de la API de persistencia ...................................................................
Cuestionario: Descripción de la API de persistencia ...............................................
Persistencia de datos ...........................................................................................
Ejercicio guiado: Persistencia de datos ..................................................................
Anotación de clases para validar beans ................................................................
Ejercicio guiado: Validación de datos ....................................................................
Creación de consultas ..........................................................................................
Ejercicio guiado: Creación de consultas .................................................................
Trabajo de laboratorio: Gestión de la persistencia .................................................
JB183-EAP7.0-es-2-20180124
147
148
157
159
168
176
180
185
192
198
v
Red Hat Application Development I: Programming in Java EE
Resumen ............................................................................................................. 206
5. Administración de relaciones entre entidades
207
Configuración de relaciones entre entidades ......................................................... 208
Ejercicio guiado: Configuración de relaciones entre entidades ................................ 220
Descripción de relaciones de varias entidades con varias entidades ........................ 229
Cuestionario: Descripción de relaciones de varias entidades con varias entidades .... 233
Trabajo de laboratorio: Administración de relaciones entre entidades ..................... 237
Resumen ............................................................................................................. 244
6. Creación de servicios REST
Descripción de conceptos de servicios web ...........................................................
Cuestionario: Servicios web ..................................................................................
Creación de servicios REST con JAX-RS ..................................................................
Ejercicio guiado: Exposición de un servicio REST ....................................................
Consumo de un servicio REST ..............................................................................
Cuestionario: Consumo de un servicio REST ..........................................................
Trabajo de laboratorio: Creación de servicios REST ................................................
Resumen .............................................................................................................
245
246
250
252
260
267
273
276
286
7. Implementación de Contextos e Inyección de dependencia (CDI)
287
Contraste entre la inyección de dependencias y la inyección de recursos ................. 288
Ejercicio guiado: Inyección de dependencia ........................................................... 293
Aplicación de alcances contextuales ..................................................................... 300
Ejercicio guiado: Aplicación de alcances ................................................................ 303
Trabajo de laboratorio: Implementación de Contextos e Inyección de
dependencia ....................................................................................................... 310
Resumen ............................................................................................................. 318
8. Creación de aplicaciones de mensajería con JMS
319
Descripción de conceptos de mensajería .............................................................. 320
Cuestionario: Descripción de conceptos de mensajería .......................................... 324
Descripción de la arquitectura de JMS .................................................................. 327
Cuestionario: Descripción de la arquitectura de JMS .............................................. 333
Creación de un Cliente JMS .................................................................................. 336
Ejercicio guiado: Creación de un cliente JMS .......................................................... 341
Creación de MDB ................................................................................................ 346
Ejercicio guiado: Creación de un Bean controlado por mensajes ............................. 350
Trabajo de laboratorio: Creación de aplicaciones de mensajería con JMS ................. 356
Resumen ............................................................................................................. 366
9. Protección de aplicaciones Java EE
367
Descripción de la especificación de JAAS ............................................................... 368
Cuestionario: Descripción de la especificación de JAAS ........................................... 373
Configuración de un dominio de seguridad en JBoss EAP ....................................... 376
Ejercicio guiado: Configuración de un dominio de seguridad en JBoss EAP ............... 380
Protección de una API REST ................................................................................. 385
Ejercicio guiado: Protección de una API REST ........................................................ 391
Trabajo de laboratorio: Protección de aplicaciones Java EE ..................................... 399
Resumen ............................................................................................................. 414
10. Revisión completa: Red Hat Application Development I: Programming in Java EE
415
Revisión completa .............................................................................................. 416
Trabajo de laboratorio: Creación de una API mediante JAX-RS ................................. 418
vi
JB183-EAP7.0-es-2-20180124
Trabajo de laboratorio: Persistencia de datos con JPA ............................................ 436
Trabajo de laboratorio: Protección de la API REST con JAAS .................................... 458
JB183-EAP7.0-es-2-20180124
vii
viii
Convenciones del documento
Notas y advertencias
Nota
Las "notas" son consejos, atajos o enfoques alternativos para una tarea
determinada. Omitir una nota no debe tener consecuencias negativas, pero quizás
se pase por alto algún truco que puede simplificar una tarea.
Importante
En las cajas (boxes) con la etiqueta "Importante", se detallan cosas que se olvidan
con facilidad: cambios de configuración que solo se aplican a la sesión actual o
servicios que se deben reiniciar para poder aplicar una actualización. Omitir una
caja (box) con la etiqueta "Importante" no provocará pérdida de datos, pero puede
causar irritación y frustración.
Advertencia
No se deben omitir las "advertencias". Es muy probable que omitir las advertencias
provoque pérdida de datos.
Referencias
En "Referencias", se describe el lugar donde se puede encontrar documentación
externa relevante para un tema.
JB183-EAP7.0-es-2-20180124
ix
x
Introducción
Red Hat Application Development I:
Programming in Java EE
Red Hat I Application Development I: Programming in Java EE (JB183) expone a los
desarrolladores experimentados de Java Standard Edition (Java SE) al mundo de Java
Enterprise Edition (Java EE). Los estudiantes aprenderán las diferentes especificaciones
que constituyen Java EE. A través de trabajos prácticos de laboratorio, los estudiantes
transformarán una aplicación de línea de comandos de Java SE simple en una aplicación
empresarial de varios niveles mediante diferentes especificaciones de Java EE, incluidos
Enterprise Java Beans, API de persistencia Java, Servicio de mensajería Java, JAX-RS para
servicios REST, Contextos e Inyección de dependencia, y seguridad.
Objetivos
• Describir las diferencias entre las arquitecturas de aplicaciones de Java SE y Java EE.
• Crear componentes de aplicaciones mediante las especificaciones EJB, JPA, JAX-RS, CDI, JMS
y JAAS.
• Desarrollar los componentes de backend necesarios para respaldar una aplicación web de
tres niveles mediante herramientas Maven y JBoss.
• Implementar aplicaciones en Red Hat JBoss Enterprise Application Platform.
Destinatarios
• Desarrolladores con experiencia en Java SE.
Requisitos previos
• Dominio en el desarrollo de aplicaciones de Java SE.
• Dominio en el uso de un IDE como Red Hat Developer Studio o Eclipse.
• Se recomienda experiencia en Maven.
JB183-EAP7.0-es-2-20180124
xi
Introducción
Orientación sobre el entorno del aula
Figura 0.1: Entorno del aula
En este curso, el sistema de cómputo principal utilizado para las actividades prácticas
de aprendizaje es workstation. Los estudiantes también utilizarán otra máquina,
services, para estas actividades. Ambos sistemas se encuentran en el dominio DNS
lab.example.com.
Todos los sistemas de cómputo de los estudiantes tienen una cuenta de usuario estándar
(student) con la contraseña student. La contraseña root de todos los sistemas de los
estudiantes es redhat.
Máquinas del aula
Nombre de la máquina
Direcciones IP
Rol
workstation.lab.example.com 172.25.250.254
Estación de trabajo
gráfica utilizada para la
administración del sistema
services.lab.example.com
Aloja el repositorio Nexus
172.25.250.10
Una función adicional de workstation es que actúa como enrutador entre la red que
conecta las máquinas de los estudiantes y la red del aula. Si la workstation está apagada,
otras máquinas de estudiantes solo podrán acceder a sistemas en la red de estudiantes.
Hay varios sistemas en el aula que brindan servicios de soporte. Dos servidores,
content.example.com y materials.example.com son fuentes de software y materiales
del trabajo de laboratorio utilizados en actividades prácticas. Se proveerá información sobre
cómo utilizar estos servidores en las instrucciones para estas actividades.
Control de la estación
En la parte superior de la consola, se describe el estado de su máquina.
xii
JB183-EAP7.0-es-2-20180124
Orientación sobre el entorno del aula
Estados de la máquina
Estado
Descripción
none (ninguno)
Todavía no se ha iniciado su máquina. Cuando se inicie, su máquina
arrancará en un estado recientemente inicializado (el disco se habrá
restablecido).
starting (en inicio)
Su máquina está por arrancar.
running (en
ejecución)
Su máquina se está ejecutando y está disponible (o bien, cuando
arranque, pronto lo estará).
stopping (en
detención)
Su máquina está por apagarse.
stopped (detenida)
Su máquina se ha apagado completamente. Al iniciarse, su máquina
arrancará en el mismo estado en el que estaba cuando se apagó (el
disco se preserva).
impaired (afectada)
No se puede realizar una conexión de red en su máquina. En
general, este estado se logra cuando un estudiante ha corrompido
las reglas de conexión de la red o del cortafuegos. Si se restablece la
máquina y el estado permanece, o si es intermitente, deberá abrir
un caso de soporte.
Según el estado de su máquina, tendrá disponibles una selección de las siguientes acciones.
Acciones de la máquina
Acción
Descripción
Start Station (Iniciar
estación)
Iniciar ("encender") la máquina.
Stop Station
(Detener estación)
Detener ("apagar") la máquina y preservar el contenido del disco.
Reset Station
(Restablecer
estación)
Detener ("apagar") la máquina y restablecer el disco de modo que
vuelva a su estado original. Precaución: Se perderá cualquier
trabajo generado en el disco.
Refresh (Actualizar)
Si se actualiza la página, se volverá a realizar un sondeo del estado
de la máquina.
Increase Timer
(Aumentar
temporizador)
Agrega 15 minutos al temporizador para cada clic.
Temporizador de la estación
Su inscripción al aprendizaje en línea de Red Hat le da derecho a una cierta cantidad de
tiempo de uso del equipo. Para ayudarlo a conservar su tiempo, las máquinas tienen un
temporizador asociado, que se inicializa en 60 minutos cuando se inicia su máquina.
El temporizador funciona como un "switch de seguridad", que disminuye mientras funciona
su máquina. Si el temporizador se reduce por debajo de 0, puede optar por incrementar el
temporizador.
JB183-EAP7.0-es-2-20180124
xiii
Introducción
Internacionalización
Soporte de idioma
Red Hat Enterprise Linux 7 admite oficialmente 22 idiomas: inglés, asamés, bengalí, chino
(simplificado), chino (tradicional), francés, alemán, guyaratí, hindi, italiano, japonés, canarés,
coreano, malayalam, maratí, oriya, portugués (brasileño), panyabí, ruso, español, tamil y
telugú.
Selección de idioma por usuario
Es posible que los usuarios prefieran usar un idioma para su entorno de escritorio distinto
al predeterminado del sistema. Quizás también quieran definir su cuenta para usar una
distribución del teclado o un método de entrada distinto.
Configuración de idioma
En el entorno de escritorio GNOME, posiblemente el usuario deba definir el idioma de
su preferencia y el método de entrada la primera vez que inicie sesión. Si no es así, la
manera más simple para un usuario individual de definir el idioma de su preferencia y el
método de entrada es usando la aplicación Region & Language (Región e idioma). Ejecute el
comando gnome-control-center region o, en la barra superior, seleccione (Usuario) >
Settings(Configuración). En la ventana que se abre, seleccione Region & Language (Región
e idioma). El usuario puede hacer clic en la caja (box) Language (Idioma) y seleccionar el
idioma de su preferencia en la lista que aparece. Esto también actualizará la configuración
de Formats (Formatos) mediante la adopción del valor predeterminado para ese idioma. La
próxima vez que el usuario inicie sesión, se efectuarán los cambios.
Estas configuraciones afectan el entorno de escritorio GNOME y todas las aplicaciones,
incluida gnome-terminal, que se inician dentro de este. Sin embargo, no se aplican a la
cuenta si el acceso a ella es mediante un inicio de sesión ssh desde un sistema remoto o
desde una consola de texto local (como tty2).
xiv
JB183-EAP7.0-es-2-20180124
Selección de idioma por usuario
nota
Un usuario puede hacer que su entorno de intérprete de comandos use la misma
configuración de LANG que su entorno gráfico, incluso cuando inicia sesión
mediante una consola de texto o mediante ssh. Una manera de hacer esto es
colocar un código similar al siguiente en el archivo ~/.bashrc del usuario. Este
código de ejemplo definirá el idioma empleado en un inicio de sesión en interfaz de
texto, de modo que coincida con el idioma actualmente definido en el entorno de
escritorio GNOME del usuario:
i=$(grep 'Language=' /var/lib/AccountService/users/${USER} \
| sed 's/Language=//')
if [ "$i" != "" ]; then
export LANG=$i
fi
Es posible que algunos idiomas, como el japonés, coreano, chino y otros con un
conjunto de caracteres no latinos, no se vean correctamente en consolas de texto
locales.
Se pueden crear comandos individuales para utilizar otro idioma mediante la configuración
de la variable LANG en la línea de comandos:
[user@host ~]$ LANG=fr_FR.utf8 date
jeu. avril 24 17:55:01 CDT 2014
Los comandos subsiguientes se revertirán y utilizarán el idioma de salida predeterminado del
sistema. El comando locale se puede usar para comprobar el valor actual de LANG y otras
variables de entorno relacionadas.
Configuración del método de entrada
GNOME 3 en Red Hat Enterprise Linux 7 emplea de manera automática el sistema de
selección de método de entrada IBus, que permite cambiar las distribuciones del teclado y
los métodos de entrada de manera rápida y sencilla.
La aplicación Region & Language (Región e idioma) también se puede usar para habilitar
métodos de entrada alternativos. En la ventana de la aplicación Region & Language (Región
e idioma), la caja (box) Input Sources (Fuentes de entrada) muestra los métodos de entrada
disponibles en este momento. De forma predeterminada, es posible que English (US) (Inglés
[EE. UU.]) sea el único método disponible. Resalte English (US) (Inglés [EE. UU.]) y haga clic en
el icono de keyboard (teclado) para ver la distribución actual del teclado.
Para agregar otro método de entrada, haga clic en el botón +, en la parte inferior izquierda
de la ventana Input Sources (Fuentes de entrada). Se abrirá la ventana Add an Input Source
(Agregar una fuente de entrada). Seleccione su idioma y, luego, el método de entrada o la
distribución del teclado de su preferencia.
Una vez que haya más de un método de entrada configurado, el usuario puede alternar entre
ellos rápidamente escribiendo Super+Space (en ocasiones denominado Windows+Space).
También aparecerá un indicador de estado en la barra superior de GNOME con dos funciones:
por un lado, indica el método de entrada activo; por el otro lado, funciona como un menú
JB183-EAP7.0-es-2-20180124
xv
Introducción
que puede usarse para cambiar de un método de entrada a otro o para seleccionar funciones
avanzadas de métodos de entrada más complejos.
Algunos de los métodos están marcados con engranajes, que indican que tienen opciones de
configuración y capacidades avanzadas. Por ejemplo, el método de entrada japonés Japanese
(Kana Kanji) (japonés [Kana Kanji]) le permite al usuario editar previamente texto en latín
y usar las teclas de Down Arrow (flecha hacia abajo) y Up Arrow (flecha hacia arriba) para
seleccionar los caracteres correctos que se usarán.
El indicador también puede ser de utilidad para los hablantes de inglés de Estados Unidos.
Por ejemplo, dentro de English (United States) (Inglés [Estados Unidos]) está la configuración
del teclado English (international AltGr dead keys) (Inglés [internacional, teclas inactivas
AltGr]), que trata AltGr (o la tecla Alt derecha) en un teclado de 104/105 teclas de
una PC como una tecla modificadora "Bloq Mayús secundaria" y tecla de activación de
teclas inactivas para escribir caracteres adicionales. Hay otras distribuciones alternativas
disponibles, como Dvorak.
nota
Cualquier carácter Unicode puede ingresarse en el entorno de escritorio GNOME,
si el usuario conoce el código Unicode del carácter, escribiendo Ctrl+Shift+U,
seguido por el código. Después de ingresar Ctrl+Shift+U, aparecerá una u
subrayada que indicará que el sistema espera la entrada del código Unicode.
Por ejemplo, la letra del alfabeto griego en minúscula lambda tiene el código U
+03BB y puede ingresarse escribiendo Ctrl+Shift+U, luego 03bb y, por último,
Enter.
Configuración de idioma predeterminado en todo el
sistema
El idioma predeterminado del sistema está definido en US English (inglés de EE. UU.), que
utiliza la codificación UTF-8 de Unicode como su conjunto de caracteres (en_US.utf8), pero
puede cambiarse durante o después de la instalación.
Desde la línea de comandos, root puede cambiar los ajustes de configuración regional de
todo el sistema con el comando localectl. Si localectl se ejecuta sin argumentos,
mostrará los ajustes actuales de configuración regional de todo el sistema.
Para configurar el idioma de todo el sistema, ejecute el comando localectl set-locale
LANG=locale, donde locale es el $LANG adecuado de la tabla "Referencia de códigos de
idioma" en este capítulo. El cambio tendrá efecto para usuarios en su próximo inicio de
sesión y se almacena en /etc/locale.conf.
[root@host ~]# localectl set-locale LANG=fr_FR.utf8
En GNOME, un usuario administrativo puede cambiar esta configuración en Region &
Language (Región e idioma) y con un clic en el botón Login Screen (Pantalla de inicio de
sesión) ubicado en la esquina superior derecha de la ventana. Al cambiar la opción de
Language (Idioma) de la pantalla de inicio de sesión, también ajustará el valor de idioma
predeterminado de todo el sistema en el archivo de configuración /etc/locale.conf.
xvi
JB183-EAP7.0-es-2-20180124
Paquetes de idiomas
Importante
Las consolas de texto locales como tty2 están más limitadas en las fuentes
que pueden mostrar que las sesiones gnome-terminal y ssh. Por ejemplo, los
caracteres del japonés, coreano y chino posiblemente no se visualicen como se
espera en una consola de texto local. Por este motivo, es posible que tenga sentido
usar el inglés u otro idioma con un conjunto de caracteres latinos para la consola de
texto del sistema.
De manera similar, las consolas de texto locales admiten una cantidad de métodos
de entrada más limitada y esto se administra de manera separada desde el entorno
de escritorio gráfico. La configuración de entrada global disponible se puede
configurar mediante localectl tanto para consolas virtuales de texto locales
como para el entorno gráfico X11. Para obtener más información, consulte las
páginas del manual localectl(1), kbd(4) y vconsole.conf(5).
Paquetes de idiomas
Si utiliza un idioma diferente al inglés, posiblemente desee instalar "paquetes de idiomas"
adicionales para disponer de traducciones adicionales, diccionarios, etc. Para ver la lista de
paquetes de idiomas disponibles, ejecute yum langavailable. Para ver la lista de paquetes
de idiomas actualmente instalados en el sistema, ejecute yum langlist. Para agregar un
paquete de idioma adicional al sistema, ejecute yum langinstall code, donde code es el
código en corchetes después del nombre del idioma en la salida de yum langavailable.
Referencias
Páginas del manual: locale(7), localectl(1), kbd(4), locale.conf(5),
vconsole.conf(5), unicode(7), utf-8(7) y yum-langpacks(8).
Las conversiones entre los nombres de los diseños X11 del entorno de escritorio
gráfico y sus nombres en localectl se pueden encontrar en el archivo /usr/
share/X11/xkb/rules/base.lst.
JB183-EAP7.0-es-2-20180124
xvii
Introducción
Referencia de códigos de idioma
Códigos de idioma
Idioma
Valor $LANG
Inglés (EE. UU.)
en_US.utf8
Asamés
as_IN.utf8
Bengalí
bn_IN.utf8
Chino (simplificado)
zh_CN.utf8
Chino (tradicional)
zh_TW.utf8
Francés
fr_FR.utf8
Alemán
de_DE.utf8
Guyaratí
gu_IN.utf8
Hindi
hi_IN.utf8
Italiano
it_IT.utf8
Japonés
ja_JP.utf8
Canarés
kn_IN.utf8
Coreano
ko_KR.utf8
Malayalam
ml_IN.utf8
Maratí
mr_IN.utf8
Odia
or_IN.utf8
Portugués (brasileño)
pt_BR.utf8
Panyabí
pa_IN.utf8
Ruso
ru_RU.utf8
Español
es_ES.utf8
Tamil
ta_IN.utf8
Telugú
te_IN.utf8
xviii
JB183-EAP7.0-es-2-20180124
TRAINING
CAPÍTULO 1
TRANSICIÓN A APLICACIONES
CON VARIOS NIVELES
Descripción general
Meta
Describir características de Java EE y distinguir entre las
aplicaciones Java EE y Java SE.
Objetivos
• Describir "aplicación empresarial" y nombrar algunos de
los beneficios de las aplicaciones Java EE.
• Comparar las características de Java EE y Java SE.
• Describir las especificaciones y los números de versión
de Java EE 7 y el proceso utilizado para introducir API
nuevas y actualizadas en Java EE.
• Describir las diferentes arquitecturas con varios niveles.
• Instalar JBoss Developer Studio, Maven y JBoss
Enterprise Application Platform.
Secciones
• Descripción de las aplicaciones empresariales (y
cuestionario)
• Comparación de características de Java EE y Java SE (y
cuestionario)
• Descripción del Proceso de la comunidad Java (y
cuestionario)
• Descripción de la arquitectura de aplicaciones con
varios niveles (y cuestionario)
• Instalación de las herramientas de desarrollo de Java (y
ejercicio guiado)
JB183-EAP7.0-es-2-20180124
1
Capítulo 1. Transición a aplicaciones con varios niveles
Descripción de las aplicaciones
empresariales
Objetivos
Después de completar esta sección, los estudiantes deberán ser capaces de describir los
conceptos y beneficios básicos de las aplicaciones empresariales.
Aplicaciones empresariales
Una aplicación empresarial es una aplicación de software generalmente utilizada en grandes
organizaciones de negocios. Las aplicaciones empresariales a menudo proporcionan las
siguientes funciones:
• Soporte para usuarios simultáneos y sistemas externos.
• Soporte para comunicación sincrónica y asíncrona a través de diferentes protocolos.
• Capacidad de manejar cargas de trabajo de transacciones que coordinan entre repositorios
de datos, como colas y bases de datos.
• Soporte para que la escalabilidad maneje el crecimiento futuro.
• Una plataforma flexible y distribuida para garantizar alta disponibilidad.
• Soporte para un control de acceso altamente seguro para diferentes tipos de usuarios.
• La capacidad de integrarse con sistemas de back-end y servicios web.
Ejemplos típicos de aplicaciones empresariales incluyen la Planificación de recursos de
empresa (ERP), la Gestión de relaciones con el cliente (CRM), los Sistemas de gestión de
contenido (CMS), sistemas de comercio electrónico, Internet y portales de intranet.
Beneficios de las aplicaciones empresariales Java EE
Java Enterprise Edition (Java EE) es una especificación para desarrollar aplicaciones
empresariales utilizando Java. Es un estándar independiente de la plataforma, desarrollado
con la orientación del Proceso de la comunidad Java (JCP).
La especificación Java EE 7 consta de una serie de interfaces de programación de
aplicaciones (API) de componentes implementadas por un servidor de aplicaciones. La
Red Hat JBoss Enterprise Application Platform (EAP) que utilizará en este curso implementa el
estándar Java EE.
Los beneficios de desarrollar aplicaciones empresariales basadas en Java EE son:
• Las aplicaciones independientes de la plataforma se pueden desarrollar y se ejecutarán en
muchos tipos diferentes de sistemas operativos (tanto en PC pequeñas como en grandes
mainframes).
• Las aplicaciones son portátiles en servidores de aplicaciones que cumplen con Java EE,
debido al estándar Java EE.
2
JB183-EAP7.0-es-2-20180124
Beneficios de las aplicaciones empresariales Java EE
• La especificación Java EE proporciona un gran número de API, generalmente usadas por
aplicaciones empresariales, como servicios web, mensajería asíncrona, transacciones,
conectividad de la base de datos, conjuntos (pools) de hilos, utilidades de procesamiento
por lotes y seguridad. No hay necesidad de desarrollar estos componentes en forma
manual, de modo que es posible reducir el tiempo de desarrollo.
• Un gran número de aplicaciones y componentes de terceros, listos para usar, dirigidos a
dominios específicos, como finanzas, seguros, telecomunicaciones y otros sectores, están
certificados para ejecutarse e integrarse con servidores de aplicaciones Java EE.
• Un gran número de herramientas sofisticadas, como IDE, sistemas de monitoreo,
marcos (frameworks) de integración de aplicaciones empresariales (EAI) y herramientas
de medición de rendimiento, están disponibles para aplicaciones Java EE de terceros
proveedores.
Referencias
JCP
https://www.jcp.org/en/home/index
JB183-EAP7.0-es-2-20180124
3
Capítulo 1. Transición a aplicaciones con varios niveles
Cuestionario: Descripción de las aplicaciones
empresariales
Elija las respuestas correctas para las siguientes preguntas:
1.
¿Cuáles dos de los siguientes enunciados se pueden considerar una aplicación
empresarial? (Elija dos opciones).
a.
b.
c.
d.
e.
2.
¿Cuáles dos de los siguientes enunciados acerca de la especificación Java EE y los
servidores de aplicaciones son correctos? (Elija dos opciones).
a.
b.
c.
d.
e.
3.
Existen diferentes versiones de la especificación para diferentes sistemas
operativos. Las aplicaciones se deben implementar de manera diferente en cada
sistema operativo, aprovechando las funciones específicas del sistema operativo.
Aunque una aplicación se implementa para cumplir completamente con Java EE,
necesita reimplementar ciertas funciones y recopilar la aplicación al implementarla
en diferentes servidores que cumplen con Java EE.
Una aplicación que cumpla completamente con Java EE se puede implementar
en diferentes servidores que cumplen con Java EE, sin tener que recopilar ni
reimplementar funciones.
Un servidor de aplicaciones que cumple con Java EE proporciona instalaciones de
mensajería asíncrona.
Un servidor de aplicaciones que cumple con Java EE no proporciona funciones
de agrupación de hilos de manera predeterminada. El desarrollador debe
implementar esta función en forma manual.
¿Qué enunciado que describe un servidor de aplicaciones que cumple con Java EE
es correcto?
a.
b.
c.
d.
4
Un sistema bancario en línea para un banco con millones de clientes.
Un programa que calcula el análisis factorial de los números entre 1 y 100 000.
Un sistema incorporado en tiempo real que controla un satélite remoto.
Una puerta de enlace de pago en línea para una empresa de tarjetas de crédito
que procesa millones de transacciones por día.
Un programa que simula una representación 3D de un avión para estudiar el
impacto de la turbulencia en aviones de diferentes formas y tamaños.
La capacidad de crear servicios web (SOAP, REST) no se proporciona de manera
predeterminada.
No hay instalaciones de gestión de transacciones disponibles. El desarrollador
debe gestionar todas las transacciones en forma manual.
El servidor de aplicaciones proporciona una gestión de transacciones automática.
De requerirse, el desarrollador puede escribir código para también gestionar
transacciones en forma manual.
No se proporcionan API de conectividad de base de datos (JPA) de manera
predeterminada. Para la conexión a las bases de datos, se deben utilizar librerías
externas de terceros.
JB183-EAP7.0-es-2-20180124
4.
Una empresa llamada ABC Inc está migrando una aplicación de mainframe
heredada grande y compleja, escrita en COBOL™, a la plataforma Java EE. ¿Cuáles
tres de las siguientes funciones se pueden aprovechar desde un servidor de
aplicaciones que cumple con Java EE sin tener que implementarlas en forma
manual? (Elija tres opciones).
a.
b.
c.
d.
e.
Conectividad de la base de datos a un RDBMS (que cumple con JDBC).
Una utilidad que lee y escribe archivos codificados de EBCDIC hacia y desde un
sistema de mainframe heredado con un sistema de archivos propietario.
Seguridad basada en funciones.
Operaciones de lotes para una ejecución programada de una aplicación de
informes, que genera informes de un RDBMS diaria, mensual y trimestralmente.
Un adaptador personalizado para comunicarse con un sistema de gestión de base
de datos jerárquico heredado remoto que no cumple con JDBC.
JB183-EAP7.0-es-2-20180124
5
Capítulo 1. Transición a aplicaciones con varios niveles
Solución
Elija las respuestas correctas para las siguientes preguntas:
1.
¿Cuáles dos de los siguientes enunciados se pueden considerar una aplicación
empresarial? (Elija dos opciones).
a.
b.
c.
d.
e.
2.
¿Cuáles dos de los siguientes enunciados acerca de la especificación Java EE y los
servidores de aplicaciones son correctos? (Elija dos opciones).
a.
b.
c.
d.
e.
3.
b.
c.
d.
6
Existen diferentes versiones de la especificación para diferentes sistemas
operativos. Las aplicaciones se deben implementar de manera diferente en cada
sistema operativo, aprovechando las funciones específicas del sistema operativo.
Aunque una aplicación se implementa para cumplir completamente con Java EE,
necesita reimplementar ciertas funciones y recopilar la aplicación al implementarla
en diferentes servidores que cumplen con Java EE.
Una aplicación que cumpla completamente con Java EE se puede implementar
en diferentes servidores que cumplen con Java EE, sin tener que recopilar ni
reimplementar funciones.
Un servidor de aplicaciones que cumple con Java EE proporciona instalaciones
de mensajería asíncrona.
Un servidor de aplicaciones que cumple con Java EE no proporciona funciones
de agrupación de hilos de manera predeterminada. El desarrollador debe
implementar esta función en forma manual.
¿Qué enunciado que describe un servidor de aplicaciones que cumple con Java EE
es correcto?
a.
4.
Un sistema bancario en línea para un banco con millones de clientes.
Un programa que calcula el análisis factorial de los números entre 1 y 100 000.
Un sistema incorporado en tiempo real que controla un satélite remoto.
Una puerta de enlace de pago en línea para una empresa de tarjetas de crédito
que procesa millones de transacciones por día.
Un programa que simula una representación 3D de un avión para estudiar el
impacto de la turbulencia en aviones de diferentes formas y tamaños.
La capacidad de crear servicios web (SOAP, REST) no se proporciona de manera
predeterminada.
No hay instalaciones de gestión de transacciones disponibles. El desarrollador
debe gestionar todas las transacciones en forma manual.
El servidor de aplicaciones proporciona una gestión de transacciones
automática. De requerirse, el desarrollador puede escribir código para también
gestionar transacciones en forma manual.
No se proporcionan API de conectividad de base de datos (JPA) de manera
predeterminada. Para la conexión a las bases de datos, se deben utilizar librerías
externas de terceros.
Una empresa llamada ABC Inc está migrando una aplicación de mainframe
heredada grande y compleja, escrita en COBOL™, a la plataforma Java EE. ¿Cuáles
tres de las siguientes funciones se pueden aprovechar desde un servidor de
JB183-EAP7.0-es-2-20180124
Solución
aplicaciones que cumple con Java EE sin tener que implementarlas en forma
manual? (Elija tres opciones).
a.
b.
c.
d.
e.
Conectividad de la base de datos a un RDBMS (que cumple con JDBC).
Una utilidad que lee y escribe archivos codificados de EBCDIC hacia y desde un
sistema de mainframe heredado con un sistema de archivos propietario.
Seguridad basada en funciones.
Operaciones de lotes para una ejecución programada de una aplicación de
informes, que genera informes de un RDBMS diaria, mensual y trimestralmente.
Un adaptador personalizado para comunicarse con un sistema de gestión de base
de datos jerárquico heredado remoto que no cumple con JDBC.
JB183-EAP7.0-es-2-20180124
7
Capítulo 1. Transición a aplicaciones con varios niveles
Comparación de características de Java EE y
Java SE
Objetivos
Tras finalizar esta sección, los estudiantes deberán ser capaces de realizar lo siguiente:
• Describir las diferencias entre Java EE y Java SE.
• Ejecutar la aplicación desde la línea de comando.
• Obtener una vista previa de la aplicación web.
Comparación de Java Enterprise Edition (Java EE) y
Java SE
Cuando instala el kit Java Development Kit (JDK) para su sistema operativo, proporciona
el recopilador, el depurador, las herramientas y el entorno de tiempo de ejecución para
alojar sus aplicaciones Java, la Máquina virtual Java (JVM) y un gran conjunto de clases
de componentes reutilizables comúnmente usados por aplicaciones. La interfaz de
programación de aplicaciones (API) proporciona paquetes y clases para redes, E/S, análisis
XML, conectividad de base de datos, desarrollo de interfaces gráficas de usuario (GUI) y
mucho más. Esta API se conoce comúnmente como Java Standard Edition (Java SE).
Java SE generalmente se usa para desarrollar programas, herramientas y utilidades
independientes, que se ejecutan principalmente desde la línea de comando, programas de
GUI y procesos de servidores que necesitan ejecutarse como daemons (esto es, programas
que se ejecutan continuamente en segundo plano, hasta que se detienen).
La especificación Java EE es un conjunto de API basado en Java SE. Proporciona un entorno de
tiempo de ejecución para ejecutar aplicaciones empresariales multiproceso, transaccionales,
seguras y escalables. Es importante observar que, a diferencia de Java SE, Java EE es
básicamente un conjunto de especificaciones estándares para una API, y los entornos de
tiempo de ejecución que implementan estas API generalmente se denominan servidores de
aplicaciones.
Un servidor de aplicaciones que envía un conjunto de pruebas denominado Technology
Compatibility Kit (TCK) para Java EE se conoce como un servidor de aplicaciones que cumple
con Java EE. Existen diferentes versiones de Java EE. Si bien se agregan en forma incremental
API y funciones nuevas en cada nueva versión, la compatibilidad con versiones anteriores se
mantiene de manera estricta.
Java EE incluye soporte para varios perfiles o subconjuntos de API. Por ejemplo, la
especificación Java EE 7 define dos perfiles: el perfil completo y el perfil web.
El perfil web de Java EE 7 está diseñado para el desarrollo de aplicaciones web y admite un
subconjunto de API definido por tecnologías basadas en la Web y relacionadas con Java EE 7.
El perfil completo de Java EE 7 contiene todas las API definidas por Java EE 7 (incluidos todos
los ítems en el perfil web). Al desarrollar los EJB, las aplicaciones de mensajería y los servicios
web (en contraste con las aplicaciones web), debe usar el perfil completo.
8
JB183-EAP7.0-es-2-20180124
Compilación, empaquetado e implementación de aplicaciones Java SE y Java EE
Un servidor de aplicaciones que cumple con Java EE 7, como
Red Hat JBoss Enterprise Application Platform (EAP), implementa ambos perfiles y
proporciona una serie de API que generalmente se utilizan en aplicaciones empresariales,
incluidas las siguientes:
• Batch API (API de lote)
• API de Java para el procesamiento de JSON (JSON-P)
• Utilidades de simultaneidad
• API para WebSocket
• Servicio de mensajería Java (JMS)
• API de persistencia Java (JPA)
• Arquitectura de conector Java (JCA)
• API de Java para servicios web RESTful (JAX-RS)
• API de Java para servicios web XML (JAX-WS)
• API de Servlet
• Caras de servidor Java (JSF)
• Páginas de servidor Java (JSP)
• Contextos e Inyección de dependencia (CDI)
• API de transacción Java (JTA)
• Enterprise Java Beans (EJB)
• API de validación de beans
Compilación, empaquetado e implementación de
aplicaciones Java SE y Java EE
En el caso de aplicaciones Java SE independientes relativamente simples, el código se
puede compilar, empaquetar y ejecutar en la línea de comando usando el compilador y las
herramientas de tiempo de ejecución (java, javac, jar, jdb, etc.) que forman parte del JDK.
Varios Entornos de desarrollo integrado (IDE) maduros, como Red Hat JBoss Developer Studio
(JBDS) o Eclipse, se utilizan para simplificar el proceso de compilación y empaquetado.
La forma preferida de enviar aplicaciones Java independientes con un enfoque neutro con
respecto a la plataforma es empaquetar la aplicación como un archivo Java Archive (JAR).
Los archivos JAR pueden hacerse ejecutables de manera opcional al agregar entradas de
manifiestos (un archivo de texto simple empaquetado junto con las clases de Java dentro del
archivo JAR) al archivo JAR para indicar la principal clase ejecutable.
Las aplicaciones Java EE constan de varios componentes que dependen de un gran
número de archivos JAR que se requieren durante el tiempo de ejecución. El proceso
de implementación para aplicaciones Java EE es diferente. Las aplicaciones Java
JB183-EAP7.0-es-2-20180124
9
Capítulo 1. Transición a aplicaciones con varios niveles
EE se implementan en un servidor de aplicaciones compatible con Java EE y estas
implementaciones pueden ser de diferentes tipos:
• JAR files (Archivos JAR): módulos individuales de una aplicación y Enterprise Java Beans
(EJB) se pueden implementar como archivos JAR separados. Las librerías y los marcos
(frameworks) de terceros también se empaquetan como archivos JAR. Si su aplicación
depende de estas librerías, los archivos JAR de la librería se deben implementar en el
servidor de aplicaciones. Los archivos JAR tienen una ampliación .jar.
• Web Archive (WAR) files (Archivos WAR): si su aplicación Java EE tiene un front-end
basado en la Web o está proporcionando extremos de servicio RESTful, el código y los
activos relacionados con el front-end web y los servicios se pueden empaquetar como
un archivo WAR. Un archivo WAR tiene una ampliación .war y es básicamente un archivo
comprimido que contiene código, HTML estático, imágenes, CSS y activos JS, así como
archivos de descriptores de implementación XML, junto con archivos JAR dependientes
empaquetados en su interior.
• Enterprise Archive (EAR) files (Archivos EAR): un archivo EAR tiene una ampliación de
.ear y, básicamente, se trata de un archivo comprimido con uno o más archivos WAR o JAR
y algunos descriptores de implementación XML en su interior. Es útil en escenarios en los
que la aplicación cuenta con varios archivos WAR o reutiliza algunos archivos JAR comunes
en módulos. En dichos casos, es más sencillo implementar y gestionar la aplicación como
una única unidad implementable.
También es una práctica recomendada usar una herramienta de compilación, como Apache
Maven, para simplificar la compilación, el empaquetado, las pruebas, la ejecución y la
implementación de aplicaciones Java SE y Java EE. Maven cuenta con una arquitectura de
complementos (plug-ins) para ampliar su funcionalidad core.
Existen complementos (plug-ins) de Maven para la compilación, el empaquetado y la
implementación de aplicaciones Java EE. Se admiten todos los tipos de implementación.
Maven también puede implementar aplicaciones y anular su implementación hacia y desde
JBoss EAP sin reiniciar el servidor de aplicaciones. Los Entornos de desarrollo integrado
(IDE), como JBoss Developer Studio (JBDS), también cuentan con soporte nativo para Maven
integrado de manera predeterminada. Todas las tareas de Maven se pueden ejecutar desde
dentro del propio JBDS sin usar la línea de comando.
Para ejecutar una aplicación independiente que usa solamente una API de Java SE, como, por
ejemplo, la aplicación To Do List basada en la línea de comando que está empaquetada como
un archivo JAR, puede usar el comando java -jar:
[student@workstation todojse]$ java -jar todojse-1.0.jar
10
JB183-EAP7.0-es-2-20180124
Compilación, empaquetado e implementación de aplicaciones Java SE y Java EE
Figura 1.1: La aplicación To Do List basada en Java SE independiente
En contraste, la versión basada en la Web se implementa en el servidor de aplicaciones que
cumple con Java EE. La aplicación To Do List de ejemplo está empaquetada como un archivo
WAR que se implementa en un servidor de aplicaciones como EAP.
Si ya se implementó una versión anterior del archivo WAR, se anula la implementación de
la versión antigua y se implementa la versión nueva sin reiniciar el servidor de aplicaciones.
Dicho proceso se denomina implementación en caliente y se utiliza ampliamente durante el
desarrollo y las pruebas, así como en implementaciones de producción.
Figura 1.2: Lista de tareas de la aplicación To Do List basada en la Web Java EE 7
JB183-EAP7.0-es-2-20180124
11
Capítulo 1. Transición a aplicaciones con varios niveles
Figura 1.3: Agregue una tarea a la aplicación To Do List
Referencias
JSR de la Especificación Java EE 7
https://www.jcp.org/en/jsr/detail?id=342
Notas sobre la versión Red Hat JBoss EAP 7
https://access.redhat.com/documentation/en/red-hat-jboss-enterpriseapplication-platform/version-7.0
12
JB183-EAP7.0-es-2-20180124
Cuestionario: Comparación de Java EE y Java SE
Cuestionario: Comparación de Java EE y Java
SE
Elija las respuestas correctas para las siguientes preguntas:
1.
¿Cuál de las siguientes API es parte de la especificación Java EE 7?
a.
b.
c.
d.
e.
2.
¿Cuáles dos de los siguientes enunciados acerca de la especificación Java EE 7 son
correctos? (Elija dos opciones).
a.
b.
c.
d.
3.
Java EE 7 es una especificación, no una implementación que se puede utilizar
directamente.
Debe haber implementaciones de Java EE 7 por separado para diferentes sistemas
operativos.
El estándar Java EE 7 define dos perfiles: web y completo.
El estándar Java EE 7 define cuatro perfiles: pequeño, medio, web y completo.
¿Cuáles dos de los siguientes enunciados acerca de Java SE son correctos? (Elija dos
opciones).
a.
b.
c.
d.
4.
Conectividad de base de datos Java (JDBC)
Caras de servidor Java (JSF)
Ampliaciones de criptografía Java (JCE)
Swing Java
Seguridad Java
Java SE se utiliza para desarrollar aplicaciones independientes, como
herramientas, utilidades y aplicaciones GUI, que se pueden ejecutar desde la línea
de comando.
Debe haber implementaciones de Java SE por separado para diferentes sistemas
operativos.
Java SE se puede utilizar para escribir una aplicación a fin de almacenar datos en la
base de datos.
Java SE no se puede utilizar para escribir aplicaciones multiproceso.
¿Cuáles dos de los siguientes enunciados acerca de los tipos de implementación en
Java EE 7 son correctos? (Elija dos opciones).
a.
b.
c.
d.
Las aplicaciones web generalmente vienen empaquetadas como archivos JAR para
su implementación en un servidor de aplicaciones.
Las aplicaciones web generalmente vienen empaquetadas como archivos WAR
para su implementación en un servidor de aplicaciones.
Un archivo WAR puede contener archivos EAR y JAR, así como descriptores de
implementación.
Un archivo EAR puede contener archivos WAR, archivos JAR y descriptores de
implementación.
JB183-EAP7.0-es-2-20180124
13
Capítulo 1. Transición a aplicaciones con varios niveles
e.
5.
¿Cuáles dos de los siguientes enunciados acerca de Apache Maven son correctos?
(Elija dos opciones).
a.
b.
c.
d.
e.
14
Un archivo EAR no se puede implementar directamente dentro de un servidor de
aplicaciones. Debe implementar los archivos WAR y JAR en su interior de manera
separada.
Maven se puede usar para compilar, empaquetar y probar ambas aplicaciones
Java EE y Java SE.
Maven se puede usar únicamente para compilar, empaquetar y probar
aplicaciones Java EE. Maven no se puede utilizar para compilar, empaquetar y
probar aplicaciones Java SE.
Maven no puede implementar aplicaciones ni anular su implementación hacia o
desde JBoss EAP. Debe reiniciar manualmente el servidor de aplicaciones después
de implementar o dejar de implementar.
Maven puede implementar aplicaciones y anular su implementación
automáticamente desde JBoss EAP. No hay necesidad de reiniciar el servidor de
aplicaciones después de implementar o dejar de implementar.
No hay soporte de IDE para tareas de Maven. Todas las tareas de Maven se deben
invocar desde la línea de comando.
JB183-EAP7.0-es-2-20180124
Solución
Solución
Elija las respuestas correctas para las siguientes preguntas:
1.
¿Cuál de las siguientes API es parte de la especificación Java EE 7?
a.
b.
c.
d.
e.
2.
¿Cuáles dos de los siguientes enunciados acerca de la especificación Java EE 7 son
correctos? (Elija dos opciones).
a.
b.
c.
d.
3.
Java EE 7 es una especificación, no una implementación que se puede utilizar
directamente.
Debe haber implementaciones de Java EE 7 por separado para diferentes sistemas
operativos.
El estándar Java EE 7 define dos perfiles: web y completo.
El estándar Java EE 7 define cuatro perfiles: pequeño, medio, web y completo.
¿Cuáles dos de los siguientes enunciados acerca de Java SE son correctos? (Elija dos
opciones).
a.
b.
c.
d.
4.
Conectividad de base de datos Java (JDBC)
Caras de servidor Java (JSF)
Ampliaciones de criptografía Java (JCE)
Swing Java
Seguridad Java
Java SE se utiliza para desarrollar aplicaciones independientes, como
herramientas, utilidades y aplicaciones GUI, que se pueden ejecutar desde la
línea de comando.
Debe haber implementaciones de Java SE por separado para diferentes sistemas
operativos.
Java SE se puede utilizar para escribir una aplicación a fin de almacenar datos
en la base de datos.
Java SE no se puede utilizar para escribir aplicaciones multiproceso.
¿Cuáles dos de los siguientes enunciados acerca de los tipos de implementación en
Java EE 7 son correctos? (Elija dos opciones).
a.
b.
c.
d.
e.
Las aplicaciones web generalmente vienen empaquetadas como archivos JAR para
su implementación en un servidor de aplicaciones.
Las aplicaciones web generalmente vienen empaquetadas como archivos WAR
para su implementación en un servidor de aplicaciones.
Un archivo WAR puede contener archivos EAR y JAR, así como descriptores de
implementación.
Un archivo EAR puede contener archivos WAR, archivos JAR y descriptores de
implementación.
Un archivo EAR no se puede implementar directamente dentro de un servidor de
aplicaciones. Debe implementar los archivos WAR y JAR en su interior de manera
separada.
JB183-EAP7.0-es-2-20180124
15
Capítulo 1. Transición a aplicaciones con varios niveles
5.
¿Cuáles dos de los siguientes enunciados acerca de Apache Maven son correctos?
(Elija dos opciones).
a.
b.
c.
d.
e.
16
Maven se puede usar para compilar, empaquetar y probar ambas aplicaciones
Java EE y Java SE.
Maven se puede usar únicamente para compilar, empaquetar y probar
aplicaciones Java EE. Maven no se puede utilizar para compilar, empaquetar y
probar aplicaciones Java SE.
Maven no puede implementar aplicaciones ni anular su implementación hacia o
desde JBoss EAP. Debe reiniciar manualmente el servidor de aplicaciones después
de implementar o dejar de implementar.
Maven puede implementar aplicaciones y anular su implementación
automáticamente desde JBoss EAP. No hay necesidad de reiniciar el servidor de
aplicaciones después de implementar o dejar de implementar.
No hay soporte de IDE para tareas de Maven. Todas las tareas de Maven se deben
invocar desde la línea de comando.
JB183-EAP7.0-es-2-20180124
Descripción del Proceso de la comunidad Java
Descripción del Proceso de la comunidad
Java
Objetivos
Tras finalizar esta sección, los estudiantes deberán ser capaces de realizar lo siguiente:
• Describir el propósito del Proceso de la comunidad Java (JCP).
• Describir el proceso mediante el cual el JCP define, publica y mantiene las Solicitudes de
especificación Java (JSR).
• Explorar los números de especificación y versión de API que constituyen la especificación
de Java EE 7.
El Proceso de la comunidad Java (JCP)
El Proceso de la comunidad Java (JCP) es un proceso abierto y de participación para desarrollar,
mantener y revisar las especificaciones de tecnología Java. El JCP fomenta la evolución de la
plataforma Java en colaboración con la comunidad internacional de desarrolladores de Java.
Todo individuo o toda organización pueden unirse al JCP y participar en el proceso de
estandarización. El JCP gestiona la especificación de un gran número de API a través de las
Solicitudes de especificación Java (JSR).
Los miembros pueden enviar propuestas de JSR para cualquier API de Java. La propuesta
será revisada por el Comité Ejecutivo (EC) de JCP, que está compuesto de varios líderes sénior
de la comunidad Java elegidos por los miembros de JCP. Una vez aprobada, la propuesta es
gestionada por un equipo de miembros de JCP denominado Grupo de expertos (Expert Group,
EG).
El grupo de expertos es responsable de definir, finalizar y mantener la especificación de la
API bajo el liderazgo de un Director de especificaciones (Specification Lead, SL). Cuando la JSR
está lista para su publicación, es aprobada por el comité ejecutivo y se vuelve un estándar
de JCP. Cada JSR puede evolucionar y se puede perfeccionar en forma incremental según las
necesidades de los desarrolladores de Java y las tendencias de tecnologías actuales.
Todas las API individuales que constituyen la especificación Java EE 7, gestionada en una
JSR por separado, han sido desarrolladas bajo el proceso JCP y tienen JSR individuales
gestionadas por grupos de expertos separados, que se especializan en un ámbito particular
de la tecnología.
El JCP, en colaboración con los grupos de expertos, también es responsable de publicar un
Technology Compatibility Kit (TCK), un conjunto de prueba que verifica si una implementación
cumple con la especificación. Por ejemplo, se dice que un servidor de aplicaciones es
"compatible con Java EE 7" solo si aprueba el kit TCK de Java EE 7 por completo, sin errores ni
fallas.
La especificación Java EE 7 (JSR 342)
La especificación Java EE 7 se estandarizó y se publicó como JSR 342. La propia
especificación Java EE 7 combina una serie de API, cada una de las cuales está estandarizada
con su propio número de JSR único. Diferentes versiones de las API individuales tienen sus
JB183-EAP7.0-es-2-20180124
17
Capítulo 1. Transición a aplicaciones con varios niveles
propios números de JSR. Por ejemplo, la especificación EJB 3.1 está estandarizada como JSR
318, mientras que EJB 3.0 se estandarizó como JSR 220, y la especificación EJB 2.1, como
JSR 153.
La especificación Java EE 7 (JSR 342) está disponible en http://download.oracle.com/otndocs/jcp/
java_ee-7-fr-eval-spec/index.html
Para conocer el proceso detallado seguido por el JCP al crear y lanzar JSR, y obtener una lista
completa de todas las JSR y todos los detalles de cada una, consulte los enlaces provistos en
las Referencias al final de esta sección.
En la siguiente tabla se enumeran las API en la Web y los perfiles completos en Java EE 7, así
como su versión y sus números de JSR correspondientes:
Perfil
Especificación
Versión
N.º de JSR
Web
API de Java para WebSocket
1.0
356
API de Java para el procesamiento de JSON
(JSON-P)
1.0
353
API de Java Servlet
3.1
340
Caras de servidor Java (JSF)
2.2
344
Lenguaje de expresión (EL)
3.0
341
Páginas de servidor Java (JSP)
2.3
245
Librería de etiquetas estándar para páginas
de JavaServer (JSTL)
1.2
52
Contextos e Inyección de dependencia (CDI)
1.1
346
Inyección de dependencias para Java
1.0
330
Validación de beans
1.1
349
Beans administrados
1.0
316
Enterprise JavaBeans (EJB) (EJB)
3.2
345
Interceptores
1.2
318
API de persistencia Java (JPA)
2.1
338
Anotaciones comunes para la plataforma
Java
1.2
250
API de transacción Java (JTA)
1.2
907
API de Java para servicios web RESTful (JAXRS)
2.0
339
Aplicaciones por lotes para la plataforma
Java
1.0
352
Utilidades de simultaneidad para Java EE
1.0
236
Arquitectura de conector Java EE (JCA)
1.7
322
API de servicio de mensajería Java (JMS)
2.0
343
API de JavaMail
1.5
919
API de Java para servicios web basados en
XML (JAX-WS)
2.2
224
Completo
18
JB183-EAP7.0-es-2-20180124
La especificación Java EE 7 (JSR 342)
Perfil
Especificación
Versión
N.º de JSR
Arquitectura Java para referencias XML
(JAXB)
2.2
222
API de Java para registros XML (JAXR)
1.0
93
Interfaz de proveedor de servicio de
autenticación de Java para contenedores
(JASPIC)
1.1
196
nota
El perfil completo de Java EE 7 incluye todas las API del perfil web.
Referencias
El Proceso de la comunidad Java (JCP)
https://jcp.org/en/home/index
Solicitudes de especificación Java (JSR)
https://jcp.org/en/jsr/overview
El proceso JCP
https://jcp.org/en/procedures/overview
JB183-EAP7.0-es-2-20180124
19
Capítulo 1. Transición a aplicaciones con varios niveles
Cuestionario: Descripción del Proceso de la
comunidad Java (JCP)
Elija las respuestas correctas para las siguientes preguntas:
1.
¿Cuál de los siguientes enunciados acerca de JCP es verdadero?
a.
b.
c.
d.
e.
2.
¿Cuáles dos de los siguientes enunciados acerca del proceso JCP son correctos?
(Elija dos opciones).
a.
b.
c.
d.
e.
3.
c.
d.
e.
Arquitectura de conector Java (JCA)
Interfaz de proveedor de servicio de autenticación de Java para contenedores
(JASPIC)
API de servicio de mensajería Java (JMS)
API de Java para servicios web RESTful (JAX-RS)
API de transacción Java (JTA)
¿Cuáles dos de los siguientes enunciados acerca del perfil completo de Java EE 7 son
correctos? (Elija dos opciones).
a.
b.
c.
20
Cualquier miembro, empresa u organización individual puede proponer una JSR.
El kit Technology Compatibility Kit (TCK) de Java EE 7 es solo un conjunto de prueba
para verificar la implementación de la especificación Java EE 7. No es obligatorio
que se superen todas las pruebas para que una implementación sea declarada
como compatible y certificada.
Una implementación se puede definir como certificada y compatible si aprueba el
50 % de las pruebas en el TCK.
El Comité Ejecutivo de JCP gestiona y supervisa de manera exclusiva el desarrollo y
la evolución de cada JSR.
Los Grupos de expertos (EG) gestionan el desarrollo y la evolución de las JSR bajo
el liderazgo de un Director de especificaciones (SL).
¿Cuáles dos de las siguientes API forman parte del perfil web Java EE 7? (Elija dos
opciones).
a.
b.
4.
Solo las organizaciones con ingresos de USD 1 millón o más pueden unirse al JCP.
Solo las empresas u organizaciones registradas tienen permitido ingresar al JCP.
La membresía al JCP se da por invitación únicamente. No se puede unir al JCP
como individuo u organización sin invitación.
La membresía al JCP está abierta a organizaciones, empresas e individuos.
Solo el Comité Ejecutivo (EC) del JCP puede proponer una nueva Solicitud de
especificación Java (JSR).
La API de persistencia Java (JPA) no forma parte del perfil completo de Java EE 7.
La API de Java para servicios web basados en XML (JAX-WS) no forma parte del
perfil completo de Java EE 7.
La API de servicio de mensajería Java (JMS) forma parte del perfil completo de Java
EE 7.
JB183-EAP7.0-es-2-20180124
d.
e.
El perfil completo contiene todas las API en el perfil web.
El perfil completo contiene solo algunas API del perfil web.
JB183-EAP7.0-es-2-20180124
21
Capítulo 1. Transición a aplicaciones con varios niveles
Solución
Elija las respuestas correctas para las siguientes preguntas:
1.
¿Cuál de los siguientes enunciados acerca de JCP es verdadero?
a.
b.
c.
d.
e.
2.
¿Cuáles dos de los siguientes enunciados acerca del proceso JCP son correctos?
(Elija dos opciones).
a.
b.
c.
d.
e.
3.
c.
d.
e.
Arquitectura de conector Java (JCA)
Interfaz de proveedor de servicio de autenticación de Java para contenedores
(JASPIC)
API de servicio de mensajería Java (JMS)
API de Java para servicios web RESTful (JAX-RS)
API de transacción Java (JTA)
¿Cuáles dos de los siguientes enunciados acerca del perfil completo de Java EE 7 son
correctos? (Elija dos opciones).
a.
b.
c.
d.
22
Cualquier miembro, empresa u organización individual puede proponer una JSR.
El kit Technology Compatibility Kit (TCK) de Java EE 7 es solo un conjunto de prueba
para verificar la implementación de la especificación Java EE 7. No es obligatorio
que se superen todas las pruebas para que una implementación sea declarada
como compatible y certificada.
Una implementación se puede definir como certificada y compatible si aprueba el
50 % de las pruebas en el TCK.
El Comité Ejecutivo de JCP gestiona y supervisa de manera exclusiva el desarrollo y
la evolución de cada JSR.
Los Grupos de expertos (EG) gestionan el desarrollo y la evolución de las JSR
bajo el liderazgo de un Director de especificaciones (SL).
¿Cuáles dos de las siguientes API forman parte del perfil web Java EE 7? (Elija dos
opciones).
a.
b.
4.
Solo las organizaciones con ingresos de USD 1 millón o más pueden unirse al JCP.
Solo las empresas u organizaciones registradas tienen permitido ingresar al JCP.
La membresía al JCP se da por invitación únicamente. No se puede unir al JCP
como individuo u organización sin invitación.
La membresía al JCP está abierta a organizaciones, empresas e individuos.
Solo el Comité Ejecutivo (EC) del JCP puede proponer una nueva Solicitud de
especificación Java (JSR).
La API de persistencia Java (JPA) no forma parte del perfil completo de Java EE 7.
La API de Java para servicios web basados en XML (JAX-WS) no forma parte del
perfil completo de Java EE 7.
La API de servicio de mensajería Java (JMS) forma parte del perfil completo de
Java EE 7.
El perfil completo contiene todas las API en el perfil web.
JB183-EAP7.0-es-2-20180124
Solución
e.
El perfil completo contiene solo algunas API del perfil web.
JB183-EAP7.0-es-2-20180124
23
Capítulo 1. Transición a aplicaciones con varios niveles
Descripción de la arquitectura de
aplicaciones con varios niveles
Objetivos
Tras finalizar esta sección, los estudiantes deberán ser capaces de explicar aplicaciones y
arquitecturas con varios niveles.
Arquitectura de aplicaciones con varios niveles
Las aplicaciones Java EE fueron diseñadas teniendo en mente una arquitectura con varios
niveles. La aplicación está dividida en componentes y cada uno cumple un propósito
específico. Cada componente está ordenado de manera lógica en un nivel. Algunos de los
niveles se ejecutan en máquinas y servidores físicos separados. La lógica de negocio de la
aplicación se puede ejecutar en servidores de aplicaciones alojados en un centro de datos,
mientras que los datos reales para la base de datos se pueden almacenar en un servidor
separado.
La ventaja de usar arquitecturas con niveles es que, a medida que la aplicación escala para
gestionar más y más usuarios finales, cada uno de los niveles se puede escalar de manera
independiente para gestionar la mayor carga de trabajo al agregar más servidores (un
proceso conocido como "escalamiento" [scaling out]). También existe el beneficio adicional de
que los componentes entre niveles se pueden actualizar en forma independiente sin afectar
otros componentes.
En una arquitectura clásica de aplicaciones Java EE basada en la Web, existen cuatro niveles:
• Client Tier (Nivel de cliente): suele ser un explorador para reproducir la interfaz de usuario
en las máquinas del usuario final o un applet incorporado en una página web (cada vez
más raro).
• Web Tier (Nivel web): los componentes del nivel web se ejecutan dentro de un servidor
de aplicaciones y generan HTML u otro tipo de marcado que los componentes pueden
reproducir o consumir en el nivel de cliente. Este nivel también puede asistir a clientes no
interactivos, como otros sistemas empresariales (tanto internos como externos), a través
de protocolos como Protocolo simple de acceso a objetos (SOAP) o servicios
web Transferencia de estado representacional (REST).
• Business Logic Tier (Nivel de lógica de negocio): los componentes del nivel de lógica de
negocio contienen la lógica de negocio core para la aplicación. Por lo general, estos son
una combinación de Enterprise Java Beans (EJB), Objetos comunes antiguos de Java (POJO),
Beans de entidad, Beans controlados por mensajes y Objetos de acceso de datos (DAO),
que se comunican con sistemas de almacenamiento persistentes, como RDBMS, LDAP y
otros.
• Enterprise Information Systems (EIS) Tier (Nivel de Sistemas de información empresariales
[EIS]): muchas aplicaciones empresariales almacenan y manipulan datos persistentes que
son consumidos por varios sistemas y aplicaciones dentro de una organización. Entre
los ejemplos, se incluyen sistemas de administración de bases de datos relacionales
(RDBMS), servicios de directorio del Protocolo ligero de acceso a directorios (LDAP), bases
24
JB183-EAP7.0-es-2-20180124
Tipos de arquitecturas de aplicaciones con varios niveles
de datos NoSQL, bases de datos en memoria, mainframes u otros sistemas de back-end
que almacenan y gestionan los datos de una organización de manera segura.
Tipos de arquitecturas de aplicaciones con varios
niveles
La especificación Java EE fue diseñada para admitir varios tipos diferentes de aplicaciones de
múltiples niveles. A continuación, se mencionan brevemente algunos de los más comunes:
Arquitectura centrada en la Web
Este tipo de arquitectura es para aplicaciones simples con un front-end basado en explorador
y un back-end simple desarrollados por Servlets, Páginas de servidor Java (JSP) o Caras de
servidor Java (JSF). No se utilizan características como transacciones, mensajería asíncrona y
acceso a la base de datos.
Figura 1.4: Arquitectura simple centrada en la Web
Arquitectura basada en componentes que combina funciones web y lógica de negocio
En esta arquitectura, un explorador con nivel de cliente se comunica con un nivel web
que consta de Servlets, JSP o páginas JSF, que son responsables de visualizar la interfaz de
usuario, controlar el flujo de la página y la seguridad. La lógica de negocio core se aloja en
un nivel separado de lógica de negocio, que cuenta con componentes de Java EE, como EJB,
Beans de entidad (JPA) y Beans controlados por mensajes (MDB). Los componentes del nivel
de lógica de negocio se integran con sistemas de información empresarial, como bases de
datos relacionales y aplicaciones de back-office que exponen una API para gestionar datos
persistentes y proporcionan capacidades transaccionales para la aplicación.
JB183-EAP7.0-es-2-20180124
25
Capítulo 1. Transición a aplicaciones con varios niveles
Figura 1.5: Arquitectura basada en componentes que combina funciones web y lógica de negocio
Arquitectura de negocio a negocio (Business-to-Business, B2B)
En este tipo de arquitectura, el front-end generalmente no es una interfaz gráfica de usuario
(GUI) interactiva a la que acceden usuarios finales, sino un sistema interno o externo que se
integra con la aplicación e intercambia datos mediante un protocolo estándar mutuamente
entendido, como Invocación de método remoto (RMI), HTTP, Protocolo simple de acceso a
objetos (SOAP) o Transferencia de estado representacional (REST).
26
JB183-EAP7.0-es-2-20180124
Tipos de arquitecturas de aplicaciones con varios niveles
Figura 1.6: Arquitectura de negocio a negocio
Arquitectura de aplicaciones de los servicios web
Las arquitecturas de aplicaciones modernas a menudo están diseñadas para basarse en
servicios web. En esta arquitectura, la aplicación proporciona una API a la cual se accede
a través de un protocolo basado en HTTP, como SOAP o REST, vía un conjunto de servicios
(extremos) que corresponden a la función de negocio de la aplicación. Estos servicios son
consumidos por aplicaciones no interactivas (pueden ser internas o de terceros) o un
front-end HTML/JavaScript interactivo, mediante marcos (frameworks) como AngularJS,
Backbone.js, React y muchos más.
JB183-EAP7.0-es-2-20180124
27
Capítulo 1. Transición a aplicaciones con varios niveles
Figura 1.7: Arquitectura simple de aplicaciones de los servicios web
28
JB183-EAP7.0-es-2-20180124
Cuestionario: Arquitectura de aplicaciones con varios niveles
Cuestionario: Arquitectura de aplicaciones
con varios niveles
Elija las respuestas correctas para las siguientes preguntas:
1.
Se le ha solicitado que diseñe un componente que calcule tasas de descuento para
diferentes productos en una aplicación de compras en línea. ¿A qué nivel lógico
pertenece este componente?
a.
b.
c.
d.
e.
2.
¿Cuáles dos de las siguientes aplicaciones combinan con una arquitectura simple
centrada en la Web? (Elija dos opciones).
a.
b.
c.
d.
e.
3.
Nivel de cliente
Nivel web
Nivel de lógica de negocio
Nivel de datos o EIS
Ninguno de los anteriores
Una aplicación de servlet basada en explorador, que imprime la hora actual en tres
zonas horarias diferentes de los EE. UU.: Pacífico (PST), Central (CST) y Este (EST).
Una aplicación que rastrea la ubicación de una flota de automóviles mediante GPS.
Una aplicación que lee datos de un mainframe grande y, luego, los almacena
en una base de datos relacional. La aplicación también permite que un sistema
externo de terceros acceda a los datos de la base de datos mediante servicios web
SOAP.
Una aplicación de comprobación de estado que se implementa en un servidor de
aplicaciones, que muestra un estado "OK" (al acceder desde un explorador) si el
servidor funciona normalmente.
Una aplicación que proporciona información meteorológica de ciudades alrededor
del mundo. La aplicación acepta un nombre de ciudad como entrada (a través de
un extremo REST) y, luego, proporciona información meteorológica actual y un
pronóstico de 5 días en formato XML.
¿Cuáles dos de los siguientes enunciados acerca de una arquitectura negocio a
negocio (Business-to-Business, B2B) son correctos? (Elija dos opciones).
a.
b.
c.
d.
e.
Las aplicaciones B2B deben estar siempre basadas en la Web y deben contar con
un front-end interactivo.
Las aplicaciones B2B solo deben admitir un protocolo único por motivos de
seguridad.
Las aplicaciones B2B se comunican siempre mediante RMI.
Las aplicaciones B2B se pueden comunicar a través de RMI, SOAP, REST o un
protocolo mutuamente aceptado.
Las aplicaciones B2B pueden admitir consumidores y usuarios tanto interactivos
como no interactivos.
JB183-EAP7.0-es-2-20180124
29
Capítulo 1. Transición a aplicaciones con varios niveles
4.
¿Cuáles dos de los siguientes enunciados acerca de la arquitectura basada en
componentes que combina funciones web y lógica de negocio son correctos? (Elija
dos opciones).
a.
b.
c.
d.
30
Las transacciones se gestionan en un nivel de lógica de negocio (en EJB).
Las transacciones deben gestionarse siempre en la capa web.
No se puede utilizar la mensajería asíncrona que utiliza Beans controlados por
mensajes (MDB).
Se puede utilizar la mensajería asíncrona que utiliza Beans controlados por
mensajes (MDB).
JB183-EAP7.0-es-2-20180124
Solución
Solución
Elija las respuestas correctas para las siguientes preguntas:
1.
Se le ha solicitado que diseñe un componente que calcule tasas de descuento para
diferentes productos en una aplicación de compras en línea. ¿A qué nivel lógico
pertenece este componente?
a.
b.
c.
d.
e.
2.
¿Cuáles dos de las siguientes aplicaciones combinan con una arquitectura simple
centrada en la Web? (Elija dos opciones).
a.
b.
c.
d.
e.
3.
Una aplicación de servlet basada en explorador, que imprime la hora actual en
tres zonas horarias diferentes de los EE. UU.: Pacífico (PST), Central (CST) y Este
(EST).
Una aplicación que rastrea la ubicación de una flota de automóviles mediante GPS.
Una aplicación que lee datos de un mainframe grande y, luego, los almacena
en una base de datos relacional. La aplicación también permite que un sistema
externo de terceros acceda a los datos de la base de datos mediante servicios web
SOAP.
Una aplicación de comprobación de estado que se implementa en un servidor
de aplicaciones, que muestra un estado "OK" (al acceder desde un explorador)
si el servidor funciona normalmente.
Una aplicación que proporciona información meteorológica de ciudades alrededor
del mundo. La aplicación acepta un nombre de ciudad como entrada (a través de
un extremo REST) y, luego, proporciona información meteorológica actual y un
pronóstico de 5 días en formato XML.
¿Cuáles dos de los siguientes enunciados acerca de una arquitectura negocio a
negocio (Business-to-Business, B2B) son correctos? (Elija dos opciones).
a.
b.
c.
d.
e.
4.
Nivel de cliente
Nivel web
Nivel de lógica de negocio
Nivel de datos o EIS
Ninguno de los anteriores
Las aplicaciones B2B deben estar siempre basadas en la Web y deben contar con
un front-end interactivo.
Las aplicaciones B2B solo deben admitir un protocolo único por motivos de
seguridad.
Las aplicaciones B2B se comunican siempre mediante RMI.
Las aplicaciones B2B se pueden comunicar a través de RMI, SOAP, REST o un
protocolo mutuamente aceptado.
Las aplicaciones B2B pueden admitir consumidores y usuarios tanto
interactivos como no interactivos.
¿Cuáles dos de los siguientes enunciados acerca de la arquitectura basada en
componentes que combina funciones web y lógica de negocio son correctos? (Elija
dos opciones).
a.
Las transacciones se gestionan en un nivel de lógica de negocio (en EJB).
JB183-EAP7.0-es-2-20180124
31
Capítulo 1. Transición a aplicaciones con varios niveles
b.
c.
d.
32
Las transacciones deben gestionarse siempre en la capa web.
No se puede utilizar la mensajería asíncrona que utiliza Beans controlados por
mensajes (MDB).
Se puede utilizar la mensajería asíncrona que utiliza Beans controlados por
mensajes (MDB).
JB183-EAP7.0-es-2-20180124
Instalación de las herramientas de desarrollo de Java
Instalación de las herramientas de desarrollo
de Java
Objetivos
Tras finalizar esta sección, los estudiantes deberán ser capaces de realizar lo siguiente:
• Describir las funciones y el proceso de instalación del editor JBoss Developer Studio.
• Describir cómo usar Maven para gestionar dependencias de aplicaciones.
Red Hat JBoss Developer Studio (JBDS)
JBoss Developer Studio (JBDS) es un Entorno de desarrollo integrado (IDE) provisto por Red Hat
para simplificar el desarrollo de aplicaciones de Java EE. Es un conjunto de complementos
(plug-ins) integrados y comprobados que viene con la plataforma Eclipse™. JBDS cuenta con
las siguientes funciones incorporadas:
• Complementos (plug-ins) para simplificar el desarrollo de aplicaciones mediante el
middleware Red Hat JBoss.
• Complementos (plug-ins) y asistentes de verificación de la unidad para realizar el
Desarrollo Controlado por Pruebas (TDD).
• Un depurador visual para depurar aplicaciones locales y remotas de Java.
• Sintaxis resaltada y completador de códigos para las API Java EE más comunes, como JPA,
JSF, JSP, EL y muchas más.
• Integración Maven para simplificar compilaciones de proyectos, empaquetado, pruebas e
implementación.
• Los adaptadores de unidades y complementos (plug-ins) para trabajar con JBoss EAP.
Puede controlar el ciclo de vida (iniciar, detener, reiniciar, implementar, anular la
implementación) de EAP sin dejar la IDE.
Instalación de JBoss Developer Studio
JBDS tiene un instalador de archivos JAR independiente de la plataforma y se ejecuta en Mac
OS X, Windows y Linux (GTK), usando widgets de IU nativos. Es necesario un SDK de Java. Para
JBDS 10 y versiones posteriores, se requiere Java 1.8 SDK o una versión posterior. Comience
el proceso de instalación abriendo un terminal y ejecutando un comando:
[student@workstation todojse]$ java -jar devstudio-10.0.0.GA-standalone-installer.jar
El instalador proporciona una serie de asistentes que le solicitan la ubicación en la que desea
instalar el IDE, el JVM que desea usar, si está instalado algún servidor JBoss EAP y si desea
agregarlo a JBDS. Por último, le solicita aceptar el acuerdo de usuario final antes de instalar el
IDE en la ubicación que brindó.
JB183-EAP7.0-es-2-20180124
33
Capítulo 1. Transición a aplicaciones con varios niveles
Si elije la opción de agregar de manera automática atajos al menú de inicio, debe ver estos
atajos en el menú de aplicaciones de su sistema operativo. Haga doble clic en el icono JBDS
para iniciar el JBDS IDE.
nota
El instalador de JBDS registra las opciones elegidas durante la instalación gráfica y
almacena dicha información en un archivo nombrado InstallConfigRecord.xml
en el directorio raíz de la instalación. De esta manera, es posible repetir la
instalación en varios sistemas usando el siguiente comando:
[student@workstation todojse]$ java -jar devstudio-10.0.0.GA-standaloneinstaller.jar InstallConfigRecord.xml
Apache Maven
La práctica recomendada actual para desarrollar, probar, compilar, empaquetar e
implementar aplicaciones Java SE y Java EE es usar Apache Maven. Maven es una herramienta
de administración de proyectos que usa un enfoque declarativo (en un archivo XML
denominado pom.xml en la raíz de la carpeta del proyecto) para especificar cómo compilar,
empaquetar, ejecutar (para aplicaciones Java SE) e implementar aplicaciones junto con
información de dependencias.
Maven cuenta con un núcleo (core) pequeño y un gran número de complementos (plug-ins)
que amplían la funcionalidad core para proporcionar funciones como:
• Ciclos de vida de compilación preestablecidos para productos finales, denominados
artefactos, como WAR, EAR y JAR.
• Prácticas recomendadas integradas, como ubicaciones de archivos fuente y evaluaciones
de pruebas de unidades para cada compilación.
• Administración de dependencias con descarga automática de dependencias faltantes.
• Conjunto exhaustivo de complementos (plug-ins), que incluye complementos específicos
para el desarrollo y la implementación de JBoss.
• Generación de informes de proyectos, que incluye documentos de Java, cobertura de
pruebas y mucho más.
En esta sección, se analizan las características y construcciones operacionales de Maven que
se utilizan en el presente curso. Para comenzar, se utiliza el archivo de proyecto Maven, un
documento XML que describe el artefacto, sus dependencias, las propiedades del proyecto y
los complementos (plug-ins) que se invocarán en cualquiera de los pasos disponibles del ciclo
de vida. Este archivo se nombra siempre pom.xml. El siguiente es un ejemplo abreviado de
un archivo pom.xml de un proyecto:
<?xml version="1.0"?>
<project xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/
xsd/maven-4.0.0.xsd"
xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
34
JB183-EAP7.0-es-2-20180124
Apache Maven
<modelVersion>4.0.0</modelVersion>
<groupId>com.redhat.training</groupId>
<artifactId>example</artifactId>
<version>0.0.1</version>
<packaging>war</packaging>
<name></name>
<dependencies>
<dependency>
<groupId>org.richfaces.ui</groupId>
<artifactId>richfaces-components-ui</artifactId>
<version>4.0.0.Final</version>
</dependency>
...
</dependencies>
<build>
<plugins>
<plugin>
<artifactId>maven-compiler-plugin</artifactId>
<version>2.3.2</version>
<configuration>
<source>1.8</source>
<target>1.8</target>
</configuration>
</plugin>
</plugins>
...
</build>
</project>
group-id es como un paquete de Java.
artifact-id es un nombre de proyecto.
version es la versión del proyecto.
packaging define la forma en que se empaquetará el proyecto. En este caso, es un tipo
war.
dependency describe los recursos de los que depende un proyecto. Se requieren estos
recursos para compilar y ejecutar el proyecto correctamente. Maven descarga y vincula
las dependencias de los repositorios especificados.
plugins son los complementos del proyecto.
Una de las ventajas de usar Maven consiste en el manejo automático de la compilación
de códigos fuente y la inclusión de recursos en el artefacto. Maven crea una estructura de
proyecto estándar. Las siguientes convenciones para la denominación de directorios son
obligatorias:
Estructuras del directorio de Maven
Recurso
Directorio
Resultado
Código fuente de Java
src/main/java
El directorio contiene clases
de Java incluidas en WEBINF/classes para un WAR
o raíz de un JAR.
Archivos de configuración
src/main/resources
El directorio contiene
archivos de configuración
incluidos en WEB-INF/
JB183-EAP7.0-es-2-20180124
35
Capítulo 1. Transición a aplicaciones con varios niveles
Recurso
Directorio
Resultado
classes para un WAR o raíz
de un JAR.
Código de prueba de Java
src/test/java
El directorio contiene el
código fuente de prueba.
Archivos de configuración de
prueba
src/test/resources
El directorio contiene los
recursos de prueba.
Cuando les asigna un nombre a las dependencias en el archivo pom.xml, puede otorgarles
un alcance. Estos alcances controlan el lugar en el que se utiliza la dependencia dentro del
ciclo de vida de compilación y si se incluyen en el artefacto. A continuación, se exponen los
alcances más frecuentes:
Alcances de las dependencias de Maven
Alcance
Resultado
compile
Compile (compilación) es el alcance
predeterminado si no se especifica ningún
otro alcance y se requiere resolver las
declaraciones import.
test
Test (prueba) se requiere para compilar y
ejecutar las pruebas de las unidades. No se
incluye en el artefacto.
runtime
La dependencia runtime (tiempo
de ejecución) no se requiere para la
compilación. Se usa para cualquier ejecución
y se incluye en el artefacto.
provided
El alcance provided (provisto) es como
compile y el contenedor proporciona la
dependencia en el tiempo de ejecución. Se
usa durante la compilación y la prueba.
Maven se integra en JBDS; pero le recomendamos invocarlo desde la línea de comando. A
continuación, se exponen algunos comando habituales:
• mvn package evalúa y compila el artefacto.
• mvn package -Dmaven.test.skip=true compila el artefacto y omite todas las pruebas.
• mvn jboss-as:deploy: Para implementar el artefacto en la instancia que se ejecuta en
$JBOSS_HOME (supone que el complemento [plug-in] se configuró en pom.xml).
• mvn install es similar al paquete, pero instala el artefacto en el repositorio local de
Maven para que se utilice como dependencia en otros proyectos.
36
JB183-EAP7.0-es-2-20180124
Apache Maven
nota
Las IDE como JBoss Developer Studio son conscientes de los proyectos Maven y
usted puede ejecutar las tareas Maven directamente de dentro de IDE sin requerir
el uso de la línea de comando.
A lo largo de este curso, estará desarrollando en forma incremental una web basada en la
aplicación To Do List que se implementa en un servidor de aplicaciones JBoss EAP 7 y usa
varias API de la especificación Java EE 7. La aplicación almacena los datos en una base de
datos MySQL. Debe hacer un amplio uso de Maven y JBDS en este curso para gestionar el
empaquetado y la implementación de la aplicación.
Para compilar, empaquetar y ejecutar una aplicación independiente que usa solo la API
Java SE, como la aplicación To Do List basada en la línea de comando, mediante Maven, debe
ejecutar los siguientes comandos:
[student@workstation todojse]$ mvn clean package
[student@workstation todojse]$ java -jar target/todojse-1.0.jar
El comando mvn clean package compila la aplicación como un archivo JAR ejecutable y el
comando java -jar la ejecuta.
Por otro lado, la aplicación To Do List basada en la Web se compila y se implementa en EAP
usando el siguiente comando:
[student@workstation todojse]$ mvn clean package wildfly:deploy
El comando anterior elimina el antiguo archivo WAR, compila el código y compila un archivo
WAR implementado en una instancia en ejecución de EAP. Si ya se implementó una versión
anterior de la aplicación, se anula la implementación de la versión antigua y se implementa
la versión nueva sin reiniciar el servidor de aplicaciones. Dicho proceso se denomina hot
deployment (implementación en caliente) y se utiliza ampliamente durante el desarrollo y las
pruebas, así como en implementaciones de producción.
Referencias
Eclipse
https://eclipse.org
Apache Maven
https://maven.apache.org
JB183-EAP7.0-es-2-20180124
37
Capítulo 1. Transición a aplicaciones con varios niveles
Ejercicio guiado: Ejecución de la aplicación To
Do List
Resultados
Debe poder importar el proyecto de la línea de comando de la aplicación To Do List en
JBoss Developer Studio y ejecutarlo usando Maven.
Antes de comenzar
Abra una ventana de terminal en la máquina virtual workstation y ejecute el siguiente
comando para descargar los archivos del trabajo de laboratorio necesarios para este taller.
[student@workstation ~]$ lab todojse setup
1.
Importe el proyecto todojse en el JBoss Developer Studio IDE (JBDS).
1.1. Haga doble clic en el icono de JBDS del escritorio de la máquina virtual
workstation.
1.2. Seleccione una carpeta de espacio de trabajo.
En la ventana Eclipse Launcher, ingrese /home/student/JB183/workspace en el
campo Workspace (Espacio de trabajo), marque la casilla de verificación Use this
as the default and do not ask again (Usar como valor predeterminado y no
volver a preguntar) y, luego, haga clic en Launch (Iniciar).
Figura 1.8: Seleccione el espacio de trabajo de JBDS
nota
Seleccione No para cerrar el cuadro de diálogo Uso de Red Hat
JBoss Developer Studio.
38
JB183-EAP7.0-es-2-20180124
1.3. En el menú de JBDS, haga clic en File (Archivo) > Import (Importar)para abrir el
asistente para Import (Importar).
En la página Select (Seleccionar), seleccione Maven > Existing Maven Projects
(Proyectos de Maven existentes) y, luego, haga clic en Next (Siguiente).
1.4. En la página Maven projects (Proyectos de Maven), haga clic en Browse (Explorar)
para abrir la ventana Select root folder (Seleccionar carpeta raíz).
Diríjase al directorio /home/student/JB183/labs/. Seleccione la carpeta todojse
y haga clic en OK (Aceptar).
En la página Maven projects (Proyectos de Maven), haga clic en Finish (Finalizar).
1.5. Observe la barra de estado de JBDS (esquina inferior derecha) para monitorear el
progreso de la operación de importación. Es probable que la descarga de todas las
dependencias requeridas demore unos minutos.
2.
Explore el archivo pom.xml de Maven.
2.1. Expanda el ítem todojse en el panel Project Explorer (Explorador de proyectos)
izquierdo y haga doble clic en el archivo pom.xml.
La pestaña Overview (Descripción general) se puede ver en la principal ventana del
editor, mostrando una vista avanzada del proyecto. En esta pestaña se muestran
Group Id, Artifact Id y Version (generalmente abreviados como las
coordenadas GAV de un proyecto o módulo).
2.2. Haga clic en la pestaña Dependencies (Dependencias) para ver las dependencias
(librerías, marcos [frameworks] y módulos de los que depende este proyecto) del
proyecto. En este caso, no tenemos dependencias en ninguna librería externa y solo
utilizamos la Librería estándar de Java.
2.3. Haga clic en la pestaña pom.xml para ver el texto completo del archivo pom.xml.
Repase brevemente los detalles GAV para este proyecto:
<groupId>com.redhat.training</groupId>
<artifactId>todojse</artifactId>
<version>1.0</version>
El formato del empaquetado para este proyecto como jar. Maven garantiza que,
cuando se compile el proyecto, generará un archivo JAR con entradas MANIFEST
adecuadas, que contienen metadatos acerca del archivo jar.
<packaging>jar</packaging>
El proyecto es compatible con JDK 1.8.
<!-- maven-compiler-plugin -->
<maven.compiler.target>1.8</maven.compiler.target>
<maven.compiler.source>1.8</maven.compiler.source>
JB183-EAP7.0-es-2-20180124
39
Capítulo 1. Transición a aplicaciones con varios niveles
Maven es ampliable mediante una gran cantidad de plug-ins (complementos).
Puede controlar diferentes aspectos de la forma en que un proyecto se compila,
se empaqueta, se prueba y se implementa declarando complementos (plug-ins)
adecuados.
En este proyecto, está usando exec-maven-plugin para ejecutar la clase principal
en este proyecto de la línea de comando o desde dentro del JBoss Developer Studio.
El método principal, que sirve como punto de entrada para la aplicación cuando se
ejecuta, se declara como la clase com.redhat.training.TestTodoMap.
<artifactId>exec-maven-plugin</artifactId>
<version>1.5.0</version>
<executions>
<execution>
<goals>
<goal>java</goal>
</goals>
</execution>
</executions>
<configuration>
<mainClass>com.redhat.training.TestTodoMap</mainClass>
</configuration>
También utiliza maven-assembly-plugin para compilar un archivo JAR ejecutable
independiente de la plataforma, que se puede ejecutar usando un comando java
-jar. Aunque este proyecto no usa ninguna dependencia externa, los proyectos
con un gran número de archivos JAR dependientes se pueden empaquetar como
un único fat jar grande que se puede ejecutar directamente sin agregar en forma
explícita todos los archivos JAR dependientes a la CLASSPATH.
<artifactId>maven-assembly-plugin</artifactId>
<version>2.6</version>
<executions>
<execution>
<id>package-jar-with-dependencies</id>
<phase>package</phase>
<goals>
<goal>single</goal>
</goals>
3.
Explore el código fuente de la aplicación.
3.1. Navegue a src/main/java/com/redhat/training en el Project Explorer
(Explorador de proyectos) y haga doble clic en la clase TestTodoMap.java para ver el
código fuente en la principal ventana del editor.
3.2. La aplicación todojse es una aplicación de línea de comando sin interfaces gráficas
de usuario. El método main invoca la función executeMenu(), que muestra un
menú al usuario con una serie de opciones para gestionar la To Do List.
La clase TodoMap.java contiene la lógica de negocio principal de esta aplicación.
Esta clase almacena y gestiona un Map (Mapa) de objetos TodoItem. La clase
TodoItem es una simple clase Java Bean que encapsula los atributos de una To
Do List; a saber, un campo item (ítem), que contiene la descripción de tareas y un
campo status (estado) que indica si la tarea está pendiente o completa.
40
JB183-EAP7.0-es-2-20180124
El archivo Status.java declara una enumeración con las dos opciones para el
estado de un ítem, ya sea PENDING (PENDIENTE) o COMPLETED (COMPLETADO).
3.3. Revise brevemente el código fuente de los métodos addTodo(), printTodo(),
completeTodo(), deleteTodo() y findItemTodo() en la clase TodoMap para
comprender de qué manera las tareas se crean, se enumeran y se marcan como
completadas, eliminadas o encontradas respectivamente.
Estos métodos se invocan desde la sentencia switch, o case, en la clase principal
ejecutable, según la opción de menú seleccionada por el usuario. Si el usuario
selecciona Q, la aplicación existe.
nota
La aplicación todojse no es persistente con ningún dato del programa.
Los ítems de tareas de To Do List se almacenan en un objeto Map en la
memoria y los datos se pierden cuando el programa se cierra.
4.
Compile y ejecute el todojse de la línea de comando mediante Maven.
4.1. Antes de ejecutar la aplicación desde dentro de la IDE JBDS, compile y ejecute la
aplicación de la línea de comando mediante Maven.
Abra una nueva ventana de terminal y diríjase a la carpeta /home/student/JB183/
labs/todojse:
[student@workstation ~]$ cd /home/student/JB183/labs/todojse
Ahora puede compilar y empaquetar la aplicación como archivo JAR mediante la
meta package de Maven.
4.2. Compile la aplicación ejecutando el siguiente comando:
[student@workstation todojse]$ mvn clean package
[INFO] Scanning for projects...
[INFO]
[INFO] -----------------------------------------------------------------------[INFO] Building todojse 1.0
[INFO] -----------------------------------------------------------------------[INFO]
...
[INFO] --- maven-jar-plugin:2.4:jar (default-jar) @ todojse --[INFO] Building jar: /home/student/JB183/labs/todojse/target/todojse-1.0.jar
...
[INFO]
[INFO]
[INFO]
[INFO]
[INFO]
BUILD SUCCESS
-----------------------------------------------------------------------Total time: 09:48 mins
Finished at: 2017-09-20T08:13:03+05:30
Final Memory: 22M/303M
JB183-EAP7.0-es-2-20180124
41
Capítulo 1. Transición a aplicaciones con varios niveles
Verifique que pueda ver el mensaje BUILD SUCCESS de Maven y que
todojse-1.0.jar se haya compilado y copiado correctamente en la carpeta /
home/student/JB183/labs/todojse/target.
4.3. Ejecute la aplicación mediante el complemento (plug-in) exec de Maven:
[student@workstation todojse]$ mvn exec:java
[INFO] Scanning for projects...
[INFO]
[INFO] -----------------------------------------------------------------------[INFO] Building todojse 1.0
[INFO] -----------------------------------------------------------------------[INFO]
[INFO] --- exec-maven-plugin:1.5.0:java (default-cli) @ todojse ---
[N]ew | [C]omplete | [R]ead | [D]elete | [L]ist | [Q]uit:
4.4. Explore la funcionalidad de la aplicación creando, completando, leyendo,
enumerando y eliminando algunos ítems de tareas. Ingrese Q para quitar la
aplicación.
5.
Ejecute el todojse como archivo JAR ejecutable.
5.1. El comando mvn clean package que ejecutó anteriormente utiliza el complemento
(plug-in) de ensamblaje Maven para compilar un archivo JAR ejecutable de manera
independiente.
Ejecute la aplicación mediante el siguiente comando:
[student@workstation todojse]$ java -jar target/todojse-1.0.jar
5.2. Verifique que la aplicación se haya iniciado y que aparezca el menú principal.
6.
Compile y ejecute todojse desde dentro de JBDS.
6.1. Puede compilar, empaquetar y ejecutar la aplicación desde dentro de JBDS utilizando
un complemento (plug-in) Maven incorporado en el IDE.
El complemento (plug-in) JBDS Maven viene con un conjunto de Configuraciones
de ejecución empaquetadas previamente para limpiar y compilar proyectos. No
obstante, debe crear una Configuración de ejecución personalizada y usarla para
compilar, empaquetar y ejecutar el proyecto.
6.2. Haga clic con el botón derecho en el proyecto todojse en el Project Explorer
(Explorador de proyectos) y haga clic en Run As (Ejecutar como) > Run
Configurations (Configuraciones de ejecución) para abrir la ventana Run
Configurations (Configuraciones de ejecución).
Desplácese hacia abajo en la lista de opciones del panel izquierdo y seleccione la
opción Maven Build:
42
JB183-EAP7.0-es-2-20180124
Figura 1.9: Configuración de ejecución de Maven
6.3. En el menú superior izquierdo de la ventana Run Configurations (Configuraciones de
ejecución), haga clic en New Launch Configuration (Nueva configuración de inicio)
para crear una nueva configuración de lanzamiento:
Figura 1.10: Nueva configuración de ejecución
6.4. En la ventana de nueva configuración de ejecución, agregue los siguientes detalles:
• Name (Nombre): maven package and exec
• Base Directory (Directorio base): Haga clic en Workspace y, luego, seleccione el
proyecto todojse y haga clic en OK (Aceptar).
• Goals (Metas): clean package exec:java
JB183-EAP7.0-es-2-20180124
43
Capítulo 1. Transición a aplicaciones con varios niveles
Figura 1.11: Detalles de la configuración de ejecución para Maven
Deje todos los demás campos con sus valores predeterminados y haga clic en Apply
(Aplicar).
6.5. Haga clic en Run (Ejecutar) en la parte inferior de la ventana Run Configurations
(Configuraciones de ejecución).
El complemento (plug-in) JBDS ahora deberá iniciar, compilar, empaquetar y ejecutar
la aplicación. Observe la pestaña Console (Consola) en la parte inferior para ver el
procesos de compilación y verificar que la aplicación se ejecute y aparezca el menú
principal.
6.6. Salga de la aplicación ingresando Q en el menú principal de la aplicación To Do List.
6.7. Haga clic con el botón derecho en el proyecto todojse en Project Explorer
(Explorador de proyectos) y seleccione Close Project (Cerrar proyecto).
Esto concluye el ejercicio guiado.
44
JB183-EAP7.0-es-2-20180124
Resumen
Resumen
En este capítulo, aprendió lo siguiente:
• Las aplicaciones empresariales se caracterizan por su capacidad de manejar cargas de
trabajo de transacciones, integración de varios componentes, seguridad, arquitecturas
distribuidas y escalabilidad.
• Java Enterprise Edition (Java EE) es una especificación para desarrollar aplicaciones
empresariales utilizando Java. Es un estándar independiente de la plataforma, desarrollado
bajo los auspicios del Proceso de la comunidad Java (JCP). Un sistema de software que
implementa la especificación Java EE se denomina servidor de aplicaciones.
• La API Java SE proporciona un conjunto completo de componentes modulares y
reutilizables para implementar aplicaciones Java. Java EE se basa en Java SE y proporciona
un conjunto de API que se centran en desarrollar aplicaciones empresariales.
• Las aplicaciones Java EE están desarrolladas para contar con varios niveles y pueden
admitir varias arquitecturas, según el caso de uso.
• Red Hat JBoss Developer Studio es un IDE basado en Eclipse™, proporcionado por Red Hat,
que cuenta con un conjunto de complementos (plug-ins) y herramientas integrados para
simplificar el desarrollo de aplicaciones empresariales Java EE. Admite varios servidores de
aplicaciones y puede gestionar el ciclo de vida del servidor de aplicaciones desde dentro de
la propia IDE.
• Apache Maven es la herramienta elegida para compilar, empaquetar e implementar las
aplicaciones Java SE y Java EE. JBDS proporciona soporte incorporado para Maven. Los
proyectos se pueden compilar, probar, empaquetar e implementar en servidores de
aplicaciones mediante complementos (plug-ins) Maven.
JB183-EAP7.0-es-2-20180124
45
46
TRAINING
CAPÍTULO 2
EMPAQUETADO E
IMPLEMENTACIÓN DE UNA
APLICACIÓN DE JAVA EE
Descripción general
Meta
Describir la arquitectura de un servidor de aplicaciones
Java EE, empaquetar una aplicación e implementar la
aplicación en un servidor EAP.
Objetivos
• Identificar las características clave de los servidores de
aplicaciones y describir el servidor de Java EE.
• Enumerar los tipos de recursos JNDI más comunes y sus
convenciones de nomenclatura típicas.
• Empaquetar una aplicación de Java EE simple e
implementarla en JBoss EAP mediante Maven.
Secciones
• Descripción de un servidor de aplicaciones (y
cuestionario)
• Identificación de recursos JNDI (y ejercicio guiado)
• Empaquetado e implementación de una aplicación de
Java EE (y ejercicio guiado)
Trabajo de laboratorio
JB183-EAP7.0-es-2-20180124
• Empaquetado e implementación de una aplicación de
Java EE
47
Capítulo 2. Empaquetado e implementación de una aplicación de Java EE
Descripción de un servidor de aplicaciones
Objetivos
Tras finalizar esta sección, los estudiantes deberán ser capaces de realizar lo siguiente:
• Identificar las características clave de los servidores de aplicaciones y describir la
arquitectura del servidor de Java EE.
• Identificar diferentes tipos de contenedores y perfiles de servidor.
Servidores de aplicaciones
Un servidor de aplicaciones es un componente de software que proporciona el entorno
y la infraestructura de tiempo de ejecución que se necesitan para alojar y administrar
aplicaciones empresariales Java EE. El servidor de aplicaciones cuenta con funciones
como simultaneidad, arquitectura de componentes distribuida, portabilidad para varias
plataformas, gestión de transacciones, servicios web, asignación relacional de objetos para
bases de datos (ORM), mensajería asíncrona y seguridad para aplicaciones empresariales.
En una aplicación Java SE, el desarrollador debe implementar estas funciones en forma
manual, lo que requiere mucho tiempo y es difícil de implementar correctamente.
Figura 2.1: Aplicaciones Java SE en relación a Java EE
Red Hat JBoss Enterprise Application Platform (EAP)
Red Hat JBoss Enterprise Application Platform 7, JBoss EAP 7 o simplemente, EAP, es un
servidor de aplicaciones para alojar y gestionar aplicaciones Java EE.
EAP 7 se basa en estándares abiertos, sobre la base del software de código abierto Wildfly, y
proporciona las siguientes funciones:
• Una infraestructura confiable, ligera, compatible y conforme con los estándares para
implementar aplicaciones.
• Una estructura modular que permite a los usuarios activar servicios únicamente cuando
se los requiere. Esto mejora el rendimiento y la seguridad, y reduce los tiempos de inicio y
reinicio.
48
JB183-EAP7.0-es-2-20180124
Red Hat JBoss Enterprise Application Platform (EAP)
• Una consola de gestión basada en la Web y la interfaz de la línea de comando (CLI) de
gestión para configurar el servidor y proporcionar la capacidad de realizar scripts y
automatizar tareas.
• Está certificada para ambos perfiles, el Java EE 7 completo y el web.
• Una gestión centralizada de varias instancias de servidores y hosts físicos.
• También se proporcionan opciones preconfiguradas para funciones, como clústeres de alta
disponibilidad, mensajería y almacenamiento en caché distribuido.
EAP 7 facilita el desarrollo de aplicaciones empresariales, ya que brinda las API Java EE
para acceder a las bases de datos, la autenticación y la mensajería. Una funcionalidad de
aplicaciones común también está soportada por las API y los marcos (frameworks) Java EE,
provistos por EAP, para desarrollar interfaces de usuario web, exponer servicios web,
implementar criptografía y demás funciones. JBoss EAP también facilita la administración
brindando métricas de tiempo de ejecución, servicios de clústeres y automatización.
EAP cuenta con una arquitectura modular con una infraestructura core simple que controla
el ciclo de vida útil del servidor de aplicaciones básico y proporciona capacidades de
administración. La infraestructura core es responsable por cargar y descargar módulos.
Los módulos implementan la mayor parte de las API Java EE 7. Cada módulo de API de
componente Java EE se implementa como un subsistema, que se puede configurar, agregar
o eliminar, según se requiera, a través del archivo de configuración de EAP o la interfaz de
administración. Por ejemplo, para configurar el acceso a la base de datos en EAP, configure
los detalles de conexión de la base de datos en el subsistema datasources.
Figura 2.2: Arquitectura de EAP 7
Un concepto importante de la arquitectura EAP es el concepto de un módulo. Un módulo
proporciona códigos (clases de Java) para ser utilizados por los servicios EAP o por
aplicaciones.
Los módulos se cargan en un Cargador de clase aislado, y las clases de otros módulos
solo se pueden ver cuando se lo solicita implícitamente. Esto significa que un módulo se
JB183-EAP7.0-es-2-20180124
49
Capítulo 2. Empaquetado e implementación de una aplicación de Java EE
puede implementar sin ninguna preocupación con respecto a los conflictos posibles con la
implementación de otros módulos. Todos los códigos que se ejecutan en EAP, incluido el
código provisto por el núcleo (core), se ejecutan dentro de módulos. Esto incluye el código de
la aplicación, lo que implica que las aplicaciones se aíslan de sí mismas y de los servicios EAP.
Esta arquitectura modular permite un control detallado de la visibilidad de códigos. Una
aplicación puede ver un módulo que expone una versión particular de una API, mientras
que otra aplicación puede ver un segundo módulo que presenta una versión diferente de la
misma API.
Un desarrollador de aplicaciones puede controlar esta visibilidad manualmente y puede ser
muy útil en algunos escenarios. Sin embargo, para los casos más comunes, EAP 7 decide
automáticamente qué módulos presentar a una aplicación, según su uso de las API Java EE.
Contenedores
Un contenedor en un componente lógico dentro de un servidor de aplicaciones, que
proporciona un contexto de tiempo de ejecución para aplicaciones implementadas en el
servidor de aplicaciones. Un contenedor actúa como interfaz entre los componentes de
la aplicación y los servicios de infraestructura de bajo nivel provistos por el servidor de
aplicaciones.
Existen diferentes contenedores para diferentes tipos de componentes en una aplicación.
Los componentes de aplicaciones se implementan en contenedores y se proporcionan para
otras implementaciones. La implementación se basa en los descriptores de implementación
(archivos de configuración XML que se empaquetan junto con el código) o anotaciones a
nivel del código que indican la manera en que los componentes se deben implementar y
configurar.
Hay dos tipos principales de contenedores dentro de un servidor de aplicaciones Java EE:
• Contenedores web: Implementa y configura componentes web, como Servlets, JSP, JSF y
otros activos relacionados con la Web.
• Contenedores EJB: Implementa y configura componentes relacionados con EJB, JPA y JMS.
Estos tipos de implementaciones se describen en detalle en capítulos posteriores.
Los contenedores son responsables de la seguridad, las transacciones, las búsquedas JNDI, la
conectividad remota y más. Los contenedores también pueden gestionar servicios de tiempo
de ejecución, como ciclos de vida útil de componentes EJB y web, agrupaciones de fuente
de datos, persistencia de datos y mensajería JMS. Por ejemplo, la especificación Java EE le
permite configurar de forma declarativa la seguridad, para que únicamente los usuarios
autorizados puedan invocar la funcionalidad provista por un componente. Esta restricción
se configura usando descriptores de implementación XML o anotaciones en código. El
contenedor lee estos metadatos durante el tiempo de implementación y los componentes se
configuran en consecuencia.
Perfiles Java EE 7
Un perfil en el contexto de un servidor de aplicaciones Java EE es un conjunto de API de
componentes dirigido a un tipo específico de aplicaciones. Los perfiles son un nuevo
concepto que se introduce en Java EE 6. Actualmente, existen dos perfiles definidos en
Java EE 7 y el servidor de aplicaciones JBoss EAP admite ambos perfiles por completo:
50
JB183-EAP7.0-es-2-20180124
Perfiles Java EE 7
• Perfil completo: contiene todas las tecnologías Java EE, incluidas todas las API en el perfil
web, así como otras.
• Perfil web: contiene una pila (stack) completa de API Java EE para desarrollar aplicaciones
web dinámicas.
Existen más de 30 tecnologías diferentes que comprenden el perfil completo de Java EE.
Cada una de estas tecnologías tiene su propia especificación JSR y su propio número de
versión. En combinación, brindan una lista impresionante de capacidades que permiten que
las aplicaciones Java EE se conecten a bases de datos, publiquen y utilicen servicios web,
proporcionen aplicaciones web, realicen transacciones, implementen políticas de seguridad
y se conecten con una gran cantidad de recursos externos para tareas, como mensajería,
asignación de nombres, envío de correos electrónicos y comunicación con aplicaciones que
no pertenecen a Java.
El perfil web contiene las tecnologías de Java EE basadas en la Web que comúnmente utilizan
desarrolladores web, como Servlets, Páginas de servidor Java, Caras de servidor Java, CDI, JPA,
JAX-RS, WebSockets y una versión reducida de Enterprise Java Beans (EJB) conocida como EJB
Lite. Muchas de estas tecnologías se describen en detalle a lo largo de este curso.
Referencias
Para obtener más información, consulte el capítulo Introducción a JBoss EAP de la
Guía de desarrollo para Red Hat JBoss EAP 7.0:
https://access.redhat.com/documentation/en/red-hat-jboss-enterpriseapplication-platform/
JB183-EAP7.0-es-2-20180124
51
Capítulo 2. Empaquetado e implementación de una aplicación de Java EE
Cuestionario: Descripción de un servidor de
aplicaciones
Elija las respuestas correctas para las siguientes preguntas:
1.
¿Cuál de los siguientes enunciados acerca de las aplicaciones Java SE y Java EE es
verdadero?
a.
b.
c.
d.
e.
2.
¿Cuáles tres de los siguientes enunciados acerca de JBoss EAP son verdaderos? (Elija
tres opciones).
a.
b.
c.
d.
e.
f.
3.
b.
c.
d.
52
EAP se basa en el servidor web de código abierto Tomcat.
EAP se basa en el servidor de aplicaciones de código abierto Wildfly.
Todas las API de Java EE se empaquetan como componentes modulares en EAP 7.
Todos los módulos se activan de manera predeterminada y no se puede agregar ni
eliminar ningún módulo.
Todas las API de Java EE se empaquetan como componentes modulares en EAP 7.
Estos módulos se puede activar solo cuando se lo requiere.
En EAP 7, la agrupación en clústeres de alta disponibilidad, la mensajería y
el almacenamiento en caché distribuido no están disponibles de manera
predeterminada; debe usar productos de terceros para habilitar estas funciones.
EAP 7 admite tanto el perfil Java EE 7 web como el completo.
¿Cuáles dos de los siguientes enunciados acerca de la arquitectura EAP 7 son
verdaderos? (Elija dos opciones).
a.
4.
Las aplicaciones Java EE se alojan y gestionan mediante un servidor de
aplicaciones.
Las aplicaciones Java SE no se pueden conectar a una base de datos y realizar
transacciones.
Las aplicaciones Java SE son siempre uniproceso. Solo las aplicaciones Java EE son
multiproceso.
Las aplicaciones Java EE no pueden realizar mensajería asíncrona.
En las aplicaciones Java EE, el desarrollador debe implementar manualmente el
subprocesamiento múltiple y la concurrencia.
El concepto de módulos se aplica únicamente a los servicios proporcionados por
EAP. Las aplicaciones no se pueden ejecutar como módulos.
Tanto las aplicaciones desarrolladas por el usuario como los servicios
proporcionados por EAP se pueden ejecutar como módulos.
Los módulos tienen un alcance de visibilidad global; es decir, todos los módulos
pueden acceder a clases de otros módulos en EAP en forma implícita.
EAP tiene control exclusivo de la visibilidad. Las clases de otros módulos se deben
solicitar de manera explicita.
¿Cuál de los siguientes enunciados acerca de los contenedores en un servidor de
aplicaciones es verdadero?
JB183-EAP7.0-es-2-20180124
a.
b.
c.
d.
e.
5.
¿Cuáles dos de los siguientes enunciados acerca de los perfiles de Java EE son
verdaderos? (Elija dos opciones).
a.
b.
c.
d.
e.
6.
Solo debe haber un contenedor en un servidor de aplicaciones. No se permiten
múltiples contenedores dentro de un solo servidor de aplicaciones.
En las implementaciones, los contenedores solo pueden leer descriptores de
implementación XML. Las anotaciones de nivel de código no son compatibles.
Los componentes de aplicaciones pueden configurar la seguridad de manera
declarativa en los descriptores de implementación XML o las anotaciones a nivel
de código.
Un contenedor web puede implementar componentes EJB, JMS y JPA.
El contenedor EJB se debe ejecutar en forma separada, fuera del servidor de
aplicaciones. El servidor de aplicaciones solo admite la ejecución del contenedor
web dentro de este.
Un perfil es una recopilación de API enfocada en tipos de aplicación específicos.
El concepto de un perfil se introdujo en Java EE 7.
La especificación Java EE 7 define tres perfiles: web, ejb y completo.
La especificación Java EE 7 define cuatro perfiles: web, ejb, jms y completo.
La especificación Java EE 7 define dos perfiles: web y completo.
¿Cuáles dos de los siguientes enunciados acerca del perfil web son verdaderos?
(Elija dos opciones).
a.
b.
c.
d.
e.
El perfil web cuenta con todas las API en el perfil completo, así como otras API
enfocadas en tecnologías web.
JBoss EAP no admite el perfil web.
El perfil web admite Beans controlados por mensajes (MDB) JMS.
EJB Lite forma parte del perfil web.
CDI forma parte del perfil web.
JB183-EAP7.0-es-2-20180124
53
Capítulo 2. Empaquetado e implementación de una aplicación de Java EE
Solución
Elija las respuestas correctas para las siguientes preguntas:
1.
¿Cuál de los siguientes enunciados acerca de las aplicaciones Java SE y Java EE es
verdadero?
a.
b.
c.
d.
e.
2.
¿Cuáles tres de los siguientes enunciados acerca de JBoss EAP son verdaderos? (Elija
tres opciones).
a.
b.
c.
d.
e.
f.
3.
b.
c.
d.
El concepto de módulos se aplica únicamente a los servicios proporcionados por
EAP. Las aplicaciones no se pueden ejecutar como módulos.
Tanto las aplicaciones desarrolladas por el usuario como los servicios
proporcionados por EAP se pueden ejecutar como módulos.
Los módulos tienen un alcance de visibilidad global; es decir, todos los módulos
pueden acceder a clases de otros módulos en EAP en forma implícita.
EAP tiene control exclusivo de la visibilidad. Las clases de otros módulos se
deben solicitar de manera explicita.
¿Cuál de los siguientes enunciados acerca de los contenedores en un servidor de
aplicaciones es verdadero?
a.
b.
54
EAP se basa en el servidor web de código abierto Tomcat.
EAP se basa en el servidor de aplicaciones de código abierto Wildfly.
Todas las API de Java EE se empaquetan como componentes modulares en EAP 7.
Todos los módulos se activan de manera predeterminada y no se puede agregar ni
eliminar ningún módulo.
Todas las API de Java EE se empaquetan como componentes modulares en
EAP 7. Estos módulos se puede activar solo cuando se lo requiere.
En EAP 7, la agrupación en clústeres de alta disponibilidad, la mensajería y
el almacenamiento en caché distribuido no están disponibles de manera
predeterminada; debe usar productos de terceros para habilitar estas funciones.
EAP 7 admite tanto el perfil Java EE 7 web como el completo.
¿Cuáles dos de los siguientes enunciados acerca de la arquitectura EAP 7 son
verdaderos? (Elija dos opciones).
a.
4.
Las aplicaciones Java EE se alojan y gestionan mediante un servidor de
aplicaciones.
Las aplicaciones Java SE no se pueden conectar a una base de datos y realizar
transacciones.
Las aplicaciones Java SE son siempre uniproceso. Solo las aplicaciones Java EE son
multiproceso.
Las aplicaciones Java EE no pueden realizar mensajería asíncrona.
En las aplicaciones Java EE, el desarrollador debe implementar manualmente el
subprocesamiento múltiple y la concurrencia.
Solo debe haber un contenedor en un servidor de aplicaciones. No se permiten
múltiples contenedores dentro de un solo servidor de aplicaciones.
En las implementaciones, los contenedores solo pueden leer descriptores de
implementación XML. Las anotaciones de nivel de código no son compatibles.
JB183-EAP7.0-es-2-20180124
Solución
c.
d.
e.
5.
¿Cuáles dos de los siguientes enunciados acerca de los perfiles de Java EE son
verdaderos? (Elija dos opciones).
a.
b.
c.
d.
e.
6.
Los componentes de aplicaciones pueden configurar la seguridad de manera
declarativa en los descriptores de implementación XML o las anotaciones a
nivel de código.
Un contenedor web puede implementar componentes EJB, JMS y JPA.
El contenedor EJB se debe ejecutar en forma separada, fuera del servidor de
aplicaciones. El servidor de aplicaciones solo admite la ejecución del contenedor
web dentro de este.
Un perfil es una recopilación de API enfocada en tipos de aplicación específicos.
El concepto de un perfil se introdujo en Java EE 7.
La especificación Java EE 7 define tres perfiles: web, ejb y completo.
La especificación Java EE 7 define cuatro perfiles: web, ejb, jms y completo.
La especificación Java EE 7 define dos perfiles: web y completo.
¿Cuáles dos de los siguientes enunciados acerca del perfil web son verdaderos?
(Elija dos opciones).
a.
b.
c.
d.
e.
El perfil web cuenta con todas las API en el perfil completo, así como otras API
enfocadas en tecnologías web.
JBoss EAP no admite el perfil web.
El perfil web admite Beans controlados por mensajes (MDB) JMS.
EJB Lite forma parte del perfil web.
CDI forma parte del perfil web.
JB183-EAP7.0-es-2-20180124
55
Capítulo 2. Empaquetado e implementación de una aplicación de Java EE
Identificación de recursos JNDI
Objetivo
Después de completar esta sección, los estudiantes deberán ser capaces de enumerar los
tipos de recursos JNDI más comunes y sus convenciones de nomenclatura típicas.
Recursos JNDI
En una aplicación multinivel distribuida que ejecuta diferentes componentes en varios
servidores, los componentes necesitan comunicarse entre sí. Por ejemplo, un cliente Java
puede invocar métodos en un EJB implementado en una máquina separada; y el componente
EJB se comunica con una base de datos para obtener datos. La Interfaz de nombrado y
Directorio Java (JNDI) es una API de Java para un servicio de directorio (búsqueda de recursos)
que permite a los componentes descubrir y buscar objetos a través de un nombre lógico.
Un recurso es un objeto lógico que los componentes pueden buscar y utilizar en una
aplicación Java EE. Cada recurso está identificado por un nombre único, denominado el
nombre JNDI o una referencia de recursos JNDI. Por ejemplo, el nombre JNDI de la fuente de
datos Conectividad de base de datos Java (JDBC), que se brinda de manera predeterminada
(indicando una base de datos H2 incorporada) en JBoss EAP es java:jboss/datasources/
ExampleDS.
Los recursos JNDI no están restrictos a fuentes de datos JDBC. Se pueden configurar varios
tipos de recursos, como objetos JMS ConnectionFactory, colas y temas de mensajería,
servidores de correo electrónico, conjuntos (pools) de hilos y otros.
Cada una de las diferentes referencias JNDI se organizan en un namespace lógico, en una
jerarquía de árbol, generalmente denominada JNDI tree (Árbol JNDI). Los siguientes son
algunos de los espacios de nombre más comunes en el servidor de aplicaciones JBoss EAP:
• Las fuentes de datos JDBC están registradas bajo el espacio de nombre java:jboss/
datasources/*.
• Los recursos relacionados con JMS se registran con el nombre de espacio java:jboss/
jms/* (Colas JMS bajojava:jboss/jms/queue/* y Temas bajo java:jboss/jms/
topic/*).
• Los recursos relacionados con el correo electrónico se registran con el nombre de espacio
java:jboss/mail/*.
• Los recursos relacionados con subprocesamiento múltiple y concurrencia se registran con
el nombre de espacio java:jboss/ee/concurrency/*.
Inyección de recursos mediante CDI
Java EE 7 proporciona Contextos e Inyección de dependencia (CDI) para permitir que los
componentes obtengan referencias a otros objetos de componentes, así como recursos de
servidores de aplicaciones sin crear instancias de los objetos de componentes o recursos del
servidor en forma manual. Esto permite una arquitectura de bajo acoplamiento, en la que el
cliente no necesita estar al tanto de todos los detalles de implementación de nivel inferior del
objeto invocado.
56
JB183-EAP7.0-es-2-20180124
Inyección de recursos mediante CDI
Después de configurar las referencias de recursos JNDI necesarias a nivel del servidor de
aplicaciones, puede inyectar los recursos en aplicaciones que requieren el recurso usando la
anotación @Resource. El servidor de aplicaciones crea instancias del recurso en el tiempo de
ejecución y proporciona una referencia al recurso.
Por ejemplo, supongamos que ha configurado una referencia de fuente de datos JDBC como
la siguiente en un archivo de configuración EAP:
<subsystem xmlns="urn:jboss:domain:datasources:4.0">
<datasources>
<datasource jndi-name="java:jboss/datasources/ExampleDS" pool-name="ExampleDS"
enabled="true" use-java-context="true">
<connection-url>jdbc:h2:mem:test;DB_CLOSE_DELAY=-1;DB_CLOSE_ON_EXIT=FALSE</
connection-url>
<driver>h2</driver>
<security>
<user-name>sa</user-name>
<password>sa</password>
</security>
</datasource>
</datasources>
...
Ahora, puede inyectar la fuente de datos java:jboss/datasources/ExampleDS en la
aplicación de la siguiente manera:
public class TestDS {
@Resource(name="java:jboss/datasources/ExampleDS")
private javax.sql.DataSource ds;
// Use the DataSource reference to create a Connection etc..
De forma similar, si configuró un recursos de Cola JMS como el siguiente en EAP:
<jms-queue name="helloWorldQueue" entries="java:jboss/jms/queue/helloWorldQueue"/>
Ahora puede enviar mensajes a esta cola inyectando el recurso en una clase de cliente JMS:
@Resource(mappedName = "java:jboss/jms/queue/helloWorldQueue")
private Queue helloWorldQueue;
@Inject
JMSContext context;
// Use the Queue object to send messages...
try {
context.createProducer().send(helloWorldQueue, "Hello World!");
...
}
JB183-EAP7.0-es-2-20180124
57
Capítulo 2. Empaquetado e implementación de una aplicación de Java EE
Referencias
Para obtener más información, consulte el capítulo de Búsqueda de JNDI Remota y
el capítulo Contextos e Inyección de dependencia (CDI) de la Guía de desarrollo para
Red Hat JBoss EAP:
https://access.redhat.com/documentation/en/red-hat-jboss-enterpriseapplication-platform/7.0/
58
JB183-EAP7.0-es-2-20180124
Ejercicio guiado: Identificación de recursos JNDI
Ejercicio guiado: Identificación de recursos
JNDI
En este ejercicio, explorará las referencias de recursos JNDI en el servidor de aplicaciones.
Resultados
Deberá ser capaz de iniciar EAP de una ventana de terminal y explorar las referencias de
recursos JNDI en el servidor de aplicaciones.
Antes de comenzar
JBoss EAP ya se encuentra instalado en la carpeta /opt/jboss-eap-7.0 y se puede acceder
a él con la variable de entorno JBOSS_HOME que indica esta carpeta.
Abra una ventana de terminal en la máquina virtual de la estación de trabajo y ejecute el
siguiente comando para verificar que EAP no se esté ejecutando actualmente:
[student@workstation ~]$ lab jndi setup
Pasos
1. Inicie JBoss EAP en una nueva ventana de terminal.
1.1. Abra una ventana de terminal desde la máquina virtual de la estación de trabajo
(Aplicaciones > Favoritos > Terminal o haga clic con el botón derecho en el
escritorio, seleccione Abrir en Terminal) y ejecute los siguientes comandos para
iniciar EAP:
[student@workstation ~]$ cd $JBOSS_HOME/bin
[student@workstation bin]$ ./standalone.sh -c standalone-full.xml
nota
A lo largo de este curso, iniciará EAP en modo independiente con el perfil
standalone-full, que contiene todas las API y subsistemas que admiten
el perfil Java EE 7 completo.
1.2. Cuando el servidor de aplicaciones se inicia, el servidor imprime mensajes de
registro en la consola en que inició EAP. Si EAP se inicia correctamente, verá una
salida similar a la siguiente:
13:35:23,615 INFO [org.jboss.as] (Controller Boot Thread) WFLYSRV0051: Admin
console listening on http://127.0.0.1:9990
13:35:23,615 INFO [org.jboss.as] (Controller Boot Thread) WFLYSRV0025: JBoss
EAP 7.0.0.GA (WildFly Core 2.1.2.Final-redhat-1) started in 3321ms - Started
319 of 604 services (393 services are lazy, passive or on-demand)
2.
Vea los mensajes de registro del servidor en la consola, así como el archivo de registro
del servidor.
JB183-EAP7.0-es-2-20180124
59
Capítulo 2. Empaquetado e implementación de una aplicación de Java EE
2.1. EAP imprime mensajes de registros del servidor en la consola en la que lo inició.
Revise brevemente los mensajes de registro desplazándose hacia arriba y hacia
abajo en la ventana del terminal.
2.2. Aunque ver los mensajes de registro en la consola es útil para solucionar problemas
básicos, EAP imprime muchos mensajes cuando se implementan varias aplicaciones
y ver estos mensajes en la consola se vuelve difícil de manejar. Para permitir un
análisis fuera de línea, EAP también escribe mensajes de registro en un archivo.
Abra el archivo /opt/jboss-eap-7.0/standalone/log/server.log en un editor
de texto. Revise brevemente los mensajes en el archivo de registro.
3.
Explore las referencias de recursos JNDI en el archivo de registro de servidores.
3.1. El servidor de aplicaciones mantiene una lista de referencias de recursos JNDI. Los
recursos necesarios para las aplicaciones, como por ejemplo, el correo, las fuentes
de datos JDBC y las fábricas y colas de conexiones JMS están vinculadas a nombres
únicos identificables bajo los respectivos espacios de nombre.
Las fuentes de datos JDBC están vinculadas al espacio de nombre java:jboss/
datasources/*. En el archivo /opt/jboss-eap-7.0/standalone/log/
server.log, verifique poder ver las dos siguientes referencias de fuentes de datos:
WFLYJCA0001: Bound data source [java:jboss/datasources/ExampleDS]
WFLYJCA0001: Bound data source [java:jboss/datasources/MySQLDS]
nota
Consejo: puede usar la capacidad de búsqueda de su editor de texto
(generalmente Ctrl+F) o usar el comando grep para encontrar las
referencias en el archivo de registro:
[student@workstation ~]$ grep -i "datasources" /opt/jboss-eap-7.0/
standalone/log/server.log
Los puntos de referencia ExampleDS a una base de datos H2 incorporada que se
envía con EAP. Los puntos de referencia MySQLDS a una base de datos MySQL que
usará la aplicación To Do List Java EE que debe compilar durante este curso.
3.2. Explore las referencias JNDI relacionadas con JMS.
En el archivo /opt/jboss-eap-7.0/standalone/log/server.log, verifique
poder ver las siguientes referencias relacionadas con JMS:
WFLYMSGAMQ0002: Bound messaging object to jndi name java:jboss/exported/jms/
RemoteConnectionFactory
...
WFLYMSGAMQ0002: Bound messaging object to jndi name java:/ConnectionFactory
...
WFLYMSGAMQ0002: Bound messaging object to jndi name java:jboss/
DefaultJMSConnectionFactory
60
JB183-EAP7.0-es-2-20180124
4.
Detenga EAP presionando Ctrl+C en la ventana de terminal en la que inició las
instancias.
Esto concluye el ejercicio guiado.
JB183-EAP7.0-es-2-20180124
61
Capítulo 2. Empaquetado e implementación de una aplicación de Java EE
Empaquetado e implementación de una
aplicación de Java EE
Objetivo
Después de completar esta sección, los estudiantes deberán ser capaces de empaquetar una
aplicación Java EE simple e implementarla en JBoss EAP mediante Maven.
Empaquetado e implementación de aplicaciones de
Java EE
Las aplicaciones Java EE se pueden empaquetar de diferentes formas para implementar en
un servidor de aplicaciones compatible. Según el tipo de aplicación y los componentes que
contiene, las aplicaciones se pueden empaquetar en diferentes tipos de implementación
(archivos comprimidos que contienen clases, activos de aplicaciones y descriptores de
implementación de XML). Los tres tipos de implementación más comunes son:
• JAR files: los archivos JAR pueden contener clases de Objetos comunes antiguos de Java
(POJO), Beans de entidad JPA, clases de Java de utilidad, EJB y MDB. Al implementarse en un
servidor de aplicaciones, dependiendo del tipo de componentes dentro del archivo JAR, el
servidor de aplicaciones busca descriptores de implementación XML o anotaciones a nivel
de código, e implementa cada componente en consecuencia.
Figura 2.3: Estructura del archivo EJB JAR de muestra
• Archivos WAR: Un archivo WAR se utiliza para empaquetar aplicaciones web. Puede
contener uno o más archivos JAR, así como archivos del descriptor de implementación XML
en las carpetas WEB-INF o WEB-INF/classes/META-INF.
62
JB183-EAP7.0-es-2-20180124
Empaquetado e implementación de aplicaciones de Java EE
Figura 2.4: Estructura del archivo WAR de muestra
• EAR files: un archivo EAR contiene varios archivos JAR y WAR, así como descriptores de
implementación XML en la carpeta META-INF.
Figura 2.5: Estructura del archivo EAR de muestra
JB183-EAP7.0-es-2-20180124
63
Capítulo 2. Empaquetado e implementación de una aplicación de Java EE
nota
De estar presentes, los descriptores de implementación XML reemplazan a las
anotaciones de nivel de código. Para un componente dado, evite duplicar la
configuración en ambos lugares.
Empaquetado e implementación de aplicaciones de
Java EE en EAP
Maven proporciona varios complementos (plug-ins) útiles para simplificar el empaquetado y
la implementación en EAP durante el desarrollo del ciclo de vida útil.
El maven-war-plugin crea archivos WAR de su aplicación, siempre que usted siga el diseño
del código fuente Maven estándar. El maven-war-plugin se puede declarar en la sección
<build> de su archivo pom.xml Maven:
<build>
<finalName>todo</finalName>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-war-plugin</artifactId>
<version>${version.war.plugin}</version>
<extensions>false</extensions>
<configuration>
<failOnMissingWebXml>false</failOnMissingWebXml>
</configuration>
</plugin>
</plugins>
</build>
En forma similar, el maven-ear-plugin crea archivos EAR del código fuente de su aplicación.
Se declara en la sección <build> de su archivo pom.xml Maven. Debe indicar los archivos
WAR que se deben empaquetar dentro del archivo EAR con la etiqueta <webModule>:
<build>
<finalName>todo</finalName>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-ear-plugin</artifactId>
<version>${version.ear.plugin}</version>
<configuration>
<version>6</version>
<defaultLibBundleDir>lib</defaultLibBundleDir>
<modules>
<webModule>
<groupId>com.redhat.training</groupId>
<artifactId>todojee-web</artifactId>
<contextRoot>/todo-ear</contextRoot>
</webModule>
</modules>
<fileNameMapping>no-version</fileNameMapping>
</configuration>
</plugin>
64
JB183-EAP7.0-es-2-20180124
Empaquetado e implementación de aplicaciones de Java EE en EAP
</plugins>
</build>
Puede usar Maven para implementar aplicaciones en JBoss EAP, mediante el wildflymaven-plugin, que proporciona funciones para implementar aplicaciones y anular
su implementación en EAP. Admite la implementación de los tres tipos de unidades de
implementación: JAR, WAR y EAR. Puede declarar el complemento (plug-in) en el archivo
pom.xml Maven de su proyecto:
<plugin>
<groupId>org.wildfly.plugins</groupId>
<artifactId>wildfly-maven-plugin</artifactId>
<version>${version.wildfly.maven.plugin}</version>
</plugin>
Para compilar, empaquetar e implementar una aplicación en EAP, ejecute el siguiente
comando de la carpeta raíz de su proyecto:
[student@workstation todojee]$ mvn clean package wildfly:deploy
Para anular la implementación de una aplicación de EAP, ejecute el siguiente comando de la
carpeta raíz de su proyecto:
[student@workstation todojee]$ mvn wildfly:undeploy
Referencias
Para obtener más información, consulte el capítulo Implementación de aplicaciones
mediante Maven de la Guía de desarrollo para Red Hat JBoss EAP 7.0:
https://access.redhat.com/documentation/en/red-hat-jboss-enterpriseapplication-platform/
JB183-EAP7.0-es-2-20180124
65
Capítulo 2. Empaquetado e implementación de una aplicación de Java EE
Ejercicio guiado: Empaquetado e
implementación de una aplicación de Java EE
Resultados
Deberá ser capaz de importar un simple proyecto de la aplicación web Java EE en JBoss
Developer Studio y empaquetarlo e implementarlo en EAP.
Antes de comenzar
Abra una ventana de terminal en la máquina virtual de la estación de trabajo y ejecute el
siguiente comando para descargar los archivos del trabajo de laboratorio necesarios para
este taller.
[student@workstation ~]$ lab hello-web setup
1.
Importe el proyecto hello-web en el JBoss Developer Studio IDE (JBDS).
1.1. Inicie JBDS haciendo doble clic en el icono del escritorio de la máquina virtual
workstation.
1.2. En la ventana Eclipse Launcher, ingrese /home/student/JB183/workspace en el
campo Workspace (Espacio de trabajo) y, luego, haga clic en Launch (Iniciar).
Figura 2.6: Diálogo del espacio de trabajo JBDS
1.3. En el menú de JBDS, haga clic en File (Archivo) > Import (Importar)para abrir el
asistente para Import (Importar).
1.4. En la página Select (Seleccionar), haga clic en Maven > Existing Maven Projects
(Proyectos de Maven existentes) y, luego, haga clic en Next (Siguiente).
1.5. En la página Maven projects (Proyectos de Maven), haga clic en Browse (Explorar)
para abrir la ventana Select root folder (Seleccionar carpeta raíz). Diríjase al
directorio /home/student/JB183/labs/. Seleccione la carpeta hello-web y haga
clic en OK (Aceptar).
1.6. En la página Maven projects (Proyectos de Maven), haga clic en Finish (Finalizar).
66
JB183-EAP7.0-es-2-20180124
1.7. Observe la barra de estado de JBDS para monitorear el progreso de la operación
de importación. Es probable que la descarga de todas las dependencias requeridas
demore unos minutos.
2.
Explore el archivo pom.xml Maven del proyecto y el archivo POM principal.
2.1. Expanda el ítem hello-web en el panel Project Explorer (Explorador de proyectos)
izquierdo y haga doble clic en el archivo pom.xml.
La pestaña Overview (Descripción general) se puede ver en la principal ventana
del editor y muestra una vista avanzada del proyecto. En esta pestaña se muestran
Group Id, Artifact Id y la Version (generalmente se los conoce como las
coordenadas GAV de un proyecto o módulo) del proyecto hello-web, así como su
elemento principal.
2.2. Haga clic en la pestaña Dependencies (Dependencias) para ver las dependencias
(librerías, marcos [frameworks] y módulos de los que depende este proyecto) del
proyecto.
2.3. Haga clic en la pestaña pom.xml para ver el texto completo del archivo pom.xml.
Repase brevemente las coordenadas GAV para este proyecto:
<artifactId>hello-web</artifactId>
<packaging>war</packaging>
<name>Hello World web app Project</name>
<description>This is the hello-web project</description>
<parent>
<groupId>com.redhat.training</groupId>
<artifactId>parent-pom</artifactId>
<version>1.0</version>
<relativePath>../pom.xml</relativePath>
</parent>
...
<build>
<plugins>
<plugin>
<artifactId>maven-war-plugin</artifactId>
<version>${version.war.plugin}</version>
...
</plugin>
El formato de packaging (empaquetado) se declara como war. Maven se
asegura de que, cuando se compile el proyecto, se generará un archivo WAR que
se puede implementar en EAP.
Este proyecto hereda las declaraciones y propiedades del archivo POM principal,
que se ubica en /home/student/JB183/labs/pom.xml. El archivo POM
principal declara muchos atributos y propiedades que pueden ser usados
por varios proyectos secundarios que hacen referencia a él. Es una práctica
recomendada de Maven declarar repositorios, dependencias maestras, lista de
materiales (BOM) y demás atributos usados en varios proyectos para evitar la
duplicación.
JB183-EAP7.0-es-2-20180124
67
Capítulo 2. Empaquetado e implementación de una aplicación de Java EE
Debido a que este proyecto es una aplicación web compilada como archivo
WAR, se configura el complemento (plug-in) WAR de Maven (maven-warplugin).
2.4. Abra el archivo pom principal /home/student/JB183/labs/pom.xml mediante
JBDS (haga clic en File (Archivo) > Open File (Abrir archivo)) o en un editor de texto.
2.5. El archivo POM principal declara una cantidad de propiedades comúnmente usadas
en todos los proyectos de este curso. Por ejemplo, el archivo declara que la versión
de la lista de materiales de JBoss EAP (BOM) es 7.0.0.GA y que todos los proyectos
estarán compilados en JDK 1.8.
El complemento (plug-in) Wildfly Maven también se declara en el archivo POM
principal. Se utiliza para implementar el archivo WAR del proyecto en EAP y depende
de la variable del entorno JBOSS_HOME para identificar la instancia de JBoss EAP en
la que el archivo WAR se debe implementar.
3.
Configure una instancia de servidor EAP en JBDS.
3.1. Haga clic en la pestaña Servers (Servidores) en la parte inferior de JBDS, debajo del
área del editor principal.
Figura 2.7: La pestaña de servidores JBDS
3.2. Haga clic en Sin servidores disponibles para definir un nuevo servidor EAP.
3.3. En la ventana Define a New Server (Definir un nuevo servidor), seleccione la opción
Red Hat JBoss Enterprise Application Platform 7.0 y haga clic en Next (Siguiente).
68
JB183-EAP7.0-es-2-20180124
Figura 2.8: Defina un nuevo servidor EAP
3.4. En la ventana Create a new Server Adapter (Crear un nuevo adaptador del servidor),
deje los campos con sus valores predeterminados, como se muestra en la figura a
continuación, y haga clic en Next (Siguiente).
JB183-EAP7.0-es-2-20180124
69
Capítulo 2. Empaquetado e implementación de una aplicación de Java EE
Figura 2.9: Configuración del nuevo adaptador del servidor
3.5. En la ventana JBoss Runtime (Tiempo de ejecución de JBoss), haga clic en Browse
(Explorar), junto al campo Home Directory (Directorios de inicio) y seleccione la
carpeta /opt/jboss-eap-7.0 (la variable $JBOSS_HOME del entorno indica esta
tarjeta).
Debido a que estará ejecutando el perfil standalone-full, edite el campo
Archivo de configuración y cámbielo a standalone-full.xml del standalone.xml
predeterminado.
Para continuar, haga clic en Next (Siguiente).
70
JB183-EAP7.0-es-2-20180124
Figura 2.10: Configuración del tiempo de ejecución de JBoss EAP
3.6. En la pantalla Add and Remove (Agregar y eliminar), haga clic en Finish (Finalizar).
3.7. Ahora debe ver una nueva entrada de servidor denominada Red Hat JBoss EAP 7.0,
agregada a la pestaña Servers (Servidores) de JBDS. Haga clic en esta entrada para
expandirla.
Figura 2.11: Agregue un nuevo servidor JBoss EAP
4.
Inicie EAP desde dentro de JBDS.
4.1. Haga clic con el botón derecho en Red Hat JBoss EAP 7.0 en la pestaña Servers
(Servidores) y haga clic en Start (Iniciar) (icono verde de reproducción) para iniciar la
instancia EAP recientemente agregada.
JB183-EAP7.0-es-2-20180124
71
Capítulo 2. Empaquetado e implementación de una aplicación de Java EE
Figura 2.12: Inicio de un servidor JBoss EAP
4.2. Cuando EAP se inicia, imprime mensajes en la pestaña Console (Consola) de JBDS.
Verifique que no aparezcan errores en la consola.
Figura 2.13: Salida del registro de la consola JBoss EAP
72
JB183-EAP7.0-es-2-20180124
5.
Compile, empaquete e implemente la aplicación hello-web.
5.1. Abra una nueva ventana de terminal en la máquina virtualworkstation y ejecute
los siguientes comandos para compilar, empaquetar e implementar la aplicación
hello-web mediante Maven:
[student@workstation ~]$ cd /home/student/JB183/labs/hello-web
[student@workstation hello-web]$ mvn clean package wildfly:deploy
Al ejecutar el comando anterior, verá la siguiente salida:
[INFO]
[INFO]
[INFO]
[INFO]
[INFO]
[INFO]
[INFO]
-----------------------------------------------------------------------BUILD SUCCESS
-----------------------------------------------------------------------Total time: 24.160 s
Finished at: 2016-11-20T01:08:05-05:00
Final Memory: 34M/248M
------------------------------------------------------------------------
5.2. Haga clic en Console (Consola) en JBDS para el servidor EAP y observe la
implementación de la aplicación hello-web:
01:08:03,664 INFO [org.jboss.as.server.deployment] (MSC service thread 1-1)
WFLYSRV0027: Starting deployment of "hello-web.war" (runtime-name: "helloweb.war")
…
01:08:05,624 INFO [org.wildfly.extension.undertow] (ServerService Thread Pool
-- 72) WFLYUT0021: Registered web context: /hello-web
01:08:05,705 INFO [org.jboss.as.server] (management-handler-thread - 1)
WFLYSRV0010: Deployed "hello-web.war" (runtime-name : "hello-web.war")
6.
Acceda a la aplicación hello-web mediante un explorador.
6.1. Verifique que no aparezcan errores en la consola al implementar la aplicación.
Use un explorador web en la máquina virtual workstation para dirigirse a http://
localhost:8080/hello-web para acceder a la aplicación hello-web.
Figura 2.14: La aplicación hello-web.
JB183-EAP7.0-es-2-20180124
73
Capítulo 2. Empaquetado e implementación de una aplicación de Java EE
7.
Ingrese John Doe en el campo Enter your name (Ingrese su nombre) y haga clic en
Submit (Enviar).
8.
Verifique que el servidor procese la entrada y responda con el mensaje Hola, así como la
hora actual en el servidor.
Figura 2.15: La respuesta de la aplicación hello-web.
9.
Anule la implementación de la aplicación y detenga EAP.
9.1. En la ventana del terminal donde ejecutó el comando Maven para implementar
la aplicación, ejecute el siguiente comando para anular la implementación de la
aplicación de EAP:
[student@workstation hello-web]$ mvn wildfly:undeploy
Al ejecutar el comando anterior, se anula la implementación del archivo helloweb.war de EAP. Verá la siguiente salida en la pestaña Console (Consola) de EAP:
21:00:31,705 INFO [org.wildfly.extension.undertow] (ServerService Thread Pool
-- 77) WFLYUT0022: Unregistered web context: /hello-web
21:00:31,988 INFO [org.jboss.as.server] (management-handler-thread - 13)
WFLYSRV0009: Undeployed "hello-web.war" (runtime-name: "hello-web.war")
9.2. Haga clic con el botón derecho en el proyecto hello-web en Project Explorer
(Explorador de proyectos) y seleccione Close Project (Cerrar proyecto) para cerrar el
proyecto.
9.3. Haga clic con el botón derecho en Red Hat JBoss EAP 7.0 en la pestaña Servers
(Servidores) y haga clic en Stop (Detener) para detener la instancia EAP.
Esto concluye el ejercicio guiado.
74
JB183-EAP7.0-es-2-20180124
Trabajo de laboratorio: Empaquetado e implementación de aplicaciones en un servidor de aplicaciones
Trabajo de laboratorio: Empaquetado e
implementación de aplicaciones en un
servidor de aplicaciones
En este trabajo de laboratorio, aprenderá a empaquetar e implementar una aplicación
Java EE mediante Maven y JBoss Developer Studio.
Resultados
Deberá ser capaz de empaquetar e implementar la aplicación To Do List Java EE en JBoss EAP
mediante Maven y JBoss Developer Studio.
Antes de comenzar
Abra una ventana de terminal en la máquina virtual workstation y ejecute el siguiente
comando para descargar los archivos necesarios para este trabajo de laboratorio.
[student@workstation ~]$ lab todojee setup
Pasos
1. Importe el proyecto todojee en JBoss Developer Studio IDE (JBDS).
2.
Inicie EAP desde dentro de JBDS.
3.
Compile, empaquete e implemente la aplicación todo-app en EAP.
4.
Verifique la implementación correcta del archivo todo-app en EAP.
5.
Navegue a http://localhost:8080/todo para acceder a la aplicación todo.
JB183-EAP7.0-es-2-20180124
75
Capítulo 2. Empaquetado e implementación de una aplicación de Java EE
Aplicación To Do List
6.
Explore la funcionalidad de la aplicación To Do List.
6.1. Explore las tareas existentes.
Un conjunto de tareas se carga previamente en la base de datos. Revise las
tareas utilizando la barra de paginación debajo de la lista de tareas. La casilla de
verificación Done (Listo) se selecciona para tareas completadas y el texto Description
(Descripción) aparece en tamaño de fuente tachado:
Una tarea completada
6.2. Agregue una nueva tarea.
Puede agregar una nueva tarea ingresando una descripción de tareas en el panel
Add Task (Agregar tarea) en el lado derecho. Si la tarea ya está completa, puede
seleccionar la casilla de verificación Completed (Completo). Haga clic en Save
(Guardar) para agregar su tarea a la base de datos.
76
JB183-EAP7.0-es-2-20180124
Agregue una tarea
Solo se muestran cinco tareas por página. Use la barra de paginación en la parte
inferior de la tabla para navegar a la última página y verifique que aparezca su tarea
recientemente agregada.
6.3. Complete una tarea.
Para completar una tarea, solo debe activar la casilla de verificación en la columna
Done (Listo) en cada tarea. Al volver a activar la casilla de verificación, se revierte el
estado y se marca la tarea como pendiente.
6.4. Elimine una tarea.
Para eliminar una tarea, haga clic en la X roja de la última columna de la derecha.
7.
Limpieza y calificación.
7.1. Para verificar que ha implementado correctamente la aplicación, abra una nueva
ventana de terminal en la máquina virtual de la estación de trabajo y ejecute el
siguiente comando para calificar este trabajo de laboratorio:
[student@workstation ~]$ lab todojee grade
Si observa fallas después de ejecutar el comando, vea los errores, solucione los
problemas de implementación y corrija los errores. Vuelva a ejecutar el script de
calificación y verifique que el resultado sea satisfactorio.
7.2. En la ventana del terminal donde ejecutó el comando Maven para implementar
la aplicación, ejecute el siguiente comando para anular la implementación de la
aplicación de EAP:
[student@workstation todojee]$ mvn wildfly:undeploy
21:16:33,035 INFO [org.jboss.as.server] (management-handler-thread - 9)
WFLYSRV0009: Undeployed "todo.war" (runtime-name: "todo.war")
7.3. Haga clic con el botón derecho en el proyecto todojee en Project Explorer
(Explorador de proyectos) y seleccione Close Project (Cerrar proyecto).
JB183-EAP7.0-es-2-20180124
77
Capítulo 2. Empaquetado e implementación de una aplicación de Java EE
7.4. Haga clic con el botón derecho en Red Hat JBoss EAP 7.0 en la pestaña Servers
(Servidores) y haga clic en Stop (Detener) para detener la instancia EAP.
Esto concluye el trabajo de laboratorio.
78
JB183-EAP7.0-es-2-20180124
Solución
Solución
En este trabajo de laboratorio, aprenderá a empaquetar e implementar una aplicación
Java EE mediante Maven y JBoss Developer Studio.
Resultados
Deberá ser capaz de empaquetar e implementar la aplicación To Do List Java EE en JBoss EAP
mediante Maven y JBoss Developer Studio.
Antes de comenzar
Abra una ventana de terminal en la máquina virtual workstation y ejecute el siguiente
comando para descargar los archivos necesarios para este trabajo de laboratorio.
[student@workstation ~]$ lab todojee setup
Pasos
1. Importe el proyecto todojee en JBoss Developer Studio IDE (JBDS).
1.1. Haga clic en el icono JBDS de la máquina virtual workstation para iniciar JBDS.
1.2. En el menú de JBDS, haga clic en File (Archivo) > Import (Importar)para abrir el
asistente para Import (Importar).
1.3. En la página Select (Seleccionar), seleccione Maven > Existing Maven Projects
(Proyectos de Maven existentes) y, luego, haga clic en Next (Siguiente).
1.4. En la página Maven projects (Proyectos de Maven), haga clic en Browse (Explorar)
para abrir la ventana Select root folder (Seleccionar carpeta raíz).
1.5. Diríjase al directorio /home/student/JB183/labs/. Seleccione la carpeta todojee
y haga clic en OK (Aceptar).
1.6. En la página Maven projects (Proyectos de Maven), haga clic en Finish (Finalizar).
1.7. Espere hasta que JBDS termine de importar el proyecto.
2.
Inicie EAP desde dentro de JBDS.
2.1. Haga clic con el botón derecho en Red Hat JBoss EAP 7.0 en la pestaña Servers
(Servidores) y, luego, haga clic en Start (Inicio) para iniciar la instancia EAP
configurada en el ejercicio anterior.
2.2. Cuando EAP se inicia, imprime mensajes en la pestaña Console (Consola) de JBDS.
Verifique que no se encuentren errores en la consola.
3.
Compile, empaquete e implemente la aplicación todo-app en EAP.
Abra una nueva ventana de terminal en la estación de trabajo y ejecute los siguientes
comandos:
[student@workstation ~]$ cd /home/student/JB138/labs/todojee
[student@workstation todojee]$ mvn clean package wildfly:deploy
...
JB183-EAP7.0-es-2-20180124
79
Capítulo 2. Empaquetado e implementación de una aplicación de Java EE
[INFO] -----------------------------------------------------------------------[INFO] BUILD SUCCESS
[INFO] -----------------------------------------------------------------------[INFO] Total time: 9.295 s
[INFO] Finished at: 2016-11-21T16:05:11+05:30
[INFO] Final Memory: 27M/389M
[INFO] ------------------------------------------------------------------------
4.
Verifique la implementación correcta del archivo todo-app en EAP.
Cambie a la pestaña Console (Consola) de JBDS y observe la implementación de la
aplicación todo-app. Verifique que no aparezcan errores en la consola:
WFLYSRV0027: Starting deployment of "todo.war" (runtime-name: "todo.war")
WFLYUT0021: Registered web context: /todo
WFLYSRV0010: Deployed "todo.war" (runtime-name : "todo.war")
5.
Navegue a http://localhost:8080/todo para acceder a la aplicación todo.
Aplicación To Do List
6.
Explore la funcionalidad de la aplicación To Do List.
6.1. Explore las tareas existentes.
Un conjunto de tareas se carga previamente en la base de datos. Revise las
tareas utilizando la barra de paginación debajo de la lista de tareas. La casilla de
verificación Done (Listo) se selecciona para tareas completadas y el texto Description
(Descripción) aparece en tamaño de fuente tachado:
80
JB183-EAP7.0-es-2-20180124
Solución
Una tarea completada
6.2. Agregue una nueva tarea.
Puede agregar una nueva tarea ingresando una descripción de tareas en el panel
Add Task (Agregar tarea) en el lado derecho. Si la tarea ya está completa, puede
seleccionar la casilla de verificación Completed (Completo). Haga clic en Save
(Guardar) para agregar su tarea a la base de datos.
Agregue una tarea
Solo se muestran cinco tareas por página. Use la barra de paginación en la parte
inferior de la tabla para navegar a la última página y verifique que aparezca su tarea
recientemente agregada.
6.3. Complete una tarea.
Para completar una tarea, solo debe activar la casilla de verificación en la columna
Done (Listo) en cada tarea. Al volver a activar la casilla de verificación, se revierte el
estado y se marca la tarea como pendiente.
6.4. Elimine una tarea.
Para eliminar una tarea, haga clic en la X roja de la última columna de la derecha.
7.
Limpieza y calificación.
7.1. Para verificar que ha implementado correctamente la aplicación, abra una nueva
ventana de terminal en la máquina virtual de la estación de trabajo y ejecute el
siguiente comando para calificar este trabajo de laboratorio:
[student@workstation ~]$ lab todojee grade
Si observa fallas después de ejecutar el comando, vea los errores, solucione los
problemas de implementación y corrija los errores. Vuelva a ejecutar el script de
calificación y verifique que el resultado sea satisfactorio.
JB183-EAP7.0-es-2-20180124
81
Capítulo 2. Empaquetado e implementación de una aplicación de Java EE
7.2. En la ventana del terminal donde ejecutó el comando Maven para implementar
la aplicación, ejecute el siguiente comando para anular la implementación de la
aplicación de EAP:
[student@workstation todojee]$ mvn wildfly:undeploy
21:16:33,035 INFO [org.jboss.as.server] (management-handler-thread - 9)
WFLYSRV0009: Undeployed "todo.war" (runtime-name: "todo.war")
7.3. Haga clic con el botón derecho en el proyecto todojee en Project Explorer
(Explorador de proyectos) y seleccione Close Project (Cerrar proyecto).
7.4. Haga clic con el botón derecho en Red Hat JBoss EAP 7.0 en la pestaña Servers
(Servidores) y haga clic en Stop (Detener) para detener la instancia EAP.
Esto concluye el trabajo de laboratorio.
82
JB183-EAP7.0-es-2-20180124
Resumen
Resumen
En este capítulo, aprendió lo siguiente:
• Un servidor de aplicaciones proporciona el entorno y la infraestructura de tiempo de
ejecución que se necesitan para alojar y administrar aplicaciones empresariales Java EE.
• Red Hat JBoss EAP 7 es un servidor de aplicaciones que cumple con Java EE 7 y proporciona
una infraestructura confiable, de alto rendimiento, liviana y compatible para implementar
aplicaciones Java EE.
• Un perfil en el contexto de un servidor de aplicaciones Java EE es un conjunto de API
relacionadas dirigido a un tipo específico de aplicaciones. La especificación Java EE 7 define
dos perfiles: web y completo. JBoss EAP es completamente compatible con ambos perfiles.
• Un recurso es un objeto lógico que los componentes pueden buscar y utilizar en una
aplicación Java EE. Cada recurso está identificado por un nombre único, denominado el
nombre JNDI o una referencia de recursos JNDI.
• Las referencias JNDI se organizan en un namespace lógico, en una jerarquía de árbol,
generalmente denominada JNDI tree (Árbol JNDI).
• Puede inyectar recursos de JNDI en aplicaciones que requieren el recurso utilizando una
anotación @Resource.
• Las aplicaciones Java EE se empaquetan y se implementan en diferentes formatos. Los tres
más comunes son: archivos JAR, WAR y EAR.
• Maven implementa aplicaciones en JBoss EAP, mediante el complemento (plug-in)
Wildfly Maven, que proporciona funciones para implementar aplicaciones y anular su
implementación en EAP. Admite la implementación de los tres tipos de unidades de
implementación: JAR, WAR y EAR.
JB183-EAP7.0-es-2-20180124
83
84
TRAINING
CAPÍTULO 3
CREACIÓN DE ENTERPRISE JAVA
BEANS
Descripción general
Meta
Crear Enterprise Java Beans.
Objetivos
• Convertir un POJO en un EJB.
• Acceder a un EJB de manera local y remota.
• Describir el ciclo de vida de los EJB.
• Describir las transacciones gestionadas por contenedor
y gestionadas por bean y delimitar cada una en un EJB.
Secciones
• Conversión de un POJO a un EJB (y ejercicio guiado)
• Acceso local y remoto a un EJB (y ejercicio guiado)
• Descripción del ciclo de vida de un EJB (y cuestionario)
• Delimitación de transacciones implícitas y explícitas (y
cuestionario)
Trabajo de laboratorio
JB183-EAP7.0-es-2-20180124
• Creación de Enterprise Java Beans
85
Capítulo 3. Creación de Enterprise Java Beans
Conversión de un POJO en un EJB
Objetivo
Tras finalizar esta sección, los estudiantes deberán ser capaces de convertir un POJO en un
EJB.
Descripción de Enterprise Java Beans (EJB)
Un Enterprise Java Bean (EJB) es un componente de Java EE que, por lo general, se usa para
encapsular lógica de negocio en una aplicación empresarial. A diferencia de los beans Java
simples de Java SE, donde el desarrollador debe implementar explícitamente conceptos como
subprocesamiento múltiple, concurrencia, transacciones y seguridad, en un EJB, el servidor
de aplicaciones proporciona estas características en el tiempo de ejecución y permite que el
desarrollador se centre en redactar la lógica de negocio para la aplicación.
El uso de EJB para modelar la lógica de negocio de una aplicación empresarial tiene varias
ventajas:
• Los EJB brindan servicios de sistema de bajo nivel, como subprocesamiento múltiple y
concurrencia, sin que el desarrollador tenga que escribir código explícitamente para esos
servicios. Esto es importante para las aplicaciones empresariales que tienen una gran
cantidad de usuarios que acceden a la aplicación al mismo tiempo.
• La lógica de negocio se encapsula en un componente portátil que se puede distribuir en
varias máquinas de manera transparente para los clientes, y le permite balancear la carga
de las solicitudes cuando acceden muchos clientes a la aplicación al mismo tiempo.
• El código cliente se simplifica debido a que el cliente puede centrarse solo en los aspectos
de la interfaz de usuario, sin mezclar lógica de negocio. Por ejemplo, observe cómo la
aplicación To Do List de Java SE combina el código de interfaz de usuario y la lógica core
para la gestión de listas en el mismo proceso y, a menudo, en la misma clase.
• Los EJB brindan capacidades transaccionales a las aplicaciones empresariales, donde una
cantidad de usuarios accede al mismo tiempo a la aplicación y el servidor de aplicaciones
garantiza la integridad de datos con el uso de las transacciones.
• Los componentes de EJB se pueden proteger para el acceso de grupos o roles. El servidor
de aplicaciones brinda una API para los servicios de autenticación y autorización, sin que el
desarrollador tenga que escribir código explícitamente.
• Se puede acceder a los EJB con diferentes tipos de clientes, como clientes remotos
independientes, otros componentes de Java EE o clientes de servicio web que utilizan
protocolos estándares como SOAP o REST.
Revisión de los tipos de EJB
La especificación de Java EE define dos tipos diferentes de EJB:
• Sesión: Realiza una operación cuando se lo invoca desde un cliente. Por lo general,
la lógica de negocio core de una aplicación se expone como API de alto nivel (patrón
de fachada de sesión) que se puede distribuir y a la que se puede acceder con varios
protocolos (RMI, JNDI, servicios web).
86
JB183-EAP7.0-es-2-20180124
Descripción de los beans de sesión
• Bean controlado por mensaje (MDB): Usado para la comunicación asíncrona entre
componentes en una aplicación Java EE y que se puede usar para recibir mensajes
compatibles con el Servicio de mensajería Java (JMS) y realizar algunas acciones según el
contenido de los mensajes recibidos.
Descripción de los beans de sesión
Un bean de sesión proporciona una interfaz a los clientes y encapsula métodos de lógica de
negocio que se pueden invocar con varios clientes, ya sea local o remotamente, en diferentes
protocolos. Los EJB de sesión se pueden agrupar e implementar en varias máquinas de
manera transparente para los clientes. El estándar Java EE no define formalmente los detalles
generales de cómo se deben agrupar los EJB. Cada servidor de aplicaciones proporciona sus
propios mecanismos para agrupación en clústeres y alta disponibilidad. Por lo general, la
interfaz de un bean de sesión expone una API detallada que encapsula la lógica de negocio
core de la aplicación.
Hay tres tipos diferentes de beans de sesión, según el caso de uso de la aplicación, que se
pueden implementar en un servidor de aplicaciones compatible con Java EE:
Beans de sesión sin estado (SLSB)
Un bean de sesión sin estado no mantiene el estado conversacional con clientes entre
llamadas. Cuando los clientes interactúan con el bean de sesión sin estado e invocan
métodos en él, el servidor de aplicaciones asigna una instancia de un conjunto (pool) de
beans de sesión sin estado, de los cuales ya se crearon instancias previamente. Una vez
que un cliente completa la invocación y se desconecta, la instancia del bean se coloca
nuevamente en el conjunto (pool) o se destruye.
Un bean de sesión sin estado es útil en escenarios donde la aplicación tiene que prestar
servicio a una gran cantidad de clientes que acceden al mismo tiempo a los métodos de
negocio del bean. Por lo general, pueden escalarse mejor que los beans de sesión con estado
debido a que el servidor de aplicaciones no tiene que mantener el estado y los beans se
pueden distribuir en varias máquinas en una implementación grande.
Tenga en cuenta que, cuando trabaja con EJB sin estado, debe asegurarse de no definir
construcciones y elementos de datos con estado que necesiten compartirse entre varios
clientes (por ejemplo, estructuras similares a asignaciones que contienen una caché). Estos
tipos de casos de uso se resolverían más adecuadamente mediante el uso de un bean de
sesión con estado o un bean de sesión singleton.
Un bean de sesión sin estado también es la opción predilecta para exponer extremos de
servicio REST o SOAP para clientes de servicios web. Las anotaciones simples se agregan a
la clase del bean y a los métodos para tener esta funcionalidad sin tener que escribir código
repetitivo para la comunicación de servicios web.
Beans de sesión con estado (SFSB)
En contraste con los beans de sesión sin estado, los beans de sesión con estado mantienen el
estado conversacional con clientes en varias llamadas. Hay una relación de uno a uno entre
la cantidad de instancias de beans con estado y la cantidad de clientes. Cuando un cliente
completa la interacción con el bean y se desconecta, la instancia del bean se destruye. Un
nuevo cliente genera un nuevo bean con estado con su propio estado único. El servidor de
aplicaciones garantiza que cada cliente reciba la misma instancia de un bean de sesión con
estado para cada invocación de método.
JB183-EAP7.0-es-2-20180124
87
Capítulo 3. Creación de Enterprise Java Beans
Los beans de sesión con estado se usan en escenarios donde el estado conversacional debe
mantenerse con un cliente durante la interacción. Por ejemplo, un carrito de compras realiza
un seguimiento de la cantidad de ítems que un cliente agrega al carrito en una aplicación de
comercio electrónico. Cada carrito del cliente se encapsula en el estado del bean y el estado
se actualiza cuando el cliente agrega, actualiza o elimina ítems de compra.
Cuando el desarrollador compila un EJB con estado, cualquier atributo de nivel de clase
se debe incluir como private (privado) y se crean los métodos getter y setter para
proporcionar acceso a estos atributos. Este es un patrón común utilizado en el desarrollo
de Java pero también se incorpora automáticamente cuando trabaja con EJB que respaldan
páginas JSF (Caras de servidor Java). Cuando utiliza lenguaje de expresión (EL) en el código
fuente de JSF para asignar campos de formularios a atributos EJB, se puede acceder
automáticamente a los atributos EJB a través de getters y setters sin usar explícitamente el
nombre del método.
Un ejemplo de un bean de sesión con estado se muestra debajo con sus métodos getter y
setter:
@Stateful
@Named("hello")
public class Hello {
private String name;
@Inject
private PersonService personService;
public void sayHello() throws IllegalStateException, SecurityException, SystemException
{
String response = personService.hello(name);
FacesContext.getCurrentInstance().addMessage(null, new FacesMessage(response));
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
Beans de sesión singleton
Los beans de sesión singleton son beans de sesión de los cuales se crean instancias una vez
por aplicación y que existen durante el ciclo de vida de la aplicación. Cada solicitud del cliente
para un bean singleton va a la misma instancia. Los beans de sesión singleton se usan en
escenarios donde se comparte una única instancia de bean empresarial entre varios clientes.
A diferencia de los beans de sesión sin estado, que están agrupados por el servidor de
aplicaciones, solo hay una instancia de un bean de sesión singleton en la memoria. De
manera similar a los beans de sesión sin estado, los beans de sesión singleton también
se pueden usar para implementar extremos de servicios web. Un desarrollador puede
proporcionar anotaciones para indicar que el servidor de aplicaciones debe iniciar el bean en
el inicio como una optimización de rendimiento (por ejemplo, conexiones de bases de datos,
búsquedas JNDI, creación de fábrica de conexión remota JMS y mucho más).
88
JB183-EAP7.0-es-2-20180124
Beans controlados por mensajes
Beans controlados por mensajes
Un bean controlado por mensaje (MDB) permite que las aplicaciones Java EE procesen
mensajes de manera asíncrona. Una vez implementado en un servidor de aplicaciones,
escucha los mensajes JMS y, para cada mensaje recibido, realiza una acción (se invoca el
método onMessage() de MDB). Los MDB brindan un modelo sin conexión directa impulsado
por eventos para el desarrollo de aplicaciones. Los MDB no se inyectan en o invocan con
código cliente, pero se desencadenan con la recepción de mensajes.
El MDB no tiene estado y no mantiene un estado conversacional con los clientes. El servidor
de aplicaciones mantiene un conjunto (pool) de MDB y administra su ciclo de vida al asignar y
devolver instancias desde y hacia el conjunto. También pueden participar en transacciones y
el servidor de aplicaciones se ocupa del reenvío de mensajes y la confirmación de recepción
de mensajes según el resultado del procesamiento de mensajes.
Existen muchos casos de uso donde se pueden utilizar los MDB. El más popular es para
desvincular sistemas y prevenir que sus API se conecten muy estrechamente con la
invocación directa. En cambio, dos sistemas se pueden comunicar al enviar mensajes
de manera asíncrona, lo que garantiza que los dos sistemas puedan evolucionar
independientemente sin tener impacto uno en el otro.
Generación de un EJB automáticamente mediante
JBoss Developer Studio
JBDS proporciona varias plantillas que se pueden aprovechar para generar código
automáticamente. Mediante una plantilla, es posible sacar provecho de JBDS para generar la
shell de un EJB automáticamente. Debe seguir estos pasos para lograrlo:
1.
En el panel Project Explorer (Explorador de proyectos) del lado izquierdo de JBDS,
seleccione el proyecto al que desea agregarle una clase EJB y, luego, haga clic con el
botón derecho en el nombre del proyecto. Seleccione New (Nuevo) y desplácese a la
parte inferior y seleccione Other (Otro).
2.
Cuando se abra el panel de búsqueda, diríjase a EJB y elija Session Bean (EJB 3.x) (Bean
de sesión [EJB 3.x]).
JB183-EAP7.0-es-2-20180124
89
Capítulo 3. Creación de Enterprise Java Beans
Figura 3.1: Crear un nuevo EJB en JBDS
3.
90
Proporcione el nombre del paquete, así como el nombre de la clase para la clase EJB.
Especifique también el estado y haga clic en Next (Siguiente).
JB183-EAP7.0-es-2-20180124
Generación de un EJB automáticamente mediante JBoss Developer Studio
Figura 3.2: Asignarle un nombre a la nueva clase EJB Java en JBDS y definir su estado
4.
De manera opcional, especifique un nombre alternativo para utilizar cuando inyecta este
EJB, así como el tipo de transacción para el EJB y, luego, haga clic en Finish (Finalizar).
JB183-EAP7.0-es-2-20180124
91
Capítulo 3. Creación de Enterprise Java Beans
Figura 3.3: Proporcionar un nombre para el EJB y definir el tipo de transacción
5.
La nueva clase EJB se abre en la ventana del editor.
Figura 3.4: La nueva clase EJB, creada automáticamente.
92
JB183-EAP7.0-es-2-20180124
Conversión de un POJO en un EJB
Conversión de un POJO en un EJB
Convertir un POJO en un EJB es un proceso simple en el que se anota el POJO con una o más
anotaciones definidas en el estándar Java EE y se ejecuta el EJB resultante en el contexto de
un servidor de aplicaciones. Tomemos el caso de un POJO de la aplicación To Do List:
public class TodoBean {
public void addTodo(TodoItem item) {
...
}
public void findTodo(int id) {
...
}
public void updateTodo(TodoItem item) {
...
}
public void deleteTodo(int id) {
...
}
}
El POJO tiene cuatro métodos de negocio para agregar, buscar, actualizar y eliminar ítems
pendientes. Para convertir este POJO en un EJB de sesión sin estado, solo debe agregar una
anotación @Stateless en el POJO.
@Stateless
public class TodoBean {
...
}
Para convertir este POJO en un bean de sesión con estado, agregue la anotación @Stateful:
@Stateful
public class TodoBean {
...
}
En ambos casos, el servidor de aplicaciones garantiza automáticamente que los métodos
del EJB se ejecuten en un contexto transaccional. Puede anotar el EJB con anotaciones
relacionadas con la seguridad y exponer el EJB como un extremo de servicio web al agregar
anotaciones de servicios web desde el estándar de Java EE.
Para convertir este POJO en un bean de sesión singleton, agregue la anotación @Singleton:
@Singleton
public class TodoBean {
...
}
En escenarios donde desea que un bean singleton realice una inicialización antes de iniciar
las solicitudes de servicio de cliente, puede agregar la anotación @Startup en la clase
singleton para indicarle al contenedor EJB que se requiere esta clase durante la secuencia de
JB183-EAP7.0-es-2-20180124
93
Capítulo 3. Creación de Enterprise Java Beans
inicialización de la aplicación y que se debe crear primero, antes de que se creen instancias
de cualquier otro EJB. Es importante tener en cuenta que la aplicación no podrá iniciarse si
cualquier EJB con la anotación @Startup lanza una excepción durante la inicialización.
También es posible anotar un método de inicialización con la anotación @PostConstruct,
que le indica al contenedor EJB que invoque a ese método de inmediato después de crear
instancias del EJB.
En el siguiente ejemplo se muestra un EJB que se inicializa para el arranque de la aplicación y
utiliza el método init() para definir su estado inicial:
@Singleton
@Startup
public class TodoBean {
@PostConstruct
public void init() {
// do some initialization
}
...
}
Otra diferencia importante entre los POJO y EJB es que, debido a que el contenedor EJB crea
instancias de los EJB, estos no pueden usar un constructor que se base en argumentos. Esto
se debe a que el contenedor EJB no puede definir adecuadamente estos argumentos cuando
crea una instancia del EJB.
Por este motivo, si está trabajando para convertir una clase de POJO que actualmente usa
un constructor con argumentos en un EJB, necesita encontrar una manera de proporcionar
lógica equivalente en un constructor sin argumentos. Si no se proporciona un constructor
para una clase de EJB, el contenedor EJB usa el constructor sin argumentos predeterminado
proporcionado por JVM. Si una clase de EJB proporciona solo un constructor con argumentos,
el contenedor genera un error durante la implementación de la aplicación.
Demostración: Conversión de un POJO en un EJB
1.
Ejecute el siguiente comando para preparar archivos usados por esta demostración.
[student@workstation ~]$ demo convert-ejb setup
2.
Inicie JBDS e importe el proyecto convert-ejb.
Este proyecto es una aplicación web simple que usa una página JSF respaldada por
un EJB con estado y alcance de solicitud para brindar una respuesta aleatoria a una
pregunta.
3.
Inspeccione el archivo pom.xml y observe la dependencia en la especificación del EJB.
<dependency>
<groupId>javax.enterprise</groupId>
<artifactId>cdi-api</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.jboss.spec.javax.ejb</groupId>
94
JB183-EAP7.0-es-2-20180124
Demostración: Conversión de un POJO en un EJB
<artifactId>jboss-ejb-api_3.2_spec</artifactId>
<scope>provided</scope>
</dependency>
Ambas librerías deben utilizar las anotaciones de CDI y EJB, pero se pueden marcar
como proporcionadas debido a que están ubicadas en el servidor de aplicaciones y
están disponibles en el tiempo de ejecución sin estar comprimidas en el paquete WAR.
4.
Revise la página JSF src/main/web-app/index.xhtml, que define la vista que le
aparece al usuario en su explorador.
<ui:define name="content">
<h1>Magic 8 Ball web app</h1>
<br class="clear"/>
<h:form id="form">
<p class="input">
<h:outputLabel value="Enter your question:" for="question" />
<h:inputText value="">#{eightBall.question}" id="question" required="true"
requiredMessage="Question is required"/>
</p>
<br class="clear"/>
<br class="clear"/>
<p class="input">
<h:commandButton action="">#{eightBall.answerQuestion()}" value="Submit"
styleClass="btn" />
</p>
<br class="clear"/>
<br class="clear"/>
<h:messages styleClass="messages"/>
</h:form>
</ui:define>
JSF usa lenguaje de expresión (EL) para conectar elementos en la IU con métodos de un
EJB. Por ejemplo, la expresión de EL #{eightBall.question} usa automáticamente
getter y setter para el campo question (pregunta) en la clase de EJB EightBall.java.
Otro ejemplo de EL es la expresión #{eightBall.answerQuestion()}, que asigna el
método answerQuestion() en la clase de EJB EightBall.
5.
Actualice la clase de bean administrado EightBall utilizada por la página JSF.
//TODO Make @RequestScoped
//TODO Name "eightBall"
public class EightBall {
private String question;
//TODO Inject this EJB
Magic8BallBean eightBallEJB;
public String ask() {
return eightBallEJB.answerQuestion(question);
}
public void answerQuestion() {
String response = ask();
FacesContext.getCurrentInstance().addMessage(null, new FacesMessage(response));
}
JB183-EAP7.0-es-2-20180124
95
Capítulo 3. Creación de Enterprise Java Beans
public String getQuestion() {
return question;
}
public void setQuestion(String question) {
this.question = question;
}
}
Este bean es @RequestScoped debido a que contiene el valor actual de la variable
question, que cambia con cada envío de solicitud o formulario.
Actualice las anotaciones del nivel de la clase para incluir las anotaciones
@RequestScoped y @Named("eightBall") para convertir la clase EightBall en un
EJB que esté disponible para ser utilizado por la página JSF.
@RequestScoped
@Named("eightBall")
public class EightBall {
Incluya una anotación @EJB para inyectar Magic8BallBean en este bean.
@EJB
Magic8BallBean eightBallEJB;
6.
Actualice la clase Magic8BallBean para que sea un EJB sin estado y pueda ser usado
por el EJB EightBall.
//TODO Make this a stateless EJB
public class Magic8BallBean {
public String answerQuestion(String question) {
String[] answers = new String[10];
answers[0] = "It is decidedly so.";
answers[1] = "Ask again later.";
answers[2] = "My reply is no.";
answers[3] = "Cannot predict now.";
answers[4] = "Don't count on it.";
answers[5] = "As I see it, yes.";
answers[6] = "Signs point to yes.";
answers[7] = "My sources say no.";
answers[8] = "Yes.";
answers[9] = "No.";
String answer = answers[ThreadLocalRandom.current().nextInt(0, 10)];
// respond back with Question: {question}
|
Answer: {answer}.
return "Question: " + question + "
|
Answer: " + answer;
}
}
Actualice la anotación del nivel de la clase para incluir @Stateless. Esto hace que el EJB
esté disponible para la inyección.
//TODO Make this a stateless EJB
96
JB183-EAP7.0-es-2-20180124
Demostración: Conversión de un POJO en un EJB
@Stateless
public class Magic8BallBean {
7.
Inicie el servidor JBoss EAP local dentro de JBDS.
En la pestaña Servers (Servidores) en el panel inferior de JBDS, haga clic con el botón
derecho en el servidor Red Hat JBoss EAP 7.0 [Stopped] y haga clic en el botón verde
para iniciar el servidor. Observe la Console (Consola) hasta que el servidor se inicie y vea
el mensaje iniciado:
INFO [org.jboss.as] (Controller Boot Thread) WFLYSRV0025: JBoss EAP 7.0.0.GA
(WildFly Core 2.1.2.Final-redhat-1) started
Se debe encender el servidor para poder ejecutar las pruebas de Arquillian debido a que
requieren el contenedor EJB proporcionado por JBoss EAP.
8.
Ejecute la prueba JUnit test/java/com/redhat/training/ejb/EJBTest.java
proporcionada para verificar que la inyección del bean esté funcionando
adecuadamente; para ello, haga clic con el botón derecho en el archivo EJBTest.java y
en Run As (Ejecutar como) y, luego, haga clic en JUnit Test (Prueba de JUnit).
@RunWith(Arquillian.class)
public class EJBTest {
@Inject
private EightBall eightBall;
@Deployment
public static WebArchive createDeployment() {
return ShrinkWrap.create(WebArchive.class,"convert-ejbtest.war").addClass(EightBall.class).addClass(Magic8BallBean.class)
.addAsManifestResource(EmptyAsset.INSTANCE,
ArchivePaths.create("beans.xml"));
}
@Test
public void testEightBallEJB() {
eightBall.setQuestion("Is it going to rain today?");
Assert.assertNotNull(eightBall.ask());
}
}
La clase de la prueba usa Arquillian para ejecutarse en el servidor. Esto es necesario
porque se requiere un contenedor para que CDI funcione adecuadamente. Asegúrese
de que se supere la prueba para verificar que el bean de respaldo y el EJB se hayan
inyectado correctamente.
9.
Implemente la aplicación en el servidor JBoss EAP local y pruébela en un explorador. En
una ventana de terminal, ejecute el siguiente comando:
[student@workstation ~]$ cd /home/student/JB183/labs/convert-ejb
[student@workstation convert-ejb]$ mvn wildfly:deploy
Abra la siguiente URL en su explorador http://localhost:8080/convert-ejb,
pruebe la aplicación y asegúrese de que responda preguntas correctamente.
JB183-EAP7.0-es-2-20180124
97
Capítulo 3. Creación de Enterprise Java Beans
10. Anule la implementación de la aplicación y detenga el servidor.
[student@workstation convert-ejb]$ mvn wildfly:undeploy
Referencias
Para obtener más información, consulte el capítulo Beans de sesión y el capítulo
Beans controlados por mensajes (MDB) de la Guía de desarrollo para Red Hat
JBoss EAP:
https://access.redhat.com/documentation/en/red-hat-jboss-enterpriseapplication-platform/7.0/
98
JB183-EAP7.0-es-2-20180124
Ejercicio guiado: Creación de un EJB sin estado
Ejercicio guiado: Creación de un EJB sin
estado
En este ejercicio, creará un EJB sin estado que se invocará y los resultados se mostrarán en
una página web.
Resultados
Deberá poder implementar un EJB sin estado que pueda invocarse desde un bean
administrado por JSF.
Antes de comenzar
Abra una ventana de terminal en la máquina virtual workstation y ejecute el siguiente
comando para descargar los archivos necesarios para este trabajo de laboratorio.
[student@workstation ~]$ lab stateless-ejb setup
Pasos
1. Abra JBDS e importe el proyecto de Maven.
1.1. Haga doble clic en el icono de JBoss Developer Studio en el escritorio de la estación
de trabajo para abrir JBDS. Seleccione el espacio de trabajo /home/student/
JB183/workspace y haga clic en OK (Aceptar).
1.2. En el menú de JBDS, haga clic en File (Archivo) > Import (Importar)para abrir el
asistente para Import (Importar).
1.3. En la página Select (Seleccionar), haga clic en Maven > Existing Maven Projects
(Proyectos de Maven existentes) y, luego, haga clic en Next (Siguiente).
1.4. En la página Maven projects (Proyectos de Maven), haga clic en Browse (Explorar)
para abrir la ventana Select root folder (Seleccionar carpeta raíz). Diríjase al
directorio /home/student/JB183/labs/. Seleccione la carpeta stateless-ejb y
haga clic en OK (Aceptar).
1.5. En la página Maven projects (Proyectos de Maven), haga clic en Finish (Finalizar).
1.6. Observe la barra de estado de JBDS para monitorear el progreso de la operación
de importación. Es probable que la descarga de todas las dependencias requeridas
demore unos minutos.
2.
Explore el archivo pom.xml del proyecto; para ello, expanda el ítem stateless-ejb en la
pestaña Project Explorer (Explorador de proyectos) en el panel izquierdo de JBDS y haga
doble clic en el archivo pom.xml.
2.1. Haga clic en la pestaña Overview (Descripción general) en la ventana principal del
editor. En esta pestaña se muestra una vista general del proyecto, y los cambios
que se realizaron en esta ventana se aplicarán en la sección adecuada del archivo
pom.xml.
JB183-EAP7.0-es-2-20180124
99
Capítulo 3. Creación de Enterprise Java Beans
2.2. Haga clic en la pestaña Dependencies (Dependencias) para ver las dependencias
(librerías, marcos [frameworks] y módulos de los que depende este proyecto) del
proyecto.
2.3. Haga clic en la pestaña pom.xml para ver el texto completo del archivo pom.xml.
Observe que la API del EJB se declaró como una dependencia con alcance
proporcionado. Esto se debe a que JBoss EAP implementa el perfil Java EE
completo y, por lo tanto, proporciona las librerías de EJB necesarias en el tiempo de
ejecución.
<dependency>
<groupId>org.jboss.spec.javax.ejb</groupId>
<artifactId>jboss-ejb-api_3.2_spec</artifactId>
<scope>provided</scope>
</dependency>
Esto también le indica a Maven que no comprima estas librerías en el archivo WAR
final.
3.
Explore el código fuente de la aplicación.
3.1. Revise la página JSF que invoca el EJB; para ello, expanda el ítem stateless-ejb en la
pestaña Project Explorer (Explorador de proyectos) en el panel izquierdo de JBDS.
Expanda las carpetas stateless-ejb > src > main > webapp y haga doble clic en el
archivo index.xhtml.
<h:form id="form">
<p class="input">
<h:outputLabel value="Enter your name:" for="name" />
<h:inputText value="#{hello.name}" id="name" required="true"
requiredMessage="Name is required"/>
</p>
<br class="clear"/>
<br class="clear"/>
<p class="input">
<h:commandButton action="#{hello.sayHello()}" value="Submit"
styleClass="btn" />
</p>
<br class="clear"/>
<br class="clear"/>
<h:messages styleClass="messages"/>
</h:form>
El valor de Lenguaje de expresión (Expression Language, EL) #{hello.sayHello()}
se invoca cuando se envía el formulario web.
nota
Para ver la fuente real del archivo index.xhtml, haga clic en la pestaña
Source (Fuente).
3.2. Explore el archivo Java Hello.java.
100
JB183-EAP7.0-es-2-20180124
En el ítem stateless-ejb expandido de la pestaña Project Explorer (Explorador de
proyectos) del panel izquierdo de JBDS, seleccione stateless-ejb > Java Resources
(Recursos de Java) > src/main/java > com.redhat.training.ui y expándalo. Haga
doble clic en el archivo Hello.java.
Observe que el EJB sin estado se inyecta mediante la anotación @EJB.
@EJB
private HelloBean helloEJB;
3.3. Explore el archivo Java HelloBean.java.
En el ítem stateless-ejb expandido de la pestaña Project Explorer (Explorador de
proyectos) del panel izquierdo de JBDS, seleccione stateless-ejb > Java Resources
(Recursos de Java) > src/main/java > com.redhat.training.ejb y expándalo. Haga
doble clic en el archivo HelloBean.java.
public class HelloBean {
public String sayHello(String name) {
// respond back with Hello, {name}.
return "Hello, " + name;
}
}
Este bean define el método público sayHello, que repite una cadena que se envía
como entrada.
4.
Inicie EAP.
Seleccione la pestaña Servers (Servidores) en el panel inferior de JBDS. Se debe haber
agregado el servidor JBoss EAP en un trabajo de laboratorio anterior. Haga clic con el
botón derecho en el servidor Red Hat JBoss EAP 7.0 [Stopped] y haga clic en el botón
verde de inicio para iniciar el servidor. Observe la Console (Consola) hasta que el servidor
se inicie y vea el siguiente mensaje:
INFO [org.jboss.as] (Controller Boot Thread) WFLYSRV0025: JBoss EAP 7.0.0.GA
(WildFly Core 2.1.2.Final-redhat-1) started
5.
Ejecute la prueba de JUnit e inspeccione el resultado.
5.1. Revise la clase de prueba EJBTest.java
En el ítem stateless-ejb expandido de la pestaña Project Explorer (Explorador de
proyectos) del panel izquierdo de JBDS, seleccione stateless-ejb > Java Resources
(Recursos de Java) > src/test/java > com.redhat.training.ejb y haga doble clic en el
archivo EJBTest.java.
...
@RunWith(Arquillian.class)
public class EJBTest {
@Inject
JB183-EAP7.0-es-2-20180124
101
Capítulo 3. Creación de Enterprise Java Beans
private Hello hello;
@Deployment
public static WebArchive createDeployment() {
return ShrinkWrap.create(WebArchive.class,"stateless-ejbtest.war").addClass(HelloBean.class).addClass(Hello.class)
.addAsManifestResource(EmptyAsset.INSTANCE,
ArchivePaths.create("beans.xml"));
}
@Test
public void testHelloEJB() {
hello.setName("John Doe");
String result = hello.greet();
assertEquals("Hello, John Doe", result);
}
}
...
La clase de prueba está anotada con @RunWith(Arquillian.class) para
garantizar que JBDS utilice Arquillian Runner para implementar la aplicación en el
servidor para realizar pruebas.
El bean de respaldo Hello se inyecta con las siguientes líneas:
@Inject
private Hello hello;
5.2. Haga clic con el botón derecho en el nombre del archivo EJBTest.java en el panel
izquierdo, haga clic en la opción Run As (Ejecutar como) y seleccione JUnit Test
(Prueba de JUnit) para ejecutar el método de prueba.
5.3. Expanda el panel JUnit al hacer doble clic en la pestaña JUnit.
Observe que la prueba falló debido a una NameNotFoundException.
Se produce una NameNotFoundException debido a que nunca se creó una
instancia de la clase EJB. Para corregirlo, la clase HelloBean debe anotarse con una
anotación @Stateless para que la clase se convierta en un EJB a fin de que pueda
inyectarse y pueda crearse una instancia de ella.
6.
Actualice HelloBean para que se convierta en un EJB sin estado.
6.1. Actualice HelloBean con la anotación @Stateless:
import javax.ejb.Stateless;
@Stateless
public class HelloBean {
public String sayHello(String name) {
// respond back with Hello, {name}.
return "Hello, " + name;
}
}
102
JB183-EAP7.0-es-2-20180124
6.2. Presione Ctrl+S para guardar los cambios.
7.
Vuelva a ejecutar la prueba de unidad y asegúrese de que la prueba sea exitosa.
7.1. Haga clic con el botón derecho en el nombre del archivo EJBTest.java en el panel
izquierdo, haga clic en la opción Run As (Ejecutar como) y seleccione JUnit Test
(Prueba de JUnit) para volver a ejecutar la prueba.
7.2. Observe el servidor Console (Consola) en JBDS. Los siguientes mensajes confirman
referencias JNDI en EAP para el EJB de sesión sin estado:
INFO [org.jboss.as.ejb3.deployment] (MSC service thread 1-3) WFLYEJB0473: JNDI
bindings for session bean named 'HelloBean' in deployment unit 'deployment
"test.war"' are as follows:
java:global/test/HelloBean!com.redhat.training.ejb.HelloBean
java:app/test/HelloBean!com.redhat.training.ejb.HelloBean
java:module/HelloBean!com.redhat.training.ejb.HelloBean
java:global/test/HelloBean
java:app/test/HelloBean
java:module/HelloBean
7.3. Observe el resultado de la prueba en la pestaña JUnit Test (Prueba de JUnit) en JBDS.
Esta vez la prueba fue satisfactoria.
Figura 3.5: Resultado satisfactorio de la prueba de JUnit.
8.
Implemente la aplicación en JBoss EAP con Maven; para ello, ejecute los siguientes
comandos:
[student@workstation ~]$ cd /home/student/JB183/labs/stateless-ejb
[student@workstation stateless-ejb]$ mvn wildfly:deploy
Una vez finalizado, verá BUILD SUCCESS como se muestra en el próximo ejemplo:
[INFO]
[INFO]
[INFO]
[INFO]
[INFO]
[INFO]
[INFO]
-----------------------------------------------------------------------BUILD SUCCESS
-----------------------------------------------------------------------Total time: 17.116 s
Finished at: 2016-12-01T07:26:55-05:00
Final Memory: 35M/210M
------------------------------------------------------------------------
También valide la implementación exitosa en el registro del servidor que aparece en la
pestaña Console (Consola) en JBDS. Debe aparecer lo siguiente en el registro cuando la
aplicación se implementa de manera correcta:
JB183-EAP7.0-es-2-20180124
103
Capítulo 3. Creación de Enterprise Java Beans
INFO [org.jboss.as.server] (management-handler-thread - 9) WFLYSRV0010: Deployed
"stateless-ejb.war" (runtime-name : "stateless-ejb.war")
9.
Pruebe la aplicación en un explorador.
9.1. Abra la siguiente URL en un explorador en la máquina virtual workstation:
http://localhost:8080/stateless-ejb.
Figura 3.6: Página de inicio de la aplicación.
9.2. Ingrese Shadowman en el cuadro de texto denominado Enter your name: (Ingrese su
nombre:) y haga clic en Submit (Enviar).
La página se actualiza con el mensaje Hello Shadowman:
Figura 3.7: Respuesta de la aplicación.
10. Anule la implementación de la aplicación y detenga EAP.
104
JB183-EAP7.0-es-2-20180124
10.1.Ejecute el siguiente comando para anular la implementación de la aplicación:
[student@workstation stateless-ejb]$ mvn wildfly:undeploy
10.2.Haga clic con el botón derecho en el proyecto stateless-ejb en Project Explorer
(Explorador de proyectos) y seleccione Close Project (Cerrar proyecto).
10.3.Haga clic con el botón derecho en el servidor Red Hat JBoss EAP 7.0 en la pestaña
Servers (Servidores) de JBDS y haga clic en Stop (Detener).
Esto concluye el ejercicio guiado.
JB183-EAP7.0-es-2-20180124
105
Capítulo 3. Creación de Enterprise Java Beans
Acceso local y remoto a un EJB
Objetivos
Tras finalizar esta sección, los estudiantes deberán ser capaces de acceder a un EJB de
manera local y remota.
Acceso a los EJB
Un EJB es un componente portátil que contiene lógica de negocio que se ejecuta en un
servidor de aplicaciones. Los clientes pueden acceder a los EJB de manera local, si el cliente y
EJB forman parte de la misma aplicación, o a través de una Interfaz remota, si el EJB se ejecuta
de manera remota.
Si el cliente y EJB son locales, es decir que se ejecutan dentro del mismo proceso JVM, el
cliente puede invocar todos los métodos públicos en el EJB. En los casos donde el EJB es
remoto, debe proporcionarse una Interfaz remota, que es una interfaz simple de Java que
expone los métodos de negocio de un EJB. La clase de EJB implementa los métodos en la
interfaz remota y se ocultan los detalles de la implementación a los clientes.
Acceso a EJB locales mediante la anotación @EJB
Supongamos que definió un EJB de la siguiente manera:
@Stateless
public class TodoBean {
public void addTodo(TodoItem item) {
...
}
public void findTodo(int id) {
...
}
...
}
...
}
Los clientes pueden invocar métodos en el EJB al inyectar el EJB directamente en el código
mediante la anotación @EJB:
public class TodoClient {
@EJB
TodoBean todo;
TodoItem item = new TodoItem();
item.setDescription("Buy milk");
item.setStatus("PENDING");
//invoke EJB methods
todo.addTodo(item);
...
}
106
JB183-EAP7.0-es-2-20180124
Acceso remoto a los EJB
Acceso remoto a los EJB
En escenarios donde el cliente se ejecuta fuera del contexto de un servidor de aplicaciones
Java EE, o si un componente de Java EE que se ejecuta en un servidor de aplicaciones necesita
acceder a otro EJB que está implementado en un servidor de aplicaciones remoto, puede
usar JNDI para buscar el EJB.
Para asegurarse de que los clientes remotos puedan consumir un EJB, debe declarar una
interfaz enumerando los métodos de negocio del EJB y hacer que el EJB implemente y
anule estos métodos. Por ejemplo, suponiendo que desea brindar un EJB que realiza varias
operaciones matemáticas, declare una interfaz y enumere los métodos de la siguiente
manera:
package com.redhat.training.ejb;
public interface Calculator {
public int add(int a, int b);
public int multiply(int a, int b);
...
}
Ahora tiene que proporcionar implementaciones concretas de estos métodos en el EJB e
indicar que Calculator es la interfaz remota del EJB mediante la anotación @Remote:
package com.redhat.training.ejb;
@Stateless
@Remote(Calculator.class)
public class CalculatorBean implements Calculator {
@Override
public int add(int a, int b) {
return a + b;
}
@Override
public int multiply(int a, int b) {
return a * b;
}
...
}
...
}
Su EJB ahora está listo para ser empaquetado e implementado en un servidor de aplicaciones
y puede servir a clientes remotos.
Búsqueda de EJB remotos mediante JNDI
Los estándares de Java EE han especificado un esquema de búsqueda de JNDI estándar para
los clientes que buscan EJB. Tiene el siguiente aspecto:
/<application-name>/<module-name>/<bean-name>!<fully-qualified-interface-name>
• application-name: El nombre de la aplicación es el nombre del EAR en el que se
implementa el EJB (sin la ampliación .ear). Si el JAR del EJB no se implementa en un EAR,
JB183-EAP7.0-es-2-20180124
107
Capítulo 3. Creación de Enterprise Java Beans
aparece en blanco. El nombre de la aplicación también puede especificarse en el descriptor
de la implementación application.xml del EAR.
• module-name: Por defecto, el nombre del módulo es el nombre del archivo JAR del EJB
(sin el sufijo .jar). El nombre del módulo se puede sobrescribir en el descriptor de la
implementación ejb-jar.xml.
• bean-name: El nombre del EJB que se invocará (la clase de implementación).
• fully-qualified-interface-name: El nombre de la clase completamente calificado de la
interfaz remota. Incluya el nombre completo del paquete.
Si tenemos en cuenta la lista de códigos que se muestra arriba y suponemos que el EJB está
empaquetado dentro de un archivo denominado calculator-ejb.jar, que, a su vez, está
empaquetado en un archivo EAR denominado myapp.ear, los clientes pueden buscar el EJB
mediante la siguiente cadena de búsqueda:
myapp/calculator-ejb/CalculatorBean!com.redhat.training.ejb.Calculator
Cuando se implementa un EJB, el servidor de aplicaciones enumera los diferentes referencias
de JNDI para el EJB en los registros del servidor. En la siguiente lista se muestran las entradas
de JDNI si el EJB está empaquetado e implementado como un archivo JAR, y no como un
archivo EAR:
INFO [org.jboss.as.ejb3.deployment] (MSC service thread 1-2) WFLYEJB0473: JNDI bindings
for session bean named 'CalculatorBean' in deployment unit 'deployment "calculatorejb.jar"' are as follows:
java:global/calculator-ejb/CalculatorBean!com.redhat.training.ejb.Calculator
java:app/calculator-ejb/CalculatorBean!com.redhat.training.ejb.Calculator
java:module/CalculatorBean!com.redhat.training.ejb.Calculator
java:global/calculator-ejb/CalculatorBean
java:app/calculator-ejb/CalculatorBean
java:module/CalculatorBean
Un programa cliente JNDI de muestra que utiliza el esquema de nomenclatura de JNDI para
buscar los EJB remotos tiene el siguiente aspecto:
package com.redhat.training.client;
public class CalculatorClient {
public static void main(String[] args) throws Exception {
String JNDI_URL= "myapp/calculator-ejb/CalculatorBean!com.redhat.training.ejb.Calculator";
try {
Context ic = new InitialContext();
Calculator calc = (Calculator) ic.lookup(JNDI_URL);
System.out.println("Response from server = " + calc.add(1,2);
...
}
catch (Exception e) {
// handle the exception
}
}
108
JB183-EAP7.0-es-2-20180124
Búsqueda de EJB remotos mediante JNDI
...
}
También necesita proporcionar un archivo denominado jndi.properties en la ruta de la
clase del programa cliente, con el nombre de host, la dirección IP, el puerto y los detalles de
seguridad (si está protegido del acceso remoto) del servidor de aplicaciones remoto donde se
está ejecutando el EJB.
java.naming.factory.initial=org.jboss.naming.remote.client.InitialContextFactory
java.naming.provider.url=http-remoting://10.2.0.15:8080
jboss.naming.client.ejb.context=true
InitialContext de la API de JNDI es una construcción genérica estándar de Java EE para
realizar búsquedas de componentes implementados en un servidor de aplicaciones. Busca
jndi.properties en la ruta de la clase con un conjunto de propiedades. Algunas de las
propiedades son comunes a todos los servidores de aplicaciones y algunas son específicas de
cada servidor de aplicaciones.
Los diferentes servidores de aplicaciones tienen sus propias formas específicas de buscar
componentes de EJB de manera óptima, pero no hablaremos sobre esto en el curso. Consulte
las referencias que aparecen al final de esta sección para obtener más detalles.
Referencias
Para obtener más información, consulte el capítulo de EJB de la Guía de desarrollo
para Red Hat JBoss EAP:
https://access.redhat.com/documentation/en/red-hat-jboss-enterpriseapplication-platform/7.0/
JB183-EAP7.0-es-2-20180124
109
Capítulo 3. Creación de Enterprise Java Beans
Ejercicio guiado: Acceso remoto a un EJB
En este ejercicio, creará una aplicación de línea de comando para acceder a un EJB remoto
implementado en JBoss EAP.
Resultados
Debe poder acceder a un EJB implementado en un servidor de aplicaciones remoto mediante
la búsqueda JNDI e invocar sus métodos de negocio.
Antes de comenzar
Abra una ventana de terminal en la máquina virtual de la estación de trabajo y ejecute el
siguiente comando para descargar los archivos necesarios para este trabajo de laboratorio.
[student@workstation ~]$ lab hello-remote setup
1.
Abra JBDS e importe los proyectos de Maven.
Este ejercicio guiado consta de dos subproyectos de Maven, hello-remote-ejb y
hello-remote-client, que están ubicados en la carpeta del proyecto principal en el
directorio /home/student/JB183/labs/hello-remote.
El proyecto hello-remote-ejb instala un EJB al que se accede de manera remota en JBoss
EAP para que esté disponible para clientes externos a través de búsquedas JNDI. El
proyecto hello-remote-client es una aplicación de Java SE que accede de manera remota
(desde otra JVM) al EJB.
1.1. Haga doble clic en el icono de JBoss Developer Studio en el escritorio de la estación
de trabajo para abrir JBDS. Seleccione el espacio de trabajo /home/student/
JB183/workspace y haga clic en OK (Aceptar).
1.2. Importe el proyecto hello-remote-ejb al seleccionarlo desde el menú principal de
JBDS File (Archivo) > Import... (Importar…).
Desde el cuadro de diálogo Import (Importar), seleccione Maven > Existing Maven
Projects (Proyectos de Maven existentes) y haga clic en Next > (Siguiente >). Haga
clic en Browse... (Explorar…), elija /home/student/JB183/labs/hello-remote/
ejb y haga clic en OK (Aceptar) para elegir el proyecto de Maven.
Haga clic en Finish (Finalizar) para completar el proceso de importación en JBDS.
Una vez completo, se enumera un nuevo proyecto nombrado hello-remote-ejb en la
vista Project Explorer (Explorador de proyectos).
1.3. Importe el proyecto hello-remote-client al seleccionarlo desde el menú principal de
JBDS File (Archivo) > Import... (Importar…).
Desde el cuadro de diálogo Import (Importar), seleccione Maven > Existing Maven
Projects (Proyectos de Maven existentes) y haga clic en Next > (Siguiente >). Haga
clic en Browse... (Explorar…), elija /home/student/JB183/labs/hello-remote/
client y haga clic en OK (Aceptar) para elegir el proyecto de Maven.
Haga clic en Finish (Finalizar) para completar el proceso de importación en JBDS.
110
JB183-EAP7.0-es-2-20180124
Una vez completo, se enumera un nuevo proyecto nombrado hello-remote-client en
la vista Project Explorer (Explorador de proyectos).
El proyecto hello-remote-client es una aplicación de Java SE que accede de
manera remota (desde otra JVM) al EJB.
2.
Explore el código fuente del proyecto hello-remote-ejb.
2.1. Revise el archivo pom.xml.
Expanda el ítem hello-remote-ejb en la pestaña Project Explorer (Explorador de
proyectos) en el panel izquierdo de JBDS y haga doble clic en el archivo pom.xml.
Haga clic en la pestaña pom.xml para ver el texto completo del archivo pom.xml.
Observe el uso de maven-ejb-plugin para empaquetar este EJB.
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-ejb-plugin</artifactId>
<version>${version.ejb.plugin}</version>
<configuration>
<ejbVersion>3.1</ejbVersion>
<generateClient>true</generateClient>
</configuration>
</plugin>
Tenga en cuenta que el empaquetado se declaró como ejb. Esto le indica a Maven
cómo empaquetar el artefacto implementable final.
<packaging>ejb</packaging>
2.2. Explore el archivo HelloRemote.java de la interfaz empresarial.
En la pestaña Project Explorer (Explorador de proyectos) en el panel izquierdo
de JBDS, seleccione src/main/java > com.redhat.training.ejb y haga doble clic en
HelloRemote.java para ver el archivo.
package com.redhat.training.ejb;
public interface HelloRemote {
public String sayHello(String name);
}
Observe que esta es una interfaz simple de Java con un método público sayHello
que toma un argumento name de cadena y devuelve una cadena. Cuando trabaja
con EJB, es común utilizar interfaces para definir los métodos que están disponibles,
independientemente de la implementación.
2.3. Explore el archivo HelloBean.java de la clase de implementación.
JB183-EAP7.0-es-2-20180124
111
Capítulo 3. Creación de Enterprise Java Beans
En la pestaña Project Explorer (Explorador de proyectos) en el panel izquierdo
de JBDS, seleccione src/main/java > com.redhat.training.ejb y haga doble clic en
HelloBean.java para ver el archivo.
@Stateless
public class HelloBean implements HelloRemote {
public String sayHello(String name) {
// respond back with "Hello, {name}".
return "Hello, " + name;
}
}
Observe que esta clase de EJB implementa el método sayHello de la interfaz
HelloRemote y observe la anotación @Stateless que marca esta clase como un
EJB sin estado.
3.
Seleccione la pestaña Servers (Servidores) en JBDS para iniciar EAP. Haga clic con el botón
derecho en la entrada del servidor Red Hat JBoss EAP 7.0 [Stopped] y haga clic en la
opción Start (Iniciar) verde para iniciar el servidor.
Observe la Console (Consola) hasta que el servidor se inicie y vea el mensaje iniciado:
INFO [org.jboss.as] (Controller Boot Thread) WFLYSRV0025: JBoss EAP 7.0.0.GA
(WildFly Core 2.1.2.Final-redhat-1) started
4.
Compile e implemente el EJB en JBoss EAP, y observe las referencia de JNDI para el EJB.
4.1. Abra un nuevo terminal y cambie a la carpeta /home/student/JB183/labs/
hello-remote/ejb/.
[student@workstation ~]$ cd /home/student/JB183/labs/hello-remote/ejb
Compile e implemente el EJB en JBoss EAP al ejecutar el siguiente comando:
[student@workstation ejb]$ mvn clean wildfly:deploy
La compilación debe ejecutarse correctamente y usted debe ver la siguiente salida:
[student@workstation ejb]$ mvn clean wildfly:deploy
[INFO] Scanning for projects...
[INFO]
[INFO] -----------------------------------------------------------------------[INFO] Building hello-remote-ejb 1.0
[INFO] -----------------------------------------------------------------------[INFO]
[INFO] <<< wildfly-maven-plugin:1.0.2.Final:deploy (default-cli) < package @
hello-remote-ejb <<<
...
[INFO] -----------------------------------------------------------------------[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
112
JB183-EAP7.0-es-2-20180124
4.2. Dentro de JBDS, observe la pestaña Console (Consola) y verifique que la
implementación sea correcta.
Observe las referencias de JNDI para su bean de sesión sin estado:
JNDI bindings for session bean named 'HelloBean' in deployment unit 'deployment
"hello-ejb-remote.jar"' are as follows:
java:global/hello-ejb-remote/HelloBean!com.redhat.training.ejb.HelloRemote
java:app/hello-ejb-remote/HelloBean!com.redhat.training.ejb.HelloRemote
java:module/HelloBean!com.redhat.training.ejb.HelloRemote
java:global/hello-ejb-remote/HelloBean
java:app/hello-ejb-remote/HelloBean
java:module/HelloBean
JBoss EAP requiere que los EJB estén enlazados en el espacio de nombre
java:jboss/exported/* para permitirles a los clientes externos buscar e invocar
el EJB.
Observe que no hay referencias de JNDI "exportadas". Necesita proporcionarle una
interfaz remota al EJB para que esté enlazado en este espacio de nombre.
5.
Edite la clase de implementación del proyecto de EJB HelloBean.java para permitir las
búsquedas de JNDI remotas y volver a implementar la aplicación.
5.1. Edite la clase de implementación HelloBean.java para permitir las búsquedas
de JNDI remotas. Agregue la anotación @Remote en su clase de implementación y
guarde el archivo.
import javax.ejb.Remote;
import javax.ejb.Stateless;
@Stateless
@Remote(HelloRemote.class)
public class HelloBean implements HelloRemote {
public String sayHello(String name) {
// respond back with "Hello, {name}".
return "Hello, " + name;
}
}
5.2. Presione Ctrl+S para guardar sus cambios.
5.3. Ejecute el siguiente comando para volver a implementar el EJB en EAP:
[student@workstation ejb]$ mvn clean wildfly:deploy
5.4. Observe las referencias de JNDI nuevamente. Esta vez debe ver la referencia de JNDI
exportada en la pestaña Console (Consola) de JBDS:
JNDI bindings for session bean named 'HelloBean' in deployment unit 'deployment
"hello-ejb-remote.jar"' are as follows:
java:global/hello-ejb-remote/HelloBean!com.redhat.training.ejb.HelloRemote
...
JB183-EAP7.0-es-2-20180124
113
Capítulo 3. Creación de Enterprise Java Beans
java:jboss/exported/hello-ejb-remote/HelloBean!
com.redhat.training.ejb.HelloRemote
...
6.
Instale el artefacto hello-remote-ejb en el repositorio local mediante Maven para que
esté disponible para el proyecto cliente durante la compilación:
[student@workstation ejb]$ cd /home/student/JB183/labs/hello-remote/ejb
[student@workstation ejb]$ mvn install
La compilación debe realizarse correctamente y debe recibir un mensaje de compilación
correcta.
[student@workstation ejb]$ mvn install
[INFO] -----------------------------------------------------------------------[INFO] Building hello-remote-ejb 1.0
[INFO] -----------------------------------------------------------------------...
[INFO] --- maven-install-plugin:2.4:install (default-install) @ hello-remote-ejb --[INFO] Installing /home/student/JB183/labs/hello-remote/ejb/target/hello-ejbremote.jar to /home/student/.m2/repository/com/redhat/training/hello-remote-ejb/1.0/
hello-remote-ejb-1.0.jar
[INFO] Installing /home/student/JB183/labs/hello-remote/ejb/pom.xml to /home/
student/.m2/repository/com/redhat/training/hello-remote-ejb/1.0/hello-remoteejb-1.0.pom
[INFO] Installing /home/student/JB183/labs/hello-remote/ejb/target/hello-ejb-remoteclient.jar to /home/student/.m2/repository/com/redhat/training/hello-remote-ejb/1.0/
hello-remote-ejb-1.0-client.jar
[INFO] -----------------------------------------------------------------------[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
7.
Explore el código fuente del proyecto hello-client y actualícelo para buscar
HelloBean mediante JNDI.
7.1. Expanda el ítem hello-client en la pestaña Project Explorer (Explorador de
proyectos) en el panel izquierdo de JBDS y haga doble clic en el archivo pom.xml.
Haga clic en la pestaña pom.xml para ver el texto completo del archivo pom.xml.
Observe la dependencia en el artefacto de maven hello-remote-ejb que instaló
anteriormente.
<dependency>
<groupId>com.redhat.training</groupId>
<artifactId>hello-remote-ejb</artifactId>
<type>ejb-client</type>
<version>1.0</version>
</dependency>
El tipo de dependencia es ejb-client. Esto le indica a Maven que este artefacto es
un cliente para los EJB definidos en el artefacto hello-remote-ejb, que se utiliza
para la compilación de códigos.
7.2. Explore el archivo HelloClient.java.
114
JB183-EAP7.0-es-2-20180124
En la pestaña Project Explorer (Explorador de proyectos) en el panel izquierdo de
JBDS, seleccione src/main/java > com.redhat.training.client y haga doble clic en
HelloClient.java para ver el archivo.
public class HelloClient {
public static void main(String[] args) throws Exception {
//TODO Update JNDI address for the HelloBean
String uriJNDI = "";
try {
Context ic = new InitialContext();
// Lookup the remote EJB
HelloRemote hello = (HelloRemote) ic.lookup(uriJNDI);
// Invoke the sayHello method
System.out.println("Response from server = " + hello.sayHello("Shadowman"));
} catch (NamingException ex) {
ex.printStackTrace();
}
}
}
7.3. Corrija el código HelloClient para que pueda invocar el EJB remoto al especificar
una cadena de búsqueda de JNDI para la creación de instancias InitialContext.
Edite el archivo HelloClient.java y modifique la variable uriJNDI para
especificar la cadena de búsqueda de JNDI que será utilizada por la creación de
instancias InitialContext. Defina el nombre de JNDI para que coincida con lo
siguiente:
String uriJNDI = "hello-ejb-remote/HelloBean!com.redhat.training.ejb.HelloRemote";
7.4. En la pestaña Project Explorer (Explorador de proyectos) en el panel izquierdo de
JBDS, seleccione src/main/resources y haga doble clic en jndi.properties para ver el
archivo en la pestaña principal.
Haga clic en la pestaña Source (Fuente) para ver y editar el archivo de propiedades.
7.5. Actualice el archivo jndi.properties para usar http-remoting para acceder al
EJB que se ejecuta en el servidor de JBoss EAP local.
Defina la propiedad java.naming.provider.url con el valor httpremoting://127.0.0.1:8080, como se muestra en el siguiente ejemplo:
java.naming.factory.initial=org.jboss.naming.remote.client.InitialContextFactory
java.naming.provider.url=http-remoting://127.0.0.1:8080
jboss.naming.client.ejb.context=true
El cliente ahora se está conectando a la instancia del servidor de JBoss EAP local que
se ejecuta en el puerto 8080.
Presione Ctrl+S para guardar sus cambios.
8.
Abra una ventana de terminal y diríjase al directorio /home/student/JB183/labs/
hello-remote/client.
JB183-EAP7.0-es-2-20180124
115
Capítulo 3. Creación de Enterprise Java Beans
[student@workstation ~]$ cd /home/student/JB183/labs/hello-remote/client/
Ejecute el cliente mediante Maven:
[student@workstation client]$ mvn package exec:java
Debe ver la siguiente salida:
[student@workstation client]$ mvn package exec:java
[INFO] Scanning for projects...
[INFO]
[INFO] -----------------------------------------------------------------------[INFO] Building hello-client 1.0
[INFO] -----------------------------------------------------------------------...
[INFO] --- exec-maven-plugin:1.2.1:java (default-cli) @ hello-client --... Channel ID b334091e (outbound) of Remoting connection 63a791c4
to /127.0.0.1:8080
INFO: JBoss EJB Client version 2.1.6.Final-redhat-1 ...
Response from server = Hello, Shadowman
...
Verá la respuesta Hello, Shadowman del EJB. Esto verifica que el EJB remoto se invocó
correctamente.
9.
Limpie el ejercicio y detenga EAP.
9.1. Anule la implementación del EJB mediante el siguiente comando Maven:
[student@workstation ejb]$ mvn wildfly:undeploy
9.2. Haga clic con el botón derecho en el proyecto hello-remote-ejb en Project
Explorer (Explorador de proyectos) y seleccione Close Project (Cerrar proyecto).
9.3. Haga clic con el botón derecho en el proyecto hello-client en Project Explorer
(Explorador de proyectos) y seleccione Close Project (Cerrar proyecto) para cerrar el
proyecto.
9.4. Haga clic con el botón derecho en el servidor Red Hat JBoss EAP 7.0 en la pestaña
Servers (Servidores) de JBDS y haga clic en Stop (Detener).
Esto concluye el ejercicio guiado.
116
JB183-EAP7.0-es-2-20180124
Descripción del ciclo de vida de un EJB
Descripción del ciclo de vida de un EJB
Objetivo
Tras finalizar esta sección, los estudiantes deberán ser capaces de describir el ciclo de vida de
los EJB
Compresión del ciclo de vida de un EJB
Los componentes de un EJB en una aplicación se ejecutan en el contexto de un contenedor
dentro de un servidor de aplicaciones. El contenedor es responsable de administrar el ciclo de
vida (creación, ejecución y destrucción) de los EJB. Cada uno de los diferentes tipos de EJB (sin
estado, con estado, singleton, MDB) tiene su propio ciclo de vida.
Descripción del ciclo de vida del bean de sesión con
estado
Un bean de sesión con estado tiene tres estados diferentes en su ciclo de vida:
• Does Not Exist (No existe): El EJB con estado no se crea y no existe en la memoria del
servidor de aplicaciones.
• Ready (Listo): El EJB con estado (objeto) se crea en la memoria del servidor de aplicaciones
por una llamada JNDI o inyección CDI y está listo para que los clientes invoquen sus
métodos de negocio.
• Passivated (Pasivado): Dado que un EJB con estado tiene un estado de objeto que perdura
durante varias llamadas del cliente, el servidor de aplicaciones puede decidir pasivar
(desactivar) el EJB en un almacenamiento secundario para optimizar el consumo de la
memoria. Activará el EJB para que tenga el estado Listo cuando un cliente invoque cualquier
método en el EJB. El desarrollador no tiene un control directo de activación y pasivación,
y el servidor de aplicaciones lo maneja de forma transparente basado en determinados
algoritmos.
JB183-EAP7.0-es-2-20180124
117
Capítulo 3. Creación de Enterprise Java Beans
Figura 3.8: Revisión del ciclo de vida de un bean de sesión con estado
Descripción del ciclo de vida del bean de sesión sin
estado
Debido a que un bean de sesión no tiene estado y nunca se pasiva, tiene solo dos estados
diferentes durante su ciclo de vida:
• Does Not Exist (No existe): El EJB sin estado no se crea y no existe en la memoria del
servidor de aplicaciones.
• Ready (Listo): El EJB sin estado (objeto) se crea en la memoria del servidor de aplicaciones
por una llamada JNDI o inyección CDI y está listo para que los clientes invoquen sus
métodos de negocio.
Por lo general, el servidor de aplicaciones crea y mantiene un conjunto (pool) de instancias
EJB de sesión sin estado en la memoria para optimizar el rendimiento. Cada vez que un
cliente invoca métodos de negocio en el EJB, el servidor de aplicaciones asigna un bean del
conjunto (pool). Una vez que se ejecuta el método, se devuelve el bean al conjunto (pool).
Este proceso es transparente para el desarrollador, y el servidor de aplicaciones lo maneja
automáticamente. El tamaño del conjunto (pool) se puede configurar mediante descriptores
de implementación y en los archivos de configuración del servidor de aplicaciones.
118
JB183-EAP7.0-es-2-20180124
Descripción del ciclo de vida del bean de sesión singleton
Figura 3.9: Revisión del ciclo de vida de un bean de sesión sin estado
Descripción del ciclo de vida del bean de sesión
singleton
De manera similar a un bean de sesión sin estado, un bean de sesión singleton tiene dos
estados diferentes en su ciclo de vida:
• Does Not Exist (No existe): El singleton no se crea y no existe en la memoria del servidor
de aplicaciones.
• Ready (Listo): El EJB singleton (un único objeto) se crea en la memoria del servidor de
aplicaciones en el inicio, o a través de una inyección de CDI, y está listo para que los clientes
invoquen sus métodos de negocio.
Debido a que solo hay una instancia del EJB durante su ciclo de vida, no existe el concepto de
un conjunto (pool). Las políticas de acceso concurrentes en el bean pueden ser controladas
por descriptores de implementación o anotaciones de nivel del código.
Revisión de las anotaciones del ciclo de vida de un
bean
Los estándares Java EE para EJB especifican el concepto de Devoluciones de llamadas
(Callbacks). Una devolución de llamada (callback) es un mecanismo mediante el cual los
eventos del ciclo de vida de un bean empresarial se pueden interceptar y el desarrollador
puede ejecutar códigos personalizados durante estos eventos. El desarrollador anota
métodos en el bean con un conjunto de anotaciones que, luego, el servidor de aplicaciones
puede invocar y ejecutar.
En la siguiente tabla se indican los diferentes tipos de EJB y los métodos del ciclo de vida y las
anotaciones disponibles para cada uno:
JB183-EAP7.0-es-2-20180124
119
Capítulo 3. Creación de Enterprise Java Beans
Tipo de bean
Anotación
Descripción
Bean de sesión
con estado
@PostConstruct
se invoca cuando se crea un bean por primera vez.
@PreDestroy
se invoca cuando se destruye un bean.
@PostActivate
se invoca cuando se carga un bean para utilizarlo
después de la activación.
@PrePassivate
se invoca cuando un bean está a punto de pasivarse.
Bean de sesión
sin estado
@PostConstruct
se invoca cuando se crea un bean por primera vez.
@PreDestroy
se invoca cuando se destruye o elimina un bean del
conjunto (pool) de beans.
Bean de sesión
singleton
@PostConstruct
se invoca cuando se crea un bean por primera vez.
@PreDestroy
se invoca cuando se destruye un bean.
@Startup
El servidor de aplicaciones crea instancias del
singleton en el inicio.
120
JB183-EAP7.0-es-2-20180124
Cuestionario: El ciclo de vida de un EJB
Cuestionario: El ciclo de vida de un EJB
Elija las respuestas correctas para las siguientes preguntas:
1.
¿Cuáles dos de los siguientes enunciados acerca del ciclo de vida de un EJB de
sesión con estado son correctos? (Elija dos opciones).
a.
b.
c.
d.
e.
2.
¿Cuáles dos de los siguientes enunciados acerca del ciclo de vida de un EJB de
sesión sin estado son correctos? (Elija dos opciones).
a.
b.
c.
d.
e.
3.
El desarrollador tiene control directo sobre la pasivación o activación de EJB sin
estado.
El conjunto (pool) de beans de EJB sin estado tiene tamaño fijo y no puede
cambiarse.
Por lo general, el servidor de aplicaciones mantiene un conjunto (pool) de EJB sin
estado.
El tamaño del conjunto (pool) de beans EJB sin estado se puede cambiar mediante
descriptores de implementación y archivos de configuración del servidor de
aplicaciones.
Los EJB sin estado que se encuentran en el estado Listo pueden ser invocados solo
por clientes remotos a través de JNDI.
¿Cuáles de los siguientes enunciados acerca del ciclo de vida de un EJB singleton
son correctos?
a.
b.
c.
d.
4.
Los clientes pueden invocar métodos en un bean de sesión con estado que se
encuentra en el estado Pasivado.
Los clientes pueden invocar métodos en un bean de sesión con estado que se
encuentra en el estado Listo.
El desarrollador tiene control directo sobre la pasivación y activación del EJB con
estado a través de la invocación de métodos directamente en el bean.
El desarrollador no tiene control directo sobre la pasivación o activación de los EJB
con estado. El servidor de aplicaciones administra la activación y pasivación de los
EJB con estado.
Los EJB con estado que se encuentran en el estado Listo pueden ser invocados
solo por clientes remotos a través de JNDI.
El servidor de aplicaciones mantiene un conjunto (pool) de EJB singleton de los
cuales se crean instancias en el inicio.
Los EJB singleton se pueden pasivar y activar como los EJB con estado.
El servidor de aplicaciones crea y mantiene una única instancia del EJB singleton
en el inicio si el EJB singleton tiene la anotación @Startup.
El tamaño de conjunto (pool) de los EJB singleton se puede controlar mediante
descriptores de implementación y archivos de configuración del servidor de
aplicaciones.
¿Cuáles tres de los siguientes enunciados acerca de las anotaciones del ciclo de
vida de un bean son correctos? (Elija tres opciones).
JB183-EAP7.0-es-2-20180124
121
Capítulo 3. Creación de Enterprise Java Beans
a.
b.
c.
d.
e.
f.
122
Los métodos de un EJB de sesión con estado se pueden anotar con
@PrePassivate.
Los métodos de un EJB de sesión sin estado se pueden anotar con
@PrePassivate.
Un EJB singleton se puede anotar con @PostConstruct.
La anotación @Initialize en un EJB singleton indica que el servidor de aplicaciones
creará instancias del singleton en el inicio.
Los métodos de un EJB de sesión con estado se pueden anotar con @PreDestroy.
Los métodos de un EJB de sesión sin estado se pueden anotar con
@PostActivate.
JB183-EAP7.0-es-2-20180124
Solución
Solución
Elija las respuestas correctas para las siguientes preguntas:
1.
¿Cuáles dos de los siguientes enunciados acerca del ciclo de vida de un EJB de
sesión con estado son correctos? (Elija dos opciones).
a.
b.
c.
d.
e.
2.
¿Cuáles dos de los siguientes enunciados acerca del ciclo de vida de un EJB de
sesión sin estado son correctos? (Elija dos opciones).
a.
b.
c.
d.
e.
3.
El desarrollador tiene control directo sobre la pasivación o activación de EJB sin
estado.
El conjunto (pool) de beans de EJB sin estado tiene tamaño fijo y no puede
cambiarse.
Por lo general, el servidor de aplicaciones mantiene un conjunto (pool) de EJB
sin estado.
El tamaño del conjunto (pool) de beans EJB sin estado se puede cambiar
mediante descriptores de implementación y archivos de configuración del
servidor de aplicaciones.
Los EJB sin estado que se encuentran en el estado Listo pueden ser invocados solo
por clientes remotos a través de JNDI.
¿Cuáles de los siguientes enunciados acerca del ciclo de vida de un EJB singleton
son correctos?
a.
b.
c.
d.
4.
Los clientes pueden invocar métodos en un bean de sesión con estado que se
encuentra en el estado Pasivado.
Los clientes pueden invocar métodos en un bean de sesión con estado que se
encuentra en el estado Listo.
El desarrollador tiene control directo sobre la pasivación y activación del EJB con
estado a través de la invocación de métodos directamente en el bean.
El desarrollador no tiene control directo sobre la pasivación o activación de los
EJB con estado. El servidor de aplicaciones administra la activación y pasivación
de los EJB con estado.
Los EJB con estado que se encuentran en el estado Listo pueden ser invocados
solo por clientes remotos a través de JNDI.
El servidor de aplicaciones mantiene un conjunto (pool) de EJB singleton de los
cuales se crean instancias en el inicio.
Los EJB singleton se pueden pasivar y activar como los EJB con estado.
El servidor de aplicaciones crea y mantiene una única instancia del EJB singleton
en el inicio si el EJB singleton tiene la anotación @Startup.
El tamaño de conjunto (pool) de los EJB singleton se puede controlar mediante
descriptores de implementación y archivos de configuración del servidor de
aplicaciones.
¿Cuáles tres de los siguientes enunciados acerca de las anotaciones del ciclo de
vida de un bean son correctos? (Elija tres opciones).
a.
Los métodos de un EJB de sesión con estado se pueden anotar con
@PrePassivate.
JB183-EAP7.0-es-2-20180124
123
Capítulo 3. Creación de Enterprise Java Beans
b.
c.
d.
e.
f.
124
Los métodos de un EJB de sesión sin estado se pueden anotar con
@PrePassivate.
Un EJB singleton se puede anotar con @PostConstruct.
La anotación @Initialize en un EJB singleton indica que el servidor de aplicaciones
creará instancias del singleton en el inicio.
Los métodos de un EJB de sesión con estado se pueden anotar con
@PreDestroy.
Los métodos de un EJB de sesión sin estado se pueden anotar con
@PostActivate.
JB183-EAP7.0-es-2-20180124
Delimitación de transacciones implícitas y explícitas
Delimitación de transacciones implícitas y
explícitas
Objetivos
Tras finalizar esta sección, los estudiantes deberán ser capaces de describir las transacciones
implícitas y explícitas.
Comprensión de las transacciones en aplicaciones
de Java EE
Una aplicación empresarial típica de Java EE almacena y manipula datos en uno o más
almacenes de datos persistentes, como un sistema de gestión de bases de datos relacionales
(RDBMS). Por lo general, varias aplicaciones a la vez acceden a los datos críticos para la
empresa almacenados en estas bases de datos y es fundamental garantizar la integridad de
datos. Las Transacciones garantizan que se mantenga la integridad de datos al controlar el
acceso concurrente a los datos y garantizan que una transacción de negocio fallida no deje el
sistema en un estado inconsistente o inválido.
Una Transacción es una serie de acciones que deben ejecutarse como una unidad atómica
simple. Sigue un enfoque "a todo o nada", donde las acciones se ejecutan correctamente o no
se ejecutan de ningún modo.
Figura 3.10: Uso de una transacción para revertir una falla
La figura anterior muestra los pasos para una aplicación de gestión de pedidos, donde
se debe ejecutar una secuencia de operaciones cuando un cliente realiza un pedido. La
transacción comienza con la ejecución del método saveOrder(), que almacena el pedido
en una base de datos de Pedidos y, luego invoca el método raisePurchaseOrder(), que
establece un pedido de compras en otra base de datos mantenida por el departamento
JB183-EAP7.0-es-2-20180124
125
Capítulo 3. Creación de Enterprise Java Beans
de finanzas. El flujo se traslada al método updateInventory(), que actualiza la base de
datos del inventario y, luego, le envía un correo electrónico al cliente mediante el método
sendEmail().
Si todos los métodos de la transacción se ejecutan sin errores ni fallas, la transacción
tiene un estado Committed (Confirmado). Si, por ejemplo, se produce un error en el
método updateInventory(), la aplicación debe asegurarse de que las acciones de los
métodos anteriores que participaron en la transacción (es decir, raisePurchaseOrder() y
saveOrder()) se reviertan y de que el estado general del sistema vuelva al estado que tenía
al comienzo de la transacción. Esta reversión se conoce como Rollback.
El estándar de Java EE especifica la API de transacción Java (JTA) que proporciona gestión
de transacciones para las aplicaciones que se ejecutan en un servidor de aplicaciones
compatible con el servidor de aplicaciones Java EE. Esta API proporciona una interfaz
avanzada para confirmar y restaurar transacciones en las aplicaciones. Por ejemplo, si la
API de persistencia Java (JPA) se utiliza junto con JTA, el desarrollador no tiene que escribir y
rastrear explícitamente las instrucciones de confirmación y revertir de SQL en la aplicación. La
API de JTA se ocupa de estas operaciones de manera independiente de la base de datos.
Hay dos maneras diferentes de administrar transacciones en Java EE:
• Transacción implícita o administrada por contenedores (CMT): El servidor de
aplicaciones administra el límite de la transacción y confirma y restaura automáticamente
las transacciones sin que el desarrollador escriba código para administrar transacciones.
Esta es la opción predeterminada a menos que el desarrollador la sobrescriba
explícitamente.
• Transacción explícita o administrada por beans (BMT): El desarrollador administra
las transacciones en forma de código en el nivel del bean (en los EJB). El desarrollador es
responsable de controlar el límite y alcance de la transacción de manera explícita.
Revisión de la semántica de transacciones
administradas por contenedores (CMT)
En CMT, el servidor de aplicaciones inicia una transacción implícitamente al comienzo de
un método EJB y confirma la transacción al final del método, a menos que se produzca un
error o una excepción. En ese caso, se restaura la transacción. Las excepciones de tiempo
de ejecución desencadenan automáticamente una reversión realizada por el servidor
de aplicaciones. La agrupación de transacciones dentro de un método de bean único no
está permitida. Un desarrollador puede sobrescribir el comportamiento transaccional
predeterminado en el nivel del método con anotaciones denominadas Atributos de
transacción.
Los EJB que utilizan CMT no deben usar métodos API de JTA que entren en conflicto
con el límite y alcance de la transacción del servidor de aplicaciones. Por ejemplo, no se
pueden utilizar los métodos JTA, como commit(), setAutoCommit() y rollback(), o
las clases JDBC como java.sql.Connection y los métodos commit() y rollback() de
javax.jms.Session. Si requiere control explícito sobre el flujo de la transacción, debe usar
transacciones administradas por beans. Además, los EJB que usan CMT no deben usar la
interfaz javax.transaction.UserTransaction.
126
JB183-EAP7.0-es-2-20180124
Revisión de la semántica de transacciones administradas por contenedores (CMT)
Ajuste de los atributos de transacción
En CMT, los atributos de transacción controlan el alcance de una transacción y permiten a un
desarrollador administrar las transacciones en el nivel del método individual en un EJB.
Por ejemplo, observe el siguiente fragmento de código donde un EJB sin estado invoca un
método de otro EJB sin estado:
@Stateless
public class TodoService {
@Inject
UserService user;
public void login(String user, String password) {
user.authenticate(user,password);
}
...
}
@Stateless
public class UserService {
public boolean authenticate(String user, String password) {
...
}
...
}
nota
@Inject puede inyectar cualquier bean, incluidos EJB, mientras que @EJB solo
puede inyectar EJB. @Inject se describe con mayor detalle en el capítulo titulado
Implementación de Contextos e Inyección de dependencia.
Los atributos de transacción se pueden usar para controlar el alcance y contexto con los que
se ejecutan los métodos de la clase UserService.
La especificación de Java EE define seis atributos de transacción. Se ilustran debajo con
referencia al fragmento de código que aparece arriba:
@TransactionAttribute(TransactionAttributeType.REQUIRED)
Si el método login() se está ejecutando dentro de una transacción e invoca el método
authenticate() en la clase UserService, authenticate() se ejecuta dentro de la misma
transacción. Si no hay una transacción cuando se invoca authenticate(), el servidor de
aplicaciones inicia una nueva transacción antes de ejecutar authenticate(). Este es el
atributo de transacción predeterminado a menos que se sobrescriba explícitamente con
otras anotaciones de atributo de transacción.
@TransactionAttribute(TransactionAttributeType.REQUIRES_NEW)
Si el método login() se está ejecutando dentro de una transacción e invoca el método
authenticate() en la clase UserService, el servidor de aplicaciones suspende la
transacción e inicia una nueva transacción antes de ejecutar authenticate(). Una vez
que authenticate() finaliza la ejecución, el control vuelve a login() y la transacción
suspendida se reanuda. Si no hay una transacción cuando se invoca authenticate(), el
JB183-EAP7.0-es-2-20180124
127
Capítulo 3. Creación de Enterprise Java Beans
servidor de aplicaciones inicia una nueva transacción antes de ejecutar authenticate().
Este atributo garantiza que su método se ejecute siempre con una nueva transacción.
@TransactionAttribute(TransactionAttributeType.MANDATORY)
Si el método login() se está ejecutando dentro de una transacción e invoca el método
authenticate() en la clase UserService, authenticate() se ejecuta dentro de la misma
transacción. Si no hay una transacción cuando se invoca authenticate(), el servidor de
aplicaciones arroja una TransactionRequiredException. Use este atributo si desea que
un método se ejecute siempre en el contexto de la transacción del cliente que realiza la
invocación.
@TransactionAttribute(TransactionAttributeType.NOT_SUPPORTED)
Si el método login() se está ejecutando dentro de una transacción e invoca el método
authenticate() en la clase UserService, el servidor de aplicaciones suspende la
transacción y ejecuta authenticate() sin un contexto de transacción. Una vez que
authenticate() finaliza la ejecución, el control vuelve a login() y la transacción
suspendida se reanuda. Si no hay una transacción cuando se invoca authenticate(), el
servidor de aplicaciones no inicia una nueva transacción antes de ejecutar authenticate().
Use este atributo para los métodos que no necesitan transacciones.
@TransactionAttribute(TransactionAttributeType.SUPPORTS)
Si el método login() se está ejecutando dentro de una transacción e invoca el método
authenticate(), authenticate() se ejecuta dentro de la misma transacción. Si no hay
una transacción cuando se invoca authenticate(), el servidor de aplicaciones NO inicia
una nueva transacción antes de ejecutar authenticate().
@TransactionAttribute(TransactionAttributeType.NEVER)
Si el método login() se está ejecutando dentro de una transacción e invoca el método
authenticate() en la clase UserService, el servidor de aplicaciones arroja una
RemoteException. Si no hay una transacción cuando se invoca authenticate(), el
servidor de aplicaciones no inicia una nueva transacción antes de ejecutar authenticate().
Ajuste de los atributos de transacción
Los atributos de transacción se declaran al anotar el método o la clase de EJB con
una anotación javax.ejb.TransactionAttribute y al establecerlo en una de las
constantes de enumeración javax.ejb.TransactionAttributeType. Si anota al EJB con
@TransactionAttribute en el nivel de la clase, el atributo especificado se aplica a todos
los métodos del EJB. Anotar métodos específicos con @TransactionAttribute aplica el
atributo solo a ese método. Las declaraciones de atributo del nivel del método sobrescriben
las declaraciones del nivel de la clase.
Observe la siguiente clase de EJB con varios métodos:
@Stateless
public class TodoEJB {
@TransactionAttribute(TransactionAttributeType.REQUIRED)
public void createTodo(TodoItem item) {
...
}
@TransactionAttribute(TransactionAttributeType.NEVER)
public List<TodoItem> listTodos() {
...
128
JB183-EAP7.0-es-2-20180124
Comprensión de la semántica de las transacciones administradas por beans (BMT)
}
@TransactionAttribute(TransactionAttributeType.REQUIRES_NEW)
public void logCreateTodo(TodoItem item) {
...
}
El método createTodo() se anota con el atributo de transacción
@TransactionAttribute(TransactionAttributeType.REQUIRED),
y el método listTodos() se anota con el atributo de transacción
@TransactionAttribute(TransactionAttributeType.NEVER), debido a que este
método realiza únicamente una operación de solo lectura de la base de datos y no inserta,
elimina ni actualiza datos.
El método logCreateTodo() se anota con el atributo de transacción
@TransactionAttribute(TransactionAttributeType.REQUIRES_NEW), que garantiza
que este método se ejecute siempre con una nueva transacción, incluso si se invoca desde
otro método que se ejecuta con su propio contexto de transacción.
Comprensión de la semántica de las transacciones
administradas por beans (BMT)
Puede utilizar las transacciones administradas por beans (BMT) en escenarios donde necesita
control detallado sobre cuándo inicia y finaliza una transacción y cuándo realizar una
confirmación y reversión. Puede usar los métodos begin(), commit() y rollback() de la
interfaz javax.transaction.UserTransaction para controlar los límites y el alcance de la
transacción de manera explícita.
En aplicaciones que utilizan BMT, no debe usar los métodos getRollbackOnly() y
setRollbackOnly() de la interfaz javax.ejb.EJBContext. En cambio, use los métodos
getStatus() y rollback() de la interfaz javax.transaction.UserTransaction.
Un EJB de muestra con una transacción administrada por beans tiene el siguiente aspecto:
@Stateless
@TransactionManagement(TransactionManagementType.BEAN)
public class BeanManagedEJB {
@Inject
private UserTransaction tx;
public Integer saveOrder(Order order) {
try {
tx.begin();
Integer orderId = createOrder(order);
raisePurchaseOrder(orderId);
sendEMail("Your order # " + orderId + "has been placed successfully..." );
tx.commit();
return orderId;
} catch (Exception e) {
JB183-EAP7.0-es-2-20180124
129
Capítulo 3. Creación de Enterprise Java Beans
tx.rollback();
return null;
}
...
Esta clase es un EJB sin estado.
Marque este EJB como una transacción administrada por beans; para ello, use la
anotación @TransactionManagement(TransactionManagementType.BEAN).
Inyecte un objeto UserTransaction. Esto se utiliza para iniciar, confirmar y restaurar
transacciones en este EJB.
Inicie una transacción.
Si todos los métodos se ejecutan correctamente sin errores, confirme la transacción.
Si se produce una excepción debido a una falla, revierta la transacción.
130
JB183-EAP7.0-es-2-20180124
Ejercicio guiado: Delimitación de transacciones
Ejercicio guiado: Delimitación de
transacciones
En este ejercicio, tomará un EJB sin estado y lo actualizará para utilizar transacciones
administradas por beans.
Resultados
Deberá poder implementar un EJB sin estado que utilice transacciones administradas por
beans.
Antes de comenzar
Abra una ventana de terminal en la máquina virtual workstation y ejecute el siguiente
comando para descargar los archivos necesarios para este trabajo de laboratorio.
[student@workstation ~]$ lab bean-transactions setup
Pasos
1. Abra JBDS e importe el proyecto de Maven.
1.1. Haga doble clic en el icono de JBoss Developer Studio en el escritorio de la estación
de trabajo para abrir JBDS. Seleccione el espacio de trabajo /home/student/
JB183/workspace y haga clic en OK (Aceptar).
1.2. En el menú de JBDS, haga clic en File (Archivo) > Import (Importar)para abrir el
asistente para Import (Importar).
1.3. En la página Select (Seleccionar), haga clic en Maven > Existing Maven Projects
(Proyectos de Maven existentes) y, luego, haga clic en Next (Siguiente).
1.4. En la página Maven projects (Proyectos de Maven), haga clic en Browse (Explorar)
para abrir la ventana Select root folder (Seleccionar carpeta raíz). Diríjase
al directorio /home/student/JB183/labs/. Seleccione la carpeta beantransactions y haga clic en OK (Aceptar).
1.5. En la página Maven projects (Proyectos de Maven), haga clic en Finish (Finalizar).
1.6. Observe la barra de estado de JBDS para monitorear el progreso de la operación
de importación. Es probable que la descarga de todas las dependencias requeridas
demore unos minutos.
2.
Explore el código fuente de la aplicación.
2.1. Revise la página JSF que invoca el EJB; para ello, expanda el ítem bean-transactions
en la pestaña Project Explorer (Explorador de proyectos) en el panel izquierdo de
JBDS. Expanda las carpetas bean-transactions > src > main > webapp y haga doble
clic en el archivo index.xhtml.
El valor de Lenguaje de expresión (Expression Language, EL) #{hello.sayHello()}
se invoca cuando se envía el formulario web.
2.2. Revise el bean de respaldo Hello con alcance de solicitud utilizado por la página JSF.
JB183-EAP7.0-es-2-20180124
131
Capítulo 3. Creación de Enterprise Java Beans
Expanda el ítem bean-transactions en la pestaña Project Explorer (Explorador
de proyectos) del panel izquierdo de JBDS, seleccione bean-transactions > Java
Resources (Recursos de Java) > src/main/java > com.redhat.training.ui y expándalo.
Haga doble clic en el archivo Hello.java.
Tenga en cuenta que este EJB utiliza CDI para inyectar el EJB PersonService, que es
donde se agregará la lógica de la transacción.
2.3. Explore la clase de EJB PersonService.java.
En el ítem bean-transactions expandido en la pestaña Project Explorer (Explorador
de proyectos) del panel izquierdo de JBDS, seleccione bean-transactions > Java
Resources (Recursos de Java) > src/main/java > com.redhat.training.service y
expándalo. Haga doble clic en el archivo PersonService.java.
@Stateless
// TODO Add a transaction manage type of 'BEAN'
public class PersonService {
@PersistenceContext
private EntityManager entityManager;
// TODO Inject a UserTransaction to be used by this EJB
public String hello(String name) {
try {
// TODO start a new transaction
// let's grab the current date and time on the server
LocalDateTime today = LocalDateTime.now();
// format it nicely for on-screen display
DateTimeFormatter format = DateTimeFormatter.ofPattern("MMM dd yyyy hh:mm:ss
a");
String fdate = today.format(format);
// Create a new Person object and persist to database
Person p = new Person();
p.setName(name);
entityManager.persist(p);
// respond back with Hello and convert the name to UPPERCASE. Also, send the
// current time on the server.
return "Hello " + name.toUpperCase() + "!. " + "Time on the server is: " +
fdate;
} catch(Exception e) {
//TODO roll-back the transaction
throw new EJBException(e);
}
}
}
Tenga en cuenta que este EJB ya está marcado como @Stateless, pero actualmente
no contiene administración de transacciones. El método hello() crea una nueva
entrada en la base de datos para cada persona que ingresa un nombre en la IU y
devuelve un saludo que contiene la hora y fecha actuales.
132
JB183-EAP7.0-es-2-20180124
3.
Actualice PersonService para utilizar transacciones administradas por beans.
3.1. Anote la clase PersonService con la anotación
@TransactionManagement(TransactionManagementType.BEAN) para habilitar
las transacciones administradas por beans en el EJB:
@Stateless
@TransactionManagement(TransactionManagementType.BEAN)
public class PersonService {
...
Esta anotación previene que el contenedor administre transacciones y le permite al
EJB administrar las transacciones manualmente.
3.2. Agregue el siguiente código para usar la inyección de recursos para inyectar una
instancia de la clase UserTransaction en el EJB para la administración manual de
transacciones:
public class PersonService {
@PersistenceContext
private EntityManager entityManager;
//TODO Inject a UserTransaction to be used by this EJB
@Resource
UserTransaction tx;
...
La anotación @Resource le indica al contenedor que asigne un nuevo objeto de
transacción y lo inyecte en este EJB en el tiempo de ejecución.
3.3. Agregue el siguiente código al EJB para comenzar la transacción:
...
public String hello(String name) {
try {
//TODO start a new transaction
tx.begin();
...
Agregue el siguiente código para confirmar la transacción:
...
public String hello(String name) {
try {
// TODO start a new transaction
tx.begin();
// let's grab the current date and time on the server
LocalDateTime today = LocalDateTime.now();
// format it nicely for on-screen display
DateTimeFormatter format = DateTimeFormatter.ofPattern("MMM dd yyyy hh:mm:ss
a");
String fdate = today.format(format);
// Create a new Person object and persist to database
Person p = new Person();
JB183-EAP7.0-es-2-20180124
133
Capítulo 3. Creación de Enterprise Java Beans
p.setName(name);
entityManager.persist(p);
//TODO commit the transaction
tx.commit();
...
Agregue el siguiente código para revertir la transacción en el evento de una
excepción:
...
public String hello(String name) {
try {
// TODO start a new transaction
tx.begin()
// let's grab the current date and time on the server
LocalDateTime today = LocalDateTime.now();
// format it nicely for on-screen display
DateTimeFormatter format = DateTimeFormatter.ofPattern("MMM dd yyyy hh:mm:ss
a");
String fdate = today.format(format);
// Create a new Person object and persist to database
Person p = new Person();
p.setName(name);
entityManager.persist(p);
//TODO commit the transaction
tx.commit();
// respond back with Hello and convert the name to UPPERCASE. Also, send the
// current time on the server.
return "Hello " + name.toUpperCase() + "!. " + "Time on the server is: " +
fdate;
} catch(Exception e) {
//TODO roll-back the transaction
tx.rollback();
throw new EJBException(e);
}
...
Guarde los cambios en el archivo con Ctrl+S.
3.4. Después de agregar la reversión de la transacción, observe que JBDS le informa
sobre una excepción no controlada. Pase el mouse sobre la línea tx.rollback()
subrayada en rojo y haga clic en Add throws declaration (Agregar declaración de
generación). El encabezado del método ahora tiene el siguiente aspecto:
public String hello(String name) throws IllegalStateException,
SecurityException, SystemException {
Guarde los cambios en el archivo con Ctrl+S.
4.
Inicie EAP e implemente la aplicación con Maven.
4.1. Haga clic en la pestaña Servers (Servidores) en el panel inferior de JBDS. Haga clic
con el botón derecho en el servidor Red Hat JBoss EAP 7.0 [Stopped] y haga clic en el
134
JB183-EAP7.0-es-2-20180124
botón de inicio verde para iniciar el servidor. Observe la Console (Consola) hasta ver
el siguiente mensaje:
INFO [org.jboss.as] (Controller Boot Thread) WFLYSRV0025: JBoss EAP 7.0.0.GA
(WildFly Core 2.1.2.Final-redhat-1) started
4.2. Implemente la aplicación en JBoss EAP con Maven con los siguientes comandos:
[student@workstation ~]$ cd /home/student/JB183/labs/bean-transactions
[student@workstation bean-transactions]$ mvn wildfly:deploy
Una vez completado, debe ver BUILD SUCCESS:
...
[INFO]
[INFO]
[INFO]
[INFO]
[INFO]
[INFO]
[INFO]
-----------------------------------------------------------------------BUILD SUCCESS
-----------------------------------------------------------------------Total time: 17.116 s
Finished at: 2017-09-21T07:26:55-05:00
Final Memory: 35M/210M
------------------------------------------------------------------------
4.3. Valide la implementación en el registro de servidores que se muestra en la pestaña
Console (Consola) en JBDS:
INFO [org.jboss.as.server] (management-handler-thread - 9) WFLYSRV0010:
Deployed "bean-transactions.war" (runtime-name : "bean-transactions.war")
5.
Pruebe la aplicación.
5.1. En la máquina virtual workstation, utilice un explorador para ir a la siguiente URL:
http://localhost:8080/bean-transactions.
5.2. Ingrese Shadowman en el cuadro de texto denominado Enter your name: (Ingrese su
nombre:) y haga clic en Submit (Enviar).
La página se actualiza con el mensaje Hello SHADOWMAN!. Time on the server is: Sep 21
2017 04:15:04 PM
6.
Anule la implementación de la aplicación y detenga EAP.
6.1. Ejecute los siguientes comandos para anular la implementación de la aplicación:
[student@workstation ~]$ cd /home/student/JB183/labs/bean-transactions
[student@workstation bean-transactions]$ mvn wildfly:undeploy
6.2. Haga clic con el botón derecho en el proyecto bean-transactions en Project
Explorer (Explorador de proyectos) y seleccione Close Project (Cerrar proyecto) para
cerrar este proyecto.
6.3. Haga clic con el botón derecho en el servidor Red Hat JBoss EAP 7.0 en la pestaña
Servers (Servidores) de JBDS y haga clic en Stop (Detener).
JB183-EAP7.0-es-2-20180124
135
Capítulo 3. Creación de Enterprise Java Beans
Esto concluye el ejercicio guiado.
136
JB183-EAP7.0-es-2-20180124
Trabajo de laboratorio: Creación de Enterprise Java Beans
Trabajo de laboratorio: Creación de
Enterprise Java Beans
En este trabajo de laboratorio, convertirá una clase de POJO en un EJB en la aplicación To Do
List y mostrará una lista de los ítems Todo en una página web.
Resultados
Deberá poder modificar una aplicación To Do List escrita con anterioridad y convertir una
clase POJO en un EJB. La aplicación acepta una lista de cosas por hacer como entrada del
usuario, y muestra la lista de ítems Todo en una página web.
Antes de comenzar
Abra una ventana de terminal en la máquina virtual de la estación de trabajo y ejecute el
siguiente comando para descargar los archivos necesarios para este trabajo de laboratorio.
[student@workstation ~]$ lab ejb-assessment setup
1.
Abra JBDS e importe el proyecto ejb-assessment ubicado en el directorio /home/
student/JB183/labs/ejb-assessment.
2.
Revise la clase Item.java de la carpeta /home/student/JB183/labs/ejbassessment/src/main/java/com/redhat/training/todo/model.
Esta clase modela un único ítem Todo en la aplicación. Tiene tres atributos: un id, una
descripción y un atributo booleano que indica si la tarea se completó o no.
3.
Revise la clase ItemRepository.java de la carpeta /home/student/JB183/labs/
ejb-assessment/src/main/java/com/redhat/training/todo/data.
Esta clase simula una base de datos en la memoria y almacena la lista de ítems Todo.
Tiene métodos para agregar ítems, ver un único ítem y ver una lista de todos los ítems.
Tenga en cuenta que esta clase está anotada con @ApplicationScoped, que significa
que los objetos de esta clase se mantienen en el alcance (activos) siempre y cuando la
aplicación esté implementada y en ejecución en el servidor de aplicaciones.
Tenga en cuenta que el método seedTodoList() se anotó con @PostConstruct. Este
método completa la lista Todo con tres ítems una vez que se inicializa la clase.
4.
Explore la clase ItemService.java, que es un POJO simple que contiene métodos
para agregar ítems, ver un ítem Todo y enumerar todos los ítems Todo. Este archivo está
ubicado en la carpeta /home/student/JB183/labs/ejb-assessment/src/main/
java/com/redhat/training/todo/service.
Tenga en cuenta que esta clase inyecta la clase ItemRepository e invoca métodos para
agregar, ver y enumerar los ítems Todo.
5.
Convierta el POJO ItemService en un EJB sin estado.
6.
Explore la clase ItemResourceRESTService, que proporciona puntos finales REST
para la interfaz de usuario front-end (basada en AngularJS). Este archivo está ubicado en
JB183-EAP7.0-es-2-20180124
137
Capítulo 3. Creación de Enterprise Java Beans
la carpeta /home/student/JB183/labs/ejb-assessment/src/main/java/com/
redhat/training/todo/rest.
Tenga en cuenta que esta clase necesita usar el EJB ItemService para invocar los
métodos de EJB, y brinda una respuesta JSON para la capa de front-end.
7.
Inyecte el EJB ItemService en la clase ItemResourceRESTService.
Agregue la siguiente importación en la clase ItemResourceRESTService para la
anotación:
import javax.ejb.EJB
8.
Inicie JBoss EAP desde dentro de JBDS.
9.
Compile e implemente la aplicación en JBoss EAP con Maven.
10. Observe las referencias JNDI del EJB ItemService en los registros del servidor de JBoss
EAP:
11. Explore la aplicación.
11.1.Diríjase a http://localhost:8080/ejb-assessment en un explorador para
acceder a la aplicación.
11.2.En el lado izquierdo de la página web, observe que se agregaron tres ítems Todo en
el método @PostConstruct de la clase ItemRepository.
Observe la pestaña Console (Consola) de JBDS y verifique que la información
de registro del método register() en el EJB ItemService y del método
seedTodoList() en la clase ItemRepository esté visible.
11.3.En el lado derecho de la página web, agregue algunos ítems Todo; para ello,
introduzca texto en el campo Description (Descripción) y haga clic en Save (Guardar).
Verifique que la tarea que agregó aparezca en la tabla Task List (Lista de tareas) en la
izquierda.
Observe la pestaña Console (Consola) de JBDS y verifique que la información de
registro del método register() en el EJB ItemService esté visible.
11.4.Agregue algunas tareas más y observe que la aplicación muestra automáticamente
una barra de paginación en la parte inferior de la lista Task List (Lista de tareas) si
hay más de cinco tareas.
12. Abra una nueva ventana de terminal y ejecute el siguiente comando para calificar el
trabajo de laboratorio:
[student@workstation ~]$ lab ejb-assessment grade
El script de calificación debe indicar SUCCESS (CORRECTO). Si se produce una falla, revise
los errores y corríjalos hasta que vea un mensaje que indique SUCCESS (CORRECTO).
13. Realice la limpieza.
138
JB183-EAP7.0-es-2-20180124
13.1.Anule la implementación de la aplicación en JBoss EAP con Maven con los siguientes
comandos:
[student@workstation ~]$ cd /home/student/JB183/labs/ejb-assessment
[student@workstation ejb-assessment]$ mvn wildfly:undeploy
13.2.Haga clic con el botón derecho en el proyecto ejb-assessment en Project Explorer
(Explorador de proyectos) y seleccione Close Project (Cerrar proyecto).
13.3.Haga clic con el botón derecho en el servidor Red Hat JBoss EAP 7.0 en la pestaña
Servers (Servidores) de JBDS y haga clic en Stop (Detener).
Esto concluye el trabajo de laboratorio.
JB183-EAP7.0-es-2-20180124
139
Capítulo 3. Creación de Enterprise Java Beans
Solución
En este trabajo de laboratorio, convertirá una clase de POJO en un EJB en la aplicación To Do
List y mostrará una lista de los ítems Todo en una página web.
Resultados
Deberá poder modificar una aplicación To Do List escrita con anterioridad y convertir una
clase POJO en un EJB. La aplicación acepta una lista de cosas por hacer como entrada del
usuario, y muestra la lista de ítems Todo en una página web.
Antes de comenzar
Abra una ventana de terminal en la máquina virtual de la estación de trabajo y ejecute el
siguiente comando para descargar los archivos necesarios para este trabajo de laboratorio.
[student@workstation ~]$ lab ejb-assessment setup
1.
Abra JBDS e importe el proyecto ejb-assessment ubicado en el directorio /home/
student/JB183/labs/ejb-assessment.
1.1. Haga doble clic en el icono de JBoss Developer Studio en el escritorio de la estación
de trabajo para abrir JBDS. Deje la configuración predeterminada (/home/student/
JB183/workspace) y haga clic en OK (Aceptar).
1.2. En el menú de JBDS, haga clic en File (Archivo) > Import (Importar)para abrir el
asistente para Import (Importar).
1.3. En la página Select (Seleccionar), haga clic en Maven > Existing Maven Projects
(Proyectos de Maven existentes) y, luego, haga clic en Next (Siguiente).
1.4. En la página Maven projects (Proyectos de Maven), haga clic en Browse (Explorar)
para abrir la ventana Select root folder (Seleccionar carpeta raíz). Diríjase al
directorio /home/student/JB183/labs/. Seleccione la carpeta ejb-assessment y
haga clic en OK (Aceptar).
1.5. En la página Maven projects (Proyectos de Maven), haga clic en Finish (Finalizar).
1.6. Observe la barra de estado de JBDS para monitorear el progreso de la operación
de importación. Es probable que la descarga de todas las dependencias requeridas
demore unos minutos.
2.
Revise la clase Item.java de la carpeta /home/student/JB183/labs/ejbassessment/src/main/java/com/redhat/training/todo/model.
Esta clase modela un único ítem Todo en la aplicación. Tiene tres atributos: un id, una
descripción y un atributo booleano que indica si la tarea se completó o no.
3.
Revise la clase ItemRepository.java de la carpeta /home/student/JB183/labs/
ejb-assessment/src/main/java/com/redhat/training/todo/data.
Esta clase simula una base de datos en la memoria y almacena la lista de ítems Todo.
Tiene métodos para agregar ítems, ver un único ítem y ver una lista de todos los ítems.
140
JB183-EAP7.0-es-2-20180124
Solución
Tenga en cuenta que esta clase está anotada con @ApplicationScoped, que significa
que los objetos de esta clase se mantienen en el alcance (activos) siempre y cuando la
aplicación esté implementada y en ejecución en el servidor de aplicaciones.
Tenga en cuenta que el método seedTodoList() se anotó con @PostConstruct. Este
método completa la lista Todo con tres ítems una vez que se inicializa la clase.
4.
Explore la clase ItemService.java, que es un POJO simple que contiene métodos
para agregar ítems, ver un ítem Todo y enumerar todos los ítems Todo. Este archivo está
ubicado en la carpeta /home/student/JB183/labs/ejb-assessment/src/main/
java/com/redhat/training/todo/service.
Tenga en cuenta que esta clase inyecta la clase ItemRepository e invoca métodos para
agregar, ver y enumerar los ítems Todo.
5.
Convierta el POJO ItemService en un EJB sin estado.
Anote la clase ItemService con la anotación @Stateless para convertir este POJO en
un EJB.
import javax.ejb.Stateless
@Stateless
public class ItemService {
6.
Explore la clase ItemResourceRESTService, que proporciona puntos finales REST
para la interfaz de usuario front-end (basada en AngularJS). Este archivo está ubicado en
la carpeta /home/student/JB183/labs/ejb-assessment/src/main/java/com/
redhat/training/todo/rest.
Tenga en cuenta que esta clase necesita usar el EJB ItemService para invocar los
métodos de EJB, y brinda una respuesta JSON para la capa de front-end.
7.
Inyecte el EJB ItemService en la clase ItemResourceRESTService.
Agregue la anotación @EJB en la declaración ItemService.
@EJB
ItemService itemService;
Agregue la siguiente importación en la clase ItemResourceRESTService para la
anotación:
import javax.ejb.EJB
8.
Inicie JBoss EAP desde dentro de JBDS.
Seleccione la pestaña Servers (Servidores) en JBDS. Haga clic con el botón derecho en
la entrada del servidor Red Hat JBoss EAP 7.0 [Stopped] y haga clic en la opción Start
(Iniciar) verde para iniciar el servidor. Observe la pestaña Console (Consola) de JBDS
hasta que el servidor se inicie y vea el siguiente mensaje:
JB183-EAP7.0-es-2-20180124
141
Capítulo 3. Creación de Enterprise Java Beans
INFO [org.jboss.as] (Controller Boot Thread) WFLYSRV0025: JBoss EAP 7.0.2.GA
(WildFly Core 2.1.8.Final-redhat-1) started
9.
Compile e implemente la aplicación en JBoss EAP con Maven.
Abra un nuevo terminal y cambie el directorio por la carpeta /home/student/JB183/
labs/ejb-assessment.
[student@workstation ~]$ cd /home/student/JB183/labs/ejb-assessment
Compile e implemente el EJB en JBoss EAP al ejecutar el siguiente comando:
[student@workstation ejb-assessment]$ mvn clean wildfly:deploy
La compilación debe ejecutarse correctamente y usted debe ver la siguiente salida:
[student@workstation ejb-assessment]$ mvn clean package wildfly:deploy
[INFO] Scanning for projects...
[INFO]
[INFO] -----------------------------------------------------------------------[INFO] Building TODO List Project 1.0
[INFO] -----------------------------------------------------------------------[INFO] [INFO] <<< wildfly-maven-plugin:1.0.2.Final:deploy (default-cli) < package @
ejb-assessment <<<
...
[INFO] --- maven-war-plugin:2.1.1:war (default-war) @ ejb-assessment --...
[INFO] --- wildfly-maven-plugin:1.0.2.Final:deploy (default-cli) @ ejb-assessment
[INFO] -----------------------------------------------------------------------[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
10. Observe las referencias JNDI del EJB ItemService en los registros del servidor de JBoss
EAP:
En la pestaña Console (Consola) de JBDS, debe ver lo siguiente:
JNDI bindings for session bean named 'ItemService' in deployment unit 'deployment
"ejb-assessment.war"' are as follows:
java:global/ejb-assessment/ItemService!com.redhat.training.todo.service.ItemService
java:app/ejb-assessment/ItemService!com.redhat.training.todo.service.ItemService
java:module/ItemService!com.redhat.training.todo.service.ItemService
java:global/ejb-assessment/ItemService
java:app/ejb-assessment/ItemService
java:module/ItemService
11. Explore la aplicación.
11.1.Diríjase a http://localhost:8080/ejb-assessment en un explorador para
acceder a la aplicación.
11.2.En el lado izquierdo de la página web, observe que se agregaron tres ítems Todo en
el método @PostConstruct de la clase ItemRepository.
142
JB183-EAP7.0-es-2-20180124
Solución
Observe la pestaña Console (Consola) de JBDS y verifique que la información
de registro del método register() en el EJB ItemService y del método
seedTodoList() en la clase ItemRepository esté visible.
Debe ver la siguiente salida de registro:
11:19:52,488 INFO [com.redhat.training.todo.service.ItemService] (default
task-21) Fetching all TODO items...
11:19:52,489 INFO [com.redhat.training.todo.data.ItemRepository] (default
task-21) Seeding TODO List cache...
11.3.En el lado derecho de la página web, agregue algunos ítems Todo; para ello,
introduzca texto en el campo Description (Descripción) y haga clic en Save (Guardar).
Verifique que la tarea que agregó aparezca en la tabla Task List (Lista de tareas) en la
izquierda.
Observe la pestaña Console (Consola) de JBDS y verifique que la información de
registro del método register() en el EJB ItemService esté visible.
Por ejemplo, si agrega una nueva tarea denominada Plan Project, debe ver la
siguiente salida de registro:
11:21:23,985 INFO [com.redhat.training.todo.service.ItemService] (default
task-23) Adding new task: Plan Project
11.4.Agregue algunas tareas más y observe que la aplicación muestra automáticamente
una barra de paginación en la parte inferior de la lista Task List (Lista de tareas) si
hay más de cinco tareas.
12. Abra una nueva ventana de terminal y ejecute el siguiente comando para calificar el
trabajo de laboratorio:
[student@workstation ~]$ lab ejb-assessment grade
El script de calificación debe indicar SUCCESS (CORRECTO). Si se produce una falla, revise
los errores y corríjalos hasta que vea un mensaje que indique SUCCESS (CORRECTO).
13. Realice la limpieza.
13.1.Anule la implementación de la aplicación en JBoss EAP con Maven con los siguientes
comandos:
[student@workstation ~]$ cd /home/student/JB183/labs/ejb-assessment
[student@workstation ejb-assessment]$ mvn wildfly:undeploy
13.2.Haga clic con el botón derecho en el proyecto ejb-assessment en Project Explorer
(Explorador de proyectos) y seleccione Close Project (Cerrar proyecto).
13.3.Haga clic con el botón derecho en el servidor Red Hat JBoss EAP 7.0 en la pestaña
Servers (Servidores) de JBDS y haga clic en Stop (Detener).
JB183-EAP7.0-es-2-20180124
143
Capítulo 3. Creación de Enterprise Java Beans
Esto concluye el trabajo de laboratorio.
144
JB183-EAP7.0-es-2-20180124
Resumen
Resumen
En este capítulo, aprendió lo siguiente:
• Un Enterprise Java Bean (EJB) es un componente portátil de Java EE que, por lo general,
se usa para encapsular lógica de negocio en una aplicación empresarial. Se ejecuta en
un servidor de aplicaciones y se puede consumir por clientes remotos, así como otros
componentes de Java EE que se ejecutan localmente en el mismo proceso JVM.
• Un EJB proporciona subprocesamiento múltiple, concurrencia, transacciones y seguridad
para aplicaciones empresariales sin requerir que el desarrollador escriba códigos para
estas características de manera explícita. Además, el desarrollador puede agregar
anotaciones en el EJB para exponer los métodos de negocio como extremos de servicio
web.
• Hay dos tipos diferentes de EJB: Beans de sesión y Beans controlados por mensajes (MDB). Los
beans de sesión pueden ser de tres tipos: Beans de sesión sin estado (SLSB), Beans de sesión
con estado (SFSB) y Beans de sesión singleton.
• Un bean controlado por mensaje (MDB) permite que las aplicaciones Java EE procesen
mensajes de manera asíncrona. Un MDB escucha mensajes JMS. Realiza una acción por
cada mensaje recibido. Los MDB brindan un modelo sin conexión directa impulsado por
eventos para el desarrollo de aplicaciones.
• Si un cliente de EJB y el EJB se ejecutan localmente en el mismo proceso JVM, los clientes
pueden inyectar referencias directamente en el EJB mediante la anotación @EJB. Si el
cliente es remoto, se utilizan las búsquedas de JNDI.
• Los componentes de un EJB en una aplicación se ejecutan en el contexto de un contenedor
dentro de un servidor de aplicaciones. El contenedor es responsable de administrar el ciclo
de vida (creación, ejecución y destrucción) de los EJB. Cada uno de los diferentes tipos de
EJB (sin estado, con estado, singleton, MDB) tiene su propio ciclo de vida.
• Java EE respalda las Transacciones para garantizar que se mantenga la integridad de datos
al controlar el acceso concurrente a los datos y para garantizar que una transacción de
negocio fallida no deje el sistema en un estado inconsistente o inválido.
• En Java EE, las transacciones se pueden administrar de dos maneras: Transacciones
administradas por contenedores (CMT) y Transacciones administradas por beans (BMT).
• En CMT, el servidor de aplicaciones administra las transacciones sin que el desarrollador
tenga que escribir código explícito y el alcance se puede controlar mediante el uso de
Atributos de transacción. El servidor de aplicaciones puede realizar automáticamente una
reversión cuando encuentra una falla o una excepción.
• En BMT, el desarrollador es responsable de administrar las transacciones y tiene pleno
control del alcance de las transacciones. El desarrollador tiene que confirmar y revertir
manualmente las transacciones si se produce una excepción o una falla.
JB183-EAP7.0-es-2-20180124
145
146
TRAINING
CAPÍTULO 4
GESTIÓN DE LA PERSISTENCIA
Descripción general
Meta
Crear entidades de persistencia con validaciones.
Objetivos
• Describir la API de persistencia.
• Conservar datos en un almacén de datos mediante
entidades.
• Anotar beans para validar datos.
• Crear una consulta mediante
Java Persistence Query Language.
Secciones
• Descripción de la API de persistencia (y cuestionario)
• Persistencia de datos (y ejercicio guiado)
• Anotación de clases para validar beans (y ejercicio
guiado)
• Creación de consultas (y ejercicio guiado)
Trabajo de laboratorio
JB183-EAP7.0-es-2-20180124
Gestión de la persistencia
147
Capítulo 4. Gestión de la persistencia
Descripción de la API de persistencia
Objetivos
Tras finalizar esta sección, los estudiantes deberán ser capaces de realizar lo siguiente:
• Comprender los conceptos de asignación relacional de objetos.
• Describir las anotaciones y clases de entidad.
• Describir cómo utilizar EntityManager en un EJB.
• Describir un descriptor XML de contexto de persistencia.
• Describir el impacto de las transacciones en la persistencia.
Asignación relacional de objetos
La persistencia es cuando una aplicación almacena datos en un almacén permanente, como
un archivo sin formato, archivo XML o una base de datos para la durabilidad. Las bases de
datos relacionales son uno de los almacenes de datos más comunes que una aplicación
empresarial utiliza para preservar datos para volver a utilizarlos.
Los datos de negocio en una aplicación empresarial de Java EE se definen como objetos Java.
Estos objetos se preservan en las tablas de base de datos correspondientes. Los objetos
Java y las tablas de base de datos utilizan diferentes tipos de datos, como una String en
Java y Varchar en una base de datos, para almacenar datos de negocio. A medida que se
transfieren los datos entre la aplicación y la base de datos como resultado de operaciones de
escritura, se pueden generar diferencias entre el modelo del objeto y el modelo relacional.
Esta discrepancia es conocida como disparidad de impedancia, y los desarrolladores de
aplicaciones deben escribir código para justificarla si el proveedor de persistencia aún no
solucionó la disparidad.
Figura 4.1: Disparidad de impedancia
La técnica para automatizar la corrección de la disparidad de impedancia es conocida como
Asignación relacional de objetos (ORM). El software de ORM utiliza metadatos para describir
la asignación entre las clases definidas en una aplicación y el esquema de una tabla de base
de datos. La asignación se proporciona en archivos de configuración XML o anotaciones.
148
JB183-EAP7.0-es-2-20180124
Anotaciones y clases de entidad
Por ejemplo, desea almacenar objetos de la clase TodoItem en la tabla de base de datos
TodoItem; ORM asigna el nombre de la clase Java al nombre de una tabla de base de datos
y los atributos de la clase se asignan a los campos correspondientes en la tabla de manera
automática.
Figura 4.2: Asignación relacional de objetos
Java EE proporciona la especificación de Java Persistence API (JSR 338) que varios proveedores
de ORM implementan. Hay muchos productos de software de ORM disponibles en el
mercado, como Hibernate y EclipseLink. Una ORM completamente implementada brinda
técnicas de optimización, almacenamiento en caché, portabilidad de base de datos,
lenguaje de consulta y persistencia de objetos. Los tres conceptos clave relacionados con
Java Persistence API son entidades, unidades de persistencia y contexto de persistencia.
Anotaciones y clases de entidad
Una entidad es un objeto de dominio liviano que tiene capacidad de persistencia. Una clase
de entidad se asigna a una tabla en una base de datos relacional. Cada instancia de una
clase de entidad tiene un campo de clave primaria. El campo de clave primaria se utiliza para
asignar una instancia de entidad a una fila de una tabla de base de datos. Todos los atributos
no transitorios se asignan a campos de una tabla de base de datos. En una tabla de base de
datos, cada instancia persistida de una entidad tiene una identidad de persistencia que la
identifica en una tabla. En Java, una entidad es una clase de Objetos comunes antiguos de
Java (POJO) que está marcada con la anotación @Entity. Todos los campos de una clase de
entidad se almacenan en la base de datos de manera predeterminada y se conocen como
campos persistentes. Los atributos que se declaran como transitorios no se almacenan en
una tabla de base de datos y se conocen como no persistentes.
Declaración de clases de entidad
Una clase de entidad se declara de la siguiente manera:
import javax.persistence.*;
import java.io.*;
@Entity
public class TodoItem implements Serializable {
@Id
private int id;
//primary key -- required for an Entity class
private String item;
private String status;
public TodoItem(){ } //No argument constructor
// other constructor
public TodoItem(String item,String status) {
this.item=item;
this.status=status;
JB183-EAP7.0-es-2-20180124
149
Capítulo 4. Gestión de la persistencia
}
//Setter and Getter methods
public String getItem() {
return item;
}
public void setItem(String item) {
this.item = item;
}
public String getStatus() {
return status;
}
public void setStatus(String status) {
this.status = status;
}
}
La relación predeterminada entre una clase de entidad y una tabla de base de datos es la
siguiente:
Asignación predeterminada de entidad a tabla
Entidad
Tabla
Clase de entidad
Nombre de tabla
Atributos de la clase de entidad
Columnas de una tabla de base de datos
Instancia de entidad
Registro o fila de una tabla de base de datos
Utilización de las anotaciones JPA
Las anotaciones se utilizan para decorar las clases Java, los campos y los métodos con
metadatos para la asignación, configuración, consulta, validación, etc., para que se compilen
y estén disponibles en el tiempo de ejecución. A continuación se muestran algunas
anotaciones comunes:
@Entity
La anotación @Entity especifica que una clase es una entidad. Una clase Java se puede
configurar como una clase de entidad sin utilizar una anotación @Entity al asignarla en el
archivo de configuración orm.xml. orm.xml contiene todos los detalles de configuración
requeridos para declarar una clase Java como una entidad.
@Table
La anotación @Table se utiliza para especificar la asignación entre una clase de entidad y
una tabla. Se utiliza cuando el nombre de una clase de entidad es diferente al nombre de una
tabla en la base de datos.
@Entity
@Table(name="ThingsToDo")
public class TodoItem {
...
}
La clase de entidad TodoItem se asigna a la tabla ThingsToDo.
150
JB183-EAP7.0-es-2-20180124
Anotaciones y clases de entidad
@Column
La anotación @Column se utiliza para asignar un campo o una propiedad a una columna de la
base de datos.
@Entity
@Table(name="ThingsToDo")
public class TodoItems implements Serializable {
@Column(name="itemname")
private String item;
...
Un atributo item se asigna a la columna itemname de la tabla.
@Temporal
La anotación @Temporal se utiliza con un tipo Date de atributo. La base de datos almacena
una fecha de manera diferente que las clases Java. La anotación Temporal administra la
asignación para un tipo java.util.Date o java.util.Calendar y lo convierte en un tipo
de fecha adecuado en la base de datos.
@Entity
public class TodoItem implements Serializable {
...
@Temporal(TemporalType.DATE)
private Date completionDate;
@Transient
La anotación Transient se utiliza para especificar un campo no persistente.
@Entity
public class TodoItem implements Serializable {
...
@Transient
private int countPending;
El campo countPending no se guarda en la tabla de la base de datos.
@Id
La anotación@Id se utiliza para especificar la clave primaria. El campo id se utiliza para
identificar una fila única en la tabla de la base de datos.
@Entity
public class TodoItem implements Serializable {
@Id
private int id;
...
}
Una clave primaria puede ser un tipo simple de Java o un valor compuesto, que consta de
varios campos. Para una clave primaria compuesta, se define una clase de clave primaria. La
anotación @EmbeddedId o @IdClass se utiliza para especificar la clave primaria compuesta.
JB183-EAP7.0-es-2-20180124
151
Capítulo 4. Gestión de la persistencia
Referencias
Para obtener más información acerca de la configuración de claves compuestas,
consulte la API pública para Red Hat JBoss EAP 7, que se encuentra en
https://developers.redhat.com/apidocs/eap/7.0.0/
Generación de ID
Cada instancia de entidad se asigna a una fila en una tabla de base de datos. Cada fila de
una tabla es única y está identificada con un ID único, conocido como una identidad de
entidad persistente. La identidad de entidad persistente se genera a partir del campo de
clave primaria. Se requiere un campo de clave primaria en cada clase de entidad. Una clave
primaria simple debe ser uno de los siguientes tipos:
• Tipos primitivos de Java: byte, short, int, long o char
• Tipo java.lang.String
• Clases de Java Wrapper para tipos primitivos: Byte, Short, Integer, Long o Character
• Tipos temporales: java.util.Date o java.sql.Date
La anotación @Id se utiliza parar especificar una clave primaria simple. La anotación
@GeneratedValue se aplica al campo o a la propiedad de la clave primaria para especificar
la estrategia de generación de la clave primaria. La anotación @GeneratedValue brinda un
elemento GenerationType del tipo de enumeración. Las cuatro estrategias de generación
de clave primaria son las siguientes:
GenerationType.AUTO
La estrategia AUTO es la estrategia de generación de ID predeterminada y significa que el
proveedor de JPA usa cualquier estrategia de su elección para generar la clave primaria.
Hibernate selecciona la estrategia de generación basada en el dialecto específico de la base
de datos.
@Entity
public class TodoItem implements Serializable {
@Id
@GeneratedValue(GenerationType.AUTO)
private int id;
...
}
GenerationType.SEQUENCE
La estrategia SEQUENCE significa que el proveedor de JPA usa la secuencia de la base de datos
para generar la clave primaria. La secuencia se debe crear en la base de datos, y el nombre
de la secuencia se proporciona en el elemento generator.
/* ITEMS_SEQ sequence
create sequence ITEMS_SEQ
MINVALUE 1
START WITH 1
152
JB183-EAP7.0-es-2-20180124
Descripción del administrador de entidades
INCREMENT BY 1
*/
@Entity
public class TodoItem implements Serializable {
@Id
@GeneratedValue(GenerationType.SEQUENCE, generator="ITEMS_SEQ"))
private int id;
...
}
GenerationType.IDENTITY
La estrategia IDENTITY significa que el proveedor de JPA usa la columna de identidad de la
base de datos para generar la clave primaria.
@Entity
public class TodoItem {
@Id
@GeneratedValue(GenerationType.IDENTITY)
private int id;
...
}
GenerationType.TABLE
La estrategia TABLE significa que el proveedor de JPA utiliza la tabla de generación de ID de la
base de datos. Esta es una tabla separada que se utiliza para generar el valor del ID. La tabla
de generación de ID tiene dos columnas. La primera columna es una cadena que identifica la
secuencia del generador, y la segunda columna es un valor entero que almacena la secuencia
del ID.
@Entity
public class TodoItem implements Serializable {
@TableGenerator(name="Items_gen",
table="ITEM_ID_GEN",
pkColumnName="GEN_NAME",
valueColumnName="GEN_VAL",
pkColumnValue="ITEM_ID",
allocationSize=60)
@Id
@GeneratedValue(Generator="Items_gen")
private int id;
...
}
Descripción del administrador de entidades
La API EntityManager se define para realizar operaciones de persistencia. Un administrador
de entidades obtiene la referencia a una entidad y realiza las operaciones CRUD (Crear, leer,
actualizar y eliminar) en la base de datos. Una instancia EntityManager puede obtenerse
de un objeto EntityManagerFactory. Un administrador de entidades trabaja dentro
de un conjunto de instancias de entidades administradas. Estas instancias de entidades
administradas son conocidas como el contexto de persistencia del administrador de entidades.
Podemos considerar al contexto de persistencia como una instancia única de una unidad de
persistencia. Una unidad de persistencia es un conjunto de todas las clases de entidades y
un archivo persistence.xml almacenado en una colección de archivos de aplicaciones. El
persistence.xml es un archivo de configuración que contiene información acerca de las
clases de entidad, fuentes de datos, tipos de transacción y otros datos de configuración.
JB183-EAP7.0-es-2-20180124
153
Capítulo 4. Gestión de la persistencia
Creación del administrador de entidades en EJB
Un objeto EntityManagerFactory se crea para la unidad de persistencia, y este objeto se
utiliza para obtener una instancia de EntityManager.
@Stateless
public class ItemService {
//ItemPU is the name of the persistence unit
EntityManagerFactory emFactory = Persistence.createEntityManagerFactory("ItemPU");
EntityManager em = emFactory.createEntityManager();
....
}
Otra manera de obtener una instancia de EntityManager en los objetos administrados
de Java EE, como un EJB, es la técnica de productor. Se puede inyectar un objeto
mediante Contextos e Inyección de dependencia (CDI). CDI es un conjunto de servicios de
administración de componentes que permite la inyección de dependencias con seguridad
de tipos. Hablaremos sobre CDI con mayor detalle más adelante en este curso. Una clase de
productor define un método de productor que devuelve el tipo de datos que se inyecta en
otra clase.
public class EMProducer {
@Produces
@PersistenceContext(unitName= "ItemPU")
private EntityManager em;
}
Una clase de EJB puede inyectar EntityManager mediante la anotación @Inject.
@Stateless
public class ItemService{
@Inject
private EntityManager em;
public void registerItem(Item item) throws Exception {
...
em.persist(item);
....
}
public void removeItem(Long id) throws Exception {
...
em.remove(findById(id));
....
}
public void updateItem(Item item) {
em.merge(item);
}
}
Descripción de la unidad de persistencia
Una unidad de persistencia describe los ajustes de configuración relacionados con una
fuente de datos, transacciones, clases concretas y asignación relacional de objetos. Una
unidad de persistencia se configura en un archivo persistence.xml en el directorio METAINF de la aplicación. Cada aplicación que usa persistencia tiene al menos una unidad de
persistencia. Una unidad de persistencia contiene información acerca del nombre de la
unidad de persistencia, la fuente de datos y el tipo de transacción. Hablaremos sobre el
archivo persistence.xml en la siguiente sección.
154
JB183-EAP7.0-es-2-20180124
Impacto de transacciones en la persistencia
<?xml version="1.0" encoding="UTF-8"?>
<persistence version="2.1"
xmlns="http://xmlns.jcp.org/xml/ns/persistence" xmlns:xsi="http://www.w3.org/2001/
XMLSchema-instance"
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/persistence
http://xmlns.jcp.org/xml/ns/persistence/persistence_2_1.xsd">
<persistence-unit name="Items" transaction-type="JTA">
<jta-data-source>java:jboss/datasources/MySQLDS</jta-data-source>
<properties>
<property name="hibernate.dialect" value="org.hibernate.dialect.MySQLDialect" />
<property name="hibernate.hbm2ddl.auto" value="update" />
<property name="hibernate.show_sql" value="true" />
<property name="hibernate.format_sql" value="true" />
</properties>
</persistence-unit>
</persistence>
Impacto de transacciones en la persistencia
Una transacción es una unidad simple de trabajo que está compuesta por una serie de
operaciones. Si una de las operaciones falla en la transacción, la transacción completa
vuelve a su estado original antes del inicio de la transacción. Si se pueden ejecutar todas las
operaciones, la transacción se confirma y no se requiere una restauración. Cuando trabaja
con persistencia, las transacciones garantizan que los cambios en una base de datos no
se completen parcialmente como resultado de una falla en la operación. JPA proporciona
comportamiento transaccional para operaciones en los recursos de JPA mediante dos
enfoques para las transacciones:
• Transacciones locales de recursos
• Transacciones de JTA
Las transacciones locales de recursos son transacciones con un alcance que abarca un único
recurso, como una fuente de datos. Por ejemplo, si una aplicación está configurada para
usar las transacciones locales de recursos, el administrador de entidades que está vinculado
con la fuente de datos que no corresponde a JTA utiliza la clase EntityTransaction para
administrar la transacción. Sin embargo, esta transacción solo se aplica a las operaciones
en esa fuente de datos individual en función del administrador de entidades, que limita
transacciones más complejas para abarcar varias fuentes de datos o varios sistemas de
mensajería.
En contraste, las transacciones de JTA abarcan todos los recursos en un contenedor. En lugar
de hacer referencia a EntityTransaction del administrador de entidades, JTA usa la clase
UserTransaction que le permite iniciar, confirmar o restaurar transacciones de manera
independiente del recurso o de los recursos. Esta separación de la transacción de un recurso
único permite que las transacciones contengan operaciones complejas que abarcan varios
recursos, como múltiples fuentes de datos y sistemas de mensajería JMS.
Referencias
JSR de persistencia de Java
https://www.jcp.org/en/jsr/detail?id=338
JB183-EAP7.0-es-2-20180124
155
Capítulo 4. Gestión de la persistencia
Referencias
Para obtener más información, consulte la Guía de desarrollo para API de persistencia
Java para Red Hat JBoss EAP 7, que se encuentra en
https://docs.jboss.org/author/display/AS7/JPA+Reference+Guide/
156
JB183-EAP7.0-es-2-20180124
Cuestionario: Descripción de la API de persistencia
Cuestionario: Descripción de la API de
persistencia
Elija las respuestas correctas para las siguientes preguntas:
1.
¿Cuál de las siguientes anotaciones se requiere para convertir una clase de Java SE
en una clase de entidad?
a.
b.
c.
d.
2.
¿Cuáles de las siguientes propiedades no se definen en el archivo
persistence.xml?
a.
b.
c.
d.
e.
3.
Nombre de la unidad de persistencia
Tipo de transacción
URL de fuente de datos
Proveedor de base de datos
Parámetros específicos del proveedor
¿Cuáles dos de los siguientes enunciados acerca del administrador de entidades
son correctos? (Elija dos opciones).
a.
b.
c.
d.
4.
@Table
@Produces
@Entity
@EntityManager
Los objetos del administrador de entidades se asignan a las filas de una tabla de
base de datos.
Un administrador de entidades realiza las operaciones Crear, leer, actualizar y
eliminar (CRUD) en una entidad.
Un administrador de entidades tiene un contexto de persistencia asociado.
Un administrador de entidades puede producir un conjunto de objetos
EntityManagerFactory.
¿Cuál es la estrategia de generación de ID que utiliza una columna de base de datos
para generar el ID?
a.
b.
c.
d.
SEQUENCE_GENERATOR
TABLE
SEQUENCE
IDENTITY
JB183-EAP7.0-es-2-20180124
157
Capítulo 4. Gestión de la persistencia
Solución
Elija las respuestas correctas para las siguientes preguntas:
1.
¿Cuál de las siguientes anotaciones se requiere para convertir una clase de Java SE
en una clase de entidad?
a.
b.
c.
d.
2.
¿Cuáles de las siguientes propiedades no se definen en el archivo
persistence.xml?
a.
b.
c.
d.
e.
3.
b.
c.
d.
Los objetos del administrador de entidades se asignan a las filas de una tabla de
base de datos.
Un administrador de entidades realiza las operaciones Crear, leer, actualizar y
eliminar (CRUD) en una entidad.
Un administrador de entidades tiene un contexto de persistencia asociado.
Un administrador de entidades puede producir un conjunto de objetos
EntityManagerFactory.
¿Cuál es la estrategia de generación de ID que utiliza una columna de base de datos
para generar el ID?
a.
b.
c.
d.
158
Nombre de la unidad de persistencia
Tipo de transacción
URL de fuente de datos
Proveedor de base de datos
Parámetros específicos del proveedor
¿Cuáles dos de los siguientes enunciados acerca del administrador de entidades
son correctos? (Elija dos opciones).
a.
4.
@Table
@Produces
@Entity
@EntityManager
SEQUENCE_GENERATOR
TABLE
SEQUENCE
IDENTITY
JB183-EAP7.0-es-2-20180124
Persistencia de datos
Persistencia de datos
Objetivos
Tras finalizar esta sección, los estudiantes deberán ser capaces de realizar lo siguiente:
• Describir los requisitos de las clases de entidad.
• Describir propiedades y campos de entidad.
• Describir la interfaz EntityManager y los métodos clave.
Creación de una clase de entidad
Una clase de entidad es similar a una clase POJO estándar, pero una entidad tiene varias
distinciones importantes que requieren la administración por parte de EntityManager. Para
convertir una clase POJO en una entidad, prefije una anotación @Entity en el encabezado
de la clase. Además, se debe poder acceder a cada variable de instancia a través del uso de
los métodos getter y setter. Por último, la clase debe tener al menos un constructor que no
tenga argumentos, aunque la clase pueda tener otros constructores que tienen argumentos.
A continuación se muestra un ejemplo de una clase de entidad:
@Entity
public abstract class Customer {
@Id
private int custId;
private String custName;
....
public Customer(){ } // No argument constructor
//setter and getter methods
public String getCustName() {
return custName;
}
public void setCustName(String custName) {
this.custName = custName;
}
...
}
Propiedades y campos de entidad
Los datos no transitorios en una clase de entidad se persisten en una tabla de base de datos.
Un proveedor de JPA puede cargar datos de una tabla de base de datos en una clase de
entidad y almacenar datos de una clase de entidad en una tabla de base de datos. La manera
en que el proveedor accede al estado es conocida como el modo de acceso. Hay dos modos
de acceso: acceso basado en el campo y acceso basado en la propiedad.
Acceso basado en el campo
El acceso basado en el campo se provee mediante la anotación de campos. Un campo
persistente en una clase de entidad se debe declarar con acceso de nivel privado, protegido o
de paquete. Un campo persistente debe ser uno de los siguientes tipos:
JB183-EAP7.0-es-2-20180124
159
Capítulo 4. Gestión de la persistencia
• Tipos primitivos de Java: byte, short, int, long o char
• Tipo java.lang.String
• Clases de Java Wrapper para tipos primitivos: Byte, Short, Integer, Long o Character
• Tipos temporales: java.util.Date o java.sql.Date
• Tipos enumerados
• Clases incorporables, otras entidades y colecciones de entidades
Los métodos getter y setter pueden estar presentes como no. El siguiente es un ejemplo de
acceso basado en campos:
@Entity
public class Customer implements Serializable {
// Note that the fields are annotated
@Id
protected int custId;
protected String custName;
@Temporal(TemporalType.DATE)
protected Date registrationDate;
@Column(name="address")
protected Address custAddress;
.....
}
nota
Se requiere una interfaz Serializable para clases de entidades a las que se
accede a través de una interfaz remota.
El acceso basado en campos proporciona una flexibilidad adicional ya que los campos o
métodos de ayuda que no deben formar parte del estado persistente se pueden excluir
mediante la anotación @Transient u omitiendo los métodos getter y setter.
Acceso basado en propiedades
Para proporcionar acceso basado en propiedades, se deben definir los métodos getter
y setter en una clase de entidad Java. El acceso basado en propiedades proporciona una
mejor encapsulación, ya que el acceso se da solo a través de métodos. El acceso basado en
propiedades se brinda anotando los métodos getter. El tipo de retorno del método getter
determina el tipo de propiedad. El tipo de retorno del método getter debe ser el mismo
que el tipo de argumento enviado al método setter. Los métodos getter y setter deben ser
públicos o protegidos y deben seguir las convenciones de denominación de Java bean. El
siguiente es un ejemplo de acceso basado en propiedades:
@Entity
public class Customer implements Serializable {
protected int custId;
protected String custName;
protected Date registrationDate;
protected Address custAddress;
....
//Note the getter methods are annotated
160
JB183-EAP7.0-es-2-20180124
Propiedades y campos de entidad
@Id
public int getCustId(){
return custId;
}
public String getCustName(){
return custName;
}
@Temporal(TemporalType.DATE)
public Date getRegistrationDate(){
return registrationDate;
}
@Column(name="address")
public Address getCustAddress(){
return custAddress;
}
....
//Setter methods
}
Estados de entidades
Una entidad puede existir en uno de cuatro estados durante su vida útil. Estos cuatro estados
son:
• New State (Estado nuevo): una instancia de entidad creada mediante el operador new de
Java está en un estado nuevo o temporal. Una instancia de entidad no tiene una identidad
persistente y aún no está relacionada con el contexto de persistencia.
• Managed State (Estado gestionado): una instancia de entidad con una identidad
persistente, relacionada con un contexto de persistencia se encuentra en estado
gestionado o persistente. Cuando se realiza un cambio en los datos de campos de
entidades gestionadas, se lo sincroniza con los datos de la tabla de la base de datos. Una
instancia de entidad se encuentra en estado gestionado después de que una aplicación
invoca el método persist, find o merge de un administrador de entidades.
• Removed State (Estado eliminado): una entidad persistente se puede eliminar de la tabla
de base de datos de muchas maneras. Una instancia de entidad gestionada se puede
eliminar de la tabla de la base de datos cuando se confirma una transacción o cuando se
invoca un método remove de un administrador de entidades. A continuación, una entidad
pasa a estar en estado eliminado.
• Detached State (Estado separado): una entidad tiene una identidad de entidad
persistente pero no está relacionada con el contexto de persistencia. Esto puede ocurrir
cuando la entidad está serializada o al final de una transacción. Este estado se conoce
como estado separado.
JB183-EAP7.0-es-2-20180124
161
Capítulo 4. Gestión de la persistencia
Figura 4.3: Relación de los componentes de JPA
Interfaz EntityManager y métodos clave
La interfaz javax.persistence.EntityManager se usa para interactuar con el contexto de
persistencia. Las instancias de entidades y sus ciclos de vida se gestionan dentro del contexto
de persistencia. La API javax.persistence.EntityManager se utiliza para crear nuevas
instancias de entidades, encontrar instancias de entidades a través de su clave primaria,
realizar consultas a través de instancias de entidades y eliminar las instancias de entidades
existentes. Los métodos clave de EntityManager son:
persist()
El método persist() persiste a una entidad y la vuelve gestionada. El método
persist() inserta una fila en una tabla de base de datos. El método persist() arroja
PersistenceException si falla la operación persist.
@Stateless
public class CustomerServices {
....
public void saveCustomer(Customer customer) {
...
try{
entityManager.persist(customer);
}catch(PersistenceException persistenceException){
// code to handle PersistenceException
}
}
}
find()
El método find() busca una entidad de una clase específica por su clave primaria y devuelve
una instancia de entidad gestionada. Si no se encuentra el objeto, devuelve uno nulo.
@Stateless
public class CustomerServices {
....
162
JB183-EAP7.0-es-2-20180124
Interfaz EntityManager y métodos clave
public void getCustomer(Customer customer) {
...
Customer customer;
try{
customer = entityManager.find(Customer.class,custId);
if (customer != null){
System.out.print(customer.getCustName());
} else }
System.out.print("Not Found");
}
}catch(Exception exception){
// code to handle PersistenceException
}
}
}
contains()
El método contains() toma una instancia como argumento y verifica si esta se encuentra
en el contexto de persistencia:
@Stateless
public class CustomerServices {
....
public boolean saveCustomer(Customer customer) {
...
entityManager.persist(customer);
return entityManager.contains(customer);
}
}
merge()
El método merge() actualiza los datos en la tabla para una entidad separada existente. El
método merge() inserta una nueva fila en una tabla de base de datos para una entidad que
se encuentra en un estado nuevo o temporal. Después de la operación de fusión, una entidad
se encuentra en estado gestionado.
@Stateless
public class CustomerServices {
....
public void updateCustomer(Customer customer) {
...
Customer customer;
try{
customer = entityManager.find(Customer.class,custId);
entityManager.merge(customer);
}catch(Exception exception){
// code to handle PersistenceException
}
}
}
remove()
El método remove() elimina una entidad gestionada. Para eliminar una entidad separada,
invoque un método find() que regresa una instancia gestionada y, luego, invoque el
método remove().
@Stateless
JB183-EAP7.0-es-2-20180124
163
Capítulo 4. Gestión de la persistencia
public class CustomerServices {
....
public void deleteCustomer(Customer customer) {
...
Customer customer;
try{
customer = entityManager.find(Customer.class,custId);
entityManager.remove(customer);
}catch(Exception exception){
// code to handle PersistenceException
}
}
}
clear()
El método clear() elimina el contexto de persistencia. Después de esta operación, todas las
entidades gestionadas se encuentran en estado separado.
...
try{
entityManager.clear();
}catch(Exception exception){
// code to handle PersistenceException
}
refresh()
El método refresh() actualiza el estado de una instancia de entidad de una tabla de base
de datos. Los datos actuales en una instancia de entidad se sobrescriben con los datos
capturados de una tabla de base de datos.
...
try{
entityManager.refresh(customer);
}catch(Exception exception){
// code to handle PersistenceException
}
Etiquetas importantes del archivo
persistence.xml
El archivo persistence.xml es un archivo de configuración estándar que contiene las
unidades de persistencia. Cada unidad de persistencia tiene un nombre único.
<?xml version="1.0" encoding="UTF-8"?>
<persistence version="2.1"
xmlns="http://xmlns.jcp.org/xml/ns/persistence" xmlns:xsi="http://www.w3.org/2001/
XMLSchema-instance"
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/persistence
http://xmlns.jcp.org/xml/ns/persistence/persistence_2_1.xsd">
<persistence-unit name="Items"
transaction-type="JTA">
<jta-data-source>java:jboss/datasources/MySQLDS</jta-data-source>
164
JB183-EAP7.0-es-2-20180124
Demostración: Persistencia de datos
<properties>
<property name="hibernate.dialect" value="org.hibernate.dialect.MySQLDialect" />
<property name="hibernate.hbm2ddl.auto" value="update" />
<property name="hibernate.show_sql" value="true" />
<property name="hibernate.format_sql" value="true" />
</properties>
</persistence-unit>
</persistence>
persistence-unit name es el nombre de la unidad de persistencia. El nombre de la
unidad de persistencia se utiliza para obtener el EntityManager.
transaction-type puede ser JTA o RESOURCE_LOCAL. El tipo de transacción define
el tipo de transacciones que una aplicación pretende realizar. Las transacciones
de contenedor usan la API de transacción Java (JTA), provista en cada servidor
de aplicaciones Java EE. En las transacciones de tipo JTA, un contenedor es
responsable de crear el administrador de entidades y de realizarle un seguimiento. En
RESOURCE_LOCAL, usted es responsable de crear el administrador de entidades y de
realizarle un seguimiento.
jta-data-source es el nombre de la fuente de datos. Cada unidad de persistencia
debe tener una conexión de base de datos. El proveedor de JPA encuentra la fuente de
datos por nombre con el servicio de búsqueda de JNDI durante el inicio.
Propiedades adicionales, tanto estándares como específicas del proveedor, se
pueden establecer en el elemento properties (propiedades). La propiedad
hibernate.Dialect especifica qué base de datos se utiliza. La propiedad
hibernate.hbm2ddl.auto con un valor update actualiza el esquema de manera
automática. La propiedad hibernate.show-sql con un valor true permite el registro
de enunciados SQL en la consola.
Demostración: Persistencia de datos
1.
Ejecute el siguiente comando para preparar archivos usados por esta demostración.
[student@workstation ~]$ demo persist-data setup
2.
Inicie JBDS e importe el proyecto persist-data.
Este proyecto es una simple aplicación web JSF que muestra todos los datos
almacenados en la base de datos. También utiliza CDI para inyectar el EJB responsable
de consultar la base de datos.
3.
Inspeccione el archivo pom.xml y observe la dependencia en las librerías hibernate para
la especificación JPA y las clases de administrador de la entidad.
<dependency>
<groupId>org.hibernate.javax.persistence</groupId>
<artifactId>hibernate-jpa-2.1-api</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-entitymanager</artifactId>
<scope>provided</scope>
</dependency>
JB183-EAP7.0-es-2-20180124
165
Capítulo 4. Gestión de la persistencia
4.
Actualice la clase de entidad Member (Miembro) para incluir las anotaciones JPA
@Entity, @Id y @GeneratedValue para convertir la clase POJO a una clase de entidad.
@Entity
public class Member implements Serializable{
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String emailId;
...
}
5.
Actualice el método saveMember en la clase MemberService para persist()
(mantener) la instancia Member (Miembro) en la base de datos.
public class MemberService {
@PersistenceContext(unitName="member-unit")
private EntityManager entityManager;
public String saveMember(String emailId) {
try {
Member member = new Member();
member.setEmailId(emailId);
entityManager.persist(member);
return "Congratulations ! you will get our weekly Tech Letter at " + emailId ;
} catch (Exception e) {
throw new EJBException(e);
}
}
6.
Observe el método getAllmembers() de la clase MemberService. Este método
contiene el código JPA utilizado para consultar la tabla de la base de datos.
public List<Member> getAllmembers() {
TypedQuery<Member> query = entityManager.createQuery("SELECT m FROM Member m",
Member.class);
List<Member> members = query.getResultList();
return members;
}
7.
Inicie el servidor JBoss EAP local dentro de JBDS.
En la pestaña Servers del panel inferior de JBDS, haga clic con el botón derecho en el
servidor Red Hat JBoss EAP 7.0 [Stopped] y haga clic en el botón verde para iniciar el
servidor. Observe la Console (Consola) hasta que el servidor se inicie y vea el mensaje
iniciado:
INFO [org.jboss.as] (Controller Boot Thread) WFLYSRV0025: JBoss EAP 7.0.0.GA
(WildFly Core 2.1.2.Final-redhat-1) started
8.
166
Implemente la aplicación en el servidor JBoss EAP local y pruébela en un explorador
ejecutando los siguientes comandos en una ventana de terminal:
JB183-EAP7.0-es-2-20180124
Demostración: Persistencia de datos
[student@workstation ~]$ cd /home/student/JB183/labs/persist-data
[student@workstation persist-data]$ mvn wildfly:deploy
Abra http://localhost:8080/persist-data/ en un explorador en la máquina
virtual workstation y asegúrese de que la aplicación esté almacenando y mostrando
correctamente las direcciones de correo electrónico de los miembros.
9.
Anule la implementación de la aplicación y detenga el servidor.
[student@workstation persist-data]$ mvn wildfly:undeploy
Referencias
Para obtener más información, consulte la Guía de desarrollo para API de persistencia
Java para Red Hat JBoss EAP 7, que se encuentra en
https://docs.jboss.org/author/display/AS7/JPA+Reference+Guide/
JB183-EAP7.0-es-2-20180124
167
Capítulo 4. Gestión de la persistencia
Ejercicio guiado: Persistencia de datos
En este ejercicio, persistirá datos de la aplicación en la base de datos.
Resultados
Deberá poder crear una clase de entidad y persistir datos de entidad.
Antes de comenzar
Abra una ventana de terminal en la máquina virtual workstation y ejecute el siguiente
comando para descargar los archivos del trabajo de laboratorio necesarios para este ejercicio
guiado.
[student@workstation ~]$ lab persist-entity setup
Pasos
1. Importe el proyecto persist-entity en el IDE de JBoss Developer Studio (JBDS).
1.1. Inicie JBDS haciendo doble clic en el icono del escritorio de la máquina virtual
workstation.
1.2. En la ventana Eclipse Launcher, ingrese /home/student/JB183/workspace en el
campo Workspace (Espacio de trabajo) y, luego, haga clic en Launch (Iniciar).
1.3. En el menú de JBDS, haga clic en File (Archivo) > Import (Importar)para abrir el
asistente para Import (Importar).
1.4. En la página Select (Seleccionar), haga clic en Maven > Existing Maven Projects
(Proyectos de Maven existentes) y, luego, haga clic en Next (Siguiente).
1.5. En la página Maven projects (Proyectos de Maven), haga clic en Browse (Explorar)
para abrir la ventana Select root folder (Seleccionar carpeta raíz). Diríjase al
directorio /home/student/JB183/labs/. Seleccione la carpeta persist-entity y
haga clic en OK (Aceptar).
1.6. En la página Maven projects (Proyectos de Maven), haga clic en Finish (Finalizar).
1.7. Observe la barra de estado de JBDS para monitorear el progreso de la operación
de importación. Es probable que la descarga de todas las dependencias requeridas
demore unos minutos.
nota
El proyecto persist-entity tiene un error de compilación cuando se
importa. Este error se resuelve en los siguientes pasos del trabajo de
laboratorio.
2.
168
Convierta una clase Person simple de Java en una clase de entidad.
JB183-EAP7.0-es-2-20180124
2.1. En la pestaña Project Explorer (Explorador de proyectos) del panel izquierdo
de JBDS, seleccione persist-entity > Java Resources > src/main/java >
com.redhat.training.model y expanda el paquete.
2.2. Haga doble clic en el archivo Person.java del paquete com.redhat.training.model
seleccionado para abrir la clase en el editor.
2.3. Agregue la anotación @Entity en la clase Person en el paquete
com.redhat.training.model. Agregue la anotación @Entity e importe la librería
javax.persistence.Entity.
//add the required libraries
import javax.persistence.Entity;
//add @Entity annotation to the Person class
@Entity
public class Person {
.....
}
nota
Agregar la anotación @Entity crea un error de compilación. Este error se
puede ignorar y se resolverá en un paso posterior.
2.4. La clase de entidad Person debe implementar la interfaz Serializable. Importe e
implemente la interfaz Serializable.
import javax.persistence.Entity;
import java.io.Serializable;
@Entity
public class Person implements Serializable {
.....
}
2.5. Agregue las anotaciones @Id y @GeneratedValue(strategy =
GenerationType.IDENTITY) en el atributo id de la clase Person para convertirla
en una clave primaria con la estrategia de generación de clave IDENTITY. Agregue
la anotación @Column(name="name") en el atributo personName para asignarla al
campo name en la tabla de la base de datos. Importe las librerías requeridas.
...
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.Column;
@Entity
public class Person implements Serializable {
//add annotations for primary key
@Id
JB183-EAP7.0-es-2-20180124
169
Capítulo 4. Gestión de la persistencia
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
//add @Column(name="name") annotation to map column in database table
@Column(name="name")
private String personName;
....
}
2.6. Presione Ctrl+S para guardar sus cambios.
3.
Abra la clase PersonService en el paquete com.redhat.training.services y
agregue la funcionalidad de persistencia para guardar Person en la base de datos y
buscar a una persona de la base de datos.
3.1. En la pestaña Project Explorer (Explorador de proyectos) del panel izquierdo
de JBDS, seleccione persist-entity > Java Resources > src/main/java >
com.redhat.training.services y expanda el paquete.
3.2. Haga doble clic en el archivo PersonService.java del paquete
com.redhat.training.services para abrir la clase PersonService en el panel del
editor.
3.3. Se requiere el objeto EntityManager para realizar las operaciones de persistencia
en la clase PersonService. Agregue una anotación @PersistenceContext para
obtener un objeto EntityManager:
import javax.persistence.PersistenceContext;
import javax.persistence.EntityManager;
@Stateless
public class PersonService {
//TODO: obtain an EntityManager instance using @PersistenceContext
@PersistenceContext(unitName="hello")
private EntityManager entityManager;
...
}
3.4. Persista una clase Person en la base de datos mediante el administrador
de entidades y agregue el siguiente código en el método public String
hello(String name), de la siguiente manera:
public String hello(String name) {
...
// call persist() method of entity manager to save the data
entityManager.persist(p);
...
}
3.5. Busque el nombre de una persona mediante el uso de id y agregue el método
getPerson(Long id) en la clase PersonService. En la instrucción de devolución,
use el método find() del administrador de entidades para devolver el atributo
name de Person en función del id.
// TODO:add public String getPerson(Long id) method here to fetch result
//by Person id using find() method
170
JB183-EAP7.0-es-2-20180124
public String getPerson(Long id) {
return entityManager.find(Person.class, id).getPersonName();
}
3.6. Observe el método getAllPersons() que devuelve todos los objetos Person
almacenados en la base de datos:
// Get all Person objects in the Database
public List<Person> getAllPersons() {
TypedQuery<Person> query = entityManager.createQuery("SELECT p FROM Person
p", Person.class);
List<Person> persons = query.getResultList();
return persons;
}
3.7. Presione Ctrl+S para guardar sus cambios.
4.
Abra la clase Hello en el paquete com.redhat.training.ui. Elimine el comentario de
los métodos getPerson() y getPersons() para agregar la funcionalidad de interfaz de
usuario para ver el nombre de una única persona y todos los nombres almacenados en
la base de datos.
4.1. En la pestaña Project Explorer (Explorador de proyectos) del panel izquierdo
de JBDS, seleccione persist-entity > Java Resources > src/main/java >
com.redhat.training.ui y expanda el paquete.
4.2. Haga doble clic en el archivo Hello.java del paquete com.redhat.training.ui
seleccionado. La clase Hello se abre en el panel del editor.
4.3. Elimine los comentarios del método public void getPerson().
public void getPerson() {
try {
String response = personService.getPerson(id);
FacesContext.getCurrentInstance().addMessage(null, new
FacesMessage(response));
}catch(Exception e){
System.out.println(e.getCause());
if(e.getCause() != null && e.getCause() instanceof
ConstraintViolationException) {
ConstraintViolationException ex = (ConstraintViolationException)
e.getCause();
String violations = "";
for(ConstraintViolation<?> cv: ex.getConstraintViolations()) {
violations += cv.getMessage() + "\n";
System.out.println("Violations: "+violations);
}
FacesContext.getCurrentInstance().addMessage(null, new
FacesMessage(violations));
}
}
}
JB183-EAP7.0-es-2-20180124
171
Capítulo 4. Gestión de la persistencia
4.4. Elimine los comentarios del método public List<Person> getPersons().
public List<Person> getPersons() {
return personService.getAllPersons();
}
5.
Compile e implemente la aplicación.
5.1. Seleccione la pestaña Servers (Servidores) en el panel inferior de JBDS para iniciar
EAP. Haga clic con el botón derecho en el servidor Red Hat JBoss EAP 7.0 [Stopped] y
haga clic en el botón verde de inicio para iniciar el servidor.
5.2. Compile la aplicación persist-entity mediante los siguientes comandos en la ventana
del terminal:
[student@workstation ~]$ cd /home/student/JB183/labs/persist-entity
[student@workstation persist-entity]$ mvn clean package
5.3. Implemente la aplicación persist-entity mediante el siguiente comando en la
ventana del terminal:
[student@workstation persist-entity]$ mvn wildfly:deploy
6.
Pruebe la persistencia de la aplicación.
6.1. Use un explorador web en la máquina virtual workstation para navegar a http://
localhost:8080/persist-entity/ para acceder a la aplicación persist-entity.
Figura 4.4: La aplicación persist-entity
6.2. Ingrese Samuel en el campo Enter your name (Ingrese su nombre) y haga clic en
Submit (Enviar).
172
JB183-EAP7.0-es-2-20180124
6.3. Verifique que el servidor procese la entrada y responda con un saludo utilizando el
nombre que usted ingresó, así como la hora actual en el servidor.
Figura 4.5: La respuesta de la aplicación persist-entity
6.4. Repita el paso anterior al menos 2 veces más, ingresando diferentes nombres para
completar la base de datos.
6.5. Haga clic en View all names (Ver todos los nombres) para comprobar que todos los
nombres estén almacenados en la base de datos.
Figura 4.6: La lista de todos los nombres
Haga clic en el enlace Atrás [/persist-entity/index.xhtml] para ir a la página de inicio.
6.6. Para buscar el nombre de una persona individual en la base de datos, haga clic en el
enlace Search a name (Buscar un nombre).
JB183-EAP7.0-es-2-20180124
173
Capítulo 4. Gestión de la persistencia
Figura 4.7: Búsqueda de un nombre individual en la base de datos
6.7. Introduzca el valor de un id en el campo Enter Id: (Ingresar id:) y haga clic en Submit
(Enviar).
Figura 4.8: Visualización de un nombre individual de la base de datos
Haga clic en Back (Atrás) para ir a la página de inicio.
7.
Anule la implementación de la aplicación y detenga EAP.
7.1. En la ventana del terminal donde ejecutó el comando Maven para implementar
la aplicación, ejecute el siguiente comando para anular la implementación de la
aplicación:
[student@workstation persist-entity]$ mvn wildfly:undeploy
174
JB183-EAP7.0-es-2-20180124
7.2. Haga clic con el botón derecho en el proyecto persist-entity en Project Explorer
(Explorador de proyectos) y seleccione Close Project (Cerrar proyecto) para cerrar
este proyecto.
7.3. Haga clic con el botón derecho en Red Hat JBoss EAP 7.0 en la pestaña Servers
(Servidores) y haga clic en Stop (Detener) para detener la instancia EAP.
Esto concluye el ejercicio guiado.
JB183-EAP7.0-es-2-20180124
175
Capítulo 4. Gestión de la persistencia
Anotación de clases para validar beans
Objetivos
Tras finalizar esta sección, los estudiantes deberán ser capaces de realizar lo siguiente:
• Explicar la validación de beans.
• Usar las restricciones y anotaciones de la validación de beans.
• Comparar la invocación automática y manual.
Validación de beans
Las aplicaciones de Java almacenan datos en objetos de Java. Estos objetos de Java viajan a
través de la red, enviados como argumentos a métodos, y están presentes en las diferentes
capas de una aplicación Java EE. Para mantener la integridad de los datos, la validación de
datos es un requisito importante en la lógica de aplicaciones. Los desarrolladores deben
escribir código de validación de datos en las diferentes capas de la aplicación para la
validación de datos, que es propensa a errores y lleva mucho tiempo. La especificación de la
API de validación de beans se proporciona para evitar la duplicación de códigos y simplificar
la validación de datos. La validación de beans es un modelo para validar datos en objetos
de Java al usar anotaciones incorporadas y personalizadas que pueden aplicar restricciones
predefinidas. La validación de beans es común a todas las capas de aplicaciones web Java EE
y Java. Java proporciona la API 1.1 de validación de beans en JSR 349. JPA admite validación de
tiempo de ejecución de clases de entidad a través de API de validación de beans. JBoss EAP
cumple por completo con JSR 349.
Restricciones y anotaciones de la validación de
beans
Las restricciones de validación son las reglas aplicadas para validar datos. Estas restricciones
se aplican en la forma de anotaciones para atributos, métodos, propiedades o constructores.
La API 1.1 de validación de beans permite el uso de restricciones de validación en
argumentos y devuelve valores de métodos y constructores. Java proporciona restricciones
incorporadas y también admite restricciones personalizadas definidas por el usuario. El
paquete javax.validation.constraints contiene varias restricciones incorporadas.
Algunas anotaciones comunes:
Anotación
Descripción
@NotNull
La anotación @NotNull verifica que
el valor en el campo o la propiedad
no sea nulo.
@NotNull
private String itemName;
@Null
La anotación @Null verifica que el
valor en el campo o la propiedad
sea nulo.
@Null
private String comments;
@Size
La anotación @Size verifica que
el tamaño del campo sea entre el
mín. y el máx., incluidos los valores
límite.
176
Ejemplo
@Size (min=3, max=40)
private String name;
JB183-EAP7.0-es-2-20180124
Restricciones y anotaciones de la validación de beans
Anotación
Descripción
@Min
La anotación @Min verifica que el
valor en el campo o la propiedad
sea superior o equivalente al valor
definido en el Mín. El valor es un
elemento requerido del tipo long.
@Max
La anotación @Max verifica que el
valor en el campo o la propiedad
sea inferior o equivalente al valor
definido en el Máx. El valor es un
elemento requerido del tipo long.
@Digits
La anotación @Digits verifica
la precisión y escala del campo.
El campo debe ser un número
dentro del rango especificado. El
rango se define por el integer
(número entero) y los elementos de
fraction (fracción). Un número
entero y la fracción son elementos
requeridos de un tipo int.
@DecimalMin
La anotación @DecimalMín
verifica si el valor en el campo o
la propiedad es un valor decimal
superior o equivalente al valor
definido en el DecimalMín. El valor
es un elemento requerido del tipo
String.
@DecimalMax
La anotación @DecimalMáx
verifica si el valor en el campo o
la propiedad es un valor decimal
superior o equivalente al valor
definido en el DecimalMáx. El valor
es un elemento requerido del tipo
String.
@Future
La anotación @Future verifica si el
valor en el campo o la propiedad es
una fecha futura.
@Future
private Date promotionDate;
@Past
La anotación @Past verifica si el
valor en el campo o la propiedad es
una fecha pasada.
@Past
private Date startDate;
@Pattern
La anotación @Pattern verifica
si el valor en el, campo o la
propiedad coincide con la expresión
regexp. La expresión regular es
un elemento requerido del tipo
String.
JB183-EAP7.0-es-2-20180124
Ejemplo
@Min(100)
private int minStock;
@Max(1000)
private int maxStock;
@Digits (integer=7, fraction=2)
private double monthlySale;
@DecimalMin("8.5")
private double minTax;
@DecimalMax("19.5")
private double maxTax;
@Pattern (regexp="\\(\\d{3}\\)\
\d{3}-\\d{4}")
private String phoneNumber;
177
Capítulo 4. Gestión de la persistencia
Anotación
Descripción
@AssertFalse La anotación @AssertFalse
verifica si el valor en el campo o la
propiedad es falso.
@AssertTrue
La anotación @AssertTrue
verifica si el valor en el campo o la
propiedad es falso.
Ejemplo
@AssertFalse
private boolean isAvailable;
@AssertTrue
private boolean isOrdered;
Todas las anotaciones de validación de beans tienen atributos opcionales, como el atributo
message (mensaje), el cual se puede usar para mostrar un mensaje personalizado si no se
puede realizar la validación. Algunas anotaciones tienen atributos requeridos. Por ejemplo, la
anotación DecimalMáx. tiene un atributo de valor de tipo String para representar un valor
máximo. Los siguientes son algunos ejemplos:
@NotNull con el atributo message (mensaje) puede tener un mensaje personalizado que se
puede mostrar en lugar de un mensaje predeterminado si no se puede realizar la validación.
@NotNull(message="Address cannot be a null value")
private Address address;
Invocación automática en relación a la manual
Invocación automática
El servidor de aplicaciones Java EE 7 proporciona el paquete Hibernate Validator, que
incluye anotaciones de validación de beans, así como una invocación automática de las
restricciones de validación. Al adjuntar una anotación en un campo de entidad, Hibernate
valida automáticamente que los datos coincidan con las restricciones de anotación colocadas
en los campos. Por ejemplo, el siguiente snippet de código demuestra el uso de una
restricción @Size(min=4), aplicándola al atributo personName de la clase Person (Persona).
Al crear una instancia de una entidad, si los datos presentados no cumplen con la restricción
de validación, en este caso en que el tamaño de la Cadena es de al menos cuatro caracteres,
se devuelve un error. El servidor de aplicaciones y el marco (framework) del validador
verifican automáticamente la restricción antes de persistir una entidad a una base de datos.
...
//Using validation in constructor
public Person(@NotNull String personName){
this.personName=personName;
}
//using validation in method parameter
public void setPersonName(@Size(min=4) String name){
this.personName=personName;
}
...
Invocación manual
Si bien muchos marcos (frameworks) validan automáticamente campos de entidad
basados en estas anotaciones de validación, ocasionalmente, los desarrolladores necesitan
desencadenar la validación de beans por programación. Para validar la instancia de una
entidad por programación, use la API javax.validation.Validator. La interfaz de
validator proporciona métodos para validar una entidad entera o una única propiedad
178
JB183-EAP7.0-es-2-20180124
Invocación automática en relación a la manual
de una entidad. El siguiente código ilustra cómo crear una instancia ValidatorFactory y
Validator y usa el validator para validar un objeto.
...
Person p = new Person();
p.setPersonName("RH");
ValidatorFactory validatorFactory = Validation.buildDefaultValidatorFactory();
Validator validator = validatorFactory.getValidator();
Set<ConstraintViolation<Person>> constraintViolations = validator.validate(p);
...
Este código crea un conjunto de ContstraintViolations que se pueden iterar para
ver todas las violaciones que se producen en función de las anotaciones de la entidad. El
siguiente es un ejemplo de iteración del conjunto constraintViolations y registro de
cada error:
for (ConstraintViolation<Person> cv : constraintViolations) {
log.error(cv.getMessage());
}
Referencias
API de validación de beans
http://beanvalidation.org/
Referencias
Para obtener más información, consulte la guía JBoss Application Server 7 para Red
Hat JBoss EAP 7 en
https://docs.jboss.org/author/display/AS71/Documentation/
JB183-EAP7.0-es-2-20180124
179
Capítulo 4. Gestión de la persistencia
Ejercicio guiado: Validación de datos
En este ejercicio, validará datos en una clase de entidad usando validación de beans.
Resultados
Deberá ser capaz de validar un campo de beans mediante restricciones incorporadas.
Antes de comenzar
Abra una ventana de terminal en la máquina virtual workstation y ejecute el siguiente
comando para descargar los archivos del trabajo de laboratorio necesarios para este ejercicio
guiado.
[student@workstation ~]$ lab validate-data setup
Pasos
1. Importe el proyecto validate-data en el IDE de JBoss Developer Studio (JBDS).
1.1. Inicie JBDS haciendo doble clic en el icono del escritorio de la máquina virtual
workstation.
1.2. En la ventana Eclipse Launcher, ingrese /home/student/JB183/workspace en el
campo Workspace (Espacio de trabajo) y, luego, haga clic en Launch (Iniciar).
1.3. En el menú de JBDS, haga clic en File (Archivo) > Import (Importar)para abrir el
asistente para Import (Importar).
1.4. En la página Select (Seleccionar), haga clic en Maven > Existing Maven Projects
(Proyectos de Maven existentes) y, luego, haga clic en Next (Siguiente).
1.5. En la página Maven projects (Proyectos de Maven), haga clic en Browse (Explorar)
para abrir la ventana Select root folder (Seleccionar carpeta raíz). Diríjase al
directorio /home/student/JB183/labs/. Seleccione la carpeta validate-data y
haga clic en OK (Aceptar).
1.6. En la página Maven projects (Proyectos de Maven), haga clic en Finish (Finalizar).
1.7. Observe la barra de estado de JBDS para monitorear el progreso de la operación
de importación. Es probable que la descarga de todas las dependencias requeridas
demore unos minutos.
2.
Agregue las restricciones de validación a la clase de entidad Person para garantizar que
el personName no quede en blanco.
2.1. En la pestaña Project Explorer (Explorador de proyectos) del panel izquierdo
de JBDS, seleccione validate-data > Java Resources > src/main/java >
com.redhat.training.model y expanda el paquete.
2.2. Haga doble clic en el archivo Person.java del paquete com.redhat.training.model
seleccionado para abrir la clase en el editor.
2.3. Importe la librería javax.validation.constraints.NotNull y agregue la
siguiente anotación @NotNull al atributo personName en la clase Person:
180
JB183-EAP7.0-es-2-20180124
//ToDo:add the validation imports
import javax.validation.constraints.NotNull;
.....
@Entity
public class Person implements Serializable{
....
//TODO: Add validation constraints here
@NotNull(message="Name cannot be blank")
@Column(name="name")
private String personName;
.....
}
2.4. Presione Ctrl+S para guardar sus cambios.
3.
Compile e implemente la aplicación.
3.1. Seleccione la pestaña Servers (Servidores) en el panel inferior de JBDS para iniciar
EAP. Haga clic con el botón derecho en el servidor Red Hat JBoss EAP 7.0 [Stopped] y
haga clic en el botón verde de inicio para iniciar el servidor.
3.2. Compile la aplicación validate-data mediante los siguientes comandos en la ventana
del terminal:
[student@workstation ~]$ cd /home/student/JB183/labs/validate-data
[student@workstation validate-data]$ mvn clean package
3.3. Implemente la aplicación validate-data mediante el siguiente comando en la
ventana del terminal:
[student@workstation validate-data]$
4.
mvn wildfly:deploy
Pruebe la aplicación para la validación del campo en blanco.
4.1. Use un explorador web en la máquina virtual workstation para navegar a http://
localhost:8080/validate-data/ para acceder a la aplicación validate-data.
4.2. No ingrese ningún dato en el campo Enter your name (Ingrese su nombre) y haga
clic en Submit (Enviar).
4.3. Verifique que el campo esté en blanco y vea la respuesta con un mensaje de error
Name cannot be blank (El nombre no puede estar en blanco).
JB183-EAP7.0-es-2-20180124
181
Capítulo 4. Gestión de la persistencia
Figura 4.9: La respuesta de nombre en blanco de la aplicación validate-data
4.4. Ingrese James en el campo Enter your name (Ingrese su nombre) y haga clic en
Submit (Enviar).
4.5. Verifique que el servidor procese la entrada y responda con un saludo utilizando el
nombre que usted ingresó.
Figura 4.10: La respuesta de la aplicación validate-data
5.
Agregue una restricción de validación a la clase de entidad Person para probar que el
personName no sea de menos de dos caracteres.
5.1. Importe la librería javax.validation.constraints.Size. Agregue la anotación
@Size(min=2, message="Name cannot be less than 2 characters") al
atributo personName en la clase Person.
//ToDo:add the validation imports
import javax.validation.constraints.NotNull;
import javax.validation.constraints.Size;
....
182
JB183-EAP7.0-es-2-20180124
@Entity
public class Person implements Serializable{
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
//TODO: Add validation constraints here
@NotNull(message="Name cannot be blank")
@Size( min=2, message="Name cannot be less than 2 characters" )
@Column(name="name")
private String personName;
.....
}
5.2. Presione Ctrl+S para guardar sus cambios.
6.
Vuelva a compilar e implementar la aplicación.
6.1. Vuelva a compilar la aplicación validate-data mediante los siguientes comandos en
la ventana del terminal:
[student@workstation validate-data]$
mvn clean package
6.2. Vuelva a implementar la aplicación validate-data mediante el siguiente comando en
la ventana del terminal:
[student@workstation validate-data]$
7.
mvn wildfly:deploy
Pruebe la aplicación para la validación del tamaño del campo del nombre.
7.1. Use un explorador web en la máquina virtual workstation para navegar a http://
localhost:8080/validate-data/ para acceder a la aplicación validate-data.
7.2. Ingrese un carácter en el campo Enter your name (Ingrese su nombre) y haga clic en
Submit (Enviar).
7.3. Verifique que el mensaje Name cannot be less than 2 characters (El nombre no
puede tener menos de dos caracteres) se muestre como respuesta.
JB183-EAP7.0-es-2-20180124
183
Capítulo 4. Gestión de la persistencia
Figura 4.11: La validación de longitud de la aplicación validate-data
7.4. Ingrese Al en el campo Enter your name (Ingrese su nombre) y haga clic enSubmit
(Enviar).
Verifique que el servidor procese la entrada y responda con un saludo utilizando el
nombre que usted ingresó y la hora actual en el servidor.
8.
Anule la implementación de la aplicación y detenga EAP.
8.1. En la ventana del terminal donde ejecutó el comando Maven para implementar
la aplicación, ejecute el siguiente comando para anular la implementación de la
aplicación:
[student@workstation validate-data]$ mvn wildfly:undeploy
8.2. Haga clic con el botón derecho en el proyecto validate-data del Project Explorer
(Explorador de proyectos) y seleccione Close Project para cerrar este proyecto.
8.3. Haga clic con el botón derecho en Red Hat JBoss EAP 7.0 en la pestaña Servers
(Servidores) y haga clic en Stop (Detener) para detener la instancia EAP.
Esto concluye el ejercicio guiado.
184
JB183-EAP7.0-es-2-20180124
Creación de consultas
Creación de consultas
Objetivo
Tras completar esta sección, los estudiantes deberán poder crear consultas mediante
Java Persistence Query Language.
Creación de consultas
Java Persistence Query Language (JPQL) es un lenguaje de consultas que no depende de
plataformas y que está definido como parte de la especificación JPA para realizar consultas en
entidades de manera orientada al objeto. JPQL es similar a SQL en sintaxis, pero las consultas
de JPQL se expresan en términos de entidades Java en lugar de tablas y columnas de una
base de datos. Los proveedores de JPA, como Hibernate, transforman consultas de JPQL
a SQL. JPQL admite las instrucciones SELECT, UPDATE y DELETE. Para comprender cómo
crear diferentes tipos de consultas con JPQL, observe un ejemplo de una clase de la entidad
Employee:
@Entity
public class Employee implements Serializable{
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String empName;
private double salary;
...
}
Una tabla Employee se crea en una base de datos para una entidad Employee. Debajo se
muestran los datos de muestra de todos los empleados de una tabla Employee:
MariaDB [todo]> select * from Employee;
+----+---------+--------+
| id | empName | salary |
+----+---------+--------+
| 1 | Tim
| 120000 |
| 2 | Joe
| 100000 |
| 3 | Tom
| 125000 |
| 4 | Mia
| 135000 |
| 5 | Matt
| 145000 |
| 6 | Kim
| 115000 |
| 7 | Nita
| 117000 |
+----+---------+--------+
7 rows in set (0.00 sec)
La consulta JPQL recuperará registros de todos los empleados de la base de datos de la
siguiente manera:
SELECT e FROM Employee e;
La API EntityManager admite métodos para crear consultas estáticas y dinámicas. El
método createNamedQuery se utiliza para crear consultas estáticas, mientras que el método
createQuery se utiliza para crear consultas dinámicas.
JB183-EAP7.0-es-2-20180124
185
Capítulo 4. Gestión de la persistencia
Una aplicación crea las consultas dinámicas en el tiempo de ejecución mediante el siguiente
proceso:
1.
Cree una cadena que contenga una consulta JPQL.
2.
Envíe la cadena al método createQuery del administrador de entidades y almacene el
objeto Query devuelto.
3.
Use el método getResultList() de la consulta para ejecutar la consulta y devolver las
filas seleccionadas de la base de datos.
...
String simpleQuery="SELECT e from Employee e";
Query query=entityManager.createQuery(simpleQuery);
List<Employee> persons = query.getResultList();
...
La consulta recupera todos los empleados de una tabla de base de datos.
1,
2,
3,
4,
5,
6,
7,
Tim, 120000.0
Joe, 100000.0
Tom, 125000.0
Mia, 135000.0
Matt, 145000.0
Kim, 115000.0
Nita, 117000.0
Las funciones de la base de datos como LOWER, UPPER, LENGTH, así como las funciones
aritméticas, también se pueden aplicar a las consultas JPQL:
...
public List<String> getAllNames(){
Query query=entityManager.createQuery("SELECT UPPER(e.empName) from Employee e");
List<String> names = query.getResultList();
return names;
}
Este código muestra todos los nombres Employee en mayúsculas:
Output:
TIM
JOE
TOM
MIA
MATT
KIM
NITA
Para brindar seguridad de tipos, JPA también admite la clase TypedQuery<?>, que permite el
tipo estático de consultas a fin de evitar problemas con la difusión de resultados. Para crear
una TypedQuery, envíe la clase que debe coincidir con el tipo de resultados de consulta al
método createQuery.
En el siguiente ejemplo se muestra una TypedQuery con seguridad de tipos:
186
JB183-EAP7.0-es-2-20180124
Creación de consultas
TypedQuery<Employee> query=entityManager.createQuery("SELECT e from Employee e where
e.salary >?1 or e.empName=?2", Employee.class);
JPQL también admite funciones aritméticas en las consultas. El siguiente es un ejemplo para
obtener la suma y un promedio de los salarios de todos los empleados:
...
public String[] getSumAndAvgSalary(){
Query query=entityManager.createQuery("SELECT sum(e.salary), round(avg(e.salary),2) from
Employee e");
Object[] sal =(Object[])query.getSingleResult();
String[] s= {"Sum of all salaries :"+sal[0],"avg of all salaries : "+sal[1]};
return s;
}
Este código genera la siguiente salida en función del conjunto de datos proporcionado:
Output:
Sum of all salaries: 857000.0
Average of all salaries: 122428.57
Observe que, en el ejemplo anterior, la consulta contiene dos campos: uno para la suma de
los salarios y el otro para el salario promedio. Cuando se devuelven varios campos como
resultado de la consulta, el método getSingleResult devuelve un arreglo Object.
Los resultados de las consultas se filtran mediante la cláusula WHERE en las consultas. La
cláusula WHERE se utiliza para definir las condiciones de los datos que la consulta devuelve.
Para crear una expresión condicional para la consulta, se pueden usar varios operadores.
Los operadores disponibles en SQL también están disponibles en JPQL. Los operandos en la
condición dependen de la expresión. Los operadores más comunes son los siguientes:
• <, =, >, <=, >=, <> se utilizan para comparar los valores aritméticos.
• IN y NOT IN se utilizan para todos los tipos. El operador IN se utiliza para determinar si los
datos de un campo son uno de los valores proporcionados en una lista de valores.
• LIKE y NOT LIKE se utilizan para los valores de cadena. Se utiliza para determinar si
los datos de un campo coinciden con una secuencia de caracteres proporcionados en la
cadena.
• BETWEEN y NOT BETWEEN se utilizan para valores aritméticos, fecha, hora y cadena.
Se utiliza para determinar si los datos de un campo residen en un rango de valores
predeterminado.
• MEMBER OF, NOT MEMBER OF, IS EMPTY e IS NOT EMPTY se utilizan para los tipos
Collection.
Puede usar caracteres especiales como _ (guión bajo) para cualquier carácter único y un
carácter % para cualquier secuencia de caracteres para compilar las expresiones de cadena.
A continuación se muestra un ejemplo simple de una consulta con una cláusula WHERE para
imprimir todos los empleados cuyo salario es superior a 120000:
Query query=entityManager.createQuery("SELECT e from Employee e where e.salary >120000");
JB183-EAP7.0-es-2-20180124
187
Capítulo 4. Gestión de la persistencia
List<Employee> persons = query.getResultList();
Este código produce la siguiente salida en función del conjunto de datos proporcionado:
Output:
3, Tom, 125000.0
4, Mia, 135000.0
5, Matt, 145000.0
Parámetros nombrados en consultas
Se pueden crear consultas con la cláusula WHERE con los parámetros nombrados en JPQL.
Un parámetro nombrado es un parámetro de consulta que funciona como marcador para
los valores reales. Una consulta se puede reutilizar y ejecutar con un conjunto diferente de
datos proporcionado en el tiempo de ejecución para un parámetro nombrado. Un parámetro
nombrado está prefijado con un carácter :.
Un parámetro nombrado se vincula a los argumentos mediante el uso del método
setParameter() de la API javax.persistence.Query. El primer parámetro del método
setParameter() es el nombre de un parámetro nombrado. El segundo parámetro es
el valor del parámetro nombrado. Un ejemplo de una consulta JPQL con un parámetro
nombrado es el siguiente:
...
public List<Employee> getEmployeesWithGreaterSalary(double salary) {
Query query=entityManager.createQuery("SELECT e from Employee e where e.salary >:sal");
query.setParameter("sal", salary);
List<Employee> persons = query.getResultList();
return persons;
}
Cuando un usuario ingresa el valor 115000 para el salario, el código devuelve la siguiente
salida:
Output:
1,
3,
4,
5,
7,
Tim, 120000.0
Tom, 125000.0
Mia, 135000.0
Matt, 145000.0
Nita, 117000.0
Parámetros posicionales en consultas
Los parámetros posicionales son parámetros de consulta en la forma de un índice o la posición
ordinal de un parámetro en la consulta. Se pueden enviar a consultas como una alternativa
a los parámetros nombrados, según la preferencia del desarrollador para la legibilidad. Los
parámetros posicionales tienen el prefijo ? seguido de la posición numérica del parámetro en
la consulta.
El método setParameter() se utiliza para vincular un parámetro posicional con una
consulta. Los valores de un parámetro posicional se brindan en el tiempo de ejecución.
El primer parámetro del método setParameter() es la posición de un parámetro en la
188
JB183-EAP7.0-es-2-20180124
Consultas nombradas
consulta y el segundo parámetro es una variable que contiene el valor del parámetro. A
continuación se muestra un ejemplo de una consulta JPQL con un parámetro posicional
donde el valor del salario para la consulta se proporciona en el primer parámetro posicional
como ?1:
...
public List<Employee> getAllPersonsWithPositionParam(double salary) {
Query query=entityManager.createQuery("SELECT e from Employee e where e.salary >?1");
query.setParameter(1, salary);
return query.getResultList();
}
Cuando el usuario introduce el valor 130000 para el salario, el código produce la siguiente
salida según el conjunto de datos proporcionado:
Output:
4, Mia, 135000.0
5, Matt, 145000.0
A continuación se muestra un ejemplo de una consulta con seguridad de tipos con dos
parámetros posicionales, donde el primer parámetro posicional ?1 hace referencia al salario
y el segundo parámetro posicional ?2 hace referencia al nombre de la consulta:
...
public List<Employee> getAllPersons(double salary, String name) {
TypedQuery<Employee> query=entityManager.createQuery("SELECT e from Employee e where
e.salary >?1 or e.empName=?2", Employee.class);
query.setParameter(1, salary);
query.setParameter(2, name);
return query.getResultList();
}
Cuando el usuario introduce los valores 130000 para el salario y Tim para el nombre, el
código produce la siguiente salida:
Output:
1, Tim, 120000.0
4, Mia, 135000.0
5, Matt, 145000.0
Consultas nombradas
Una consulta nombrada es una consulta predefinida que está adjunta a una entidad. Las
consultas nombradas se analizan en el inicio para que los errores se puedan detectar
rápidamente. Otra ventaja es que el código y las consultas están separadas. Las consultas
nombradas se definen al utilizar la anotación javax.persistence.NamedQuery. La
anotación @namedQuery se puede aplicar en el nivel de la clase de la entidad. La anotación
@NamedQuery tiene cuatro elementos: name, query, hints y lockMode.
• name es un elemento requerido de la anotación NamedQuery. Define el nombre utilizado
por los métodos EntityManager para hacer referencia a una consulta. El nombre de la
consulta nombrada debe ser único, ya que el alcance de la consulta nombrada es la unidad
de persistencia.
JB183-EAP7.0-es-2-20180124
189
Capítulo 4. Gestión de la persistencia
• query es un elemento requerido de la anotación NamedQuery y representa la cadena de la
consulta JPQL.
• El elemento hints es un elemento opcional de la anotación NamedQuery. Representa
sugerencias y propiedades de la consulta. Estas sugerencias pueden ser específicas del
proveedor.
• El elemento lockMode es un elemento opcional de la anotación NamedQuery. Representa
el tipo de modo de bloqueo para usar en la ejecución de la consulta. Cuando el tipo de
modo de bloqueo está definido como cualquier valor diferente a NONE, la consulta se debe
ejecutar en una transacción.
La consulta nombrada para ver todos los empleados con un salario superior al valor
ingresado por el usuario se define en la clase de la entidad Employee:
@Entity
@NamedQuery( name="getAllEmployees", query="select e from Employee e where e.salary > :sal")
public class Employee implements Serializable{
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String empName;
private double salary;
...
Para ejecutar la consulta nombrada, use el método createNamedQuery() de
EntityManager para crear la consulta y definir los parámetros:
...
public List<Employee> getPersonsWithNamedQuery(double salary) {
Query query=entityManager.createNamedQuery("getAllEmployees")
query.setParameter("sal", salary);
retrun query.getResultList();
}
Para definir más de una consulta nombrada para una entidad, se utiliza la anotación
@NamedQueries. Actúa como un contenedor para varias consultas; la anotación
@NamedQueries se aplica en el nivel de la clase de la entidad.
@Entity
@NamedQueries({
@NamedQuery(name="getAllEmployees",
query="select e from Employee e where e.salary > :sal"),
@NamedQuery(name="getEmployeesWithSalaryOrName",
query="select e from Employee e where e.salary > :sal or e.empName=:name")
})
public class Employee implements Serializable{
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String empName;
private double salary;
....
190
JB183-EAP7.0-es-2-20180124
Consultas nombradas
El método createNamedQuery() crea la consulta. Los parámetros se definen de la siguiente
manera:
public List<Employee> getAllPersonsWithNamedQueries(double salary, String ename) {
Query query=entityManager.createNamedQuery("getEmployeesWithSalaryOrName");
query.setParameter("sal", salary);
query.setParameter("name", ename);
List<Employee> persons = query.getResultList();
return persons;
}
JB183-EAP7.0-es-2-20180124
191
Capítulo 4. Gestión de la persistencia
Ejercicio guiado: Creación de consultas
En este ejercicio, creará consultas con parámetros nombrados y parámetros posicionales, así
como una consulta nombrada para recuperar datos de la base de datos.
Resultados
Deberá poder crear consultas nombradas y crear consultas con parámetros nombrados y
posicionales.
Antes de comenzar
Abra una ventana de terminal en la máquina virtual workstation y ejecute el siguiente
comando para descargar los archivos del trabajo de laboratorio necesarios para este ejercicio
guiado.
[student@workstation ~]$ lab create-queries setup
Pasos
1. Importe el proyecto create-queries en el IDE de JBoss Developer Studio (JBDS).
1.1. Inicie JBDS haciendo doble clic en el icono del escritorio de la máquina virtual
workstation.
1.2. En la ventana Eclipse Launcher, ingrese /home/student/JB183/workspace en el
campo Workspace (Espacio de trabajo) y, luego, haga clic en Launch (Iniciar).
1.3. En el menú de JBDS, haga clic en File (Archivo) > Import (Importar)para abrir el
asistente para Import (Importar).
1.4. En la página Select (Seleccionar), haga clic en Maven > Existing Maven Projects
(Proyectos de Maven existentes) y, luego, haga clic en Next (Siguiente).
1.5. En la página Maven projects (Proyectos de Maven), haga clic en Browse (Explorar)
para abrir la ventana Select root folder (Seleccionar carpeta raíz). Diríjase al
directorio /home/student/JB183/labs/. Seleccione la carpeta create-queries y
haga clic en OK (Aceptar).
1.6. En la página Maven projects (Proyectos de Maven), haga clic en Finish (Finalizar).
1.7. Observe la barra de estado de JBDS para monitorear el progreso de la operación
de importación. Es probable que la descarga de todas las dependencias requeridas
demore unos minutos.
2.
Agregue un método getAllPersons() en la clase PersonService para ver todas las
personas de la tabla de la base de datos Person.
2.1. En la pestaña Project Explorer (Explorador de proyectos) del panel izquierdo
de JBDS, seleccione create-queries > Java Resources > src/main/java >
com.redhat.training.services y expanda el paquete.
2.2. Haga doble clic en el archivo PersonService.java del paquete
com.redhat.training.services para abrir la clase PersonService en el panel del
editor.
192
JB183-EAP7.0-es-2-20180124
2.3. Para ver los datos de todas las personas en una tabla de base de datos, agregue un
nuevo método getAllPersons con una consulta simple:
// Get all Person objects in the Database
public List<Person> getAllPersons() {
TypedQuery<Person> query = entityManager.createQuery("SELECT p FROM Person p",
Person.class);
return query.getResultList();
}
2.4. Presione Ctrl+S para guardar sus cambios.
3.
En el bean Hello.java del paquete com.redhat.training.ui, elimine el comentario
del método getPersons() para agregar la funcionalidad para ver los nombres de todos
los objetos Person de la base de datos.
3.1. En la pestaña Project Explorer (Explorador de proyectos) del panel izquierdo
de JBDS, seleccione create-queries > Java Resources > src/main/java >
com.redhat.training.ui y expanda el paquete.
3.2. Haga doble clic en el archivo Hello.java del paquete com.redhat.training.ui para ver
la clase en el panel del editor.
3.3. Elimine los comentarios del método getPersons().
//View all persons in the database table
public List<Person> getPersons() {
return personService.getAllPersons();
}
3.4. Presione Ctrl+S para guardar sus cambios.
4.
Compile e implemente la aplicación.
4.1. Seleccione la pestaña Servers (Servidores) en el panel inferior de JBDS para iniciar
EAP. Haga clic con el botón derecho en el servidor Red Hat JBoss EAP 7.0 [Stopped] y
haga clic en el botón verde de inicio para iniciar el servidor.
4.2. Compile la aplicación create-queries mediante los siguientes comandos en la
ventana del terminal:
[student@workstation ~]$ cd /home/student/JB183/labs/create-queries
[student@workstation create-queries]$ mvn clean package
4.3. Implemente la aplicación create-queries mediante el siguiente comando en la
ventana del terminal:
[student@workstation create-queries]$ mvn wildfly:deploy
5.
Pruebe la aplicación para ver todas las personas.
JB183-EAP7.0-es-2-20180124
193
Capítulo 4. Gestión de la persistencia
5.1. Use un explorador web en la máquina virtual workstation para dirigirse a http://
localhost:8080/create-queries/ y acceder a la aplicación create-queries.
5.2. Haga clic en View All Users (Ver todos los usuarios) para ver una lista de los objetos
Person en la base de datos.
Figura 4.12: La lista de nombres en la base de datos
6.
Cree una consulta con parámetros nombrados para capturar todos los objetos Person
de la tabla de la base de datos que coinciden con un nombre específico.
6.1. Haga clic en el archivo PersonService.java para editar la clase PersonService en el
panel del editor.
6.2. Agregue un nuevo método getPersonsWithName(String name) con un
parámetro nombrado:
//Get persons whose name matches the name given in the query
public List<Person> getPersonsWithName(String name) {
TypedQuery<Person> query = entityManager.createQuery("SELECT p from Person p
where p.name =:pname", Person.class);
query.setParameter("pname", name);
return query.getResultList();
}
6.3. Presione Ctrl+S para guardar sus cambios.
7.
En la clase Hello.java del paquete com.redhat.training.ui, elimine el comentario
del método search() para ver los nombres que coinciden con la búsqueda del usuario.
7.1. Haga doble clic en el archivo Hello.java del paquete com.redhat.training.ui para ver
la clase en el panel del editor.
7.2. Elimine los comentarios del método search().
//view all persons whose name matches the name given in the query
public void search() {
results = personService.getPersonsWithName(name);
}
7.3. Presione Ctrl+S para guardar sus cambios.
194
JB183-EAP7.0-es-2-20180124
8.
Implemente la aplicación create-queries mediante el siguiente comando en la ventana
del terminal:
[student@workstation create-queries]$ mvn wildfly:deploy
9.
Pruebe la característica de búsqueda de la aplicación para verificar que la consulta
devuelva objetos Person por nombre.
9.1. Use un explorador web en la máquina virtual workstation para dirigirse a http://
localhost:8080/create-queries/ y acceder a la aplicación create-queries.
9.2. Haga clic en Search Users (Buscar usuarios) para ir a la página de búsqueda.
9.3. En el campo Name (Nombre), ingrese un nombre que ya existe en la base de datos,
como John, y haga clic en Submit (Enviar). Observe que la consulta devuelve ambas
entradas para John.
10. Modifique la consulta existente para usar parámetros posicionales para capturar objetos
Person de la tabla de la base de datos.
10.1.Haga clic en el archivo PersonService.java para editar la clase PersonService en el
panel del editor.
10.2.Actualice el método getPersonsWithName() en la clase PersonService para
utilizar parámetros posicionales:
//Get persons whose name matches the name given in the query
public List<Person> getPersonsWithName(String name) {
TypedQuery<Person> query = entityManager.createQuery("SELECT p from Person p
where p.name =?1", Person.class);
query.setParameter(1, name);
return query.getResultList();
}
10.3.Presione Ctrl+S para guardar sus cambios.
11. Implemente la aplicación create-queries mediante el siguiente comando en la ventana
del terminal:
[student@workstation create-queries]$ mvn wildfly:deploy
12. Pruebe la característica de búsqueda que usa una consulta de parámetro posicional.
12.1.Use un explorador web en la máquina virtual workstation para dirigirse a http://
localhost:8080/create-queries/ y acceder a la aplicación create-queries.
12.2.Haga clic en Search Users (Buscar usuarios) para ir a la página de búsqueda. En el
campo Name (Nombre), ingrese un nombre que ya existe en la base de datos, como
Nita, y haga clic en Submit (Enviar). Verifique que el servidor devuelva los resultados
correctos.
13. Cree una consulta nombrada para capturar objetos Person de una tabla de base de
datos.
JB183-EAP7.0-es-2-20180124
195
Capítulo 4. Gestión de la persistencia
13.1.En la pestaña Project Explorer (Explorador de proyectos) del panel izquierdo
de JBDS, seleccione create-queries > Java Resources > src/main/java >
com.redhat.training.model y expanda el paquete.
13.2.Haga doble clic en el archivo Person.java del paquete com.redhat.training.model
para abrir la clase Person en el panel del editor.
13.3.Haga clic en el archivo Person.java para editar la clase Person en el panel del editor.
13.4.Agregue la siguiente anotación NamedQuery para crear una consulta nombrada para
capturar los objetos Person por nombre:
@Entity
//add named query here
@NamedQuery(
name="getAllPersonsWithName",
query="select p from Person p where p.name = :pname")
public class Person{
@Id
private Long id;
private String name;
...
}
13.5.Presione Ctrl+S para guardar sus cambios.
13.6.En la clase PersonService.java, actualice el método getPersonsWithName para
utilizar la consulta nombrada getAllPersonsWithName:
//Get persons whose name matches the name given in the query
public List<Person> getPersonsWithName(String name) {
TypedQuery
query=entityManager.createNamedQuery("getAllPersonsWithName",Person.class);
query.setParameter("pname", name);
return query.getResultList();
}
13.7.Presione Ctrl+S para guardar sus cambios.
14. Implemente la aplicación create-queries mediante el siguiente comando en la ventana
del terminal:
[student@workstation create-queries]$ mvn wildfly:deploy
15. Pruebe la característica de búsqueda de la aplicación mediante una consulta nombrada.
15.1.Use un explorador web en la máquina virtual workstation para navegar a http://
localhost:8080/create-queries/ y acceder a la aplicación create-queries.
15.2.Haga clic en Search Users (Buscar usuarios) para ir a la página de búsqueda. En el
campo Name (Nombre), ingrese un nombre que ya existe en la base de datos, como
196
JB183-EAP7.0-es-2-20180124
John, y haga clic en Submit (Enviar). Verifique que el servidor devuelva los resultados
previstos.
16. Anule la implementación de la aplicación y detenga EAP.
16.1.En la ventana del terminal donde ejecutó el comando Maven para implementar
la aplicación, ejecute el siguiente comando para anular la implementación de la
aplicación:
[student@workstation create-queries]$ mvn wildfly:undeploy
16.2.Haga clic con el botón derecho en el proyecto create-queries en Project Explorer
(Explorador de proyectos) y seleccione Close Project (Cerrar proyecto) para cerrar
este proyecto.
16.3.Haga clic con el botón derecho en Red Hat JBoss EAP 7.0 en la pestaña Servers
(Servidores) y haga clic en Stop (Detener) para detener la instancia EAP.
Esto concluye el ejercicio guiado.
JB183-EAP7.0-es-2-20180124
197
Capítulo 4. Gestión de la persistencia
Trabajo de laboratorio: Gestión de la
persistencia
En este trabajo de laboratorio, implementará persistencia en la aplicación To Do List
mediante JPA.
Resultado
Deberá poder inyectar un administrador de entidades mediante el contexto de persistencia
predeterminado y, luego, implementar la funcionalidad de persistencia mediante la API del
administrador de entidades.
Antes de comenzar
Abra una ventana de terminal en la máquina virtual workstation y ejecute el siguiente
comando para descargar los archivos necesarios para este trabajo de laboratorio.
[student@workstation ~]$ lab persistence-lab setup
1.
Abra JBDS e importe el proyecto persistence-lab ubicado en el directorio /home/
student/JB183/labs/persistence-lab.
2.
Revise el nombre JNDI asignado al trabajo de laboratorio definido para MySQLDS en el
archivo de configuración JBoss.
3.
Revise el archivo persistence.xml que define el contexto de persistencia para la
aplicación persistence-lab}.
4.
Actualice la clase del modelo Item para que sea una entidad administrada por JPA.
Marque el campo id como el campo del identificador único, así como un valor generado.
5.
Inyecte un administrador de entidades que use el contexto de persistencia
predeterminado en la clase ItemRepository.
A continuación, actualice la clase para usar el administrador de entidades para
implementar la funcionalidad del método findById para que ya no devuelva null.
Además, actualice el método findAllItems para crear una consulta mediante JPQL que
seleccione los ítems ordenados según si se completaron o no (utilizando el campo done
[listo]).
6.
Actualice la clase EJB ItemService para inyectar un administrador de entidades
asociado con el contexto de persistencia predeterminado.
A continuación, implemente los métodos register(Item item), remove(Long
id) y update(Item item) para utilizar los métodos del administrador de entidades
adecuados.
Sugerencia: Para el método de eliminación, use el método ItemRepository.findById()
para recuperar la clase Item primero; a continuación, puede eliminar la instancia mediante el
administrador de entidades.
7.
198
Inicie JBoss EAP desde dentro de JBDS.
JB183-EAP7.0-es-2-20180124
8.
Compile e implemente la aplicación en JBoss EAP con Maven.
9.
Pruebe la aplicación en un explorador.
9.1. Abra Firefox en la máquina virtual workstation y diríjase a http://
localhost:8080/persistence-lab para acceder a la aplicación.
9.2. Agregue al menos dos nuevos ítems pendientes mediante la interfaz de la aplicación
To Do List. Si la persistencia funciona correctamente, los nuevos ítems se agregan a
la lista.
9.3. Después de haber agregado correctamente los nuevos ítems, actualice su estado a
Completados. Si la consulta funciona correctamente, los ítems marcados como done
(listo) se mueven a la parte inferior de la lista.
10. Abra una nueva ventana de terminal y ejecute el siguiente comando para calificar el
trabajo de laboratorio:
[student@workstation ~]$ lab persistence-lab grade
El script de calificación debe indicar SUCCESS (CORRECTO). Si se produce una falla, revise
los errores y corríjalos hasta que vea un mensaje que indique SUCCESS (CORRECTO).
11. Realice la limpieza.
11.1.Anule la implementación de la aplicación en JBoss EAP con Maven con los siguientes
comandos:
[student@workstation ~]$ cd /home/student/JB183/labs/persistence-lab
[student@workstation persistence-lab]$ mvn wildfly:undeploy
11.2.Haga clic con el botón derecho en el proyecto persistence-lab en Project
Explorer (Explorador de proyectos) y seleccione Close Project (Cerrar proyecto) para
cerrar este proyecto.
11.3.Haga clic con el botón derecho en el servidor Red Hat JBoss EAP 7.0 en la pestaña
Servers (Servidores) de JBDS y haga clic en Stop (Detener).
Esto concluye el trabajo de laboratorio.
JB183-EAP7.0-es-2-20180124
199
Capítulo 4. Gestión de la persistencia
Solución
En este trabajo de laboratorio, implementará persistencia en la aplicación To Do List
mediante JPA.
Resultado
Deberá poder inyectar un administrador de entidades mediante el contexto de persistencia
predeterminado y, luego, implementar la funcionalidad de persistencia mediante la API del
administrador de entidades.
Antes de comenzar
Abra una ventana de terminal en la máquina virtual workstation y ejecute el siguiente
comando para descargar los archivos necesarios para este trabajo de laboratorio.
[student@workstation ~]$ lab persistence-lab setup
1.
Abra JBDS e importe el proyecto persistence-lab ubicado en el directorio /home/
student/JB183/labs/persistence-lab.
1.1. Para abrir JBDS, haga doble clic en el icono de JBoss Developer Studio en el escritorio
de la estación de trabajo. Deje el espacio de trabajo predeterminado (/home/
student/JB183/workspace) y haga clic en OK (Aceptar).
1.2. En el menú de JBDS, haga clic en File (Archivo) > Import (Importar)para abrir el
asistente para Import (Importar).
1.3. En la página Select (Seleccionar), haga clic en Maven > Existing Maven Projects
(Proyectos de Maven existentes) y, luego, haga clic en Next (Siguiente).
1.4. En la página Maven projects (Proyectos de Maven), haga clic en Browse (Explorar)
para abrir la ventana Select root folder (Seleccionar carpeta raíz). Diríjase al
directorio /home/student/JB183/labs/. Seleccione la carpeta persistence-lab
y haga clic en OK (Aceptar).
1.5. En la página Maven projects (Proyectos de Maven), haga clic en Finish (Finalizar).
1.6. Observe la barra de estado de JBDS para monitorear el progreso de la operación
de importación. Es probable que la descarga de todas las dependencias requeridas
demore unos minutos.
2.
Revise el nombre JNDI asignado al trabajo de laboratorio definido para MySQLDS en el
archivo de configuración JBoss.
2.1. Usando su editor de texto preferido, abra el archivo de configuración EAP en /opt/
eap/standalone/configuration/standalone-full.xml:
[student@workstation ~]$ less /opt/eap/standalone/configuration/\
standalone-full.xml
2.2. Navegue al subsistema urn:jboss:domain:datasources:4.0:
<subsystem xmlns="urn:jboss:domain:datasources:4.0">
<datasources>
<datasource jndi-name="java:jboss/datasources/MySQLDS" pool-name="MySQLDS">
200
JB183-EAP7.0-es-2-20180124
Solución
<connection-url>jdbc:mysql://172.25.250.10:3306/todo</connection-url>
<driver>mysql</driver>
<security>
<user-name>todo</user-name>
<password>todo</password>
</security>
</datasource>
</datasources>
</subsystem>
Observe que MySQLDS tiene una entrada JNDI de java:jboss/datasources/
MySQLDS.
2.3. Cierre el editor de texto y no guarde los cambios al archivo standalone-full.xml.
Advertencia
Pueden producirse problemas en el trabajo de laboratorio si se introducen
cambios inesperados en el archivo de configuración.
3.
Revise el archivo persistence.xml que define el contexto de persistencia para la
aplicación persistence-lab}.
Abra el archivo persistence.xml; para ello, expanda el ítem persistence-lab en la
pestaña Project Explorer (Explorador de proyectos) en el panel izquierdo de JBDS y,
luego, haga clic en persistence-lab > Java Resources > src/main/resources > META-INF
para expandirlo. Haga doble clic en el archivo persistence.xml.
<?xml version="1.0" encoding="UTF-8"?>
<persistence version="2.1"
xmlns="http://xmlns.jcp.org/xml/ns/persistence"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/persistence http://
xmlns.jcp.org/xml/ns/persistence/persistence_2_1.xsd">
<persistence-unit name="Items" transaction-type="JTA">
<jta-data-source>java:jboss/datasources/MySQLDS</jta-data-source>
<properties>
<property name="hibernate.dialect" value="org.hibernate.dialect.MySQLDialect" />
<property name="hibernate.hbm2ddl.auto" value="update" />
<property name="hibernate.show_sql" value="true" />
<property name="hibernate.format_sql" value="true" />
</properties>
</persistence-unit>
</persistence>
4.
Actualice la clase del modelo Item para que sea una entidad administrada por JPA.
Marque el campo id como el campo del identificador único, así como un valor generado.
4.1. Abra la clase Item; para ello, expanda el ítem persistence-lab en la pestaña
Project Explorer (Explorador de proyectos) en el panel izquierdo de JBDS
y, luego, haga clic en persistence-lab > Java Resources > src/main/java >
com.redhat.training.todo.model para expandirlo. Haga doble clic en el archivo Item.
JB183-EAP7.0-es-2-20180124
201
Capítulo 4. Gestión de la persistencia
4.2. Agregue la anotación @Entity en el nivel de la clase para marcar la clase Item
como una entidad administrada por JPA que se asigna a una tabla de base de datos
nombrada "Item":
//TODO mark the Item class as an entity to be managed by JPA, mapped to a table
named 'Item'
@Entity
public class Item {
4.3. Agregue las anotaciones @Id y @GeneratedValue en el campo id para marcarlo
como el identificador único para JPA.
@Entity
public class Item {
//TODO mark this field as the unique identifier for JPA as well as a generated
value
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
4.4. Guarde los cambios usando Ctrl+S.
5.
Inyecte un administrador de entidades que use el contexto de persistencia
predeterminado en la clase ItemRepository.
A continuación, actualice la clase para usar el administrador de entidades para
implementar la funcionalidad del método findById para que ya no devuelva null.
Además, actualice el método findAllItems para crear una consulta mediante JPQL que
seleccione los ítems ordenados según si se completaron o no (utilizando el campo done
[listo]).
5.1. Abra la clase ItemRepository; para ello, expanda el ítem persistence-lab en
la pestaña Project Explorer (Explorador de proyectos) en el panel izquierdo de
JBDS y, luego, haga clic en persistence-lab > Java Resources > src/main/java >
com.redhat.training.todo.data para expandirlo. Haga doble clic en el archivo
ItemRepository.
5.2. Inyecte un administrador de entidades que utilice el contexto de persistencia
predeterminado mediante la anotación @PersistenceContext:
@ApplicationScoped
public class ItemRepository {
//TODO Inject EntityManager using the default persistence context
@PersistenceContext
private EntityManager em;
5.3. Implemente el método findById mediante el método find del administrador de
entidades:
public Item findById(Long id) {
202
JB183-EAP7.0-es-2-20180124
Solución
//TODO use the entity manager to implement the findById method
return em.find(Item.class, id);
}
5.4. Actualice el método findAllItems para crear una consulta mediante JPQL que
utilice la palabra clave ORDER BY para ordenar los resultados:
public List<Item> findAllItems() {
//TODO Create a query to select all the items in order by whether or not they
are done
TypedQuery<Item> query = em.createQuery("SELECT i FROM Item i ORDER BY i.done" ,
Item.class);
return query.getResultList();
}
5.5. Guarde los cambios usando Ctrl+S.
6.
Actualice la clase EJB ItemService para inyectar un administrador de entidades
asociado con el contexto de persistencia predeterminado.
A continuación, implemente los métodos register(Item item), remove(Long
id) y update(Item item) para utilizar los métodos del administrador de entidades
adecuados.
Sugerencia: Para el método de eliminación, use el método ItemRepository.findById()
para recuperar la clase Item primero; a continuación, puede eliminar la instancia mediante el
administrador de entidades.
6.1. Abra la clase ItemService; para ello, expanda el ítem persistence-lab en la
pestaña Project Explorer (Explorador de proyectos) en el panel izquierdo de
JBDS y, luego, haga clic en persistence-lab > Java Resources > src/main/java >
com.redhat.training.todo.service para expandirlo. Haga doble clic en el archivo
ItemService.
6.2. Inyecte un administrador de entidades que utilice el contexto de persistencia
predeterminado mediante la anotación @PersistenceContext:
import javax.persistence.PersistenceContext;
...
@Stateless
public class ItemService {
@Inject
private Logger log;
//TODO Inject an entity manager using the default persistence context
@PersistenceContext
private EntityManager em;
6.3. Implemente el método register mediante el uso del método persist en el
administrador de entidades:
JB183-EAP7.0-es-2-20180124
203
Capítulo 4. Gestión de la persistencia
public void register(Item item) throws Exception {
log.info("Adding new task: " + item.getDescription());
//TODO persist item with the entity manager
em.persist(item);
}
6.4. Implemente el método remove mediante el uso del método findById y, luego,
envíe la clase Item que se encontró al método remove del administrador de
entidades.
public void remove(Long id) {
//TODO lookup the item by its ID then remove it using the entity manager
em.remove(repository.findById(id));
}
6.5. Implemente el método update mediante el uso del método merge en el
administrador de entidades para actualizar los ítems existentes en la base de datos:
public void update(Item item) {
//TODO update the item in the database using the entity manager
em.merge(item);
}
6.6. Guarde los cambios usando Ctrl+S.
7.
Inicie JBoss EAP desde dentro de JBDS.
Seleccione la pestaña Servers (Servidores) en JBDS. Haga clic con el botón derecho en la
entrada del servidor Red Hat JBoss EAP 7.0 [Stopped] y haga clic en la opción verde Start
(Iniciar) para iniciar el servidor. Observe la pestaña Console (Consola) de JBDS hasta que
el servidor se inicie y vea el siguiente mensaje:
INFO [org.jboss.as] (Controller Boot Thread) WFLYSRV0025: JBoss EAP 7.0.2.GA
(WildFly Core 2.1.8.Final-redhat-1) started
8.
Compile e implemente la aplicación en JBoss EAP con Maven.
Ejecute los siguientes comandos para compilar e implementar la aplicación:
[student@workstation ~]$ cd /home/student/JB183/labs/persistence-lab
[student@workstation persistence-lab]$ mvn clean wildfly:deploy
Si la compilación es correcta, se mostrará la siguiente salida:
[student@workstation persistence-lab]$ mvn clean wildfly:deploy
...
[INFO] [INFO] <<< wildfly-maven-plugin:1.0.2.Final:deploy (default-cli) < package @
persistence-lab <<<
...
[INFO] --- maven-war-plugin:2.1.1:war (default-war) @ persistence-lab --...
[INFO] --- wildfly-maven-plugin:1.0.2.Final:deploy (default-cli) @ persistence-lab
204
JB183-EAP7.0-es-2-20180124
Solución
[INFO] -----------------------------------------------------------------------[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
9.
Pruebe la aplicación en un explorador.
9.1. Abra Firefox en la máquina virtual workstation y diríjase a http://
localhost:8080/persistence-lab para acceder a la aplicación.
9.2. Agregue al menos dos nuevos ítems pendientes mediante la interfaz de la aplicación
To Do List. Si la persistencia funciona correctamente, los nuevos ítems se agregan a
la lista.
9.3. Después de haber agregado correctamente los nuevos ítems, actualice su estado a
Completados. Si la consulta funciona correctamente, los ítems marcados como done
(listo) se mueven a la parte inferior de la lista.
10. Abra una nueva ventana de terminal y ejecute el siguiente comando para calificar el
trabajo de laboratorio:
[student@workstation ~]$ lab persistence-lab grade
El script de calificación debe indicar SUCCESS (CORRECTO). Si se produce una falla, revise
los errores y corríjalos hasta que vea un mensaje que indique SUCCESS (CORRECTO).
11. Realice la limpieza.
11.1.Anule la implementación de la aplicación en JBoss EAP con Maven con los siguientes
comandos:
[student@workstation ~]$ cd /home/student/JB183/labs/persistence-lab
[student@workstation persistence-lab]$ mvn wildfly:undeploy
11.2.Haga clic con el botón derecho en el proyecto persistence-lab en Project
Explorer (Explorador de proyectos) y seleccione Close Project (Cerrar proyecto) para
cerrar este proyecto.
11.3.Haga clic con el botón derecho en el servidor Red Hat JBoss EAP 7.0 en la pestaña
Servers (Servidores) de JBDS y haga clic en Stop (Detener).
Esto concluye el trabajo de laboratorio.
JB183-EAP7.0-es-2-20180124
205
Capítulo 4. Gestión de la persistencia
Resumen
En este capítulo, aprendió lo siguiente:
• La especificación de API de persistencia Java (JPA) admite la asignación relacional de
objetos y tiene tres conceptos básicos: entidades, unidad de persistencia y contexto de
persistencia.
• Una clase simple de Java se convierte en una clase de entidad mediante una anotación
@Entity.
• Una clase de entidad debe especificar una clave primaria mediante la anotación @Id.
• EntityManager realiza las operaciones CRUD (Crear, leer, actualizar y eliminar) mediante
los métodos persist(), find(), update() y delete(). Un objeto de EntityManager se
puede inyectar en un EJB mediante la anotación @Inject.
• Una unidad de persistencia contiene información acerca de una fuente de datos, el tipo
de transacción y el nombre de la unidad de persistencia. Se configura en un archivo
persistence.xml.
• La API de validación de beans configura restricciones de validación con anotaciones.
Las restricciones incorporadas están disponibles para su validación en el paquete
javax.validation.constraints.
• JPQL es un lenguaje de consulta que admite consultas dinámicas y estáticas. Las consultas
se pueden crear con parámetros nombrados y parámetros posicionales.
• Las consultas nombradas se definen en el nivel de la clase.
206
JB183-EAP7.0-es-2-20180124
TRAINING
CAPÍTULO 5
ADMINISTRACIÓN DE
RELACIONES ENTRE ENTIDADES
Descripción general
Meta
Definir y administrar relaciones entre entidades JPA.
Objetivos
• Configurar relaciones de una entidad con otra entidad y
de una entidad con varias entidades.
• Describir relaciones de varias entidades con varias
entidades.
Secciones
• Configuración de relaciones entre entidades (y ejercicio
guiado)
• Descripción de relaciones de varias entidades con varias
entidades (y cuestionario)
Trabajo de laboratorio
JB183-EAP7.0-es-2-20180124
Administración de relaciones entre entidades
207
Capítulo 5. Administración de relaciones entre entidades
Configuración de relaciones entre entidades
Objetivo
Después de completar esta sección, los estudiantes deben poder configurar relaciones entre
entidades de uno a uno y de uno a muchos.
Comprensión de las relaciones entre entidades
Cuando compilan aplicaciones empresariales, los desarrolladores usan bases de datos
relacionales para almacenar datos de negocio creados y actualizados con la aplicación. Por
lo general, los datos de la aplicación abarcan varias tablas de base de datos, por lo que es
común que los datos de una tabla deban hacer referencia a datos en otra.
Observe el siguiente ejemplo: una base de datos que almacena datos de pedidos del cliente
debe tener tres tablas, una para los clientes, otra para los ítems y una para los pedidos del
cliente. La tabla de pedidos necesita hacer referencia al registro del cliente, así como al
registro de un ítem.
Para representar estas relaciones, las bases de datos usan lo que se denomina una clave
externa. Una clave externa es una columna donde el valor de los datos de la columna es
una referencia al ID o a la clave primaria de una fila de otra tabla. Cuando un cliente carga
el registro del pedido, los datos que están en la columna de la clave externa se utilizan para
recuperar la información del cliente, así como la información del ítem. Dado que las claves
primarias hacen referencia a los datos directamente en las tablas del cliente y ítem, no hay
necesidad de duplicar esos datos en la tabla del pedido.
Cuando representan tablas de base de datos en Java EE, los desarrolladores usan beans de
entidad, uno para cada tabla. Para crear una relación entre las dos entidades, las variables del
nivel de la clase se utilizan para representar una instancia de una entidad como un atributo
de otra entidad.
En el siguiente ejemplo se muestran beans de entidad Java de muestra para los datos del
pedido del cliente que se describieron anteriormente:
Entidad de cliente:
@Entity
public class Customer {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String name;
private String email;
...
Entidad de ítem:
@Entity
208
JB183-EAP7.0-es-2-20180124
Comprensión de las relaciones entre entidades
public class Item {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String name;
private String description;
private Double price;
...
Entidad de pedido:
@Entity
public class Item {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private Customer customer;
private Item item;
...
Observe que las clases Customer (Cliente) e Item (Ítem) se utilizan como atributos de la clase
Order (Pedido). Esto, desde la perspectiva de la clase de Java, representa la relación entre
esas entidades; cada Order tiene una clase Customer y una clase Item.
Después de que se representan adecuadamente las relaciones entre entidades mediante
variables del nivel de la clase en los beans de entidad, los desarrolladores usan anotaciones
para controlar JPA y asignar correctamente esas entidades y sus relaciones cuando recuperan
datos de la base de datos. En la siguiente tabla se resumen estas anotaciones, que se cubren
en profundidad con ejemplos más adelante en el capítulo:
Anotaciones de relaciones JPA estándares
Anotación
Descripción
@OneToOne
Define una relación entre entidades como un valor único, donde
una fila de la tabla X está relacionada con una fila de la tabla Y, y
viceversa.
@OneToMany
Define una relación entre entidades como varios valores, donde una
fila de la tabla X puede estar relacionada a una o varias filas de la
tabla Y.
@ManyToOne
Define una relación entre entidades como varios valores, donde
varias filas de la tabla X pueden estar relacionadas a una única fila
de la tabla Y.
@ManyToMany
Define una relación entre entidades como varios valores, donde
varias filas de la tabla X pueden estar relacionadas a una o varias
filas de la tabla Y.
@JoinColumn
Define la columna que JPA usa como clave externa.
JB183-EAP7.0-es-2-20180124
209
Capítulo 5. Administración de relaciones entre entidades
Utilización de una relación entre entidades de uno a
uno
Use la anotación @OneToOne cuando dos entidades se relacionan entre sí, como cuando
una instancia de una entidad solo se relaciona con una única instancia de la otra entidad.
Por ejemplo, si una aplicación almacena información del usuario, pero coloca información
confidencial, como un número de seguridad social, en una tabla separada, dos entidades,
como User y UserSSN se relacionan mediante una anotación @OneToOne. Mediante la
relación @OneToOne, cada usuario tiene un único SSN, y cada SSN tiene un único usuario.
En el siguiente ejemplo se demuestran beans de entidad Java para dicho escenario:
SQL creará las tablas para estas entidades:
CREATE TABLE `UserSSN` (
`id` BIGINT not null auto_increment primary key,
`socialSecurityNumber` VARCHAR(25)
);
CREATE TABLE `User` (
`id` BIGINT not null auto_increment primary key,
`username` VARCHAR(25),
`userSSNID` BIGINT,
UNIQUE (`userSSNID`),
UNIQUE (`username`),
FOREIGN KEY (`userSSNID`) REFERENCES UserSSN(`id`) ON DELETE CASCADE
);
Datos de muestra:
MariaDB [todo]> select * from UserSSN;
+----+-----------------------+
| id | socialSecurityNumber |
+----+-----------------------+
| 1 | aaa-aa-aaaa
|
| 2 | bbb-bb-bbbb
|
| 3 | ccc-cc-cccc
|
| 4 | ddd-dd-dddd
|
+----+----------------------+
MariaDB [todo]> select * from User;
+----+----------+-----------+
| id | username | userSSNID |
+----+----------+-----------+
| 1 | usera
|
1 |
| 2 | userb
|
2 |
| 3 | userc
|
3 |
| 4 | userd
|
4 |
+----+----------+-----------+
Entidad User:
@Entity
public class User {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
210
JB183-EAP7.0-es-2-20180124
Utilización de una relación entre entidades de uno a muchos
private String username;
@OneToOne(optional=false)
@JoinColumn(name="userSSNID")
private UserSSN userSSN;
La anotación @OneToOne le indica a JPA que una única instancia de UserSSN está
relacionada con cada instancia de User. La opción optional le indica a JPA que este
campo es obligatorio, y se genera un error si la base de datos contiene una fila sin un
valor para esta columna.
La anotación @JoinColumn le indica a JPA la columna de la tabla User que debe usar
cuando busca la fila UserSSN; en este caso, se usa la columna nombrada userSSNID,
que es la clave externa configurada en la base de datos.
Entidad de UserSSN:
@Entity
public class UserSSN {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String socialSecurityNumber;
@OneToOne(optional=false, mappedBy="userSSN")
private User user;
La anotación @OneToOne le indica a JPA que una única instancia de User está
relacionada con cada instancia de UserSSN. Se usa la opción mappedBy debido a que el
registro UserSSN no tiene una referencia directa a User; en cambio, usa el nombre del
campo userSSN en la clase User.
El resultado de asignar esta relación en las entidades es que cuando se recupera un User de
la base de datos, JPA usa automáticamente un SQL JOIN para conectar la tabla User con la
tabla UserSSN y completa el objeto UserSSN de la instancia User cuando se devuelve a la
aplicación.
Utilización de una relación entre entidades de uno a
muchos
Use las anotaciones @OneToOne y @ManyToOne para asignar una relación JPA cada vez
que dos entidades se relacionen entre sí, como cuando una instancia de una entidad se
relaciona con varias instancias de la otra entidad. Un ejemplo de esto es una base de datos
utilizada para almacenar información del usuario, colocando cada usuario en un único grupo.
Para esto, se usan dos entidades: una entidad User y una entidad UserGroup. Estas dos
se relacionan con las anotaciones @OneToMany y @ManyToOne, lo que implica que varios
usuarios pertenecen a un grupo, y un grupo puede estar asignado a varios usuarios.
En el siguiente ejemplo se muestran beans de entidad Java para dicho escenario de grupo:
SQL creará las tablas para estas entidades:
CREATE TABLE `UserGroup` (
JB183-EAP7.0-es-2-20180124
211
Capítulo 5. Administración de relaciones entre entidades
`id` BIGINT not null auto_increment primary key,
`name` VARCHAR(25)
);
CREATE TABLE `User` (
`id` BIGINT not null auto_increment primary key,
`groupID` BIGINT,
`username` VARCHAR(25),
UNIQUE (`username`),
FOREIGN KEY (`groupID`) REFERENCES UserGroup(`id`)
);
Datos de muestra:
MariaDB [todo]> select * from UserGroup;
+----+----------------------+
| id | name
|
+----+----------------------+
| 1 | Group A
|
| 2 | Group B
|
| 3 | Group C
|
| 4 | Group D
|
+----+----------------------+
MariaDB [todo]> select * from User;
+----+----------+-----------+
| id | username | groupID |
+----+----------+-----------+
| 1 | usera
|
1 |
| 2 | userb
|
2 |
| 3 | userc
|
3 |
| 4 | userd
|
4 |
+----+----------+-----------+
Entidad User:
@Entity
public class User {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@ManyToOne
@JoinColumn(name="groupID")
private UserGroup userGroup;
La anotación @ManyToOne le indica a JPA que una única instancia de UserGroup está
relacionada con varias instancias de User.
La anotación @JoinColumn le indica a JPA la columna que debe usar cuando se une a la
tabla UserGroup; en este ejemplo, groupID.
Entidad UserGroup:
@Entity
public class UserGroup {
@Id
212
JB183-EAP7.0-es-2-20180124
Ajuste del rendimiento de la carga de datos de relaciones
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String name;
@OneToMany(mappedBy="userGroup")
private Set<User> users;
La anotación @OneToMany le indica a JPA que varias instancias de User pueden
pertenecer a una única instancia de UserGroup. La variable de miembro de la entidad
UserGroup es Set<User>, que permite almacenar varias instancias de User. Se usa la
opción mappedBy debido a que la tabla UserGroup no contiene una referencia a User,
por lo que JPA necesita usar el atributo userGroup en la entidad User para realizar la
asignación cuando crea la consulta.
Ajuste del rendimiento de la carga de datos de
relaciones
Cada relación asignada con anotaciones JPA de la relación requiere un procesamiento
adicional para completar los datos relacionales. Aunque, por lo general, esto no es un
problema para los conjuntos de datos pequeños o los esquemas de base de datos simples,
se vuelve complejo si los desarrolladores no son cuidadosos. Agrupar relaciones o esquemas
de base de datos complejas con muchas relaciones puede ser particularmente perjudicial
para el rendimiento, ya que a veces se deben ejecutar varias consultas para recuperar datos
necesarios o se deben usar enlaces complejos. También es muy fácil usar JPA para recuperar
más datos de lo previsto, especialmente si cada relación está asignada bidireccionalmente en
ambas entidades.
Considere un escenario donde una aplicación almacena datos de automóviles. Hay entidades
para Make (Marca), Model (Modelo), SubModel (Submodelo) y Car (Automóvil). Cada una de
estas entidades tiene una relación con la otra. Para cada Make, hay muchas instancias de
Model, y para cada Model hay varias instancias de SubModel. Además, la entidad Car tiene
relaciones @ManyToOne con cada una de estas entidades.
Entidad de marca:
@Entity
public class Make {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String name;
@OneToMany(mappedBy="make")
private Set<Model> models;
Entidad de modelo:
@Entity
public class Model {
@Id
JB183-EAP7.0-es-2-20180124
213
Capítulo 5. Administración de relaciones entre entidades
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String name;
@ManyToOne
@JoinColumn(name="makeID")
private Make make;
@OneToMany(mappedBy="model")
private Set<SubModels> submodels;
Entidad de submodelo:
@Entity
public class SubModel {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String name;
@ManyToOne
@JoinColumn(name="modelID")
private Model model;
Entidad de automóvil:
@Entity
public class Car {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@ManyToOne
@JoinColumn(name="makeID")
private Make make;
@ManyToOne
@JoinColumn(name="modelID")
private Model model;
@ManyToOne
@JoinColumn(name="subModelID")
private SubModel subModel;
private Long year;
Si todas las relaciones entre estas entidades se asignan mediante anotaciones JPA, cada
vez que se recupera un Car de la base de datos, también se recuperan Make, Model y
SubModel mediante enlaces. Sin embargo, debido a que cada una de estas entidades tiene
sus interrelaciones asignadas, también usan las anotaciones JPA para completar las variables
de sus miembros. Esto significa que SubModel recupera sus instancias de Model, luego
Model recupera sus instancias de Make y todas sus instancias de SubModel, y Make recupera
sus instancias de Model. Después de que JPA carga todos estos datos en la memoria, se
recupera la mayoría de los datos de la base de datos. Esto dificulta el rendimiento de la
aplicación con un conjunto grande de datos.
214
JB183-EAP7.0-es-2-20180124
Utilización de carga diferida (lazy loading) para mejorar el rendimiento de JPA
Utilización de carga diferida (lazy loading) para
mejorar el rendimiento de JPA
Incluso si una entidad tiene una relación asignada a otra entidad con anotaciones JPA, no
siempre es necesario recuperar esa entidad relacionada cuando carga los datos para la
primera entidad de la base de datos. Esto depende de la lógica de negocio que procesa
la instancia de la entidad que se está cargando. Por ejemplo, diferentes pantallas en una
aplicación pueden requerir que se muestren diferentes cantidades de información o tener
diferentes requisitos de rendimiento.
En el ejemplo de la entidad Car que se describió anteriormente, cargar un Model específico
cuando se recupera un Car es probablemente necesario para mostrar un automóvil
específico en la pantalla, pero cargar todas las instancias de SubModel para ese Model
probablemente no es necesario para mostrar un automóvil específico en la pantalla.
Para mitigar este problema y mejorar el rendimiento de la carga de entidades, JPA
proporciona una funcionalidad denominada carga diferida (lazy loading) o captura diferida (lazy
fetching). Con la carga diferida (lazy loading), las entidades pueden asignar relaciones, pero
cargar solo aquellas relaciones cuando sea necesario.
El tipo de captura es el comportamiento que marca si JPA carga o no entidades relacionadas.
Se pueden utilizar dos tipos de capturas: diferida (lazy) o diligente (eager). La captura diligente
está configurada de forma predeterminada, pero también se puede definir de manera
explícita. Para habilitar la carga diferida (lazy loading), los desarrolladores deben definir el
valor FetchType en la anotación de la relación JPA.
En el siguiente ejemplo se muestra una relación cargada de manera diferida:
@Entity
public class Make {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String name;
@OneToMany(mappedBy="make", fetch=FetchType.LAZY)
private Set<Model> models;
Después de agregar la carga diferida (lazy loading), cuando se recupera un Make de la base
de datos, JPA no intenta cargar todas sus instancias de modelo. Si más adelante en la lógica
de la aplicación, después de que se recupera la instancia Make de la base de datos, otra lógica
de negocio intenta invocar getModels(), JPA intenta invocar automáticamente la base de
datos y completar los modelos. Si la conexión de la entidad Manager utilizada para recuperar
la instancia Make ya no está activa, se genera una LazyInitializationException debido
a que JPA ya no puede recuperar los modelos mediante la misma conexión.
JB183-EAP7.0-es-2-20180124
215
Capítulo 5. Administración de relaciones entre entidades
Uso de la cláusula Join Fetch en las consultas JPQL
para capturar de manera diligente entidades
relacionadas
La cláusula JOIN FETCH en las consultas JPQL sobrescribe el comportamiento de carga
diferida (lazy loading) en las entidades y captura de manera diligente los datos de entidad
relacionados. JOIN FETCH es útil cuando desarrolla consultas debido a que separa la gestión
del comportamiento de captura para cada consulta. Esto permite a los desarrolladores incluir
de manera segura la captura diferida (lazy fetching) de datos de entidades relacionadas de
manera predeterminada, lo que permite mejorar el rendimiento de la aplicación mediante
la captura diligente de los datos relacionados según se requiera al incluir una cláusula JOIN
FETCH en la consulta JPQL.
Para usar una cláusula JOIN FETCH, incluya el nombre de la colección asignada por JPA
que JPA debe capturar de manera diligente. Una cláusula JOIN FETCH en una consulta
sobrescribe cualquier atributo fetch especificado en la anotación JPA que asigna la relación.
Cuando se incluye JOIN FETCH, genera que JPA incluya JOIN a la tabla asignada a la entidad
relacionada en el SQL que se genera desde la consulta JPQL. El siguiente es un ejemplo de
una consulta que usa una cláusula JOIN FETCH para cargar el conjunto de objetos model
que están relacionados con los objetos make que JPA carga desde la base de datos:
TypedQuery<Make> query = em.createQuery("SELECT ma FROM Make ma JOIN FETCH ma.models
WHERE ma.id = :id" , Make.class);
Demostración: Configuración de relaciones entre
entidades
1.
Ejecute el siguiente comando para preparar archivos usados por esta demostración.
[student@workstation ~]$ demo configure-relationships setup
2.
Inicie JBDS e importe el proyecto configure-relationships.
Este proyecto es una aplicación web simple que usa una página JSF respaldada por un
EJB de alcance de solicitud con estado para mostrar los empleados de un departamento.
También utiliza CDI para inyectar el EJB responsable de consultar la base de datos.
3.
Inspeccione el archivo pom.xml y observe la dependencia en las librerías hibernate para
la especificación JPA y las clases de administrador de la entidad.
<dependency>
<groupId>org.hibernate.javax.persistence</groupId>
<artifactId>hibernate-jpa-2.1-api</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-entitymanager</artifactId>
<scope>provided</scope>
</dependency>
216
JB183-EAP7.0-es-2-20180124
Demostración: Configuración de relaciones entre entidades
4.
Abra una nueva ventana de terminal y use el cliente mysql para consultar la base de
datos todo.
[student@workstation ~]$ mysql -hservices -utodo -ptodo
Welcome to the MariaDB monitor. Commands end with ; or \g.
Your MariaDB connection id is 11
Server version: 5.5.52-MariaDB MariaDB Server
Copyright (c) 2000, 2016, Oracle, MariaDB Corporation Ab and others.
Type 'help;' or '\h' for help. Type '\c' to clear the current input statement.
MariaDB [(none)]> use todo;
Reading table information for completion of table and column names
You can turn off this feature to get a quicker startup with -A
Database changed
Recupere los datos en la tabla Department.
MariaDB [todo]> select * from Department;
+----+-----------+
| id | name
|
+----+-----------+
| 1 | Sales
|
| 2 | Marketing |
| 3 | IT
|
+----+-----------+
3 rows in set (0.00 sec)
Recupere los datos en la tabla Manager.
MariaDB [todo]> select * from Manager;
+----+------+--------------+
| id | name | departmentID |
+----+------+--------------+ |
| 1 | Bob |
1 |
| 2 | Jill |
2 |
| 3 | Sam |
3 |
+----+------+--------------+
3 rows in set (0.00 sec)
La tabla Manager tiene un relación de uno a uno con la tabla Department.
Recupere los datos en la tabla Employee.
MariaDB [todo]> select * from Employee;
+----+---------+--------------+
| id | name
| departmentID |
+----+---------+--------------+
| 1 | William |
1 |
| 2 | Rose
|
1 |
| 3 | Pat
|
1 |
| 4 | Rodney |
2 |
| 5 | Kim
|
2 |
| 6 | Tom
|
2 |
| 7 | Matt
|
3 |
JB183-EAP7.0-es-2-20180124
217
Capítulo 5. Administración de relaciones entre entidades
| 8 | George |
3 |
| 9 | Jean
|
3 |
+----+---------+--------------+
9 rows in set (0.00 sec)
La tabla Employee tiene un relación de muchos a uno con la tabla Manager.
5.
Actualice la clase de entidad Employee para incluir las anotaciones JPA necesarias.
@Entity
public class Employee {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String name;
@ManyToOne
@JoinColumn(name="departmentID")
private Department department;
6.
Actualice la clase de entidad Manager para incluir las anotaciones JPA necesarias.
@Entity
public class Manager {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String name;
@OneToOne
@JoinColumn(name="departmentID")
private Department department;
7.
Actualice la clase de entidad Department para incluir las anotaciones JPA necesarias.
Capture de manera diligente los registros de empleado de un departamento para que
la aplicación cargue de manera previa los empleados cuando recupera los registros del
departamento.
@Entity
public class Department {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String name;
@OneToOne(mappedBy="department")
private Manager manager;
@OneToMany(mappedBy="department", fetch=FetchType.EAGER)
private Set<Employee> employees;
218
JB183-EAP7.0-es-2-20180124
Demostración: Configuración de relaciones entre entidades
8.
Inicie el servidor JBoss EAP local dentro de JBDS.
En la pestaña Servers del panel inferior de JBDS, haga clic con el botón derecho en el
servidor Red Hat JBoss EAP 7.0 [Stopped] y haga clic en el botón verde para iniciar el
servidor. Observe la Console (Consola) hasta que el servidor se inicie y vea el mensaje
iniciado:
INFO [org.jboss.as] (Controller Boot Thread) WFLYSRV0025: JBoss EAP 7.0.0.GA
(WildFly Core 2.1.2.Final-redhat-1) started
9.
Implemente la aplicación en el servidor JBoss EAP local y pruébela en un explorador. En
una ventana de terminal, ejecute el siguiente comando:
[student@workstation ~]$ cd /home/student/JB183/labs/configure-relationships
[student@workstation configure-relationships]$ mvn wildfly:deploy
Abra http://localhost:8080/configure-relationships/ en su explorador y
pruebe la aplicación para asegurarse de que muestre adecuadamente los empleados y
el administrador de diferentes departamentos.
10. Anule la implementación de la aplicación y detenga el servidor.
[student@workstation configure-relationships]$ mvn wildfly:undeploy
Referencias
Para obtener más información, consulte el capítulo JPA de la Guía de desarrollo para
Red Hat JBoss EAP 7 en
https://access.redhat.com/documentation/en/red-hat-jboss-enterpriseapplication-platform/
JB183-EAP7.0-es-2-20180124
219
Capítulo 5. Administración de relaciones entre entidades
Ejercicio guiado: Configuración de relaciones
entre entidades
En este ejercicio, configurará relaciones de entidades entre varias entidades que se utilizan en
una aplicación web basada en JSF.
Resultado
Deberá ser capaz de asignar correctamente relaciones de uno a uno y de muchos a uno
mediante las anotaciones JPA necesarias.
Antes de comenzar
Abra una ventana de terminal en la máquina virtual workstation y ejecute el siguiente
comando para descargar los archivos necesarios para este trabajo de laboratorio.
[student@workstation ~]$ lab entity-relationships setup
Pasos
1. Abra JBDS e importe el proyecto de Maven.
1.1. Haga doble clic en el icono de JBoss Developer Studio en el escritorio de la estación
de trabajo para abrir JBDS. Seleccione el espacio de trabajo /home/student/
JB183/workspace y haga clic en OK (Aceptar).
1.2. En el menú de JBDS, haga clic en File (Archivo) > Import (Importar)para abrir el
asistente para Import (Importar).
1.3. En la página Select (Seleccionar), haga clic en Maven > Existing Maven Projects
(Proyectos de Maven existentes) y, luego, haga clic en Next (Siguiente).
1.4. En la página Maven projects (Proyectos de Maven), haga clic en Browse (Explorar)
para abrir la ventana Select root folder (Seleccionar carpeta raíz). Diríjase al
directorio /home/student/JB183/labs/. Seleccione la carpeta entityrelationships y haga clic en OK (Aceptar).
1.5. En la página Maven projects (Proyectos de Maven), haga clic en Finish (Finalizar).
1.6. Observe la barra de estado de JBDS para monitorear el progreso de la operación
de importación. Es probable que la descarga de todas las dependencias requeridas
demore unos minutos.
2.
Asigne las relaciones de uno a uno entre las entidades User y Email.
2.1. Abra la clase Email; para ello, expanda el ítem entity-relationships en la pestaña
Project Explorer (Explorador de proyectos) en el panel izquierdo de JBDS y,
luego, haga clic en entity-relationships > Java Resources > src/main/java >
com.redhat.training.model y expándalo. Haga doble clic en el archivo Email.java.
Use la anotación JPA @OneToOne para asignar la relación a la entidad User:
@Entity
public class Email {
220
JB183-EAP7.0-es-2-20180124
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String address;
//TODO map relationship
@OneToOne(mappedBy="email")
private User user;
Observe que se utiliza el atributo mappedBy. Esto se debe a que la columna que
impone cómo asignar registros de usuario a registros de correo electrónico está
almacenada en la tabla de usuarios y está representada por la variable email en la
clase de la entidad User.
nota
Está previsto que JBDS genere un error en la asignación de User. Este error
se soluciona en el próximo paso.
2.2. Guarde los cambios usando Ctrl+S.
2.3. Abra la clase User; para ello, expanda el ítem entity-relationships en la pestaña
Project Explorer (Explorador de proyectos) en el panel izquierdo de JBDS y,
luego, haga clic en entity-relationships > Java Resources > src/main/java >
com.redhat.training.model y expándalo. Haga doble clic en el archivo User.java.
Utilice la anotación JPA @OneToOne para asignar la relación a la entidad Email:
@Entity
public class User {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String name;
//TODO map relationship
@OneToOne
@JoinColumn(name="emailID")
private Email email;
Observe la anotación @JoinColumn. Esto le indica a JPA la columna que debe
usar cuando une la tabla User con la tabla Email. En este caso, utiliza la columna
emailID.
2.4. Guarde los cambios usando Ctrl+S.
3.
Asigne las relaciones de uno a muchos entre las entidades UserGroup y User.
3.1. Abra la clase UserGroup; para ello, expanda el ítem entity-relationships en la
pestaña Project Explorer (Explorador de proyectos) en el panel izquierdo de
JBDS y, luego, haga clic en entity-relationships > Java Resources > src/main/
JB183-EAP7.0-es-2-20180124
221
Capítulo 5. Administración de relaciones entre entidades
java > com.redhat.training.model y expándalo. Haga doble clic en el archivo
UserGroup.java.
Use la anotación JPA @OneToMany para asignar la relación a la entidad UserGroup:
@Entity
public class UserGroup {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String name;
@OneToMany(mappedBy="userGroup")
private Set<User> users;
Observe el atributo mappedBy. Esto se usa debido a que la columna que asigna
registros de usuario a registros de grupo de usuarios está almacenada en la tabla
de usuarios y está representada por la variable userGroup en la clase de la entidad
User.
nota
Está previsto que JBDS genere un error en la asignación de User. Este error
se soluciona en el próximo paso.
3.2. Guarde los cambios usando Ctrl+S.
3.3. Abra la clase User; para ello, expanda el ítem entity-relationships en la pestaña
Project Explorer (Explorador de proyectos) en el panel izquierdo de JBDS y,
luego, haga clic en entity-relationships > Java Resources > src/main/java >
com.redhat.training.model y expándalo. Haga doble clic en el archivo User.java.
Use la anotación JPA @ManyToOne para asignar la relación a la entidad UserGroup:
@Entity
public class User {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String name;
//TODO map relationship
@OneToOne
@JoinColumn(name="emailID")
private Email email;
//TODO map relationship
@ManyToOne
@JoinColumn(name="groupID")
private UserGroup userGroup;
222
JB183-EAP7.0-es-2-20180124
Observe la anotación @JoinColumn. Esto le indica a JPA la columna que debe usar
cuando une la tabla User con la tabla UserGroup. En este caso, utiliza la columna
groupID.
3.4. Guarde los cambios usando Ctrl+S.
4.
Seleccione la pestaña Servers (Servidores) en el panel inferior de JBDS para iniciar EAP.
Haga clic con el botón derecho en el servidor Red Hat JBoss EAP 7.0 [Stopped] y haga
clic en el botón verde para iniciar el servidor. Observe la Console (Consola) hasta que el
servidor se inicie y muestre el siguiente mensaje:
INFO [org.jboss.as] (Controller Boot Thread) WFLYSRV0025: JBoss EAP 7.0.0.GA
(WildFly Core 2.1.2.Final-redhat-1) started
5.
Implemente la aplicación en JBoss EAP con Maven; para ello, ejecute los siguientes
comandos:
[student@workstation ~]$ cd /home/student/JB183/labs/entity-relationships
[student@workstation entity-relationships]$ mvn wildfly:deploy
Una vez finalizado, aparece BUILD SUCCESS, como se muestra en el próximo ejemplo:
[INFO]
[INFO]
[INFO]
[INFO]
[INFO]
[INFO]
[INFO]
-----------------------------------------------------------------------BUILD SUCCESS
-----------------------------------------------------------------------Total time: 17.116 s
Finished at: 2016-12-01T07:26:55-05:00
Final Memory: 35M/210M
------------------------------------------------------------------------
Valide la implementación en el registro de servidores que se muestra en la pestaña
Console (Consola) en JBDS. Cuando se implementa la aplicación, aparece lo siguiente en
el registro:
INFO [org.jboss.as.server] (management-handler-thread - 9) WFLYSRV0010: Deployed
"entity-relationships.war" (runtime-name : "entity-relationships.war")
6.
Pruebe la aplicación en un explorador.
6.1. Abra http://localhost:8080/entity-relationships en un explorador en la
máquina virtual workstation.
JB183-EAP7.0-es-2-20180124
223
Capítulo 5. Administración de relaciones entre entidades
Figura 5.1: Página de inicio de la aplicación
6.2. Elija un grupo del menú desplegable para verlo.
La página se actualiza con un seguimiento de pila (stack) debido a una
LazyInitializationException:
Figura 5.2: Respuesta de error de la aplicación
Observe el primer mensaje de error:
org.hibernate.LazyInitializationException: failed to lazily initialize a
collection of role: com.redhat.training.model.UserGroup.users, could not
initialize proxy - no Session
Este error se produjo debido a que la página JSF intentó cargar el conjunto de
usuarios desde un grupo que estaba cargado desde la base de datos, pero los
usuarios no estaban cargados de manera diligente. Por lo tanto, la sesión JPA
ya estaba cerrada cuando la página JSF intentó la invocación de getUsers(),
provocando la LazyInitializationException.
Este error se puede corregir con la captura diligente.
7.
224
Actualice la entidad UserGroup para usar la captura diligente en este conjunto de
usuarios.
JB183-EAP7.0-es-2-20180124
7.1. Abra la clase UserGroup; para ello, expanda el ítem entity-relationships en la
pestaña Project Explorer (Explorador de proyectos) en el panel izquierdo de
JBDS y, luego, haga clic en entity-relationships > Java Resources > src/main/
java > com.redhat.training.model y expándalo. Haga doble clic en el archivo
UserGroup.java.
Agregue el atributo fetch a la anotación JPA y defina el tipo de captura como
FetchType.EAGER para cargar la relación de manera diligente en la entidad User:
@Entity
public class UserGroup {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String name;
@OneToMany(mappedBy="userGroup", fetch=FetchType.EAGER)
private Set<User> users;
7.2. Guarde los cambios usando Ctrl+S.
8.
Vuelva a implementar la aplicación en JBoss EAP con Maven; para ello, ejecute los
siguientes comandos:
[student@workstation entity-relationships]$ mvn wildfly:deploy
Una vez finalizado, verá BUILD SUCCESS como se muestra en el próximo ejemplo:
[INFO]
[INFO]
[INFO]
[INFO]
[INFO]
[INFO]
[INFO]
-----------------------------------------------------------------------BUILD SUCCESS
-----------------------------------------------------------------------Total time: 17.116 s
Finished at: 2016-12-01T07:26:55-05:00
Final Memory: 35M/210M
------------------------------------------------------------------------
Valide la implementación en el registro de servidores que se muestra en la pestaña
Console (Consola) en JBDS. Cuando su aplicación se haya implementado correctamente,
esto aparece en el registro:
INFO [org.jboss.as.server] (management-handler-thread - 9) WFLYSRV0016: Replaced
deployment "entity-relationships.war" with deployment "entity-relationships.war"
9.
Pruebe la aplicación en un explorador.
9.1. Abra http://localhost:8080/entity-relationships en un explorador en la
máquina virtual workstation.
9.2. Elija un grupo y vea sus usuarios. La aplicación muestra los usuarios y las direcciones
de correo electrónico del grupo.
JB183-EAP7.0-es-2-20180124
225
Capítulo 5. Administración de relaciones entre entidades
Figura 5.3: Respuesta correcta de la aplicación
10. Actualice la entidad UserGroup para usar la captura diferida (lazy fetching) en este
conjunto de usuarios y sobrescriba este comportamiento mediante el uso de JOIN
FETCH en la consulta.
10.1.En la clase de la entidad UserGroup de com.redhat.training.model,
actualice el atributo fetch de la anotación JPA y defina el tipo de extracción como
FetchType.LAZY para cargar de manera diferida (lazy fetching) la relación en la
entidad User:
@Entity
public class UserGroup {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String name;
@OneToMany(mappedBy="userGroup", fetch=FetchType.LAZY)
private Set<User> users;
10.2.Guarde los cambios usando Ctrl+S.
10.3.Actualice el EJB UserBean en el paquete com.redhat.training.ejb para usar
JOIN FETCH para capturar de manera diligente el conjunto de usuarios de un grupo.
@Stateless
public class UserBean {
@Inject
private EntityManager em;
public Set<UserGroup> getAllUserGroups(){
226
JB183-EAP7.0-es-2-20180124
TypedQuery<UserGroup> query = em.createQuery("SELECT g FROM UserGroup g JOIN
FETCH g.users" , UserGroup.class);
return new HashSet<UserGroup>(query.getResultList());
}
}
10.4.Guarde los cambios usando Ctrl+S.
11. Vuelva a implementar la aplicación en JBoss EAP con Maven; para ello, ejecute los
siguientes comandos:
[student@workstation entity-relationships]$ mvn wildfly:deploy
Una vez finalizado, verá BUILD SUCCESS como se muestra en el próximo ejemplo:
[INFO]
[INFO]
[INFO]
[INFO]
[INFO]
[INFO]
[INFO]
-----------------------------------------------------------------------BUILD SUCCESS
-----------------------------------------------------------------------Total time: 17.116 s
Finished at: 2016-12-01T07:26:55-05:00
Final Memory: 35M/210M
------------------------------------------------------------------------
Valide la implementación en el registro de servidores que se muestra en la pestaña
Console (Consola) en JBDS. Cuando su aplicación se haya implementado correctamente,
esto aparece en el registro:
INFO [org.jboss.as.server] (management-handler-thread - 9) WFLYSRV0016: Replaced
deployment "entity-relationships.war" with deployment "entity-relationships.war"
12. Pruebe la aplicación en un explorador.
12.1.Abra http://localhost:8080/entity-relationships en un explorador en la
máquina virtual workstation.
12.2.Elija un grupo y vea sus usuarios. La aplicación muestra los usuarios y las direcciones
de correo electrónico del grupo sin errores.
13. Anule la implementación de la aplicación y detenga JBoss EAP.
13.1.Ejecute el siguiente comando para anular la implementación de la aplicación:
[student@workstation entity-relationships]$ mvn wildfly:undeploy
13.2.Para cerrar el proyecto, haga clic con el botón derecho en el proyecto entityrelationships del Project Explorer (Explorador de proyectos) y seleccione Close
Project (Cerrar proyecto).
13.3.Haga clic con el botón derecho en el servidor Red Hat JBoss EAP 7.0 en la pestaña
Servers (Servidores) de JBDS y haga clic en Stop (Detener).
JB183-EAP7.0-es-2-20180124
227
Capítulo 5. Administración de relaciones entre entidades
Esto concluye el ejercicio guiado.
228
JB183-EAP7.0-es-2-20180124
Descripción de relaciones de varias entidades con varias entidades
Descripción de relaciones de varias
entidades con varias entidades
Objetivo
Después de completar esta sección, los estudiantes deben poder configurar relaciones de
varias entidades con varias entidades.
Comprensión de relaciones de muchos a muchos
Una relación de muchos a muchos ocurre cuando cada fila de una tabla tiene varias filas
relacionadas en otra tabla, y viceversa. Un ejemplo de este tipo de relación es la asignación
de trabajadores a proyectos. Se puede asignar cada trabajador a uno o más proyectos y cada
proyecto puede tener múltiples trabajadores. Es difícil representar este tipo de relación en
una base de datos relacional debido a que, a diferencia de la relación de uno a muchos, no se
puede utilizar una única columna en una de las tablas para representar la relación.
Para representar una relación de muchos a muchos en una base de datos relacional, se debe
usar una tabla intermediaria conocida como tabla transversal o tabla de unión. Una tabla de
unión es una tabla intermediaria donde cada fila de la tabla representa combinaciones de
dos ID, uno de cada una de las tablas.
Un ejemplo de una relación de muchos a muchos en una aplicación empresarial es una base
de datos que almacena datos de inscripción a una clase. Una tabla Student se utiliza para
almacenar registros de estudiante, y una tabla Class se utiliza para almacenar clases. Una
tabla de unión StudentXClass se utiliza para relacionar estas dos tablas y para representar
cuáles son los estudiantes que están en determinadas clases. Si utiliza estas tres tablas, es
posible encontrar los estudiantes de una determinada clase, así como las clases para un
determinado estudiante al unirse a la tabla StudentXClass.
A continuación se muestran los beans de entidad Java para la aplicación de inscripción a
clase:
SQL creará las tablas para estas entidades:
CREATE TABLE `Student` (
`id` BIGINT not null auto_increment primary key,
`name` VARCHAR(50)
);
CREATE TABLE `Class` (
`id` BIGINT not null auto_increment primary key,
`name` VARCHAR(50)
);
CREATE TABLE `StudentXClass` (
`id` BIGINT not null auto_increment primary key,
`studentID` BIGINT not null,
`classID` BIGINT not null,
FOREIGN KEY (`studentID`) REFERENCES Student(`id`),
FOREIGN KEY (`classID`) REFERENCES Class(`id`)
);
Entidad de estudiante:
JB183-EAP7.0-es-2-20180124
229
Capítulo 5. Administración de relaciones entre entidades
@Entity
public class Student {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String name;
private Set<Class> classes;
Entidad de clase:
@Entity
public class Class {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String name;
private Set<Student> students;
Observe que no hay una entidad para la tabla de unión StudentXClass. Esto se debe a
que los objetos Java no requieren un objeto intermediario para representar una relación
de muchos a muchos entre otros dos proyectos. Crear un Conjunto de cada una de las
entidades como variable de miembro de la otra, como se observa en el ejemplo anterior, es
suficiente para crear la relación de muchos a muchos entre los dos objetos.
Utilización de anotaciones JPA para asignar
relaciones de muchos a muchos
En lugar de utilizar una única JPA @JoinColumn, use una anotación @JoinTable y dos
anotaciones @JoinColumn para asignar una relación de muchos a muchos. La anotación
@JoinTable es el puente que permite que JPA use la tabla de unión para completar las
relaciones entre dos objetos, y las dos anotaciones @JoinColumn se utilizan para definir
cómo unir la tabla de unión con la tabla de entidad, así como la tabla de entidad relacionada.
Cuando especifica la anotación @JoinTable, los atributos name, joinColumns e
inverseJoinColumns son requeridos y se utilizan de la siguiente manera:
@JoinTable(
name="$JOIN_TABLE_NAME",
joinColumns=@JoinColumn(name="$JOIN_TABLE_COLUMN_NAME"),
inverseJoinColumns=@JoinColumn(name="$JOIN_TABLE_COLUMN2_NAME"))
private Set<RelatedEntity> entities;
name
El nombre de la tabla de unión que JPA utilizará cuando complete la relación.
joinColumns
La columna de la tabla de unión que se utiliza para volver a unirse a la clase de entidad.
Este atributo acepta una instancia de @JoinColumn, que está definida con un atributo
name que se asigna al nombre de la columna en la tabla de unión.
230
JB183-EAP7.0-es-2-20180124
Utilización de anotaciones JPA para asignar relaciones de muchos a muchos
inverseJoinColumns
La columna de la tabla de unión que se utiliza para unirse desde la tabla de unión a la
clase de entidad relacionada. Este atributo acepta una instancia de @JoinColumn, que
está definida con un atributo name que se asigna al nombre de la columna en la tabla de
unión.
Una última consideración cuando asigna relaciones de muchos a muchos en las entidades
JPA es que cada relación necesita únicamente su tabla de unión asignada en un lado de la
relación. Por lo general, los desarrolladores se refieren a este lado como el propietario o
lado principal de la relación. Una vez que se define un lado principal, la entidad relacionada
o entidad secundaria puede usar el atributo mappedBy en la anotación @ManyToMany para
hacer referencia a la variable en la entidad propietaria que asigna la relación.
Importante
Solo un lado de una relación JPA se puede asignar como propietaria. Si ambas
entidades tienen la tabla de unión asignada y ninguna usa mappedBy, JPA intenta
persistir filas duplicadas en la tabla de unión.
Si continuamos con el ejemplo de inscripción a clase de la sección anterior, las clases de
entidad JPA anotadas correctamente Student y Class coinciden con lo siguiente:
Entidad de estudiante:
@Entity
public class Student {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String name;
@ManyToMany
@JoinTable(
name="StudentXClass",
joinColumns=@JoinColumn(name="studentID"),
inverseJoinColumns=@JoinColumn(name="classID"))
private Set<Class> classes;
Marque esta relación como una relación de muchos a muchos con la anotación
@ManyToMany.
Especifique la tabla de unión que se debe usar cuando completa la relación con la
anotación @JoinTable.
Defina el atributo name con el nombre de la tabla de unión.
Especifique @JoinColumn que se utilizará para volver a unir la tabla de unión con la
tabla Student; en este caso, studentID.
Especifique @JoinColumn que se utilizará para volver a unir la tabla de unión con la
tabla Class relacionada; en este caso, classID. La inverseJoinColumn será siempre
una columna en la entidad relacionada.
Entidad de clase:
JB183-EAP7.0-es-2-20180124
231
Capítulo 5. Administración de relaciones entre entidades
@Entity
public class Class {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String name;
@ManyToMany(mappedBy="classes")
private Set<Student> students;
Especifique la entidad Class como la secundaria de la relación con la entidad Student;
para ello, utilice el atributo mappedBy en la anotación @ManyToMany. Haga referencia a
la variable del objeto Student que se puede usar para asignar la relación; en este caso,
classes.
Referencias
Para obtener más información, consulte el capítulo JPA de la Guía de desarrollo para
Red Hat JBoss EAP 7 en
https://access.redhat.com/documentation/en/red-hat-jboss-enterpriseapplication-platform/
232
JB183-EAP7.0-es-2-20180124
Cuestionario: Descripción de relaciones de varias entidades con varias entidades
Cuestionario: Descripción de relaciones de
varias entidades con varias entidades
Elija las respuestas correctas para las siguientes preguntas:
1.
¿Cuáles son las tres anotaciones de JPA requeridas para asignar completamente
una relación de muchos a muchos? (Elija tres opciones):
a.
b.
c.
d.
e.
2.
@JoinColumn
@ManyToOne
@JoinTable
@OneToMany
@ManyToMany
¿Qué escenario describe una relación de muchos a muchos?
a.
Datos que representan cuáles son los animales que están en determinados
zoológicos en cualquier momento dato.
Datos que representan hijos y sus madres biológicas.
Datos que representan atletas profesionales y sus equipos actuales.
Datos que representan documentos de investigación y cada diario que los ha
publicado.
b.
c.
d.
3.
SQL:
CREATE TABLE `Employee` (
`id` BIGINT not null auto_increment primary key,
`name` VARCHAR(50)
);
CREATE TABLE `Project` (
`id` BIGINT not null auto_increment primary key,
`name` VARCHAR(50)
);
CREATE TABLE `EmployeeXProject` (
`id` BIGINT not null auto_increment primary key,
`employeeID` BIGINT not null,
`projectID` BIGINT not null,
FOREIGN KEY (`employeeID`) REFERENCES Employee(`id`),
FOREIGN KEY (`projectID`) REFERENCES Project(`id`)
);
Entidad de estudiante:
@Entity
public class Employee {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
JB183-EAP7.0-es-2-20180124
233
Capítulo 5. Administración de relaciones entre entidades
private String name;
private Set<Project> projects;
Entidad de clase:
@Entity
public class Project {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String name;
private Set<Employee> employees;
Dada la estructura de las tablas de la base de datos en los ejemplos del código
anterior, ¿cuáles son las dos anotaciones JPA que se utilizarían en el Set de
instancias Project para que la entidad Employee sea la propietaria de la relación?
(Elija dos opciones).
a.
b.
@ManyToMany(mappedBy="projects")
@JoinTable(name="Project",
joinColumns=@JoinColumn(name="id"),
c.
inverseJoinColumns=@JoinColumn(name="id"))
@JoinTable(name="EmployeeXProject",
joinColumns=@JoinColumn(name="employeeID"),
d.
4.
inverseJoinColumns=@JoinColumn(name="projectID"))
@ManyToMany
Dada la estructura de las clases de entidades del ejemplo anterior, ¿que anotación
JPA debe utilizarse en el Set de instancias Employee para que la entidad Proyect
sea la secundaria de la relación?
a.
b.
@ManyToMany(mappedBy="projects")
@JoinTable(name="Project",
joinColumns=@JoinColumn(name="id"),
c.
inverseJoinColumns=@JoinColumn(name="id"))
@JoinTable(name="EmployeeXProject",
joinColumns=@JoinColumn(name="employeeID"),
d.
234
inverseJoinColumns=@JoinColumn(name="projectID"))
@ManyToMany
JB183-EAP7.0-es-2-20180124
Solución
Solución
Elija las respuestas correctas para las siguientes preguntas:
1.
¿Cuáles son las tres anotaciones de JPA requeridas para asignar completamente
una relación de muchos a muchos? (Elija tres opciones):
a.
b.
c.
d.
e.
2.
@JoinColumn
@ManyToOne
@JoinTable
@OneToMany
@ManyToMany
¿Qué escenario describe una relación de muchos a muchos?
a.
Datos que representan cuáles son los animales que están en determinados
zoológicos en cualquier momento dato.
Datos que representan hijos y sus madres biológicas.
Datos que representan atletas profesionales y sus equipos actuales.
Datos que representan documentos de investigación y cada diario que los ha
publicado.
b.
c.
d.
3.
SQL:
CREATE TABLE `Employee` (
`id` BIGINT not null auto_increment primary key,
`name` VARCHAR(50)
);
CREATE TABLE `Project` (
`id` BIGINT not null auto_increment primary key,
`name` VARCHAR(50)
);
CREATE TABLE `EmployeeXProject` (
`id` BIGINT not null auto_increment primary key,
`employeeID` BIGINT not null,
`projectID` BIGINT not null,
FOREIGN KEY (`employeeID`) REFERENCES Employee(`id`),
FOREIGN KEY (`projectID`) REFERENCES Project(`id`)
);
Entidad de estudiante:
@Entity
public class Employee {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String name;
private Set<Project> projects;
JB183-EAP7.0-es-2-20180124
235
Capítulo 5. Administración de relaciones entre entidades
Entidad de clase:
@Entity
public class Project {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String name;
private Set<Employee> employees;
Dada la estructura de las tablas de la base de datos en los ejemplos del código
anterior, ¿cuáles son las dos anotaciones JPA que se utilizarían en el Set de
instancias Project para que la entidad Employee sea la propietaria de la relación?
(Elija dos opciones).
a.
b.
@ManyToMany(mappedBy="projects")
@JoinTable(name="Project",
joinColumns=@JoinColumn(name="id"),
c.
inverseJoinColumns=@JoinColumn(name="id"))
@JoinTable(name="EmployeeXProject",
joinColumns=@JoinColumn(name="employeeID"),
d.
4.
inverseJoinColumns=@JoinColumn(name="projectID"))
@ManyToMany
Dada la estructura de las clases de entidades del ejemplo anterior, ¿que anotación
JPA debe utilizarse en el Set de instancias Employee para que la entidad Proyect
sea la secundaria de la relación?
a.
b.
@ManyToMany(mappedBy="projects")
@JoinTable(name="Project",
joinColumns=@JoinColumn(name="id"),
c.
inverseJoinColumns=@JoinColumn(name="id"))
@JoinTable(name="EmployeeXProject",
joinColumns=@JoinColumn(name="employeeID"),
d.
236
inverseJoinColumns=@JoinColumn(name="projectID"))
@ManyToMany
JB183-EAP7.0-es-2-20180124
Trabajo de laboratorio: Administración de relaciones entre entidades
Trabajo de laboratorio: Administración de
relaciones entre entidades
En este trabajo de laboratorio, actualizará las clases de entidades con las anotaciones JPA
adecuadas para asignar relaciones entre entidades.
Resultado
Deberá poder asignar una relación de uno a uno y de uno a muchos entre entidades.
Antes de comenzar
Abra una ventana de terminal en la máquina virtual de la estación de trabajo y ejecute el
siguiente comando para descargar los archivos necesarios para este trabajo de laboratorio.
[student@workstation ~]$ lab manage-relationships setup
1.
Abra JBDS e importe el proyecto manage-relationships ubicado en el directorio /
home/student/JB183/labs/manage-relationships.
2.
Asigne la relación de uno a uno entre Teacher y Classroom con anotaciones JPA.
3.
Asigne la relación de uno a muchos entre las entidades Classroom y Student y capture
de manera diligente la lista de estudiantes cuando se carga un objeto Classroom desde
la base de datos.
4.
Inicie JBoss EAP desde dentro de JBDS.
5.
Compile e implemente la aplicación en JBoss EAP con Maven.
6.
Pruebe la aplicación en un explorador.
6.1. Abra Firefox en la máquina virtual workstation y diríjase a http://
localhost:8080/manage-relationships para acceder a la aplicación.
6.2. En la pantalla de la aplicación, actualice el aula con las diferentes opciones.
Asegúrese de que los datos del maestro y estudiante se completen correctamente
para cada aula.
Esto verifica que JPA puede utilizar las anotaciones proporcionadas para asignar
relaciones entre las entidades y completar los datos de la relación correctamente.
7.
Abra una nueva ventana de terminal y ejecute el siguiente comando para calificar el
trabajo de laboratorio:
[student@workstation ~]$ lab manage-relationships grade
El script de calificación debe indicar SUCCESS (CORRECTO). Si se produce una falla, revise
los errores y corríjalos hasta que vea un mensaje que indique SUCCESS (CORRECTO).
8.
Realice la limpieza.
JB183-EAP7.0-es-2-20180124
237
Capítulo 5. Administración de relaciones entre entidades
8.1. Anule la implementación de la aplicación en JBoss EAP con Maven con los siguientes
comandos:
[student@workstation ~]$ cd /home/student/JB183/labs/manage-relationships
[student@workstation manage-relationships]$ mvn wildfly:undeploy
8.2. Haga clic con el botón derecho en el proyecto manage-relationships en Project
Explorer (Explorador de proyectos) y seleccione Close Project (Cerrar proyecto) para
cerrar este proyecto.
8.3. Haga clic con el botón derecho en el servidor Red Hat JBoss EAP 7.0 en la pestaña
Servers (Servidores) de JBDS y haga clic en Stop (Detener).
Esto concluye el trabajo de laboratorio.
238
JB183-EAP7.0-es-2-20180124
Solución
Solución
En este trabajo de laboratorio, actualizará las clases de entidades con las anotaciones JPA
adecuadas para asignar relaciones entre entidades.
Resultado
Deberá poder asignar una relación de uno a uno y de uno a muchos entre entidades.
Antes de comenzar
Abra una ventana de terminal en la máquina virtual de la estación de trabajo y ejecute el
siguiente comando para descargar los archivos necesarios para este trabajo de laboratorio.
[student@workstation ~]$ lab manage-relationships setup
1.
Abra JBDS e importe el proyecto manage-relationships ubicado en el directorio /
home/student/JB183/labs/manage-relationships.
1.1. Para abrir JBDS, haga doble clic en el icono de JBoss Developer Studio en el escritorio
de la estación de trabajo. Deje el espacio de trabajo predeterminado (/home/
student/JB183/workspace) y haga clic en OK (Aceptar).
1.2. En el menú de JBDS, haga clic en File (Archivo) > Import (Importar)para abrir el
asistente para Import (Importar).
1.3. En la página Select (Seleccionar), haga clic en Maven > Existing Maven Projects
(Proyectos de Maven existentes) y, luego, haga clic en Next (Siguiente).
1.4. En la página Maven projects (Proyectos de Maven), haga clic en Browse (Explorar)
para abrir la ventana Select root folder (Seleccionar carpeta raíz). Diríjase al
directorio /home/student/JB183/labs/. Seleccione la carpeta managerelationships y haga clic en OK (Aceptar).
1.5. En la página Maven projects (Proyectos de Maven), haga clic en Finish (Finalizar).
1.6. Observe la barra de estado de JBDS para monitorear el progreso de la operación
de importación. Es probable que la descarga de todas las dependencias requeridas
demore unos minutos.
2.
Asigne la relación de uno a uno entre Teacher y Classroom con anotaciones JPA.
2.1. Abra la clase Classroom; para ello, expanda el ítem manage-relationships en
la pestaña Project Explorer (Explorador de proyectos) en el panel izquierdo de
JBDS y, luego, haga clic en manage-relationships > Java Resources > src/main/
java > com.redhat.training.model y expándalo. Haga doble clic en el archivo
Classroom.java.
Utilice la anotación JPA @OneToOne para asignar la relación a la entidad Teacher:
@Entity
public class Classroom {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
JB183-EAP7.0-es-2-20180124
239
Capítulo 5. Administración de relaciones entre entidades
private String name;
//TODO map relationship
@OneToOne(mappedBy="classroom")
private Teacher teacher;
Use el atributo mappedBy para asignar registros de aula a registros de maestro
mediante la columna de la tabla Teacher, que está representada por la variable
classroom en la clase de entidad Teacher.
nota
JBDS debe generar un error en la asignación a Teacher, y esto está previsto.
Este error se soluciona en el próximo paso.
2.2. Guarde los cambios usando Ctrl+S.
2.3. Para abrir la clase Teacher, expanda el ítem manage-relationships en la pestaña
Project Explorer (Explorador de proyectos) en el panel izquierdo de JBDS. Haga clic
en manage-relationships > Java Resources (Recursos de Java) > src/main/java >
com.redhat.training.model y expándalo. Haga doble clic en el archivo Teacher.java.
Utilice la anotación JPA @OneToOne para asignar la relación a la entidad Classroom:
@Entity
public class Teacher {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String name;
@OneToOne
@JoinColumn(name="classroomID")
private Classroom classroom;
Use la anotación @JoinColumn para indicarle a JPA cuál es la columna que debe
usar cuando une la tabla Teacher con la tabla Classroom; en este caso, la columna
classroomID.
2.4. Guarde los cambios usando Ctrl+S.
3.
Asigne la relación de uno a muchos entre las entidades Classroom y Student y capture
de manera diligente la lista de estudiantes cuando se carga un objeto Classroom desde
la base de datos.
3.1. Para abrir la clase Classroom, expanda el ítem manage-relationships en la pestaña
Project Explorer (Explorador de proyectos) en el panel izquierdo de JBDS. Haga
clic en manage-relationships > Java Resources (Recursos de Java) > src/main/
java > com.redhat.training.model y expándalo. Haga doble clic en el archivo
Classroom.java.
Use la anotación JPA @OneToMany para asignar la relación a la entidad UserGroup:
240
JB183-EAP7.0-es-2-20180124
Solución
@Entity
public class Classroom {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String name;
//TODO map relationship
@OneToOne(mappedBy="classroom")
private Teacher teacher;
//TODO map relationship
@OneToMany(mappedBy="classroom")
private Set<Student> students;
Use el atributo mappedBy para hacer referencia a la columna que asigna registros de
estudiante con registros de aula, ubicada en la tabla Student y representada por la
variable classroom en la clase de entidad Student.
nota
Está previsto que JBDS genere un error en la asignación de Student. Este
error se soluciona en el próximo paso.
3.2. Actualice la relación @OneToMany para que se cargue de manera diligente mediante
el atributo fetch:
//TODO map relationship
@OneToMany(mappedBy="classroom", fetch=FetchType.EAGER)
private Set<Student> students;
3.3. Guarde los cambios usando Ctrl+S.
3.4. Abra la clase Student; para ello, expanda el ítem manage-relationships en la
pestaña Project Explorer (Explorador de proyectos) en el panel izquierdo de JBDS
y, luego, haga clic en manage-relationships > Java Resources > src/main/java >
com.redhat.training.model y expándalo. Haga doble clic en el archivo Student.java.
Utilice la anotación JPA @ManyToOne para asignar la relación a la entidad Classroom:
@Entity
public class Student {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String name;
@ManyToOne
@JoinColumn(name="classroomID")
JB183-EAP7.0-es-2-20180124
241
Capítulo 5. Administración de relaciones entre entidades
private Classroom classroom;
Utilice la anotación @JoinColumn para indicarle a JPA cuál es la columna que debe
usar cuando une la tabla Student con la tabla Classroom. En este caso, use la
columna classroomID.
3.5. Guarde los cambios usando Ctrl+S.
4.
Inicie JBoss EAP desde dentro de JBDS.
Seleccione la pestaña Servers (Servidores) en JBDS. Haga clic con el botón derecho en la
entrada del servidor Red Hat JBoss EAP 7.0 [Stopped] y haga clic en la opción verde Start
(Iniciar) para iniciar el servidor. Observe la pestaña Console (Consola) de JBDS hasta que
el servidor se inicie y vea el siguiente mensaje:
INFO [org.jboss.as] (Controller Boot Thread) WFLYSRV0025: JBoss EAP 7.0.2.GA
(WildFly Core 2.1.8.Final-redhat-1) started
5.
Compile e implemente la aplicación en JBoss EAP con Maven.
Abra un nuevo terminal y cambie el directorio por la carpeta /home/student/JB183/
labs/manage-relationships.
[student@workstation ~]$ cd /home/student/JB183/labs/manage-relationships
Compile e implemente el EJB en JBoss EAP al ejecutar el siguiente comando:
[student@workstation manage-relationships]$ mvn clean wildfly:deploy
Si la compilación es correcta, se mostrará la siguiente salida:
[student@workstation manage-relationships]$ mvn clean package wildfly:deploy
...
[INFO] [INFO] <<< wildfly-maven-plugin:1.0.2.Final:deploy (default-cli) < package @
manage-relationships <<<
...
[INFO] --- maven-war-plugin:2.1.1:war (default-war) @ manage-relationships --...
[INFO] --- wildfly-maven-plugin:1.0.2.Final:deploy (default-cli) @ managerelationships
[INFO] -----------------------------------------------------------------------[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
6.
Pruebe la aplicación en un explorador.
6.1. Abra Firefox en la máquina virtual workstation y diríjase a http://
localhost:8080/manage-relationships para acceder a la aplicación.
6.2. En la pantalla de la aplicación, actualice el aula con las diferentes opciones.
Asegúrese de que los datos del maestro y estudiante se completen correctamente
para cada aula.
242
JB183-EAP7.0-es-2-20180124
Solución
Esto verifica que JPA puede utilizar las anotaciones proporcionadas para asignar
relaciones entre las entidades y completar los datos de la relación correctamente.
7.
Abra una nueva ventana de terminal y ejecute el siguiente comando para calificar el
trabajo de laboratorio:
[student@workstation ~]$ lab manage-relationships grade
El script de calificación debe indicar SUCCESS (CORRECTO). Si se produce una falla, revise
los errores y corríjalos hasta que vea un mensaje que indique SUCCESS (CORRECTO).
8.
Realice la limpieza.
8.1. Anule la implementación de la aplicación en JBoss EAP con Maven con los siguientes
comandos:
[student@workstation ~]$ cd /home/student/JB183/labs/manage-relationships
[student@workstation manage-relationships]$ mvn wildfly:undeploy
8.2. Haga clic con el botón derecho en el proyecto manage-relationships en Project
Explorer (Explorador de proyectos) y seleccione Close Project (Cerrar proyecto) para
cerrar este proyecto.
8.3. Haga clic con el botón derecho en el servidor Red Hat JBoss EAP 7.0 en la pestaña
Servers (Servidores) de JBDS y haga clic en Stop (Detener).
Esto concluye el trabajo de laboratorio.
JB183-EAP7.0-es-2-20180124
243
Capítulo 5. Administración de relaciones entre entidades
Resumen
En este capítulo, aprendió lo siguiente:
• Las relaciones entre clases de entidades JPA se asignan mediante anotaciones.
• Hay tres tipos principales de relaciones entre entidades: de uno a uno, de uno a muchos y
de muchos a muchos. JPA proporciona anotaciones para asignar cada uno de estos tipos de
relaciones.
• Cuando asigna relaciones entre entidades, es importante tener en cuenta las implicaciones
de rendimiento generadas por hacer que JPA cargue entidades relacionadas. Cuando sea
posible, aproveche la carga diferida (lazy loading) para evitar un rendimiento más lento
cuando no se necesiten entidades relacionadas.
• JPA proporciona el atributo fetch, que se puede definir en FetchType.EAGER
o FetchType.LAZY cuando asigna entidades relacionadas. Si la captura diferida
(lazy fetching) se utiliza para asignar relaciones, intentar cargar los datos de
esa relación después de la sesión del administrador de entidades genera una
LazyInitializationException.
• Una relación de muchos a muchos requiere el uso de una tabla de unión. JPA proporciona
la anotación @JoinTable por este motivo, y saca provecho de varias @JoinColumn para
asignar relaciones de muchos a muchos entre dos entidades.
• Cuando un desarrollador asigna una relación de muchos a muchos entre dos entidades,
solo un lado se puede asignar mediante el uso de @JoinTable: el lado propietario. El otro
lado de la relación, conocido como el lado secundario, debe asignarse mediante el atributo
mappedBy en la anotación @ManyToMany, que hace referencia a una variable en la entidad
propietaria.
244
JB183-EAP7.0-es-2-20180124
TRAINING
CAPÍTULO 6
CREACIÓN DE SERVICIOS REST
Descripción general
Meta
Crear API REST mediante la especificación de JAX-RS.
Objetivos
• Describir conceptos de servicios web y enumerar tipos
de servicios web.
• Crear un servicio REST mediante la especificación de
JAX-RS.
• Crear una aplicación cliente que pueda invocar las API
REST de manera remota.
Secciones
• Descripción de conceptos de servicios web (y
cuestionario)
• Creación de servicios REST con JAX-RS (y ejercicio
guiado)
• Consumo de un servicio REST (y cuestionario)
Trabajo de laboratorio
JB183-EAP7.0-es-2-20180124
Creación de servicios REST
245
Capítulo 6. Creación de servicios REST
Descripción de conceptos de servicios web
Objetivo
Tras finalizar esta sección, los estudiantes deberán ser capaces de describir conceptos y tipos
de servicios web.
Servicios web
Los servicios web exponen una comunicación estandarizada para la interoperabilidad
entre componentes de aplicaciones por HTTP. Al abstraer aplicaciones en componentes
individuales que se comunican a través de servicios web, cada sistema queda con un
acoplamiento bajo entre sí. Esta separación proporciona una mayor capacidad de modificar
aplicaciones o integrar nuevos sistemas en la aplicación. El uso de un formato estándar para
la transferencia de datos, como JSON o XML, permite que las aplicaciones consuman servicios
web para requerir solo la capacidad de crear una solicitud HTTP al servicio y procesar la
respuesta del servicio.
En los últimos años, se han vuelto populares las aplicaciones empresariales basadas
en servicios web. Uno de los principales motivos de este aumento en la adopción es la
necesidad de que las aplicaciones admitan varios dispositivos, como una computadora de
escritorio y un dispositivo móvil, al tiempo que reduce el tiempo de desarrollo para admitir
varias aplicaciones. Al abstraer la capa de presentación específica del dispositivo, la capa
de datos se convierte en una capa de servicio. Esta separación permite a las organizaciones
desarrollar rápidamente aplicaciones en una variedad de plataformas, al tiempo que se
reutiliza un back-end compartido, compilado con servicios web. Este enfoque reduce el
tiempo para desarrollar aplicaciones y mantener cambios al back-end al no requerir la
repetición del trabajo en todas las plataformas compatibles.
Por ejemplo, considere una aplicación web para un banco. La empresa desea expandirse al
mercado móvil con una aplicación móvil totalmente nueva. El primer paso del desarrollador
es exponer una API para acceder a los datos de la aplicación. Al exponer el back-end del
banco con una capa de servicios web, el front-end de la aplicación web se separa de la
lógica de negocio de la aplicación. Como resultado, los desarrolladores de la aplicación del
banco pueden crear un front-end de aplicación móvil mediante servicios web sin afectar
la aplicación del front-end existente. Otro beneficio de exponer la capa de servicios es que
otras aplicaciones de front-end o componentes web que necesiten los datos de la aplicación
también pueden invocar los mismos extremos de servicio. El siguiente gráfico demuestra esta
arquitectura:
246
JB183-EAP7.0-es-2-20180124
Tipos de servicios web
Figura 6.1: Arquitectura de aplicaciones de los servicios web
Tipos de servicios web
En este curso, se abordan dos implementaciones de servicios web:
• Servicios web RESTful JAX-RS
• Servicios web JAX-WS
Ambas implementaciones proporcionan los mismos beneficios del uso de servicios web,
como el bajo acoplamiento y protocolos estandarizados; no obstante, JAX-WS y JAX-RS
difieren en una cantidad importante de formas que se deben considerar cuidadosamente,
según el enfoque que se use.
JB183-EAP7.0-es-2-20180124
247
Capítulo 6. Creación de servicios REST
nota
Este curso usa JAX-RS principalmente. Los debates sobre JAX-WS se incluyen para
proporcionar un contraste con los servicios web RESTful.
JAX-RS
JAX-RS es la API de Java para crear servicios web RESTful livianos. En Red Hat JBoss EAP 7,
la implementación de JAX-RS es RESTEasy, que cumple por completo con la especificación
JSR-311, titulada API de Java para servicios web RESTful 2.0, y proporciona funciones
adicionales para un desarrollo eficiente de servicios REST.
Los desarrolladores pueden compilar servicios web RESTEasy usando anotaciones para
marcar ciertas clases y métodos como extremos. Cada extremo representa una URL que
una aplicación de cliente puede invocar y, según el tipo de anotación, especifica el tipo de
solicitud HTTP.
A diferencia de otros enfoques a servicios web, los servicios web RESTful pueden usar un
formato de mensajes más pequeño, como JSON, en comparación con XML y otros para crear
más sobrecarga para cada solicitud Cada extremo se puede anotar para determinar tanto el
formato de los datos recibidos como el formato de los datos devueltos al cliente. Asimismo,
los servicios web RESTful no requieren el uso de una WSDL o algo similar a lo que se requiere
al consumir servicios JAX-WS. Esto simplifica mucho el uso de servicios web RESTful, ya que
los consumidores simplemente pueden realizar solicitudes a extremos individuales en un
servicio.
JAX-WS
JAX-WS es la API de Java para servicios web basados en XML mediante el Protocolo simple de
acceso a objetos (SOAP). JBossWS es la implementación que cumple con la especificación API
de Java JSR-224 para servicios web basados en XML 2.2 para JAX-WS en Red Hat JBoss EAP 7.
Para definir un protocolo estándar para la comunicación entre aplicaciones, los servicios
JAX-WS usan un archivo de definición XML escrito mediante el Lenguaje de descripción
de servicios web (WSDL). En muchas formas, WSDL simplifica la creación de servicios web
permitiendo que una IDE, como JBoss Developer Studio, use la definición de servicios para
crear clientes que puedan interactuar con el servicio en forma automática. No obstante, esta
definición de servicio no requiere más mantenimiento a los desarrolladores del servicio. Los
servicios JAX-WS también requieren de los clientes y consumidores que realicen solicitudes
formales en comparación con JAX-RS, que puede realizar solicitudes a extremos individuales
simplemente a través de HTTP.
Referencias
API de Java JSR-311 para servicios web RESTful 2.0
http://www.jcp.org/en/jsr/detail?id=311
API de Java JSR-224 para servicios web basados en XML 2.2
http://www.jcp.org/en/jsr/detail?id=224
248
JB183-EAP7.0-es-2-20180124
JAX-WS
Referencias
Para obtener más información, consulte la Guía de desarrollo de servicios web para
Red Hat JBoss EAP 7 en
https://access.redhat.com/documentation/en-us/
red_hat_jboss_enterprise_application_platform/
JB183-EAP7.0-es-2-20180124
249
Capítulo 6. Creación de servicios REST
Cuestionario: Servicios web
Una los siguientes ítems con sus equivalentes en la tabla.
La especificación JAX-WS.
La especificación para JAX-RS.
La implementación EAP del servicio SOAP.
La implementación del servicio web basado en la anotación.
Un formato XML que describe los extremos para un servicio SOAP.
Un formato de datos ligero y legible utilizado por los servicios web RESTful.
Término
Definición
WSDL
JSON
API de Java JSR-311
para servicios web
RESTful 2.0
API de Java JSR-224
para servicios web
basados en XML 2.2
RESTEasy
JBossWS
250
JB183-EAP7.0-es-2-20180124
Solución
Solución
Una los siguientes ítems con sus equivalentes en la tabla.
Término
Definición
WSDL
Un formato XML que describe los extremos
para un servicio SOAP.
JSON
Un formato de datos ligero y legible utilizado
por los servicios web RESTful.
API de Java JSR-311
para servicios web
RESTful 2.0
La especificación para JAX-RS.
API de Java JSR-224
para servicios web
basados en XML 2.2
La especificación JAX-WS.
RESTEasy
La implementación del servicio web basado en
la anotación.
JBossWS
La implementación EAP del servicio SOAP.
JB183-EAP7.0-es-2-20180124
251
Capítulo 6. Creación de servicios REST
Creación de servicios REST con JAX-RS
Objetivos
Tras finalizar esta sección, los estudiantes deberán ser capaces de realizar lo siguiente:
• Cree servicios REST con JAX-RS.
• Consuma los servicios web REST.
• Diferencie los métodos HTTP.
Servicios web RESTful con JAX-RS
Como se describe en la sección anterior, JAX-RS es la API de Java utilizada para crear servicios
web RESTful. Los servicios web REST están diseñados para ser lo más simples posible
para mejorar el uso, tanto para los desarrolladores de los servicios como para los clientes
que los consumen. Para mantener un nivel de simplicidad, los servicios web deben tener
conformidad con varios estándares para ser considerados RESTful. Al implementar una
capa de servicio web, los desarrolladores pueden abstraer la capa del front-end y crear
una aplicación compuesta por muchos componentes con bajo acoplamiento. Este tipo de
arquitectura se conoce como arquitectura de servidor de cliente y es un requisito para los
servicios web REST.
Otra función característica de las aplicaciones web RESTful es que los servicios no poseen
estado. Cada solicitud realizada a un servicio web RESTful debe proporcionar una respuesta
que contenga toda la información requerida por el cliente, aunque el servicio no puede ser
responsable de conservar ninguna información con respecto al estado de la sesión. Esta
restricción garantiza que el cliente es responsable de mantener el estado de la sesión en
lugar de complicar el servicio web REST de manera innecesaria.
La versión JEE de la aplicación To Do List utiliza un servicio web RESTful para abstraer el frontend Angular de la capa de lógica de negocio y datos. Al crear extremos específicos, como el
extremo para obtener una lista de todos los ítems de To Do List, los clientes pueden obtener
la información en el formato JSON para facilitar el análisis. El siguiente ejemplo ilustra una
solicitud HTTP GET en el extremo REST lookupItemById() de la aplicación To Do List, que
devuelve un ítem de To Do List de la base de datos MySQL basada en el ID del ítem:
[student@workstation todojee]$ curl localhost:8080/todo/api/items/1
El servicio devuelve la siguiente respuesta JSON:
{"id":1,"description":"Pick up newspaper","done":false,"user":null}
Todo cliente con acceso al servicio puede obtener esta información, lo que permite que
varias aplicaciones de front-end aprovechen los mismos datos y lógica de negocio para la
aplicación To Do List. Los clientes solo necesitan poder realizar solicitudes HTTP y analizar las
respuestas del servicio para consumir el servicio REST.
Java EE 7 admite JAX-RS 2.0, que hace muy sencillo el desarrollo de servicios web RESTful
y el cumplimiento de sus estándares. JAX-RS utiliza varias anotaciones para definir el
comportamiento de los servicios web. Estas anotaciones se colocan directamente en la clase
252
JB183-EAP7.0-es-2-20180124
Creación de servicios web RESTful
de servicio para crear diferentes tipos de extremos y definir parámetros. Para facilitar el
desarrollo de servicios web, JBoss Developer Studio cuenta con un asistente para crear todos
los archivos necesarios para definir el servicio web RESTful.
Creación de servicios web RESTful
Un servicio web RESTful JAX-RS consta de una o más clases que utilizan las anotaciones JAXRS para crear un servicio web. El primer paso para crear el servicio web es crear una clase
que extends (amplíe) la clase javax.ws.rs.core.Application. Además de declarar el
servicio web RESTful, la nueva subclase también se utiliza para definir la URI de base para el
servicio web con la anotación @ApplicationPath. A continuación se muestra un ejemplo de
una clase que amplía javax.ws.rs.core.Application:
import javax.ws.rs.ApplicationPath;
import javax.ws.rs.core.Application;
@ApplicationPath("/api")
public class Service extends Application {
//Can be left empty
}
La anotación @ApplicationPath establece la base URI para el servicio web. En el ejemplo
anterior, la ruta de la aplicación se configura para ser /api. Como resultado, todas las
solicitudes a este servicio deben estar precedidas por "/api" en la URI. Por ejemplo, si la ruta
de contexto de la aplicación es hello-world y la aplicación está disponible en http://
localhost:8080/helloworld, el servicio REST se expone en http://localhost:8080/
helloworld/api/. Esta URI a menudo se expande al agregar extremos y rutas adicionales.
nota
De manera opcional, puede usar la nueva subclase para proporcionar
implementaciones personalizadas para algunos métodos en la clase principal; no
obstante, no se necesita realizar ninguna otra modificación que la del ejemplo dado
para implementar un servicio web RESTful.
Una alternativa a clasificar la clase javax.ws.rs.core.Application como subclase es
utilizar la web.xml en la aplicación para definir la clase javax.ws.rs.core.Application
y especificar la URI de base. En el siguiente ejemplo de web.xml se proporciona la misma
funcionalidad que el ejemplo anterior basado en Java, sin necesitar la creación de una clase
Java adicional:
<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">
<servlet>
<servlet-name>javax.ws.rs.core.Application</servlet-name>
</servlet>
<servlet-mapping>
<servlet-name>javax.ws.rs.core.Application</servlet-name>
<url-pattern>/api/*</url-pattern>
</servlet-mapping>
...
JB183-EAP7.0-es-2-20180124
253
Capítulo 6. Creación de servicios REST
</web-app>
La clase de recursos raíz RESTful
El uso de las anotaciones JAX-RS disponibles es una forma fácil de crear un servicio web
RESTful de una clase POJO existente. Por ejemplo, considere un POJO básico, como el
siguiente:
public class HelloWorld {
public String hello() {
return "Hello World!";
}
}
Al marcar esta clase y método con las siguientes anotaciones, el método queda expuesto
como extremo dentro del servicio web RESTful:
@Stateless
@Path("hello")
@Consumes(MediaType.APPLICATION_JSON)
@Produces(MediaType.APPLICATION_JSON)
public class HelloWorld {
@GET
public String hello() {
return "Hello World!";
}
}
Este ejemplo de servicio expone el método hello() a una solicitud HTTP GET estándar. Por
lo tanto, acceder a http://localhost:8080/hello-world/api/hello mediante una
solicitud HTTP GET (como con un explorador web) devuelve la Cadena Hello World! en
JSON.
Recuerde que la anotación @ApplicationPath en la clase Aplicación dicta la URI de base
para todas las solicitudes a esa aplicación. Asimismo, dentro de la clase de servicio RESTful,
la clase y los métodos y extremos individuales se puede designar con su propio valor @Path
específico. Esto permite a los usuarios del servicio acceder de una manera más intuitiva a un
recurso.
Observando el ejemplo anterior, observe que la anotación @Path a nivel de la clase
se estableció como hello. Esta anotación crea otra capa en la URI, además de la
@ApplicationPath existente. En la siguiente muestra se ve otro ejemplo de la modificación
de la anotación @Path:
@Stateless
@Path("hello")
@Consumes(MediaType.APPLICATION_JSON)
@Produces(MediaType.APPLICATION_JSON)
public class HelloWorld {
@GET
@Path("person/{id}/name")
public String hello(String id) {
return "Hello" + id + "!";
}
254
JB183-EAP7.0-es-2-20180124
La clase de recursos raíz RESTful
}
En esta instancia, se invoca este método al realizar una solicitud a la siguiente URI: http://
localhost:8080/hello-world/api/hello/person/1/name. La parte {id} de la ruta es
una variable, que se denota por las llaves que la rodean, y es el cliente quien debe presentar
su valor en la URI para cada solicitud.
En la siguiente tabla se resumen las anotaciones disponibles para lo que resta de la clase de
recursos raíz RESTful:
Anotaciones de JAX-RS
Anotación
Descripción
@ApplicationPath
La anotación @ApplicationPath se aplica a la subclase de la clase
javax.ws.rs.core.Application y define la URI de base para el
servicio web.
@Path
La anotación @Path define la URI de base tanto para toda la clase
de raíz como para un método individual. La ruta puede contener
una ruta estática explícita, como hello o puede contener una
variable que se debe enviar en la solicitud. Se hace referencia a este
valor utilizando la anotación @PathParam.
@Consumes
La anotación @Consumes define el tipo de contenido de la
solicitud aceptado por la clase o el método de servicio. Si un tipo
incompatible se envía al servicio, el servidor devuelve un error 415
de HTTP, "Tipo de medio no compatible". Entre los parámetros
aceptables se incluye application/json, application/xml,
text/html o cualquier otro tipo de MIME.
@Produces
La anotación @Produces define el tipo de contenido de la respuesta
que la clase o el método de servicio devuelve. Entre los parámetros
aceptables se incluye application/json, application/xml,
text/html o cualquier otro tipo de MIME.
@GET
La anotación @GET se aplica a un método para crear un extremo
para el tipo de solicitud HTTP GET, generalmente usado para
recuperar datos.
@POST
La anotación @POST se aplica a un método para crear un extremo
para el tipo de solicitud HTTP POST, generalmente usado para
recuperar datos.
@DELETE
La anotación @DELETE se aplica a un método para crear un extremo
para el tipo de solicitud HTTP DELETE, generalmente usado para
recuperar datos.
@PUT
La anotación @PUT se aplica a un método para crear un extremo
para el tipo de solicitud HTTP PUT, generalmente usado para
recuperar datos.
@PathParam
La anotación @PathParam se utiliza para recuperar un parámetro
enviado a través de la URI, como http://localhost:8080/
hello-web/api/hello/1.
@QueryParam
La anotación @QueryParam se utiliza para recuperar un parámetro
enviado a través de la URI como parámetro de consulta, como
http://localhost:8080/hello-web/api/hello?id=1.
JB183-EAP7.0-es-2-20180124
255
Capítulo 6. Creación de servicios REST
Personalización de solicitudes y respuestas
Uno de los aspectos destacados de JAX-RS es la capacidad de personalizar el tipo MIME
de la solicitud y la respuesta. Para las organizaciones que requieren el cumplimiento
de un tipo específico de solicitud o respuesta, como XML, las anotaciones @Produces
o @Consumes se pueden modificar para hacer cumplir XML como el tipo de respuesta y
solicitud, respectivamente. Puede preferir usar JSON, ya que es un tipo MIME liviano, en
relación a XML y puede reducir el ancho de banda.
La anotación @Produces define el tipo de medio MIME para la respuesta regresada por el
servicio. La anotación @Consumes define el tipo de medio MIME para la solicitud requerida
por el servicio. Tanto @Produces como @Consumes se pueden aplicar en el nivel de método,
el nivel de clase o ambos. Si se aplica a ambos, la anotación a nivel de método se establece
como prioritaria y anula la anotación de nivel de clase. Si no se define ninguna anotación
para @Produces o @Consumes en el método, el método establece el tipo MIME como
predeterminado a nivel de clase.
En el siguiente ejemplo se define una clase JAX-RS que demuestra el uso de las anotaciones
@Produces y @Consumes:
@Stateless
@Path("hello")
@Produces(MediaType.APPLICATION_JSON)
public class HelloWorld {
@GET
@Produces("text/html")
{
public String hello()
return "<b>Hello World!</b>";
}
@GET
@Path("newest")
public Person getNewestPerson()
...implementation omitted...
}
{
@POST
@Consumes(MediaType.APPLICATION_JSON)
public String savePerson(Person person)
...implementation omitted...
}
{
}
El método hello () devuelve la salida que el cliente debe esperar que esté en HTML.
Esto es dictado por la anotación @Produces("text/html") del nivel de método.
El método getNewestPerson() devuelve la salida que el cliente debe esperar que esté
en JSON. Esto es dictado por @Produces(MediaType.APPLICATION_JSON) de nivel de
clase, ya que no hay anotación @Produces de nivel de método.
El método savePerson(Person person) requiere que la solicitud esté en JSON; de lo
contrario, el cliente recibe un error HTTP 415 de tipo de medio no compatible.
256
JB183-EAP7.0-es-2-20180124
Métodos HTTP
Métodos HTTP
El protocolo HTTP define varios métodos mediante los cuales el protocolo ejecuta diferentes
acciones. El cliente es responsable de especificar el tipo de solicitud, además de la ruta a la
solicitud. La siguiente es una lista de métodos:
• GET: el método GET recopila datos.
• POST: el método POST crea una nueva entidad.
• DELETE: el método DELETE quita una entidad.
• PUT: El método PUT actualiza una entidad.
Cada método HTTP tiene una anotación con nombre similar que se utiliza para anotar
métodos en una clase de servicio RESTful. Si dos métodos Java existen en la misma ruta,
JAX-RS determina qué método usar haciendo coincidir el método HTTP en la solicitud HTTP
realizada por el cliente y la anotación en el método. El siguiente es un ejemplo de una clase
de servicio web RESTful:
@Stateless
@Path("hello")
@Produces(MediaType.APPLICATION_JSON)
@Consumes(MediaType.APPLICATION_JSON)
public class HelloWorld {
@GET
@Path("person")
public List<Person> getPersons() {
...implementation omitted...
}
@POST
@Path("person")
public String savePerson(Person person) {
...implementation omitted...
}
@PUT
@Path("person")
public String updatePerson(Person person) {
...implementation omitted...
}
@DELETE
@Path("person/{id}")
public String deletePerson(@PathParam("id") String id) {
...implementation omitted...
}
}
Este método devuelve una representación JSON de la lista Java de objetos Person
cuando una solicitud HTTP GET se realiza a la siguiente URI: http://localhost:8080/
hello-world/hello/person.
Este método crea un objeto Person cuando una solicitud HTTP POST con
la representación JSON de Person se realiza a la siguiente URI: http://
localhost:8080/hello-world/hello/person.
JB183-EAP7.0-es-2-20180124
257
Capítulo 6. Creación de servicios REST
Este método actualiza un objeto Person cuando una solicitud HTTP POST con la
representación JSON de Person existente se realiza a la siguiente URI: http://
localhost:8080/hello-world/hello/person.
Este método elimina un objeto Person cuando se realiza una solicitud HTTP DELETE a la
siguiente URI: http://localhost:8080/hello-world/hello/person/1.
Observe que los métodos GET, POST y PUT se encuentran todos en la misma ruta; no
obstante, el cliente puede dictar qué extremo se alcanza en función del método HTTP que se
especifica en la solicitud.
Inyección de parámetros de la URI
En muchas instancias, los clientes que usan los servicios web RESTful necesitan solicitar
información específica de un servicio. Esto se logra proporcionando parámetros en la URI, ya
sea como parámetro de ruta o parámetro de consulta.
Para usar un parámetro de ruta en un método JAX-RS, anótelo con la anotación @PathParam.
Esta anotación generalmente se usa cuando el cliente solicita un recurso específico, como los
datos del usuario. El siguiente es un ejemplo de parámetro de ruta:
@GET
@Path("{id}")
public Person getPerson(@PathParam("id")
Long id) {
return entityManager.find(Person.class, id);
}
El cliente envía el id de variable en la URI como parte de la ruta. Por ejemplo, la
siguiente solicitud es compatible con este método: http://localhost:8080/helloweb/api/hello/1. El 1 se envía al método getPerson().
La anotación @PathParam asigna la variable de la ruta al parámetro del método Java.
Para usar un parámetro de consulta en un método JAX-RS, anótelo con la anotación
@QueryParam. Esta anotación generalmente se usa en búsquedas y al filtrar datos, como el
filtrado de usuarios por sus preferencias de correo electrónico. El siguiente es un ejemplo
de parámetro de consulta utilizado para determinar qué usuarios desean que se les envíen
correos electrónicos en función de la variable sendEmail:
@GET
@Path("users")
public List<Person> getUsers(@QueryParam("sendEmail") String sendEmail
) {
return entityManager.find("SELECT * USERS WHERE user.sendEmail=" + sendEmail);
}
La anotación @QueryParam asigna el valor que se utiliza en la URI para sendEmail a
una Cadena Java con el mismo nombre. El siguiente es un ejemplo de URI que envía un
indicador false (falso) a la variable sendEmail: http://localhost:8080/helloworld/api/users?sendEmail=false.
El método return (devolver) aprovecha el valor asignado a la variable Java utilizándolo
para consultar la base de datos y filtrar todos los usuarios por parámetro. En esta
instancia, el parámetro de consulta enumera los usuarios en función del valor de la
variable sendEmail.
En muchas instancias, un parámetro de consulta requiere un valor predeterminado para
evitar que la solicitud del cliente falle por no proporcionar un parámetro y para permitir que
258
JB183-EAP7.0-es-2-20180124
Inyección de parámetros de la URI
los desarrolladores del servicio creen un único método para buscar y filtrar sin realizar todos
los parámetros requeridos. El siguiente es el mismo ejemplo con el valor predeterminado
para la variable sendEmail establecida en False:
@GET
@Path("users")
public List<Person> getUsers(@DefaultValue("True") @QueryParam("sendEmail") String
sendEmail) {
return entityManager.find("SELECT * USERS WHERE user.sendEmail=" + sendEmail);
}
Con el valor predeterminado establecido como True, el cliente ya no necesita ingresar
el valor en la URI; en cambio, puede realizar una solicitud a http://localhost:8080/
hello-world/api/users para recibir una lista de usuarios que están aceptando correos
electrónicos.
Referencias
Para obtener más información, consulte la Guía de desarrollo de servicios web para
Red Hat JBoss EAP 7 en
https://access.redhat.com/documentation/en-us/
red_hat_jboss_enterprise_application_platform/
JB183-EAP7.0-es-2-20180124
259
Capítulo 6. Creación de servicios REST
Ejercicio guiado: Exposición de un servicio
REST
En este ejercicio, expondrá una REST API para una aplicación.
Resultados
Deberá ser capaz de crear un servicio web RESTful y probarlo mediante curl.
Antes de comenzar
Abra una ventana de terminal en la máquina virtual workstation y ejecute el siguiente
comando para descargar los archivos del trabajo de laboratorio necesarios para este taller.
[student@workstation ~]$ lab hello-rest setup
Pasos
1. Importe el proyecto hello-rest en JBoss Developer Studio IDE (JBDS).
1.1. Haga doble clic en el icono de JBDS del escritorio de la máquina virtual workstation
para iniciarlo.
1.2. Ingrese /home/student/JB183/workspace en el campo Workspace de la ventana
Eclipse Launcher y, luego, haga clic en Launch (Iniciar).
1.3. En el menú de JBDS, haga clic en File (Archivo) > Import (Importar)para abrir el
asistente para Import (Importar).
1.4. En la página Select (Seleccionar), haga clic en Maven > Existing Maven Projects
(Proyectos de Maven existentes) y, luego, haga clic en Next (Siguiente).
1.5. En la página Maven projects (Proyectos de Maven), haga clic en Browse (Explorar)
para abrir la ventana Select root folder (Seleccionar carpeta raíz). Diríjase al
directorio /home/student/JB183/labs/. Seleccione la carpeta hello-rest y
haga clic en OK (Aceptar).
1.6. En la página Maven projects (Proyectos de Maven), haga clic en Finish (Finalizar).
1.7. Observe la barra de estado de JBDS para monitorear el progreso de la operación
de importación. Es probable que la descarga de todas las dependencias requeridas
demore unos minutos.
2.
Cree el contexto de raíz para el servicio web.
2.1. En el ítem hello-rest de la pestaña Project Explorer (Explorador de proyectos) del
panel izquierdo de JBDS, seleccione hello-rest > Java Resources > src/main/java >
com.redhat.training.rest y expanda el paquete.
2.2. Haga clic con el botón derecho en com.redhat.training.rest y haga clic en New
(Nueva) > Class (Clase).
2.3. En el campo Name (Nombre) ingrese Service. Haga clic en Finish (Finalizar).
260
JB183-EAP7.0-es-2-20180124
2.4. En la nueva clase, agregue la anotación @ApplicationPath, importe la librería y
especifique la ruta como /api:
package com.redhat.training.rest;
import javax.ws.rs.ApplicationPath;
@ApplicationPath("/api")
Public class Service {
}
2.5. Complete la clase importando y ampliando la clase
javax.ws.rs.core.Application:
package com.redhat.training.rest;
import javax.ws.rs.ApplicationPath;
import javax.ws.rs.core.Application;
@ApplicationPath("/api")
Public class Service extends Application {
}
2.6. Presione Ctrl+S para guardar sus cambios.
3.
Abra y actualice el servicio web RESTful PersonService.java en el paquete
com.redhat.training.rest para que no tenga estado, usando la anotación
@Stateless.
//TODO Add the stateless annotation
@Stateless
4.
Agregue la anotación @Path para realizar los puntos finales para esta clase de servicio
web disponible en http://localhost:8080/hello-rest/api/persons/:
//TODO Add a Path for persons
@Path("persons")
5.
Defina el tipo de medio @Consumes y @Produces para el servicio.
5.1. Agregue la anotación @Consumes para permitir que el servicio consuma JSON:
//TODO Add a Consumes annotation for JSON
@Consumes(MediaType.APPLICATION_JSON)
5.2. Agregue la anotación @Produces para permitir que el servicio produzca JSON:
//TODO Add a Produces annotation for JSON
@Produces(MediaType.APPLICATION_JSON)
JB183-EAP7.0-es-2-20180124
261
Capítulo 6. Creación de servicios REST
6.
Configure los métodos getPerson(), getAllPersons(), deletePerson() y
savePerson() en la clase PersonService.java para que estén disponibles como
extremos REST.
6.1. Exponga el método getPerson(Long id) agregando la anotación @GET:
//TODO add GET annotation
@GET
public Person getPerson(Long id) {
return entityManager.find(Person.class, id);
}
6.2. Actualice el método getPerson(Long id) para permitir que los consumidores del
servicio REST soliciten una Persona con un ID específico, mediante el extremo REST,
agregando las anotaciones @Path y @PathParam:
@GET
//TODO add path for ID
@Path("{id}")
public Person getPerson(@PathParam("id") Long id) {
return entityManager.find(Person.class, id);
}
Una solicitud GET a http://localhost:8080/hello-rest/api/persons/3
ahora devuelve la representación JSON del objeto Person con ID 3.
6.3. Agregue la anotación @GET al método getAllPersons() para exponer el método
como extremo REST:
//TODO add GET annotation
@GET
public List<Person> getAllPersons() {
TypedQuery<Person> query = entityManager.createQuery("SELECT p FROM Person p",
Person.class);
List<Person> persons = query.getResultList();
return persons;
}
Una solicitud GET a http://localhost:8080/hello-rest/api/persons/ ahora
devuelve la representación JSON de todos los objetos Person en la base de datos.
6.4. Agregue la anotación para @DELETE al método deletePerson(Long id) para
permitir que las solicitudes HTTP DELETE eliminen un objeto Person de la base de
datos:
//TODO add DELETE annotation
@DELETE
public void deletePerson(Long id) {
try {
try {
tx.begin();
entityManager.remove(getPerson(id));
} finally {
tx.commit();
}
262
JB183-EAP7.0-es-2-20180124
} catch (Exception e) {
throw new EJBException();
}
}
6.5. De manera similar al método que devuelve un objeto Person individual, el método
deletePerson requiere un parámetro de ID para eliminar un objeto Person
específico de la base de datos. Actualice el método con una anotación @Path y
una anotación PathParam para permitir a los usuarios enviar ese parámetro en la
solicitud HTTP:
@DELETE
//TODO add Path for ID
@Path("{id}")
public void deletePerson(@PathParam("id") Long id) {
try {
try {
tx.begin();
entityManager.remove(getPerson(id));
} finally {
tx.commit();
}
} catch (Exception e) {
throw new EJBException();
}
}
Una solicitud DELETE a http://localhost:8080/hello-rest/api/persons/3
ahora elimina el objeto Person con ID 3 de la base de datos.
6.6. Agregue una anotación @POST al método savePerson(Person person) para crear
un extremo para guardar un objeto Person en la base de datos:
//TODO add POST annotation
@POST
public Response savePerson(Person person) {
try {
...
}
Una solicitud POST a http://localhost:8080/hello-rest/api/persons/ con
una representación JSON de un objeto Person ahora envía a esa persona a la base
de datos.
6.7. Presione Ctrl+S para guardar sus cambios.
7.
Seleccione la pestaña Servers (Servidores) en el panel inferior de JBDS para iniciar EAP.
Haga clic con el botón derecho en el servidor Red Hat JBoss EAP 7.0 [Stopped] y haga clic
en el botón verde de inicio para iniciar el servidor.
8.
Implemente la aplicación hello-rest mediante los siguientes comandos en la ventana del
terminal:
[student@workstation ~]$ cd /home/student/JB183/labs/hello-rest
[student@workstation hello-rest]$ mvn wildfly:deploy
JB183-EAP7.0-es-2-20180124
263
Capítulo 6. Creación de servicios REST
9.
Pruebe la API REST mediante curl y el complemento (plug-in) de cliente REST de Firefox.
9.1. Inicie Firefox en la máquina virtual workstation y haga clic en el complemento
(plug-in) de cliente REST en la barra de herramientas del explorador.
Figura 6.2: Complemento (plug-in) de cliente REST de Firefox
9.2. En la barra de herramientas superior, haga clic en Headers (Encabezados) y
seleccione Custom Header (Encabezado personalizado) para agregar un nuevo
encabezado personalizado a la solicitud.
9.3. Introduzca la siguiente información en el cuadro de diálogo del encabezado
personalizado:
• Name (Nombre): Content-Type
• Value (Valor): application/json
Figura 6.3: Creación de un encabezado de solicitud personalizado en el cliente REST
Haga clic en Okay (Aceptar).
9.4. Seleccione POST como el Method (Método). En el formulario URL, ingrese http://
localhost:8080/hello-rest/api/persons.
9.5. En la sección Body (Cuerpo) de la solicitud, agregue la siguiente representación JSON
de una entidad Person:
{"name":"Shadowman"}
Haga clic en Send (Enviar).
9.6. Verifique en la pestaña Response Headers (Encabezados de respuesta) que Status
Code (Código de estado) sea 200 OK.
264
JB183-EAP7.0-es-2-20180124
Figura 6.4: Creación de un encabezado de solicitud personalizado en el cliente REST
9.7. Cambie la sección Body de la solicitud con lo siguiente para desencadenar la
validación de beans para la entidad Person que requiere que el atributo name
(nombre) cuente con más de dos caracteres:
{"name":"a"}
Haga clic en Send (Enviar) y observe que la respuesta vuelve con un código de estado
de 500 para indicar un error de servidor.
9.8. En una ventana de terminal, use el siguiente comando curl en la ventana de
terminal para realizar una solicitud HTTP GET para recuperar todos los objetos
Person:
[student@workstation hello-rest]$ curl \
http://localhost:8080/hello-rest/api/persons
Busque el objeto Person creado en el paso anterior en la salida:
[{"id":1,"name":"Shadowman"}]
9.9. Elimine el objeto Person recientemente creado mediante la siguiente solicitud
DELETE HTTP con el ID de Person correcto:
[student@workstation hello-rest]$ curl -X DELETE \
http://localhost:8080/hello-rest/api/persons/1
9.10.Ejecute el siguiente comando para verificar que se haya eliminado el objeto Person:
[student@workstation hello-rest]$ curl http://localhost:8080/hello-rest/api/
persons
En la salida se muestra que el objeto Person ya no se encuentra en la base de datos.
JB183-EAP7.0-es-2-20180124
265
Capítulo 6. Creación de servicios REST
10. Anule la implementación de la aplicación y detenga EAP.
10.1.En la ventana de terminal, ejecute el siguiente comando para anular la
implementación de la aplicación de EAP:
[student@workstation hello-rest]$ mvn wildfly:undeploy
10.2.Haga clic con el botón derecho en el proyecto hello-rest en Project Explorer
(Explorador de proyectos) y seleccione Close Project (Cerrar proyecto) para cerrar el
proyecto.
10.3.Haga clic con el botón derecho en Red Hat JBoss EAP 7.0 en la pestaña Servers
(Servidores) y haga clic en Stop (Detener) para detener la instancia EAP.
Esto concluye el ejercicio guiado.
266
JB183-EAP7.0-es-2-20180124
Consumo de un servicio REST
Consumo de un servicio REST
Objetivos
Tras finalizar esta sección, los estudiantes deberán ser capaces de realizar lo siguiente:
• Invoque una API REST de manera remota.
• Describa los códigos de estado HTTP.
Consumo de Servicios REST
Una de las principales ventajas de los servicios REST es que existen muchas formas de
interactuar con los servicios o consumirlos. Los desarrolladores no están limitados a un
idioma o enfoque específico para utilizar un servicio REST. Como se vio en el ejercicio
anterior, puede usar un simple comando curl para comunicarse con un extremo REST o usar
un complemento (plug-in) de explorador para probar un servicio REST.
Si bien curl y otros enfoques de solicitud HTTP básica proporcionan un enfoque más simple
y rápido de interactuar y probar un servicio REST, muchos desarrolladores prefieren utilizar
librerías específicas de un idioma para comunicarse mediante programación con los servicios
REST para proporcionar una mayor funcionalidad y control de las solicitudes y respuestas.
En Java, por ejemplo, JAX-RS 2.0 proporciona una nueva API que puede enviar y recibir
solicitudes de HTTP a servicios web RESTful locales y remotos. Existen tres componentes
diferenciados de un cliente JAX-RS:
• Client: el cliente crea instancias de WebTarget.
• WebTarget: el destino URL de un extremo REST.
• Response: el objeto que contiene la respuesta del servicio.
El primer paso para crear un cliente REST es iniciar un objeto Client mediante
ClientBuilder. El siguiente código crea un nuevo Client:
Client client = ClientBuilder.newClient();
Al final de la solicitud, asegúrese de cerrar también el Client mediante el siguiente código:
client.close();
A continuación, use el Client recientemente creado para crear una instancia WebTarget
basada en el extremo REST. El siguiente código crea un WebTarget que se conecta a un
servicio REST que se ejecuta a nivel local, disponible en http://localhost:8080/todo/
api/users:
Client client = ClientBuilder.newClient();
WebTarget webTarget = client.target("http://localhost:8080/todo/api/users");
Por último, para obtener el objeto Response del servicio web, use el webTarget para
configurar y compilar la solicitud. En el siguiente ejemplo se compila y se ejecuta una solicitud
GET HTTP al extremo REST de webTarget y se devuelve un objeto Response:
JB183-EAP7.0-es-2-20180124
267
Capítulo 6. Creación de servicios REST
Client client = ClientBuilder.newClient();
WebTarget webTarget = client.target("http://localhost:8080/todo/api/users");
Response response = webTarget.request().get();
Configuración del objetivo
Muchos desarrolladores descubren que un cliente necesita varios WebTarget para
interactuar con diferentes extremos REST para cumplir con los requisitos de una solicitud o,
más comúnmente, necesitan parametrizar la solicitud. Para ello, los desarrolladores pueden
anexar elementos adicionales para ampliar la ruta del WebTarget. En el siguiente ejemplo
se demuestra cómo crear una instancia WebTarget de la ruta adicional, que está dirigida a
http://localhost:8080/todo/api/items:
WebTarget webTarget = client.target("http://localhost:8080/todo/api");
WebTarget items = webTarget.path("items");
El método path() también puede crear una URL parametrizada cuando un servicio REST
requiere un parámetro de ruta. Por ejemplo, el servicio REST de la aplicación To Do List
requiere un ID al intentar devolver un único ítem to-do. La siguiente es la sintaxis del
comando curl para devolver el ítem basado en su ID:
[student@workstation ~]$ curl http://localhost:8080/todo/api/items/{id}
Y en el siguiente ejemplo se demuestra cómo solicitar el ítem con un ID de 1:
[student@workstation ~]$ curl http://localhost:8080/todo/api/items/1
El uso del cliente programático JAX-RS es muy similar al uso de curl. El WebTarget anexa la
variable de ruta y, luego, usa el método resolveTemplate para reemplazar la variable con
el valor deseado. En el siguiente ejemplo se crea la misma solicitud que el comando curl
anterior:
WebTarget webTarget = client.target("http://localhost:8080/todo/api");
WebTarget itemTarget = webTarget.path("items/{id}").resolveTemplate("id", "1");
Además de admitir variables de ruta, la librería del cliente JAX-RS también admite parámetros
de consulta. La siguiente sintaxis genera una solicitud a la URL http://localhost:8080/
todo/api/items?id=1:
WebTarget webTarget = client.target("http://localhost:8080/todo/api");
WebTarget itemTarget = webTarget.path("items").queryParam("id", "1");
Creación de la solicitud
El método request() de la clase WebTarget permite a los desarrolladores definir el tipo de
solicitud HTTP para realizar al extremo REST. Por ejemplo, la siguiente solicitud representa
una solicitud GET en el WebTarget y devuelve un objeto de respuesta:
Response response = webTarget.request().get();
268
JB183-EAP7.0-es-2-20180124
Análisis de la respuesta
Una solicitud POST utiliza el método post() para enviar datos en un formato específico,
como XML o JSON, al servicio REST. Por ejemplo, el siguiente código realiza una solicitud POST
a un WebTarget, enviando una entidad Item formateada como JSON:
Item item = new Item(1, "Clean the car", "false")
Response response = webTarget.request().post(Entity.json(item));
Cada tipo de solicitud HTTP cuenta con un método correspondiente. Los siguientes métodos
están disponibles para solicitudes HTTP:
• get()
• post()
• delete()
• put()
Análisis de la respuesta
Después de realizar una solicitud con el WebTarget, el destino devuelve un objeto Response.
Este objeto Response se debe asignar a la entidad o tipo de objeto esperado. Por ejemplo,
si la solicitud es para un campo único, la respuesta se puede asignar a una String (Cadena).
Si la solicitud es para un objeto complejo, como un Item de la aplicación To Do List, el Item
se debe asignar a Item.class. A continuación, se muestra una solicitud de cliente completa
para obtener un Item específico con un ID de 1 que asigna el objeto Response a un objeto
Item:
Client client = ClientBuilder.newBuilder().build();
WebTarget webTarget = client.target("http://localhost:8080/todo/api");
WebTarget itemTarget = webTarget.path("items/{id}").resolveTemplate("id", "1");
Response response = itemTarget.request().get();
Item foundItem = response.readEntity(Item.class);
client.close();
Cree una nueva instancia de Client mediante ClientBuilder.
Defina la base para una nueva instancia WebTarget.
Cree otra instancia WebTarget que compile sobre la ruta del WebTarget de base,
solicitando el Item con ID 1.
Use el WebTarget para realizar una solicitud GET.
Asigne el objeto Response a una entidad Item.
Cierre el cliente cuando no existan más WebTarget para inicializar.
Autenticación con Clientes REST
En muchas instancias, las API REST se protegen con algún tipo de seguridad, como la
autenticación Basic o Digest. La autenticación Basic a menudo es la forma más simple y más
común de restringir el acceso a las API REST. Para ello se requiere que los desarrolladores
provean credenciales cifradas con un Codificador Base 64 al realizar solicitudes a la API REST.
El siguiente es un ejemplo de solicitud para un recurso protegido con autenticación Basic:
JB183-EAP7.0-es-2-20180124
269
Capítulo 6. Creación de servicios REST
String username = "redhat";
String password = "Shadowman";
String userpass = username + ":" + password;
String encodedCred = new BASE64Encoder().encode(userpass.getBytes());
Client client = ClientBuilder.newBuilder().build();
WebTarget webTarget = client.target("http://localhost:8080/todo/api");
WebTarget itemTarget = webTarget.path("items/{id}").resolveTemplate("id", "1");
Response response = itemTarget.request().header("Authorization", "Basic " +
encodedCred).get();
Item foundItem = response.readEntity(Item.class);
client.close();
Una cadena del nombre de usuario que puede acceder al recurso solicitado.
La contraseña del nombre de usuario.
La cadena que representa el formato requerido para el encabezado de autenticación
Basic. La sintaxis es username:password.
La cadena que contiene las credenciales codificadas de Base 64.
La solicitud requiere el encabezado "Autorización" y la carga debe contener una cadena
con el siguiente formato: "Basic <encodedCredentials>.
Con motivo de pruebas, el complemento (plug-in) de cliente REST en Firefox proporciona
a los desarrolladores la capacidad de agregar encabezados de Autenticación para acceder
a recursos protegidos con autenticación Basic. Para agregar autenticación, haga clic en
AuthenticationBasic Authentication.
Figura 6.5: El diálogo de credenciales de autenticación Basic
Ingrese las credenciales de usuario y haga clic en Okay (Aceptar). La sección Headers
(Encabezados) cuenta con las credenciales codificadas automáticamente para la solicitud
HTTP.
270
JB183-EAP7.0-es-2-20180124
Códigos de Estado HTTP
Figura 6.6: El encabezado de autenticación de REST client
Códigos de Estado HTTP
Al realizar una solicitud HTTP, el servidor devuelve un código de estado para indicarle al
cliente si la solicitud se realizó correctamente o si se produjo un error, informando el tipo
de error mediante una variedad de códigos numéricos. Los códigos del estado HTTP más
comunes:
Tabla de Códigos de Estado HTTP
Código del Estado
HTTP
Descripción
200
"OK" (Correcto). La solicitud se realizó correctamente.
400
"Bad Request" (Mala solicitud). La solicitud está dañada o indica el
extremo erróneo.
403
"Forbidden" (Prohibido). Ese cliente no ingresó las credenciales
correctas.
404
"Not Found" (No encontrado). No se encontró la ruta o extremo o el
recurso no existe.
405
"Method Not Allowed" (Método no permitido). El cliente intentó usar
un método HTTP en un extremo que no lo admite.
409
"Conflict" (Conflicto). El objeto solicitado no se puede crear porque
ya existe.
500
"Internal Server Error" (Error interno del servidor). El servidor no
pudo procesar la solicitud. Póngase en contacto con el propietario
del servicio REST para investigar el motivo.
Referencias
API de Java JSR-311 para servicios web RESTful 2.0
http://www.jcp.org/en/jsr/detail?id=311
API de Java JSR-224 para servicios web basados en XML 2.2
http://www.jcp.org/en/jsr/detail?id=224
JB183-EAP7.0-es-2-20180124
271
Capítulo 6. Creación de servicios REST
Referencias
Para obtener más información, consulte la Guía de desarrollo de servicios web para
Red Hat JBoss EAP 7 en
https://access.redhat.com/documentation/en-us/
red_hat_jboss_enterprise_application_platform/
272
JB183-EAP7.0-es-2-20180124
Cuestionario: Consumo de un servicio REST
Cuestionario: Consumo de un servicio REST
Una los siguientes ítems con sus equivalentes en la tabla.
"Bad Request" (Mala solicitud).
"Internal Server Error" (Error interno del servidor).
"OK" (Correcto).
Crea instancias de objetos que indican extremos REST.
Representa un extremo REST.
Un objeto devuelto de una solicitud.
Término
Definición
Código del Estado
HTTP 200
Código del Estado
HTTP 400
Código del Estado
HTTP 500
WebTarget
Client
JB183-EAP7.0-es-2-20180124
273
Capítulo 6. Creación de servicios REST
Término
Definición
Response
274
JB183-EAP7.0-es-2-20180124
Solución
Solución
Una los siguientes ítems con sus equivalentes en la tabla.
Término
Definición
Código del Estado
HTTP 200
"OK" (Correcto).
Código del Estado
HTTP 400
"Bad Request" (Mala solicitud).
Código del Estado
HTTP 500
"Internal Server Error" (Error interno del
servidor).
WebTarget
Representa un extremo REST.
Client
Crea instancias de objetos que indican
extremos REST.
Response
Un objeto devuelto de una solicitud.
JB183-EAP7.0-es-2-20180124
275
Capítulo 6. Creación de servicios REST
Trabajo de laboratorio: Creación de servicios
REST
En este trabajo de laboratorio, aprenderá a exponer el servicio REST de la aplicación To Do
List Java EE.
Resultados
Deberá ser capaz de exponer y probar el servicio REST de la aplicación To Do List Java EE.
Antes de comenzar
Abra una ventana de terminal en la máquina virtual workstation y ejecute el siguiente
comando para descargar los archivos necesarios para este trabajo de laboratorio.
[student@workstation ~]$ lab todo-rest setup
Pasos
1. Importe el proyecto todo-rest en JBoss Developer Studio IDE (JBDS).
2.
Cree la clase de contexto de raíz nombrada JaxRsActivator.java en el
paquete com.redhat.training.todo.rest para el servicio REST. Establezca la
ApplicationPath como /api.
3.
Cree y configure la anotación Path (Ruta) para que la clase
com.redhat.training.todo.rest.ItemResourceRESTService.java realice los
extremos para esta clase de servicio web disponible en http://localhost:8080/
todo-rest/api/items/.
4.
Existen cuatro métodos en la clase ItemResourceRESTService.java que necesitan
exponerse como extremo REST:
• listAllItems()
• lookupItemById(long id)
• createItem(Item item)
• deleteItem(long id)
Actualice el método listAllItems() en la clase ItemResourceRESTService.java
para cumplir con los siguientes requisitos:
• Disponible con una solicitud GET.
• Produce JSON.
5.
Actualice el método lookupItemById(long id) en la clase
ItemResourceRESTService.java para cumplir con los siguientes requisitos:
• Disponible con una solicitud GET.
• Produce JSON.
276
JB183-EAP7.0-es-2-20180124
• Cree un parámetro de ruta para el id, de manera que un item individual esté
disponible con la siguiente URL: http://localhost:8080/todo-rest/apiitems/{id}
6.
Actualice el método createItem(Item item) en la clase
ItemResourceRESTService.java para cumplir con los siguientes requisitos:
• Disponible con la solicitud POST.
• Produce y consume JSON.
7.
Actualice el método deleteItem(long id) en la clase
ItemResourceRESTService.java para cumplir con los siguientes requisitos:
• Disponible con la solicitud DELETE.
• Cree un parámetro de ruta para el id, de manera que se elimine un item individual
con la solicitud DELETE en la siguiente URL: http://localhost:8080/todo-rest/
api-items/{id}
8.
Seleccione la pestaña Servers (Servidores) en el panel inferior de JBDS para iniciar EAP.
Haga clic con el botón derecho en el servidor Red Hat JBoss EAP 7.0 [Stopped] y haga clic
en el botón verde de inicio para iniciar el servidor.
9.
Implemente la aplicación todo-rest mediante los siguientes comandos en la ventana del
terminal:
[student@workstation ~]$ cd /home/student/JB183/labs/todo-rest
[student@workstation todo-rest]$ mvn wildfly:deploy
10. Cree un Item mediante el complemento (plug-in) de cliente REST de Firefox al realizar
una solicitud POST a http://localhost:8080/todo-rest/api/items
11. Pruebe la funcionalidad para enumerar todos los ítems y elimine el Item agregado en el
paso anterior mediante curl o el complemento (plug-in) de Firefox.
12. Limpieza y calificación.
12.1.Para verificar que ha implementado correctamente la aplicación, abra una nueva
ventana de terminal en la máquina virtual workstation y ejecute el siguiente
comando para calificar este trabajo de laboratorio:
[student@workstation ~]$ lab todo-rest grade
Si observa fallas después de ejecutar el comando, vea los errores, solucione los
problemas de implementación y corrija los errores. Vuelva a ejecutar el script de
calificación y verifique que el resultado sea satisfactorio.
12.2.En la ventana del terminal donde ejecutó el comando Maven para implementar
la aplicación, ejecute el siguiente comando para anular la implementación de la
aplicación de EAP:
[student@workstation todo-rest]$ mvn wildfly:undeploy
JB183-EAP7.0-es-2-20180124
277
Capítulo 6. Creación de servicios REST
12.3.Haga clic con el botón derecho en el proyecto todo-rest en Project Explorer
(Explorador de proyectos) y seleccione Close Project (Cerrar proyecto).
12.4.Haga clic con el botón derecho en Red Hat JBoss EAP 7.0 en la pestaña Servers
(Servidores) y haga clic en Stop (Detener) para detener la instancia EAP.
Esto concluye el trabajo de laboratorio.
278
JB183-EAP7.0-es-2-20180124
Solución
Solución
En este trabajo de laboratorio, aprenderá a exponer el servicio REST de la aplicación To Do
List Java EE.
Resultados
Deberá ser capaz de exponer y probar el servicio REST de la aplicación To Do List Java EE.
Antes de comenzar
Abra una ventana de terminal en la máquina virtual workstation y ejecute el siguiente
comando para descargar los archivos necesarios para este trabajo de laboratorio.
[student@workstation ~]$ lab todo-rest setup
Pasos
1. Importe el proyecto todo-rest en JBoss Developer Studio IDE (JBDS).
1.1. Haga clic en el icono JBDS de la máquina virtual workstation para iniciar JBDS.
1.2. En el menú de JBDS, haga clic en File (Archivo) > Import (Importar)para abrir el
asistente para Import (Importar).
1.3. En la página Select (Seleccionar), seleccione Maven > Existing Maven Projects
(Proyectos de Maven existentes) y, luego, haga clic en Next (Siguiente).
1.4. En la página Maven projects (Proyectos de Maven), haga clic en Browse (Explorar)
para abrir la ventana Select root folder (Seleccionar carpeta raíz).
1.5. Diríjase al directorio /home/student/JB138/labs/. Seleccione la carpeta todorest y haga clic en OK (Aceptar).
1.6. En la página Maven projects (Proyectos de Maven), haga clic en Finish (Finalizar).
1.7. Espere hasta que JBDS termine de importar el proyecto.
2.
Cree la clase de contexto de raíz nombrada JaxRsActivator.java en el
paquete com.redhat.training.todo.rest para el servicio REST. Establezca la
ApplicationPath como /api.
2.1. En el ítem todo-rest de la pestaña Project Explorer (Explorador de proyectos) del
panel izquierdo de JBDS, seleccione todo-rest > Java Resources > src/main/java >
com.redhat.training.todo.rest y expanda el paquete.
2.2. Haga clic con el botón derecho en com.redhat.training.todo.rest y haga clic en New
(Nueva) > Class (Clase).
2.3. En el campo Name (Nombre), ingrese JaxRsActivator. Haga clic en Finish
(Finalizar).
2.4. En la nueva clase, agregue la anotación @ApplicationPath, importe la librería y
especifique la ruta como /api:
package com.redhat.training.todo.rest;
import javax.ws.rs.ApplicationPath;
JB183-EAP7.0-es-2-20180124
279
Capítulo 6. Creación de servicios REST
@ApplicationPath("/api")
Public class JaxRsActivator {
}
2.5. Complete la clase importando y ampliando la clase
javax.ws.rs.core.Application:
package com.redhat.training.todo.rest;
import javax.ws.rs.ApplicationPath;
import javax.ws.rs.core.Application;
@ApplicationPath("/api")
Public class JaxRsActivator extends Application {
}
2.6. Presione Ctrl+S para guardar sus cambios.
3.
Cree y configure la anotación Path (Ruta) para que la clase
com.redhat.training.todo.rest.ItemResourceRESTService.java realice los
extremos para esta clase de servicio web disponible en http://localhost:8080/
todo-rest/api/items/.
Abra y actualice el servicio web RESTful ItemResourceRESTService.java en el
paquete com.redhat.training.todo.rest para establecer la @Path para el servicio /
items:
// *Add the path annotation*
@Path("/items")
@RequestScoped
public class ItemResourceRESTService {
4.
Existen cuatro métodos en la clase ItemResourceRESTService.java que necesitan
exponerse como extremo REST:
• listAllItems()
• lookupItemById(long id)
• createItem(Item item)
• deleteItem(long id)
Actualice el método listAllItems() en la clase ItemResourceRESTService.java
para cumplir con los siguientes requisitos:
• Disponible con una solicitud GET.
• Produce JSON.
4.1. En la clase ItemResourceRESTService.java, agregue una anotación @GET al
método listAllItems():
280
JB183-EAP7.0-es-2-20180124
Solución
// *Add listAllItems() annotations* //
@GET
public List<Item> listAllItems() {
return repository.findAllItemsForUser(currentUser);
}
4.2. Establezca el método listAllItems() para producir JSON con la siguiente
anotación:
// *Add listAllItems() annotations* //
@GET
@Produces(MediaType.APPLICATION_JSON)
public List<Item> listAllItems() {
return repository.findAllItemsForUser(currentUser);
}
4.3. Presione Ctrl+S para guardar sus cambios.
5.
Actualice el método lookupItemById(long id) en la clase
ItemResourceRESTService.java para cumplir con los siguientes requisitos:
• Disponible con una solicitud GET.
• Produce JSON.
• Cree un parámetro de ruta para el id, de manera que un item individual esté
disponible con la siguiente URL: http://localhost:8080/todo-rest/apiitems/{id}
5.1. En la clase ItemResourceRESTService.java, agregue una anotación @GET al
método lookupItemById(long id):
// *Add lookupItemById() annotations* //
@GET
public Item lookupItemById(long id) {
5.2. Cree una anotación @Path y establézcala para ser el parámetro de id configurando
la anotación @PathParam en el encabezado del método:
// *Add lookupItemById() annotations* //
@GET
@Path("/{id}")
public Item lookupItemById(@PathParam("id") long id) {
5.3. Establezca el método lookupItemById(long id) para producir JSON con la
siguiente anotación:
// *Add lookupItemById() annotations* //
@GET
@Path("/{id}")
@Produces(MediaType.APPLICATION_JSON)
public Item lookupItemById(@PathParam("id") long id) {
JB183-EAP7.0-es-2-20180124
281
Capítulo 6. Creación de servicios REST
5.4. Presione Ctrl+S para guardar sus cambios.
6.
Actualice el método createItem(Item item) en la clase
ItemResourceRESTService.java para cumplir con los siguientes requisitos:
• Disponible con la solicitud POST.
• Produce y consume JSON.
6.1. En la clase ItemResourceRESTService.java, agregue una anotación @POST al
método createItem(Item item):
// *Add createItem() annotations* //
@POST
public Response createItem(Item item) {
6.2. Establezca el método createItem(Item item) para consumir JSON con la
siguiente anotación:
// *Add createItem() annotations* //
@POST
@Consumes(MediaType.APPLICATION_JSON)
public Response createItem(Item item) {
6.3. Establezca el método createItem(Item item) para producir JSON con la siguiente
anotación:
// *Add createItem() annotations* //
@POST
@Consumes(MediaType.APPLICATION_JSON)
@Produces(MediaType.APPLICATION_JSON)
public Response createItem(Item item) {
6.4. Presione Ctrl+S para guardar sus cambios.
7.
Actualice el método deleteItem(long id) en la clase
ItemResourceRESTService.java para cumplir con los siguientes requisitos:
• Disponible con la solicitud DELETE.
• Cree un parámetro de ruta para el id, de manera que se elimine un item individual
con la solicitud DELETE en la siguiente URL: http://localhost:8080/todo-rest/
api-items/{id}
7.1. En la clase ItemResourceRESTService.java, agregue una anotación @DELETE al
método deleteItem(long id):
//*Add deleteItem() annotations*//
@DELETE
public void deleteItem(long id) {
itemService.remove(id);
}
282
JB183-EAP7.0-es-2-20180124
Solución
7.2. Cree una anotación @Path y establézcala para ser el parámetro de id configurando
la anotación @PathParam en el encabezado del método:
//*Add deleteItem() annotations*//
@DELETE
@Path("/{id}")
public void deleteItem(@PathParam("id") long id) {
itemService.remove(id);
}
7.3. Presione Ctrl+S para guardar sus cambios.
8.
Seleccione la pestaña Servers (Servidores) en el panel inferior de JBDS para iniciar EAP.
Haga clic con el botón derecho en el servidor Red Hat JBoss EAP 7.0 [Stopped] y haga clic
en el botón verde de inicio para iniciar el servidor.
9.
Implemente la aplicación todo-rest mediante los siguientes comandos en la ventana del
terminal:
[student@workstation ~]$ cd /home/student/JB183/labs/todo-rest
[student@workstation todo-rest]$ mvn wildfly:deploy
10. Cree un Item mediante el complemento (plug-in) de cliente REST de Firefox al realizar
una solicitud POST a http://localhost:8080/todo-rest/api/items
10.1.Inicie Firefox en la máquina virtual workstation y haga clic en el complemento
(plug-in) de cliente REST en la barra de herramientas del explorador.
Complemento (plug-in) de cliente REST de Firefox
10.2.En la barra de herramientas superior, haga clic en Headers (Encabezados) y en
Custom Header (Encabezado personalizado) para agregar un nuevo encabezado
personalizado a la solicitud.
10.3.Introduzca la siguiente información en el cuadro de diálogo del encabezado
personalizado:
• Name (Nombre): Content-Type
• Value (Valor): application/json
JB183-EAP7.0-es-2-20180124
283
Capítulo 6. Creación de servicios REST
Creación de un encabezado de solicitud personalizado en el cliente REST
Haga clic en Okay (Aceptar).
10.4.Seleccione POST como el Method (Método). En el formulario URL, ingrese http://
localhost:8080/todo-rest/api/items.
10.5.En la sección Body (Cuerpo) de la solicitud, agregue la siguiente representación JSON
de una entidad Item:
{"description":"Complete chapter 6 lab"}
Haga clic en Send (Enviar).
10.6.Verifique en la pestaña Response Headers (Encabezados de respuesta) que Status
Code (Código de estado) sea 200 OK.
Creación de un encabezado de solicitud personalizado en el cliente REST
11. Pruebe la funcionalidad para enumerar todos los ítems y elimine el Item agregado en el
paso anterior mediante curl o el complemento (plug-in) de Firefox.
11.1.En una ventana de terminal, use el siguiente comando curl para realizar una
solicitud HTTP GET para recuperar todos los objetos Item:
284
JB183-EAP7.0-es-2-20180124
Solución
[student@workstation todo-rest]$ curl \
http://localhost:8080/todo-rest/api/items
Busque el Item creado en el paso anterior en la salida:
[{"id":1,"description":"Complete chapter 6 lab","done":false,"user":
{"id":1,"username":"Guest"}}]
11.2.Elimine el nuevo Item mediante la siguiente solicitud DELETE HTTP con el ID de Item
correcto:
[student@workstation todo-rest]$ curl -X DELETE \
http://localhost:8080/todo-rest/api/items/1
11.3.Ejecute el siguiente comando para verificar que se haya eliminado el Item:
[student@workstation todo-rest]$ curl http://localhost:8080/todo-rest/api/items
En la salida se muestra que el objeto Item ya no se encuentra en la base de datos.
12. Limpieza y calificación.
12.1.Para verificar que ha implementado correctamente la aplicación, abra una nueva
ventana de terminal en la máquina virtual workstation y ejecute el siguiente
comando para calificar este trabajo de laboratorio:
[student@workstation ~]$ lab todo-rest grade
Si observa fallas después de ejecutar el comando, vea los errores, solucione los
problemas de implementación y corrija los errores. Vuelva a ejecutar el script de
calificación y verifique que el resultado sea satisfactorio.
12.2.En la ventana del terminal donde ejecutó el comando Maven para implementar
la aplicación, ejecute el siguiente comando para anular la implementación de la
aplicación de EAP:
[student@workstation todo-rest]$ mvn wildfly:undeploy
12.3.Haga clic con el botón derecho en el proyecto todo-rest en Project Explorer
(Explorador de proyectos) y seleccione Close Project (Cerrar proyecto).
12.4.Haga clic con el botón derecho en Red Hat JBoss EAP 7.0 en la pestaña Servers
(Servidores) y haga clic en Stop (Detener) para detener la instancia EAP.
Esto concluye el trabajo de laboratorio.
JB183-EAP7.0-es-2-20180124
285
Capítulo 6. Creación de servicios REST
Resumen
En este capítulo, aprendió lo siguiente:
• JAX-RS es la API de Java para crear servicios web RESTful livianos.
• Implementar una capa de servicio web permite a los desarrolladores abstraer la capa
de front-end y crear una aplicación compuesta por muchos componentes con bajo
acoplamiento.
• Un servicio web RESTful JAX-RS consta de una o más clases que utilizan las anotaciones JAXRS para crear un servicio web.
• La anotación @ApplicationPath establece la base URI para el servicio web.
• El protocolo HTTP define varios métodos mediante los cuales el protocolo ejecuta
diferentes acciones. El cliente es responsable de especificar el tipo de solicitud, además de
la ruta a la solicitud.
• La anotación @PathParam asigna una variable de una ruta al parámetro del método Java.
• El método request() de la clase WebTarget permite a los desarrolladores definir el tipo
de solicitud HTTP para realizar a un extremo REST.
• Un estado de código HTTP de 200 indica que la solicitud se realizó correctamente.
286
JB183-EAP7.0-es-2-20180124
TRAINING
CAPÍTULO 7
IMPLEMENTACIÓN DE
CONTEXTOS E INYECCIÓN DE
DEPENDENCIA (CDI)
Descripción general
Meta
Describir casos de uso típicos para utilizar la CDI e
implementarla correctamente en una aplicación.
Objetivos
• Describir la inyección de recursos, la inyección de
dependencias y las diferencias entre ellas.
• Aplicar alcances en beans de manera adecuada.
Secciones
• Contraste entre la inyección de dependencias y la
inyección de recursos (y ejercicio guiado)
• Aplicación de alcances contextuales (y ejercicio guiado)
Trabajo de laboratorio
JB183-EAP7.0-es-2-20180124
Implementación de Contextos e Inyección de dependencia
(CDI)
287
Capítulo 7. Implementación de Contextos e Inyección de dependencia (CDI)
Contraste entre la inyección de
dependencias y la inyección de recursos
Objetivos
Tras finalizar esta sección, los estudiantes deberán ser capaces de realizar lo siguiente:
• Describir la inyección de dependencias.
• Describir cómo crear un calificador.
• Usar un productor para inyectar elementos que no sean beans.
Comprensión de la inyección de dependencias de
contexto
La especificación Contextos e Inyección de dependencia (CDI) es una de muchas
especificaciones secundarias dentro de la especificación Java EE. Aunque CDI se incorporó en
Java EE 6, los conceptos detrás de CDI han estado presentes en varios marcos (frameworks)
como Spring, Google Guice y otros. El Proceso de la comunidad Java presentó la forma
definitiva de la Solicitud de especificación Java 299 en diciembre de 2009. JSR 346 define
formalmente CDI para la plataforma Java EE 7. Esto significa que todo servidor de
aplicaciones certificado como un servidor que cumple con Java EE 7, como JBoss EAP, debe
admitir los contextos y la inyección de dependencia de manera nativa.
Hay dos partes primarias de CDI: Contextos e Inyección de dependencia. Dado que se
relacionan con CDI, los contextos se refieren a la capacidad de definir aplicaciones por
alcance de datos, lo que se debate en mayor detalle en la próxima sección. La inyección
de dependencias, según la especifica CDI, es el proceso mediante el cual se pueden crear
instancias de objetos automáticamente en otros objetos de aplicación con seguridad
de tipos. La decisión de qué implementación específica de un objeto se inyectará puede
demorarse hasta la implementación de la aplicación. En otros marcos (frameworks), la
inyección se basa en la coincidencia de cadenas. CDI mejora esto a través de una inyección de
tipos, en la que los tipos se controlan en el momento de la compilación. Incluir la seguridad
de tipos expone los errores de la inyección de manera anticipada en el ciclo de vida de
desarrollo y simplifica la depuración.
Uno de los principales beneficios de la inyección de dependencias (Dependency Injection, DI)
es el bajo acoplamiento de los componentes de la aplicación. Por ejemplo, los componentes
de cliente y servidor se acoplan levemente porque se pueden insertar varias versiones
diferentes del servidor en el cliente. El cliente trabaja con una interfaz y desconoce con
qué servidor está hablando. Es posible aprovechar la inyección durante el tiempo de
implementación para usar objetos específicos para diferentes tipos de entornos, como los de
producción y pruebas. Por ejemplo, puede inyectar una fuente de datos de producción o de
pruebas en función de su entorno de implementación.
CDI es similar al uso de la inyección de recursos para inyectar recursos como
@PersistenceContext y un archivo persistence.xml. Ambos enfoques crean
dependencias de recursos administradas por el contenedor y acoplan de manera flexible los
componentes de la aplicación. Sin embargo, se diferencian de varias maneras. Dado que la
inyección de recursos usa el nombre JNDI para inyectar un recurso, la inyección de recursos
288
JB183-EAP7.0-es-2-20180124
Uso de la inyección de dependencias
no tiene seguridad de tipos como CDI. CDI tiene seguridad de tipos porque las instancias
de los objetos se crean según el tipo. Además, CDI puede inyectar clases de Java regulares,
mientras que la inyección de recursos no puede inyectar clases regulares y, en cambio, hace
referencia al recurso por nombres de JNDI.
Uso de la inyección de dependencias
CDI no está automáticamente activa en una aplicación web, EJB o librería Java (JAR) porque
no sería eficiente que el contenedor analizara cada aplicación y cada librería. Para habilitar
CDI en las aplicaciones web, coloque un archivo vacío nombrado beans.xml en el directorio
WEB-INF. Para los archivos JAR, incluso los que contienen EJB, coloque el archivo beans.xml
en el directorio META-INF. El archivo marcador beans.xml no necesita contener nada para
activar CDI.
No hay declaración ni anotación especial necesaria para que un bean participe en CDI. Esto
se compara con un EJB, que requiere una anotación que marque su tipo como @Stateless,
@MessageDriven, etc.
Para inyectar una instancia de un bean en una variable de instancia de otra clase, use
la anotación @Inject. Cuando el contenedor examine la clase anotada en el momento
de la implementación, intentará encontrar un único bean que coincida con el tipo de
bean anotado. Si el contenedor encuentra más de una coincidencia, creará un error de
dependencia ambigua. Por lo general, la anotación @Inject se utiliza con una declaración
de miembro o en el parámetro constructor de la clase Java. La siguiente es una clase de
utilidades de ejemplo nombrada EmailValidator con un método checkEmail público:
public class EmailValidator {
public bool checkEmail(String email) {
...
}
}
Para aprovechar esta clase y el método con CDI, inyecte una instancia de la clase mediante el
siguiente código de inyección:
public class Form {
...
@Inject
private EmailValidator emailValidator;
public void submitForm(){
...
emailValidator.checkEmail(email);
...
}
}
Observe que nunca se declara una nueva clase EmailValidator. Mediante la inyección, el
contenedor crea automáticamente instancias de la clase EmailValidator.
Uso de calificadores
Un calificador es una anotación personalizada que se puede aplicar a una clase de bean
en el lugar de la inyección para definir el tipo de bean que se inyectará. Esto es útil cuando
JB183-EAP7.0-es-2-20180124
289
Capítulo 7. Implementación de Contextos e Inyección de dependencia (CDI)
un bean inyecta una interfaz, pero hay varias implementaciones que utilizan la misma
interfaz. Cuando ocurre este tipo de inyección ambigua, el contenedor no puede elegir cuál
es la implementación que debe inyectar. Los calificadores resuelven esta ambigüedad al
permitirles a los usuarios crear anotaciones de calificador personalizadas para indicar cuál es
la implementación que el contenedor debe utilizar.
Los calificadores se definen mediante la siguiente plantilla:
@Qualifier
@Retention(RUNTIME)
@Target({TYPE, METHOD, FIELD, PARAMETER}) public @interface qualifier-name { }
Por ejemplo, el siguiente es un calificador que crea la anotación @SlowBike:
@Qualifier
@Retention(RUNTIME)
@Target({TYPE, METHOD, FIELD, PARAMETER}) public @interface SlowBike { }
Después de crear ese calificador, anote el encabezado de la clase con @SlowBike:
@SlowBike
public class Moped extends Bike implements Vehicle<Motorized> {
Cuando un bean inyecta la interfaz Vehicle y usa la anotación SlowBike, el contenedor crea
automáticamente una instancia de la implementación de la clase Moped:
public class Inventory {
@Inject
@SlowBike
private Bike myBike;
...
}
Por defecto, todos los beans de CDI tienen un calificador. Si no se especifica uno, el
calificador es @Default. Además, si se anota un bean de manera explícita con la anotación
@Default, ese bean funciona como la implementación predeterminada cuando no se
especifica otro calificador en el punto de inyección.
Uso de productores
Una ventaja del uso de CDI es que se pueden retrasar las decisiones de implementación
después del tiempo de compilación. Para facilitar este comportamiento, los Productores
proporcionan la capacidad de que se tomen decisiones de implementación en el tiempo de
ejecución mediante lógica personalizable para determinar cómo tomar estas decisiones.
Los productores son métodos o atributos de objeto que producen un objeto inyectable. La
ventaja de los productores es que la anotación permite que se puedan inyectar objetos que
no sean beans.
La anotación @Produces se puede adjuntar a los siguientes objetos:
• Beans administrados
• Primitivos, como int y long
290
JB183-EAP7.0-es-2-20180124
Comparación entre EJB y CDI
• Tipos con parámetros, como Set<String>
• Tipos sin formato, como Set
El siguiente es un ejemplo de un método anotado con @Produces y la inyección de
PaymentStrategy:
@Produces @Preferred public PaymentStrategy getStrategicPaymentStrategy() { ... }
public class CalculateInterestBean {
...
@Inject @Preferred PaymentStrategy strategy;
...
En el ejemplo anterior se muestra un productor que se utiliza en una declaración de método.
Es muy común utilizar un calificador en un método de productor para distinguir el tipo de
objeto disponible para la inyección. Combinar calificadores y productores les permite a
los desarrolladores proporcionar varios métodos de productor y, luego, usar inyecciones
ambiguas con un calificador para distinguir cuál es el método de productor que se debe
utilizar. En el ejemplo anterior, el objeto se inyecta con el calificador @Preferred.
Cuando anota un atributo en una clase Java con @Produces, puede inyectarse en un atributo
en un bean administrado. Esto es útil para declarar y utilizar recursos Java EE, como fuentes
de datos y registradores. Al igual que los métodos productores, los campos productores a
menudo se anotan con un calificador.
@Produces @AccountsDB Connection connection = new Connection(userId, password);
Comparación entre EJB y CDI
Diferenciar EJB y CDI es importante porque hay una superposición de características entre las
dos especificaciones. En aplicaciones Java EE 7 compiladas para ejecutarse en JBoss EAP, es
común que los desarrolladores usen ambas tecnologías en conjunto. Todos los EJB son beans
de CDI y, por lo tanto, tienen acceso a la inyección de dependencias y son elegibles para ser
inyectados.
La especificación de EJB se basa en la especificación de CDI y proporciona incluso más
funcionalidades, distinguiendo entre beans con y sin estado. Los EJB también proporcionan
otras funcionalidades como características de concurrencia, agrupación de beans y
seguridad, así como otras que no están incluidas en CDI. Cuando crea un bean, se
recomienda no usar un EJB si no se requieren las características de un EJB. En cambio, use
CDI para administrar contextos e inyección de dependencia.
Referencias
JSR 346 Contextos e inyección de dependencia para Java
https://www.jcp.org/en/jsr/detail?id=346
JB183-EAP7.0-es-2-20180124
291
Capítulo 7. Implementación de Contextos e Inyección de dependencia (CDI)
Referencias
Para obtener más información, consulte el capítulo de CDI de la Guía de desarrollo
para Red Hat JBoss EAP:
https://access.redhat.com/documentation/en/red-hat-jboss-enterpriseapplication-platform/7.0/
292
JB183-EAP7.0-es-2-20180124
Ejercicio guiado: Inyección de dependencia
Ejercicio guiado: Inyección de dependencia
En este ejercicio, creará e inyectará una clase de utilidades con un calificador.
Resultados
Deberá poder crear un calificador e inyectar un bean.
Antes de comenzar
Abra una ventana de terminal en la máquina virtual workstation y ejecute el siguiente
comando para descargar los archivos del trabajo de laboratorio necesarios para este ejercicio
guiado.
[student@workstation ~]$ lab dependency-injection setup
Pasos
1. Importe el proyecto dependency-injection en el IDE de JBoss Developer Studio (JBDS).
1.1. Inicie JBDS haciendo doble clic en el icono del escritorio de la máquina virtual
workstation.
1.2. En la ventana Eclipse Launcher, ingrese /home/student/JB183/workspace en el
campo Workspace (Espacio de trabajo) y, luego, haga clic en Launch (Iniciar).
1.3. En el menú de JBDS, haga clic en File (Archivo) > Import (Importar)para abrir el
asistente para Import (Importar).
1.4. En la página Select (Seleccionar), haga clic en Maven > Existing Maven Projects
(Proyectos de Maven existentes) y, luego, haga clic en Next (Siguiente).
1.5. En la página Maven projects (Proyectos de Maven), haga clic en Browse (Explorar)
para abrir la ventana Select root folder (Seleccionar carpeta raíz). Diríjase al
directorio /home/student/JB183/labs/. Seleccione la carpeta dependencyinjection y haga clic en OK (Aceptar).
1.6. En la página Maven projects (Proyectos de Maven), haga clic en Finish (Finalizar).
1.7. Observe la barra de estado de JBDS para monitorear el progreso de la operación
de importación. Es probable que la descarga de todas las dependencias requeridas
demore unos minutos.
2.
Cree una nueva interfaz denominada NameUtil.java con el método
sanitizeName(String name).
2.1. En la pestaña Project Explorer (Explorador de proyectos) en el panel izquierdo de
JBDS, haga clic con el botón derecho en dependency-injection > Java Resources
> src/main/java > com.redhat.training.util y haga clic en New (Nueva) > Interface
(Interfaz).
2.2. Ingrese NameUtil como el nombre de la interfaz y haga clic en Finish (Finalizar).
2.3. Agregue el siguiente código a la nueva interfaz para el método
sanitizeName(String name):
JB183-EAP7.0-es-2-20180124
293
Capítulo 7. Implementación de Contextos e Inyección de dependencia (CDI)
package com.redhat.training.util;
public interface NameUtil {
public String sanitizeName(String name);
}
2.4. Presione Ctrl+S para guardar sus cambios.
3.
Cree dos nuevas clases en el paquete com.redhat.com.training.util que
implementen la interfaz NameUtil.
3.1. En la pestaña Project Explorer (Explorador de proyectos) en el panel izquierdo de
JBDS, haga clic con el botón derecho en dependency-injection > Java Resources >
src/main/java > com.redhat.training.util y haga clic en New (Nueva) > Class (Clase).
3.2. Ingrese AllCaps como el nombre de la clase y haga clic en Finish (Finalizar).
3.3. Actualice el encabezado de la clase para implementar la interfaz NameUtil:
package com.redhat.training.util;
public class AllCaps implements NameUtil{
}
3.4. Desplace el mouse sobre el nombre de la clase AllCaps y haga clic en Add
unimplemented methods (Agregar métodos no implementados) para crear el
método sanitizeName(String name) y eliminar el error. El código resultante
tiene el siguiente aspecto:
@Override
public String sanitizeName(String name) {
// TODO Auto-generated method stub
return null;
}
3.5. Actualice la instrucción return para devolver la cadena name en letras mayúsculas:
return name.toUpperCase();
3.6. Presione Ctrl+S para guardar sus cambios.
3.7. En la pestaña Project Explorer (Explorador de proyectos) en el panel izquierdo de
JBDS, haga clic con el botón derecho en dependency-injection > Java Resources >
src/main/java > com.redhat.training.util y haga clic en New (Nueva) > Class (Clase).
3.8. Ingrese TitleCase como el nombre de la clase y haga clic en Finish (Finalizar).
3.9. Actualice el encabezado de la clase para implementar la interfaz NameUtil:
294
JB183-EAP7.0-es-2-20180124
package com.redhat.training.util;
public class TitleCase implements NameUtil{
}
3.10.Desplace el mouse sobre el nombre de la clase TitleCase y haga clic en Add
unimplemented methods (Agregar métodos no implementados) para crear el
método sanitizeName(String name) y eliminar el error. El código resultante
tiene el siguiente aspecto:
@Override
public String sanitizeName(String name) {
// TODO Auto-generated method stub
return null;
}
3.11.Actualice la instrucción return para devolver la cadena name con la primera letra en
mayúscula:
return name.substring(0,1).toUpperCase() + name.substring(1);
3.12.Presione Ctrl+S para guardar sus cambios.
4.
Agregue un método @PostConstruct a cada nuevo bean de utilidades que imprima una
instrucción de registro para declarar cuándo se inyecta el bean.
4.1. En la clase AllCaps.java, agregue el siguiente método @PostConstruct y la
importación javax.annotation.PostConstruct:
import javax.annotation.PostConstruct;
...
@PostConstruct
public void printTitle(){
System.out.println("***AllCaps PostConstruct***");
}
4.2. Presione Ctrl+S para guardar sus cambios.
4.3. En la clase TitleCase.java, agregue el siguiente método @PostConstruct y la
importación javax.annotation.PostConstruct:
import javax.annotation.PostConstruct;
...
@PostConstruct
public void printTitle(){
System.out.println("***TitleCase PostConstruct***");
JB183-EAP7.0-es-2-20180124
295
Capítulo 7. Implementación de Contextos e Inyección de dependencia (CDI)
}
4.4. Presione Ctrl+S para guardar sus cambios.
5.
Inyecte la interfaz NameUtil en la clase PersonService.java y use el método
sanitizeName(String name) antes de conservar el nombre en la base de datos.
5.1. En la pestaña Project Explorer (Explorador de proyectos) en el panel izquierdo
de JBDS, haga clic en dependency-injection > Java Resources > src/main/
java > com.redhat.training.ejb y expándalo. Haga doble clic en el archivo
PersonService.java.
5.2. Después del encabezado de la clase, agregue el siguiente código para inyectar la
interfaz NameUtil en la clase PersonService:
...
public class PersonService {
@Inject
private NameUtil nameUtil;
...
5.3. Agregue la siguiente línea al método hello() para limpiar la entrada del nombre
antes de almacenar la entidad Person en la base de datos y mostrar el nombre:
//TODO sanitize name
name = nameUtil.sanitizeName(name);
5.4. Presione Ctrl+S para guardar sus cambios. Observe que la advertencia del punto
de inyección indica Multiple beans are eligible for injection to the
injection point (Hay varios beans elegibles para la inyección en el punto de
inyección). Esto se debe a que la interfaz no tiene un calificador para indicar cuál es
la implementación que se debe usar.
6.
Cree un nuevo calificador y use el calificador en la clase de utilidades para resolver el
punto de inyección ambiguo.
6.1. En la pestaña Project Explorer (Explorador de proyectos) en el panel izquierdo de
JBDS, haga clic con el botón derecho en dependency-injection > Java Resources
> src/main/java > com.redhat.training.util y haga clic en New (Nuevo) > Other…
(Otro…).
6.2. Ingrese Qualifier (Calificador) en el cuadro de búsqueda. Seleccione Qualifier
Annotation (Anotación de calificador) y haga clic en Next (Siguiente).
6.3. Introduzca Title (Título) en el campo Name (Nombre) y haga clic en Finish (Finalizar).
6.4. En la clase com.redhat.training.util.TitleCase, agregue el calificador al
encabezado de la clase:
@Title
public class TitleCase implements NameUtil{
296
JB183-EAP7.0-es-2-20180124
...
6.5. Presione Ctrl+S para guardar sus cambios.
6.6. Vuelva a PersonService.java y observe que la advertencia ya no está presente.
7.
Seleccione la pestaña Servers (Servidores) en el panel inferior de JBDS para iniciar EAP.
Haga clic con el botón derecho en el servidor Red Hat JBoss EAP 7.0 [Stopped] y haga
clic en el botón verde para iniciar el servidor. Observe la Console (Consola) hasta que el
servidor se inicie y muestre el siguiente mensaje:
INFO [org.jboss.as] (Controller Boot Thread) WFLYSRV0025: JBoss EAP 7.0.0.GA
(WildFly Core 2.1.2.Final-redhat-1) started
8.
Implemente la aplicación en JBoss EAP con Maven; para ello, ejecute los siguientes
comandos:
[student@workstation ~]$ cd /home/student/JB183/labs/dependency-injection
[student@workstation dependency-injection]$ mvn wildfly:deploy
9.
Pruebe la aplicación.
9.1. Use un explorador web en la máquina virtual workstation para ir a http://
localhost:8080/dependency-injection/ y acceder a la aplicación dependencyinjection.
9.2. En el cuadro de texto, ingrese shadowman y haga clic en Submit (Enviar). El servidor
devuelve lo siguiente:
Hello SHADOWMAN! ...
Debido a que no hay un calificador especificado en el punto de inyección de
NameUtil, se utiliza el bean que tiene la anotación @Default.
nota
Si no se especifica un calificador, la clase asume el calificador @Default.
9.3. En los registros del servidor EAP, observe que la salida del método posterior a la
construcción para la clase AllCaps ocurre solo después de que hace clic en Submit
(Enviar), pero antes de que se utilice el método del objeto:
***AllCaps PostConstruct***
###Before NameUtil method used###
Hibernate:
insert
into
Person
...
JB183-EAP7.0-es-2-20180124
297
Capítulo 7. Implementación de Contextos e Inyección de dependencia (CDI)
10. Actualice la inyección NameUtil para usar el calificador @Title.
10.1.Haga clic en el archivo PersonService.java para editar la clase PersonService en el
panel del editor.
10.2.Agregue la siguiente línea para cambiar la implementación para NameUtil e importe
com.redhat.training.util.Title:
@Inject @Title
private NameUtil nameUtil;
10.3.Presione Ctrl+S para guardar sus cambios.
11. Vuelva a implementar la aplicación con el siguiente comando:
[student@workstation dependency-injection]$ mvn wildfly:deploy
12. Pruebe la aplicación nuevamente.
12.1.Use un explorador web en la máquina virtual workstation para ir a http://
localhost:8080/dependency-injection/ y acceder a la aplicación dependencyinjection.
12.2.En el cuadro de texto, ingrese redhat y haga clic en Submit (Enviar). El servidor
devuelve lo siguiente:
Hello Redhat! ...
12.3.En los registros del servidor EAP, observe que se imprime la salida del método
posterior a la construcción para la clase TitleCase:
***TitleCase PostConstruct***
###Before NameUtil method used###
Hibernate:
insert
into
Person
...
13. Anule la implementación de la aplicación y detenga EAP.
13.1.En la ventana del terminal donde ejecutó el comando Maven para implementar
la aplicación, ejecute el siguiente comando para anular la implementación de la
aplicación:
[student@workstation dependency-injection]$ mvn wildfly:undeploy
13.2.Haga clic con el botón derecho en el proyecto dependency-injection en Project
Explorer (Explorador de proyectos) y seleccione Close Project (Cerrar proyecto) para
cerrar este proyecto.
298
JB183-EAP7.0-es-2-20180124
13.3.Haga clic con el botón derecho en Red Hat JBoss EAP 7.0 en la pestaña Servers
(Servidores) y haga clic en Stop (Detener) para detener la instancia EAP.
Esto concluye el ejercicio guiado.
JB183-EAP7.0-es-2-20180124
299
Capítulo 7. Implementación de Contextos e Inyección de dependencia (CDI)
Aplicación de alcances contextuales
Objetivo
Tras finalizar esta sección, los estudiantes deberán ser capaces de aplicar alcances en los
beans de manera adecuada.
Comprensión de la importancia del alcance en las
aplicaciones de Java EE
El contenedor de Red Hat JBoss EAP administra el ciclo de vida de todos los beans de CDI
definidos en las aplicaciones que se implementan en el servidor. Cuando administra los ciclos
de vida de beans con estados, JBoss necesita comprender cuándo se deben conservar las
diferentes instancias de un bean en la memoria y cuándo es seguro destruir la instancia
de un bean. Además, el contenedor necesita comprender cuándo debe crear una nueva
instancia de un bean en lugar de utilizar una existente. La cantidad de tiempo en la que el
contenedor mantiene activa una instancia específica de un bean es conocida como el alcance
de un bean.
Por lo general, cuando se utiliza la inyección de dependencias para inyectar un bean, la
instancia inyectada de ese bean necesita poder mantener su estado durante la interacción
del usuario con la aplicación. El contenedor determina durante cuánto tiempo la instancia
debe mantener su estado según el alcance del bean. Los alcances pueden definirse como
duraderos, donde el estado de un bean se conserva en diferentes usuarios y solicitudes. De
manera alternativa, los beans pueden destruirse y recrearse con cada solicitud entrante.
Hay varios alcances disponibles en la especificación de CDI. Para definir el alcance de un
bean, utilice una anotación de nivel de clase como se muestra en el siguiente ejemplo:
@ApplicationScoped
public class HelloCounterBean {
En la siguiente lista se resume cada alcance disponible:
Alcance de solicitud
• Usa la anotación @RequestScoped.
• El contenedor mantiene cada instancia de un bean con alcance de solicitud solo para una
única solicitud HTTP.
• Una vez que se completa la solicitud, se destruye la instancia del bean.
Alcance de sesión
• Usa la anotación @SessionScoped.
• El contenedor mantiene cada instancia de un bean con alcance de sesión para cada sesión
del usuario. Una sesión abarca varias solicitudes pero, por lo general, vence después
de una cantidad configurable de inactividad o después de que se borra la caché del
explorador.
• Una vez que vence la sesión, se destruye la instancia del bean.
300
JB183-EAP7.0-es-2-20180124
Asignación de nombres para beans
Alcance de conversación
• Usa la anotación @ConversationScoped.
• El contenedor maneja al alcance de conversación de manera similar al alcance de sesión
en cuanto a que mantiene el estado asociado con un usuario del sistema y abarca varias
solicitudes en el servidor.
• Sin embargo, a diferencia del alcance de sesión, el alcance de conversación mantiene el
estado asociado con una pestaña particular del explorador web en una aplicación web
(por lo general, los exploradores comparten cookies entre las pestañas, como la cookie de
sesión; este no es el caso del alcance de sesión).
• La aplicación debe delimitar manualmente el inicio y final de una conversación. Las
conversaciones abarcan todas las solicitudes en una única pestaña del explorador hasta
que la aplicación finaliza explícitamente la aplicación o hasta que se agota el tiempo de
espera.
• Un usuario puede tener varias conversaciones simultáneas en varias pestañas.
Alcance de aplicación
• Usa la anotación @ApplicationScoped.
• El contenedor mantiene cada instancia de un bean con alcance de aplicación durante toda
la implementación de la aplicación.
• Solo cuando se apaga el contenedor, o se vuelve a implementar la aplicación, se destruye la
instancia del bean.
Singleton
• Usa la anotación @Singleton.
• El contenedor mantiene solo una única instancia de los beans singleton. Cualquier bean
que inyecta un singleton obtiene la misma instancia debido a que solo hay una.
Asignación de nombres para beans
Otra característica útil de la especificación de CDI es la capacidad de asignarles nombres
simples a los beans. Estos nombres brindan un mecanismo para hacer referencia a los beans
mediante el lenguaje de expresión (Expression Language, EL) encontrado en librerías frontend, como páginas de Caras de servidor Java (JSF). Esta funcionalidad les proporciona a los
desarrolladores una manera sencilla de inyectar beans directamente a la capa de IU de una
aplicación.
Para asignar un nombre a un bean administrado por CDI, utilice la anotación @Named. De
manera opcional, especifique el nombre para el bean de manera explícita, o la propiedad
del nombre utiliza el nombre de la clase con una primera letra en minúsculas de manera
predeterminada.
Por ejemplo, la siguiente clase es un bean con alcance de sesión con la anotación @Named:
@SessionScoped
@Named
public class Hello implements Serializable{
private static final long serialVersionUID = 1L;
JB183-EAP7.0-es-2-20180124
301
Capítulo 7. Implementación de Contextos e Inyección de dependencia (CDI)
private String name
Para hacer referencia a los atributos del bean Hello con JSF, utilice la sintaxis
#{beanName.attributeName}. Esto hace referencia directa a la variable de miembro y JSF
puede convocar automáticamente los métodos getter y setter relacionados para mantener
el valor en el bean actualizado automáticamente con el valor del campo de entrada del
formulario.
El siguiente es un fragmento de una página de JSF que utiliza EL con el bean nombrado
hello, que hace referencia directa a la variable name y la asigna a un campo de entrada en
un formulario de JSF:
<h:inputText value="#{hello.name}" id="name" required="true" requiredMessage="Name is
required"/>
Referencias
Para obtener más información, consulte el capítulo de CDI de la Guía de desarrollo
para Red Hat JBoss EAP:
https://access.redhat.com/documentation/en/red-hat-jboss-enterpriseapplication-platform/7.0/
302
JB183-EAP7.0-es-2-20180124
Ejercicio guiado: Aplicación de alcances
Ejercicio guiado: Aplicación de alcances
En este ejercicio, aplicará diferentes alcances en los EJB y probará los efectos en una
aplicación.
Resultado
Deberá poder implementar diferentes alcances en clases de EJB mediante anotaciones.
Antes de comenzar
Abra una ventana de terminal en la máquina virtual workstation y ejecute el siguiente
comando para descargar los archivos necesarios para este ejercicio:
[student@workstation ~]$ lab apply-scopes setup
Pasos
1. Abra JBDS e importe el proyecto de Maven.
1.1. Haga doble clic en el icono de JBoss Developer Studio en el escritorio de la estación
de trabajo para abrir JBDS. Seleccione el espacio de trabajo /home/student/
JB183/workspace y haga clic en OK (Aceptar).
1.2. En el menú de JBDS, haga clic en File (Archivo) > Import (Importar)para abrir el
asistente para Import (Importar).
1.3. En la página Select (Seleccionar), haga clic en Maven > Existing Maven Projects
(Proyectos de Maven existentes) y, luego, haga clic en Next (Siguiente).
1.4. En la página Maven projects (Proyectos de Maven), haga clic en Browse (Explorar)
para abrir la ventana Select root folder (Seleccionar carpeta raíz). Diríjase al
directorio /home/student/JB183/labs/. Seleccione la carpeta apply-scopes y
haga clic en OK (Aceptar).
1.5. En la página Maven projects (Proyectos de Maven), haga clic en Finish (Finalizar).
1.6. Observe la barra de estado de JBDS para monitorear el progreso de la operación
de importación. Es probable que la descarga de todas las dependencias requeridas
demore unos minutos.
2.
Revise la página de JSF que utiliza la aplicación.
Abra la página de JSF index.xhtml; para ello, expanda el ítem apply-scopes de la
pestaña Project Explorer (Explorador de proyectos) en el panel izquierdo de JBDS y,
luego, haga clic en apply-scopes > src > main > webapp y expándalo. Haga doble clic
en el archivo index.xhtml y, luego, haga clic en Source (Fuente) en la parte inferior del
editor.
<h:form id="form">
<p class="input">
<h:outputLabel value="Enter your name:" for="name" />
<h:inputText value="#{hello.name}">
requiredMessage="Name is required"/>
</p>
JB183-EAP7.0-es-2-20180124
id="name" required="true"
303
Capítulo 7. Implementación de Contextos e Inyección de dependencia (CDI)
<br class="clear"/>
<br class="clear"/>
<p class="input">
<h:commandButton action="#{hello.sayHello()}" value="Submit" styleClass="btn" /
>
</p>
<br class="clear"/>
<br class="clear"/>
<h:messages styleClass="messages"/>
<p>Greeted <h:outputText value="#{counter.currentCount}"/>
server restart. </p>
</h:form>
person(s) since last
Un EJB nombrado hello se utiliza para brindar el nombre de la variable, así como el
método sayHello().
Un EJB nombrado counter se utiliza para proporcionar la variable currentCount.
3.
Actualice la clase de EJB PersonService para que no tenga un estado.
3.1. Abra la clase PersonService; para ello, expanda el ítem apply-scopes en
la pestaña Project Explorer (Explorador de proyectos) en el panel izquierdo
de JBDS y, luego, haga clic en apply-scopes > Java Resources > src/main/
java > com.redhat.training.ejb y expándalo. Haga doble clic en el archivo
PersonService.java.
La clase de EJB se mantiene sin estado. No tiene variables de miembro que necesiten
especificarse para cada instancia de la clase. El contenedor de aplicaciones puede
usar indistintamente cada instancia.
3.2. Actualice la clase de EJB para incluir la anotación @Stateless para indicarle al
contenedor que el bean no tiene un estado y que se puede administrar como tal.
//TODO mark as a stateless EJB
@Stateless
//TODO assign the name "personService" to this EJB
public class PersonService {
3.3. Actualice la clase de EJB para incluir la anotación @Named para indicarle al
contenedor que el bean debe estar disponible con el nombre personService.
//TODO mark as a stateless EJB
@Stateless
//TODO assign the name "personService" to this EJB
@Named("personService")
public class PersonService {
3.4. Guarde los cambios en el archivo con Ctrl+S.
4.
Actualice HelloCounterBean para que esté en el alcance de la aplicación y utilice el
nombre counter.
4.1. Abra la clase HelloCounterBean; para ello, expanda el ítem apply-scopes en
la pestaña Project Explorer (Explorador de proyectos) en el panel izquierdo
de JBDS y, luego, haga clic en apply-scopes > Java Resources > src/main/
304
JB183-EAP7.0-es-2-20180124
java > com.redhat.training.ejb y expándalo. Haga doble clic en el archivo
HelloCounterBean.java.
La clase de EJB mantiene un estado. Se debe mantener la variable count. En este
caso, configúrela para que use el alcance de la aplicación para que el estado de este
EJB se mantenga durante la vida útil de la aplicación.
Además, debido a que el conteo debe ser un valor único para la aplicación, solo se
requiere una instancia de HelloCounterBean.
4.2. Actualice la clase de EJB para que utilice la anotación @ApplicationScoped para
indicarle al contenedor que el estado del bean debe mantenerse activo durante la
vida útil de la aplicación.
//TODO make application scoped
@ApplicationScoped
//TODO make a singleton
//TODO assign the name "counter" to this EJB
public class HelloCounterBean {
4.3. Actualice la clase de EJB para que utilice la anotación @Singleton para indicarle al
contenedor que solo una única instancia del bean debe mantenerse activa durante
la vida útil de la aplicación.
//TODO make application scoped
@ApplicationScoped
//TODO make a singleton
@Singleton
//TODO assign the name "counter" to this EJB
public class HelloCounterBean {
4.4. Además, recuerde que la página de JSF hace referencia a este bean con el nombre
counter.
Actualice la clase de EJB para incluir la anotación @Named para indicarle al
contenedor que el bean debe estar disponible con el nombre counter.
//TODO make application scoped
@ApplicationScoped
//TODO make a singleton
@Singleton
//TODO assign the name "counter" to this EJB
@Named("counter")
public class HelloCounterBean {
4.5. Guarde los cambios en el archivo con Ctrl+S.
5.
Actualice el bean de respaldo Hello para que esté en el alcance de la sesión y use el
nombre hello.
5.1. Abra la clase Hello; para ello, expanda el ítem apply-scopes en la pestaña Project
Explorer (Explorador de proyectos) en el panel izquierdo de JBDS y, luego, haga
JB183-EAP7.0-es-2-20180124
305
Capítulo 7. Implementación de Contextos e Inyección de dependencia (CDI)
clic en apply-scopes > Java Resources > src/main/java > com.redhat.training.ui y
expándalo. Haga doble clic en el archivo Hello.java.
Esta clase también mantiene el estado. Se debe mantener la variable name. En este
caso, configúrela para que use el alcance de la sesión para que el estado de esta
clase se mantenga durante la vida útil de la sesión de cada usuario en la aplicación.
5.2. Actualice la clase para que utilice la anotación @SessionScoped para indicarle al
contenedor que el estado del bean debe mantenerse activo durante la vida útil de la
sesión de cada usuario.
//TODO set the scope
@SessionScoped
//TODO assign the name "hello" to this EJB
public class Hello implements Serializable{
5.3. Además, recuerde que la página de JSF hace referencia a este bean con el nombre
hello.
Actualice la clase para incluir la anotación @Named para indicarle al contenedor que
el bean debe estar disponible con el nombre hello.
//TODO set the scope
@SessionScoped
//TODO assign the name "hello" to this EJB
@Named("hello")
public class Hello implements Serializable{
5.4. Guarde los cambios en el archivo con Ctrl+S.
6.
Seleccione la pestaña Servers (Servidores) en el panel inferior de JBDS para iniciar EAP.
Haga clic con el botón derecho en el servidor Red Hat JBoss EAP 7.0 [Stopped] y haga
clic en el botón verde para iniciar el servidor. Observe la Console (Consola) hasta que el
servidor se inicie y muestre el siguiente mensaje:
INFO [org.jboss.as] (Controller Boot Thread) WFLYSRV0025: JBoss EAP 7.0.0.GA
(WildFly Core 2.1.2.Final-redhat-1) started
7.
Implemente la aplicación en JBoss EAP con Maven; para ello, ejecute los siguientes
comandos:
[student@workstation ~]$ cd /home/student/JB183/labs/apply-scopes
[student@workstation apply-scopes]$ mvn wildfly:deploy
Confirme la implementación correcta en el registro del servidor que aparece en la
pestaña Console (Consola) en JBDS. Cuando se implementa la aplicación, aparece lo
siguiente en el registro:
INFO [org.jboss.as.server] (management-handler-thread - 9) WFLYSRV0010: Deployed
"apply-scopes.war" (runtime-name : "apply-scopes.war")
306
JB183-EAP7.0-es-2-20180124
8.
Pruebe los alcances de la aplicación en un explorador.
8.1. Abra http://localhost:8080/apply-scopes en un explorador en la máquina
virtual workstation.
8.2. Ingrese Shadowman en el cuadro de texto denominado Enter your name: (Ingrese su
nombre:) y haga clic en Submit (Enviar).
La página se actualiza con el mensaje Hello SHADOWMAN!. Time on the server is: Nov 02
2017 03:55:03 PM
El mensaje del contador se actualiza a Greeted 1 person(s) since the last server restart
(Se saludó a 1 persona desde el último reinicio del servidor).
8.3. Diríjase a http://localhost:8080/apply-scopes nuevamente en el explorador
en la máquina workstation.
Observe que el nombre Shadowman aún aparece en el campo de texto. Este campo
se mantiene en las actualizaciones de la página. Esto se debe a que el bean que
respalda el campo del formulario está en el alcance de la sesión. Además, el
contador aún muestra 1 persona, debido a que el bean del contador está en el
alcance de la aplicación.
9.
Borre la caché y actualice la página.
9.1. En Firefox, diríjase a about:preferences en la URL para acceder a la página de
preferencias de Firefox.
9.2. Haga clic en Privacy (Privacidad) y, luego, en clear your recent history (borrar su
historial reciente). Haga clic en Clear Now (Borrar ahora) para que el explorador cree
una nueva sesión.
9.3. Actualice la página con Ctrl+R.
Observe que el nombre Shadowman no aparece en el campo de texto. Este campo
se mantiene en las actualizaciones de la página, pero no en una nueva sesión. Sin
embargo, el contador aún muestra 1 persona, debido a que el bean del contador
está en el alcance de la aplicación.
10. Actualice la clase de EJB Hello para que esté al alcance de la solicitud.
10.1.Abra la clase Hello; para ello, expanda el ítem apply-scopes en la pestaña Project
Explorer (Explorador de proyectos) en el panel izquierdo de JBDS y, luego, haga
clic en apply-scopes > Java Resources > src/main/java > com.redhat.training.ui y
expándalo. Haga doble clic en el archivo Hello.java.
10.2.Actualice la clase para que utilice la anotación @RequestScoped para indicarle al
contenedor que el estado del bean debe mantenerse activo durante la vida útil de la
solicitud de cada usuario.
//TODO set the scope
@RequestScoped
//TODO assign the name "hello" to this EJB
@Named("hello")
public class Hello implements Serializable{
JB183-EAP7.0-es-2-20180124
307
Capítulo 7. Implementación de Contextos e Inyección de dependencia (CDI)
10.3.Guarde los cambios en el archivo con Ctrl+S.
11. Vuelva a implementar la aplicación en JBoss EAP con Maven; para ello, ejecute los
siguientes comandos:
[student@workstation apply-scopes]$ mvn wildfly:deploy
Confirme la implementación correcta en el registro del servidor que aparece en la
pestaña Console (Consola) en JBDS. Cuando se implementa la aplicación, aparece lo
siguiente en el registro:
INFO [org.jboss.as.server] (management-handler-thread - 9) WFLYSRV0010: Deployed
"apply-scopes.war" (runtime-name : "apply-scopes.war")
12. Pruebe los alcances de la aplicación en un explorador.
12.1.Abra http://localhost:8080/apply-scopes en un explorador en la máquina
virtual workstation.
12.2.Ingrese Shadowman en el cuadro de texto denominado Enter your name: (Ingrese su
nombre:) y haga clic en Submit (Enviar).
La página se actualiza con el mensaje Hello SHADOWMAN!. Time on the server is: Nov 02
2017 03:55:03 PM
El mensaje del contador se actualiza a Greeted 1 person(s) since the last server restart
(Se saludó a 1 persona desde el último reinicio del servidor).
12.3.Diríjase a http://localhost:8080/apply-scopes nuevamente en un explorador
en la máquina virtual workstation.
Observe que el nombre Shadowman fue borrado del campo de texto. Este campo ya
no se mantiene en las actualizaciones de la página. Esto se debe a que el bean que
respalda el campo del formulario ahora está en el alcance de la solicitud. Además,
el contador aún muestra 1 persona, debido a que el bean del contador está en el
alcance de la aplicación.
13. Anule la implementación de la aplicación y detenga JBoss EAP.
13.1.Ejecute el siguiente comando para anular la implementación de la aplicación:
[student@workstation apply-scopes]$ mvn wildfly:undeploy
13.2.Para cerrar el proyecto, haga clic con el botón derecho en el proyecto apply-scopes
en Project Explorer (Explorador de proyectos) y seleccione Close Project (Cerrar
proyecto).
13.3.Haga clic con el botón derecho en el servidor Red Hat JBoss EAP 7.0 en la pestaña
Servers (Servidores) de JBDS y haga clic en Stop (Detener).
308
JB183-EAP7.0-es-2-20180124
Esto concluye el ejercicio guiado.
JB183-EAP7.0-es-2-20180124
309
Capítulo 7. Implementación de Contextos e Inyección de dependencia (CDI)
Trabajo de laboratorio: Implementación de
Contextos e Inyección de dependencia
En este trabajo de laboratorio, utilizará conceptos de CDI para completar la aplicación To Do
List.
Resultado
Deberá poder inyectar recursos y definir contextos para los beans.
Antes de comenzar
Abra una ventana de terminal en la máquina virtual workstation y ejecute el siguiente
comando para descargar los archivos necesarios para este trabajo de laboratorio.
[student@workstation ~]$ lab cdi-lab setup
1.
Abra JBDS e importe el proyecto cdi-lab ubicado en el directorio /home/student/
JB183/labs/cdi-lab.
2.
En la clase Resources.java, defina el contexto de persistencia para que
EntityManager sea la persistencia predeterminada y para que este contexto se
pueda inyectar en otras clases. Actualice el método produceLog() para que el Logger
configurable se pueda inyectar.
3.
Actualice ItemRepository y UserRepository para que no tengan estado y para que
usen el administrador de entidades con el contexto de persistencia predeterminado.
4.
Actualice las clases ItemService.java y UserService.java para que no tengan
estado y para inyectar Logger y EntityManager.
5.
Actualice ItemResourceRESTService para que tenga alcance de solicitud e inyecte
UserRepository, ItemRepository, ItemService, UserService y Logger.
6.
Inicie JBoss EAP desde dentro de JBDS.
7.
Compile e implemente la aplicación en JBoss EAP con Maven.
8.
Pruebe la aplicación en un explorador.
8.1. Abra Firefox en la máquina virtual workstation y diríjase a http://
localhost:8080/cdi-lab para acceder a la aplicación.
8.2. Agregue al menos dos nuevos ítems pendientes mediante la interfaz de la aplicación
To Do List y verifique que la aplicación funcione de la manera prevista.
9.
Abra una nueva ventana de terminal y ejecute el siguiente comando para calificar el
trabajo de laboratorio:
[student@workstation ~]$ lab cdi-lab grade
El script de calificación debe indicar SUCCESS (CORRECTO). Si se produce una falla, revise
los errores y corríjalos hasta que vea un mensaje que indique SUCCESS (CORRECTO).
310
JB183-EAP7.0-es-2-20180124
10. Realice la limpieza.
10.1.Anule la implementación de la aplicación en JBoss EAP con Maven con los siguientes
comandos:
[student@workstation ~]$ cd /home/student/JB183/labs/cdi-lab
[student@workstation cdi-lab]$ mvn wildfly:undeploy
10.2.Haga clic con el botón derecho en el proyecto cdi-lab en Project Explorer
(Explorador de proyectos) y seleccione Close Project (Cerrar proyecto).
10.3.Haga clic con el botón derecho en el servidor Red Hat JBoss EAP 7.0 en la pestaña
Servers (Servidores) de JBDS y haga clic en Stop (Detener).
Esto concluye el trabajo de laboratorio.
JB183-EAP7.0-es-2-20180124
311
Capítulo 7. Implementación de Contextos e Inyección de dependencia (CDI)
Solución
En este trabajo de laboratorio, utilizará conceptos de CDI para completar la aplicación To Do
List.
Resultado
Deberá poder inyectar recursos y definir contextos para los beans.
Antes de comenzar
Abra una ventana de terminal en la máquina virtual workstation y ejecute el siguiente
comando para descargar los archivos necesarios para este trabajo de laboratorio.
[student@workstation ~]$ lab cdi-lab setup
1.
Abra JBDS e importe el proyecto cdi-lab ubicado en el directorio /home/student/
JB183/labs/cdi-lab.
1.1. Para abrir JBDS, haga doble clic en el icono de JBoss Developer Studio en el escritorio
de la estación de trabajo. Deje el espacio de trabajo predeterminado (/home/
student/JB183/workspace) y haga clic en OK (Aceptar).
1.2. En el menú de JBDS, haga clic en File (Archivo) > Import (Importar)para abrir el
asistente para Import (Importar).
1.3. En la página Select (Seleccionar), haga clic en Maven > Existing Maven Projects
(Proyectos de Maven existentes) y, luego, haga clic en Next (Siguiente).
1.4. En la página Maven projects (Proyectos de Maven), haga clic en Browse (Explorar)
para abrir la ventana Select root folder (Seleccionar carpeta raíz). Diríjase al
directorio /home/student/JB183/labs/. Seleccione la carpeta cdi-lab y haga clic
en OK (Aceptar).
1.5. En la página Maven projects (Proyectos de Maven), haga clic en Finish (Finalizar).
1.6. Observe la barra de estado de JBDS para monitorear el progreso de la operación
de importación. Es probable que la descarga de todas las dependencias requeridas
demore unos minutos.
2.
En la clase Resources.java, defina el contexto de persistencia para que
EntityManager sea la persistencia predeterminada y para que este contexto se
pueda inyectar en otras clases. Actualice el método produceLog() para que el Logger
configurable se pueda inyectar.
2.1. Abra la clase Resources; para ello, expanda el ítem cdi-lab en la pestaña Project
Explorer (Explorador de proyectos) en el panel izquierdo de JBDS y, luego, haga
clic en cdi-lab > Java Resources > src/main/java > com.redhat.training.todo.util y
expándalo. Haga doble clic en el archivo Resources.java.
2.2. Agregue la anotación @PersistenceContext a EntityManager y la importación
de javax.persistence.PersistenceContext correspondiente a la clase
Resources:
import javax.persistence.PersistenceContext;
312
JB183-EAP7.0-es-2-20180124
Solución
@PersistenceContext
private EntityManager em;
2.3. Agregue la anotación @Produces a EntityManager y la importación de
javax.enterprise.inject.Produces correspondiente a la clase Resources:
import javax.enterprise.inject.Produces;
@PersistenceContext
@Produces
private EntityManager em;
2.4. Agregue la anotación @Produces al método produceLog():
@Produces
public Logger produceLog(InjectionPoint injectionPoint) {
...
2.5. Guarde los cambios en el archivo con Ctrl+S.
3.
Actualice ItemRepository y UserRepository para que no tengan estado y para que
usen el administrador de entidades con el contexto de persistencia predeterminado.
3.1. Abra la clase ItemRepository; para ello, expanda el ítem cdi-lab en la
pestaña Project Explorer (Explorador de proyectos) en el panel izquierdo
de JBDS y, luego, haga clic en cdi-lab > Java Resources > src/main/java >
com.redhat.training.todo.data y expándalo. Haga doble clic en el archivo
ItemRepository.java.
3.2. Agregue la anotación @Stateless para que la clase ItemRepository no tenga
estado y agregue la importación de javax.ejb.Stateless correspondiente:
import javax.ejb.Stateless;
@Stateless
public class ItemRepository {
3.3. Agregue una anotación @Inject a la instancia de EntityManager y agregue la
importación de javax.inject.Inject correspondiente:
import javax.inject.Inject;
@Inject
private EntityManager em;
3.4. Guarde los cambios en el archivo con Ctrl+S.
JB183-EAP7.0-es-2-20180124
313
Capítulo 7. Implementación de Contextos e Inyección de dependencia (CDI)
3.5. Abra la clase UserRepository en el paquete com.redhat.training.todo.data; para
ello haga doble clic en el archivo UserRepository.java.
3.6. Agregue la anotación @Stateless para que la clase UserRepository no tenga
estado y agregue la importación de javax.ejb.Stateless correspondiente:
import javax.ejb.Stateless;
@Stateless
public class UserRepository {
3.7. Agregue una anotación @Inject a la instancia de EntityManager y agregue la
importación de javax.inject.Inject correspondiente:
import javax.inject.Inject;
@Inject
private EntityManager em;
3.8. Guarde los cambios en el archivo con Ctrl+S.
4.
Actualice las clases ItemService.java y UserService.java para que no tengan
estado y para inyectar Logger y EntityManager.
4.1. Abra la clase ItemService; para ello, expanda el ítem cdi-lab en la pestaña Project
Explorer (Explorador de proyectos) en el panel izquierdo de JBDS y, luego, haga clic
en cdi-lab > Java Resources > src/main/java > com.redhat.training.todo.service y
expándalo. Haga doble clic en el archivo ItemService.java.
4.2. Agregue la anotación @Stateless para que la clase ItemService no tenga estado y
agregue la importación de javax.ejb.Stateless correspondiente:
import javax.ejb.Stateless;
@Stateless
public class ItemService {
4.3. Agregue una anotación @Inject a la instancia EntityManager:
@Inject
private EntityManager em;
4.4. Agregue una anotación @Inject a la instancia Logger:
@Inject
private Logger log;
4.5. Guarde los cambios en el archivo con Ctrl+S.
314
JB183-EAP7.0-es-2-20180124
Solución
4.6. Abra la clase UserService en cdi-lab > Java Resources > src/main/java >
com.redhat.training.todo.service; para ello, haga doble clic en el archivo
UserService.java.
4.7. Agregue la anotación @Stateless para que la clase UserService no tenga estado y
agregue la importación de javax.ejb.Stateless correspondiente:
import javax.ejb.Stateless;
@Stateless
public class UserService {
4.8. Agregue una anotación @Inject a la instancia EntityManager:
@Inject
private EntityManager em;
4.9. Agregue una anotación @Inject a la instancia Logger:
@Inject
private Logger log;
4.10.Guarde los cambios en el archivo con Ctrl+S.
5.
Actualice ItemResourceRESTService para que tenga alcance de solicitud e inyecte
UserRepository, ItemRepository, ItemService, UserService y Logger.
5.1. Abra la clase ItemResourceRESTService; para ello, expanda el ítem cdilab en la pestaña Project Explorer (Explorador de proyectos) en el panel
izquierdo de JBDS y, luego, haga clic en cdi-lab > Java Resources > src/main/
java > com.redhat.training.todo.rest y expándalo. Haga doble clic en el archivo
ItemResourceRESTService.java.
5.2. Agregue la anotación @RequestScoped para que la clase
ItemResourceRESTService no tenga estado y agregue la importación de
javax.enterprise.context.RequestScoped correspondiente:
import javax.enterprise.context.RequestScoped;
@RequestScoped
public class ItemResourceRESTService {
5.3. Agregue una anotación @Inject a Logger y agregue la importación de
javax.inject.Inject correspondiente:
import javax.inject.Inject;
@Inject
JB183-EAP7.0-es-2-20180124
315
Capítulo 7. Implementación de Contextos e Inyección de dependencia (CDI)
private Logger log;
5.4. Agregue una anotación @Inject a la instancia ItemRepository:
@Inject
private ItemRepository repository;
5.5. Agregue una anotación @Inject a la instancia UserRepository:
@Inject
private UserRepository userRepo;
5.6. Agregue una anotación @Inject a la instancia ItemService:
@Inject
private ItemService itemService;
5.7. Agregue una anotación @Inject a la instancia UserService:
@Inject
private UserService userService;
5.8. Guarde los cambios en el archivo con Ctrl+S.
6.
Inicie JBoss EAP desde dentro de JBDS.
Seleccione la pestaña Servers (Servidores) en JBDS. Haga clic con el botón derecho en la
entrada del servidor Red Hat JBoss EAP 7.0 [Stopped] y haga clic en la opción verde Start
(Iniciar) para iniciar el servidor. Observe la pestaña Console (Consola) de JBDS hasta que
el servidor se inicie y vea el siguiente mensaje:
INFO [org.jboss.as] (Controller Boot Thread) WFLYSRV0025: JBoss EAP 7.0.2.GA
(WildFly Core 2.1.8.Final-redhat-1) started
7.
Compile e implemente la aplicación en JBoss EAP con Maven.
Abra un nuevo terminal y cambie el directorio por la carpeta /home/student/JB183/
labs/cdi-lab.
[student@workstation ~]$ cd /home/student/JB183/labs/cdi-lab
Compile e implemente el EJB en JBoss EAP al ejecutar el siguiente comando:
[student@workstation cdi-lab]$ mvn clean wildfly:deploy
8.
Pruebe la aplicación en un explorador.
8.1. Abra Firefox en la máquina virtual workstation y diríjase a http://
localhost:8080/cdi-lab para acceder a la aplicación.
316
JB183-EAP7.0-es-2-20180124
Solución
8.2. Agregue al menos dos nuevos ítems pendientes mediante la interfaz de la aplicación
To Do List y verifique que la aplicación funcione de la manera prevista.
9.
Abra una nueva ventana de terminal y ejecute el siguiente comando para calificar el
trabajo de laboratorio:
[student@workstation ~]$ lab cdi-lab grade
El script de calificación debe indicar SUCCESS (CORRECTO). Si se produce una falla, revise
los errores y corríjalos hasta que vea un mensaje que indique SUCCESS (CORRECTO).
10. Realice la limpieza.
10.1.Anule la implementación de la aplicación en JBoss EAP con Maven con los siguientes
comandos:
[student@workstation ~]$ cd /home/student/JB183/labs/cdi-lab
[student@workstation cdi-lab]$ mvn wildfly:undeploy
10.2.Haga clic con el botón derecho en el proyecto cdi-lab en Project Explorer
(Explorador de proyectos) y seleccione Close Project (Cerrar proyecto).
10.3.Haga clic con el botón derecho en el servidor Red Hat JBoss EAP 7.0 en la pestaña
Servers (Servidores) de JBDS y haga clic en Stop (Detener).
Esto concluye el trabajo de laboratorio.
JB183-EAP7.0-es-2-20180124
317
Capítulo 7. Implementación de Contextos e Inyección de dependencia (CDI)
Resumen
En este capítulo, aprendió lo siguiente:
• Uno de los principales beneficios de la inyección de dependencias (Dependency Injection,
DI) es el bajo acoplamiento de los componentes de la aplicación. Los componentes de
cliente y servidor se acoplan levemente porque se pueden insertar varias versiones
diferentes del servidor en el cliente.
• CDI puede inyectar clases de Java regulares, mientras que la inyección de recursos no
puede inyectar clases regulares y, en cambio, hace referencia al recurso por nombres de
JNDI.
• Para habilitar CDI en las aplicaciones web, coloque el archivo marcador beans.xml en el
directorio WEB-INF. Para los archivos JAR, incluso los que contienen EJB, coloque el archivo
beans.xml en el directorio META-INF.
• Si no se especifica un calificador para un bean, el calificador utiliza @Default como valor
predeterminado y el bean se convierte en la implementación predeterminada para el tipo o
los tipos de bean.
• La ventaja de los productores es que la anotación permite que se puedan inyectar objetos
que no sean beans.
• Los siguientes alcances están disponibles en CDI:
◦ @RequestScoped
◦ @SessionScoped
◦ @ConversationScoped
◦ @ApplicationScoped
◦ @Singleton
• La anotación @Named brinda un mecanismo para hacer referencia a los EJB mediante el
lenguaje de expresión (Expression Language, EL) encontrado en librerías front-end, como
páginas de Caras de servidor Java (JSF).
318
JB183-EAP7.0-es-2-20180124
TRAINING
CAPÍTULO 8
CREACIÓN DE APLICACIONES DE
MENSA JERÍA CON JMS
Descripción general
Meta
Crear clientes de mensajería que envían y reciben
mensajes mediante la API de JMS.
Objetivos
• Describir la API de JMS y nombrar a los objetos que se
utilizan para enviar y recibir mensajes.
• Describir los componentes que constituyen la API de
JMS.
• Crear un cliente JMS que produce y consume mensajes
mediante la API de JMS.
• Crear, comprimir e implementar un bean controlado
por mensaje.
Secciones
• Descripción de conceptos de mensajería (y cuestionario)
• Descripción de la arquitectura de JMS (y cuestionario)
• Creación de un cliente JMS (y ejercicio guiado)
• Creación de beans controlados por mensajes (y ejercicio
guiado)
Trabajo de laboratorio
JB183-EAP7.0-es-2-20180124
Creación de aplicaciones de mensajería con JMS
319
Capítulo 8. Creación de aplicaciones de mensajería con JMS
Descripción de conceptos de mensajería
Objetivos
Tras finalizar esta sección, los estudiantes deberán ser capaces de realizar lo siguiente:
• Describir la API JMS
• Identificar los objetos usados para enviar y recibir mensajes
Comprensión de conceptos de mensajería asíncrona
La tecnología de mensajería o message-oriented-middleware (MOM) (middleware orientado
a mensajes) es una herramienta fundamental utilizada por muchos desarrolladores en
empresas alrededor del mundo. Al activar el procesamiento asíncrono entre sistemas
acoplados de forma flexible, la mensajería permite que los desarrolladores compilen
aplicaciones que sean más eficientes, más fácilmente escaladas y más confiables.
Las soluciones de mensajería usan un concepto denominado colas o destinos para facilitar
la transferencia de datos de una aplicación a otra. Los desarrolladores usan colas como
un intermedio entre aplicaciones. Las colas almacenan datos de un mensaje a medida
que se transfiere de un remitente a un receptor. Mediante una cola, la aplicación que envía
(remitente) no espera por una respuesta de la aplicación que recibe (receptor). En cambio,
al usar la mensajería, el remitente envía un mensaje en una cola y el receptor recupera el
mensaje de la cola y, luego, procesa el mensaje.
Un ejemplo práctico de comunicación asíncrona entre aplicaciones es una aplicación web
de front-end para un negocio de comercio electrónico que envía datos de pedidos a un
sistema de procesamiento de pedidos de back-end. Cuando un cliente realiza un pedido,
la aplicación web no necesita el sistema de procesamiento para completar el pedido. En
cambio, la aplicación solo necesita enviar un mensaje con la información del pedido a la
cola de pedidos, y el sistema de back-end puede procesarla posteriormente. A continuación,
la aplicación web envía al cliente a una pantalla de confirmación de pedidos, mientras la
aplicación de back-end procesa el pedido.
Otro beneficio significativo de usar la mensajería para facilitar la integración de aplicaciones
es que la mensajería permite un acoplamiento flexible. Se dice que las aplicaciones que
usan la mensajería tienen un acoplamiento flexible porque el único requisito para que se
comuniquen es una conexión al destino y un formato de mensaje convenido. En un sistema
de mensajería, los componentes de las aplicaciones se pueden escribir en muchos idiomas
diferentes, siempre y cuando los componentes adhieran al mismo formato de mensaje.
La flexibilidad que proporciona esta separación clara de aplicaciones también favorece la
compatibilidad de la escalabilidad. Por ejemplo, en el ejemplo anterior de aplicación de
comercio electrónico, cuando crece la cola de pedidos, nuevas instancias de la aplicación
de back-end de procesamiento de pedidos se pueden crear fácilmente y pueden consumir
mensajes de la cola en forma simultánea, lo que permite un procesamiento de datos mucho
más rápido.
Uso de una cola para mensajería punto a punto
Point-to-point messaging (mensajería punto a punto) se produce cuando dos aplicaciones
están conectadas mediante una cola, y cada mensaje que envía un productor es recibido por
un solo consumidor. Si bien pueden haber varios productores o consumidores conectados a
320
JB183-EAP7.0-es-2-20180124
Uso de un tema para la mensajería de publicación/suscripción
una cola en particular, un único par de productores y consumidores procesa cada mensaje.
Los desarrolladores de aplicaciones a menudo usan el modelo de mensajería punto a punto
por la simplicidad y escalabilidad que proporciona.
Figura 8.1: Una cola en uso por aplicaciones del productor y el consumidor
Los consumidores de una cola usan un modelo basado en demanda para recuperar nuevos
mensajes. Esto implica que cualquier cliente receptor generalmente necesita sondear la
cola para nuevos mensajes. Asimismo, en el modelo punto a punto, un consumidor de cola
generalmente debe reconocer el procesamiento correcto del mensaje o se devuelve a la cola
para volver a intentarlo. Parte de este comportamiento de reintentos depende del código de
aplicaciones y la tecnología de mensajería.
Uso de un tema para la mensajería de publicación/
suscripción
Publish-subscribe messaging se produce cuando varias aplicaciones se suscriben a un tema
en particular, y todo mensaje enviado a ese tema se duplica y se copia a cada uno de los
suscriptores. Los temas son muy similares a las colas, siendo el número de consumidores la
única diferencia.
Los desarrolladores se refieren a los consumidores de un tema como suscriptores. Cuando
una nueva aplicación se suscribe a un tema, pasa a recibir todo nuevo mensaje que se envía a
ese tema, pero no recibe ningún mensaje anterior enviado al tema antes de la suscripción.
Figura 8.2: Un tema en uso por un productor y un conjunto de aplicaciones del consumidor
A diferencia de la mensajería punto a punto, que usa un modelo basado en demanda, la
mensajería de publicación/suscripción usa un modelo basado en demanda. En un modelo
JB183-EAP7.0-es-2-20180124
321
Capítulo 8. Creación de aplicaciones de mensajería con JMS
basado en demanda, el proveedor de mensajería es responsable de enviar mensajes
entrantes a todos los suscriptores.
Cuando una aplicación crea una nueva suscripción a un tema para recibir mensajes, existen
dos tipos de suscripciones: duradera y no duradera. Durante el proceso de suscripción, la
aplicación debe especificar si su suscripción es duradera o no duradera. Al usar suscripciones
durables, si la aplicación se desconecta del tema temporalmente, todo mensaje enviado al
tema mientras la aplicación esté desconectada se guarda y se entrega la próxima vez que
el suscriptor durable se vuelve a conectar. Alternativamente, una suscripción no durable no
guarda ningún mensaje recibido mientras el suscriptor está desconectado.
Revisión de la Especificación JMS en JSR-343
La especificación Java EE define la API Java Message Service (JMS) (Servicio de mensajes de
Java) para crear un método estandarizado para que las aplicaciones de Java se conecten a,
y utilicen, soluciones de mensajería empresarial. JMS se introdujo por primera vez en 2002
como JSR-914 y la versión actual usada por Java EE 7, v2.0, se mantiene bajo JSR-343.
Los desarrolladores usan un conjunto de terminología común al desarrollar aplicaciones JMS.
Estos términos y sus definiciones se encuentran en la siguiente tabla:
Conceptos JMS
Término
Definición
Cliente JMS
Programas o aplicaciones de Java que envían o reciben mensajes.
Mensaje
Generalmente, datos en formato estandarizado, enviados entre
aplicaciones o clientes.
Proveedor JMS
El componente o la tecnología de mensajería que implementa la API
JMS. Al trabajar con JBoss EAP, un proveedor JMS se compila en el
servidor o se puede configurar un proveedor externo.
Objeto administrado
Una aplicación usa un objeto JMS previamente configurado, definido
en la configuración del contenedor. Generalmente, las fábricas de
conexiones y los destinos son objetos administrados, definidos a
nivel del servidor y puestos a disposición por el contenedor para su
uso por aplicaciones implementadas.
Fábrica de
conexiones
Un objeto Java que el cliente usa para crear nuevas conexiones al
proveedor JMS.
Destino
Un objeto Java usado por un cliente para especificar la cola o el
tema donde el cliente envía o recibe mensajes.
Referencias
Para obtener más información, consulte el capítulo JMS de la Guía de desarrollo para
Red Hat JBoss EAP 7 en
https://access.redhat.com/documentation/en/red-hat-jboss-enterpriseapplication-platform/
322
JB183-EAP7.0-es-2-20180124
Revisión de la Especificación JMS en JSR-343
Referencias
JMS 2.0 JSR
https://jcp.org/en/jsr/detail?id=343
JB183-EAP7.0-es-2-20180124
323
Capítulo 8. Creación de aplicaciones de mensajería con JMS
Cuestionario: Descripción de conceptos de
mensajería
Una los siguientes ítems con sus equivalentes en la tabla.
El componente de middleware orientado a mensajes usado por una aplicación JMS.
Objeto Java usado por un cliente JMS que especifica dónde el client intercambia mensajes.
Tipo de destino usado con la mensajería de publicación/suscripción.
Tipo de destino usado con mensajería punto a punto.
Un objeto de Java usado para proporcionar a la aplicación conexiones al proveedor JMS.
Un programa de Java que envía o recibe mensajes.
Una suscripción que guarda mensajes de temas cuando la aplicación no está conectada.
Término
Definición
Cola
Tema
Proveedor JMS
Destino
Cliente JMS
324
JB183-EAP7.0-es-2-20180124
Término
Definición
Suscripción durable
Fábrica de conexiones
JB183-EAP7.0-es-2-20180124
325
Capítulo 8. Creación de aplicaciones de mensajería con JMS
Solución
Una los siguientes ítems con sus equivalentes en la tabla.
Término
Definición
Cola
Tipo de destino usado con mensajería punto a
punto.
Tema
Tipo de destino usado con la mensajería de
publicación/suscripción.
Proveedor JMS
El componente de middleware orientado a
mensajes usado por una aplicación JMS.
Destino
Objeto Java usado por un cliente JMS que
especifica dónde el client intercambia
mensajes.
Cliente JMS
Un programa de Java que envía o recibe
mensajes.
Suscripción durable
Una suscripción que guarda mensajes de temas
cuando la aplicación no está conectada.
Fábrica de conexiones
Un objeto de Java usado para proporcionar a la
aplicación conexiones al proveedor JMS.
326
JB183-EAP7.0-es-2-20180124
Descripción de la arquitectura de JMS
Descripción de la arquitectura de JMS
Objetivo
Después de completar esta sección, los estudiantes deberán ser capaces de describir
componentes que integran la API JMS.
Uso de JMS en JBoss EAP 7 con el Agente de
mensajes Apache Artemis incorporado
Para proporcionar funcionalidad de mensajería JMS a sus usuarios, JBoss Enterprise
Application Platform 7 aprovecha un agente de mensajería Apache ActiveMQ Artemis como
proveedor JMS incorporado. Este proveedor JMS incorporado permite a los desarrolladores
compilar, implementar e integrar rápidamente aplicaciones de mensajería en JBoss EAP sin el
costo o gastos generales de un proveedor de mensajería externo.
Si bien Apache ActiveMQ Artemis se lanzó y se vende como un producto independiente de
Red Hat JBoss AMQ 7, se incluye una única instancia de agente incorporado, que se basa
en una versión similar de Artemis, con cada instancia de JBoss EAP 7. La implementación
de Artemis reemplaza al agente HornetQ usado por JBoss EAP 6, y es completamente
compatible con versiones anteriores.
JBoss EAP 7 implementa y gestiona Artemis como el subsistema messaging-activemq.
Después de instalar JBoss EAP, el único perfil que permite el subsistema de mensajería es el
archivo de configuración standalone-full.xml. Por lo tanto, es necesario usar este archivo
de configuración o habilitar en forma manual el subsistema messaging-activemq en un
archivo de configuración standalone.xml personalizado.
nota
El uso del agente de mensajería ActiveMQ Artemis incorporado requiere
prácticamente la misma configuración y administración que un agente Artemis
independiente. Si bien estas tareas no se encuentran dentro del alcance de este
curso, se puede consultar más información sobre estos temas y otros materiales
relacionados con Artemis en el curso de capacitación de Red Hat JB440: Red Hat
JBoss AMQ Administration.
Uso de objetos administrados para JMS con
JBoss EAP 7
Una práctica común al trabajar con mensajería en un servidor de aplicaciones como
JBoss EAP 7 es mantener los destinos JMS y las fábricas de conexión de manera
administrativa en la configuración del servidor, en lugar de en el propio código de
aplicaciones. La razón para ello es que la fábrica de conexiones y los objetos de destino
generalmente incluyen una configuración específica del entorno, como hosts y puertos, así
como información potencialmente confidencial, como nombres de usuario y contraseñas.
Asimismo, según el proveedor JMS que se use, los parámetros de configuración necesarios
para conectarse a ese proveedor pueden cambiar drásticamente. El uso de objetos
JB183-EAP7.0-es-2-20180124
327
Capítulo 8. Creación de aplicaciones de mensajería con JMS
administrados simplifica el código de aplicaciones al proporcionar una capa de abstracción
para extraer esta configuración específica de proveedores del desarrollador.
Los clientes JMS acceden a estos objetos administrativos mediante las interfaces API JMS
que son independientes del proveedor JMS, lo que permite que el cliente JMS se ejecute
posiblemente con muchos proveedores de JMS diferentes, con pocas o ninguna modificación
al código durante el switching.
Al usar JBoss EAP 7, los objetos administrados por JM se crean como parte del subsistema
messaging-activemq. El siguiente snippet del standalone-full.xml incluye algunos
ejemplos de objetos administrados:
<subsystem xmlns="urn:jboss:domain:messaging-activemq:1.0">
<server name="default">
...Some configuration omitted...
<jms-queue name="ExpiryQueue" entries="java:/jms/queue/ExpiryQueue"/>
<jms-queue name="DLQ" entries="java:/jms/queue/DLQ"/>
<jms-queue name="helloWorldQueue" entries="queue/helloWorldQueue java:jboss/jms/
queue/helloWorldQueue"/>
<jms-queue name="TodoListQueue" entries="queue/TodoListQueue java:jboss/jms/queue/
TodoListQueue"/>
<connection-factory name="InVmConnectionFactory" entries="java:/ConnectionFactory"
connectors="in-vm"/>
<connection-factory name="RemoteConnectionFactory" entries="java:jboss/exported/
jms/RemoteConnectionFactory" connectors="http-connector"/>
<pooled-connection-factory name="activemq-ra" transaction="xa" entries="java:/JmsXA
java:jboss/DefaultJMSConnectionFactory" connectors="in-vm"/>
</server>
</subsystem>
Esto define una cola JMS como un objeto administrado con los nombres JNDI queue/
TodoListQueue y java:jboss/jms/queue/TodoListQueue.
Esto define una fábrica de conexiones JMS como un objeto administrado con el nombre
JNDI java:/ConnectionFactory.
nota
La creación de objetos administrados para JBoss EAP 7 no está dentro del alcance
de este curso. Más información sobre este tema y otros temas relacionados con
EAP se puede consultar en el curso de capacitación de Red Hat JB248: JBoss
Administration I
Revisión de componentes de un Cliente JMS
Existe una serie de componentes de la API JMS que conforman un cliente JMS. Algunos de
estos componentes se proporcionan como objetos administrados y otros deben ser creados
mediante programación por un desarrollador utilizando la API JMS. En la siguiente lista se
resumen los componentes de alto nivel que son necesarios para compilar un cliente JMS:
Fábrica de conexiones
Una fábrica de conexiones JMS es un objeto administrado que el cliente usa para crear
una conexión al proveedor JMS. Generalmente, la fábrica de conexiones incluye los
parámetros de conexión o autenticación necesarios predefinidos por un administrador
de servidores. Cada fábrica de conexiones es una instancia de las interfaces
328
JB183-EAP7.0-es-2-20180124
Uso de objetos administrados para JMS con JBoss EAP 7
ConnectionFactory, QueueConnectionFactory o TopicConnectionFactory de
Java.
Generalmente, la fábrica de conexiones gestionada, provista por el contenedor de
aplicaciones, se inyecta en un cliente JMS usando la anotación CDI de @Resource.
@Resource(lookup = "java:jboss/ConnectionFactory")
private static ConnectionFactory connectionFactory;
También es posible realizar la búsqueda de JNDI en forma manual si no se define un
objeto administrado:
final Properties env = new Properties();
env.put(Context.INITIAL_CONTEXT_FACTORY, INITIAL_CONTEXT_FACTORY);
env.put(Context.PROVIDER_URL, System.getProperty(Context.PROVIDER_URL,
PROVIDER_URL));
env.put(Context.SECURITY_PRINCIPAL, userName);
env.put(Context.SECURITY_CREDENTIALS, password);
namingContext = new InitialContext(env);
// Perform the JNDI lookups
String cfString = System.getProperty("connection.factory",
DEFAULT_CONNECTION_FACTORY);
ConnectionFactory connectionFactory = (ConnectionFactory)
namingContext.lookup(cfString);
Destino
Un destino JMS es un objeto administrado que un cliente usa para especificar la dirección
de destino para los mensajes que envía o la dirección de origen en la que debe recibir
mensajes. Dependiendo del tipo de mensajería, un destino puede ser una queue (cola)
o un topic (tema). Algunos clientes JMS inclusive interactúan con varios destinos. De
manera similar a la fábrica de conexiones, el destino generalmente se inyecta en el
cliente a través de la anotación CDI @Resource o mediante una búsqueda JNDI para
encontrar el objeto administrado.
@Resource(lookup = "jms/MyQueue")
private static Queue queue;
@Resource(lookup = "jms/MyTopic")
private static Topic topic;
Conexión
Una conexión JMS es la conexión virtual al proveedor JMS y generalmente se crea en el
cliente usando la API JMS para crear un contexto JMS. La implementación de la conexión
no afecta el código del cliente JMS, y el administrador de servidores la gestiona por
completo en la configuración de la fábrica de conexiones.
Sesión
Una sesión JMS es un recurso uniproceso tanto para enviar como para recibir mensajes,
y se crea mediante una conexión. La clase Session (Sesión) es generalmente la que
crea el listener, el productor y los objetos del mensaje. Las sesiones de JMS también
proporcionan el comportamiento transaccional, lo que permite a los clientes JMS
proporcionar transacciones locales y agrupar un conjunto de acciones de mensajes, ya
JB183-EAP7.0-es-2-20180124
329
Capítulo 8. Creación de aplicaciones de mensajería con JMS
sea enviar o recibir, en una única unidad de trabajo que se puede confirmar si funciona
correctamente o revertir en caso de que se produzca una falla.
Contexto
Un contexto JMS es la combinación de una sesión y una conexión que proporciona tanto
una conexión directa al proveedor de JMS como una clase uniproceso que se puede usar
para enviar mensajes al, y recibir mensajes del, proveedor. Al escribir en un cliente JMS,
un desarrollador generalmente crea un objeto JMSContext de la ConnectionFactory
que se inyectó.
JMSContext context = connectionFactory.createContext();
Productor de mensajes
Un objeto de productor de mensajes JMS se crea por un contexto JMS y se puede usar
para enviar mensajes a un destino. La API JMS v2.0 proporciona la interfaz JMSProducer
para representar productores de mensajes. El contexto JMS proporciona un método para
crear un productor mediante un contexto:
JMSProducer producer = context.createProducer();
Los desarrolladores pueden usar una invocación simple de método para enviar un
mensaje a un destino:
producer.send(dest, message);
Consumidor de mensajes
Un objeto de consumidor de mensajes JMS se crea por un contexto JMS y se puede usar
para recibir mensajes de un destino en forma asíncrona. La API JMS v2.0 proporciona
la interfaz JMSConsumer para representar consumidores de mensajes. El contexto JMS
proporciona un método para crear un consumidor conectado a un destino mediante un
contexto:
JMSConsumer consumer = context.createConsumer(dest);
Los desarrolladores pueden usar una invocación simple de método para recibir
mensajes:
Message m = consumer.receive();
El método receive() toma un parámetro de tiempo límite opcional, que define por
cuánto tiempo debe bloquear la ejecución antes de arrojar el resultado null (nulo).
En cambio, para la entrega asíncrona, se debe usar un MessageListener o un bean
controlado por mensajes. Esto se aborda en detalle en una sección posterior.
Comprensión de los componentes de un mensaje
JMS
Los mensajes JMS usan un formato básico estandarizado para permitir que los mensajes
sean lo suficientemente flexibles como para adaptarse a la mayoría de los casos de uso,
así como proporcionan potencial de compatibilidad con tecnologías de mensajería no
330
JB183-EAP7.0-es-2-20180124
Comprensión de los componentes de un mensaje JMS
pertenecientes a JMS. La estructura básica de un mensaje de JMS está conformada por tres
partes: encabezados, propiedades y un cuerpo.
Encabezados
Los encabezados de JMS son usados por clientes y proveedores para enrutar y clasificar
mensajes. Los encabezados son pares de claves/valores, pero existe un conjunto
predeterminado de claves posibles definidas por la especificación de JMS. La interfaz
Message (Mensaje) proporciona métodos para obtener y establecer todos los valores de
encabezado posibles.
En la siguiente tabla se resumen los campos de encabezados JMS y cómo los
desarrolladores de aplicaciones y proveedores de JMS suelen usarlos:
Campos de encabezado JMS
Nombre del
encabezado
Descripción
JMSMessageID
El identificador único para cada mensaje enviado por un
proveedor se establece automáticamente.
JMSDestination
El destino al que se envía el mensaje.
JMSPriority
El nivel de prioridad del mensaje. Este se puede usar para
reordenar mensajes y hacer avanzar mensajes más importantes
al tope de la cola.
JMSDeliveryMode
Las configuraciones de persistencia del mensaje controlan cómo
el proveedor gestiona el mensaje.
JMSDeliveryTime
El sello de hora cuando el proveedor de JMS entregó el mensaje.
JMSTimeStamp
El sello de hora cuando el mensaje se entregó al proveedor de
JMS.
JMSExpiration
La hora en que el mensaje se debe considerar caducado.
JMSCorrelationID El ID de otro mensaje, usado para relacionar dos mensajes,
generalmente establecido por el cliente.
JMSReplyTo
El destino al que se pueden enviar las respuestas a mensajes.
JMSRedelivered
El estado de si actualmente, el mensaje es o no es una reentrega.
El método send (enviar) del cliente JMS establece automáticamente la mayoría de los
encabezados de JMS, pero el cliente debe establecer algunos encabezados en forma
manual, por lo que estos quedan en blanco si no se establecen. Estos encabezados
específicos del cliente incluyen JMSReplyTo, JMSCorrelationID y JMSType.
Propiedades
Las propiedades proporcionan la capacidad de establecer valores adicionales en un
mensaje JMS, que no fueron provistos por encabezados estándares. De manera similar a
los encabezados, las propiedades son pares de claves/valores, sin limitaciones sobre las
posibles claves que puede usar un desarrollador. Las propiedades se deben establecer
antes de enviar un mensaje y son de solo lectura en mensajes JMS recibidos por un
cliente.
La interfaz Message proporciona los siguientes métodos para obtener y establecer
las propiedades específicas para todos los tipos primitivos, como int y double. La
superclase Object (Objeto) también se admite, y permite que las propiedades sean
JB183-EAP7.0-es-2-20180124
331
Capítulo 8. Creación de aplicaciones de mensajería con JMS
de cualquier tipo. También hay un método getPropertyNames() que devuelve una
Enumeration (Enumeración) de todos los posibles nombres de propiedad para un
mensaje JMS específico.
Cuerpo del mensaje
La API JMS incluye seis tipos diferentes de mensajes basados en el tipo de objeto del
cuerpo del mensaje. Esto permite a los desarrolladores enviar y recibir diferentes tipos de
datos fácilmente mediante una variedad de objetos de Java para representar el cuerpo
del mensaje, que permite una mayor flexibilidad. En la siguiente tabla se resumen los seis
tipos y el contenido de sus cuerpos:
Tipos de cuerpos de mensaje JMS
Tipo
Cuerpo del mensaje
Message
Un cuerpo vacío. Este tipo de mensaje contiene solo
encabezados y propiedades, y se usa cuando la aplicación no
necesita el cuerpo de un mensaje.
BytesMessage
Un flujo de bytes, generalmente usado para codificar un cuerpo
para que coincida con un formato de mensaje existente.
StreamMessage
Un flujo de primitivos de Java que debe leerse de manera
secuencial.
TextMessage
Un objeto String de Java que puede incluir datos de JSON o
XML.
ObjectMessage
Cualquier objeto de Java serializable.
MapMessage
Un conjunto de pares claves/valores en el que las claves son
objetos String y los valores son primitivos de Java.
La API JMS proporciona métodos en Context para crear cada tipo de mensaje. A
continuación, se muestra un ejemplo de la creación de un TextMessage:
TextMessage hello = context.createTextMessage();
hello.setText("Hello World!");
context.createProducer().send(hello);
Referencias
Para obtener más información, consulte el capítulo JMS de la Guía de desarrollo para
Red Hat JBoss EAP 7 en
https://access.redhat.com/documentation/en/red-hat-jboss-enterpriseapplication-platform/
332
JB183-EAP7.0-es-2-20180124
Cuestionario: Descripción de la arquitectura de JMS
Cuestionario: Descripción de la arquitectura
de JMS
Una los siguientes ítems con sus equivalentes en la tabla.
Conexión
Consumidor
Fábrica de conexiones
Contexto
Productor
Destino
Sesión
Descripción
Componente
JMS
Un objeto administrado usado por clientes para
establecer la dirección de destino u origen para los
mensajes.
Un objeto administrado que el cliente usa para crear
una conexión al proveedor JMS.
Un recurso uniproceso tanto para enviar como para
recibir mensajes, creado mediante una conexión.
La combinación de una sesión y una conexión, que
proporciona tanto una conexión al proveedor de JMS
como una clase uniproceso para enviar mensajes al
proveedor y recibir mensajes de este.
La conexión virtual al proveedor JMS, generalmente
creada en el cliente usando primero la API JMS para
crear un contexto JMS.
JB183-EAP7.0-es-2-20180124
333
Capítulo 8. Creación de aplicaciones de mensajería con JMS
Descripción
Componente
JMS
Creado por un contexto JMS y utilizado para enviar
mensajes a un destino.
Creado por un contexto JMS y utilizado para recibir
mensajes de un destino en forma asíncrona.
334
JB183-EAP7.0-es-2-20180124
Solución
Solución
Una los siguientes ítems con sus equivalentes en la tabla.
Descripción
Componente
JMS
Un objeto administrado usado por clientes para
establecer la dirección de destino u origen para los
mensajes.
Destino
Un objeto administrado que el cliente usa para crear
una conexión al proveedor JMS.
Fábrica de
conexiones
Un recurso uniproceso tanto para enviar como para
recibir mensajes, creado mediante una conexión.
Sesión
La combinación de una sesión y una conexión, que
proporciona tanto una conexión al proveedor de JMS
como una clase uniproceso para enviar mensajes al
proveedor y recibir mensajes de este.
Contexto
La conexión virtual al proveedor JMS, generalmente
creada en el cliente usando primero la API JMS para
crear un contexto JMS.
Conexión
Creado por un contexto JMS y utilizado para enviar
mensajes a un destino.
Productor
Creado por un contexto JMS y utilizado para recibir
mensajes de un destino en forma asíncrona.
Consumidor
JB183-EAP7.0-es-2-20180124
335
Capítulo 8. Creación de aplicaciones de mensajería con JMS
Creación de un Cliente JMS
Objetivo
Después de completar esta sección, los estudiantes deberán ser capaces de crear un cliente
JMS que produce y consume mensajes usando la API JMS.
Acceso a objetos administrados en un Cliente JMS
En cualquier empresa que usa middleware orientado a mensajes, los administradores
del sistema y, en algunas ocasiones, los desarrolladores, son responsables de configurar
servidores de aplicaciones para tener los objetos administrados necesarios para admitir
mensajería. Estos objetos administrados contienen toda la configuración de conexión
relevante requerida para comunicarse con el proveedor de JMS. Al usar Red Hat JBoss EAP 7,
los nombres de JNDI para estos objetos se encuentra en el archivo de configuración
standalone.xml. Al eliminar estos detalles de conexión del código de la aplicación, esta se
vuelve menos frágil y fácil de mover de un entorno a otro.
Después de definir los objetos administrados, estos quedan disponibles directamente del
código de cliente JMS mediante técnicas como la inyección de dependencias. Por ejemplo,
las aplicaciones usan JNDI para encontrar los objetos de la fábrica de destino y conexión
necesarios para crear un objeto JMSContext.
La anotación @Resource inyecta directamente un objeto de destino, como una Cola
(Queue) o Tema (Topic), así como un objeto ConnectionFactory que usa solo el
nombre JNDI definido en la configuración del servidor. En el siguiente ejemplo se muestra la
anotación @Resource para inyectar un objeto Queue (Cola):
@Resource(mappedName = "java:jboss/jms/queue/helloWorldQueue")
private Queue helloWorldQueue;
La anotación @Resource también puede inyectar unaConnectionFactory, como se
muestra en el siguiente ejemplo:
@Resource(mappedName = "java:comp/DefaultJMSConnectionFactory")
private static ConnectionFactory connectionFactory;
private JMSContext context;
@PostConstruct
public void init(){
context = connectionFactory.createContext();
}
Después de usar la anotación @Resource para inyectarla, se usa ConnectionFactory
para crear el objeto JMSContext. JMSContext crea otros objetos JMS, como mensajes,
productores o consumidores.
De manera alternativa, se puede crear un objeto JMSContext mediante una anotación
@Inject. Este enfoque elimina el código innecesario para crear el objeto JMSContext
cuando la fábrica de conexiones ya no requiere más personalización en el código de la
aplicación. Existen dos opciones: especificar el nombre JNDI de la fábrica de conexiones para
336
JB183-EAP7.0-es-2-20180124
Uso de la Interfaz del productor de mensajes para enviar mensajes
usar al crear el objeto JMSContext, o usar la fábrica de conexiones predeterminada para el
servidor de aplicaciones.
Para usar la fábrica de conexiones predeterminada para el servidor de aplicaciones, use la
anotación @Inject como se muestra en el siguiente ejemplo, antes de declarar el objeto
JMSContext:
@Inject
private JMSContext context;
Si se requiere una fábrica de conexiones específica además de la predeterminada, use la
anotación @JMSConnectionFactory junto con la anotación @Inject como se muestra en el
siguiente ejemplo:
@Inject
@JMSConnectionFactory("jms/MyConnectionFactory")
private JMSContext context;
Uso de la Interfaz del productor de mensajes para
enviar mensajes
Un productor de mensajes es un tipo de objeto JMS creado por el objeto JMSContext
que envía mensajes JMS de un destino. La interfaz JMSProducer, introducida en JMS v2.0
encapsula la funcionalidad de los productores de mensajes. La clase del productor es liviana
y no consume muchos recursos del sistema cuando se crea o se encuentra en uso. Por este
motivo, una única instancia de JMSProducer se puede volver a utilizar para enviar varios
mensajes o el productor se puede crear para cada mensaje.
En el siguiente ejemplo se muestra cómo se crea una instancia JMSProducer, se la almacena
en una variable y, luego, se usa esa variable para enviar un mensaje a un destino:
JMSProducer producer = context.createProducer();
TextMessage message = context.createTextMessage(msg);
producer.send(helloWorldQueue, message);
Cree la instancia JMSProducer mediante el objeto JMSContext y almacénela en la
variable producer (productor).
Use el objeto JMSContext para crear una nueva instancia TextMessage de un objeto
String nombrado msg.
Envíe el mensaje al objeto de destino helloWorldQueue mediante la variable producer
(productor).
Uso de la Interfaz del consumidor de mensajes para
recibir mensajes
Un consumidor de mensajes es un tipo de objeto JMS creado por el objeto JMSContext
que recibe mensajes JMS de un destino de manera asíncrona, a través de una invocación
de método. La interfaz JMSConsumer, introducida en JMS v2.0 se usa para encapsular la
funcionalidad de los consumidores de mensajes.
JB183-EAP7.0-es-2-20180124
337
Capítulo 8. Creación de aplicaciones de mensajería con JMS
La principal funcionalidad de un consumidor de mensajes se proporciona mediante el
método receive(), que incluye un parámetro de timeout (tiempo límite) opcional para
establecer el tiempo que debe esperar una aplicación para recibir un mensaje del destino
antes del tiempo límite. Para crear un consumidor de mensajes flexible, asegúrese de
manejar las excepciones de JMS que se generan durante la entrega de mensajes. Use un
bloque try-catch al recibir mensajes de un destino y maneje todas las excepciones JMS
relevantes.
El proveedor de JMS puede asignar algunos recursos en nombre de un JMSConsumer. Los
clientes JMS deben cerrar consumidores cuando no son necesarios, en lugar de depender de
la recopilación de residuos, que puede no realizarse en forma oportuna, aumentando así el
peso que la aplicación coloca en el sistema. Generalmente, el consumidor invoca el método
close() en el bloque finally una vez que ya no se necesite el consumidor.
En el siguiente ejemplo se muestra cómo se crea una instancia JMSConsumer, se almacena
el consumidor en una variable y, luego, se usa esa variable para recibir un mensaje de un
destino:
JMSConsumer consumer = context.createConsumer(helloWorldQueue);
try {
TextMessage msg = (TextMessage) consumer.receiveNoWait();
if(msg != null) {
System.out.println("Received Message: "+ msg);
return msg.getBody(String.class);
}else {
return null;
}
}
catch (Exception e) {
e.printStackTrace();
return null;
} finally {
consumer.close();
}
Cree la instancia JMSConsumer usando el objeto JMSContext y envíe la
helloWorldQueue como destino; luego, almacénelo en la variable consumer.
Use receiveNoWait para verificar un nuevo mensaje en la cola, pero no bloquee si no
hay ningún mensaje disponible.
Cierre el objeto del consumidor para liberar la conexión y conservar recursos.
Demostración: Creación de un Cliente JMS
1.
Ejecute el siguiente comando para preparar archivos usados por esta demostración.
[student@workstation ~]$ demo hello-jms setup
2.
Inicie JBDS e importe el proyecto hello-jms.
Este proyecto es una aplicación web simple que usa una página JSF respaldada por un
EJB con alcance de solicitud y estado para imprimir un mensaje de bienvenida simple
para el usuario después de ingresar su nombre. También envía un mensaje JMS a una
338
JB183-EAP7.0-es-2-20180124
Demostración: Creación de un Cliente JMS
cola cada vez que saluda al usuario. También existe la opción de recuperar el mensaje
más antiguo de la cola y mostrarlo en la página.
3.
Inspeccione el archivo pom.xml y observe la dependencia en la librería JBoss que el
servidor usa para la especificación JMS.
<dependency>
<groupId>org.jboss.spec.javax.jms</groupId>
<artifactId>jboss-jms-api_2.0_spec</artifactId>
<scope>provided</scope>
</dependency>
4.
Actualice la clase EJB JMSClient en el paquete messaging para inyectar un objeto
JMSContext conectado al agente Artemis incorporado y asigne la Cola al objeto
administrado usando JNDI:
@Startup
@Singleton
public class JMSClient {
//TODO Map the destination queue to the admin object using JNDI and @Resource
@Resource(mappedName = "java:jboss/jms/queue/helloWorldQueue")
private Queue helloWorldQueue;
//TODO Inject a JMSContext to get a Connection and Session to the embedded
broker
@Inject
JMSContext context;
...
5.
En la clase EJB JMSClient, termine el método sendMessage y cree un
MessageProducer para enviar cada mensaje de saludo entrante a la cola:
public void sendMessage(String msg) {
try {
//TODO Create a JMSProducer
JMSProducer producer = context.createProducer();
//TODO Create a TextMessage
TextMessage message = context.createTextMessage(msg);
//TODO Send the message
producer.send(helloWorldQueue, message);
System.out.println("Sent Message: "+ msg);
}
catch (Exception e) {
e.printStackTrace();
}
}
6.
En la clase EJB JMSClient, finalice el método getMessage, cree una clase
MessageConsumer y, luego, reciba el mensaje más antiguo de la cola:
public String getMessage() {
//TODO Create a JMS Consumer
JB183-EAP7.0-es-2-20180124
339
Capítulo 8. Creación de aplicaciones de mensajería con JMS
JMSConsumer consumer = context.createConsumer(helloWorldQueue);
try {
//TODO receive a message without waiting
TextMessage msg = (TextMessage) consumer.receiveNoWait();
if(msg != null) {
System.out.println("Received Message: "+ msg);
return msg.getBody(String.class);
}else {
return null;
}
}
catch (Exception e) {
e.printStackTrace();
return null;
} finally {
//TODO close the consumer
consumer.close();
}
}
7.
Inicie el servidor JBoss EAP local dentro de JBDS.
8.
Implemente la aplicación en el servidor JBoss EAP local y pruébela en un explorador. En
una ventana de terminal, ejecute el siguiente comando:
[student@workstation ~]$ cd /home/student/JB183/labs/hello-jms
[student@workstation hello-jms]$ mvn wildfly:deploy
Abra http://localhost:8080/hello-jms/ en su explorador, luego, pruebe la
aplicación y asegúrese de que muestre el saludo correctamente para cada nombre que
se ingresa y que pueda recuperar esos saludos de la cola correctamente.
9.
Anule la implementación de la aplicación y detenga el servidor.
[student@workstation hello-jms]$ mvn wildfly:undeploy
Referencias
Para obtener más información, consulte el capítulo JMS de la guía Configuración de
mensajería para Red Hat JBoss EAP 7 en
https://access.redhat.com/documentation/en/red-hat-jboss-enterpriseapplication-platform/
340
JB183-EAP7.0-es-2-20180124
Ejercicio guiado: Creación de un cliente JMS
Ejercicio guiado: Creación de un cliente JMS
En este ejercicio, escribirá un cliente JMS que usa la API JMS y la cola ubicada en el agente
Artemis en JBoss EAP para enviar y recibir mensajes JMS.
Resultado
Deberá ser capaz de usar la API JMS y los objetos administrados provistos por JBoss EAP para
compilar una instancia de MessageProducer y una interfaz MessageConsumer para enviar y
recibir mensajes de una cola.
Antes de comenzar
Abra una ventana de terminal en la máquina virtual workstation y ejecute el siguiente
comando para descargar los archivos necesarios para este trabajo de laboratorio.
[student@workstation ~]$ lab jms-client setup
Pasos
1. Abra JBDS e importe el proyecto de Maven.
1.1. Haga doble clic en el icono de JBoss Developer Studio en el escritorio de la estación
de trabajo para abrir JBDS. Seleccione el espacio de trabajo /home/student/
JB183/workspace y haga clic en OK (Aceptar).
1.2. En el menú de JBDS, haga clic en File (Archivo) > Import (Importar)para abrir el
asistente para Import (Importar).
1.3. En la página Select (Seleccionar), haga clic en Maven > Existing Maven Projects
(Proyectos de Maven existentes) y, luego, haga clic en Next (Siguiente).
1.4. En la página Maven projects (Proyectos de Maven), haga clic en Browse (Explorar)
para abrir la ventana Select root folder (Seleccionar carpeta raíz). Diríjase al
directorio /home/student/JB183/labs/. Seleccione la carpeta jms-client y
haga clic en OK (Aceptar).
1.5. En la página Maven projects (Proyectos de Maven), haga clic en Finish (Finalizar).
1.6. Observe la barra de estado de JBDS para monitorear el progreso de la operación
de importación. Es probable que la descarga de todas las dependencias requeridas
demore unos minutos.
2.
Revise los nombres JNDI de los objetos JMS administrados, definidos en la configuración
del servidor JBoss EAP 7 local.
2.1. Abra una ventana de terminal en la máquina virtual workstation y, usando su
editor de texto preferido, abra el archivo de configuración EAP en /opt/eap/
standalone/configuration/standalone-full.xml.
[student@workstation ~]$ less /opt/eap/standalone/configuration/\
standalone-full.xml
2.2. Desplácese hacia abajo en el archivo hasta ver el subsistema
urn:jboss:domain:messaging-activemq:1.0:
JB183-EAP7.0-es-2-20180124
341
Capítulo 8. Creación de aplicaciones de mensajería con JMS
<subsystem xmlns="urn:jboss:domain:messaging-activemq:1.0">
<server name="default">
...
<jms-queue name="ExpiryQueue" entries="java:/jms/queue/ExpiryQueue"/>
<jms-queue name="DLQ" entries="java:/jms/queue/DLQ"/>
<jms-queue name="helloWorldQueue" entries="queue/helloWorldQueue java:jboss/
jms/queue/helloWorldQueue"/>
<jms-queue name="TodoListQueue" entries="queue/TodoListQueue java:jboss/jms/
queue/TodoListQueue"/>
<connection-factory name="InVmConnectionFactory" entries="java:/
ConnectionFactory" connectors="in-vm"/>
<connection-factory name="RemoteConnectionFactory" entries="java:jboss/
exported/jms/RemoteConnectionFactory" connectors="http-connector"/>
<pooled-connection-factory name="activemq-ra" transaction="xa" entries="java:/
JmsXA java:jboss/DefaultJMSConnectionFactory" connectors="in-vm"/>
</server>
</subsystem>
Note que la helloWorldQueue tiene una entrada JNDI de java:jboss/jms/
queue/helloWorldQueue.
2.3. Cierre el editor de texto y no guarde los cambios al archivo standalone-full.xml.
Advertencia
Pueden producirse problemas en el ejercicio si se introducen cambios
inesperados en el archivo de configuración.
3.
Configure el contexto JMS y el destino.
3.1. Abra la clase JMSClient; para ello, expanda el ítem jms-client en la pestaña Project
Explorer (Explorador de proyectos) en el panel izquierdo de JBDS y, luego, haga clic
en jms-client > Java Resources > src/main/java > com.redhat.training.messaging y
expándalo. Haga doble clic en el archivo JMSClient.java.
3.2. Use la anotación @Inject para inyectar el objeto JMSContext predeterminado,
que proporciona una conexión al agente Artemis incorporado que se ejecuta en el
servidor JBoss local.
//TODO Inject a JMSContext to get a Connection and Session to the embedded
broker
@Inject
JMSContext context;
3.3. Use la anotación @Resource para inyectar el destino helloWorldQueue:
/TODO Map the destination queue to the admin object using JNDI and @Resource
@Resource(mappedName = "java:jboss/jms/queue/helloWorldQueue")
private Queue helloWorldQueue;
Asegúrese de que el atributo mappedName esté configurado correctamente en el
nombre JNDI de la cola.
342
JB183-EAP7.0-es-2-20180124
3.4. Guarde los cambios en el archivo con Ctrl+S.
4.
Cree un productor JMS que coloque los mensajes en el helloWorldQueue.
4.1. Use el método createProducer provisto por la interfaz JMSContext para compilar
una instancia de MessageProducer en el método sendMessage:
//TODO Create a JMSProducer
JMSProducer producer = context.createProducer();
4.2. Cree un TextMessage usando la interfaz JMSContext para asignar el valor del
parámetro msg en el cuerpo del mensaje JMS:
//TODO Create a TextMessage
TextMessage message = context.createTextMessage(msg);
4.3. Envíe el mensaje al destino, usando el productor:
//TODO Send the message
producer.send(helloWorldQueue, message);
5.
Cree un consumidor JMS que lea un mensaje de la helloWorldQueue.
5.1. Use el método createConsumer provisto por la interfaz JMSContext para compilar
una instancia de MessageConsumer para el destino helloWorldQueue en el
método getMessage:
//TODO Create a JMS Consumer
JMSConsumer consumer = context.createConsumer(helloWorldQueue);
5.2. Intente leer un mensaje de la cola, sin esperar si hay mensajes disponibles. Use el
método receiveNoWait provisto por la interfaz MessageConsumer y difunda el
resultado en una instancia TextMessage:
//TODO receive a message without waiting
TextMessage msg = (TextMessage) consumer.receiveNoWait();
5.3. Cierre el consumidor después de completar todo mediante el método close
(cerrar):
//TODO close the consumer
consumer.close();
6.
Seleccione la pestaña Servers (Servidores) en el panel inferior de JBDS para iniciar EAP.
Haga clic con el botón derecho en el servidor Red Hat JBoss EAP 7.0 [Stopped] y haga
clic en el botón verde para iniciar el servidor. Observe la Console (Consola) hasta que el
servidor se inicie y muestre el siguiente mensaje:
JB183-EAP7.0-es-2-20180124
343
Capítulo 8. Creación de aplicaciones de mensajería con JMS
INFO [org.jboss.as] (Controller Boot Thread) WFLYSRV0025: JBoss EAP 7.0.0.GA
(WildFly Core 2.1.2.Final-redhat-1) started
7.
Implemente la aplicación en JBoss EAP con Maven; para ello, ejecute los siguientes
comandos:
[student@workstation ~]$ cd /home/student/JB183/labs/jms-client
[student@workstation jms-client]$ mvn wildfly:deploy
Una vez finalizado, aparece BUILD SUCCESS, como se muestra en el próximo ejemplo:
[INFO]
[INFO]
[INFO]
[INFO]
[INFO]
[INFO]
[INFO]
-----------------------------------------------------------------------BUILD SUCCESS
-----------------------------------------------------------------------Total time: 17.116 s
Finished at: 2016-12-01T07:26:55-05:00
Final Memory: 35M/210M
------------------------------------------------------------------------
Valide la implementación en el registro de servidores que se muestra en la pestaña
Console (Consola) en JBDS. Cuando se implementa la aplicación, aparece lo siguiente en
el registro:
INFO [org.jboss.as.server] (management-handler-thread - 9) WFLYSRV0010: Deployed
"jms-client.war" (runtime-name : "jms-client.war")
8.
Pruebe la aplicación en un explorador.
8.1. Abra http://localhost:8080/jms-client en un explorador en la máquina
virtual workstation.
Figura 8.3: Página de inicio de la aplicación
8.2. Intente leer un mensaje de la cola usando el botón Get Oldest Message On Queue
(Obtener el mensaje más antiguo de la cola). Aparece el mensaje Queue is empty!
(¡La cola está vacía!).
344
JB183-EAP7.0-es-2-20180124
8.3. Ingrese su nombre en el campo de texto y presione el botón Submit (Enviar). La
aplicación lo saluda y muestra la hora actual en el servidor:
Figura 8.4: Aplicación saludando al usuario por su nombre con la hora actual del servidor
8.4. Intente leer un mensaje de la cola usando el botón Get Oldest Message On Queue
(Obtener el mensaje más antiguo de la cola). En este momento, se muestra el último
saludo:
Figura 8.5: Obtener el mensaje más antiguo de la cola
9.
Anule la implementación de la aplicación y detenga JBoss EAP.
9.1. Ejecute el siguiente comando para anular la implementación de la aplicación:
[student@workstation jms-client]$ mvn wildfly:undeploy
9.2. Para cerrar el proyecto, haga clic con el botón derecho en el proyecto jms-client
del Project Explorer (Explorador de proyectos) y seleccione Close Project (Cerrar
proyecto).
9.3. Haga clic con el botón derecho en el servidor Red Hat JBoss EAP 7.0 de la pestaña
Servers (Servidores) de JBDS y haga clic en Stop (Detener).
Esto concluye el ejercicio guiado.
JB183-EAP7.0-es-2-20180124
345
Capítulo 8. Creación de aplicaciones de mensajería con JMS
Creación de MDB
Objetivo
Tras finalizar esta sección, los estudiantes deberán ser capaces de crear, empaquetar e
implementar un bean controlado por mensajes.
Comprensión del ciclo de vida de un bean
controlado por mensajes
La especificación Java EE incluye beans controlados por mensajes (MDB) como mecanismo para
permitir un consumo asíncrono de mensajes de un destino JMS. A diferencia de otros beans
de Java, los MDB no exponen funciones o métodos públicos para que otros beans accedan
a través de la inyección de dependencias. En cambio, toda la comunicación con un MDB se
lleva a cabo a través de un JMS. Cada MDB está configurado para escuchar en un destino JMS
específico, a través de un objeto administrado.
El servidor de aplicaciones, JBoss EAP, es responsable de gestionar el ciclo de vida de
un MDB. El servidor de aplicaciones define un conjunto (pool) de MDB, que permite un
procesamiento simultáneo de mensajes. El procesamiento de mensajes simultáneos
proporciona una mejora considerable en el rendimiento de mensajes. El servidor crea los
MDB en el conjunto (pool) automáticamente durante el inicio. Cuando el destino en el que
está escuchando el MDB recibe un nuevo mensaje, el contenedor de aplicaciones invoca
automáticamente el método onMessage en una de las instancias MDB previamente creadas.
La interfaz MessageListener, que todos los MDB deben implementar, requiere este
método onMessage. Una vez que el MDB termina el procesamiento, la instancia del MDB se
devuelve al conjunto (pool) para su reutilización. El uso de un conjunto (pool) de MDB mejora
el rendimiento de la aplicación, ya que, cuando el destino recibe mensajes, ya se crearon
instancias de la clase MDB que está lista para procesar el mensaje de inmediato.
Figura 8.6: Los MDB escuchan colas específicas
Debido a la ejecución asíncrona y el comportamiento de agrupación del MDB, su
implementación tiene varios requisitos. Los MDB deben estar completamente sin estado, y
no deben conservar el estado de conversación o los datos del mensaje. No obstante, los MDB
pueden usar la inyección para acceder a otros recursos con estado, como información de
conexión del proveedor de JMS, una conexión de base de datos o una instancia de otro EJB.
Asimismo, todas las instancias de una clase de MDB deben ser iguales y no tienen relación
con ningún cliente específico, lo que permite que el contenedor de la aplicación elija entre
todas ellas; esto hace posible la simultaneidad.
346
JB183-EAP7.0-es-2-20180124
Revisión de la Interfaz de Listener de Mensajes
La interfaz MessageConsumer de JMS difiere de una MDB en varias formas importantes.
El desarrollador debe invocar manualmente el MessageConsumer, mientras se crea
una MDB de manera automática. Asimismo, el consumidor de JMS solo permite una
verificación asíncrona para nuevos mensajes y generalmente es uniproceso, a menos que el
desarrollador implemente manualmente la simultaneidad. En contraste, el MDB es asíncrono
y multiproceso. Por estos motivos, los MDB son una solución más sólida para las aplicaciones
Java EE que necesitan consumir mensajes de un destino de manera asíncrona.
Revisión de la Interfaz de Listener de Mensajes
Todas las MDB deben implementar la interfaz MessageListener. El único método público
requerido por esta interfaz es el método onMessage, que toma un mensaje JMS como
argumento y tiene un tipo de devolución void (nulo). El servidor de aplicaciones invoca
automáticamente este método y envía el mensaje de JMS que se recibió en el destino
como el argumento. El desarrollador de MDB es responsable de implementar la lógica de
procesamiento de mensajes personalizados que se ejecuta al invocar onMessage.
En el siguiente ejemplo de MessageListener se muestra un método onMessage que
verifica el tipo de mensaje y registra el contenido del mensaje:
public class QueueListener implements MessageListener {
private final static Logger LOGGER = Logger.getLogger(this.class.getName());
public void onMessage(Message rcvMessage) {
TextMessage msg = null;
try {
if (rcvMessage instanceof TextMessage) {
msg = (TextMessage) rcvMessage;
LOGGER.info("Received Message from helloWorldQueue ===> " + msg.getText());
} else {
LOGGER.warn("Incorrect Message Type!");
}
} catch (JMSException e) {
throw new RuntimeException(e);
}
}
...
Observe que el método de ejemplo es completamente sin estado y no conserva datos de
mensajes. El ejemplo tampoco realiza suposiciones acerca del tipo de mensaje, ya que esto
no se puede garantizar. El MDB debe fallar correctamente si recibe un tipo de mensaje
inesperado.
Uso de anotaciones para configurar un MDB
Por último, debe activar el MDB, registrarlo con el contenedor y configurar los objetos
administrados que el MDB usa para determinar el destino que se debe escuchar. Para activar
un MDB y relacionarlo con un destino, use la anotación @MessageDriven. Esta anotación usa
propiedades de configuración de activación para configurar el MDB.
Los MDB requieren una sola propiedad, destinationLookup, que es el nombre de JNDI del
objeto administrado de destino. Los MDB también admiten algunas propiedades opcionales
usadas para personalizar la conexión o el comportamiento del MDB. Envíe estas propiedades
en la anotación @MessageDriven usando el parámetro activationConfig. Use una
JB183-EAP7.0-es-2-20180124
347
Capítulo 8. Creación de aplicaciones de mensajería con JMS
anotación @ActivationConfigProperty para cada propiedad, especificando el nombre
de la propiedad con el atributo propertyName y el valor de la propiedad con el atributo
propertyValue.
El siguiente es un ejemplo de la configuración de un MDB por parte de estas anotaciones:
@MessageDriven(name = "QueueListener"
, activationConfig
= {
@ActivationConfigProperty(propertyName = "destinationLookup"
"queue/helloWorldQueue"),
@ActivationConfigProperty(propertyName = "destinationType"
"javax.jms.Queue")
})
public class QueueListener implements MessageListener {
, propertyValue =
, propertyValue =
El atributo name (nombre) establece el nombre del MDB como QueueListener. Este es
el nombre que el MDB usa para registrarse en el árbol JNDI. Este atributo es opcional y el
nombre predeterminado es el nombre de clase si no se especifica lo contrario.
El atributo activationConfig envía un conjunto de anotaciones
@ActivationConfigProperty para configurar el MDB.
La propiedad destinationLookup establece el nombre JNDI del destino por el que el
MDB debe escuchar.
La propiedad destinationType especifica si el destino es una queue (cola) o topic
(tema). Esta propiedad es opcional.
En la siguiente tabla se resumen algunas de las demás propiedades de configuración de
activación disponibles:
Propiedades de configuración de activación de JMS
Nombre de propiedad
Descripción de propiedad
destinationLookup
El nombre de JNDI de la queue (cola) o topic (tema). Esta
propiedad es obligatoria.
connectionFactoryLookup El nombre de JNDI de la fábrica de conexiones que se debe
usar para conectarse al destino. Esta propiedad es opcional
y, si no se especifica, se usa la fábrica de conexiones
agrupadas, nombrada activemq-ra.
destinationType
El tipo de destino, ya sea javax.jms.Queue o
javax.jms.Topic. Esta propiedad es obligatoria.
messageSelector
Una cadena usada para seleccionar un subconjunto de
mensajes disponibles. La sintaxis es similar a la sintaxis SQL
y se describe en detalle en la especificación JMS, y está más
allá del alcance de este curso. Esta propiedad es opcional.
acknowledgeMode
El tipo de reconocimiento cuando no se usa la
JMS gestionada. Los valores válidos son Autoacknowledge o Dups-ok-acknowledge. Esta propiedad
es opcional. De no especificarse, el valor predeterminado es
Auto-acknowledge.
348
JB183-EAP7.0-es-2-20180124
Uso de anotaciones para configurar un MDB
Referencias
Para obtener más información, consulte el capítulo JMS de la guía Configuración de
mensajería para Red Hat JBoss EAP 7 en
https://access.redhat.com/documentation/en/red-hat-jboss-enterpriseapplication-platform/
JB183-EAP7.0-es-2-20180124
349
Capítulo 8. Creación de aplicaciones de mensajería con JMS
Ejercicio guiado: Creación de un Bean
controlado por mensajes
En este ejercicio, creará un bean controlado por mensajes para leer mensajes de una cola de
manera asíncrona.
Resultado
Deberá ser capaz de implementar un bean controlado por mensajes para leer mensajes de
una cola.
Antes de comenzar
Abra una ventana de terminal en la máquina virtual workstation y ejecute el siguiente
comando para descargar los archivos necesarios para este ejercicio.
[student@workstation ~]$ lab create-mdb setup
Pasos
1. Abra JBDS e importe el proyecto de Maven.
1.1. Haga doble clic en el icono de JBoss Developer Studio en el escritorio de la estación
de trabajo para abrir JBDS. Seleccione el espacio de trabajo /home/student/
JB183/workspace y haga clic en OK (Aceptar).
1.2. En el menú de JBDS, haga clic en File (Archivo) > Import (Importar)para abrir el
asistente para Import (Importar).
1.3. En la página Select (Seleccionar), haga clic en Maven > Existing Maven Projects
(Proyectos de Maven existentes) y, luego, haga clic en Next (Siguiente).
1.4. En la página Maven projects (Proyectos de Maven), haga clic en Browse (Explorar)
para abrir la ventana Select root folder (Seleccionar carpeta raíz). Diríjase al
directorio /home/student/JB183/labs/. Seleccione la carpeta create-mdb y
haga clic en OK (Aceptar).
1.5. En la página Maven projects (Proyectos de Maven), haga clic en Finish (Finalizar).
1.6. Observe la barra de estado de JBDS para monitorear el progreso de la operación
de importación. Es probable que la descarga de todas las dependencias requeridas
demore unos minutos.
2.
Revise el conjunto (pool) MDB y los objetos administrados definidos en el archivo de
configuración JBoss EAP.
2.1. Usando su editor de texto preferido, abra el archivo de configuración EAP en /opt/
eap/standalone/configuration/standalone-full.xml.
[student@workstation ~]$ less /opt/eap/standalone/configuration/\
standalone-full.xml
2.2. Consulte la configuración del conjunto (pool) de hilos MDB en el archivo
standalone-full.xml navegando al subsistema urn:jboss:domain:ejb3:4.0:
350
JB183-EAP7.0-es-2-20180124
<subsystem xmlns="urn:jboss:domain:ejb3:4.0">
...
<mdb>
<resource-adapter-ref resource-adapter-name="${ejb.resource-adaptername:activemq-ra.rar}"/>
<bean-instance-pool-ref pool-name="mdb-strict-max-pool"/>
</mdb>
<pools>
<bean-instance-pools>
<strict-max-pool name="slsb-strict-max-pool" derive-size="fromworker-pools" instance-acquisition-timeout="5" instance-acquisition-timeoutunit="MINUTES"/>
<strict-max-pool name="mdb-strict-max-pool" derive-size="from-cpu-count"
instance-acquisition-timeout="5" instance-acquisition-timeout-unit="MINUTES"/
>
</bean-instance-pools>
</pools>
...
</subsystem>
Configure el conjunto (pool) nombrado mdb-strict-max-pool como el
conjunto de hilos que debe usar JBoss para todos los MDB.
Cree el conjunto (pool) de hilos nombrado mdb-strict-max-pool,
estableciendo su tamaño en relación al conteo total de CPU y definiendo un
valor de tiempo límite de espera cuando hay MDB disponibles antes de arrojar
un error.
2.3. Vea el objeto administrado creado para la cola helloWorldQueue al navegar al
subsistema urn:jboss:domain:messaging-activemq:1.0:
<subsystem xmlns="urn:jboss:domain:messaging-activemq:1.0">
<server name="default">
...
<jms-queue name="ExpiryQueue" entries="java:/jms/queue/ExpiryQueue"/>
<jms-queue name="DLQ" entries="java:/jms/queue/DLQ"/>
<jms-queue name="helloWorldQueue" entries="queue/helloWorldQueue java:jboss/
jms/queue/helloWorldQueue"/>
<jms-queue name="TodoListQueue" entries="queue/TodoListQueue java:jboss/jms/
queue/TodoListQueue"/>
<connection-factory name="InVmConnectionFactory" entries="java:/
ConnectionFactory" connectors="in-vm"/>
<connection-factory name="RemoteConnectionFactory" entries="java:jboss/
exported/jms/RemoteConnectionFactory" connectors="http-connector"/>
<pooled-connection-factory name="activemq-ra" transaction="xa"
entries="java:/JmsXA java:jboss/DefaultJMSConnectionFactory" connectors="invm"/>
</server>
</subsystem>
Note que la helloWorldQueue tiene una entrada JNDI de java:jboss/jms/
queue/helloWorldQueue.
2.4. Cierre el editor de texto y no guarde los cambios al archivo standalone-full.xml.
JB183-EAP7.0-es-2-20180124
351
Capítulo 8. Creación de aplicaciones de mensajería con JMS
Advertencia
Pueden producirse problemas en el trabajo de laboratorio si se introducen
cambios inesperados en el archivo de configuración.
3.
Revise la clase EJB PersonService.
Abra la clase PersonService; para ello, expanda el ítem create-mdb en la pestaña
Project Explorer (Explorador de proyectos) en el panel izquierdo de JBDS y, luego, haga
clic en create-mdb > Java Resources > src/main/java > com.redhat.training.service y
expándalo. Haga doble clic en el archivo PersonService.java.
@Inject
JMSClient jmsUtil;
...
// Send a JMS message to the 'helloWorldQueue'
jmsUtil.sendMessage("Said Hello to " + name.toUpperCase() + " at " + fdate);
...
Observe que esta clase de servicio usa una instancia del bean JMSClient para enviar un
mensaje a una cola, cada vez que una persona es saludada por la aplicación.
4.
Revise la clase EJB JMSClient.
Abra la clase JMSClient; para ello, expanda el ítem create-mdb en la pestaña Project
Explorer (Explorador de proyectos) en el panel izquierdo de JBDS y, luego, haga clic
en create-mdb > Java Resources > src/main/java > com.redhat.training.messaging y
expándalo. Haga doble clic en el archivo JMSClient.java.
@Resource(mappedName = "java:jboss/jms/queue/helloWorldQueue")
private Queue helloWorldQueue;
@Inject
JMSContext context;
...
JMSProducer producer = context.createProducer();
Observe que esta clase crea un JMSProducer y usa una anotación @Resource para
acceder al objeto administrado para la helloWorldQueue, mediante su entrada JNDI.
5.
Cree la nueva clase de beans controlados por mensajes.
5.1. Haga clic con el botón derecho en com.redhat.training.messaging y haga clic en
New (Nueva) > Class (Clase).
5.2. En el campo Name (Nombre), ingrese QueueListener. Haga clic en Finish
(Finalizar).
5.3. Haga que la nueva clase implemente la interfaz MessageListener:
package com.redhat.training.messaging;
import javax.jms.MessageListener;
352
JB183-EAP7.0-es-2-20180124
public class QueueListener implements MessageListener{
}
5.4. Resuelva el error de compilación agregando el método onMessage(Message
message) requerido por la interfaz:
package com.redhat.training.messaging;
import javax.jms.MessageListener;
import javax.jms.Message;
public class QueueListener implements MessageListener{
@Override
public void onMessage(Message rcvMessage) {
}
}
5.5. Implemente el método onMessage para realizar una verificación simple del tipo de
mensaje y registre el resultado en el registro del servidor:
package com.redhat.training.messaging;
import
import
import
import
javax.jms.MessageListener;
javax.jms.Message;
javax.jms.JMSException;
javax.jms.TextMessage;
public class QueueListener implements MessageListener{
public void onMessage(Message rcvMessage) {
TextMessage msg = null;
try {
if (rcvMessage instanceof TextMessage) {
msg = (TextMessage) rcvMessage;
System.out.println("Received Message from helloWorldQueue ===> " +
msg.getText());
} else {
System.out.println("Message of wrong type: " +
rcvMessage.getClass().getName());
}
} catch (JMSException e) {
throw new RuntimeException(e);
}
}
}
nota
Se requiere un bloque try/catch, ya que la llamada msg.getText() puede
arrojar una JMSException si el mensaje está dañado. Verificar el tipo de
mensaje ayuda a mitigar problemas con tipos de mensaje inesperados.
JB183-EAP7.0-es-2-20180124
353
Capítulo 8. Creación de aplicaciones de mensajería con JMS
5.6. Configure el MDB utilizando la anotación @MessageDriven, configurando
las instancias @ActivationConfigProperty necesarias para leer de la
helloWorldQueue:
package com.redhat.training.messaging;
import
import
import
import
import
import
javax.ejb.ActivationConfigProperty;
javax.ejb.MessageDriven;
javax.jms.MessageListener;
javax.jms.Message;
javax.jms.JMSException;
javax.jms.TextMessage;
@MessageDriven(name = "QueueListener", activationConfig = {
@ActivationConfigProperty(propertyName = "destinationLookup", propertyValue
= "queue/helloWorldQueue"),
@ActivationConfigProperty(propertyName = "destinationType", propertyValue =
"javax.jms.Queue")
})
public class QueueListener implements MessageListener {
...
Use la anotación @MessageDriven para registrar esta clase como un MDB.
Establezca la propiedad destinationLookup con el nombre JNDI del objeto
administrado para la cola.
Establezca la propiedad destinationType con javax.jms.Queue, ya que el
destino es una cola JMS, no un tema (topic).
5.7. Guarde los cambios usando Ctrl+S.
6.
Seleccione la pestaña Servers (Servidores) en el panel inferior de JBDS para iniciar EAP.
Haga clic con el botón derecho en el servidor Red Hat JBoss EAP 7.0 [Stopped] y haga
clic en el botón verde para iniciar el servidor. Observe la Console (Consola) hasta que el
servidor se inicie y muestre el siguiente mensaje:
INFO [org.jboss.as] (Controller Boot Thread) WFLYSRV0025: JBoss EAP 7.0.0.GA
(WildFly Core 2.1.2.Final-redhat-1) started
7.
Implemente la aplicación en JBoss EAP con Maven; para ello, ejecute los siguientes
comandos:
[student@workstation ~]$ cd /home/student/JB183/labs/create-mdb
[student@workstation create-mdb]$ mvn wildfly:deploy
Confirme la implementación correcta en el registro del servidor que aparece en la
pestaña Console (Consola) en JBDS. Cuando se implementa la aplicación, aparece lo
siguiente en el registro:
INFO [org.jboss.as.server] (management-handler-thread - 9) WFLYSRV0010: Deployed
"create-mdb.war" (runtime-name : "create-mdb.war")
354
JB183-EAP7.0-es-2-20180124
8.
Pruebe la aplicación en un explorador.
8.1. Abra http://localhost:8080/create-mdb en un explorador en la máquina
virtual workstation.
8.2. Ingrese Shadowman en el cuadro de texto denominado Enter your name: (Ingrese su
nombre:) y haga clic en Submit (Enviar).
La página se actualiza con el mensaje Hello SHADOWMAN!. Time on the server is: Nov 02
2017 03:55:03 PM
8.3. Verifique los registros de JBoss para ver si el MDB recibió el mensaje.
En JBDS, abra la pestaña de la Console (Consola) para ver los registros del servidor. Si
el MDB funciona correctamente, aparece el siguiente mensaje:
...Received Message from helloWorldQueue ===> Said Hello to SHADOWMAN at Nov 02
2017 03:55:03 PM
9.
Anule la implementación de la aplicación y detenga JBoss EAP.
9.1. Ejecute el siguiente comando para anular la implementación de la aplicación:
[student@workstation create-mdb]$ mvn wildfly:undeploy
9.2. Para cerrar el proyecto, haga clic con el botón derecho en el proyecto create-mdb
en Project Explorer (Explorador de proyectos) y seleccione Close Project (Cerrar
proyecto).
9.3. Haga clic con el botón derecho en el servidor Red Hat JBoss EAP 7.0 en la pestaña
Servers (Servidores) de JBDS y haga clic en Stop (Detener).
Esto concluye el ejercicio guiado.
JB183-EAP7.0-es-2-20180124
355
Capítulo 8. Creación de aplicaciones de mensajería con JMS
Trabajo de laboratorio: Creación de
aplicaciones de mensajería con JMS
En este trabajo de laboratorio, usará un productor de mensajes para enviar mensajes a una
cola cada vez que un ítem se actualiza en la aplicación To Do List.
Resultado
Deberá ser capaz de compilar una aplicación JMS que use un productor JMS para colocar
mensajes en una cola y un bean controlado por mensajes para escuchar en la misma cola y
registrar los mensajes en un archivo especial.
Antes de comenzar
Abra una ventana de terminal en la máquina virtual workstation y ejecute el siguiente
comando para descargar los archivos necesarios para este trabajo de laboratorio.
[student@workstation ~]$ lab messaging-lab setup
1.
Abra JBDS e importe el proyecto messaging-lab ubicado en el directorio /home/
student/JB183/labs/messaging-lab.
2.
Revise el nombre JNDI asignado al objeto administrado, definido para la TodoListQueue
en el archivo de configuración JBoss.
3.
Cree una nueva clase EJB sin estado, nombrada JMSClient, que proporcione un
método público denominado sendMessage(String msg), para enviar un mensaje a la
TodoListQueue mediante un productor de mensajes JMS.
4.
Actualice la EJB JMSClient para inyectar el objeto JMSContext predeterminado, inyecte
también el objeto administrado para la TodoListQueue y, luego, use ese contexto para
crear un JMSProducer para enviar un mensaje a la cola.
4.1. Inyecte el objeto JMSContext predeterminado para acceder a la fábrica de
conexiones predeterminadas.
4.2. Inyecte el objeto administrado TodoListQueue con una anotación @Resource.
4.3. Implemente el método sendMessage(String msg) para colocar un nuevo mensaje
en la cola usando la interfaz JMSProducer, manejando excepciones mediante la
impresión de su seguimiento de pila (stack) en la consola.
4.4. Guarde los cambios usando Ctrl+S.
5.
Actualice el ItemService para inyectar el EJB JMSClient. Agregue una invocación del
método update() en la clase ItemService para usar la instancia JMSClient para
enviar un mensaje JMS cada vez que se actualiza un ítem.
5.1. Abra la clase ItemService; para ello, expanda el ítem messaging-lab en la
pestaña Project Explorer (Explorador de proyectos) en el panel izquierdo de
JBDS y, luego, haga clic en messaging-lab > Java Resources > src/main/java >
com.redhat.training.todo.service para expandirlo. Haga doble clic en el archivo
ItemService.java.
356
JB183-EAP7.0-es-2-20180124
5.2. Importe el JMSClient e inyecte la instancia predeterminada.
5.3. En el método update (actualizar), agregue una invocación del método sendMessage
utilizando el siguiente mensaje: Item with ID:" + item.getId() + " was
updated with status Done="+ item.isDone():
5.4. Guarde los cambios usando Ctrl+S.
6.
Actualice la clase QueueListener para que sea un bean controlado por mensaje que
escucha la TodoListQueue y envía mensajes a un archivo de registro especial mediante
el método writeMessageToFile.
6.1. Abra la clase QueueListener; para ello, expanda el ítem messaging-lab en la
pestaña Project Explorer (Explorador de proyectos) en el panel izquierdo de
JBDS y, luego, haga clic en messaging-lab > Java Resources > src/main/java >
com.redhat.training.todo.messaging para expandirlo. Haga doble clic en el archivo
QueueListener.java.
6.2. Haga que la clase QueueListener implemente la interfaz MessageListener e
implemente el método onMessage() para corregir errores de recopilación:
6.3. Implemente una simple verificación del tipo de mensaje para asegurarse de que
sea una instancia de TextMessage y registre el resultado en el archivo de registro
personalizado, mediante el método writeMessageToFile(String message)
provisto. Asegúrese de usar un bloque try-catch para manejar las JMSExceptions:
6.4. Configure el MDB mediante la anotación @MessageDriven, estableciendo
las instancias @ActivationConfigProperty necesarias para leer de la
TodoListQueue:
6.5. Guarde los cambios usando Ctrl+S.
7.
Inicie JBoss EAP desde dentro de JBDS.
8.
Compile e implemente la aplicación en JBoss EAP con Maven.
9.
Pruebe la aplicación en un explorador.
9.1. Abra Firefox en la máquina virtual workstation y diríjase a http://
localhost:8080/messaging-lab para acceder a la aplicación.
9.2. Agregue al menos dos nuevos ítems pendientes mediante la interfaz de la aplicación
To Do List.
9.3. Después de haber agregado correctamente los nuevos ítems, actualice su estado a
Listos.
9.4. Abra una ventana de terminal en la máquina virtual workstation y ejecute el
siguiente comando para navegar al directorio del proyecto y ver el archivo de
registro ItemStatusLog.txt:
[student@workstation ~]$ cd /home/student/JB183/labs/messaging-lab
[student@workstation messaging-lab]$ cat ItemStatusLog.txt
JB183-EAP7.0-es-2-20180124
357
Capítulo 8. Creación de aplicaciones de mensajería con JMS
Un MDB que funciona adecuadamente imprime un texto similar al siguiente en el
registro:
Item with ID:11 was updated with status Done=true
Item with ID:12 was updated with status Done=true
10. Abra una nueva ventana de terminal y ejecute el siguiente comando para calificar el
trabajo de laboratorio:
[student@workstation ~]$ lab messaging-lab grade
El script de calificación debe indicar SUCCESS (CORRECTO). Si se produce una falla, revise
los errores y corríjalos hasta que vea un mensaje que indique SUCCESS (CORRECTO).
11. Realice la limpieza.
11.1.Anule la implementación de la aplicación en JBoss EAP con Maven con los siguientes
comandos:
[student@workstation ~]$ cd /home/student/JB183/labs/messaging-lab
[student@workstation messaging-lab]$ mvn wildfly:undeploy
11.2.Haga clic con el botón derecho en el proyecto messaging-lab en Project Explorer
(Explorador de proyectos) y seleccione Close Project (Cerrar proyecto) para cerrarlo.
11.3.Haga clic con el botón derecho en el servidor Red Hat JBoss EAP 7.0 en la pestaña
Servers (Servidores) de JBDS y haga clic en Stop (Detener).
Esto concluye el trabajo de laboratorio.
358
JB183-EAP7.0-es-2-20180124
Solución
Solución
En este trabajo de laboratorio, usará un productor de mensajes para enviar mensajes a una
cola cada vez que un ítem se actualiza en la aplicación To Do List.
Resultado
Deberá ser capaz de compilar una aplicación JMS que use un productor JMS para colocar
mensajes en una cola y un bean controlado por mensajes para escuchar en la misma cola y
registrar los mensajes en un archivo especial.
Antes de comenzar
Abra una ventana de terminal en la máquina virtual workstation y ejecute el siguiente
comando para descargar los archivos necesarios para este trabajo de laboratorio.
[student@workstation ~]$ lab messaging-lab setup
1.
Abra JBDS e importe el proyecto messaging-lab ubicado en el directorio /home/
student/JB183/labs/messaging-lab.
1.1. Para abrir JBDS, haga doble clic en el icono de JBoss Developer Studio en el escritorio
de la estación de trabajo. Deje el espacio de trabajo predeterminado (/home/
student/JB183/workspace) y haga clic en OK (Aceptar).
1.2. En el menú de JBDS, haga clic en File (Archivo) > Import (Importar)para abrir el
asistente para Import (Importar).
1.3. En la página Select (Seleccionar), haga clic en Maven > Existing Maven Projects
(Proyectos de Maven existentes) y, luego, haga clic en Next (Siguiente).
1.4. En la página Maven projects (Proyectos de Maven), haga clic en Browse (Explorar)
para abrir la ventana Select root folder (Seleccionar carpeta raíz). Diríjase al
directorio /home/student/JB183/labs/. Seleccione la carpeta messaging-lab y
haga clic en OK (Aceptar).
1.5. En la página Maven projects (Proyectos de Maven), haga clic en Finish (Finalizar).
1.6. Observe la barra de estado de JBDS para monitorear el progreso de la operación
de importación. Es probable que la descarga de todas las dependencias requeridas
demore unos minutos.
2.
Revise el nombre JNDI asignado al objeto administrado, definido para la TodoListQueue
en el archivo de configuración JBoss.
2.1. Usando su editor de texto preferido, abra el archivo de configuración EAP en /opt/
eap/standalone/configuration/standalone-full.xml:
[student@workstation ~]$ less /opt/eap/standalone/configuration/\
standalone-full.xml
2.2. Navegue al subsistema urn:jboss:domain:messaging-activemq:1.0:
<subsystem xmlns="urn:jboss:domain:messaging-activemq:1.0">
<server name="default">
...
JB183-EAP7.0-es-2-20180124
359
Capítulo 8. Creación de aplicaciones de mensajería con JMS
<jms-queue name="ExpiryQueue" entries="java:/jms/queue/ExpiryQueue"/>
<jms-queue name="DLQ" entries="java:/jms/queue/DLQ"/>
<jms-queue name="helloWorldQueue" entries="queue/helloWorldQueue java:jboss/
jms/queue/helloWorldQueue"/>
<jms-queue name="TodoListQueue" entries="queue/TodoListQueue java:jboss/jms/
queue/TodoListQueue"/>
<connection-factory name="InVmConnectionFactory" entries="java:/
ConnectionFactory" connectors="in-vm"/>
<connection-factory name="RemoteConnectionFactory" entries="java:jboss/
exported/jms/RemoteConnectionFactory" connectors="http-connector"/>
<pooled-connection-factory name="activemq-ra" transaction="xa" entries="java:/
JmsXA java:jboss/DefaultJMSConnectionFactory" connectors="in-vm"/>
</server>
</subsystem>
Note que la TodoListQueue tiene la entrada JNDI dejava:jboss/jms/queue/
TodoListQueue.
2.3. Cierre el editor de texto y no guarde los cambios al archivo standalone-full.xml.
Advertencia
Pueden producirse problemas en el trabajo de laboratorio si se introducen
cambios inesperados en el archivo de configuración.
3.
Cree una nueva clase EJB sin estado, nombrada JMSClient, que proporcione un
método público denominado sendMessage(String msg), para enviar un mensaje a la
TodoListQueue mediante un productor de mensajes JMS.
3.1. A continuación, en la pestaña Project Explorer (Explorador de proyectos) del panel
izquierdo de JBDS, haga clic en messaging-lab > Java Resources (Recursos de Java)
> src/main/java > com.redhat.training.todo.messaging, haga clic con el botón
derecho en com.redhat.training.messaging y seleccione New (Nueva) > Class
(Clase).
3.2. En el campo Name (Nombre) ingrese JMSClient. Haga clic en Finish (Finalizar).
3.3. Edite la clase JMSClient recientemente creada y agregue la anotación @Stateless
para marcarla como una EJB disponible para inyección.
import javax.ejb.Stateless;
@Stateless
public class JMSClient {
3.4. Guarde los cambios usando Ctrl+S.
4.
Actualice la EJB JMSClient para inyectar el objeto JMSContext predeterminado, inyecte
también el objeto administrado para la TodoListQueue y, luego, use ese contexto para
crear un JMSProducer para enviar un mensaje a la cola.
4.1. Inyecte el objeto JMSContext predeterminado para acceder a la fábrica de
conexiones predeterminadas.
360
JB183-EAP7.0-es-2-20180124
Solución
import javax.ejb.Stateless;
import javax.inject.Inject;
import javax.jms.JMSContext;
@Stateless
public class JMSClient {
@Inject
JMSContext context;
4.2. Inyecte el objeto administrado TodoListQueue con una anotación @Resource.
import
import
import
import
import
javax.ejb.Stateless;
javax.inject.Inject;
javax.jms.JMSContext;
javax.jms.Queue;
javax.annotation.Resource;
@Stateless
public class JMSClient {
@Inject
JMSContext context;
@Resource(mappedName = "java:jboss/jms/queue/TodoListQueue")
private Queue todoListQueue;
4.3. Implemente el método sendMessage(String msg) para colocar un nuevo mensaje
en la cola usando la interfaz JMSProducer, manejando excepciones mediante la
impresión de su seguimiento de pila (stack) en la consola.
import
import
import
import
import
import
import
javax.ejb.Stateless;
javax.inject.Inject;
javax.jms.JMSContext;
javax.jms.Queue;
javax.annotation.Resource;
javax.jms.JMSProducer;
javax.jms.TextMessage;
@Stateless
public class JMSClient {
@Inject
JMSContext context;
@Resource(mappedName = "java:jboss/jms/queue/TodoListQueue")
private Queue todoListQueue;
public void sendMessage(String msg) {
try {
JMSProducer producer = context.createProducer();
TextMessage message = context.createTextMessage(msg);
producer.send(todoListQueue, message);
System.out.println("Sent Message: "+ msg);
}
catch (Exception e) {
e.printStackTrace();
}
}
JB183-EAP7.0-es-2-20180124
361
Capítulo 8. Creación de aplicaciones de mensajería con JMS
...
4.4. Guarde los cambios usando Ctrl+S.
5.
Actualice el ItemService para inyectar el EJB JMSClient. Agregue una invocación del
método update() en la clase ItemService para usar la instancia JMSClient para
enviar un mensaje JMS cada vez que se actualiza un ítem.
5.1. Abra la clase ItemService; para ello, expanda el ítem messaging-lab en la
pestaña Project Explorer (Explorador de proyectos) en el panel izquierdo de
JBDS y, luego, haga clic en messaging-lab > Java Resources > src/main/java >
com.redhat.training.todo.service para expandirlo. Haga doble clic en el archivo
ItemService.java.
5.2. Importe el JMSClient e inyecte la instancia predeterminada.
...
import com.redhat.training.todo.messaging.JMSClient;
@Stateless
public class ItemService {
@Inject
private JMSClient jmsClient;
5.3. En el método update (actualizar), agregue una invocación del método sendMessage
utilizando el siguiente mensaje: Item with ID:" + item.getId() + " was
updated with status Done="+ item.isDone():
public void update(Item item) {
jmsClient.sendMessage("Item with ID:" + item.getId() + " was updated with
status Done="+ item.isDone());
em.merge(item);
}
5.4. Guarde los cambios usando Ctrl+S.
6.
Actualice la clase QueueListener para que sea un bean controlado por mensaje que
escucha la TodoListQueue y envía mensajes a un archivo de registro especial mediante
el método writeMessageToFile.
6.1. Abra la clase QueueListener; para ello, expanda el ítem messaging-lab en la
pestaña Project Explorer (Explorador de proyectos) en el panel izquierdo de
JBDS y, luego, haga clic en messaging-lab > Java Resources > src/main/java >
com.redhat.training.todo.messaging para expandirlo. Haga doble clic en el archivo
QueueListener.java.
6.2. Haga que la clase QueueListener implemente la interfaz MessageListener e
implemente el método onMessage() para corregir errores de recopilación:
package com.redhat.training.messaging;
import javax.jms.MessageListener;
362
JB183-EAP7.0-es-2-20180124
Solución
public class QueueListener implements MessageListener{
@Override
public void onMessage(Message rcvMessage) {
}
}
6.3. Implemente una simple verificación del tipo de mensaje para asegurarse de que
sea una instancia de TextMessage y registre el resultado en el archivo de registro
personalizado, mediante el método writeMessageToFile(String message)
provisto. Asegúrese de usar un bloque try-catch para manejar las JMSExceptions:
import
import
import
import
javax.jms.JMSException;
javax.jms.Message;
javax.jms.MessageListener;
javax.jms.TextMessage;
public class QueueListener implements MessageListener{
public void onMessage(Message rcvMessage) {
TextMessage msg = null;
try {
if (rcvMessage instanceof TextMessage) {
msg = (TextMessage) rcvMessage;
System.out.println("Received Message from TodoListQueue ===> " +
msg.getText());
writeMessageToFile(msg.getText());
} else {
System.out.println("Message of wrong type: " +
rcvMessage.getClass().getName());
}
} catch (JMSException e) {
e.printStackTrace();
}
}
}
6.4. Configure el MDB mediante la anotación @MessageDriven, estableciendo
las instancias @ActivationConfigProperty necesarias para leer de la
TodoListQueue:
import javax.ejb.ActivationConfigProperty;
import javax.ejb.MessageDriven;
@MessageDriven(name = "QueueListener", activationConfig = {
@ActivationConfigProperty(propertyName = "destinationLookup", propertyValue
= "queue/TodoListQueue"),
@ActivationConfigProperty(propertyName = "destinationType", propertyValue =
"javax.jms.Queue")
})
public class QueueListener implements MessageListener {
...
6.5. Guarde los cambios usando Ctrl+S.
7.
Inicie JBoss EAP desde dentro de JBDS.
JB183-EAP7.0-es-2-20180124
363
Capítulo 8. Creación de aplicaciones de mensajería con JMS
Seleccione la pestaña Servers (Servidores) en JBDS. Haga clic con el botón derecho en la
entrada del servidor Red Hat JBoss EAP 7.0 [Stopped] y haga clic en la opción verde Start
(Iniciar) para iniciar el servidor. Observe la pestaña Console (Consola) de JBDS hasta que
el servidor se inicie y vea el siguiente mensaje:
INFO [org.jboss.as] (Controller Boot Thread) WFLYSRV0025: JBoss EAP 7.0.2.GA
(WildFly Core 2.1.8.Final-redhat-1) started
8.
Compile e implemente la aplicación en JBoss EAP con Maven.
Abra un nuevo terminal y cambie el directorio por la carpeta /home/student/JB183/
labs/messaging-lab.
[student@workstation ~]$ cd /home/student/JB183/labs/messaging-lab
Compile e implemente el EJB en JBoss EAP al ejecutar el siguiente comando:
[student@workstation messaging-lab]$ mvn clean wildfly:deploy
Si la compilación es correcta, se mostrará la siguiente salida:
[student@workstation messaging-lab]$ mvn clean package wildfly:deploy
...
[INFO] [INFO] <<< wildfly-maven-plugin:1.0.2.Final:deploy (default-cli) < package @
messaging-lab <<<
...
[INFO] --- maven-war-plugin:2.1.1:war (default-war) @ messaging-lab --...
[INFO] --- wildfly-maven-plugin:1.0.2.Final:deploy (default-cli) @ messaging-lab
[INFO] -----------------------------------------------------------------------[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
9.
Pruebe la aplicación en un explorador.
9.1. Abra Firefox en la máquina virtual workstation y diríjase a http://
localhost:8080/messaging-lab para acceder a la aplicación.
9.2. Agregue al menos dos nuevos ítems pendientes mediante la interfaz de la aplicación
To Do List.
9.3. Después de haber agregado correctamente los nuevos ítems, actualice su estado a
Listos.
9.4. Abra una ventana de terminal en la máquina virtual workstation y ejecute el
siguiente comando para navegar al directorio del proyecto y ver el archivo de
registro ItemStatusLog.txt:
[student@workstation ~]$ cd /home/student/JB183/labs/messaging-lab
[student@workstation messaging-lab]$ cat ItemStatusLog.txt
364
JB183-EAP7.0-es-2-20180124
Solución
Un MDB que funciona adecuadamente imprime un texto similar al siguiente en el
registro:
Item with ID:11 was updated with status Done=true
Item with ID:12 was updated with status Done=true
10. Abra una nueva ventana de terminal y ejecute el siguiente comando para calificar el
trabajo de laboratorio:
[student@workstation ~]$ lab messaging-lab grade
El script de calificación debe indicar SUCCESS (CORRECTO). Si se produce una falla, revise
los errores y corríjalos hasta que vea un mensaje que indique SUCCESS (CORRECTO).
11. Realice la limpieza.
11.1.Anule la implementación de la aplicación en JBoss EAP con Maven con los siguientes
comandos:
[student@workstation ~]$ cd /home/student/JB183/labs/messaging-lab
[student@workstation messaging-lab]$ mvn wildfly:undeploy
11.2.Haga clic con el botón derecho en el proyecto messaging-lab en Project Explorer
(Explorador de proyectos) y seleccione Close Project (Cerrar proyecto) para cerrarlo.
11.3.Haga clic con el botón derecho en el servidor Red Hat JBoss EAP 7.0 en la pestaña
Servers (Servidores) de JBDS y haga clic en Stop (Detener).
Esto concluye el trabajo de laboratorio.
JB183-EAP7.0-es-2-20180124
365
Capítulo 8. Creación de aplicaciones de mensajería con JMS
Resumen
En este capítulo, aprendió lo siguiente:
• Al activar el procesamiento asíncrono entre sistemas acoplados de forma flexible, la
mensajería permite que los desarrolladores compilen aplicaciones que sean más eficientes,
más fácilmente escaladas y más confiables.
• Point-to-point messaging (mensajería punto a punto) se produce cuando dos aplicaciones
están conectadas mediante una cola, y cada mensaje que envía un productor es recibido
por un solo consumidor.
• La mensajería de Publicación/Suscripción se produce cuando varias aplicaciones se
suscriben a un tema y todo mensaje enviado a ese tema se duplica y se copia a cada uno
de los suscriptores.
• Una práctica común al trabajar con mensajería en un servidor de aplicaciones como
JBoss EAP 7 es mantener los destinos JMS y las fábricas de conexión de manera
administrativa en la configuración del servidor, en lugar de en el propio código de
aplicaciones.
• Existen tres partes para una estructura básica de un mensaje JMS: encabezados,
propiedades y un cuerpo.
• La anotación @Resource inyecta directamente un objeto de destino, como una Queue
(Cola) o Topic (Tema), así como un objeto ConnectionFactory que usa solo el
nombre JNDI definido en la configuración del servidor.
• Un productor de mensajes es un tipo de objeto JMS creado por el objeto JMSContext que
envía mensajes JMS de un destino.
• Un consumidor de mensajes es un tipo de objeto JMS creado por el objeto JMSContext
que recibe mensajes JMS de un destino de manera asíncrona, a través de una invocación de
método.
• Los beans controlados por mensajes (MDB) permiten un consumo asíncrono de mensajes
de un destino JMS.
366
JB183-EAP7.0-es-2-20180124
TRAINING
CAPÍTULO 9
PROTECCIÓN DE APLICACIONES
JAVA EE
Descripción general
Meta
Proteger una aplicación de Java EE con JAAS.
Objetivos
• Describir la especificación de JAAS.
• Configurar un dominio de seguridad en el servidor de
aplicaciones de JBoss EAP.
• Proteger una API REST con autenticación y autorización
basada en roles.
Secciones
• Descripción de la especificación de JAAS (y cuestionario)
• Configuración de un dominio de seguridad en JBoss EAP
(y ejercicio guiado)
• Protección de una API REST (y ejercicio guiado)
Trabajo de laboratorio
JB183-EAP7.0-es-2-20180124
Protección de aplicaciones Java EE
367
Capítulo 9. Protección de aplicaciones Java EE
Descripción de la especificación de JAAS
Objetivo
Tras finalizar esta sección, los estudiantes deberán ser capaces de describir la especificación
JAAS.
Seguridad Java EE
Al compilar aplicaciones empresariales que acceden a información confidencial, la seguridad
debe ser una prioridad para los desarrolladores. Los desarrolladores deben tener una
comprensión clara de quién puede acceder a la aplicación y qué partes de esta aplicación
están autorizados esos usuarios a utilizar. La definición de qué usuarios tienen acceso a
una aplicación se conoce como authentication (autenticación), mientras que los permisos
dentro de la aplicación para esos usuarios se conoce como authorization (autorización). Al
definir las restricciones de acceso a diferentes componentes de aplicaciones, los usuarios
están limitados únicamente a la cantidad mínima de acceso que requiere cada usuario. Para
personalizar la autorización en una aplicación, se aplican restricciones a un user (usuario),
que representa un individuo, o a un role (rol) que se refiere a un grupo definido de usuarios.
Por ejemplo, considere una aplicación web de tienda de libros en la que los clientes compran
libros en línea y el propietario gestiona el inventario. El usuario shadowman es un cliente
que accede al sitio y tiene el rol customer (cliente). El administrador del sitio con el nombre
de usuario redhat tiene el rol admin. El servidor autentica a los usuarios shadowman y
redhat para garantizar que cada usuario coincida con su contraseña. Una vez autenticados,
los métodos EJB se anotan para restringir el acceso a roles de usuario individual. Debido a
que los clientes no tienen permitido gestionar el inventario de la tienda, los usuarios con
el rol customer no pueden invocar métodos que gestionen el inventario, mientras que los
usuarios con el rol admin pueden realizar cambios de inventario.
Figura 9.1: Proteja la autorización EJB
El Servicio de Autenticación y Autorización de Java (JAAS) es una API de seguridad que se
usa para implementar aplicaciones de autenticación y autorización en Java (JSR-196). La
API amplía la arquitectura de control de acceso de Java Enterprise Edition para admitir la
autorización basada en usuarios. Java EE aplica la API como mecanismo para permitir y
garantizar el acceso. JAAS proporciona seguridad declarativa basada en roles en la JBoss
Enterprise Application Platform. La Declarative security (Seguridad declarativa) separa las
preocupaciones en materia de seguridad del código de la aplicación utilizando el contenedor
para gestionar la seguridad. El contenedor proporciona un sistema de autorización basado en
368
JB183-EAP7.0-es-2-20180124
Seguridad declarativa
anotaciones y descriptores XML dentro del código de aplicaciones que protege los recursos.
Este enfoque se encuentra en contraste con la seguridad programática, que requiere que cada
aplicación contenga código que gestione la seguridad.
Seguridad declarativa
El uso de la seguridad declarativa requiere de los desarrolladores y administradores
que aprovechen las anotaciones y los descriptores de implementación para definir el
comportamiento de seguridad de una aplicación. Por ejemplo, un EJB puede restringir los
aspectos de la aplicación que se basan en el rol de un usuario que solo utiliza anotaciones.
No requiere que una aplicación gestione el contexto de seguridad. Para gestionar aspectos de
seguridad, como la administración de la autenticación y la autorización, necesita descriptores
de implementación, responsables de dictar la forma en que un servidor de aplicaciones
implementa la aplicación y cómo el servidor protege la aplicación. Esto se establece en el
web.xml de la aplicación o al desarrollar con Red Hat JBoss EAP en jboss-web.xml.
Los desarrolladores usan el archivo web.xml para definir qué recursos se deben proteger en
una aplicación, cómo se los debe proteger y qué datos se usan para validar las credenciales.
El siguiente es un fragmento del web.xml que define la autenticación BASIC:
<security-constraint>
<web-resource-collection>
<web-resource-name>All resources</web-resource-name>
<url-pattern>/*</url-pattern>
</web-resource-collection>
<auth-constraint>
<role-name>*</role-name>
</auth-constraint>
</security-constraint>
<login-config>
<auth-method>BASIC</auth-method>
<realm-name>basicRealm</realm-name>
</login-config>
Los recursos a los que se aplica la restricción de seguridad. La "/*" indica que todos los
recursos están protegidos.
Los roles autorizados a acceder a los recursos. En esta instancia, todos los roles pueden
acceder a la aplicación.
El método de aplicación utilizado para acceder a las credenciales de un usuario. En una
ventana emergente, BASIC avisa al usuario ni bien se accede a la aplicación.
El nombre del dominio que almacena la información de credencial del usuario. Este
tema se analizará en más detalle en la siguiente sección.
El archivo jboss-web.xml agrega descripciones específicas de JBoss adicionales, como la
forma en que el servidor lleva a cabo la autenticación y la autorización para la aplicación. En
muchos casos, este archivo se usa para definir un dominio de seguridad, que es un conjunto
de configuraciones de seguridad declarativa de JAAS. Este archivo y otras configuraciones de
seguridad específicas de JBoss se analizarán en la siguiente sección.
El uso de los descriptores de implementación para definir aspectos de seguridad puede ser
útil, aunque también son muy restrictivos, en especial en cualquier aplicación que cuente
con más que los requisitos de seguridad más básicos. Las anotaciones que se colocan
directamente en el código de aplicaciones EJB proporcionan un enfoque más flexible y
personalizable a la seguridad. Este enfoque es útil para los métodos de protección de API
JB183-EAP7.0-es-2-20180124
369
Capítulo 9. Protección de aplicaciones Java EE
REST o para limitar ciertos roles al uso exclusivo de ciertas invocaciones de métodos dentro
de una aplicación. Anotaciones que se pueden usar para proteger un EJB:
• @SecurityDomain: ubicada al comienzo de la clase, esta anotación define el dominio de
seguridad por nombre para usar para el EJB.
• @DeclareRoles: ubicada al comienzo de la clase, esta anotación define los roles probados
para permisos en la clase. Si no se utiliza esta anotación, los roles se verifican en función de
la presencia de las anotaciones @RolesAllowed.
• @RolesAllowed: ubicada al comienzo de la clase o antes del encabezado del método, esta
anotación define una lista de uno o más roles que tienen permiso de acceso a un método.
Si se los coloca antes del encabezado de la clase, los métodos en la clase sin una anotación
adoptan esta anotación de manera predeterminada.
• @PermitAll: ubicada al comienzo de la clase o antes del encabezado del método, esta
anotación especifica que todos los roles tienen permiso de acceso a un método.
• @DenyAll: ubicada al comienzo de la clase o antes del encabezado del método, esta
anotación especifica que ningún rol tiene permiso de acceso a un método.
• @RunAs: ubicada al comienzo de la clase o antes del encabezado del método, esta
anotación especifica el rol usado al ejecutar un método. Esta anotación es útil cuando un
EJB invoca otro EJB y debe adoptar un nuevo rol por restricciones de seguridad en otro EJB.
El siguiente es un ejemplo de una clase EJB que usa el dominio de seguridad other para
restringir la autorización de acceso a sus métodos:
@Stateless
@RolesAllowed({"admin, qa"})
@SecurityDomain("other")
public class HelloWorldEJB implements HW {
@PermitAll
public String HelloWorld(String msg) {
return "Hello " + msg;
}
@RolesAllowed("admin")
public String GoodbyeAdmin(String msg) {
return "See you later, " + msg;
}
public String GoodbyeSecure(String msg) {
return "Adios, " + msg;
}
}
De manera predeterminada, la clase HelloWorldEJB pasa a restringir todos sus
métodos a solo estar disponibles a los usuarios admin y qa.
La clase está aprovechando el other dominio de seguridad. En este caso, este dominio
de seguridad utiliza los archivos de propiedad que almacenan la información de rol.
El método HelloWorld está disponible para todos los roles, no solo admin y qa.
El método GoodbyeAdmin está disponible solo a los usuarios autenticados, como el
admin.
370
JB183-EAP7.0-es-2-20180124
Seguridad Programática
El método GoodbyeSecure está disponible solo para admin y qa, ya que el método se
establece de manera predeterminada en RolesAllowed, definido a nivel de clase.
Seguridad Programática
En algunos casos, la seguridad declarativa no es suficiente para cumplir con todos los
requisitos de seguridad para una aplicación. En estas instancias, los desarrolladores pueden
preferir usar seguridad programática para tener más control sobre las decisiones de
autenticación y autorización dentro de la aplicación. El objeto EJBContext cuenta con
información acerca del contexto de seguridad actual y proporciona los siguientes dos
métodos útiles para acceder a la información acerca del usuario autenticado que se puede
usar para realizar decisiones de autorización:
• isCallerInRole(String role): devuelve un boolean que indica si un usuario
pertenece a un rol específico.
• getCallerPrincipal(): devuelve el usuario actualmente autenticado.
En el siguiente ejemplo se demuestra el uso de EJBContext para identificar un usuario y el
rol del usuario:
@Stateless
public class HelloWorldEJB implements HW {
@Resource
EJBContext context;
public String HelloWorld() {
if (context.isCallerInRole("admin")) {
return "Hello " + context.getCallerPrincipal().getName();
} else {
return "Unauthorized user.";
}
}
}
En este ejemplo, el método HelloWorld() usa el EJBContext para ver si el usuario que
invoca el método pertenece al rol admin. Si el usuario pertenece a este rol, se devuelve una
respuesta con el nombre de usuario autenticado.
Además de utilizar el EJBContext, la interfaz HttpServletRequest proporciona métodos
para gestionar en forma programática la autenticación de usuarios. Los siguientes métodos
están disponibles para autenticar usuarios con la interfaz HttpServletRequest:
• authenticate(HttpServletResponse): solicita al usuario que presente credenciales
para la autenticación.
• login(String username, String password): inicie sesión con el usuario ingresando
el username (nombre de usuario) y la password (contraseña).
• logout(): cierre la sesión del usuario autenticado actual.
JB183-EAP7.0-es-2-20180124
371
Capítulo 9. Protección de aplicaciones Java EE
Referencias
Interfaz de proveedor de servicio de autenticación de Java JSR-196 para
contenedores
https://www.jcp.org/en/jsr/detail?id=196
Referencias
Para obtener más información, consulte el capítulo Seguridad de la Guía de
desarrollo para Red Hat JBoss EAP 7 en
https://access.redhat.com/documentation/en/red-hat-jboss-enterpriseapplication-platform/
372
JB183-EAP7.0-es-2-20180124
Cuestionario: Descripción de la especificación de JAAS
Cuestionario: Descripción de la
especificación de JAAS
Una los siguientes ítems con sus equivalentes en la tabla.
Define el tipo de <login-config>, como BASIC.
Especifica la configuración de seguridad que se debe usar en la aplicación.
La anotación que define el rol que ejecuta un método.
La anotación que define los roles que pueden acceder a un método.
La anotación que define para qué roles verificar permisos.
Un conjunto de configuraciones de seguridad declarativas de JAAS en EAP.
Verificación de que un usuario es quien dice ser.
Verificación de que un usuario tiene el privilegio de realizar una acción específica.
Término
Definición
Dominio de seguridad
Autenticación
Autorización
web.xml
JB183-EAP7.0-es-2-20180124
373
Capítulo 9. Protección de aplicaciones Java EE
Término
Definición
jboss-web.xml
@DeclareRoles
@RolesAllowed
@RunAs
374
JB183-EAP7.0-es-2-20180124
Solución
Solución
Una los siguientes ítems con sus equivalentes en la tabla.
Término
Definición
Dominio de seguridad
Un conjunto de configuraciones de seguridad
declarativas de JAAS en EAP.
Autenticación
Verificación de que un usuario es quien dice
ser.
Autorización
Verificación de que un usuario tiene el privilegio
de realizar una acción específica.
web.xml
Define el tipo de <login-config>, como
BASIC.
jboss-web.xml
Especifica la configuración de seguridad que se
debe usar en la aplicación.
@DeclareRoles
La anotación que define para qué roles verificar
permisos.
@RolesAllowed
La anotación que define los roles que pueden
acceder a un método.
@RunAs
La anotación que define el rol que ejecuta un
método.
JB183-EAP7.0-es-2-20180124
375
Capítulo 9. Protección de aplicaciones Java EE
Configuración de un dominio de seguridad
en JBoss EAP
Objetivo
Después de completar esta sección, los estudiantes deberán ser capaces de configurar un
dominio de seguridad en EAP.
Dominios de seguridad en EAP
El uso de un servidor de aplicaciones como Red Hat JBoss EAP simplifica la configuración e
implementación de la seguridad para desarrolladores y administradores de aplicaciones. EAP
y otros servidores de aplicaciones proporcionan utilidades y configuraciones predefinidas que
ayudan a gestionar la autenticación y autorización. EAP gestiona la información de seguridad
del usuario en security realms (dominios de seguridad). De manera predeterminada, EAP
define ApplicationRealm, que usa los siguientes archivos para almacenar los usuarios y
sus roles, respectivamente:
• application-users.properties
• application-roles.properties
Aunque se pueden agregar dominios de seguridad a EAP, este curso usa principalmente
el ApplicationRealm para autenticación y autorización. La siguiente es la configuración
predeterminada para el ApplicationRealm:
<security-realm name="ApplicationRealm">
<authentication>
<local default-user="$local" allowed-users="*" skip-grouploading="true"/>
<properties path="application-users.properties" relativeto="jboss.server.config.dir"/>
</authentication>
<authorization>
<properties path="application-roles.properties" relativeto="jboss.server.config.dir"/>
</authorization>
</security-realm>
Observe que los dominios tienen una etiqueta cada uno para <authentication> y
<authorization>. La etiqueta <authentication> define la ruta al archivo de propiedades
del usuario. En este caso, ese archivo es application-users.properties en el directorio
de configuración del servidor EAP. Este archivo almacena nombres de usuario y contraseñas
como par de claves/valores, por ejemplo:
<username>=<password>
nota
El script EAP_HOME/bin/add-user.sh es un script de utilidad que actualiza el
archivo application-users.properties predeterminado con nuevos usuarios.
376
JB183-EAP7.0-es-2-20180124
Módulos de inicio de sesión
La etiqueta <authorization> define la ruta al archivo de propiedades del usuario. En este
caso, ese archivo es application-roles.properties, que se ubica en el directorio de
configuración del servidor EAP. Este archivo almacena usuarios y roles como pares de claves/
valores mediante la siguiente sintaxis:
<role>=<user1>,<user2>...
Módulos de inicio de sesión
Si bien los dominios de seguridad en EAP se usan para configurar las credenciales del
usuario, un security realm (dominio de seguridad) define un conjunto de configuraciones
de seguridad declarativas JAAS. La mayor ventaja de usar un enfoque declarativo para la
seguridad es que separa muchos de los detalles de seguridad de la propia aplicación. Esto
proporciona flexibilidad adicional y mantiene el código de lógica de negocio de la aplicación
con acceso de lectura y más sencillo de mantener al eliminar los detalles de implementación
para la tecnología de seguridad que utiliza el servidor de aplicaciones.
EAP incluye varios módulos de inicio de sesión incorporados, que los desarrolladores pueden
utilizar para la autenticación dentro de un dominio de seguridad. Estos módulos de inicio de
sesión incluyen la capacidad de leer información del usuario de una base de datos relacional,
un servidor LDAP o archivos planos. También es posible compilar un módulo personalizado,
dependiendo de los requisitos de seguridad de la aplicación.
El método para la autenticación del usuario se define en el dominio de seguridad. De
manera predeterminada, EAP define el other (otro) dominio de seguridad con la siguiente
configuración:
<security-domain name="other" cache-type="default">
<authentication>
...
<login-module code="RealmDirect" flag="required">
<module-option name="password-stacking" value="useFirstPass"/>
</login-module>
</authentication>
</security-domain>
El módulo de inicio de sesión RealmDirect usa un dominio de aplicaciones al realizar las
decisiones de autenticación y autorización. Si no se especificó un dominio, el módulo usa el
ApplicationRealm, y, por lo tanto, los archivos de propiedad de usuarios y roles para la
autenticación y autorización.
Para activar el other dominio de seguridad en la aplicación, agregue la siguiente etiqueta al
src/main/webapp/WEB-INF/jboss-web.xml de la aplicación:
<security-domain>other</security-domain>
Por último, para completar la autenticación, configure el archivo WEB-INF/web.xml
de la aplicación para definir el <login-config>. En el siguiente ejemplo se define el
<login-config> para usar la autenticación BASIC con el ApplicationRealm. Con
la implementación de esta configuración, a los usuarios se les solicita credenciales al
acceder a un recurso de servidor y el servidor verifica las credenciales en relación al
ApplicationRealm.
<login-config>
JB183-EAP7.0-es-2-20180124
377
Capítulo 9. Protección de aplicaciones Java EE
<auth-method>BASIC</auth-method>
<realm-name>ApplicationRealm</realm-name>
</login-config>
</web-app>
Módulo de inicio de sesión UsersRoles
El módulo de inicio de sesión UsersRoles es un módulo simple, que sirve para probar
algunas de las funcionalidades de seguridad básicas de una aplicación. El módulo
proporciona una forma de que los desarrolladores autentiquen a usuarios rápidamente
y verifiquen que las restricciones de autorización estén correctamente configuradas.
De manera similar a depender del ApplicationRealm como lo hace el other dominio
de seguridad, el módulo UsersRoles usa archivos de propiedades para almacenar las
credenciales de usuario y los datos de roles.
El siguiente es un ejemplo del módulo de inicio de sesión de UsersRoles:
<security-domain name="loginTest"
<authentication>
...
cache-type="default">
<login-module code="UsersRoles"
flag="required"
>
<module-option name="usersProperties" value="users.properties"
<module-option name="rolesProperties" value="roles.properties"
</login-module>
</authentication>
</security-domain>
/>
/>
El nombre del dominio de seguridad. Se hace referencia a este nombre en el archivo
jboss-web.xml.
El código para definir qué módulo de inicio de sesión se está usando. En este caso, se
está configurando el módulo de inicio de sesión UsersRoles.
El indicador para definir el comportamiento del módulo de inicio de sesión. required
(requerido) indica que el módulo es una autenticación requerida para iniciar sesión
correctamente.
La propiedad para definir el nombre del archivo que almacena todos los usuarios y
contraseñas como un par de claves/valores.
La propiedad para definir el nombre del archivo que almacena todos los roles de
usuarios como un par de claves/valores.
Módulo de inicio de sesión de la base de datos
En un entorno de producción, es muy poco común ver credenciales de usuario e información
de roles almacenada en archivos de propiedades almacenados a nivel local. Estos módulos y
técnicas se utilizan principalmente con motivos de prueba. Una de las soluciones que es más
práctica que los archivos de propiedad locales para gestionar las credenciales de usuarios es
almacenar la información en una base de datos. Existen muchos beneficios de utilizar una
base de datos en lugar de un archivo para almacenar la información del usuario. Las bases
de datos se pueden compartir fácilmente en varios servidores de aplicaciones; incluyen una
seguridad de datos sólida y soluciones de copia de seguridad, y son eficientes con un gran
conjunto de datos. Si una aplicación utiliza el módulo de inicio de sesión Database (Base de
datos), los usuarios de la aplicación se almacenan en una base de datos junto con los roles
con los que están relacionados los usuarios.
378
JB183-EAP7.0-es-2-20180124
Módulo de inicio de sesión de la base de datos
El siguiente es un ejemplo del módulo de inicio de sesión de Database:
<security-domain name="db" cache-type="default">
<authentication>
...
<login-module code="Database"
flag="required">
<module-option name="dsJndiName" value="java:/DataSource" />
<module-option name="principalsQuery" value="select password from Users
where username=?" />
<module-option name="rolesQuery" value="select role, 'Roles' from UserRoles where
username=?" />
</login-module>
</authentication>
</security-domain>
El código para definir qué módulo de inicio de sesión se usa. En este caso, se está
configurando el módulo de inicio de sesión Database.
La propiedad para definir el nombre JNDI utilizado para acceder a la fuente de datos.
Observe que la fuente de datos ya está configurada.
La propiedad para definir la consulta usada para obtener la contraseña para un usuario
dado. Esta consulta depende de cómo está configurada la base de datos.
La propiedad para definir la consulta usada para obtener el rol para un usuario dado.
Esta consulta depende de cómo está configurada la base de datos.
Referencias
Para obtener más información, consulte la Guía de referencia del módulo de inicio de
sesión para Red Hat JBoss EAP 7 en
https://access.redhat.com/documentation/en/red-hat-jboss-enterpriseapplication-platform/
Referencias
Para obtener más información, consulte el capítulo Seguridad de la Guía de
desarrollo para Red Hat JBoss EAP 7 en
https://access.redhat.com/documentation/en/red-hat-jboss-enterpriseapplication-platform/
JB183-EAP7.0-es-2-20180124
379
Capítulo 9. Protección de aplicaciones Java EE
Ejercicio guiado: Configuración de un
dominio de seguridad en JBoss EAP
En este ejercicio, protegerá una aplicación JEE creando un dominio de seguridad.
Resultado
Deberá ser capaz de usar un dominio de seguridad en EAP para garantizar la autenticación y
la autorización en una aplicación JEE.
Antes de comenzar
Abra una ventana de terminal en workstation y ejecute el siguiente comando para
descargar los archivos necesarios para este trabajo de laboratorio.
[student@workstation ~]$ lab security-domain setup
Pasos
1. Abra JBDS e importe el proyecto de Maven.
1.1. Haga doble clic en el icono de JBoss Developer Studio en el escritorio de la estación
de trabajo para abrir JBDS. Seleccione el espacio de trabajo /home/student/
JB183/workspace y haga clic en OK (Aceptar).
1.2. En el menú de JBDS, haga clic en File (Archivo) > Import (Importar)para abrir el
asistente para Import (Importar).
1.3. En la página Select (Seleccionar), haga clic en Maven > Existing Maven Projects
(Proyectos de Maven existentes) y, luego, haga clic en Next (Siguiente).
1.4. En la página Maven projects (Proyectos de Maven), haga clic en Browse (Explorar)
para abrir la ventana Select root folder (Seleccionar carpeta raíz). Diríjase al
directorio /home/student/JB183/labs/. Seleccione la carpeta security-domain
y haga clic en OK (Aceptar).
1.5. En la página Maven projects (Proyectos de Maven), haga clic en Finish (Finalizar).
1.6. Observe la barra de estado de JBDS para monitorear el progreso de la operación
de importación. Es probable que la descarga de todas las dependencias requeridas
demore unos minutos.
2.
Seleccione la pestaña Servers (Servidores) en el panel inferior de JBDS para iniciar EAP.
Haga clic con el botón derecho en el servidor Red Hat JBoss EAP 7.0 [Stopped] y haga
clic en el botón verde para iniciar el servidor. Observe la Console (Consola) hasta que el
servidor se inicie y muestre el siguiente mensaje:
INFO [org.jboss.as] (Controller Boot Thread) WFLYSRV0025: JBoss EAP 7.0.0.GA
(WildFly Core 2.1.2.Final-redhat-1) started
3.
380
Cree el dominio de seguridad UsersRoles en JBoss EAP y verifique que use los archivos
hello-users.properties y hello-roles.properties para autenticación y
autorización.
JB183-EAP7.0-es-2-20180124
3.1. En la ventana del terminal, ejecute el siguiente script para crear el dominio de
seguridad en el servidor EAP:
[student@workstation ~]$ cd JB183/labs/security-domain
[student@workstation security-domain]$ ./create-sd.sh
3.2. Mediante un editor de texto, abra el archivo de configuración /opt/jbosseap-7.0/standalone/configuration/standalone-full.xml y observe el
nuevo dominio de seguridad que controla al servidor de aplicaciones para utilizar los
archivos de propiedad de roles y usuarios del directorio de proyectos.
<security-domain name="userroles" cache-type="default">
<authentication>
<login-module code="UsersRoles" flag="required">
<module-option name=usersProperties" value="file:///home/student/JB183/labs/
securty-domain/hello-users.properties"/>
<module-option name="rolesProperties" value="file:///home/student/JB183/labs/
security-domain/hello-users.properties"/>
</login-module>
</authentication>
</security-domain>
4.
Actualice el archivo jboss-web.xml para utilizar el nuevo dominio de seguridad.
4.1. Abra el archivo jboss-web.xml; para ello, expanda el ítem security-domain en la
pestaña Project Explorer (Explorador de proyectos) del panel izquierdo de JBDS y,
luego, haga clic en security-domain > src/main/webapp > WEB-INF y expándalo.
Haga doble clic en el archivo jboss-web.xml.
4.2. Actualice el archivo jboss-web.xml para utilizar el nuevo dominio de seguridad
nombrado userroles:
<?xml version="1.0" encoding="ISO-8859-1"?>
<jboss-web>
<security-domain>userroles</security-domain>
</jboss-web>
4.3. Presione Ctrl+S para guardar sus cambios.
5.
Actualice el archivo web.xml para usar la autenticación BASIC y restringir el acceso al
admin.jsf de la aplicación.
5.1. Abra el archivo web.xml; para ello, expanda el ítem security-domain en la pestaña
Project Explorer (Explorador de proyectos) del panel izquierdo de JBDS y, luego, haga
clic en security-domain > src/main/webapp > WEB-INF y expándalo. Haga doble
clic en el archivo web.xml.
5.2. La primera restricción de seguridad se refiere al index.html. Esta es la página
principal de la aplicación web. Agregue la siguiente <auth-constraint> a
la restricción de seguridad index.html para limitar el acceso a este recurso
únicamente a usuarios con el rol guest y admin.
JB183-EAP7.0-es-2-20180124
381
Capítulo 9. Protección de aplicaciones Java EE
<security-constraint>
<web-resource-collection>
<web-resource-name>Index</web-resource-name>
<url-pattern>/index.html</url-pattern>
</web-resource-collection>
<!-- Add auth constraint here -->
<auth-constraint>
<role-name>guest</role-name>
<role-name>admin</role-name>
</auth-constraint>
</security-constraint>
5.3. Actualice la segunda restricción de seguridad para limitar el acceso a la página
admin.jsf únicamente a usuarios con el rol admin. Agregue una nueva authconstraint y actualice el url-pattern.
<security-constraint>
<web-resource-collection>
<web-resource-name>Secure resources</web-resource-name>
<!-- Update url pattern -->
<url-pattern>/admin.jsf</url-pattern>
</web-resource-collection>
<!-- Add auth constraint -->
<auth-constraint>
<role-name>admin</role-name>
</auth-constraint>
</security-constraint>
5.4. Establezca el auth-method de la login-config como autenticación BASIC:
<login-config>
<!-- Update the auth method -->
<auth-method>BASIC</auth-method>
</login-config>
5.5. Presione Ctrl+S para guardar sus cambios.
6.
Cree un usuario con el rol guest y un usuario con el rol admin en hellousers.properties y hello-roles.properties para acceder a la aplicación web.
6.1. En una nueva ventana de terminal, navegue al directorio /home/student/
JB183/labs/security-domain y cree un nuevo archivo nombrado hellousers.properties:
[student@workstation ~]$ cd /home/student/JB183/labs/security-domain
[student@workstation security-domain]$ gedit hello-users.properties
6.2. Actualice el archivo para que cuente con el siguiente contenido para crear dos
nuevos usuarios, customer (cliente) y owner (propietario), ambos con la contraseña
redhat1!:
customer=redhat1!
382
JB183-EAP7.0-es-2-20180124
owner=redhat1!
6.3. Guarde el archivo y cierre el editor.
6.4. Cree un archivo nombrado hello-roles.properties en el directorio /home/
student/JB183/labs/security-domain para relacionar a los nuevos usuarios
con los roles admin y guest:
[student@workstation security-domain]$ gedit hello-roles.properties
6.5. Actualice el archivo que cuenta con el siguiente contenido para asociar al rol guest
con el usuario customer y el rol admin con el usuario owner:
customer=guest
owner=admin
6.6. Guarde el archivo y cierre el editor.
7.
Implemente la aplicación security-domain mediante los siguientes comandos en la
ventana del terminal:
[student@workstation bin]$ cd /home/student/JB183/labs/security-domain
[student@workstation security-domain]$ mvn wildfly:deploy
8.
Pruebe el security-domain como usuario customer y owner navegando a http://
localhost:8080/security-domain.
8.1. En Firefox, en la máquina virtual workstation, navegue a http://
localhost:8080/security-domain.
8.2. Cuando se lo solicita, ingrese las siguientes credenciales para iniciar sesión como
customer (cliente):
• Nombre de usuario: customer
• Password (Contraseña): redhat1!
8.3. Haga clic en Admin Page para acceder a la página del admin. El servidor devuelve
un mensaje "Forbidden" (Prohibido) ya que el usuario customer no cuenta con la
autorización para acceder a esta página.
8.4. En Firefox, diríjase a about:preferences en la URL para acceder a la página de
preferencias de Firefox.
8.5. Haga clic en Privacy (Privacidad) y, luego, en clear your recent history (borrar
su historial reciente). Haga clic en Clear Now (Borrar ahora) para forzar a la
autenticación BASIC a volver a solicitarle credenciales.
8.6. En Firefox, navegue a http://localhost:8080/security-domain e ingrese las
siguientes credenciales para iniciar sesión como el usuario owner:
• Nombre de usuario: owner
• Password (Contraseña): redhat1!
JB183-EAP7.0-es-2-20180124
383
Capítulo 9. Protección de aplicaciones Java EE
8.7. En la aplicación security-domain, haga clic en Admin para acceder a la página
protegida de la aplicación para ver una lista de usuarios. Si la seguridad se configuró
correctamente, la página es accesible.
9.
Anule la implementación de la aplicación y detenga JBoss EAP.
9.1. Ejecute el siguiente comando para anular la implementación de la aplicación:
[student@workstation security-domain]$ mvn wildfly:undeploy
9.2. Para cerrar el proyecto, haga clic con el botón derecho en el proyecto securitydomain del Project Explorer (Explorador de proyectos) y seleccione Close Project
(Cerrar proyecto).
9.3. Haga clic con el botón derecho en el servidor Red Hat JBoss EAP 7.0 en la pestaña
Servers (Servidores) de JBDS y haga clic en Stop (Detener).
Esto concluye el ejercicio guiado.
384
JB183-EAP7.0-es-2-20180124
Protección de una API REST
Protección de una API REST
Objetivo
Después de completar esta sección, los estudiantes deberán ser capaces de proteger una API
rest con autorización y autenticación.
Seguridad con RESTEasy
Uno de los principales beneficios de exponer la API REST de una aplicación es que la API y los
datos posteriores se vuelven disponibles a varios componentes de aplicaciones. No obstante,
exponer la API y datos confidenciales de las aplicaciones crea el inherente riesgo adicional de
manipulación de datos por usuarios no deseados. Para garantizar la integridad de los datos
de las aplicaciones, la API REST se debe proteger tanto para la autenticación como para la
autorización. Esto significa que la API debe permitir únicamente usuarios autorizados, y debe
validar los privilegios de cada usuario antes de manipular datos.
De manera similar a la definición de seguridad en EJB, una API REST se protege con
anotaciones o programas. Si bien la seguridad programática proporciona más flexibilidad
y control de las restricciones de autorización, el uso de anotaciones para definir las
restricciones de seguridad permite más acceso de lectura y son más sencillas de
implementar. Sin embargo, algunas veces, se requieren las implementaciones programáticas
y las anotaciones de seguridad para cumplir con todos los requisitos de seguridad de una
organización.
Para permitir una seguridad basada en roles para las API REST, actualice web.xml para que
contenga el siguiente <context-param>:
<context-param>
<param-name>resteasy.role.based.security</param-name>
<param-value>true</param-value>
</context-param>
Después de establecer resteasy.role.based.security, actualice el archivo web.xml
con un conjunto de restricciones de seguridad a la ruta del servicio RESTEasy. Por ejemplo, la
siguiente restricción de seguridad limita la ruta /todo/api/ únicamente a usuarios con el rol
admin:
<security-constraint>
<web-resource-collection>
<web-resource-name>RESTEasy</web-resource-name>
<url-pattern>/todo/api/*</url-pattern>
</web-resource-collection>
<auth-constraint>
<role-name>admin</role-name>
</auth-constraint>
</security-constraint>
La ruta a la API REST. El * es un comodín. En esta instancia, cualquier URL que comienza
con /todo/api/ coincide con la restricción de seguridad.
La etiqueta para determinar qué rol puede acceder al recurso.
JB183-EAP7.0-es-2-20180124
385
Capítulo 9. Protección de aplicaciones Java EE
Cada rol enumerado en la restricción de seguridad también se debe definir en el web.xml
como <security-role>. Por ejemplo, para hacer referencia al rol admin, defina el rol
mediante las siguientes etiquetas:
<security-role>
<role-name>admin</role-name>
</security-role>
Complete el archivo web.xml definiendo el login-config. Como se analizó en la sección
anterior, esta etiqueta determina la forma en que el servidor autentica al usuario y,
opcionalmente, define el dominio de seguridad para la aplicación:
<login-config>
<auth-method>BASIC</auth-method>
<realm-name>ApplicationRealm</realm-name>
</login-config>
Anotaciones de RESTEasy
Debido a que puede ser difícil asignar patrones URL complejos para los servicios RESTEasy
en web.xml, RESTEasy proporciona varias anotaciones para proteger extremos. Después de
habilitar la seguridad basada en roles en la web.xml de la aplicación REST, use las siguientes
anotaciones para proteger extremos específicos por rol, basándose en los roles enumerados
en web.xml:
• @RolesAllowed: define el o los roles que pueden acceder al método.
• @PermitAll: todos los roles definidos en web.xml pueden acceder al método.
• @DenyAll: niega acceso a todos los roles al método.
El siguiente es un ejemplo de una clase RESTEasy con anotaciones de seguridad:
@Stateless
@Path("hello")
@Produces(MediaType.APPLICATION_JSON)
public class HelloWorld {
@PermitAll
@GET
@Produces("text/html")
public String hello() {
return "<b>Hello World!</b>";
}
@RolesAllowed("admin, guest")
@GET
@Path("newest")
public Person getNewestPerson() {
...implementation omitted...
}
@RolesAllowed("admin")
@POST
@Consumes(MediaType.APPLICATION_JSON)
public String savePerson(Person person) {
...implementation omitted...
386
JB183-EAP7.0-es-2-20180124
Demostración: Protección de una API REST
}
@DenyAll
@DELETE
public String deleteAll() {
...implementation omitted...
}
}
Demostración: Protección de una API REST
1.
Ejecute el siguiente comando para preparar archivos usados por esta demostración.
[student@workstation ~]$ demo secure-rest setup
2.
Inicie JBDS e importe el proyecto secure-rest.
Este proyecto es un servicio web RESTEasy simple que almacena y lee objetos Person de
la base de datos.
3.
Agregue los siguientes valores en el <context-param> a web.xml para activar la
seguridad RESTEasy:
<!-- TODO add RESTEasy context param -->
<context-param>
<param-name>resteasy.role.based.security</param-name>
<param-value>true</param-value>
</context-param>
4.
En la security-constraint aplicada al patrón URL de toda la aplicación, agregue los
roles guest y admin en la etiqueta auth-constraint:
<auth-constraint>
<!-- TODO add roles -->
<role-name>guest</role-name>
<role-name>admin</role-name>
</auth-constraint>
5.
Actualice la login-config para definir el método de autenticación como BASIC y usar
ApplicationRealm como dominio de seguridad:
<login-config>
<!-- TODO add login-config -->
<auth-method>BASIC</auth-method>
<realm-name>ApplicationRealm</realm-name>
</login-config>
JB183-EAP7.0-es-2-20180124
387
Capítulo 9. Protección de aplicaciones Java EE
nota
El ApplicationRealm es un dominio de seguridad predeterminado que
utiliza el archivo de propiedades /opt/eap/standalone/configuration/
application-roles.properties para roles y el archivo de propiedades /
opt/eap/standalone/configuration/application-users.properties
para usuarios. Estos archivos se gestionan mediante el script /opt/eap/bin/
add-user.sh.
Guarde los cambios en el archivo web.xml con Ctrl+S.
6.
Abra la clase de servicio RESTEasy PersonService.java. Actualice cada método @GET
con una anotación @PermitAll para permitir a los usuarios admin y guest acceder a
estos métodos:
...
@GET
// TODO add a PermitAll annotation
@PermitAll
@Path("{id}")
public Person getPerson(@PathParam("id") Long id) {
...
@GET
// TODO add a PermitAll annotation
@PermitAll
public List<Person> getAllPersons() {
...
7.
Actualice los métodos @Delete y @POST anotados para que solo autoricen al usuario
admin a ejecutar estos métodos:
...
// TODO restrict access for admin
@RolesAllowed("admin")
@DELETE
@Path("{id}")
public void deletePerson(@PathParam("id") Long id) {
...
// TODO restrict access for admin
@RolesAllowed("admin")
@POST
public Response savePerson(Person person) {
...
8.
En una nueva ventana de terminal, ejecute el script /opt/eap/bin/add-user.sh para
crear los usuarios customer y owner:
[student@workstation ~]$ cd /opt/eap/bin
[student@workstation bin]$ ./add-user.sh
9.
Use la siguiente información para crear el usuario customer (cliente) con el rol guest:
• Application User (Usuario de aplicaciones): b
388
JB183-EAP7.0-es-2-20180124
Demostración: Protección de una API REST
• Username (Nombre de usuario): customer
• Password (Contraseña): redhat1!
• Groups (Grupos): guest
• About to add user 'customer' for realm 'ApplicationRealm' (Acerca de
agregar el usuario 'customer' para el dominio 'ApplicationRealm'): yes (sí)
• Is this new user going to be used for one AS process to connect to
another AS process? (¿Este nuevo usuario se utilizará para que un proceso AS se
conecte a otro proceso AS?): no
10. Vuelva a ejecutar el script y cree el usuario owner (propietario) con el rol admin:
• Application User (Usuario de aplicaciones): b
• Username (Nombre de usuario): owner
• Password (Contraseña): redhat1!
• Groups (Grupos): admin
• About to add user 'customer' for realm 'ApplicationRealm' (Acerca de
agregar el usuario 'customer' para el dominio 'ApplicationRealm'): yes (sí)
• Is this new user going to be used for one AS process to connect to
another AS process? (¿Este nuevo usuario se utilizará para que un proceso AS se
conecte a otro proceso AS?): no
11. Inicie el servidor JBoss EAP local dentro de JBDS.
12. Ejecute el siguiente comando en una ventana de terminal para implementar la aplicación
en el servidor JBoss EAP local:
[student@workstation ~]$ cd /home/student/JB183/labs/secure-rest
[student@workstation secure-rest]$ mvn wildfly:deploy
13. Acceda al complemento (plug-in) REST-Client en Firefox en la máquina virtual
workstation. Ingrese http://localhost:8080/secure-rest/api/persons
en la URL. Haga clic en Authentication (Autenticación) y en Basic Authentication
(Autenticación básica). Inicie sesión con las siguientes credenciales:
• Username (Nombre de usuario): customer
• Password (Contraseña): redhat1!
Haga clic en Okay (Aceptar).
14. Haga clic en Send (Enviar) y observe que el código de devolución es 200. El servidor
devuelve una lista vacía de usuarios en la pestaña Response Body (Cuerpo de
respuesta).
JB183-EAP7.0-es-2-20180124
389
Capítulo 9. Protección de aplicaciones Java EE
15. Cambie el Method Type (Tipo de método) a POST. Haga clic en Headers (Encabezados)
en la barra de navegación y, luego, haga clic en Custom Header (Encabezado
personalizado). Use la siguiente información para el encabezado personalizado y haga
clic en Okay (Aceptar):
• Name (Nombre): Content/Type
• Value (Valor): application/json
16. Agregue el siguiente JSON a la sección Body de la solicitud para crear un nuevo objeto
Person con el nombre Shadowman:
{"name": "Shadowman"}
Haga clic en Send (Enviar) y observe que el servidor responde con el código HTTP 403
Forbidden.
17. Haga clic en Authentication (Autenticación) y en Basic Authentication (Autenticación
básica). Inicie sesión con las siguientes credenciales:
• Username (Nombre de usuario): owner
• Password (Contraseña): redhat1!
Haga clic en Okay (Aceptar) y, luego, vuelva a hacer clic en Send (Enviar). En este
momento, el servidor responde con el código HTTP 200 OK.
18. Cambie el Method Type (Tipo de método) a GET y vuelva a hacer clic en Send (Enviar)
para ver el nuevo usuario en la pestaña Response Body (Cuerpo de la respuesta):
[{"id":1,"name":"Shadowman"}]
19. Anule la implementación de la aplicación y detenga el servidor.
[student@workstation secure-rest]$ mvn wildfly:undeploy
Referencias
Para obtener más información, consulte la guía Desarrollo de aplicaciones de servicios
web para Red Hat JBoss EAP 7 en
https://access.redhat.com/documentation/en/red-hat-jboss-enterpriseapplication-platform/
390
JB183-EAP7.0-es-2-20180124
Ejercicio guiado: Protección de una API REST
Ejercicio guiado: Protección de una API REST
En este ejercicio, protegerá un servicio REST para la autenticación y la autorización.
Resultado
Deberá ser capaz de usar las anotaciones de seguridad RESTEasy para proteger la
autenticación y la autorización en un servicio web REST.
Antes de comenzar
Abra una ventana de terminal en la máquina virtual workstation y ejecute el siguiente
comando para descargar los archivos necesarios para este trabajo de laboratorio.
[student@workstation ~]$ lab rest-annotations setup
Pasos
1. Abra JBDS e importe el proyecto de Maven.
1.1. Haga doble clic en el icono de JBoss Developer Studio en el escritorio de la estación
de trabajo para abrir JBDS. Seleccione el espacio de trabajo /home/student/
JB183/workspace y haga clic en OK (Aceptar).
1.2. En el menú de JBDS, haga clic en File (Archivo) > Import (Importar)para abrir el
asistente para Import (Importar).
1.3. En la página Select (Seleccionar), haga clic en Maven > Existing Maven Projects
(Proyectos de Maven existentes) y, luego, haga clic en Next (Siguiente).
1.4. En la página Maven projects (Proyectos de Maven), haga clic en Browse (Explorar)
para abrir la ventana Select root folder (Seleccionar carpeta raíz). Diríjase
al directorio /home/student/JB183/labs/. Seleccione la carpeta restannotations y haga clic en OK (Aceptar).
1.5. En la página Maven projects (Proyectos de Maven), haga clic en Finish (Finalizar).
1.6. Observe la barra de estado de JBDS para monitorear el progreso de la operación
de importación. Es probable que la descarga de todas las dependencias requeridas
demore unos minutos.
2.
Seleccione la pestaña Servers (Servidores) en el panel inferior de JBDS para iniciar EAP.
Haga clic con el botón derecho en el servidor Red Hat JBoss EAP 7.0 [Stopped] y haga
clic en el botón verde para iniciar el servidor. Observe la Console (Consola) hasta que el
servidor se inicie y muestre el siguiente mensaje:
INFO [org.jboss.as] (Controller Boot Thread) WFLYSRV0025: JBoss EAP 7.0.0.GA
(WildFly Core 2.1.2.Final-redhat-1) started
3.
Actualice el archivo web.xml para permitir anotaciones de seguridad RESTEasy,
agregue una restricción de seguridad para la ruta del servicio REST y use un método de
autenticación BASIC.
3.1. Abra el archivo web.xml; para ello, expanda el ítem rest-annotations en la pestaña
Project Explorer (Explorador de proyectos) del panel izquierdo JBDS y, luego, haga
JB183-EAP7.0-es-2-20180124
391
Capítulo 9. Protección de aplicaciones Java EE
clic en rest-annotations > src/main/webapp > WEB-INF y expándalo. Haga doble
clic en el archivo web.xml.
3.2. Cree una nueva etiqueta <context-param> que contenga el parámetro
resteasy.role.based.security con un valor de true:
<!-- Add context param -->
<context-param>
<param-name>resteasy.role.based.security</param-name>
<param-value>true</param-value>
</context-param>
3.3. Cree una nueva security-constraint que restrinja todos los recursos para la
aplicación con un <url-pattern> establecido como /*:
<!-- Add security constraint -->
<security-constraint>
<web-resource-collection>
<web-resource-name>All resources</web-resource-name>
<url-pattern>/*</url-pattern>
</web-resource-collection>
</security-constraint>
3.4. Actualice <security-constraint para restringir el acceso para usuarios con el rol
guest o admin mediante la etiqueta auth-constraint:
<security-constraint>
<web-resource-collection>
<web-resource-name>All resources</web-resource-name>
<url-pattern>/*</url-pattern>
</web-resource-collection>
<auth-constraint>
<role-name>guest</role-name>
<role-name>admin</role-name>
</auth-constraint>
</security-constraint>
3.5. Defina roles de seguridad para los roles guest y admin con una etiqueta
<security-role>:
<!-- Add security role -->
<security-role>
<role-name>admin</role-name>
</security-role>
<security-role>
<role-name>guest</role-name>
</security-role>
3.6. Cree un elemento <login-config>, establezca el <auth-method> en BASIC y use
el dominio de seguridad predefinido, ApplicationRealm.
<!-- Add login config -->
392
JB183-EAP7.0-es-2-20180124
<login-config>
<auth-method>BASIC</auth-method>
<realm-name>ApplicationRealm</realm-name>
</login-config>
3.7. Presione Ctrl+S para guardar sus cambios.
4.
Esta aplicación ahora usa el ApplicationRealm para autenticar usuarios. El
ApplicationRealm es un dominio de seguridad predeterminado que utiliza el
archivo de propiedades /opt/eap/standalone/configuration/applicationroles.properties para roles y el archivo de propiedades /opt/eap/standalone/
configuration/application-users.properties para usuarios.
Use el script /opt/eap/bin/add-user.sh para crear los siguientes usuarios y sus
respectivos roles:
• customer (cliente): guest
• owner (propietario): admin
• superuser: guest, admin
4.1. Ejecute el script /opt/eap/bin/add-user.sh en una nueva ventana de terminal:
[student@workstation ~]$ cd /opt/eap/bin
[student@workstation bin]$ ./add-user.sh
4.2. Use la siguiente información para crear el usuario customer (cliente) con el rol
guest cuando se le indique:
• Application User (Usuario de aplicaciones): b
• Username (Nombre de usuario): customer
• Password (Contraseña): redhat1!
• Groups (Grupos): guest
• About to add user 'customer' for realm 'ApplicationRealm' (Acerca
de agregar el usuario 'customer' para el dominio 'ApplicationRealm'): yes (sí)
• Is this new user going to be used for one AS process to connect
to another AS process? (¿Este nuevo usuario se utilizará para que un proceso
AS se conecte a otro proceso AS?): no
4.3. Vuelva a ejecutar el script y use la siguiente información para crear el usuario owner
(propietario) con el rol guest cuando se le indique:
• Application User (Usuario de aplicaciones): b
• Username (Nombre de usuario): owner
• Password (Contraseña): redhat1!
• Groups (Grupos): admin
JB183-EAP7.0-es-2-20180124
393
Capítulo 9. Protección de aplicaciones Java EE
• About to add user 'customer' for realm 'ApplicationRealm' (Acerca
de agregar el usuario 'customer' para el dominio 'ApplicationRealm'): yes (sí)
• Is this new user going to be used for one AS process to connect
to another AS process? (¿Este nuevo usuario se utilizará para que un proceso
AS se conecte a otro proceso AS?): no
4.4. Vuelva a ejecutar el script por tercera vez y use la siguiente información para crear el
usuario superuser con admin y guest cuando se le indique:
• Application User (Usuario de aplicaciones): b
• Username (Nombre de usuario): superuser
• Password (Contraseña): redhat1!
• Groups (Grupos): admin, guest
• About to add user 'customer' for realm 'ApplicationRealm' (Acerca
de agregar el usuario 'customer' para el dominio 'ApplicationRealm'): yes (sí)
• Is this new user going to be used for one AS process to connect
to another AS process? (¿Este nuevo usuario se utilizará para que un proceso
AS se conecte a otro proceso AS?): no
4.5. Use el siguiente comando para ver el contenido de /opt/eap/standalone/
configuration/application-users.properties con el siguiente comando:
[student@workstation ~]$ less /opt/eap/standalone/configuration\
/application-users.properties
...
customer=a2d94c2cc5f3a4706e669da24b9a9bf0
owner=2f408f4ad343d859807d7593128b3767
superuser=8e90480af06a793a083c16991b11dc99
...
Observe que cada línea contiene el usuario y su contraseña con hash.
4.6. Use el siguiente comando para ver el contenido de /opt/eap/standalone/
configuration/application-roles.properties con el siguiente comando:
[student@workstation ~]$ less /opt/eap/standalone/configuration\
/application-roles.properties
...
customer=guest
owner=admin
superuser=admin,guest
...
Observe que cada línea contiene un usuario y el o los roles asignados a este.
394
JB183-EAP7.0-es-2-20180124
5.
Actualice la clase de servicio RESTEasy PersonService.java y actualice cada método
con una anotación de seguridad RESTEasy.
5.1. En la pestaña Project Explorer (Explorador de proyectos) en el panel izquierdo de
JBDS, seleccione src/main/java > com.redhat.training.rest y haga doble clic en
PersonService.java para ver el archivo.
5.2. Actualice el método getPerson() con una anotación @PermitAll para permitir
que todos los roles enumerados en web.xml puedan acceder al método:
//TODO permit all users
@PermitAll
@GET
@Path("{id}")
public Person getPerson(@PathParam("id") Long id) {
...
5.3. Actualice el método assignAllPersons() con una anotación @RolesAllowed para
permitir que solo los usuarios con el rol guest accedan al método:
//TODO allow only guest
@RolesAllowed("guest")
@GET
public List<Person> getAllPersons() {
...
5.4. Actualice el método deletePerson() DELETE con una anotación @DenyAll para
evitar que todos los roles enumerados en web.xml accedan a este método:
//TODO restrict access for all roles
@DenyAll
@DELETE
@Path("{id}")
public void deletePerson(@PathParam("id") Long id) {
...
5.5. Actualice el método savePerson() POST con una anotación @RolesAllowed para
permitir que solo los usuarios con el rol admin accedan al método:
//TODO allow only admin
@RolesAllowed("admin")
@POST
public Response savePerson(Person person) {
...
5.6. Presione Ctrl+S para guardar sus cambios.
6.
Navegue al directorio del proyecto e implemente la aplicación.
[student@workstation ~]$ cd /home/student/JB183/labs/rest-annotations
[student@workstation rest-annotations]$ mvn wildfly:deploy
JB183-EAP7.0-es-2-20180124
395
Capítulo 9. Protección de aplicaciones Java EE
7.
Use el complemento (plug-in) de cliente REST de Firefox para probar la API REST
protegida.
7.1. Acceda al complemento (plug-in) REST-Client en Firefox en la máquina virtual
workstation.
Figura 9.2: Complemento (plug-in) de cliente REST de Firefox
7.2. Ingrese http://localhost:8080/rest-annotations/api/persons en la URL.
7.3. Haga clic en Authentication (Autenticación) y en Basic Authentication (Autenticación
básica). Inicie sesión con las siguientes credenciales:
• Username (Nombre de usuario): customer
• Password (Contraseña): redhat1!
Haga clic en Okay (Aceptar).
7.4. Haga clic en Send (Enviar) y observe que el código de devolución es 200. Haga clic
en la pestaña Response Body (Cuerpo de la respuesta) y observe que la carga es
cualquier lista vacía, ya que aún no se han agregado usuarios a la base de datos.
7.5. Cambie el Method Type (Tipo de método) a POST.
7.6. Haga clic en Headers (Encabezados) en la barra de navegación y, luego, haga clic en
Custom Header (Encabezado personalizado). Use la siguiente información para el
encabezado personalizado:
• Name (Nombre): Content/Type
• Value (Valor): application/json
Haga clic en Okay (Aceptar).
7.7. Agregue el siguiente JSON a la sección Body de la solicitud para crear un nuevo
objeto Person con el nombre RedHat:
{"name": "RedHat"}
7.8. Haga clic en Send (Enviar) y observe que el servidor responde con el código HTTP
403 Forbidden ya que el usuario customer (cliente) con el rol guest no está
autorizado a usar ese método en la clase de servicio REST.
8.
Vuelva a probar la API REST con los usuarios owner y superuser.
8.1. Haga clic en Authentication (Autenticación) y en Basic Authentication (Autenticación
básica). Inicie sesión con las siguientes credenciales:
• Username (Nombre de usuario): owner
396
JB183-EAP7.0-es-2-20180124
• Password (Contraseña): redhat1!
Haga clic en Okay (Aceptar).
8.2. Haga clic en Send (Enviar) usando los mismos datos de la solicitud JSON del paso
anterior. En este momento, el servidor responde con el código HTTP 200 OK.
8.3. Verifique que los datos se hayan almacenado en la base de datos modificando
el Method Type (Tipo de método) a GET y, luego, volviendo a hacer clic en Send
(Enviar).
El servidor responde con otro código HTTP 403 Forbidden porque el usuario
owner con el rol admin no está autorizado a ver todos los usuarios.
8.4. Haga clic en Authentication (Autenticación) y en Basic Authentication (Autenticación
básica). Inicie sesión con las siguientes credenciales:
• Username (Nombre de usuario): superuser
• Password (Contraseña): redhat1!
Haga clic en Okay (Aceptar).
8.5. El usuario superuser pertenece a los roles guest y admin y, por lo tanto, puede
guardar los nuevos objetos Person y obtener una lista de todos los objetos en la
base de datos.
Con el Method Type (Tipo de método) aún establecido como GET, vuelva a hacer clic
en Send (Enviar). En este momento, el servidor responde con el código HTTP 200 OK
y los siguientes datos en la pestaña Response Body (Cuerpo de la respuesta):
[{"id":1,"name":"RedHat"}]
8.6. Mientras aún está autenticado como superuser, cambie el Method Type (Tipo de
método) a POST y agregue el siguiente JSON al Body (Cuerpo) de la solicitud:
{"name": "Shadowman"}
Haga clic en Send (Enviar).
8.7. Verifique que los datos se hayan almacenado en la base de datos modificando
el Method Type (Tipo de método) a GET y, luego, volviendo a hacer clic en Send
(Enviar).
El servidor responde con el código HTTP 200 OK y los siguientes datos en la pestaña
Response Body (Cuerpo de la respuesta):
[{"id":1,"name":"RedHat"}, {"id":2, "name":"Shadowman"}]
8.8. Elimine el objeto Person en la base de datos con el ID 1 actualizando la solicitud URL
a http://localhost:8080/rest-annotations/api/persons/1 y cambiando el
Method Type (Tipo de método) a DELETE (ELIMINAR).
JB183-EAP7.0-es-2-20180124
397
Capítulo 9. Protección de aplicaciones Java EE
8.9. Haga clic en Send (Enviar).
El servidor devuelve otro código 403 Forbidden, ya que este método se anota con
una anotación @DenyAll que evita que los usuarios lo ejecuten.
9.
Anule la implementación de la aplicación y detenga JBoss EAP.
9.1. Ejecute el siguiente comando para anular la implementación de la aplicación:
[student@workstation rest-annotations]$ mvn wildfly:undeploy
9.2. Para cerrar el proyecto, haga clic con el botón derecho en el proyecto restannotations del Project Explorer (Explorador de proyectos) y seleccione Close
Project (Cerrar proyecto).
9.3. Haga clic con el botón derecho en el servidor Red Hat JBoss EAP 7.0 en la pestaña
Servers (Servidores) de JBDS y haga clic en Stop (Detener).
Esto concluye el ejercicio guiado.
398
JB183-EAP7.0-es-2-20180124
Trabajo de laboratorio: Protección de aplicaciones Java EE
Trabajo de laboratorio: Protección de
aplicaciones Java EE
En este trabajo de laboratorio, protegerá un servicio REST de la aplicación To Do List con
autenticación y autorización.
Resultados
Deberá ser capaz de proteger un servicio REST con autorización y autenticación.
Antes de comenzar
Abra una ventana de terminal en la máquina virtual workstation y ejecute el siguiente
comando para descargar los archivos necesarios para este trabajo de laboratorio.
[student@workstation ~]$ lab securing-lab setup
Pasos
1. Abra JBDS e importe el proyecto securing-lab ubicado en el directorio /home/
student/JB183/labs/securing-lab.
2.
Inicie JBoss EAP desde dentro de JBDS.
3.
Use el script /home/student/JB183/labs/securing-lab/create-sd.sh para crear
un dominio de seguridad UsersRoles nombrado userroles.
Este dominio de seguridad usa el módulo de inicio de sesión UsersRoles para leer el
archivo de propiedad del usuario /home/student/JB183/labs/securing-lab/todousers.properties proporcionado para la autenticación y el archivo /home/student/
JB183/labs/securing-lab/todo-roles.properties para la autorización.
3.1. En la ventana del terminal, diríjase al directorio del proyecto /home/student/
JB183/labs/securing-lab/ y ejecute el script create-sd.sh:
[student@workstation ~]$ cd JB183/labs/securing-lab
[student@workstation securing-lab]$ ./create-sd.sh
3.2. Confirme que el dominio de seguridad está disponible al visualizar el contenido
de la configuración del servidor EAP /opt/jboss-eap-7.0/standalone/
configuration/standalone-full.xml.
Mediante un editor de texto, abra el archivo de configuración /opt/jbosseap-7.0/standalone/configuration/standalone-full.xml y observe el
nuevo dominio de seguridad que controla al servidor de aplicaciones para utilizar
los archivos de propiedad de roles y usuarios proporcionados en el directorio de
proyectos.
<security-domain name="userroles" cache-type="default">
<authentication>
<login-module code="UsersRoles" flag="required">
<module-option name=usersProperties" value="file:///home/student/JB183/labs/
securing-lab/todo-users.properties"/>
JB183-EAP7.0-es-2-20180124
399
Capítulo 9. Protección de aplicaciones Java EE
<module-option name="rolesProperties" value="file:///home/student/JB183/labs/
securing-lab/todo-users.properties"/>
</login-module>
</authentication>
</security-domain>
4.
Actualice el archivo jboss-web.xml para utilizar el dominio de seguridad userroles.
4.1. Abra la clase jboss-web.xml; para ello, expanda el ítem securing-lab en la pestaña
Project Explorer (Explorador de proyectos) en el panel izquierdo de JBDS. Haga clic
en securing-lab > src/main/webapp > WEB-INF y expándalo. Haga doble clic en el
archivo jboss-web.xml.
4.2. Actualice el archivo jboss-web.xml para utilizar el nuevo dominio de seguridad
nombrado userroles:
<?xml version="1.0" encoding="ISO-8859-1"?>
<jboss-web>
<security-domain>userroles</security-domain>
</jboss-web>
4.3. Presione Ctrl+S para guardar sus cambios.
5.
Actualice el archivo web.xml para habilitar las anotaciones de seguridad RESTEasy, utilice
la autenticación BASIC y restrinja el acceso a la API REST de la aplicación con la ruta /
api/* a los roles admin, guest y observer.
5.1. Abra la clase web.xml; para ello, expanda el ítem securing-lab en la pestaña Project
Explorer (Explorador de proyectos) en el panel izquierdo de JBDS. Haga clic en
securing-lab > src/main/webapp > WEB-INF y expándalo. Haga doble clic en el
archivo web.xml.
5.2. Cree una nueva etiqueta <context-param> que contenga el parámetro
resteasy.role.based.security con un valor de true:
<!-- Add context param -->
<context-param>
<param-name>resteasy.role.based.security</param-name>
<param-value>true</param-value>
</context-param>
5.3. Cree una nueva security-constraint que restrinja todos los recursos para la
aplicación con un <url-pattern> establecido como /api/*:
<!-- Add security constraint -->
<security-constraint>
<web-resource-collection>
<web-resource-name>REST Resources</web-resource-name>
<url-pattern>/api/*</url-pattern>
</web-resource-collection>
</security-constraint>
400
JB183-EAP7.0-es-2-20180124
5.4. Actualice <security-constraint para restringir el acceso para usuarios con el rol
guest, observer o admin mediante la etiqueta auth-constraint:
<security-constraint>
<web-resource-collection>
<web-resource-name>All resources</web-resource-name>
<url-pattern>/api/*</url-pattern>
</web-resource-collection>
<auth-constraint>
<role-name>guest</role-name>
<role-name>admin</role-name>
<role-name>observer</role-name>
</auth-constraint>
</security-constraint>
5.5. Defina los roles de seguridad para los roles guest, observer y admin con una
etiqueta <security-role>:
<!-- Add security role -->
<security-role>
<role-name>admin</role-name>
</security-role>
<security-role>
<role-name>guest</role-name>
</security-role>
<security-role>
<role-name>observer</role-name>
</security-role>
5.6. Cree un elemento <login-config> y defina <auth-method> como BASIC.
<!-- Add login config -->
<login-config>
<auth-method>BASIC</auth-method>
</login-config>
5.7. Presione Ctrl+S para guardar sus cambios.
6.
Actualice la clase de servicio RESTEasy ItemResourceRESTService.java y actualice
cada método con los siguientes requisitos:
• Todos los métodos GET se permiten para todos los roles.
• Todos los métodos POST se permiten para los roles admin y guest.
• Todos los métodos DELETE se permiten solo para usuarios con el rol admin.
Observe que el método @PostConstruct recupera el usuario Principal de la solicitud
para determinar qué usuario ha iniciado sesión actualmente. De esta forma, el servicio
REST solo recupera resultados para ese usuario específico.
JB183-EAP7.0-es-2-20180124
401
Capítulo 9. Protección de aplicaciones Java EE
6.1. En la pestaña Project Explorer (Explorador de proyectos) en el panel izquierdo
de JBDS, seleccione src/main/java > com.redhat.training.rest. Haga doble clic en
ItemResourceRESTService.java para ver el archivo.
6.2. Actualice el método listAllItems con una anotación @PermitAll para permitir
que todos los roles enumerados en web.xml puedan acceder al método:
@PermitAll
@GET
@Produces(MediaType.APPLICATION_JSON)
public List<Item> listAllItems() {
return repository.findAllItemsForUser(currentUser);
}
6.3. Actualice el método lookupItemById() con una anotación @PermitAll para
permitir que todos los roles enumerados en web.xml puedan acceder al método:
@PermitAll
@GET
@Path("/{id:[0-9][0-9]*}")
@Produces(MediaType.APPLICATION_JSON)
public Item lookupItemById(@PathParam("id") long id) {
...
6.4. Actualice el método createItem() POST con una anotación @RolesAllowed para
permitir que solo los usuarios con el rol admin o guest accedan al método:
@RolesAllowed({"admin", "guest"})
@POST
@Consumes(MediaType.APPLICATION_JSON)
@Produces(MediaType.APPLICATION_JSON)
public Response createItem(Item item) {
...
6.5. Actualice el método deleteItem() DELETE con una anotación @RolesAllowed
para permitir que solo los usuarios con el rol admin accedan al método:
@RolesAllowed("admin")
@DELETE
@Path("{id}")
@Consumes(MediaType.APPLICATION_JSON)
public void deleteItem(@PathParam("id") Long id) {
itemService.remove(id);
}
6.6. Presione Ctrl+S para guardar sus cambios.
7.
Compile e implemente la aplicación en JBoss EAP mediante Maven ejecutando los
siguientes comandos:
8.
Use el complemento (plug-in) de cliente REST para probar la API REST protegida en la URL
http://localhost:8080/securing-lab/api/items. Use el complemento (plug-in)
402
JB183-EAP7.0-es-2-20180124
para verificar que el usuario customer con la contraseña redhat1! tenga permisos para
acceder solo a los métodos GET y POST.
8.1. Acceda al complemento (plug-in) REST-Client en Firefox en la máquina virtual
workstation.
Complemento (plug-in) de cliente REST de Firefox
8.2. Ingrese http://localhost:8080/securing-lab/api/items en la URL.
8.3. Haga clic en Authentication (Autenticación) y en Basic Authentication (Autenticación
básica). Inicie sesión con las siguientes credenciales:
• Username (Nombre de usuario): customer
• Password (Contraseña): redhat1!
Haga clic en Okay (Aceptar).
8.4. Cambie el Method Type (Tipo de método) a POST.
8.5. Haga clic en Headers (Encabezados) en la barra de navegación y, luego, haga clic en
Custom Header (Encabezado personalizado). Use la siguiente información para el
encabezado personalizado:
• Name (Nombre): Content/Type
• Value (Valor): application/json
Haga clic en Okay (Aceptar).
8.6. Agregue el siguiente JSON al Body (Cuerpo) de la solicitud para crear un nuevo Item
con la descripción Walk the dog:
{"description": "Walk the dog"}
Haga clic en Send (Enviar).
8.7. Cambie el Method Type (Tipo de método) a GET y vuelva a hacer clic en Send (Enviar)
para ver una lista de todos los objetos Item:
[{"id:11, "description":"Walk the
dog","done":false,"user":"id":2,"username":"customer"}}]
8.8. Mediante el ID de la respuesta anterior, ingrese http://localhost:8080/
securing-lab/api/items/11 en la URL del complemento (plug-in) y cambie el
Method Type (Tipo de método) a DELETE (ELIMINAR). Haga clic en Send (Enviar) y el
servidor devolverá una respuesta 403 Forbidden.
JB183-EAP7.0-es-2-20180124
403
Capítulo 9. Protección de aplicaciones Java EE
9.
Use el complemento (plug-in) para verificar que el usuario owner con la contraseña
redhat1! tenga permisos para acceder a los métodos GET, POST y DELETE.
9.1. En el complemento (plug-in) de Firefox, haga clic en Authentication (Autenticación)
y en Basic Authentication (Autenticación básica). Inicie sesión con las siguientes
credenciales:
• Username (Nombre de usuario): owner
• Password (Contraseña): redhat1!
Haga clic en Okay (Aceptar).
9.2. Cambie el Method Type (Tipo de método) a POST y actualice la URL a http://
localhost:8080/securing-lab/api/items/.
9.3. Agregue el siguiente JSON al Body (Cuerpo) de la solicitud para crear un nuevo Item
con la descripción Test owner:
{"description": "Test owner"}
Haga clic en Send (Enviar).
9.4. Cambie el Method Type (Tipo de método) a GET y vuelva a hacer clic en Send (Enviar)
para ver una lista de todos los objetos Item:
[{"id:12, "description":"Test
owner","done":false,"user":"id":3,"username":"owner"}}]
9.5. Mediante el ID de la respuesta anterior, ingrese http://localhost:8080/
securing-lab/api/items/12 en la URL del complemento (plug-in) y cambie el
Method Type (Tipo de método) a DELETE (ELIMINAR). Haga clic en Send (Enviar) y el
servidor devolverá una respuesta 204.
9.6. Cambie el Method Type (Tipo de método) a GET, actualice la URL a http://
localhost:8080/securing-lab/api/items/ y vuelva a hacer clic en Send
(Enviar) para ver que el objeto Item se haya eliminado.
10. Use el complemento (plug-in) para verificar que el usuario viewer con la contraseña
redhat1! tenga permisos para acceder solo a los métodos GET.
10.1.En el complemento (plug-in) de Firefox, haga clic en Authentication (Autenticación)
y en Basic Authentication (Autenticación básica). Inicie sesión con las siguientes
credenciales:
• Username (Nombre de usuario): viewer
• Password (Contraseña): redhat1!
Haga clic en Okay (Aceptar).
10.2.Cambie el Method Type (Tipo de método) a POST.
404
JB183-EAP7.0-es-2-20180124
10.3.Agregue el siguiente JSON al Body (Cuerpo) de la solicitud para crear un nuevo Item
con la descripción Test viewer:
{"description": "Test viewer"}
Haga clic en Send (Enviar). El servidor devuelve un código 403 Forbidden.
10.4.Cambie el Method Type (Tipo de método) a GET y haga clic en Send (Enviar) para ver
una respuesta 200 Ok del servidor, aunque la lista esté vacía.
11. Abra una nueva ventana de terminal y ejecute el siguiente comando para calificar el
trabajo de laboratorio:
[student@workstation ~]$ lab securing-lab grade
El script de calificación debe indicar SUCCESS (CORRECTO). Si se produce una falla, revise
los errores y corríjalos hasta que vea un mensaje que indique SUCCESS (CORRECTO).
12. Realice la limpieza.
12.1.Anule la implementación de la aplicación en JBoss EAP con Maven con los siguientes
comandos:
[student@workstation ~]$ cd /home/student/JB183/labs/securing-lab
[student@workstation securing-lab]$ mvn wildfly:undeploy
12.2.Haga clic con el botón derecho en el proyecto securing-lab en el Project Explorer
(Explorador de proyectos) y seleccione Close Project (Cerrar proyecto).
12.3.Haga clic con el botón derecho en el servidor Red Hat JBoss EAP 7.0 en la pestaña
Servers (Servidores) de JBDS y haga clic en Stop (Detener).
Esto concluye el trabajo de laboratorio.
JB183-EAP7.0-es-2-20180124
405
Capítulo 9. Protección de aplicaciones Java EE
Solución
En este trabajo de laboratorio, protegerá un servicio REST de la aplicación To Do List con
autenticación y autorización.
Resultados
Deberá ser capaz de proteger un servicio REST con autorización y autenticación.
Antes de comenzar
Abra una ventana de terminal en la máquina virtual workstation y ejecute el siguiente
comando para descargar los archivos necesarios para este trabajo de laboratorio.
[student@workstation ~]$ lab securing-lab setup
Pasos
1. Abra JBDS e importe el proyecto securing-lab ubicado en el directorio /home/
student/JB183/labs/securing-lab.
1.1. Para abrir JBDS, haga doble clic en el icono de JBoss Developer Studio en el escritorio
de la estación de trabajo. Deje el espacio de trabajo predeterminado (/home/
student/JB183/workspace) y haga clic en OK (Aceptar).
1.2. En el menú de JBDS, haga clic en File (Archivo) > Import (Importar)para abrir el
asistente para Import (Importar).
1.3. En la página Select (Seleccionar), haga clic en Maven > Existing Maven Projects
(Proyectos de Maven existentes) y, luego, haga clic en Next (Siguiente).
1.4. En la página Maven projects (Proyectos de Maven), haga clic en Browse (Explorar)
para abrir la ventana Select root folder (Seleccionar carpeta raíz). Diríjase al
directorio /home/student/JB183/labs/. Seleccione la carpeta securing-lab y
haga clic en OK (Aceptar).
1.5. En la página Maven projects (Proyectos de Maven), haga clic en Finish (Finalizar).
1.6. Observe la barra de estado de JBDS para monitorear el progreso de la operación
de importación. Es probable que la descarga de todas las dependencias requeridas
demore unos minutos.
2.
Inicie JBoss EAP desde dentro de JBDS.
Seleccione la pestaña Servers (Servidores) en JBDS. Haga clic con el botón derecho en la
entrada del servidor Red Hat JBoss EAP 7.0 [Stopped] y haga clic en la opción verde Start
(Iniciar) para iniciar el servidor.
3.
Use el script /home/student/JB183/labs/securing-lab/create-sd.sh para crear
un dominio de seguridad UsersRoles nombrado userroles.
Este dominio de seguridad usa el módulo de inicio de sesión UsersRoles para leer el
archivo de propiedad del usuario /home/student/JB183/labs/securing-lab/todousers.properties proporcionado para la autenticación y el archivo /home/student/
JB183/labs/securing-lab/todo-roles.properties para la autorización.
406
JB183-EAP7.0-es-2-20180124
Solución
3.1. En la ventana del terminal, diríjase al directorio del proyecto /home/student/
JB183/labs/securing-lab/ y ejecute el script create-sd.sh:
[student@workstation ~]$ cd JB183/labs/securing-lab
[student@workstation securing-lab]$ ./create-sd.sh
3.2. Confirme que el dominio de seguridad está disponible al visualizar el contenido
de la configuración del servidor EAP /opt/jboss-eap-7.0/standalone/
configuration/standalone-full.xml.
Mediante un editor de texto, abra el archivo de configuración /opt/jbosseap-7.0/standalone/configuration/standalone-full.xml y observe el
nuevo dominio de seguridad que controla al servidor de aplicaciones para utilizar
los archivos de propiedad de roles y usuarios proporcionados en el directorio de
proyectos.
<security-domain name="userroles" cache-type="default">
<authentication>
<login-module code="UsersRoles" flag="required">
<module-option name=usersProperties" value="file:///home/student/JB183/labs/
securing-lab/todo-users.properties"/>
<module-option name="rolesProperties" value="file:///home/student/JB183/labs/
securing-lab/todo-users.properties"/>
</login-module>
</authentication>
</security-domain>
4.
Actualice el archivo jboss-web.xml para utilizar el dominio de seguridad userroles.
4.1. Abra la clase jboss-web.xml; para ello, expanda el ítem securing-lab en la pestaña
Project Explorer (Explorador de proyectos) en el panel izquierdo de JBDS. Haga clic
en securing-lab > src/main/webapp > WEB-INF y expándalo. Haga doble clic en el
archivo jboss-web.xml.
4.2. Actualice el archivo jboss-web.xml para utilizar el nuevo dominio de seguridad
nombrado userroles:
<?xml version="1.0" encoding="ISO-8859-1"?>
<jboss-web>
<security-domain>userroles</security-domain>
</jboss-web>
4.3. Presione Ctrl+S para guardar sus cambios.
5.
Actualice el archivo web.xml para habilitar las anotaciones de seguridad RESTEasy, utilice
la autenticación BASIC y restrinja el acceso a la API REST de la aplicación con la ruta /
api/* a los roles admin, guest y observer.
5.1. Abra la clase web.xml; para ello, expanda el ítem securing-lab en la pestaña Project
Explorer (Explorador de proyectos) en el panel izquierdo de JBDS. Haga clic en
securing-lab > src/main/webapp > WEB-INF y expándalo. Haga doble clic en el
archivo web.xml.
JB183-EAP7.0-es-2-20180124
407
Capítulo 9. Protección de aplicaciones Java EE
5.2. Cree una nueva etiqueta <context-param> que contenga el parámetro
resteasy.role.based.security con un valor de true:
<!-- Add context param -->
<context-param>
<param-name>resteasy.role.based.security</param-name>
<param-value>true</param-value>
</context-param>
5.3. Cree una nueva security-constraint que restrinja todos los recursos para la
aplicación con un <url-pattern> establecido como /api/*:
<!-- Add security constraint -->
<security-constraint>
<web-resource-collection>
<web-resource-name>REST Resources</web-resource-name>
<url-pattern>/api/*</url-pattern>
</web-resource-collection>
</security-constraint>
5.4. Actualice <security-constraint para restringir el acceso para usuarios con el rol
guest, observer o admin mediante la etiqueta auth-constraint:
<security-constraint>
<web-resource-collection>
<web-resource-name>All resources</web-resource-name>
<url-pattern>/api/*</url-pattern>
</web-resource-collection>
<auth-constraint>
<role-name>guest</role-name>
<role-name>admin</role-name>
<role-name>observer</role-name>
</auth-constraint>
</security-constraint>
5.5. Defina los roles de seguridad para los roles guest, observer y admin con una
etiqueta <security-role>:
<!-- Add security role -->
<security-role>
<role-name>admin</role-name>
</security-role>
<security-role>
<role-name>guest</role-name>
</security-role>
<security-role>
<role-name>observer</role-name>
</security-role>
5.6. Cree un elemento <login-config> y defina <auth-method> como BASIC.
408
JB183-EAP7.0-es-2-20180124
Solución
<!-- Add login config -->
<login-config>
<auth-method>BASIC</auth-method>
</login-config>
5.7. Presione Ctrl+S para guardar sus cambios.
6.
Actualice la clase de servicio RESTEasy ItemResourceRESTService.java y actualice
cada método con los siguientes requisitos:
• Todos los métodos GET se permiten para todos los roles.
• Todos los métodos POST se permiten para los roles admin y guest.
• Todos los métodos DELETE se permiten solo para usuarios con el rol admin.
Observe que el método @PostConstruct recupera el usuario Principal de la solicitud
para determinar qué usuario ha iniciado sesión actualmente. De esta forma, el servicio
REST solo recupera resultados para ese usuario específico.
6.1. En la pestaña Project Explorer (Explorador de proyectos) en el panel izquierdo
de JBDS, seleccione src/main/java > com.redhat.training.rest. Haga doble clic en
ItemResourceRESTService.java para ver el archivo.
6.2. Actualice el método listAllItems con una anotación @PermitAll para permitir
que todos los roles enumerados en web.xml puedan acceder al método:
@PermitAll
@GET
@Produces(MediaType.APPLICATION_JSON)
public List<Item> listAllItems() {
return repository.findAllItemsForUser(currentUser);
}
6.3. Actualice el método lookupItemById() con una anotación @PermitAll para
permitir que todos los roles enumerados en web.xml puedan acceder al método:
@PermitAll
@GET
@Path("/{id:[0-9][0-9]*}")
@Produces(MediaType.APPLICATION_JSON)
public Item lookupItemById(@PathParam("id") long id) {
...
6.4. Actualice el método createItem() POST con una anotación @RolesAllowed para
permitir que solo los usuarios con el rol admin o guest accedan al método:
@RolesAllowed({"admin", "guest"})
@POST
@Consumes(MediaType.APPLICATION_JSON)
@Produces(MediaType.APPLICATION_JSON)
public Response createItem(Item item) {
...
JB183-EAP7.0-es-2-20180124
409
Capítulo 9. Protección de aplicaciones Java EE
6.5. Actualice el método deleteItem() DELETE con una anotación @RolesAllowed
para permitir que solo los usuarios con el rol admin accedan al método:
@RolesAllowed("admin")
@DELETE
@Path("{id}")
@Consumes(MediaType.APPLICATION_JSON)
public void deleteItem(@PathParam("id") Long id) {
itemService.remove(id);
}
6.6. Presione Ctrl+S para guardar sus cambios.
7.
Compile e implemente la aplicación en JBoss EAP mediante Maven ejecutando los
siguientes comandos:
[student@workstation ~]$ cd /home/student/JB183/labs/securing-lab
[student@workstation securing-lab]$ mvn clean wildfly:deploy
8.
Use el complemento (plug-in) de cliente REST para probar la API REST protegida en la URL
http://localhost:8080/securing-lab/api/items. Use el complemento (plug-in)
para verificar que el usuario customer con la contraseña redhat1! tenga permisos para
acceder solo a los métodos GET y POST.
8.1. Acceda al complemento (plug-in) REST-Client en Firefox en la máquina virtual
workstation.
Complemento (plug-in) de cliente REST de Firefox
8.2. Ingrese http://localhost:8080/securing-lab/api/items en la URL.
8.3. Haga clic en Authentication (Autenticación) y en Basic Authentication (Autenticación
básica). Inicie sesión con las siguientes credenciales:
• Username (Nombre de usuario): customer
• Password (Contraseña): redhat1!
Haga clic en Okay (Aceptar).
8.4. Cambie el Method Type (Tipo de método) a POST.
8.5. Haga clic en Headers (Encabezados) en la barra de navegación y, luego, haga clic en
Custom Header (Encabezado personalizado). Use la siguiente información para el
encabezado personalizado:
• Name (Nombre): Content/Type
• Value (Valor): application/json
410
JB183-EAP7.0-es-2-20180124
Solución
Haga clic en Okay (Aceptar).
8.6. Agregue el siguiente JSON al Body (Cuerpo) de la solicitud para crear un nuevo Item
con la descripción Walk the dog:
{"description": "Walk the dog"}
Haga clic en Send (Enviar).
8.7. Cambie el Method Type (Tipo de método) a GET y vuelva a hacer clic en Send (Enviar)
para ver una lista de todos los objetos Item:
[{"id:11, "description":"Walk the
dog","done":false,"user":"id":2,"username":"customer"}}]
8.8. Mediante el ID de la respuesta anterior, ingrese http://localhost:8080/
securing-lab/api/items/11 en la URL del complemento (plug-in) y cambie el
Method Type (Tipo de método) a DELETE (ELIMINAR). Haga clic en Send (Enviar) y el
servidor devolverá una respuesta 403 Forbidden.
9.
Use el complemento (plug-in) para verificar que el usuario owner con la contraseña
redhat1! tenga permisos para acceder a los métodos GET, POST y DELETE.
9.1. En el complemento (plug-in) de Firefox, haga clic en Authentication (Autenticación)
y en Basic Authentication (Autenticación básica). Inicie sesión con las siguientes
credenciales:
• Username (Nombre de usuario): owner
• Password (Contraseña): redhat1!
Haga clic en Okay (Aceptar).
9.2. Cambie el Method Type (Tipo de método) a POST y actualice la URL a http://
localhost:8080/securing-lab/api/items/.
9.3. Agregue el siguiente JSON al Body (Cuerpo) de la solicitud para crear un nuevo Item
con la descripción Test owner:
{"description": "Test owner"}
Haga clic en Send (Enviar).
9.4. Cambie el Method Type (Tipo de método) a GET y vuelva a hacer clic en Send (Enviar)
para ver una lista de todos los objetos Item:
[{"id:12, "description":"Test
owner","done":false,"user":"id":3,"username":"owner"}}]
9.5. Mediante el ID de la respuesta anterior, ingrese http://localhost:8080/
securing-lab/api/items/12 en la URL del complemento (plug-in) y cambie el
JB183-EAP7.0-es-2-20180124
411
Capítulo 9. Protección de aplicaciones Java EE
Method Type (Tipo de método) a DELETE (ELIMINAR). Haga clic en Send (Enviar) y el
servidor devolverá una respuesta 204.
9.6. Cambie el Method Type (Tipo de método) a GET, actualice la URL a http://
localhost:8080/securing-lab/api/items/ y vuelva a hacer clic en Send
(Enviar) para ver que el objeto Item se haya eliminado.
10. Use el complemento (plug-in) para verificar que el usuario viewer con la contraseña
redhat1! tenga permisos para acceder solo a los métodos GET.
10.1.En el complemento (plug-in) de Firefox, haga clic en Authentication (Autenticación)
y en Basic Authentication (Autenticación básica). Inicie sesión con las siguientes
credenciales:
• Username (Nombre de usuario): viewer
• Password (Contraseña): redhat1!
Haga clic en Okay (Aceptar).
10.2.Cambie el Method Type (Tipo de método) a POST.
10.3.Agregue el siguiente JSON al Body (Cuerpo) de la solicitud para crear un nuevo Item
con la descripción Test viewer:
{"description": "Test viewer"}
Haga clic en Send (Enviar). El servidor devuelve un código 403 Forbidden.
10.4.Cambie el Method Type (Tipo de método) a GET y haga clic en Send (Enviar) para ver
una respuesta 200 Ok del servidor, aunque la lista esté vacía.
11. Abra una nueva ventana de terminal y ejecute el siguiente comando para calificar el
trabajo de laboratorio:
[student@workstation ~]$ lab securing-lab grade
El script de calificación debe indicar SUCCESS (CORRECTO). Si se produce una falla, revise
los errores y corríjalos hasta que vea un mensaje que indique SUCCESS (CORRECTO).
12. Realice la limpieza.
12.1.Anule la implementación de la aplicación en JBoss EAP con Maven con los siguientes
comandos:
[student@workstation ~]$ cd /home/student/JB183/labs/securing-lab
[student@workstation securing-lab]$ mvn wildfly:undeploy
12.2.Haga clic con el botón derecho en el proyecto securing-lab en el Project Explorer
(Explorador de proyectos) y seleccione Close Project (Cerrar proyecto).
12.3.Haga clic con el botón derecho en el servidor Red Hat JBoss EAP 7.0 en la pestaña
Servers (Servidores) de JBDS y haga clic en Stop (Detener).
412
JB183-EAP7.0-es-2-20180124
Solución
Esto concluye el trabajo de laboratorio.
JB183-EAP7.0-es-2-20180124
413
Capítulo 9. Protección de aplicaciones Java EE
Resumen
En este capítulo, aprendió lo siguiente:
• El Servicio de Autenticación y Autorización de Java (JAAS) es una API de seguridad que se
usa para implementar aplicaciones de autenticación y autorización en Java.
• La seguridad declarativa aprovecha los descriptores y las anotaciones de implementación
para definir el comportamiento de seguridad, mientras que la seguridad programática usa
librerías para gestionar la autenticación y autorización de usuarios.
• web.xml y jboss-web.xml son descriptores de implementación utilizados para definir el
comportamiento de seguridad para una aplicación.
• En EAP, un dominio de seguridad define un conjunto de configuraciones de seguridad
declarativas de JAAS.
• El dominio de seguridad predeterminado other en EAP usa ApplicationRealm, que
almacena información del usuario en los archivos de propiedades applicationusers.properties y application-roles.properties.
• Para usar las anotaciones de seguridad RESTEasy, agregue el parámetro de contexto
resteasy.role.based.security al archivo web.xml de la aplicación.
• RESTEasy proporciona las siguientes anotaciones para proteger un servicio REST con
seguridad declarativa:
◦ @DenyAll
◦ @PermitAll
◦ @RolesAllowed
414
JB183-EAP7.0-es-2-20180124
TRAINING
CAPÍTULO 10
REVISIÓN COMPLETA: RED HAT
APPLICATION DEVELOPMENT I:
PROGRAMMING IN JAVA EE
Descripción general
Meta
Revisar tareas de Red Hat Application Development I:
Programming in Java EE
Objetivos
• Revisar tareas de Red Hat Application Development I:
Programming in Java EE
Secciones
• Revisión completa
Trabajo de laboratorio
• Trabajo de laboratorio: Creación de una API mediante
JAX-RS
• Trabajo de laboratorio: Persistencia de datos con JPA
• Trabajo de laboratorio: Protección de la API REST con
JAAS
JB183-EAP7.0-es-2-20180124
415
Capítulo 10. Revisión completa: Red Hat Application Development I: Programming in Java EE
Revisión completa
Objetivos
Tras finalizar esta sección, los estudiantes deberán ser capaces de revisar y actualizar
las habilidades y los conocimientos aprendidos en Red Hat Application Development I:
Programming in Java EE .
Revisión Red Hat Application Development I:
Programming in Java EE
Antes de comenzar la revisión integral de este curso, los estudiantes deben sentirse cómodos
con los temas que se explicaron en cada capítulo.
Los estudiantes pueden consultar las secciones anteriores en el libro de textos para lecturas
complementarias.
Capítulo 1, Transición a aplicaciones con varios niveles
Describir características de Java EE y distinguir entre las aplicaciones Java EE y Java SE.
• Describir "aplicación empresarial" y nombrar algunos de los beneficios de las aplicaciones
Java EE.
• Comparar las características de Java EE y Java SE.
• Describir las especificaciones y los números de versión de Java EE 7 y el proceso utilizado
para introducir API nuevas y actualizadas en Java EE.
• Describir las diferentes arquitecturas con varios niveles.
• Instalar JBoss Developer Studio, Maven y JBoss Enterprise Application Platform.
Capítulo 2, Empaquetado e implementación de una aplicación de Java EE
Describir la arquitectura de un servidor de aplicaciones Java EE, empaquetar una aplicación e
implementar la aplicación en un servidor EAP.
• Identificar las características clave de los servidores de aplicaciones y describir el servidor
de Java EE.
• Enumerar los tipos de recursos JNDI más comunes y sus convenciones de nomenclatura
típicas.
• Empaquetar una aplicación de Java EE simple e implementarla en JBoss EAP mediante
Maven.
Capítulo 3, Creación de Enterprise Java Beans
Crear Enterprise Java Beans.
• Convertir un POJO en un EJB.
• Acceder a un EJB de manera local y remota.
• Describir el ciclo de vida de los EJB.
• Describir las transacciones gestionadas por contenedor y gestionadas por bean y delimitar
cada una en un EJB.
416
JB183-EAP7.0-es-2-20180124
Revisión Red Hat Application Development I: Programming in Java EE
Capítulo 4, Gestión de la persistencia
Crear entidades de persistencia con validaciones.
• Describir la API de persistencia.
• Conservar datos en un almacén de datos mediante entidades.
• Anotar beans para validar datos.
• Crear una consulta mediante Java Persistence Query Language.
Capítulo 5, Administración de relaciones entre entidades
Definir y administrar relaciones entre entidades JPA.
• Configurar relaciones de una entidad con otra entidad y de una entidad con varias
entidades.
• Describir relaciones de varias entidades con varias entidades.
Capítulo 6, Creación de servicios REST
Crear API REST mediante la especificación de JAX-RS.
• Describir conceptos de servicios web y enumerar tipos de servicios web.
• Crear un servicio REST mediante la especificación de JAX-RS.
• Crear una aplicación cliente que pueda invocar las API REST de manera remota.
Capítulo 7, Implementación de Contextos e Inyección de dependencia (CDI)
Describir casos de uso típicos para utilizar la CDI e implementarla correctamente en una
aplicación.
• Describir la inyección de recursos, la inyección de dependencias y las diferencias entre
ellas.
• Aplicar alcances en beans de manera adecuada.
Capítulo 8, Creación de aplicaciones de mensajería con JMS
Crear clientes de mensajería que envían y reciben mensajes mediante la API de JMS.
• Describir la API de JMS y nombrar a los objetos que se utilizan para enviar y recibir
mensajes.
• Describir los componentes que constituyen la API de JMS.
• Crear un cliente JMS que produce y consume mensajes mediante la API de JMS.
• Crear, comprimir e implementar un bean controlado por mensaje.
Capítulo 9, Protección de aplicaciones Java EE
Proteger una aplicación de Java EE con JAAS.
• Describir la especificación de JAAS.
• Configurar un dominio de seguridad en el servidor de aplicaciones de JBoss EAP.
• Proteger una API REST con autenticación y autorización basada en roles.
JB183-EAP7.0-es-2-20180124
417
Capítulo 10. Revisión completa: Red Hat Application Development I: Programming in Java EE
Trabajo de laboratorio: Creación de una API
mediante JAX-RS
En esta revisión, creará una API REST mediante JAX-RS que inyecta un EJB con código
marcador de posición para las operaciones de creación, lectura, actualización y eliminación
(CRUD).
Resultados
Usted deberá ser capaz de realizar lo siguiente:
• Crear un EJB que esté disponible para inyección.
• Exponer una API REST que consuma y produzca datos JSON mediante anotaciones JAX-RS.
• Inyectar el EJB que se creó para proporcionar lógica de negocio requerida por la API REST.
Antes de comenzar
Si no restablece workstation y services al final del último capítulo, guarde el trabajo que
desea mantener de ejercicios anteriores de esas máquinas y hágalo ahora.
Configure sus computadoras para este ejercicio iniciando sesión en workstation como
student y ejecutando el siguiente comando:
[student@workstation ~]$ lab jaxrs-review setup
Instrucciones
1. Abra JBDS e importe la estructura del proyecto jaxrs-review. Revise el código
existente y observe lo siguiente:
• El objeto modelo Employee en el paquete com.redhat.training.model representa
un registro de empleado básico con atributos para un id y un nombre.
• La clase de EJB EmployeeLogger en el paquete com.redhat.training.util
brinda un método de utilidades logAction(Employee employee, Operation
operation) para registrar acciones que se toman en el registro de un empleado en
un archivo de registro especial.
2.
Cree un EJB para proporcionar métodos con código auxiliar (stub) que implementen las
operaciones CRUD para un objeto Employee. En este ejercicio, estos métodos no usan
una persistencia real. El EJB debe cumplir con los siguientes requisitos:
• El nombre de la clase de EJB es EmployeeBean
• El EJB EmployeeBean no tiene estado y está disponible para inyección.
• El EJB EmployeeBean inyecta una instancia del EJB EmployeeLogger.
• La clase de EJB EmployeeBean implementa cuatro métodos:
◦ createEmployee(Employee e)
◦ readEmployeeById(Long id)
◦ updateEmployee(Employee e)
418
JB183-EAP7.0-es-2-20180124
◦ deleteEmployee(Employee e)
• Cada uno de los métodos del EJB EmployeeBean es solo un código auxiliar (stub) que
aún no contiene funcionalidad de persistencia real. En cambio, cada método usa la
clase de utilidades EmployeeLogger inyectada para imprimir un mensaje que simule
su operación.
• El código auxiliar (stub) del método createEmployee invoca el método logAction
en la clase EmployeeLogger y envía el objeto del empleado que se crea y un valor de
operación de CREATE.
• El código auxiliar (stub) del método readEmployeeById crea un nuevo objeto de
empleado con el nombre Example Employee y el ID que se envió al método. La
creación de este objeto debe suceder antes de que se invoque a EmployeeLogger.
El método debe invocar el método logAction y enviar este objeto de empleado y un
valor de operación de READ. Por último, el método debe devolver el objeto Employee
que se creó.
• El código auxiliar (stub) del método updateEmployee invoca el método logAction
en la clase EmployeeLogger y envía el objeto del empleado que se actualiza y un
valor de operación de UPDATE.
• El código auxiliar (stub) del método deleteEmployee invoca el método logAction
en la clase EmployeeLogger; además, envía el objeto del empleado que se elimina y
un valor de operación de DELETE.
3.
Active JAX-RS para permitir que los servicios REST se implementen como parte de la
aplicación jaxrs-review. Debe acceder a todos los servicios REST que se implementan
en la ruta /api. Esto significa que la URL completa para acceder a los servicios REST
implementados en esta aplicación coincide con la siguiente, donde service_path es
específico para cada servicio REST:
http://localhost:8080/jaxrs-review/api/service_path
4.
Cree una clase que implemente un servicio REST que inyecte el EJB, y que utilice sus
métodos CRUD para brindar una API REST para administrar los datos del empleado. El
servicio REST debe cumplir con los siguientes requisitos:
• El nombre de la clase de servicio REST es EmployeeRestService
• El servicio REST es un EJB sin estado.
• La ruta relativa para este servicio REST es employees
• La API REST produce y consume datos JSON.
• La clase EmployeeRestService inyecta una instancia del EmployeeBean nombrado
employeeBean, que utiliza para implementar la lógica de negocio de estos tres
métodos API REST.
• La API REST que el servicio EmployeeRestService proporciona incluye tres métodos,
cada uno asociado con un método HTTP diferente. Estos se resume en la siguiente
tabla:
JB183-EAP7.0-es-2-20180124
419
Capítulo 10. Revisión completa: Red Hat Application Development I: Programming in Java EE
Resumen del método EmployeeRestService
Firma del método
Método HTTP
public Employee getEmployee(Long id)
GET
public void deleteEmployee(Long id)
DELETE
public Response saveEmployee(Employee employee)
POST
• El método getEmployee devuelve datos JSON para el objeto Employee que
tiene un valor de ID específico. Este método invoca directamente el método
readEmployeeById en la instancia EmployeeBean, enviando el ID del empleado y
devolviendo el resultado. El ID del empleado se asigna automáticamente mediante un
parámetro de ruta.
Por ejemplo, la URL para obtener al empleado con un valor de ID de 1:
http://localhost:8080/jaxrs-review/api/employees/1
• El método deleteEmployee no devuelve datos y utiliza el método deleteEmployee
en EmployeeBean para eliminar al empleado con un valor de ID determinado. El ID
del empleado se asigna automáticamente mediante un parámetro de ruta. El método
debe usar el método readEmployeeById en el EJB EmployeeBean para recuperar
el registro de empleado por su ID y, luego, invocar el método deleteEmployee para
eliminar el registro del empleado recuperado.
• El método saveEmployee puede crear un nuevo empleado o actualizar uno existente.
Este método consume una representación JSON de un objeto Employee, que se
asigna automáticamente a uno de los parámetros del método. Debe devolver
un objeto javax.ws.rs.core.Response. Después de definir el método
y las anotaciones necesarias, use el fragmento de código ubicado en
saveEmployee.txt para implementar la lógica del método.
• Observe que el método saveEmployee invoca createEmployee o updateEmployee,
según si un registro Employee tiene o no un valor de ID. Si se especifica un ID, el
método readEmployeeByID se utiliza para verificar que un empleado con ese ID ya
exista; de lo contrario, se produce un error en el servidor.
5.
Inicie JBoss EAP y utilice Maven para implementar la aplicación jaxrs-review.
6.
Pruebe el servicio REST y todos sus métodos mediante el complemento (plug-in) de
cliente REST de Firefox.
• Especifique un encabezado personalizado con el nombre Content-Type y el valor
application/json.
• Use HTTP GET para probar el método getEmployee y especificar cualquier valor de
ID. Revise el archivo EmployeeLog.txt o los registros de servidor JBoss EAP para
asegurarse de que el servicio REST y EJB funcionen de la manera esperada.
• Use HTTP DELETE para probar el método deleteEmployee y especificar cualquier
valor de ID. Revise el archivo EmployeeLog.txt o los registros de servidor JBoss EAP
para asegurarse de que el servicio REST y EJB funcionen de la manera esperada.
420
JB183-EAP7.0-es-2-20180124
• Use HTTP POST e incluya el contenido del archivo Employee.json para probar el
método saveEmployee. Estos datos incluyen un ID, por lo que desencadena una
operación de lectura de empleado y, luego, una actualización. Revise el archivo
EmployeeLog.txt o los registros de servidor JBoss EAP para asegurarse de que el
servicio REST y EJB funcionen de la manera esperada.
• Elimine el valor de ID de los datos JSON y, luego, envíe otro HTTP POST para probar el
método saveEmployee. Esto desencadena una operación de creación de empleado.
Revise el archivo EmployeeLog.txt o los registros de servidor JBoss EAP para
asegurarse de que el servicio REST y EJB funcionen de la manera esperada.
Evaluación
Como el usuario student en workstation, ejecute el script lab jaxrs-review con el
argumento grade para confirmar que ha realizado este ejercicio correctamente. Corrija las
fallas informadas y vuelva a ejecutar el script hasta obtener un resultado satisfactorio.
[student@workstation ~]$ lab jaxrs-review grade
Después de que la calificación se realiza correctamente, anule la implementación del
proyecto, detenga el servidor EAP y cierre el proyecto en JBDS.
Esto concluye el trabajo de laboratorio.
JB183-EAP7.0-es-2-20180124
421
Capítulo 10. Revisión completa: Red Hat Application Development I: Programming in Java EE
Solución
En esta revisión, creará una API REST mediante JAX-RS que inyecta un EJB con código
marcador de posición para las operaciones de creación, lectura, actualización y eliminación
(CRUD).
Resultados
Usted deberá ser capaz de realizar lo siguiente:
• Crear un EJB que esté disponible para inyección.
• Exponer una API REST que consuma y produzca datos JSON mediante anotaciones JAX-RS.
• Inyectar el EJB que se creó para proporcionar lógica de negocio requerida por la API REST.
Antes de comenzar
Si no restablece workstation y services al final del último capítulo, guarde el trabajo que
desea mantener de ejercicios anteriores de esas máquinas y hágalo ahora.
Configure sus computadoras para este ejercicio iniciando sesión en workstation como
student y ejecutando el siguiente comando:
[student@workstation ~]$ lab jaxrs-review setup
Instrucciones
1. Abra JBDS e importe la estructura del proyecto jaxrs-review. Revise el código
existente y observe lo siguiente:
• El objeto modelo Employee en el paquete com.redhat.training.model representa
un registro de empleado básico con atributos para un id y un nombre.
• La clase de EJB EmployeeLogger en el paquete com.redhat.training.util
brinda un método de utilidades logAction(Employee employee, Operation
operation) para registrar acciones que se toman en el registro de un empleado en
un archivo de registro especial.
2.
Cree un EJB para proporcionar métodos con código auxiliar (stub) que implementen las
operaciones CRUD para un objeto Employee. En este ejercicio, estos métodos no usan
una persistencia real. El EJB debe cumplir con los siguientes requisitos:
• El nombre de la clase de EJB es EmployeeBean
• El EJB EmployeeBean no tiene estado y está disponible para inyección.
• El EJB EmployeeBean inyecta una instancia del EJB EmployeeLogger.
• La clase de EJB EmployeeBean implementa cuatro métodos:
◦ createEmployee(Employee e)
◦ readEmployeeById(Long id)
◦ updateEmployee(Employee e)
◦ deleteEmployee(Employee e)
• Cada uno de los métodos del EJB EmployeeBean es solo un código auxiliar (stub) que
aún no contiene funcionalidad de persistencia real. En cambio, cada método usa la
422
JB183-EAP7.0-es-2-20180124
Solución
clase de utilidades EmployeeLogger inyectada para imprimir un mensaje que simule
su operación.
• El código auxiliar (stub) del método createEmployee invoca el método logAction
en la clase EmployeeLogger y envía el objeto del empleado que se crea y un valor de
operación de CREATE.
• El código auxiliar (stub) del método readEmployeeById crea un nuevo objeto de
empleado con el nombre Example Employee y el ID que se envió al método. La
creación de este objeto debe suceder antes de que se invoque a EmployeeLogger.
El método debe invocar el método logAction y enviar este objeto de empleado y un
valor de operación de READ. Por último, el método debe devolver el objeto Employee
que se creó.
• El código auxiliar (stub) del método updateEmployee invoca el método logAction
en la clase EmployeeLogger y envía el objeto del empleado que se actualiza y un
valor de operación de UPDATE.
• El código auxiliar (stub) del método deleteEmployee invoca el método logAction
en la clase EmployeeLogger; además, envía el objeto del empleado que se elimina y
un valor de operación de DELETE.
3.
Active JAX-RS para permitir que los servicios REST se implementen como parte de la
aplicación jaxrs-review. Debe acceder a todos los servicios REST que se implementan
en la ruta /api. Esto significa que la URL completa para acceder a los servicios REST
implementados en esta aplicación coincide con la siguiente, donde service_path es
específico para cada servicio REST:
http://localhost:8080/jaxrs-review/api/service_path
4.
Cree una clase que implemente un servicio REST que inyecte el EJB, y que utilice sus
métodos CRUD para brindar una API REST para administrar los datos del empleado. El
servicio REST debe cumplir con los siguientes requisitos:
• El nombre de la clase de servicio REST es EmployeeRestService
• El servicio REST es un EJB sin estado.
• La ruta relativa para este servicio REST es employees
• La API REST produce y consume datos JSON.
• La clase EmployeeRestService inyecta una instancia del EmployeeBean nombrado
employeeBean, que utiliza para implementar la lógica de negocio de estos tres
métodos API REST.
• La API REST que el servicio EmployeeRestService proporciona incluye tres métodos,
cada uno asociado con un método HTTP diferente. Estos se resume en la siguiente
tabla:
Resumen del método EmployeeRestService
Firma del método
Método HTTP
public Employee getEmployee(Long id)
GET
JB183-EAP7.0-es-2-20180124
423
Capítulo 10. Revisión completa: Red Hat Application Development I: Programming in Java EE
Firma del método
Método HTTP
public void deleteEmployee(Long id)
DELETE
public Response saveEmployee(Employee employee)
POST
• El método getEmployee devuelve datos JSON para el objeto Employee que
tiene un valor de ID específico. Este método invoca directamente el método
readEmployeeById en la instancia EmployeeBean, enviando el ID del empleado y
devolviendo el resultado. El ID del empleado se asigna automáticamente mediante un
parámetro de ruta.
Por ejemplo, la URL para obtener al empleado con un valor de ID de 1:
http://localhost:8080/jaxrs-review/api/employees/1
• El método deleteEmployee no devuelve datos y utiliza el método deleteEmployee
en EmployeeBean para eliminar al empleado con un valor de ID determinado. El ID
del empleado se asigna automáticamente mediante un parámetro de ruta. El método
debe usar el método readEmployeeById en el EJB EmployeeBean para recuperar
el registro de empleado por su ID y, luego, invocar el método deleteEmployee para
eliminar el registro del empleado recuperado.
• El método saveEmployee puede crear un nuevo empleado o actualizar uno existente.
Este método consume una representación JSON de un objeto Employee, que se
asigna automáticamente a uno de los parámetros del método. Debe devolver
un objeto javax.ws.rs.core.Response. Después de definir el método
y las anotaciones necesarias, use el fragmento de código ubicado en
saveEmployee.txt para implementar la lógica del método.
• Observe que el método saveEmployee invoca createEmployee o updateEmployee,
según si un registro Employee tiene o no un valor de ID. Si se especifica un ID, el
método readEmployeeByID se utiliza para verificar que un empleado con ese ID ya
exista; de lo contrario, se produce un error en el servidor.
5.
Inicie JBoss EAP y utilice Maven para implementar la aplicación jaxrs-review.
6.
Pruebe el servicio REST y todos sus métodos mediante el complemento (plug-in) de
cliente REST de Firefox.
• Especifique un encabezado personalizado con el nombre Content-Type y el valor
application/json.
• Use HTTP GET para probar el método getEmployee y especificar cualquier valor de
ID. Revise el archivo EmployeeLog.txt o los registros de servidor JBoss EAP para
asegurarse de que el servicio REST y EJB funcionen de la manera esperada.
• Use HTTP DELETE para probar el método deleteEmployee y especificar cualquier
valor de ID. Revise el archivo EmployeeLog.txt o los registros de servidor JBoss EAP
para asegurarse de que el servicio REST y EJB funcionen de la manera esperada.
• Use HTTP POST e incluya el contenido del archivo Employee.json para probar el
método saveEmployee. Estos datos incluyen un ID, por lo que desencadena una
operación de lectura de empleado y, luego, una actualización. Revise el archivo
424
JB183-EAP7.0-es-2-20180124
Solución
EmployeeLog.txt o los registros de servidor JBoss EAP para asegurarse de que el
servicio REST y EJB funcionen de la manera esperada.
• Elimine el valor de ID de los datos JSON y, luego, envíe otro HTTP POST para probar el
método saveEmployee. Esto desencadena una operación de creación de empleado.
Revise el archivo EmployeeLog.txt o los registros de servidor JBoss EAP para
asegurarse de que el servicio REST y EJB funcionen de la manera esperada.
Pasos
1. Abra JBDS e importe el proyecto de Maven.
1.1. Haga doble clic en el icono de JBoss Developer Studio en el escritorio de la estación
de trabajo para abrir JBDS. Seleccione el espacio de trabajo /home/student/
JB183/workspace y haga clic en OK (Aceptar).
1.2. En el menú de JBDS, haga clic en File (Archivo) > Import (Importar)para abrir el
asistente para Import (Importar).
1.3. En la página Select (Seleccionar), haga clic en Maven > Existing Maven Projects
(Proyectos de Maven existentes) y, luego, haga clic en Next (Siguiente).
1.4. En la página Maven projects (Proyectos de Maven), haga clic en Browse (Explorar)
para abrir la ventana Select root folder (Seleccionar carpeta raíz). Diríjase al
directorio /home/student/JB183/labs/. Seleccione la carpeta jaxrs-review y
haga clic en OK (Aceptar).
1.5. En la página Maven projects (Proyectos de Maven), haga clic en Finish (Finalizar).
1.6. Observe la barra de estado de JBDS para monitorear el progreso de la operación
de importación. Es probable que la descarga de todas las dependencias requeridas
demore unos minutos.
2.
Cree un EJB para proporcionar métodos con código auxiliar (stub) para las operaciones
CRUD de un objeto Employee.
2.1. Haga clic con el botón derecho en com.redhat.training.ejb y haga clic en New
(Nueva) > Class (Clase).
2.2. En el campo Name (Nombre), ingrese EmployeeBean. Haga clic en Finish (Finalizar).
2.3. Convierta la clase EmployeeBean en un EJB sin estado disponible para inyección;
para ello, agregue la anotación @Stateless en el nivel de la clase:
import javax.ejb.Stateless;
@Stateless
public class EmployeeBean {
2.4. Inyecte una instancia del EJB EmployeeLogger para utilizar en los códigos auxiliares
(stubs) del método:
import javax.ejb.Stateless;
import javax.inject.Inject;
import com.redhat.training.util.EmployeeLogger;
JB183-EAP7.0-es-2-20180124
425
Capítulo 10. Revisión completa: Red Hat Application Development I: Programming in Java EE
@Stateless
public class EmployeeBean {
@Inject
private EmployeeLogger logger;
2.5. Implemente el código auxiliar (stub) del método createEmployee(Employee e),
que invoca el método logAction con un valor de operación de Create:
import
import
import
import
import
javax.ejb.Stateless;
javax.inject.Inject;
com.redhat.training.util.EmployeeLogger;
com.redhat.training.model.Employee;
com.redhat.training.util.EmployeeLogger.Operation;
@Stateless
public class EmployeeBean {
@Inject
private EmployeeLogger logger;
public void createEmployee(Employee e) {
logger.logAction(e, Operation.Create);
}
2.6. Implemente el código auxiliar (stub) del método readEmployeeById(Long id),
que invoca el método logAction con un valor de operación de Read:
...
@Stateless
public class EmployeeBean {
@Inject
private EmployeeLogger logger;
public void createEmployee(Employee e) {
logger.logAction(e, Operation.Create);
}
public Employee readEmployeeById(Long id) {
Employee sample = new Employee();
sample.setId(id);
sample.setName("Example Employee");
logger.logAction(sample, Operation.Read);
return sample;
}
}
2.7. Implemente el código auxiliar (stub) del método updateEmployee(Employee e),
que invoca el método logAction con un valor de operación de Update:
...
@Stateless
public class EmployeeBean {
@Inject
426
JB183-EAP7.0-es-2-20180124
Solución
private EmployeeLogger logger;
public void createEmployee(Employee e) {
logger.logAction(e, Operation.Create);
}
public Employee readEmployeeById(Long id) {
Employee sample = new Employee();
sample.setId(id);
sample.setName("Example Employee");
logger.logAction(sample, Operation.Read);
return sample;
}
}
public void updateEmployee(Employee e) {
logger.logAction(e, Operation.Update);
}
2.8. Implemente el código auxiliar (stub) del método deleteEmployee(Employee e),
que invoca el método logAction con un valor de operación de Delete:
...
@Stateless
public class EmployeeBean {
@Inject
private EmployeeLogger logger;
public void createEmployee(Employee e) {
logger.logAction(e, Operation.Create);
}
public Employee readEmployeeById(Long id) {
Employee sample = new Employee();
sample.setId(id);
sample.setName("Example Employee");
logger.logAction(sample, Operation.Read);
return sample;
}
}
public void updateEmployee(Employee e) {
logger.logAction(e, Operation.Update);
}
public void deleteEmployee(Employee e) {
logger.logAction(e, Operation.Delete);
}
2.9. Guarde los cambios en el archivo con Ctrl+S.
3.
Active JAX-RS al crear una nueva clase de aplicación y definir la ruta de la aplicación como
/api.
3.1. Haga clic con el botón derecho en com.redhat.training.rest y haga clic en New
(Nueva) > Class (Clase).
3.2. En el campo Name (Nombre) ingrese Service. Haga clic en Finish (Finalizar).
JB183-EAP7.0-es-2-20180124
427
Capítulo 10. Revisión completa: Red Hat Application Development I: Programming in Java EE
3.3. Haga que la nueva clase Service amplíe la superclase
javax.ws.rs.core.Application:
import javax.ws.rs.core.Application;
public class Service extends Application {
}
3.4. Defina la ruta de la aplicación como /api mediante la anotación en el nivel de la
clase @ApplicationPath:
import javax.ws.rs.core.Application;
import javax.ws.rs.ApplicationPath;
@ApplicationPath("/api")
public class Service extends Application {
}
3.5. Guarde los cambios en el archivo con Ctrl+S.
4.
Cree el nuevo servicio REST que inyecta el EJB y proporciona tres métodos API.
4.1. Haga clic con el botón derecho en com.redhat.training.rest y haga clic en New
(Nueva) > Class (Clase).
4.2. En el campo Name (Nombre) ingrese EmployeeRestService. Haga clic en Finish
(Finalizar).
4.3. Convierta la clase EmployeeRestService en un EJB sin estado:
import javax.ejb.Stateless;
@Stateless
public class EmployeeRestService {
4.4. Agregue las anotaciones necesarias en el nivel de la clase para definir la ruta relativa
de este servicio en employees e indique que produce y consume JSON:
import
import
import
import
import
javax.ejb.Stateless;
javax.ws.rs.Consumes;
javax.ws.rs.Produces;
javax.ws.rs.Path;
javax.ws.rs.core.MediaType;
@Stateless
@Path("employees")
@Consumes(MediaType.APPLICATION_JSON)
@Produces(MediaType.APPLICATION_JSON)
public class EmployeeRestService {
4.5. Inyecte EmployeeBean para poder ser utilizado por los métodos REST:
428
JB183-EAP7.0-es-2-20180124
Solución
import
import
import
import
import
import
import
javax.ejb.Stateless;
javax.ws.rs.Consumes;
javax.ws.rs.Path;
javax.ws.rs.Produces;
javax.ws.rs.core.MediaType;
javax.inject.Inject;
com.redhat.training.ejb.EmployeeBean;
@Stateless
@Path("employees")
@Consumes(MediaType.APPLICATION_JSON)
@Produces(MediaType.APPLICATION_JSON)
public class EmployeeRestService {
@Inject
private EmployeeBean employeeBean;
4.6. Implemente el método getEmployee(Long id), que está asignado al método
HTTP GET y utiliza un parámetro de ruta para asignar el valor del ID. El método
invoca el método readEmployeeById en el EJB EmployeeBean directamente:
import
import
import
import
import
import
import
import
import
import
javax.ejb.Stateless;
javax.ws.rs.Consumes;
javax.ws.rs.Path;
javax.ws.rs.Produces;
javax.ws.rs.core.MediaType;
javax.inject.Inject;
com.redhat.training.ejb.EmployeeBean;
com.redhat.training.model.Employee;
javax.ws.rs.GET;
javax.ws.rs.PathParam;
@Stateless
@Path("employees")
@Consumes(MediaType.APPLICATION_JSON)
@Produces(MediaType.APPLICATION_JSON)
public class EmployeeRestService {
@Inject
private EmployeeBean employeeBean;
@GET
@Path("{id}")
@Produces(MediaType.APPLICATION_JSON)
public Employee getEmployee(@PathParam("id") Long id) {
return employeeBean.readEmployeeById(id);
}
4.7. Implemente el método deleteEmployee(Long id), que está asignado al método
HTTP DELETE y utiliza un parámetro de ruta para asignar el valor del ID. El método
invoca el método deleteEmployee en el EJB EmployeeBean directamente:
import
import
import
import
import
import
javax.ejb.Stateless;
javax.ws.rs.Consumes;
javax.ws.rs.Path;
javax.ws.rs.Produces;
javax.ws.rs.core.MediaType;
javax.inject.Inject;
JB183-EAP7.0-es-2-20180124
429
Capítulo 10. Revisión completa: Red Hat Application Development I: Programming in Java EE
import
import
import
import
import
com.redhat.training.ejb.EmployeeBean;
com.redhat.training.model.Employee;
javax.ws.rs.GET;
javax.ws.rs.PathParam;
javax.ws.rs.DELETE;
@Stateless
@Path("employees")
@Consumes(MediaType.APPLICATION_JSON)
@Produces(MediaType.APPLICATION_JSON)
public class EmployeeRestService {
@Inject
private EmployeeBean employeeBean;
@GET
@Path("{id}")
@Produces(MediaType.APPLICATION_JSON)
public Employee getEmployee(@PathParam("id") Long id) {
return employeeBean.readEmployeeById(id);
}
@DELETE
@Path("{id}")
public void deletePerson(@PathParam("id") Long id) {
Employee toBeDeleted = employeeBean.readEmployeeById(id);
employeeBean.deleteEmployee(toBeDeleted);
}
4.8. Implemente la firma del método saveEmployee(Employee employee), que está
asignada al método HTTP POST. Este método consume datos JSON del registro de un
empleado y devuelve un objeto Response.
import
import
import
import
import
import
import
import
import
import
import
import
import
javax.ejb.Stateless;
javax.ws.rs.Consumes;
javax.ws.rs.Path;
javax.ws.rs.Produces;
javax.ws.rs.core.MediaType;
javax.inject.Inject;
com.redhat.training.ejb.EmployeeBean;
com.redhat.training.model.Employee;
javax.ws.rs.GET;
javax.ws.rs.PathParam;
javax.ws.rs.DELETE;
javax.ws.rs.POST;
javax.ws.rs.core.Response;
@Stateless
@Path("employees")
@Consumes(MediaType.APPLICATION_JSON)
@Produces(MediaType.APPLICATION_JSON)
public class EmployeeRestService {
@Inject
private EmployeeBean employeeBean;
@GET
@Path("{id}")
@Produces(MediaType.APPLICATION_JSON)
public Employee getEmployee(@PathParam("id") Long id) {
430
JB183-EAP7.0-es-2-20180124
Solución
return employeeBean.readEmployeeById(id);
}
@DELETE
@Path("{id}")
public void deletePerson(@PathParam("id") Long id) {
Employee toBeDeleted = employeeBean.readEmployeeById(id);
employeeBean.deleteEmployee(toBeDeleted);
}
@POST
@Consumes(MediaType.APPLICATION_JSON)
@Produces(MediaType.APPLICATION_JSON)
public Response savePerson(Employee employee) {
}
4.9. Copie el contenido de /home/student/JB183/labs/jaxrs-review/
saveEmployee.txt y péguelo en el contenido del método. Agregue una
importación para ResponseBuilder:
import javax.ws.rs.core.Response.ResponseBuilder;
...Imports omitted...
@Stateless
@Path("employees")
@Consumes(MediaType.APPLICATION_JSON)
@Produces(MediaType.APPLICATION_JSON)
public class EmployeeRestService {
@Inject
private EmployeeBean employeeBean;
...Methods omitted...
@POST
@Consumes(MediaType.APPLICATION_JSON)
@Produces(MediaType.APPLICATION_JSON)
public Response savePerson(Employee employee) {
ResponseBuilder builder;
if (employee.getId() == null) {
Employee newEmployee = new Employee();
newEmployee.setName(employee.getName());
employeeBean.createEmployee(newEmployee);
builder = Response.ok();
} else {
Employee employeeToUpdate = employeeBean.readEmployeeById(employee.getId());
if (employeeToUpdate == null) {
builder = Response.serverError();
} else {
employeeBean.updateEmployee(employee);
builder = Response.ok();
}
}
JB183-EAP7.0-es-2-20180124
431
Capítulo 10. Revisión completa: Red Hat Application Development I: Programming in Java EE
return builder.build();
}
4.10.Guarde los cambios en el archivo con Ctrl+S.
5.
Seleccione la pestaña Servers (Servidores) en el panel inferior de JBDS para iniciar EAP.
Haga clic con el botón derecho en el servidor Red Hat JBoss EAP 7.0 [Stopped] y haga clic
en el botón verde de inicio para iniciar el servidor.
6.
Implemente la aplicación jaxrs-review mediante los siguientes comandos en la ventana
del terminal:
[student@workstation ~]$ cd /home/student/JB183/labs/jaxrs-review
[student@workstation jaxrs-review]$ mvn wildfly:deploy
7.
Lea un registro de Employee mediante el complemento (plug-in) de cliente REST de
Firefox al realizar una solicitud GET a http://localhost:8080/jaxrs-review/api/
employees
7.1. Inicie Firefox en la máquina virtual workstation y haga clic en el icono del
complemento (plug-in) de cliente REST en la barra de herramientas del explorador.
Figura Error.1: Complemento (plug-in) de cliente REST de Firefox
7.2. En la barra de herramientas superior, haga clic en Headers (Encabezados) y en
Custom Header (Encabezado personalizado) para agregar un nuevo encabezado
personalizado a la solicitud.
7.3. Introduzca la siguiente información en el cuadro de diálogo del encabezado
personalizado:
• Name (Nombre): Content-Type
• Value (Valor): application/json
432
JB183-EAP7.0-es-2-20180124
Solución
Figura Error.2: Creación de un encabezado de solicitud personalizado en el cliente REST
Haga clic en Okay (Aceptar).
7.4. Seleccione GET como el Method (Método). En el formulario de URL, introduzca
http://localhost:8080/jaxrs-review/api/employees/1.
7.5. Verifique en la pestaña Response Headers (Encabezados de respuesta) que Status
Code (Código de estado) sea 200 OK.
7.6. Revise /home/student/JB183/labs/jaxrs-review/EmployeeLog.txt y
asegúrese de ver el siguiente mensaje:
Read Employee: Employee [id=1, name=Example Employee]
8.
Elimine un registro de Employee mediante el complemento (plug-in) de cliente REST de
Firefox al realizar una solicitud DELETE a http://localhost:8080/jaxrs-review/
api/employees/1.
8.1. Seleccione DELETE como el Method (Método). En el formulario de URL, introduzca
http://localhost:8080/jaxrs-review/api/employees/1.
8.2. Haga clic en Send (Enviar).
8.3. Verifique en la pestaña Response Headers (Encabezados de respuesta) que Status
Code (Código de estado) sea 204 No Content (204 Sin contenido). Esto es lo
previsto ya que el método deleteEmployee tiene un tipo de devolución void (nulo),
por lo que no se devuelve contenido al cliente REST.
8.4. Revise /home/student/JB183/labs/jaxrs-review/EmployeeLog.txt y
asegúrese de ver el siguiente mensaje:
Read Employee: Employee [id=1, name=Example Employee]
Delete Employee: Employee [id=1, name=Example Employee]
9.
Actualice un registro Employee mediante el complemento (plug-in) de cliente REST de
Firefox al realizar una solicitud POST a http://localhost:8080/jaxrs-review/api/
JB183-EAP7.0-es-2-20180124
433
Capítulo 10. Revisión completa: Red Hat Application Development I: Programming in Java EE
employees, enviando datos JSON del registro de un empleado con un ID en el cuerpo de
la solicitud.
9.1. Seleccione POST como el Method (Método). En el formulario de URL, introduzca
http://localhost:8080/jaxrs-review/api/employees/.
9.2. En la sección Body (Cuerpo) de la solicitud, agregue la siguiente representación JSON
de una entidad Employee:
{"id":1,"name":"Example Employee2"}
Haga clic en Send (Enviar).
9.3. Verifique en la pestaña Response Headers (Encabezados de respuesta) que Status
Code (Código de estado) sea 200 OK.
9.4. Revise /home/student/JB183/labs/jaxrs-review/EmployeeLog.txt y
asegúrese de ver el siguiente mensaje:
Read Employee: Employee [id=1, name=Example Employee2]
Update Employee: Employee [id=1, name=Example Employee2]
10. Cree un registro Employee mediante el complemento (plug-in) de cliente REST de Firefox
al realizar una solicitud POST a http://localhost:8080/jaxrs-review/api/
employees, enviando datos JSON del registro de un empleado sin un ID en el cuerpo de
la solicitud.
10.1.Seleccione POST como el Method (Método). En el formulario de URL, introduzca
http://localhost:8080/jaxrs-review/api/employees/.
10.2.En la sección Body (Cuerpo) de la solicitud, agregue la siguiente representación JSON
de una entidad Employee:
{"name":"Example Employee3"}
Haga clic en Send (Enviar).
10.3.Verifique en la pestaña Response Headers (Encabezados de respuesta) que Status
Code (Código de estado) sea 200 OK.
10.4.Revise /home/student/JB183/labs/jaxrs-review/EmployeeLog.txt y
asegúrese de ver el siguiente mensaje:
Create Employee: Employee [id=null, name=Example Employee3]
Evaluación
Como el usuario student en workstation, ejecute el script lab jaxrs-review con el
argumento grade para confirmar que ha realizado este ejercicio correctamente. Corrija las
fallas informadas y vuelva a ejecutar el script hasta obtener un resultado satisfactorio.
[student@workstation ~]$ lab jaxrs-review grade
434
JB183-EAP7.0-es-2-20180124
Solución
Después de que la calificación se realiza correctamente, anule la implementación del
proyecto, detenga el servidor EAP y cierre el proyecto en JBDS.
Esto concluye el trabajo de laboratorio.
JB183-EAP7.0-es-2-20180124
435
Capítulo 10. Revisión completa: Red Hat Application Development I: Programming in Java EE
Trabajo de laboratorio: Persistencia de datos
con JPA
En esta revisión, actualizará la API REST compilada en la revisión anterior para incluir
persistencia.
Resultados
Usted deberá ser capaz de realizar lo siguiente:
• Use JPA para asignar una entidad a una tabla de base de datos.
• Use JPA para asignar relaciones entre entidades.
• Use una consulta nombrada para recuperar registros de la base de datos.
Antes de comenzar
Prepare sus computadoras para este ejercicio iniciando sesión en workstation como
student y ejecutando el siguiente comando:
[student@workstation ~]$ lab jpa-review setup
Instrucciones
1. Abra JBDS e importe la estructura del proyecto jpa-review. Revise el código existente y
observe lo siguiente:
• El archivo src/main/resources/META-INF/persistence.xml define un contexto
de persistencia que se conecta a la fuente de datos MySQL definida en el servidor EAP.
• Se agregaron dos nuevos objetos modelo en el paquete
com.redhat.training.model: Manager (Gerente) y Department (Departamento).
Cada Employee (Empleado) tiene un Department (Departamento) y cada
Department (Departamento) tiene un Manager (Gerente).
2.
Cree una clase de utilidades en el paquete com.redhat.training.util que produce
instancias de EntityManager que utilizan el contexto de persistencia predeterminado.
3.
Actualice la entidad Employee para que esté administrada por JPA, y asigne las
relaciones entre las entidades Employee, Department y Manager mediante anotaciones
de JPA. Asegúrese de cumplir con los siguientes requisitos:
• El campo id es el identificador único de cada registro y se genera automáticamente
por la base de datos cuando se persiste un nuevo registro.
• La tabla Employee en la base de datos usa la columna departmentID para almacenar
la clave externa en la tabla Department.
• La tabla Manager en la base de datos usa la columna departmentID para almacenar
la clave externa en la tabla Department.
• La entidad Department asigna sus relaciones con Employee y Manager como el
objeto secundario en la relación.
436
JB183-EAP7.0-es-2-20180124
• La entidad Department captura de manera diligente la lista de instancias de
Employee a la que está relacionada.
• La entidad Employee incluye una consulta nombrada JPQL, denominada
findAllForManager, que toma el ID del registro de un gerente como su parámetro.
Esta consulta extrae la lista de empleados que se reportan a ese gerente. Esto requiere
unirse al registro department y, luego, unirse al registro manager.
Sugerencia: La instrucción JPQL que verifica el ID de administrador de un empleado es
employee.department.manager.id
• La consulta findAllForManager devuelve objetos Employee y utiliza un parámetro
nombrado denominado managerId para representar el ID de administrador.
4.
Actualice EmployeeBean para admitir la persistencia mediante EntityManager.
Asegúrese de cumplir con los siguientes requisitos:
• Los métodos EJB CRUD asignan los siguientes métodos de administrador de
entidades:
◦ createEmployee: persist(Employee e)
◦ readEmployeeById: find(Class, Object)
◦ updateEmployee: merge(Employee e)
◦ deleteEmployee: remove(Employee e)
• El método findAllForManager utiliza la consulta nombrada findAllForManager
correspondiente. Además, el parámetro managerID de la consulta está definido con el
ID de manager que se envió al método.
5.
Actualice EmployeeRestService para admitir tres nuevos métodos y asegúrese de que
cumplan con los siguientes requisitos:
• getEmployeesForManager(Manager m):
◦ Se asigna a las solicitudes HTTP GET.
◦ Usa la ruta relativa /getByManager/{managerId}.
◦ managerId es un parámetro de ruta asignado al parámetro de método.
◦ Produce datos XML.
◦ Usa el método findById en ManagerBean para buscar al gerente por el valor de su
ID.
◦ Toma el objeto de gerente encontrado y lo envía al método findAllForManager en
EmployeeBean y devuelve el resultado.
• assignEmployee(Long employeeId, Long departmentId):
◦ Se asigna a las solicitudes HTTP POST.
◦ Usa la ruta relativa /assignEmployee/{employeeId}/{departmentId}.
◦ Tanto employeeId como departmentId son parámetros de ruta que se asignan a
los parámetros de método.
JB183-EAP7.0-es-2-20180124
437
Capítulo 10. Revisión completa: Red Hat Application Development I: Programming in Java EE
◦ Usa EmployeeBean para buscar al empleado por ID y, luego, usa DepartmentBean
para buscar al departamento por ID. Por último, actualiza el registro
del departamento en el objeto del empleado y, luego, invoca el método
updateEmployee en EmployeeBean para persistir los cambios.
• assignManager(Long managerId, Long departmentId)
◦ Se asigna a las solicitudes HTTP POST.
◦ Usa la ruta relativa /assignManager/{managerId}/{departmentId}.
◦ Tanto managerId como departmentId son parámetros de ruta que se asignan a
los parámetros de método.
◦ Usa ManagerBean para buscar al gerente por ID y, luego, usa DepartmentBean
para buscar al departamento por ID. Por último, actualiza el registro del
departamento en el objeto del gerente y, luego, invoca el método updateManager
en ManagerBean para persistir los cambios.
6.
Inicie JBoss EAP y utilice Maven para implementar la aplicación jpa-review.
7.
Pruebe el servicio REST y los tres nuevos métodos mediante el complemento (plug-in) de
cliente REST de Firefox.
Si desea restablecer la base de datos durante las pruebas, ejecute el script /home/student/
JB183/labs/jpa-review/reset-database.sh proporcionado.
• Especifique un encabezado personalizado con el nombre Content-Type y el valor
application/json.
• Use HTTP GET para enviar una solicitud al extremo http://localhost:8080/
jpa-review/api/employees/getByManager/1 para probar el método
getEmployeesForManager y recuperar los empleados para el gerente con un valor
de id de 1.
Revise ResponseBody para ver los datos XML para los empleados en el departamento
administrado por Bob, que es el departamento de Sales (Ventas):
<collection>
<employee>
<department>
<id>1</id>
<manager>
<id>1</id>
<name>Bob</name>
</manager>
<name>Sales</name>
</department>
<id>1</id>
<name>William</name>
</employee>
<employee>
<department>
<id>1</id>
<manager>
<id>1</id>
<name>Bob</name>
</manager>
438
JB183-EAP7.0-es-2-20180124
<name>Sales</name>
</department>
<id>2</id>
<name>Rose</name>
</employee>
<employee>
<department>
<id>1</id>
<manager>
<id>1</id>
<name>Bob</name>
</manager>
<name>Sales</name>
</department>
<id>3</id>
<name>Pat</name>
</employee>
</collection>
• Use HTTP POST para enviar una solicitud al extremo http://localhost:8080/jpareview/api/employees/assignEmployee/1/2 para asignar al empleado con un
valor de ID de 1 al departamento con un valor de ID de 2.
• Use HTTP POST para enviar una solicitud a http://localhost:8080/jpareview/api/employees/assignManager/2/1 para asignar un nuevo Manager al
departamento de ventas.
• Use HTTP POST para enviar una solicitud a http://localhost:8080/jpa-review/
api/employees/assignManager/1/2 para asignar a Bob a un nuevo departamento
con un valor de ID de 2.
• Use HTTP GET para enviar una solicitud al extremo http://localhost:8080/
jpa-review/api/employees/getByManager/1 para probar el método
getEmployeesForManager nuevamente.
Revise ResponseBody para ver los datos XML para los empleados en el departamento
administrado por Bob, que ahora es el departamento de Marketing. Esto debe incluir
al empleado recientemente asignado y obtener un total de 4 empleados:
<collection>
<employee>
<department>
<id>2</id>
<manager>
<id>1</id>
<name>Bob</name>
</manager>
<name>Marketing</name>
</department>
<id>1</id>
<name>William</name>
</employee>
<employee>
<department>
<id>2</id>
<manager>
<id>1</id>
<name>Bob</name>
</manager>
JB183-EAP7.0-es-2-20180124
439
Capítulo 10. Revisión completa: Red Hat Application Development I: Programming in Java EE
<name>Marketing</name>
</department>
<id>4</id>
<name>Rodney</name>
</employee>
<employee>
<department>
<id>2</id>
<manager>
<id>1</id>
<name>Bob</name>
</manager>
<name>Marketing</name>
</department>
<id>5</id>
<name>Kim</name>
</employee>
<employee>
<department>
<id>2</id>
<manager>
<id>1</id>
<name>Bob</name>
</manager>
<name>Marketing</name>
</department>
<id>6</id>
<name>Tom</name>
</employee>
</collection>
Evaluación
Como el usuario student en workstation, ejecute el script lab jpa-review con el
argumento grade para confirmar que ha realizado este ejercicio correctamente. Corrija las
fallas informadas y vuelva a ejecutar el script hasta obtener un resultado satisfactorio.
[student@workstation ~]$ lab jpa-review grade
Después de que la calificación se realiza correctamente, detenga el servidor EAP y cierre el
proyecto en JBDS.
Esto concluye el trabajo de laboratorio.
440
JB183-EAP7.0-es-2-20180124
Solución
Solución
En esta revisión, actualizará la API REST compilada en la revisión anterior para incluir
persistencia.
Resultados
Usted deberá ser capaz de realizar lo siguiente:
• Use JPA para asignar una entidad a una tabla de base de datos.
• Use JPA para asignar relaciones entre entidades.
• Use una consulta nombrada para recuperar registros de la base de datos.
Antes de comenzar
Prepare sus computadoras para este ejercicio iniciando sesión en workstation como
student y ejecutando el siguiente comando:
[student@workstation ~]$ lab jpa-review setup
Instrucciones
1. Abra JBDS e importe la estructura del proyecto jpa-review. Revise el código existente y
observe lo siguiente:
• El archivo src/main/resources/META-INF/persistence.xml define un contexto
de persistencia que se conecta a la fuente de datos MySQL definida en el servidor EAP.
• Se agregaron dos nuevos objetos modelo en el paquete
com.redhat.training.model: Manager (Gerente) y Department (Departamento).
Cada Employee (Empleado) tiene un Department (Departamento) y cada
Department (Departamento) tiene un Manager (Gerente).
2.
Cree una clase de utilidades en el paquete com.redhat.training.util que produce
instancias de EntityManager que utilizan el contexto de persistencia predeterminado.
3.
Actualice la entidad Employee para que esté administrada por JPA, y asigne las
relaciones entre las entidades Employee, Department y Manager mediante anotaciones
de JPA. Asegúrese de cumplir con los siguientes requisitos:
• El campo id es el identificador único de cada registro y se genera automáticamente
por la base de datos cuando se persiste un nuevo registro.
• La tabla Employee en la base de datos usa la columna departmentID para almacenar
la clave externa en la tabla Department.
• La tabla Manager en la base de datos usa la columna departmentID para almacenar
la clave externa en la tabla Department.
• La entidad Department asigna sus relaciones con Employee y Manager como el
objeto secundario en la relación.
• La entidad Department captura de manera diligente la lista de instancias de
Employee a la que está relacionada.
• La entidad Employee incluye una consulta nombrada JPQL, denominada
findAllForManager, que toma el ID del registro de un gerente como su parámetro.
JB183-EAP7.0-es-2-20180124
441
Capítulo 10. Revisión completa: Red Hat Application Development I: Programming in Java EE
Esta consulta extrae la lista de empleados que se reportan a ese gerente. Esto requiere
unirse al registro department y, luego, unirse al registro manager.
Sugerencia: La instrucción JPQL que verifica el ID de administrador de un empleado es
employee.department.manager.id
• La consulta findAllForManager devuelve objetos Employee y utiliza un parámetro
nombrado denominado managerId para representar el ID de administrador.
4.
Actualice EmployeeBean para admitir la persistencia mediante EntityManager.
Asegúrese de cumplir con los siguientes requisitos:
• Los métodos EJB CRUD asignan los siguientes métodos de administrador de
entidades:
◦ createEmployee: persist(Employee e)
◦ readEmployeeById: find(Class, Object)
◦ updateEmployee: merge(Employee e)
◦ deleteEmployee: remove(Employee e)
• El método findAllForManager utiliza la consulta nombrada findAllForManager
correspondiente. Además, el parámetro managerID de la consulta está definido con el
ID de manager que se envió al método.
5.
Actualice EmployeeRestService para admitir tres nuevos métodos y asegúrese de que
cumplan con los siguientes requisitos:
• getEmployeesForManager(Manager m):
◦ Se asigna a las solicitudes HTTP GET.
◦ Usa la ruta relativa /getByManager/{managerId}.
◦ managerId es un parámetro de ruta asignado al parámetro de método.
◦ Produce datos XML.
◦ Usa el método findById en ManagerBean para buscar al gerente por el valor de su
ID.
◦ Toma el objeto de gerente encontrado y lo envía al método findAllForManager en
EmployeeBean y devuelve el resultado.
• assignEmployee(Long employeeId, Long departmentId):
◦ Se asigna a las solicitudes HTTP POST.
◦ Usa la ruta relativa /assignEmployee/{employeeId}/{departmentId}.
◦ Tanto employeeId como departmentId son parámetros de ruta que se asignan a
los parámetros de método.
◦ Usa EmployeeBean para buscar al empleado por ID y, luego, usa DepartmentBean
para buscar al departamento por ID. Por último, actualiza el registro
del departamento en el objeto del empleado y, luego, invoca el método
updateEmployee en EmployeeBean para persistir los cambios.
442
JB183-EAP7.0-es-2-20180124
Solución
• assignManager(Long managerId, Long departmentId)
◦ Se asigna a las solicitudes HTTP POST.
◦ Usa la ruta relativa /assignManager/{managerId}/{departmentId}.
◦ Tanto managerId como departmentId son parámetros de ruta que se asignan a
los parámetros de método.
◦ Usa ManagerBean para buscar al gerente por ID y, luego, usa DepartmentBean
para buscar al departamento por ID. Por último, actualiza el registro del
departamento en el objeto del gerente y, luego, invoca el método updateManager
en ManagerBean para persistir los cambios.
6.
Inicie JBoss EAP y utilice Maven para implementar la aplicación jpa-review.
7.
Pruebe el servicio REST y los tres nuevos métodos mediante el complemento (plug-in) de
cliente REST de Firefox.
Si desea restablecer la base de datos durante las pruebas, ejecute el script /home/student/
JB183/labs/jpa-review/reset-database.sh proporcionado.
• Especifique un encabezado personalizado con el nombre Content-Type y el valor
application/json.
• Use HTTP GET para enviar una solicitud al extremo http://localhost:8080/
jpa-review/api/employees/getByManager/1 para probar el método
getEmployeesForManager y recuperar los empleados para el gerente con un valor
de id de 1.
Revise ResponseBody para ver los datos XML para los empleados en el departamento
administrado por Bob, que es el departamento de Sales (Ventas):
<collection>
<employee>
<department>
<id>1</id>
<manager>
<id>1</id>
<name>Bob</name>
</manager>
<name>Sales</name>
</department>
<id>1</id>
<name>William</name>
</employee>
<employee>
<department>
<id>1</id>
<manager>
<id>1</id>
<name>Bob</name>
</manager>
<name>Sales</name>
</department>
<id>2</id>
<name>Rose</name>
</employee>
<employee>
JB183-EAP7.0-es-2-20180124
443
Capítulo 10. Revisión completa: Red Hat Application Development I: Programming in Java EE
<department>
<id>1</id>
<manager>
<id>1</id>
<name>Bob</name>
</manager>
<name>Sales</name>
</department>
<id>3</id>
<name>Pat</name>
</employee>
</collection>
• Use HTTP POST para enviar una solicitud al extremo http://localhost:8080/jpareview/api/employees/assignEmployee/1/2 para asignar al empleado con un
valor de ID de 1 al departamento con un valor de ID de 2.
• Use HTTP POST para enviar una solicitud a http://localhost:8080/jpareview/api/employees/assignManager/2/1 para asignar un nuevo Manager al
departamento de ventas.
• Use HTTP POST para enviar una solicitud a http://localhost:8080/jpa-review/
api/employees/assignManager/1/2 para asignar a Bob a un nuevo departamento
con un valor de ID de 2.
• Use HTTP GET para enviar una solicitud al extremo http://localhost:8080/
jpa-review/api/employees/getByManager/1 para probar el método
getEmployeesForManager nuevamente.
Revise ResponseBody para ver los datos XML para los empleados en el departamento
administrado por Bob, que ahora es el departamento de Marketing. Esto debe incluir
al empleado recientemente asignado y obtener un total de 4 empleados:
<collection>
<employee>
<department>
<id>2</id>
<manager>
<id>1</id>
<name>Bob</name>
</manager>
<name>Marketing</name>
</department>
<id>1</id>
<name>William</name>
</employee>
<employee>
<department>
<id>2</id>
<manager>
<id>1</id>
<name>Bob</name>
</manager>
<name>Marketing</name>
</department>
<id>4</id>
<name>Rodney</name>
</employee>
<employee>
444
JB183-EAP7.0-es-2-20180124
Solución
<department>
<id>2</id>
<manager>
<id>1</id>
<name>Bob</name>
</manager>
<name>Marketing</name>
</department>
<id>5</id>
<name>Kim</name>
</employee>
<employee>
<department>
<id>2</id>
<manager>
<id>1</id>
<name>Bob</name>
</manager>
<name>Marketing</name>
</department>
<id>6</id>
<name>Tom</name>
</employee>
</collection>
Pasos
1. Abra JBDS e importe el proyecto de Maven.
1.1. Haga doble clic en el icono de JBoss Developer Studio en el escritorio de la estación
de trabajo para abrir JBDS. Seleccione el espacio de trabajo /home/student/
JB183/workspace y haga clic en OK (Aceptar).
1.2. En el menú de JBDS, haga clic en File (Archivo) > Import (Importar)para abrir el
asistente para Import (Importar).
1.3. En la página Select (Seleccionar), haga clic en Maven > Existing Maven Projects
(Proyectos de Maven existentes) y, luego, haga clic en Next (Siguiente).
1.4. En la página Maven projects (Proyectos de Maven), haga clic en Browse (Explorar)
para abrir la ventana Select root folder (Seleccionar carpeta raíz). Diríjase al
directorio /home/student/JB183/labs/. Seleccione la carpeta jpa-review y
haga clic en OK (Aceptar).
1.5. En la página Maven projects (Proyectos de Maven), haga clic en Finish (Finalizar).
1.6. Observe la barra de estado de JBDS para monitorear el progreso de la operación
de importación. Es probable que la descarga de todas las dependencias requeridas
demore unos minutos.
2.
Agregue una clase de utilidades que produzca instancias de EntityManager al inyectar
el contexto de persistencia predeterminado.
2.1. Expanda el ítem jpa-review en la pestaña Project Explorer (Explorador de proyectos)
en el panel izquierdo de JBDS y, luego, haga clic en jpa-review > Java Resources
(Recursos de Java). Haga clic con el botón derecho en com.redhat.training.util y
haga clic en New (Nueva) > Class (Clase).
2.2. En el campo Name (Nombre) ingrese Resources. Haga clic en Finish (Finalizar).
JB183-EAP7.0-es-2-20180124
445
Capítulo 10. Revisión completa: Red Hat Application Development I: Programming in Java EE
2.3. Convierta a la clase Resources en un productor para las instancias EntityManager
mediante la anotación @Produces, y vincule la entidad manager al contexto de
persistencia predeterminado mediante la anotación @PersistenceContext:
import javax.enterprise.inject.Produces;
import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;
public class Resources {
@Produces
@PersistenceContext
private EntityManager em;
}
2.4. Guarde los cambios en el archivo con Ctrl+S.
3.
Actualice la clase del modelo Employee para que sea una entidad administrada por JPA.
3.1. Abra la clase Employee; para ello, expanda el ítem jpa-review en la pestaña Project
Explorer (Explorador de proyectos) en el panel izquierdo de JBDS y, luego, haga clic
en jpa-review > Java Resources > src/main/java > com.redhat.training.model para
expandirlo. Haga doble clic en el archivo Employee.java.
Agregue la anotación @Entity para marcar esta clase como una entidad
administrada por JPA:
...
//TODO Make this class an Entity
@Entity
//TODO Add a named query to find all employees for a given manager
public class Employee {
...
3.2. Configure el campo id para que sea el identificador único para la clase Employee
mediante la anotación @Id. Márquelo como un valor generado mediante la
anotación @GeneratedValue con un valor de IDENTITY:
...
//TODO Make this class an Entity
@Entity
//TODO Add a named query to find all employees for a given manager
public class Employee {
//TODO mark this as the Id field
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
3.3. Guarde los cambios en el archivo con Ctrl+S.
4.
446
Asigne las relaciones entre las entidades Employee, Manager y Department.
JB183-EAP7.0-es-2-20180124
Solución
4.1. En la clase Employee, asigne la relación de muchos a uno con la entidad
Department.
Use la anotación @ManyToOne para indicarle a JPA que asigne la relación y la
anotación @JoinColumn para especificar la columna departmentID como la clave
externa a la tabla Department:
...
//TODO Make this class an Entity
@Entity
//TODO Add a named query to find all employees for a given manager
public class Employee {
//TODO mark this as the Id field
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String name;
//TODO map the relationship using JPA annotations
@ManyToOne
@JoinColumn(name="departmentID")
private Department department;
4.2. Guarde los cambios en el archivo con Ctrl+S.
4.3. Abra la clase Department; para ello, expanda el ítem jpa-review en la pestaña
Project Explorer (Explorador de proyectos) en el panel izquierdo de JBDS.
Haga clic en jpa-review > Java Resources (Recursos de Java) > src/main/java
> com.redhat.training.model para expandirlo. Haga doble clic en el archivo
Department.java.
4.4. Asigne una relación de muchos a uno con la entidad Employee, que está asignada
por el campo department. Cargue las entidades Employee relacionadas.
Use la anotación @OneToMany y el atributo mappedBy. Además, defina el tipo de
captura como EAGER:
@Entity
public class Department {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String name;
//TODO map the relationship using JPA annotations
private Manager manager;
//TODO map the relationship using JPA annotations, fetch the list eagerly
@OneToMany(mappedBy="department", fetch=FetchType.EAGER)
private Set<Employee> employees;
JB183-EAP7.0-es-2-20180124
447
Capítulo 10. Revisión completa: Red Hat Application Development I: Programming in Java EE
4.5. Asigne una relación de uno a uno con la entidad Manager, que está asignada por el
campo department.
Use la anotación @OneToOne y el atributo mappedBy:
@Entity
public class Department {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String name;
//TODO map the relationship using JPA annotations
@OneToOne(mappedBy="department")
private Manager manager;
//TODO map the relationship using JPA annotations, fetch the list eagerly
@OneToMany(mappedBy="department", fetch=FetchType.EAGER)
private Set<Employee> employees;
nota
El error en la anotación OneToOne se resuelve en el próximo paso.
4.6. Guarde los cambios en el archivo con Ctrl+S.
4.7. Abra la clase Manager; para ello, expanda el ítem jpa-review en la pestaña
Project Explorer (Explorador de proyectos) en el panel izquierdo de JBDS.
Haga clic en jpa-review > Java Resources (Recursos de Java) > src/main/java
> com.redhat.training.model para expandirlo. Haga doble clic en el archivo
Manager.java.
4.8. Asigne la relación de uno a uno con la entidad Department, que se relaciona
mediante la columna departmentID como clave externa.
Use la anotación @OneToOne para indicarle a JPA que asigne la relación. Use la
anotación @JoinColumn para especificar departmentID como la clave externa a la
tabla Department:
@Entity
public class Manager {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String name;
//TODO map the relationship using JPA annotations
@OneToOne
@JoinColumn(name="departmentID")
private Department department;
448
JB183-EAP7.0-es-2-20180124
Solución
4.9. Guarde los cambios en el archivo con Ctrl+S.
5.
Agregue una consulta nombrada para buscar todos los empleados que se reportan a un
determinado gerente y, luego, actualice EmployeeBean para usar la consulta nombrada.
5.1. Abra la clase Employee; para ello, expanda el ítem jpa-review en la pestaña
Project Explorer (Explorador de proyectos) en el panel izquierdo de JBDS.
Haga clic en jpa-review > Java Resources (Recursos de Java) > src/main/java
> com.redhat.training.model para expandirlo. Haga doble clic en el archivo
Employee.java.
Agregue la anotación @NamedQuery para registrar la consulta con JPA. Asígnele
el nombre findAllForManager a la consulta y utilice un parámetro nombrado
denominado :managerId:
...
//TODO Make this class an Entity
@Entity
//TODO Add a named query to find all employees for a given manager
@NamedQuery(name="findAllForManager", query="select e from Employee e where
e.department.manager.id = :managerId")
public class Employee {
5.2. Guarde los cambios en el archivo con Ctrl+S.
6.
Implemente la funcionalidad de persistencia en EmployeeBean al inyectar
EntityManager e invocar los métodos adecuados para cada método de EJB.
6.1. Abra la clase EmployeeBean; para ello, expanda el ítem jpa-review en la pestaña
Project Explorer (Explorador de proyectos) en el panel izquierdo de JBDS.
Haga clic en jpa-review > Java Resources (Recursos de Java) > src/main/
java > com.redhat.training.ejb para expandirlo. Haga doble clic en el archivo
EmployeeBean.java.
6.2. Inyecte una instancia de la clase EntityManager en EmployeeBean mediante la
anotación @Inject:
@Stateless
public class EmployeeBean {
//TODO inject EntityManager
@Inject
private EntityManager em;
6.3. Implemente la funcionalidad de persistencia createEmployee mediante el método
persist en el administrador de entidades:
public void createEmployee(Employee e) {
//TODO persist employee
em.persist(e);
logger.logAction(e, Operation.Create);
}
JB183-EAP7.0-es-2-20180124
449
Capítulo 10. Revisión completa: Red Hat Application Development I: Programming in Java EE
6.4. Implemente la funcionalidad de persistencia readEmployeeById al actualizar el
método para usar el método find en el administrador de entidades:
public Employee readEmployeeById(Long id) {
//TODO find the employee by its ID
Employee employee = em.find(Employee.class,id)
logger.logAction(employee, Operation.Read);
return employee;
}
6.5. Implemente la funcionalidad de persistencia updateEmployee al actualizar el
método para usar el método merge en el administrador de entidades:
public void updateEmployee(Employee e) {
//TODO merge the employee record
em.merge(e);
logger.logAction(e, Operation.Update);
}
6.6. Implemente la funcionalidad de persistencia deleteEmployee al actualizar el
método para usar el método remove en el administrador de entidades:
public void deleteEmployee(Employee e) {
//TODO remove the employee record
em.remove(e);
logger.logAction(e, Operation.Delete);
}
6.7. Implemente la funcionalidad de persistencia findAllForManager al actualizar el
método para crear TypedQuery mediante la consulta nombrada creada en el paso
anterior. Defina el ID del objeto administrador que se envió como el parámetro
de consulta managerId. Actualice la instrucción de devolución para devolver el
resultado:
public List<Employee> findAllForManager(Manager manager) {
//TODO use the named query to find all the employees for a manager
TypedQuery<Employee> query =
em.createNamedQuery("findAllForManager",Employee.class);
query.setParameter("managerId", manager.getId());
return query.getResultList();
}
6.8. Guarde los cambios en el archivo con Ctrl+S.
7.
Actualice EmployeeRestService para agregar un nuevo método REST nombrado
getEmployeesForManager que utilice el método findAllForManager en
EmployeeBean para recuperar todos los empleados de un departamento.
7.1. Abra la clase EmployeeRestService; para ello, expanda el ítem jpa-review en
la pestaña Project Explorer (Explorador de proyectos) en el panel izquierdo de
JBDS. Haga clic en jpa-review > Java Resources (Recursos de Java) > src/main/
java > com.redhat.training.rest para expandirlo. Haga doble clic en el archivo
EmployeeRestService.java.
450
JB183-EAP7.0-es-2-20180124
Solución
7.2. Agregue el nuevo método después del comentario //TODO add method to pull
an XML list of employees for a given manager (//TODO agregar método
para extraer una lista XML de los empleados para un gerente determinado):
//TODO add method to pull an XML list of employees for a given manager id
public List<Employee> getEmployeesForManager(Long managerId){
Manager manager = managerBean.findById(managerId);
return employeeBean.findAllForManager(manager);
}
7.3. Asocie el método con las solicitudes HTTP GET mediante la anotación @GET:
@GET
public List<Employee> getEmployeesForManager(Long managerId){
Manager manager = managerBean.findById(managerId);
return employeeBean.findAllForManager(manager);
}
7.4. Defina la ruta relativa del método como /getByManager/{managerId} mediante la
anotación @Path:
@GET
@Path("getByManager/{managerId}")
public List<Employee> getEmployeesForManager(Long managerId){
Manager manager = managerBean.findById(managerId);
return employeeBean.findAllForManager(manager);
}
7.5. Agregue la anotación @PathParam en el parámetro del método managerId para
asignar el parámetro de ruta de la URL en el método automáticamente:
@GET
@Path("getByManager/{managerId}")
public List<Employee> getEmployeesForManager(@PathParam("managerId") Long
managerId){
Manager manager = managerBean.findById(managerId);
return employeeBean.findAllForManager(manager);
}
7.6. Especifique que el método produce datos XML mediante la anotación @Produces:
@GET
@Path("getByManager/{managerId}")
@Produces(MediaType.APPLICATION_XML)
public List<Employee> getEmployeesForManager(@PathParam("managerId") Long
managerId){
Manager manager = managerBean.findById(managerId);
return employeeBean.findAllForManager(manager);
}
7.7. Guarde los cambios en el archivo con Ctrl+S.
JB183-EAP7.0-es-2-20180124
451
Capítulo 10. Revisión completa: Red Hat Application Development I: Programming in Java EE
8.
Actualice EmployeeRestService para agregar un nuevo método REST nombrado
assignEmployee que asigne un empleado a un departamento mediante ID.
8.1. Agregue un nuevo método después del comentario //TODO add REST method
to assign an employee to a department by ID (//TODO agregar método
REST para asignar un empleado a un departamento por ID). Use employeeBean
para buscar el empleado por su ID y departmentBean para buscar el departamento.
A continuación, actualice el registro del departamento en el empleado y utilice el
método updateEmployee para persistir los cambios:
//TODO add REST method to assign an employee to a department by ID
public void assignEmployee(Long employeeId, Long departmentId) {
Employee e = employeeBean.readEmployeeById(employeeId);
Department d = departmentBean.findById(departmentId);
e.setDepartment(d);
employeeBean.updateEmployee(e);
}
8.2. Asocie el método con las solicitudes HTTP POST mediante la anotación @POST:
//TODO add REST method to assign an employee to a department by ID
@POST
public void assignEmployee(Long employeeId, Long departmentId) {
Employee e = employeeBean.readEmployeeById(employeeId);
Department d = departmentBean.findById(departmentId);
e.setDepartment(d);
employeeBean.updateEmployee(e);
}
8.3. Defina la ruta relativa del método como /assignEmployee/{employeeId}/
{departmentId} mediante la anotación @Path:
@POST
@Path("assignEmployee/{employeeId}/{departmentId}")
public void assignEmployee(Long employeeId, Long departmentId) {
Employee e = employeeBean.readEmployeeById(employeeId);
Department d = departmentBean.findById(departmentId);
e.setDepartment(d);
employeeBean.updateEmployee(e);
}
8.4. Agregue las anotaciones @PathParam en los parámetros del método para asignarlos
desde la ruta de la URL:
@POST
@Path("assignEmployee/{employeeId}/{departmentId}")
public void assignEmployee(@PathParam("employeeId") Long
employeeId, @PathParam("departmentId") Long departmentId) {
Employee e = employeeBean.readEmployeeById(employeeId);
Department d = departmentBean.findById(departmentId);
e.setDepartment(d);
employeeBean.updateEmployee(e);
}
8.5. Guarde los cambios en el archivo con Ctrl+S.
452
JB183-EAP7.0-es-2-20180124
Solución
9.
Actualice EmployeeRestService para agregar un nuevo método REST nombrado
assignManager que asigne un gerente a un departamento mediante ID.
9.1. Agregue un nuevo método después del comentario //TODO add REST method
to assign an manager to a department by ID (//TODO agregar método
REST para asignar un gerente a un departamento por ID). Use manager para
buscar el gerente por su ID y departmentBean para buscar el departamento. A
continuación, actualice el registro del departamento en el gerente y utilice el método
updateManager para persistir los cambios:
//TODO add REST method to assign a manager to a department by ID
public void assignManager(Long managerId, Long departmentId) {
Manager m = managerBean.findById(managerId);
Department d = departmentBean.findById(departmentId);
m.setDepartment(d);
managerBean.updateManager(m);
}
9.2. Asocie el método con las solicitudes HTTP POST mediante la anotación @POST:
//TODO add REST method to assign an employee to a department by ID
@POST
public void assignManager(Long managerId, Long departmentId) {
Manager m = managerBean.findById(managerId);
Department d = departmentBean.findById(departmentId);
m.setDepartment(d);
managerBean.updateManager(m);
}
9.3. Defina la ruta relativa del método como /assignEmployee/{managerId}/
{departmentId} mediante la anotación @Path:
@POST
@Path("assignManager/{managerId}/{departmentId}")
public void assignEmployee(Long employeeId, Long departmentId) {
Manager m = managerBean.findById(managerId);
Department d = departmentBean.findById(departmentId);
m.setDepartment(d);
managerBean.updateManager(m);
}
9.4. Agregue las anotaciones @PathParam en los parámetros del método para asignarlos
desde la ruta de la URL:
@POST
@Path("assignManager/{managerId}/{departmentId}")
public void assignManager(@PathParam("managerId") Long
managerId, @PathParam("departmentId") Long departmentId) {
Manager m = managerBean.findById(managerId);
Department d = departmentBean.findById(departmentId);
m.setDepartment(d);
managerBean.updateManager(m);
}
9.5. Guarde los cambios en el archivo con Ctrl+S.
JB183-EAP7.0-es-2-20180124
453
Capítulo 10. Revisión completa: Red Hat Application Development I: Programming in Java EE
10. Seleccione la pestaña Servers (Servidores) en el panel inferior de JBDS para iniciar EAP.
Haga clic con el botón derecho en el servidor Red Hat JBoss EAP 7.0 [Stopped] y haga clic
en el botón verde de inicio para iniciar el servidor.
11. Implemente la aplicación jpa-review mediante los siguientes comandos en la ventana del
terminal:
[student@workstation ~]$ cd /home/student/JB183/labs/jpa-review
[student@workstation jpa-review]$ mvn wildfly:deploy
12. Pruebe el nuevo método REST para extraer empleados de un gerente mediante el
complemento (plug-in) de cliente REST de Firefox.
12.1.Inicie Firefox en la máquina virtual workstation y haga clic en el icono del
complemento (plug-in) de cliente REST en la barra de herramientas del explorador.
12.2.En la barra de herramientas superior, haga clic en Headers (Encabezados) y en
Custom Header (Encabezado personalizado) para agregar un nuevo encabezado
personalizado a la solicitud.
12.3.Introduzca la siguiente información en el cuadro de diálogo del encabezado
personalizado:
• Name (Nombre): Content-Type
• Value (Valor): application/json
Haga clic en Okay (Aceptar).
12.4.Seleccione GET como el Method (Método). En el formulario de URL, introduzca
http://localhost:8080/jpa-review/api/employees/getByManager/1.
Haga clic en Send (Enviar).
12.5.Verifique en la pestaña Response Headers (Encabezados de respuesta) que Status
Code (Código de estado) sea 200 OK.
Revise la pestaña Response Body para ver los datos XML y verifique que coincidan
con lo esperado:
<collection>
<employee>
<department>
<id>1</id>
<manager>
<id>1</id>
<name>Bob</name>
</manager>
<name>Sales</name>
</department>
<id>1</id>
<name>William</name>
</employee>
<employee>
<department>
<id>1</id>
<manager>
454
JB183-EAP7.0-es-2-20180124
Solución
<id>1</id>
<name>Bob</name>
</manager>
<name>Sales</name>
</department>
<id>2</id>
<name>Rose</name>
</employee>
<employee>
<department>
<id>1</id>
<manager>
<id>1</id>
<name>Bob</name>
</manager>
<name>Sales</name>
</department>
<id>3</id>
<name>Pat</name>
</employee>
</collection>
13. Pruebe los dos nuevos métodos REST para reasignar a los gerentes y empleados a
nuevos departamentos.
13.1.Seleccione POST como el Method (Método). En el formulario de URL, introduzca
http://localhost:8080/jpa-review/api/employees/assignEmployee/1/2
para reasignar un empleado con un valor de ID de 1 a un nuevo departamento con el
ID de 2.
13.2.Haga clic en Send (Enviar).
13.3.Verifique en la pestaña Response Headers (Encabezados de respuesta) que Status
Code (Código de estado) sea 204 No Content (204 Sin contenido). Esto está
previsto porque el tipo de devolución es void (nulo).
13.4.Seleccione POST como el Method (Método). En el formulario de URL,
introduzca http://localhost:8080/jaxrs-review/api/employees/
assignManager/1/2 para reasignar un gerente con un valor de ID de 1 a un nuevo
departamento.
13.5.Haga clic en Send (Enviar).
13.6.Verifique en la pestaña Response Headers (Encabezados de respuesta) que Status
Code (Código de estado) sea 204 No Content (204 Sin contenido). Esto está
previsto porque el tipo de devolución es void (nulo).
13.7.Seleccione POST como el Method (Método). En el formulario de URL, introduzca
http://localhost:8080/jpa-review/api/employees/assignManager/2/1
para reasignar al gerente con un valor de ID de 2 al departamento de ventas,
reemplazando a Bob.
13.8.Haga clic en Send (Enviar).
13.9.Verifique en la pestaña Response Headers (Encabezados de respuesta) que Status
Code (Código de estado) sea 204 No Content (204 Sin contenido). Esto está
previsto porque el tipo de devolución es void (nulo).
JB183-EAP7.0-es-2-20180124
455
Capítulo 10. Revisión completa: Red Hat Application Development I: Programming in Java EE
13.10.
Ejecute el método getByManager nuevamente para extraer una lista de los nuevos
subalternos inmediatos de Bob.
Seleccione GET como el Method (Método). En el formulario de URL, introduzca
http://localhost:8080/jpa-review/api/employees/getByManager/1.
Haga clic en Send (Enviar).
13.11.
Verifique en la pestaña Response Headers (Encabezados de respuesta) que Status
Code (Código de estado) sea 200 OK.
Revise la pestaña Response Body para ver los datos XML y verifique que coincidan
con lo esperado:
<collection>
<employee>
<department>
<id>2</id>
<manager>
<id>1</id>
<name>Bob</name>
</manager>
<name>Marketing</name>
</department>
<id>1</id>
<name>William</name>
</employee>
<employee>
<department>
<id>2</id>
<manager>
<id>1</id>
<name>Bob</name>
</manager>
<name>Marketing</name>
</department>
<id>4</id>
<name>Rodney</name>
</employee>
<employee>
<department>
<id>2</id>
<manager>
<id>1</id>
<name>Bob</name>
</manager>
<name>Marketing</name>
</department>
<id>5</id>
<name>Kim</name>
</employee>
<employee>
<department>
<id>2</id>
<manager>
<id>1</id>
<name>Bob</name>
</manager>
<name>Marketing</name>
</department>
456
JB183-EAP7.0-es-2-20180124
Solución
<id>6</id>
<name>Tom</name>
</employee>
</collection>
Evaluación
Como el usuario student en workstation, ejecute el script lab jpa-review con el
argumento grade para confirmar que ha realizado este ejercicio correctamente. Corrija las
fallas informadas y vuelva a ejecutar el script hasta obtener un resultado satisfactorio.
[student@workstation ~]$ lab jpa-review grade
Después de que la calificación se realiza correctamente, detenga el servidor EAP y cierre el
proyecto en JBDS.
Esto concluye el trabajo de laboratorio.
JB183-EAP7.0-es-2-20180124
457
Capítulo 10. Revisión completa: Red Hat Application Development I: Programming in Java EE
Trabajo de laboratorio: Protección de la API
REST con JAAS
En esta revisión, protegerá la API REST con autenticación y autorización mediante JAAS.
Resultados
Usted deberá ser capaz de realizar lo siguiente:
• Conectar una aplicación web Java a un dominio de seguridad JBoss EAP mediante los
archivos de configuración necesarios.
• Proteger recursos específicos con autenticación básica.
• Limitar la autorización de usuarios que pueden usar servicios RESTEasy específicos
mediante anotaciones JAAS para configurar la autenticación basada en roles.
Antes de comenzar
Prepare sus computadoras para este ejercicio iniciando sesión en workstation como
student y ejecutando el siguiente comando:
[student@workstation ~]$ lab jaas-review setup
Instrucciones
1. Abra JBDS e importe la estructura del proyecto jaas-review.
2.
Agregue una nueva clase de servicio REST que cumpla con los siguientes requisitos:
• Disponible en la siguiente ruta: http://localhost:8080/jaas-review/api/
healthCheck
• Contiene el siguiente método e implementación:
public Response getHealthCheck() {
ResponseBuilder builder = Response.status(Status.OK);
return builder.build();
}
• Asociado con solicitudes HTTP GET.
• No toma argumentos y devuelve siempre una respuesta de estado HTTP "200 OK".
3.
Habilite la autenticación básica en el EmployeeRestService existente. Asegúrese de
cumplir con los siguientes requisitos:
• Inicie JBoss EAP y cree un nuevo dominio de seguridad para EAP mediante el script
create-sd.sh proporcionado. Asegúrese de iniciar JBoss EAP antes de ejecutar este
script . Este dominio de seguridad usa el módulo de inicio de sesión UsersRoles
para leer el archivo de propiedad del usuario /home/student/JB183/labs/jaasreview/todo-users.properties proporcionado para la autenticación y el archivo
/home/student/JB183/labs/jaas-review/todo-roles.properties para la
autorización. Se incluyen los siguientes usuarios:
458
JB183-EAP7.0-es-2-20180124
Usuarios de trabajos de laboratorio de revisión integral
Nombre de usuario
Contraseña
Rol
employee
redhat1!
employee
manager
redhat1!
manager
superuser
redhat1!
superuser
• Configure la aplicación para utilizar este dominio de seguridad.
• Habilite la autenticación basada en rol para RESTEasy.
• Use una restricción de seguridad para aplicar la nueva seguridad únicamente
a EmployeeRestService, no al nuevo HealthCheckService, que debe estar
disponible sin autenticación.
• Defina los siguientes roles:
◦ employee
◦ manager
◦ superuser
• Configure la aplicación para que use autenticación BÁSICA para requerir el nombre de
usuario y la contraseña para todos los recursos protegidos.
• Use la siguiente tabla para configurar cada uno de los siguientes métodos de la clase
EmployeeRestService para que estén restringidos a los roles adecuados:
Método para la asignación de roles
4.
Método
Roles permitidos
getEmployee(Long id)
All
getEmployeesForManager(Long managerId)
All
assignEmployee(Long employeeId, Long
departmentId)
manager,
superuser
assignManager(Long managerId, Long departmentId)
superuser
Use Maven para implementar la aplicación jaas-review.
nota
Los datos de la base de datos se restablecieron antes de este trabajo de
laboratorio de revisión integral.
5.
Pruebe el método de servicio REST de comprobación de estado no protegido mediante
el complemento (plug-in) de cliente REST de Firefox y sin utilizar autenticación.
• Use HTTP GET para enviar una solicitud al extremo http://localhost:8080/jaasreview/api/healthCheck para probar el método getHealthCheck.
JB183-EAP7.0-es-2-20180124
459
Capítulo 10. Revisión completa: Red Hat Application Development I: Programming in Java EE
6.
Pruebe los métodos de servicio REST protegidos mediante el complemento (plug-in) de
cliente REST de Firefox y autenticación básica. Asegúrese de que cada rol pueda acceder
solo a los métodos corregidos.
• Use las credenciales encontradas en la tabla anterior para definir la autenticación en
las solicitudes HTTP enviadas por el cliente.
• Especifique un encabezado personalizado con el nombre Content-Type y el valor
application/json.
Evaluación
Como el usuario student en workstation, ejecute el script lab jaas-review con el
argumento grade para confirmar que ha realizado este ejercicio correctamente. Corrija las
fallas informadas y vuelva a ejecutar el script hasta obtener un resultado satisfactorio.
[student@workstation ~]$ lab jaas-review grade
Después de que la calificación se realiza correctamente, detenga el servidor EAP y cierre el
proyecto en JBDS.
Esto concluye el trabajo de laboratorio.
460
JB183-EAP7.0-es-2-20180124
Solución
Solución
En esta revisión, protegerá la API REST con autenticación y autorización mediante JAAS.
Resultados
Usted deberá ser capaz de realizar lo siguiente:
• Conectar una aplicación web Java a un dominio de seguridad JBoss EAP mediante los
archivos de configuración necesarios.
• Proteger recursos específicos con autenticación básica.
• Limitar la autorización de usuarios que pueden usar servicios RESTEasy específicos
mediante anotaciones JAAS para configurar la autenticación basada en roles.
Antes de comenzar
Prepare sus computadoras para este ejercicio iniciando sesión en workstation como
student y ejecutando el siguiente comando:
[student@workstation ~]$ lab jaas-review setup
Instrucciones
1. Abra JBDS e importe la estructura del proyecto jaas-review.
2.
Agregue una nueva clase de servicio REST que cumpla con los siguientes requisitos:
• Disponible en la siguiente ruta: http://localhost:8080/jaas-review/api/
healthCheck
• Contiene el siguiente método e implementación:
public Response getHealthCheck() {
ResponseBuilder builder = Response.status(Status.OK);
return builder.build();
}
• Asociado con solicitudes HTTP GET.
• No toma argumentos y devuelve siempre una respuesta de estado HTTP "200 OK".
3.
Habilite la autenticación básica en el EmployeeRestService existente. Asegúrese de
cumplir con los siguientes requisitos:
• Inicie JBoss EAP y cree un nuevo dominio de seguridad para EAP mediante el script
create-sd.sh proporcionado. Asegúrese de iniciar JBoss EAP antes de ejecutar este
script . Este dominio de seguridad usa el módulo de inicio de sesión UsersRoles
para leer el archivo de propiedad del usuario /home/student/JB183/labs/jaasreview/todo-users.properties proporcionado para la autenticación y el archivo
/home/student/JB183/labs/jaas-review/todo-roles.properties para la
autorización. Se incluyen los siguientes usuarios:
Usuarios de trabajos de laboratorio de revisión integral
Nombre de usuario
Contraseña
Rol
employee
redhat1!
employee
JB183-EAP7.0-es-2-20180124
461
Capítulo 10. Revisión completa: Red Hat Application Development I: Programming in Java EE
Nombre de usuario
Contraseña
Rol
manager
redhat1!
manager
superuser
redhat1!
superuser
• Configure la aplicación para utilizar este dominio de seguridad.
• Habilite la autenticación basada en rol para RESTEasy.
• Use una restricción de seguridad para aplicar la nueva seguridad únicamente
a EmployeeRestService, no al nuevo HealthCheckService, que debe estar
disponible sin autenticación.
• Defina los siguientes roles:
◦ employee
◦ manager
◦ superuser
• Configure la aplicación para que use autenticación BÁSICA para requerir el nombre de
usuario y la contraseña para todos los recursos protegidos.
• Use la siguiente tabla para configurar cada uno de los siguientes métodos de la clase
EmployeeRestService para que estén restringidos a los roles adecuados:
Método para la asignación de roles
4.
Método
Roles permitidos
getEmployee(Long id)
All
getEmployeesForManager(Long managerId)
All
assignEmployee(Long employeeId, Long
departmentId)
manager,
superuser
assignManager(Long managerId, Long departmentId)
superuser
Use Maven para implementar la aplicación jaas-review.
nota
Los datos de la base de datos se restablecieron antes de este trabajo de
laboratorio de revisión integral.
5.
Pruebe el método de servicio REST de comprobación de estado no protegido mediante
el complemento (plug-in) de cliente REST de Firefox y sin utilizar autenticación.
• Use HTTP GET para enviar una solicitud al extremo http://localhost:8080/jaasreview/api/healthCheck para probar el método getHealthCheck.
6.
Pruebe los métodos de servicio REST protegidos mediante el complemento (plug-in) de
cliente REST de Firefox y autenticación básica. Asegúrese de que cada rol pueda acceder
solo a los métodos corregidos.
462
JB183-EAP7.0-es-2-20180124
Solución
• Use las credenciales encontradas en la tabla anterior para definir la autenticación en
las solicitudes HTTP enviadas por el cliente.
• Especifique un encabezado personalizado con el nombre Content-Type y el valor
application/json.
Pasos
1. Abra JBDS e importe el proyecto de Maven.
1.1. Haga doble clic en el icono de JBoss Developer Studio en el escritorio de la estación
de trabajo para abrir JBDS. Seleccione el espacio de trabajo /home/student/
JB183/workspace y haga clic en OK (Aceptar).
1.2. En el menú de JBDS, haga clic en File (Archivo) > Import (Importar)para abrir el
asistente para Import (Importar).
1.3. En la página Select (Seleccionar), haga clic en Maven > Existing Maven Projects
(Proyectos de Maven existentes) y, luego, haga clic en Next (Siguiente).
1.4. En la página Maven projects (Proyectos de Maven), haga clic en Browse (Explorar)
para abrir la ventana Select root folder (Seleccionar carpeta raíz). Diríjase al
directorio /home/student/JB183/labs/. Seleccione la carpeta jaas-review y
haga clic en OK (Aceptar).
1.5. En la página Maven projects (Proyectos de Maven), haga clic en Finish (Finalizar).
1.6. Observe la barra de estado de JBDS para monitorear el progreso de la operación
de importación. Es probable que la descarga de todas las dependencias requeridas
demore unos minutos.
2.
Agregue el nuevo servicio de comprobación de estado.
2.1. Haga clic con el botón derecho en com.redhat.training.rest y haga clic en New
(Nueva) > Class (Clase).
2.2. En el campo Name (Nombre) ingrese HealthCheckService. Haga clic en Finish
(Finalizar).
2.3. Marque HealthCheckService como un EJB sin estado:
import javax.ejb.Stateless;
@Stateless
public class HealthCheckService {
2.4. Configure la ruta relativa del servicio REST HealthCheckService para que sea /
healthCheck mediante la anotación @Path:
import javax.ejb.Stateless;
import javax.ws.rs.Path;
@Stateless
@Path("/healthCheck")
JB183-EAP7.0-es-2-20180124
463
Capítulo 10. Revisión completa: Red Hat Application Development I: Programming in Java EE
public class HealthCheckService {
2.5. Implemente el método de comprobación de estado para que devuelva una
respuesta de código de estado HTTP 200 "OK" para cada solicitud:
import
import
import
import
import
javax.ejb.Stateless;
javax.ws.rs.Path;
javax.ws.rs.core.Response;
javax.ws.rs.core.Response.ResponseBuilder;
javax.ws.rs.core.Response.Status;
@Stateless
@Path("/healthCheck")
public class HealthCheckService {
public Response getHealthCheck() {
ResponseBuilder builder = Response.status(Status.OK);
return builder.build();
}
}
nota
El método getHealthCheck() está disponible en el archivo /home/
student/JB183/labs/jaas-review/healthCheck.txt.
2.6. Asocie el método getHealthCheck con las solicitudes HTTP GET mediante la
anotación @GET de RESTEasy:
import
import
import
import
import
import
javax.ejb.Stateless;
javax.ws.rs.Path;
javax.ws.rs.core.Response;
javax.ws.rs.core.Response.ResponseBuilder;
javax.ws.rs.core.Response.Status;
javax.ws.rs.GET;
@Stateless
@Path("/healthCheck")
public class HealthCheckService {
@GET
public Response getHealthCheck() {
ResponseBuilder builder = Response.status(Status.OK);
return builder.build();
}
}
2.7. Presione Ctrl+S para guardar sus cambios.
3.
464
Inicie JBoss EAP desde dentro de JBDS.
JB183-EAP7.0-es-2-20180124
Solución
Seleccione la pestaña Servers (Servidores) en JBDS. Haga clic con el botón derecho en la
entrada del servidor Red Hat JBoss EAP 7.0 [Stopped] y haga clic en la opción verde Start
(Iniciar) para iniciar el servidor.
4.
Use el script /home/student/JB183/labs/jaas-review/create-sd.sh para crear
un dominio de seguridad UsersRoles nombrado userroles.
Este dominio de seguridad usa el módulo de inicio de sesión UsersRoles para leer el
archivo de propiedad del usuario /home/student/JB183/labs/jaas-review/todousers.properties proporcionado para la autenticación y el archivo /home/student/
JB183/labs/jaas-review/todo-roles.properties para la autorización.
4.1. En la ventana del terminal, diríjase al directorio del proyecto /home/student/
JB183/labs/jaas-review/ y ejecute el script create-sd.sh:
[student@workstation ~]$ cd JB183/labs/jaas-review
[student@workstation jaas-review]$ ./create-sd.sh
4.2. Confirme que el dominio de seguridad está disponible al visualizar el contenido
de la configuración del servidor EAP /opt/jboss-eap-7.0/standalone/
configuration/standalone-full.xml.
Mediante un editor de texto, abra el archivo de configuración /opt/jbosseap-7.0/standalone/configuration/standalone-full.xml y observe el
nuevo dominio de seguridad que controla al servidor de aplicaciones para utilizar
los archivos de propiedad de roles y usuarios proporcionados en el directorio de
proyectos.
<security-domain name="userroles" cache-type="default">
<authentication>
<login-module code="UsersRoles" flag="required">
<module-option name=usersProperties" value="file:///home/student/JB183/labs/
jaas-review/todo-users.properties"/>
<module-option name="rolesProperties" value="file:///home/student/JB183/labs/
jaas-review/todo-roles.properties"/>
</login-module>
</authentication>
</security-domain>
5.
Actualice el archivo jboss-web.xml para utilizar el dominio de seguridad userroles.
5.1. Abra la clase jboss-web.xml; para ello, expanda el ítem jaas-review en la pestaña
Project Explorer (Explorador de proyectos) en el panel izquierdo de JBDS. Haga clic
en jaas-review > Java Resources (Recursos de Java) > src/main/webapp > WEB-INF
y expándalo. Haga doble clic en el archivo jboss-web.xml.
5.2. Actualice el archivo jboss-web.xml para utilizar el nuevo dominio de seguridad
nombrado userroles:
<?xml version="1.0" encoding="ISO-8859-1"?>
<jboss-web>
<security-domain>userroles</security-domain>
JB183-EAP7.0-es-2-20180124
465
Capítulo 10. Revisión completa: Red Hat Application Development I: Programming in Java EE
</jboss-web>
5.3. Presione Ctrl+S para guardar sus cambios.
6.
Actualice el archivo web.xml para habilitar las anotaciones de seguridad RESTEasy, utilice
la autenticación BASIC y restrinja el acceso a la API REST de la aplicación con la ruta /
api/* a los roles manager, employee y superuser.
6.1. Abra la clase web.xml; para ello, expanda el ítem jaas-review en la pestaña Project
Explorer (Explorador de proyectos) en el panel izquierdo de JBDS. Haga clic en
jaas-review > Java Resources (Recursos de Java) > src/main/webapp > WEB-INF y
expándalo. Haga doble clic en el archivo web.xml.
6.2. Cree una nueva etiqueta <context-param> que contenga el parámetro
resteasy.role.based.security con un valor de true:
<!-- Add context param -->
<context-param>
<param-name>resteasy.role.based.security</param-name>
<param-value>true</param-value>
</context-param>
6.3. Cree una nueva etiqueta security-constraint que restrinja
EmployeeRestService mediante <url-pattern> definido en /api/employees/
*:
<!-- Add security constraint -->
<security-constraint>
<web-resource-collection>
<web-resource-name>Secured REST Resources</web-resource-name>
<url-pattern>/api/employees/*</url-pattern>
</web-resource-collection>
</security-constraint>
6.4. Actualice <security-constraint para restringir el acceso para usuarios con el rol
employee, manager o superuser mediante la etiqueta auth-constraint:
<security-constraint>
<web-resource-collection>
<web-resource-name>All resources</web-resource-name>
<url-pattern>/api/employees/*</url-pattern>
</web-resource-collection>
<auth-constraint>
<role-name>employee</role-name>
<role-name>manager</role-name>
<role-name>superuser</role-name>
</auth-constraint>
</security-constraint>
6.5. Defina los roles de seguridad para los roles employee, superuser y manager con
una etiqueta <security-role>:
466
JB183-EAP7.0-es-2-20180124
Solución
<!-- Add security role -->
<security-role>
<role-name>manager</role-name>
</security-role>
<security-role>
<role-name>employee</role-name>
</security-role>
<security-role>
<role-name>superuser</role-name>
</security-role>
6.6. Cree un elemento <login-config> y defina <auth-method> como BASIC.
<!-- Add login config -->
<login-config>
<auth-method>BASIC</auth-method>
</login-config>
6.7. Presione Ctrl+S para guardar sus cambios.
7.
Actualice la clase de servicio RESTEasy EmployeeRestService.java y actualice cada
método con los siguientes requisitos:
Método para la asignación de roles
Método
Roles permitidos
getEmployee(Long id)
All
deleteEmployee(Long id)
manager, superuser
saveEmployee(Employee employee)
manager, superuser
getEmployeesForManager(Long managerId)
All
createDepartment(Department d)
manager, superuser
createManager(Manager m)
superuser
assignEmployee(Long employeeId, Long departmentId)
manager, superuser
assignManager(Long managerId, Long departmentId)
superuser
7.1. En la pestaña Project Explorer (Explorador de proyectos) en el panel izquierdo
de JBDS, seleccione src/main/java > com.redhat.training.rest. Haga doble clic en
EmployeeRestService.java para ver el archivo.
7.2. Actualice el método getEmployee con una anotación @PermitAll para permitir
que todos los roles enumerados en web.xml puedan acceder al método:
@PermitAll
@GET
@Path("/byId/{id}")
@Produces(MediaType.APPLICATION_JSON)
public Employee getEmployee(@PathParam("id") Long id) {
return employeeBean.readEmployeeById(id);
}
JB183-EAP7.0-es-2-20180124
467
Capítulo 10. Revisión completa: Red Hat Application Development I: Programming in Java EE
7.3. Actualice el método getEmployeesForManager con una anotación @PermitAll
para permitir que todos los roles enumerados en web.xml puedan acceder al
método:
@PermitAll
@GET
@Path("getByManager/{managerId}")
@Produces(MediaType.APPLICATION_XML)
public List<Employee> getEmployeesForManager(@PathParam("managerId")Long
managerId){
Manager manager = managerBean.findById(managerId);
return employeeBean.findAllForManager(manager);
}
7.4. Actualice el método assignEmployee con una anotación @RolesAllowed para
permitir que solo los roles manager y superuser enumerados en web.xml puedan
acceder al método:
@RolesAllowed({"manager", "superuser"})
@POST
@Path("assignEmployee/{employeeId}/{departmentId}")
public void assignEmployee(@PathParam("employeeId")Long employeeId,
@PathParam("departmentId") Long departmentId) {
Employee e = employeeBean.readEmployeeById(employeeId);
Department d = departmentBean.findById(departmentId);
e.setDepartment(d);
employeeBean.updateEmployee(e);
}
7.5. Actualice el método assignEmployee con una anotación @RolesAllowed para
permitir que solo el rol superuser enumerado en web.xml pueda acceder al
método:
@RolesAllowed({"superuser"})
@POST
@Path("assignManager/{managerId}/{departmentId}")
public void assignManager(@PathParam("managerId")Long managerId,
@PathParam("departmentId") Long departmentId) {
Manager m = managerBean.findById(managerId);
Department d = departmentBean.findById(departmentId);
m.setDepartment(d);
managerBean.updateManager(m);
}
7.6. Presione Ctrl+S para guardar sus cambios.
8.
Implemente la aplicación jaas-review mediante los siguientes comandos en la ventana
del terminal:
[student@workstation ~]$ cd /home/student/JB183/labs/jaas-review
[student@workstation jaas-review]$ mvn wildfly:deploy
9.
468
Pruebe el nuevo método REST de comprobación de estado mediante el complemento
(plug-in) de cliente REST de Firefox.
JB183-EAP7.0-es-2-20180124
Solución
9.1. Inicie Firefox en la máquina virtual workstation y haga clic en el icono del
complemento (plug-in) de cliente REST en la barra de herramientas del explorador.
9.2. En la barra de herramientas superior, haga clic en Headers (Encabezados) y
seleccione Custom Header (Encabezado personalizado) para agregar un nuevo
encabezado personalizado a la solicitud.
9.3. Introduzca la siguiente información en el cuadro de diálogo del encabezado
personalizado:
• Name (Nombre): Content-Type
• Value (Valor): application/json
9.4. Seleccione GET como el Method (Método). En el formulario de URL, introduzca
http://localhost:8080/jaas-review/api/healthCheck. No defina una
autenticación. Este servicio debe estar disponible públicamente.
Haga clic en Send (Enviar).
9.5. Verifique en la pestaña Response Headers (Encabezados de respuesta) que Status
Code (Código de estado) sea 200 OK.
10. Use el complemento (plug-in) para verificar que el usuario employee con la contraseña
redhat1! tenga permisos para acceder solo a los métodos GET.
10.1.En el complemento (plug-in) de Firefox, haga clic en Authentication (Autenticación)
y en Basic Authentication (Autenticación básica). Inicie sesión con las siguientes
credenciales:
• Username (Nombre de usuario): employee
• Password (Contraseña): redhat1!
Haga clic en Okay (Aceptar).
10.2.Defina el Method Type (Tipo de método) en GET. Defina la URL como http://
localhost:8080/jaas-review/api/employees/getByManager/1
10.3.Haga clic en Send (Enviar). El servidor devuelve un código 200 OK.
10.4.Repita este paso con el extremo byId y asegúrese de que se devuelva un código de
respuesta 200 OK.
10.5.Cambie el Method Type (Tipo de método) a POST. Defina la URL como http://
localhost:8080/jaas-review/api/employees/assignEmployee/1/2
10.6.Haga clic en Send (Enviar). El servidor devuelve un código 403 Forbidden.
10.7.Repita los dos pasos anteriores con los métodos assignManager y
assignEmployee y asegúrese de que cada uno devuelva un código 403
Forbidden.
11. Use el complemento (plug-in) para verificar que el usuario manager con la contraseña
redhat1! tenga permisos para acceder solo a los métodos correctos.
JB183-EAP7.0-es-2-20180124
469
Capítulo 10. Revisión completa: Red Hat Application Development I: Programming in Java EE
11.1.En el complemento (plug-in) de Firefox, haga clic en Authentication (Autenticación)
y en Basic Authentication (Autenticación básica). Inicie sesión con las siguientes
credenciales:
• Nombre de usuario: manager
• Password (Contraseña): redhat1!
Haga clic en Okay (Aceptar).
11.2.Defina el Method Type (Tipo de método) en GET. Defina la URL como http://
localhost:8080/jaas-review/api/employees/getByManager/1
11.3.Haga clic en Send (Enviar). El servidor devuelve un código 200 OK.
11.4.Repita este paso con el método byId y asegúrese de que se devuelva un código de
respuesta 200 OK.
11.5.Cambie el Method Type (Tipo de método) a POST. Defina la URL como http://
localhost:8080/jaas-review/api/employees/assignEmployee/1/2
11.6.Haga clic en Send (Enviar). El servidor devuelve un código 204 No Content.
11.7.Repita los dos pasos anteriores con el método assignManager. Asegúrese de que
cada uno devuelva un código 403 Forbidden.
12. Use el complemento (plug-in) para verificar que el usuario superuser con la contraseña
redhat1! tenga permisos para acceder solo a los métodos correctos.
12.1.En el complemento (plug-in) de Firefox, haga clic en Authentication (Autenticación)
y en Basic Authentication (Autenticación básica). Inicie sesión con las siguientes
credenciales:
• Username (Nombre de usuario): superuser
• Password (Contraseña): redhat1!
Haga clic en Okay (Aceptar).
12.2.Defina el Method Type (Tipo de método) en GET. Defina la URL como http://
localhost:8080/jaas-review/api/employees/getByManager/1
12.3.Haga clic en Send (Enviar). El servidor devuelve un código 200 OK.
12.4.Repita este paso con el método byId y asegúrese de que se devuelva un código de
respuesta 200 OK.
12.5.Cambie el Method Type (Tipo de método) a POST. Defina la URL como http://
localhost:8080/jaas-review/api/employees/assignEmployee/1/2
12.6.Haga clic en Send (Enviar). El servidor devuelve un código 204 No Content.
12.7.Repita los dos pasos anteriores con el método assignManager y asegúrese de que
devuelve un código 204 No Content.
470
JB183-EAP7.0-es-2-20180124
Solución
Evaluación
Como el usuario student en workstation, ejecute el script lab jaas-review con el
argumento grade para confirmar que ha realizado este ejercicio correctamente. Corrija las
fallas informadas y vuelva a ejecutar el script hasta obtener un resultado satisfactorio.
[student@workstation ~]$ lab jaas-review grade
Después de que la calificación se realiza correctamente, detenga el servidor EAP y cierre el
proyecto en JBDS.
Esto concluye el trabajo de laboratorio.
JB183-EAP7.0-es-2-20180124
471
472
Download