Está en la página 1de 336

R evisado por: Sonia Vives y Anna Simón

Datos catalográficos
López, Ismael
Curso avanzado deJA VA JEE. Manual práctico
Primera Edición
Alfaomega Grupo Ed itor, S.A. de C.V., México

ISBN: 978-607-622-853- 1

Formato: 17 x 23 cm Pági nas: 336

Curso avanzado de J AVA JEE. Manual práctico


Ismael López Quintero
ISBN: 978-84-945683-2-9, edi ción en español publicada por Publicaciones Altaria S.L.,
Ta rragona, España
Derechos reservados © 2016 PUB LICACION ES ALTAR IA, S .L.

Primera edición: Alfaomega Grupo Editor, México, febrero 20 17

© 2017 Alfaomega Grupo Editor , S.A. de C.V.


Dr. Isidoro Olvera (Eje 2 sur) No. 74, Col. Doctores, 06720, C iudad de México.

Miembro de la Cámara Nacional de la Industri a Editorial Mex icana


Registro No. 23 17

Pág. Web: http://www.alfaomega.com.mx


E-mail: atencion alcliente@alfaomega.com.mx

ISBN: 978-607-622-853-1

Derechos r eser vados :


Esta obra es propiedad intelectual de su autor y los derec hos de publicación en lengua
española han sido legalmente transferidos al editor. Prohibida su reproducción parcial o total
por cualqu ier medio sin permiso por escrito del propietario de los derechos del copy right.

Nota importante:
La información co ntenida en esta obra tiene un fin exclusivamente did<'ictico y, por lo tanto,
no está previsto su ap rovechamiento a nivel profesional o industria l. Las indicaciones
técnicas y programas inc luidos, han s ido e laborados co n gran cuidado por e l au tor y
rep roducidos bajo estrictas normas de control. ALFAOMEGA GRUPO EDITO R, S.A. de
C.V. no será jurídicamente responsable por: errores u omisiones; daños y perjuicios que se
pudieran atribu ir al uso de la información comprendida en este libro, ni por la utilizac ión
indebida que pudiera dársele.
Edición autori zada para venta en México y todo el continente americano.
Impreso en México. Printed in Mexico.

Empresas del gr upo:


México: Alfaomega Gr upo Editor, S.A. de C.V.- Dr. Isidoro Olve ra (Eje 2 sur) No. 74, Col.
Doctores , Ciudad de México- C.P. 06720, -Tel. : (52-55) 5575-5022- Fax: (52-55) 5575-2420
/2490. Sin costo: 0 1-800-020-4396- E-rna il: atencio nalcliente@a lfaomega.com.rnx
Colombia: Alfaomega Colombia na S.A. - Calle 62 No. 20-46. Barrio San Lu is, Bogotá. Colom bi a,
Te ls.: (57-1) 746 0102/210 0415 - E-mail: c liente@alfaomega.com.co
Chile: Alfaomega Grupo Editor, S.A.- Av. Providencia 1443. Oficina 24, Santiago, Chile
Te l.: (56-2) 2235-4248- Fax: (56-2) 2235-5786- E-mail: agechile@al faomega.cl
Argentina: Alfaomega Grupo Editor Argentino, S.A.- Av. Córdoba 1215 piso 1O, C.P. 1055, Buenos Aires,
Argentina. - TelJFax: (54-11) 48 11-0887 y 4811 7183- E-mail: ventas@alfaomegaeditor.com.ar
A las personas que quiero y que pacientemente
me animan día a día.
#

Indice general

¿A quién va dirigido el libro? .... ........................ .......... ...... ... ...... .9


Cómo se ha estructurado ellibro .... .... ........ .. .......... .... ... ...... ....... 9

Capítulo 1
Introducción ........................................................................ 11

Capítulo 2
Instalación del entorno de desarrollo ............................... 17

Capítulo 3
¿Qué hay que saber del lenguaje Java y de la POO? ..... 27
3.1 Clases y objetos .. ...... ...... ... ... ... ... ...... .... .. ... .......... ... ..... ......28
3.2 Atributos y métodos ............... ...... ............ ..................... .. .... 30
3.3 Método main . Archivo Manifest .......... ................................ 37
3.4 Herencia ........ ...... ...... ... ...... ...... ............ ............. ......... ........ 39
3.5 Polimorfismo .. ...... ...... ... ... ......... ......... ................ ...... ..... .. .... 45
3.6 Clases abstractas ...... ...... ... ... ... ... ...... ...... ... .... ...... ... ... .... ....48
3.6.1 Métodos implementados en clases abstractas ................... .. .. 52
3. 7 Clases estáticas .. ...... .. ................ ........................ ............... 55
3.8 Implementación de interfaces ...... ............... ...... .................. 57
3.9 Excepciones .. ............... ...... ... ... ... ................................. .. .... 62
3.1O Ejercicio 1 .... ...... ...... ... ... ... ... ... ......... ... ............. ..... ...... .. .... 71

© Alfaomega - Altaria
Capítulo 4
Instalación del entorno servidor de aplicaciones
Oracle Weblogic Server .................................................... 73

Capítulo 5
Arquitectura de software ................................................... 97
5.1 Patrón MVC ........... ...... ........................................ ........... ....97
5.2 Arquitectura de software y servidor de aplicaciones .......... 99
5.3 Capa de presentación ....... ...... ... ... .............. .... ...... ........... 102
5.3.1 Serv/ets ..... .. ... .. .................... ... .. ............................................ 102
5.3.2 Páginas JSP ......................................................................... 111
5.4 Enterprise JavaBeans ...................................................... 120
5.4.1 Beans de entidad. Acceso usando JPA .......................... .. .... 122
5.4.2 Ejercicio 2 ............................................................................. 130
5.4.3 Beans de sesión ................................................................... 130
5.4.3.1 Sin estado: stateless...................................................................................................... 131
5.4.3.2 Ejercicio 3 ...................................................................................................................... 138
5.4.3.3 Con estado: stateful....................................................................................................... 137
5.4.3.3.1 RMI en la JVM local ............................................................................................. 150
5.4.3.3.2 RMI en el servidor de aplicaciones ...................................................................... 153
5.4.3.3.3 RMI. Interfaz remota de stateful EJB ................................................................... 159
5.4.3.4 Instancia única: singleton ............................................................................................... 161
5.4.4 Beans orientados a mensajes ............................................... 168
5.5 Ejercicio 4 .. ........................ ................. ........ ... ... ..... .. ... ...... 180

Capítulo 6
Implementación de MVC: JSF ......................................... 183
6.1 Managed beans .. ... .. .......... ......... ........ ....... ..... .. .... ...... .. ... . 184
6.2 Librerías de etiquetas en las páginas JSF ............ ........... 185
6.3 Renderizando componentes ............................................. 188
6.4 Ejemplo del carrito de la compra ...................................... 193
6.5 Plantillas con Facelets ......................................................203
6.6 Ejercicio 5 .. ........................ .............................. ................. 213

© A lfaomega - Altaria
Capítulo 7
Servicios web .... ................................................................ 217
7.1 RESTful vs. SOAP ............................. ........................... .... 218
7.2 Servicios web RESTful. JAX-RS y Jersey ............... ... ... ... 219
7.2.1 Métodos HTTP ...................................................................... 219
7.2.2 Diseño de la API ........ ............................. .............................. 220
7.2.3 Códigos de estado HTTP ........................ ............................ .. 222
7.2.4 Desarrollo del back-end ........................................................ 222
7.2.5 CRUD con un recurso .. ....................................................... .. 230
7.2.6 CRUD con subrecursos .................................... .. .................. 245
7.3 Ejercicio 6 ......... ... ...... .. .......... ......... ... ....... ........... ......... .. .. 252

Capítulo 8
Websockets ....................................................................... 253
8.1 Lado del cliente ....................................... ............. ... ... ...... 253
8.2 Lado del servidor ............. ... ..................... ............. ...... ... ...255
8.3 Implementación de una sala de chat.. ... ........ .......... ... ... ...256
8.4 Ejercicio 7 .........................................................................258

Capítulo 9
Ejercicios resueltos .......................................................... 263
9.1 Ejercicio 1 .................................... ............ ..................... .... 263
9.2 Ejercicio 2 .........................................................................268
9.3 Ejercicio 3 .........................................................................274
9 .4 Ejercicio 4 ...... ...... ........ .... ...... ... ... ...... ... ... ............... ........ ..277
9.5 Ejercicio 5 ...... .............. .... ............... ... ... ... ....................... ..297
9.6 Ejercicio 6 .........................................................................31 O
9.7 Ejercicio 7 .................................................................. ... .... 315

© Alfaomega - Altaria
¿A quién va dirigido el libro?
Este libro está dirigido a:
• Desarrolladores de software con un nivel avanzado de programación.

Se da por hecho que el lector del libro debe conocer en profundidad la programación
estructurada: los tipos de datos básicos, las estructuras de control, los procedimientos,
las funciones; y los tipos de datos complejos, los registros y las listas.
También, es recomendable que el lector tenga conocimientos de programación orien-
tada a objetos, y que sean familiares para él conceptos tales como clases, atributos,
métodos, herencia e interfaces. Si éste no fuera el caso, se recomienda realizar un curso
previo de programación orientada a objetos. Aunque se aborda un tema de repaso de
programación orientada a objetos, se hace de forma muy somera y como introducción
para los temas posteriores del libro.

Asimismo, es recomendable que se tenga experiencia previa con el lenguaje Java,


aunque seria suficiente con conocimiento previo del lenguaje C++, debido a la similitud
de su sintaxis con Java.

Cómo se ha estructurado el libro


A la hora de redactar un manual sobre Java, se plantea un universo de posibilida-
des debido a que Java en sí es un universo . Podemos plantear un manual enfocado
al lector nobel con conocimientos de programación incipientes o para un lector que
quiere desarrollar aplicaciones de escritorio mediante la biblioteca gráfica Swing. Si lo
planteamos de alguna de las formas anteriores, estamos dejando de centrarnos en la
gran fortaleza del lenguaje Java. Dicha fortaleza se llama JEE (Java Enterprise Edition
o Java Edición Empresarial), que permite desarrollar a rquitecturas de software distri-
buidas en las que los sistemas locales se intercomunican con otros sistemas gracias
al conjunto de herramientas que provee dicha plataforma.

El manual que tiene ante sí se centra en estudiar de forma generalista las principales
características de JEE.

Tras instalar el entorno de desarrollo necesario para poder empezar a desarrollar


aplicaciones Java en su edición estándar , estudiamos en un capítulo, que podríamos
llamar de repaso, las características de la programación orientada a objetos y su im-
plementación en Java, como paso inicial para poder empezar a estudiar la JEE. En el
cuarto capítulo vemos la instalación del servidor de aplicaciones Oracle WebLogic Server,
solución escogida en este manual por su robustez y su facilidad de uso. Posteriormente,
nos centramos en la JEE en sí, con capítulos dedicados a los bloques que componen
una aplicación empresarial y cómo se despliegan en el servidor, tanto en el contenedor

© Alfaomega - Altaria
de EJB como en el contenedor de servlets. Estudiamos los componentes software que
se pueden desplegar en el contenedor de EJB (llamados del mismo modo, EJB), así
como el corazón de los componentes del contenedor de servlets: las páginas JSP y los
servlets. En un capítulo posterior estudiamos el framework de la capa de presentación
que hace más fácil el trabajo con páginas JSP y con servlets: JSF. Sin centrarnos en
ninguna subimplementación de componentes, estudiamos JSF de forma generalista.
Para la conectividad de las aplicaciones dedicamos dos capítulos finales, uno centrado
en los servicios web RESTful y su implementación con JAX-RS (librería Jersey); y otro
capítulo centrado en los websockets.

Los servicios web son una solución que permite interconectar sistemas de datos a
través de Internet. La solución REST (transferencia del estado representacional) se sirve
del protocolo HTTP para poder crear una API sin estado que sirva de acceso universal
al back-end de nuestras aplicaciones, pudiendo crear cualquier tipo de clientes, por
ejemplo, clientes móviles. Con los websockets podemos crear aplicaciones web en las
que los diversos usuarios que están conectados a nuestra aplicación pueden mantener
sesiones activas y ser informados del estado de la aplicación en tiempo real. En defi-
nitiva, un manual sobre JEE que pretende ser generalista y a la vez concreto con las
tecnologías explicadas que nos encontramos en el estudio.

110 © Alfaomega - Altaria


,.
CAPITULO
Introducción

La JEE es el marco de trabajo de las aplicaciones empresariales escritas en lenguaje


J ava. Muchos desarrolladores que se enfrentan a esta tecnología por primera vez en-
cuentran un conjunto de conceptos similares y, del mismo modo, confusos. Conceptos
tales como lenguaje de programación, entorno de ejecución, kit de desarrollo o plataforma
Java están presentes en los manuales que estudian los noveles en esta materia. Java
en si es el lenguaje formal en el que se escriben las aplicaciones que podrán ejecutarse
en cualquier máquina en que esté instalado el entorno de ejecución (también conocido
como máquina virtuaQ Java. Objeto de este manual es aclarar todos estos conceptos.
Igualmente, el manual encaminará su contenido a conocer lo que es el superconjunto
de la plataforma Java, también conocido como Java Edición Empresarial (JEE). Apren-
derá a escribir aplicaciones web en lenguaje Java, así como a poner a disposición de
la comunidad de desarrolladores servicios web que puedan acceder al back-end de sus
aplicacion es.

Java es un lenguaje de programación que nació en el año 1995 de la mano de su


diseñador James Gosling y en el seno de la compañía Sun Microsystems. Lo que se
pretendió fue diseñar un lenguaje orientado a objetos, multiplataforma, cuyas aplica-
ciones fuesen fácilmente accesibles a través de Internet. Del mismo modo, se pretendió
aislar a l programador del problema de la liberación de memoria (problema presente en
los lenguajes previos y que más han influido a la sintaxis de Java: C estándar y C++).
Veamos someramente cada uno de estos conceptos de Java:

• Orientado a objetos. Durante la década de los ochenta y a comienzos de la


década de los noventa, el paradigma de programación imperante era el estructu-
rado. Con dicho paradigma, los programas eran difíciles de mantener a medida

© Alfaomega - Altaria 111


Curso avanzado de Java

que el número de lineas de código crecía; eran difíciles de mantener en el tiempo


cuando los bugs (fallos) se hacían presentes, ya que los módulos eran interde-
pendientes y no se aplicaban los patrones de diseño orientados a objetos actua-
les. El paradigma de los objetos se planteó como una alternativa al paradigma
estructurado, ya que permite encapsular en "clases" la funcionalidad aislada en
distintos módulos de la aplicación. Los módulos dejan de ser tan dependientes
unos de otros y es más fácil localizar los fallos que se encuentren en el software.
Además, comenzaron a aparecer los conocidos patrones de diseño orientados a
objetos, los cuales ofrecen soluciones elegantes a problemas comunes en el di-
seño del software. Hasta la salida al mercado de Java, la mayor aproximación a
la programación orientada a objetos se produjo con C++.

• Multiplataforma. Se conoce como plataforma a la tupla compuesta por la arqui-


tectura de la máquina y el sistema operativo que la hace funcionar. En muchos
lenguajes de programación tradicionales había que recompilar el código fuente
para cada arquitectura en la que se iba a ejecutar el software concreto. Dicha
traducción a código máquina debía hacerse considerando que existiera el compi-
lador para dicho lenguaje en el sistema operativo concreto. Con Java se pretendió
diseñar un lenguaje que sólo necesitase compilarse una vez para una máquina
universal, conocida como máquina virtual Java (JRE, Java Runtime Environment
o Entorno de Ejecución Java). Bajo esta nueva filosofía, el programador no sólo
escribe una vez la aplicación, sino que la traduce a código máquina una sola vez.
El código máquina u objeto que se compila se denomina bytecode, encapsulándose
en ficheros ".class". Es ahora competencia del fabricante de la máquina real en
la que vaya a ejecutarse el bytecode proporcionar un traductor correcto desde
la máquina virtual Java hacia la máquina real. Por este mismo motivo decimos
que Java es a la vez un lenguaje compilado e interpretado. Se compila una vez
en código comprensible por el JRE y se interpreta en tiempo de ejecución en la
máquina real en la que se h a instalado el JRE.
• Aplicaciones fácilmente accesibles a través de Internet. Desde el comienzo
de Java se implementó su sintaxis y sus características pensando en la red de
redes. Los applets de Java fueron una solución en la que, a través del navegador
y mediante protocolo HTTP, se podrían enviar al navegador aplicaciones cliente
que estuvieran en contacto con el servidor que las ha lanzado, creando clientes
ricos (clientes a los que se ha trasladado la competencia de ejecutar parte o
toda la funcionalidad de la aplicación). Del mismo modo, se diseñó para que la
interconexión fuera una de sus mayores virtudes, ideando la JEE en la que los
componentes software residiesen en contenedores de aplicaciones fácilmente
comunicables gracias al sistema de mensajería, la invocación remota de méto-
dos o RMI, CORBA o los servicios web. Todas ellas soluciones pensando en la
ejecución distribuida de aplicaciones, incluso en la ejecución distribuida de los
módulos de una misma aplicación.

112 © Alfaomega - Altaria


JEE. Manual práctico

• Liberación de memoria. En los lenguajes anteriores más usados por la comu-


nidad de programadores (C estándar y C++), la liberación de memoria debía ser
realizada de forma explícita por el desarrollador. El uso de las instrucciones ma-
lloc y realloc en C estándar y de new en C++ debía realizarse para la reserva de
memoria. Y las instrucciones free y delete debían invocarse en los dos lenguajes
respectivamente para su liberación, haciendo que el manejo de la memoria diná-
mica fuera algo más que un tedio. Java quiso liberar de este engorro al progra-
mador, haciéndole olvidar por completo el asunto de la liberación de memoria.
Cuando un bloque de memoria deja de ser apuntado por una referencia, este
bloque queda como basura. Existe un servicio en ejecución por la JRE (entorno de
ejecución) que se encarga de estudiar los objetos y la memoria usada por éstos,
y de vigilar que todos los objetos en memoria estén apuntados al menos por una
referencia. Este servicio es el recolector de basura. Cuando algún objeto deja de
ser apuntado por al menos una referencia, el recolector de basura se encarga
de forma autónoma de dicha liberación. En Java, la liberación de memoria se
realiza de forma implícita por parte del entorno de ejecución.

Como se ha comentado, existen un conjunto de conceptos confusos cuando nos


enfrentamos al estudio de Java. Algunos de ellos los explicamos brevemente en la
siguiente lista:

• Lenguaje de programación Java. Es un lenguaje formal, con una sintaxis pare-


cida a la de C++, orientado a objetos, multiplataforma, ideado para aplicaciones
en red y con liberación de memoria implícita.

• Entorno de ejecución o JRE. Es la máquina virtual en la que se ejecutan las


aplicaciones escritas en lenguaje Java. El código Java se compila a un lenguaje
intermedio, creando un código máquina conocido como bytecode.

• Kit de desarrollo. También conocido como JDK o Java Development Kit, es el


conjunto de herramientas que permiten al programador desarrollar aplicaciones
en Java. Digamos que es un superconjunto de la JRE. El kit de desarrollo de
Java está compuesto por los siguientes componentes (entre otros):
"appletviewer.exe": para desarrollo de applets (aplicaciones cliente que se
lanzan a través de la red).
"javac.exe": el compilador y traductor de código Java a bytecode.
"java.exe": es el punto de entrada al entorno JRE .
"javadoc.exe": esta aplicación se usa para desarrollar documentación de los
artefactos de Java.

• Plataforma de Java. Tradicionalmente se han conocido tres plataformas de


Java: la J2ME, la J2SE y la J2EE. Las plataformas de Java son conjuntos de API
(Application Programming Interfaces o Interfaces de Programación de Aplicaciones),
encaminadas a l desarrollo de distinto tipo de aplicaciones. Son desarrolladas bajo

© Alfaomega - Altaria 131


Curso avanzado de Java

el Java Community Process, que es un proceso de estandarización de la platafor-


ma Java que permite a las partes interesadas en el desarrollo de las plataformas
(empresas, personas físicas o j urídicas) involucrarse en la especificación de las
características de dichas plataformas. Las especificaciones se realizan mediante
las Java Specification Request o JSR. Las tres plataformas mencionadas han cam-
biado de nombre en las últimas versiones de sus plataformas a JME, JSE y JEE.
JME: Java Micro Edition. Es un conjunto de API o librerías encaminadas al
desarrollo de aplicaciones que se ejecutarán en máquinas de recursos reduci-
dos, tales como tabletas, teléfonos móviles o PDA. Cabe decir que la JME está
obsoleta debido a l desarrollo de sistemas operativos nativos para dicho tipo
de dispositivos: Android, íOs y Windows Phone, entre otros. Tenemos lengua-
jes y entornos de desarrollo para programar en dichos sistemas operativos,
o soluciones híbridas multiplataforma: Apache Cordova o Adobe PhoneGap.
JSE: Java Standard Edition. Colección de librerías necesaria para pro-
gramar aplicaciones Java en dispositivos de escritorio y servidores. Para
desarrollar aplicaciones que se ejecutarán de forma general en una máquina,
usaremos la JSE.
JEE: Java Enterprise Edition. Es la edición empresarial de Java sobre la
que versa este manual que tiene en sus manos. Contiene todas las librerías
necesarias para crear aplicaciones distribuidas de N capas, aplicaciones web,
servicios web, etc. Las aplicaciones empresariales corren sobre servidores de
aplicaciones.

Las tres plataformas mencionadas no son independientes, sino que son complemen-
tarias, estando una incluida dentro de otra. El esquema bajo el que se encuadran las
tres plataformas es el siguiente:

JEE

JSE

JME

114 © Alfaomega - Altaria


JEE. Manual práctico

Podemos ver que la JSE incluye a la JME (aunque obsoleta, sus librerías siguen
existiendo). Y que la JEE incluye a la JSE y a la JME.

Este manual que tiene ante usted repasa las nociones básicas del lenguaje Java. Una
vez instalado el entorno de desarrollo y repasada la sintaxis básica de Java y su uso, nos
centraremos en instalar el servidor de aplicaciones bajo el cual se ejecutarán nuestras
aplicaciones: WebLogic Server de Oracle. En el quinto capítulo se mostrará el acceso a
datos usando la API de persistencia de Java (JPA). Acto seguido nos centraremos en la
lógica de negocio y cómo crear objetos del dominio (Enterprise JavaBeans o EJB) que se
ejecuten en el servidor de aplicaciones. Dedicamos un capítulo a crear una aplicación
web usando el framework JSF. Del mismo modo, dedicaremos un capítulo final a crear
un servicio web RESTful que sirva para a limentar a la capa de presentación de una
aplicación móvil desarrollada bajo la solución híbrida Apache Cordova.

Los prerrequisitos para enfrentarse a la lectura de este manual es un dominio en


profundidad de la programación estructurada. También es conveniente que el lector
tenga conocimientos de la programación orientada a objetos. En el capítulo 3 se repasan
los conceptos fundamentales de este segundo paradigma de programación. También
es conveniente que el lector conozca el lenguaje de modelado unificado (UML). El libro
no pretende ser un manual de lenguaje Java, sino un manual de uso de Java Edición
Empresarial. En el capítulo 3, junto con la programación orientada a objetos, repasa-
remos sintaxis básica del lenguaje Java que el lector debería dominar.

© Alfaomega - Altaria 151


,
CAPITULO
Instalación del
entorno de
desarrollo

2.1 Instalación
Se mostrará cómo instalar el entorno de desarrollo de Java en un sistema Windows
de 64 bits. Para comenzar a desarrollar aplicaciones Java necesitamos dos componentes:

• JDK (Java Development Kit) para la JSE incluyendo la máquina virtual Java
(JRE). Necesitamos dicho componente (JDK) para poder compilar nuestras apli-
caciones a bytecode, y la JRE para poder ejecutar dicho bytecode.

• Entorno de desarrollo integrado. Es la aplicación nativa de nuestro sistema


operativo que va a permitir tareas cruciales en nuestro desarrollo. Las tareas son:
Creación y estructuración de proyectos.
Escritura del código fuente.
Ejecución del depurador.
Compilación.
Ejecución de pruebas unitarias.
Empaquetado.

Y tantas tareas relacionadas con el desarrollo como se nos ocurran.

Podemos elegir el entorno de desarrollo integrado o lOE (Integrated Development En-


vironment) para Java que queramos: Eclipse, NetBeans, BlueJ, JBuilder, JDeveloper ...
En nuestro caso usaremos NetBeans por dos motivos:

• Es fácil de usar y muy intuitivo.


• Es suministrado por Oracle, la compañía que adquirió Sun Microsystems y que
se encarga de empaquetar las plataformas de Java.

© Alfaomega - Altaria 171


Curso avanzado de Java

Para proceder a la descarga de ambos componentes nos vamos a la siguiente URL:


"h ttp: / f www .oracle.com/ technetwork/java/javase / downloads / index.html".

.... e

!: ll!ll:IE

.f java·
i llllkf
! Mft=ci!Pf

! lm ~.t<oo»m:

! lltt;e10!1"jM•}
• t,1W'*'" tlstntdo.., i """"
. ~--Ner)G i .w.=.
• O"Ml•tl u -
• J:onSERI)4 ~d:

• ~~-~~ UWnlln
• c;..M"" $)'*.. COnl!l l,fJIIIOI!Ti
. ~.n"
· JOICfle~lt
• JI!$ Jlt :l(f.i O

Nos descargamos los ejecutables de dichos componentes.

[4.] jdk·8u77·windows·x64 05/04/2016 20:32 Apli cación 191.806 KB


() netbea ns-8.1-wüncfows 05/04/2016 19:41 Afl'licación 221 .349 KB

Y comenzamos la instalación de la JDK.

Welcome t:o the Instalation Wirard for Java SE Deveiopment Kit!! Update 77

This vvizard vvill 9uide you t!ugugh !he installation process for the Java SE Development
Kit 8 Update 77.

lhe Java Mission Control profiling and diagnostics tools suite is now availai:>le as part of
theJOK.

Ne_x_t _>__~l lL ___c_


L __ _ an_~-'--~

118 © A lfaomega - Altaria


JEE. Manual práctico

Como hemos indicado, la instalación de la JDK nos solicita la ubicación de la JRE


que lleva incorporada. Le decimos que sí a la ubicación por defecto.

carpeta de Destino

Haga clic en ··cambiar·· para instalar Java e·n otra tarpeta.

Instalar en: Cambiar...


C:\P"rogram Files\Java\jrel.-8.0_77

<Atrás

Y finalizamos la instalación de la JDK.

lava SE Developmen tKit 8 Update 77 (6+bít) Successfully Instafled

Clidc Next Steps to access b.Jtorials, API documentation, developer guides, release notes
and more to he:lp vou get started with !he JDK.

Neld: Steps

Oose

© Alfaomega - Altaria 191


Curso avanzado de Java

Acto seguido procedemos a la instalación de NetBeans. Podemos observar que la


instalación de NetBeans proporciona las plataformas JME, JSE y JEE. Del mismo
modo, nos instalará un servidor de aplicaciones de libre distribución para despliegue
de aplicaciones JEE: GlassFish.

NetBeans IDE lnstaller -Cl--


\Velcome to the NetBeans lOE 8.1 Installer

lhe hstaller will installlhe lle!Beans lOE with the following JllliCb an;l runtimes.
Click Customize to select the JllliCb and runtimes to irsml.

eJ!Ic xoe
lavaSE
.l:lvaEE
Java ME
tfTML5/.:bvcSoPt
I'HP
C/C ++
Groovy
Java card ... 3 connected
Fcab.lrca on Ocmard

Runtime:s
GlassFish Server Open Sotrce Editen 4. 1.1

0J!:tomi2e.,, l n!:tollafun Size: 771~ S MB

Aceptamos los té rminos y condiciones.

li<:ense Agree~nent
PlcD!It r<:Dd the follov.in¡» ~ o.gr<:!c:ment Cl)rcfi.,lly.


~
NETBEANS !DE 8.1 ('Producl") U CENSE AGREEMENT

PLEASE READ THE Fa.LOWING UCeiSE ACREEfvENT TEJU.tS ANI>


CONOffiONS CARER.l.l.Y, INCLUOING VIITHOJTLINITATIONlHOSE
Ol9'LAYED ELSEWHERE (AS I>OICAlEO BY LIIJKS Ll!mD BEI.OW},
lléFORE USING THE SOFTWARE. THESE l<RMS ANO CONOmOIIS
CONSTIT1m A LEGAL AGRffM:Nl' BETWEEN YOU, OR THE emrY FCR
WHICH YOU ARE AN AIJlHORlZB) REPRESENTAliVE WII11 FUll
AUTHOR!TY TO ENTER UITO llllS AGREEMENT, ANO ORACLE. 6Y
CLICKJNG 'ACC'f11r OR THE EQUJVALENT YOU AGREE TO AU Of
THE l<RMS ANO CONDffiONS OFTHIS LICENSE AGREEMENT. lf YOU
DO NOT AGREE TO THIS U CENSE 00 NOT CLICK 'ACCEPT' CR
THE EQUIVALENT ANO DO I\'OT INSTALL OR USE THJS SOffi~ARE.

Copyright (e) 1997, 2015, craae andfor itsafliliates. Al


rsghts reserved.

Orcclc: ord Jovc ore regislcted trcdcm.::ark:s ofOrcdc: ord/or


ib oflilotcs. Othc:r nemes rt'C)y be tro:Jcmork::; of thc:r
respective owncr:;.

r.l ....................................... --=--::::. . . . . .. . . .. . .,


"'-' 1..~.~».\.lt!~.~.~·~.!:l-~......,.!!ll!:~!!!~~~

< Bad< 11 Next > 11 Cancel

120 © Alfaomega - Altaria


JEE. Manual práctico

Le indicamos dónde se encuentra la JDK para la JSE instalada previamente.

Net8e:a11s lDf 8.1 InstallaOOn


Choose the instalatian fulder and DK ~ .

Install the NeiBEans !DE to:


lcc..::'f'...:.rogr
c: ~om :::..:..:
Fks=.:.'~'~<:.:.ts<:=•:.::
"'...:.s:.:.
·'_ _ _ _ _ _ _ _ _ _ _ _ _ _ ___,ll a,ow:;e . ..

J[:K- ro.. the Ne!Beons IOe:


lc...:
._ :'f'_rogr
~..-· f_ic_•..:.
\lov
_ •''id_
-kL_.s_.o.:.
_n_ _ _ _ _ _ _ _ _ _ _ _ _ _ _,_ ,ll 6roN:;c •••

<llod 11 Ncxt> 1 1 Concd

Indicamos la ruta para la instalación del servidor de aplicaciones GlassFish.

Glassfish 4.1.11nstallation
Choose the ilstalation folder. ® Ndlelalll
Install G'assfish lo:
._ : 'i'_rog
le ...: -=-am
_ fle_s-=
'(¡fa
;._ssfi_sh_-4
_._l._l _ _ _ _ _ _ _ _ _ _ _ _ _ _ __,j j Browse •.•

JDK- for GlassRsh aoolialtion server:


._l
c...:
:V'_'og
~ ram
_ Fie_•..:.
l);l_v_
•~::..·d_
k t_.a_.0.::._7_
7 _ _ _ _ _ _ _ _ _ _ _ _ _ _...:..J
v j1 Brow!:e ...

<Bad< 11 Next > 11 cancel

© Alfaomega - Altaria 211


Curso avanzado de Java

Procedemos a confirmar la instalación del IDE.

Summary
Cick Instan to start the insllllation.
' t.' ' . . •
111

Nell!eans lOE lnstallation Folder:


C:'f'rogram Files\j\letseans 8.1

GlassFish Server Open Source Edticn -4.1.1 lnstallation Fdder:


C: 'f'rogr;om Files'Qiassflsh-1. 1.1

~ Check for Updates


1he NetBeans installer can autornalically check fOr updates of instaled plug'ins
using your Internet connedion.

Total Installation Size:


nl,5M6

<Back 1 [ )nsbiij l 1 Cancel

Completamos la instalación.

Setup Complete
Ckk Finish b:> flnish the Netseans lOE setup.

Installation completed sucoessfully.


101 updates successfuny instaled.

To launch the lOE, use either the Start menu or the NetBeans desktop icon.

To change inst:alled components and add NetBeans plugins, use Pkgn Manager that is an int~al part of NetBeans
!DE.

~ Contribute to the Netseans project by providing anonymous us:age data

• lf you agree to parlkbate, the lOE will keep trad< of the ligh.fevel ~atures you use
• The collected anonymous data will be submit1ed toa us:age statistics databa se at netbeans.org server

The usage statistics i\'l ~ the development team to better mderstand user requiremenls and prioritize
i'nprovements in future releases. We cannot and will not reverse-engineer that rollected data to fnd spedfic
details concerning yOIS projects. See more information.

F'iniSii il
¡r...........................

122 © Alfaomega - Altaria


JEE. Manual práctico

Una vez instalado el entorno de desarrollo, podemos echar a andar NetBeans para
desarrollar nuestra primera aplicación Java.

Para crear nuestra primera aplicación Java, seleccionamos la opción "Archivo ->
Nuevo Proyecto" (New Project).

Debug Prof[fe Team Herramientas Ventana Ayuda

(;11 Open Project ...


OIP'en Recent Project
Clase Project
Clase Other Projects
CloseAII Project~

Abrir archivo ...


Abrir archivo reciente

ii>r·oject Groups...

r_oJ_
1-l-'
il ·e_c.t_P
_r_o_p_ert s _ _ _ _ _ _ _ _ acer una visita guiada
_i_e_

l'mport P' oject • rroll>arr un proyecto de ejemplo


Exporrt Project •
nuevo en el iDE
Salir

© Alfaomega - Altaria 231


Curso avanzado de Java

Nuestro nuevo proyecto es de tipo Java: "Java Application".

l. Choose Prciect
2. ... Q, '"""" L-------------------------------------~
C . -ies:
:~ Java
i--{)1 JavaFX Java Class Lbrary

~ ij!, J.Jva Wcb


Java ProjectWth Existila Sot.rces
Proyecto .bvoFrc:c-fonn
Hll JavaEE
i-Q\ ~ffNLS/Ja•laSO'iPt
!--{)~ Java M ee>>bedded
1 Q1 Jav;, C<Jrd
~-(l, ~'·en
Hll PHP
l--{)1 G-oovy
!--{)~ C/C++ V

Crear una nueva iplicaeión Java 5f en un proyecto estiinda- def IDE. Tadién puede generar
ooa d:lse oríndoal en el proyecto. Provecto estándar usa un scrit.JJ Ant oene:ndo DOr eliDE
por~ <Onotruir, cjceub', y dq)Ur.Y c:lpro•;ccto.

<Atrás ~te> Termma car.ala' 11 A)\Jda

A continuación creamos un nuevo proyecto llamado HolaMundo. Va a tener una clase


principal con el método main en el paquete principal.

Pasos Nombre y ubicacicin

l. Oloose A'oject Noobe P'OYecto: 1HJiaJ.Itrdo


2. Nombre y ubicación

lbcaóón del proyecto: le:'iJ.ersllsma~menls'l'letBeansProjeds 1 1 Examinar ...


Corpoall)foyecto:

O l.har una carpeta dedicada para aina(cnar laa bibliotecas


C.rpEta de Eíbliotecas: Examinar ...
Usuarios y proyectos diferer.tes pueden co~ Las
mi9'nas Iberias de ccmoiadón (ver la A\I'UCa oora más
delales).

< Atrás Termimr IJ Coralar IJ Ayuda

124 © A lfaomega - Altaria


JEE. Manual práctico

Escribimos el siguiente código en nuestra clase "HolaMundo.java":


package principal;
public class HolaMundo {
public static void main(String[] args) {
System.out.println(" Hola M undo ");
}
}
Con el botón derecho del ratón nos posicionamos sobre el proyecto y lo compilamos:
opción "Clean & Build".

Archivo Editar Ver Nla vegar Fuente Ree:structiurar Ru n li}ebu:g

tJ 'EJ ¡;¡ 'IJ11l) t.- l l<defaultconfig> v 1

Pre staciones
8·-

Gener.a te Javadoc

Run

Finalmente, lanzamos la aplicación sobre la JRE. Seleccionamos "Run".

Arthivo Editar Ver Navegar Fuente Reestructurar Run l>ebug Profi'le

! 'EJ 'EJ ~ 'iJ !11,) f!J ! l<defaultconfig> •

New •
Build
Clean ami Build
Clean

Rul!l

Profile
Test Alt+F6

La salida por consola de nuestra aplicación será la siguiente:

Hola Mundo

© Alfaomega - Altaria 251


,
CAPITULO
¿ ué hay que sa-
ber del lenguaje
Java y de la POO?

El estudiante que quiera conocer el lenguaje Java debería conocer previamente y


en profundidad el paradigma de la programación estructurada. Conceptos tales como
tipos de datos simples y complejos (registros, listas ... ), estructuras de control, funciones,
procedimientos, paso de parámetros y librerías deben estar en el argot técnico del lector.
Si éste no fuera el caso, se recomienda la lectura de un manual previo de introducción
a la programación . Del mismo modo se recomienda que previamente se desarrollen
aplicaciones a nivel de programación estructurada. La programación se aprende con
muchas horas de máquina.

También es importante que el lector conozca el paradigma de los objetos. Dicho


paradigma se conoce como POO o Programación Orientada a Objetos. De todas formas,
en este capítulo repasaremos los siguientes conceptos de programación orientada a
objetos, a la vez que veremos su implementación en lenguaje Java, lenguaje orientado
a objetos por excelencia. Los conceptos serán los siguientes:

• Clases y objetos .
• Atributos y métodos .
• Método main. Archivo Manifest.
• Herencia .
• Polimorfismo .
• Clases abstractas .
• Clases estáticas .
• Implementación de interfaces .
• Excepciones .

© Alfaomega - Altaria 271


Curso avanzado de Java

3.1 Clases y objetos


Como se comentó en la introducción, las aplicaciones complejas eran dificiles de
mantener usando el paradigma estructurado. Para evitar la interconexión de artefactos
de código fuente en las aplicaciones con varios miles de líneas de código, se pensó en
un nuevo paradigma de programación en el que las unidades de ejecución se aseme-
jaran a los objetos del mundo real. Por ejemplo, si pensamos en una persona (objeto
del mundo real), podríamos crear un molde para las personas (a dicho molde lo deno-
minamos clase), creando instancias particulares de la persona, denominadas objetos.
De esta forma, cada objeto persona es responsable de sus propias características y de
realizar las acciones correspondientes en sus características. Como diría el sabio "más
sabe el tonto en su casa que el sabio en la ajena".
Imaginemos que una persona tiene las siguientes características:
• Color de pelo.
• Color de gafas.
• Idiomas que habla.
• Alergias que sufre.
• Cuantas características más se nos ocurran.
De este modo, cada persona es responsable de teñirse el pelo del color que quiera (si
es que quiere), es responsable de comprarse las gafas de un color u otro, de aprender
más idiomas o p e rfeccionar los que sabe, de ir al alergólogo o no ...
Digamos que en el paradigma estructurado, al no existir el ámbito de privacidad,
era fácil que un módulo externo modificase las características privadas de un objeto.
En la programación orientada a objetos, un objeto diferente al propietario de las carac-
terísticas no debería modificarlas si no es a través de las acciones que el objeto tiene
establecidas. En otras palabras, es como si le dijéramos al objeto "debes teñirte el pelo
de tal color", siendo el objeto responsable del teñido. O sea, cada objeto es responsable
de modificar sus características.

En el ámbito de la POO, la clase anterior se modelaría de la siguiente forma:

Persona.

-color de pe lo.
-color de gafas.
idiomas que habla.
alergias que sufre .

+teñir el pelo.
+comprar gafas de otro co lor.
+apre nder un nuevo idioma.
+perfeccionar un idioma.
+ir al alerg ólogo.

128 © Alfaomega - Altaria


JEE. Manual práctico

Como podemos ver en este ejemplo, las características propias de una clase (molde
de un objeto) las señalizamos precedidas de un signo "-", porque son privadas; y las
acciones públicas que pueden invocar otros objetos las precedemos de un signo " +".
Una clase también puede tener acciones privadas que sólo pued en invocar las accio-
nes propias d e la clase. Por ejemplo, podríamos tener la acción "ducharse", invocable
desde cualquier acción de las anteriores , pero que sólo puede ejecutar la propia clase .

Persona. 1

-color de pelo.
-color de gafas.
-i diomas que ha bla.
-alergias que sufre.

+teñir el pelo.
+comprar gafas de otro color.
+aprender un nuevo idioma.
+peñeccionar un idioma.
+ir al alergólogo.
-du charse.

Lo que h emos visto hasta ahora es un ejemplo de clase con características y acciones.
Un objeto no es más que una instancia particular de una clase. Por ejemplo, podríamos
tener un objeto de la clase "Persona" llamado Ismael. En el lenguaje UML (lenguaje de
modelado unificado), quedaría como sigue:

Ismael.

-color de pelo: negro.


-color de gafas: negro.
-idiom as que habla: inglés, portugués, caste llano.
-alergias que sufre: ácaros del polvo.

+teñir el pelo.
+comprar gafas de otro color.
+aprender un nuevo idioma .
+perfecci onar un idioma.
+ir al alergólogo
-ducharse .

© Alfaomega - Altaria 291


Curso avanzado de Java

3.2 Atributos y métodos


Hemos visto los dos conceptos fundamentales de POO: clases y objetos. Del mismo
modo hemos tenido una aproximación a los atributos y los métodos. En nuestro ejem-
plo, podemos realizar la siguiente igualdad:

• Caracteristicas = atributos.

• Acciones = métodos.
Comencemos con el código Java. Los ficheros fuente en Java deben estar dentro de
paquetes. Un paquete es una abstracción de un espacio de nombres. Digamos que una
clase puede ver todas las clases que hay en su paquete. Para poder ver las clases que
hay en otros paquetes debe importar las clases una por una o bien importar el paquete
completo en el que está la clase.

Lo normal es que los atributos de una clase sean privados y que los métodos de
la clase sean públicos. Como ya se ha comentado también, pueden existir (de hecho,
existen habitualmente) métodos privados de la propia clase.

Existen muchas formas de definir los atributos de las clases. Tantas como tipos de
datos simples. Los tipos de datos enumerados se definen de la siguiente forma en Java:

package paquete;
public enum ColorPelo {
NEGRO, RUBIO, PEliRROJO, CASTANO;
}

Lo cual, volviendo a lo que hemos explicado, indica que dicho tipo de dato enumerado
llamado ColorPelo pertenece al paquete llamado paquete y que sus posibles valores son
negro, rubio, pelirrojo y castaño. Los valores de los tipos enumerados se definen en
mayúscula por convención. Veamos el código que define el ejemplo de la clase "Perso-
na". Los valores de los atributos serán enumerados .

El modelo del dominio del ejemplo que vamos a ilustrar en Java es el siguiente:

130 © Alfaomega - Altaria


JEE. Manual práctico

Persona

• colorPelo: enum;
• colorGafas: enum;
-idiomas; Nillelldioma
-alergias ;

¡-------------u·:______:·~ idioma: (enum)


~ nivel: int
...
Personao;
...
cambia rColorPe lo{enum): voiá;
...
cambia rColorGafas(enum): voia; + getters & setters
...
mejorarldioma(Nivelldioma): void;
~ aprenderldioma{ldiomas): void;

~ detectarAiergia(Aiergias) void;
~ mostrarPersonaQ : vo id;
- encontrarldioma(ldiomas): int;
- encontrarAiergia(Aiergias): boolean:

'1 *

Alergias

(enum)

Del modelo del dominio podemos deducir que la clase "Persona" tendrá dos listas, una
de alergias y otra de nivel de idiomas . Veamos su traducción al lenguaje Java. Primero
veamos la estructura de los ficheros de código fuente y su distribución en paquetes.

EJ...~ OO]fjemPoPersona
ti
13.. Pa(JJf!tes de fuente~
8
E§ domillio
!·ij Alergias.java
i·l!
ColotGafas.java
¡. ~ Colo<Pelo .java
¡... ~ I cfiomas.java
¡. . ~ r-ivelldioma.java
! !.... ~ Persona.java
e m ptir>q>al
, L..~ Mai1.java
$· ~ Pa(JJf!tes de prueba
$·l'j Bib&ot =
IÍI· [j Bibfioleals de pruebas

Hemos creado dos paquetes: dominio (con el modelo del dominio) y principal (con el
método main). Evidentemente, todas las clases y enumerados son visibles dentro del
mismo paquete. Para hacer uso de las clases y enumerados en el paquete principal

© Alfaomega - Altaria 311


C urso a vanza do de Java

hemos de usar las correspondientes instrucciones import. Veamos los ficheros de código
fuente. Empecemos por los enu merados.

• "ColorPelo.ja va ":
package dominio;
public enum ColorPelo {
NEGRO, RUBIO, PELIRROJO, CASTANO;
}

• "ColorGafas.ja va ":
package dominio;
public enum ColorGafas {
NEGRO, GRIS, AZUL, ROJO;
}

• "Idiomas.j ava ":


package dominio;
public enum Idiomas {
CASTELLANO, INGLES, FRANCES, PORTUGUES, ALEMAN, CHINO;
}

• "Alergias .java ":

package dominio;
public enum Alergias {
ACAROS, LACTOSA, POLEN, GRAMINEAS;
}

• Clase "Nivelldioma" con su s getters & setters : "Nivelldioma .java ":


package dominio;
public class Nivelldioma {
private Idiomas idioma;
private int nivel;
public Idiomas getldioma() {
return idioma;
}
public void setld ioma(ldiomas idioma) {
this.id ioma =idioma;
}
public int getNivel() {
return nivel;
}

132 © A lfaomega - Altaria


JEE. Manual práctic o

public void setNivel(int nivel) {


this.nivel = nivel;
}
}

• Clase "Persona.java":
package dominio;
import java.utii.Arraylist;
import java.util.lterator;
import java.utii.List;
public class Persona {
prívate ColorPelo colorPelo;
prívate ColorGafas colorGafas;
prívate List<Nivelldioma> idiomas;
prívate List<Aiergias> alergias;
public Persona(){// Constructor de la clase
this.colorPelo =ColorPelo.CASTANO;
this.colorGafas =ColorGafas.NEGRAS;
this.idiomas = new Arraylist<Nivelld ioma>();
this.alergias = new Arraylist<Aiergias>();
}
public void cambiarColorPelo(ColorPelo nuevoColor) {
this.colorPelo = nuevoColor;
}
public void cambiarColorGafas(ColorGafas nuevoColor) {
this.colorGafas = nuevoColor;
}
11 Prerrequisito: el idioma debe estar en la lista.
11 Sí no existe se añadirá como nuevo idioma.
public void mejorarldioma(Nivelldioma nivelldioma) {
Idiomas idioma = nivelldioma.getldioma();
int lugarlista = this.encontrarld ioma(idioma);
if (lugarlista == -1) {
this.id iomas.ad d( nivelldioma);
} else {
Nivelldioma nivelldiomaEncontrado
= this.idiomas.get(lugarlista);
ni ve lid ioma Encontrado.setN ivel (n ivelld iom a .getN ivel ());
}
}
11 Prerrequisito: el idioma no debe estar en la lista.
11 Sí existe no se añadirá.
public void aprenderldioma(ldiomas nuevoldioma) {
int lugarlista = this.encontrarldioma(nuevoldioma);
if (lugarlista == -1) {
Nivelld ioma nivelldioma =new Nivelldioma();

© Alfaomega - Altaria 331


Curso avanzado de Java

n ivelld ioma .setldioma( n uevold ioma);


nivelldioma .setN ivei{O);
th is.idiomas.add( n ive lid ioma );
}
}
11 Prerrequisito: la alergia no debe estar en la lista.
11 Si existe en la lista no se añadirá.
public void detectarAiergia{Aiergias nuevaAiergia) {
if (!this.encontrarAiergia(nuevaAiergia)) {
th is.a lergias.add (nuevaAiergia);
}
}
public void mostrarPersona() {
System.out.println("Los datos de la"
+"persona son los siguientes.".);
System.out.print("EI color del pelo es:");
if (this.colorPelo == ColorPelo.NEGRO) {
System .out. println ("negro".);
} else if (this.colorPelo == ColorPelo.RUBIO) {
System .out. println ("rubio".);
} else if (this.colorPelo == ColorPelo.CASTANO) {
System .out. p rintln ("castaño".};
} else if (this.colorPelo == ColorPelo.PELIRROJO) {
System .out. p rintln ("pelirrojo".);
}
System.out.print("EI color de sus gafas es: ");
if (this.colorGafas == ColorGafas.AZUL) {
System .out. println (" azu 1". );
} else if (this.colorGafas == ColorGafas.GRIS) {
System .out.println ("gris" .);
} else if {this.colorGafas == ColorGafas.NEGRO} {
System .out. println ("negro".);
) else if (this.colorGafas == ColorGafas.ROJO) {
System .out. p rintln ("rojo".);
}
lterator it = this.idiomas.iterator();
if (!it.hasNext()) {
System.out.println{"No habla idiomas".);
} else {
System.out.println{"Los idiomas que habla son:");
while (it.hasNext()) {
Nivelldioma niveiEsteldioma = (Nivelldioma) it.next();
Idiomas idioma= niveiEsteldioma.getldioma();
int niveiEsteldiomaValor = niveiEsteldioma .getN ivel();
if (idioma == ldiomas.ALEMAN) {
System.out.println("- " +"Alemán"

134 © A lfaomega - Altaria


JEE. Manual práctico

+ "- Nivel:" + niveiEsteldiomaVa lor + "".);


} else if (idioma== ldiomas.CASTELLANO) {
System.out.println("- " +"Castellano"
+"- Nivel:"+ niveiEsteldiomaVa lor + "".);
} else if (idioma== ldiomas.CHINO) {
System.out.println("- " +"Chi no"
+ "- Nivel: " + niveiEsteldiomaVa lor + "".);
} else if (idioma== ldiomas.FRANCES) {
System.out.println("- " +"Francés"
+"- Nivel: " + niveiEsteldiomaVa lor + "".);
} else if (idioma== ldiomas.ING LES) {
System.out.println("-" +"Inglés"
+ "- Nivel: " + niveiEsteldiomaVa lor + "".);
} else if (idioma== ldiomas.PORTUGUES) {
System.out.println("- " +"Portugués"
+"- Nivel:"+ niveiEsteldiomaVa lor + '"'.);
}
}
}
it = this.a lergias.iterator();
if (!it.hasNext()) {
System.out.println("No tiene alergias" );
} else {
System.out.println(" La persona sufre las siguientes alergias:");
while (it.hasNext()) {
Alergias estaAiergia = (Alergias) it.next();
if (est aAiergia == Alergias.ACAROS) {
System.out.println("- Alérgico a los ácaros del polvo".);
} else if (estaAiergia == Alergias.GRAMINEAS) {
System.out.println("- Alérgico a las gramíneas".);
} else if (estaAiergia == Alergias.LACTOSA) {
System.out.println("- Intolerante a la lactosa".);
} else if (estaAiergia ==Alergias. POLEN) {
System.out.println("- Alérgico al polen".);
}
)
}
}
private int encontrarld ioma(ldiomas id ioma) {
int i = O·1
boolean encontrado= false;
lterator it = this.idiomas.iterator();
while (it.hasNext() && !encontrado) {
Nivelld ioma esteNivelldioma = (Nivelldioma) it .next();
Idiomas esteld ioma = esteNivelldioma.getldioma();
if (este Id ioma == idioma) {

© Alfaomega - Altaria 351


Curso avanzado de Java

encontrado = true;
} else {
i++'
'
}
}
if (!encontra do} {
i = -11·
}
return i;
}
private boolean encontrarAiergia(Aiergias alergia) {
boolean encontrado= fa lse;
lterator it = t his.alergias.iterator(};
w hile (it.hasNext(} && !encontrado) {
Alergias estaAiergia = (Alergias) it.next(};
if (estaAiergia == alergia) {
encontrado = true;
}
}
re t urn encontrado;
}
}

• Fina lmente, la clase principa l que h ace ej ecu tar tod o el código, "Ma in.java":
package principa l;
import dom inio .*;
public class M ain {
public static void main(String[) args) {
Persona ismael = new Persona(};
ismael.cam biarColorPelo( ColorPelo.N EG RO);
ismael.cam biarColorGaf as( ColorGafas. NEGRO);
ismael.aprenderldio ma(ldiomas.CASTELLANO);
ismael.a prenderldio m a( Id iom as.l NG LES);
ismael.a prenderldiom a( Idiomas. PO RTU GU ES);
Nivelldioma nivelldiomaCastellano = new Nivelldioma(};
Nivelldioma nivelldiomalngles = new Nivelldioma(};
Nivelldioma nivelldiomaPortugues = new Nivelldioma();
n ivelld iom aCast ella no.setld iom a( ldiom as.CASTELLANO );
nivelldiomaCastellano.setNivei(S); 11 lengua materna
n ivelld iom aIngles .setld ioma ( ldiom as. l NGLES);
nivelldiomalngles.setNivel(3); 11 Intermed io
n ivelld iomaPortugues.setld ioma (Idiomas.PO RTUG UES);
nivelldiomaPortugues.setNivel(2); 11 Básico avanzado
ismael.m ejorarldioma (n ivelld iom a1ngles );
ismael.m ejorarldioma (n ivelld iom aCastellano );

136 © A lfaomega - Altaria


JEE. Manual práctic o

ism ael. m ejora rl d io m a( nivelldioma Port ugues);


is m ael.det ectar Alergia(A 1e rg ias. ACARO S);
is m ael.detectarAlergia(A 1e rgi as. ACA ROS);
ism ael.mostra rPersona ();
}
}
Ejecutando nuestro código, tenemos la s iguiente salida en consola:
Los datos de la persona son los siguientes ...
El color del pelo es: negro.
El color de sus gafas es: negro.
Los idiom as que habla son:
- Castellano - Nivel: 5.
- Inglés - Nivel: 3.
-Portugués- Nivel: 2.
La persona sufre las siguientes alergias:
-Alérgico a los ácaros del polvo.

En el ejemplo mostramos el uso de la variable this para hacer referencia a los atri-
butos y los métodos de la propia clase. Vemos que la sintaxis d el lenguaje Java es
similar a la sintaxis del lenguaje C++. Este capítulo pretende dar un somero repaso a
la programación orientada a objetos y la programación en Java. El manual no pretende
ser un manual de lenguaje Java en sí. Si se tiene dificultad con la comprensión de este
ejemplo, el lector debe leer y practicar previamente con la sintaxis del lenguaje Java.

3.3 Método main. Archivo Manifest


En el proyecto Java que estemos desarrollando debe haber un método main (excep-
tuando el caso de los applets, donde toma el control el programa "appletviewer.exe"). Si
quisiéramos distribuir el programa que hemos escrito en el apartado anterior, lo ideal
sería distribuir el programa compilado en bytecode. Pero para poder ejecutarlo debemos
indicarle a la máquina virtual de J ava (JRE) en qué clase se encuentra el método main
de nuestro proyecto. El método main se encuentra en la clase estática "Main", situada
en el paquete principal. Debemos indicar dicha información en el archivo Manifest.

El método main se encuentra s iempre en una clase estática. Una clase estática es
aquella que no necesita objeto para poder ejecutar sus métodos. En relación a lo que
hemos explicado d e clases y objetos, digamos que a las clases estáticas "les basta con
el molde".

Cualquier IDE de desarrollo en Java permite crear archivos ".jar" compilados con
archivos Manifest. Veamos cómo hacerlo en NetBeans. Con el botón derecho sobre el
proyecto pulsamos en "Clean and Build".

© Alfaomega - Altaria 371


Curso avanzado de Java

ArchLvo Editar Ver Naveg:a• F,uemte Reestructu•ar Rum1 Debug

l • f1J l 1<default config> vi


-
New

Bui ldl
Clean and Build
Clean
Generate Javadoc

En nuestro workspace se habrá creado una carpeta dist dentro del proyecto que
contendrá el archivo ".jar" compilado.

¡j d!ominio

li MEliA-INF
¡]j pr.iirncipal

Dentro de cada una de las carpetas tendremos los archivos ".class" compilados en
bytecode, mientras que la carpeta "META-INF" lo que contiene es el archivo "MANIFEST.
MF". Veamos el contenido del archivo Manifest:
Manifest-Version: 1.0
Ant-Version: Apache Ant 1.9.4
Created-By: 1.8.0_77-b03 (Oracle Corporation)
Class-Path:
X-COMMENT: Main-Ciass will be added automatically by build
Main-Ciass: principai.Main

El archivo Manifest sirve para indicarle a la JRE a qué clase debe ir para encontrar el
método "public static void main(StringiJ args)O". De hecho, con el archivo ".jar" generado,
podemos distribuir nuestra aplicación sin necesidad de que el usuario tenga instalada
en su máquina la JDK. Con la JRE será suficiente. Sí es cierto que en la ruta PATH
del sistema operativo debe estar la ruta hasta la entrada a la JRE (fichero "java.exe").

138 © Alfaomega - Altaria


JEE. Manual práctico

Nombre de la ·varíab'le: 1 JAVA_HOME

Valor de la varia't>le;: 1 C:lprogram Files\lavaüdk1.8.0_77

Aceptar 11 Car<~G91ar

Nombre della var.iable: ILP


_a_th_ _ _ _ _ _ _ _ _ _ _ ___¡

V'alor de la varia'ble;:

Ac:ep t:ar 11 Cancelar

La variable "Path" incluye la variable "JAVA_HOME", y la variable "JAVA_HOME"


incluye la ruta hacia "java.exe". Ahora en el navegador podemos escribir: "java -jar
001 EjemploPersona.jar".

3.4 Herencia
Si hay una característica que define la taxonomía de los objetos del mundo real,
esta es, sin lugar a dudas, la herencia. La taxonomía es la ciencia de la clasifica-
ción. Podemos realizar una clasificación del mundo de los vehículos. En la raíz de la
clasificación estaría la clase "vehículo". Tenemos varios tipos de vehículos: b icicleta,
ciclomotor, motocicleta, turismo, fu rgón, autocaravana o autobús (y muchos más) . Del
mismo modo, si definimos cada tipo del sub tipo "bicicleta", podemos ver que tenemos

© Alfaomega - Altaria 391


Curso avanzado de Java

de tipo infantil o adulto. Dentro de las bicicletas de adulto tenemos de tipo montaña,
de carretera, para practicar tria!... Veamos cómo quedaría definida esta clasificación
en el lenguaje de modelado unificado UML.

:::f VeriCIJO

T
1

DieK teta Cidomator Motocidetn Twismo F~gón AutocariWüna '-rtobús

~
l'
1

lnfl'lnlil
re Adulo ;¡-

Uontañ.. r--- C•reteu - Triol

Evidentemente, todos los vehículos comparten datos. E n primera instancia se nos


ocurren tres datos que comparten todos los tipos de vehículos:
• Número de ruedas.
• Máxima velocidad que puede alcanzar.

• Máxima masa que puede soportar.

Acudiendo a la "bicicleta", también se nos ocurren características que pueden com-


partir estos tipos de vehículos.
• Número de velocidades.

• ¿Sirve para competir? Es un dato de tipo Sí/No (booleano).


Acudiendo a l subtipo d e "adulto", podemos tener otro dato booleano:

• ¿Amateur o profesional? Es un dato de tipo Sí/No (booleano). Sí: profesional.


No: amateur.
La idea de la herencia es que cada subtipo de objeto tenga todas las características,
tan públicas como protegidas de sus ancestros. Por ejemplo, una bicicleta de adulto
de carretera tendrá todas las características siguientes:

• Número de ruedas.
• Máxima velocidad que puede alcanzar.

• Máxima masa que puede soportar.


• Número de velocid a des.

• ¿Sirve para competir?

• ¿Amateur o profesional?

1 40 © A lfaomega - Altaria
JEE. Manual práctic o

Como hemos indicado, Jos atributos o métodos protegidos son aquellos que son visi-
bles por las clases que heredan de otra, pero no por clases ajenas. La palabra reservada
con la que definimos estos atributos es protected.
La palabra reservada con la que definimos una clase que deriva o extiende de otra
es extends.
Como ejemplo, definamos las clases "Vehículo", "Bicicleta", "BicicletaAdulto", "Bici-
cletalnfantil" y "BicicletaCarretera", así como una clase principal que haga uso de ellas.

Veamos cómo estructuramos el código en paquetes dentro de nuestro nuevo pro-


yecto Java.

B-··b 002V'ehiwlo
J$·· ~ Paquetes de fuentes
1 $··· tEJ dominio
l !··· ~ Biddeta.java
: !···· ~ BiddetaAdulto. java
f···· ~ BiádetaCarretera.java
f ·· ~ Biádetalnfantil.java
L.. ~ vemwlo. java
EJ... §9 prindpal
L..~ Main.java
$. ~ Paquetes de prueba
$. {i Bibliotecas
@.. {i Bibliotecas de pruebas

• Clase "Vehiculo.java":
package dominio;
public class Vehículo {
protected int numeroRuedas;
protected int masaMaximaSoportable; 11 Kg
protected int velocidadMaximaAicanzable; 11 Km/hora
public int getNumeroRuedas() {
return numeroRuedas;
}
public void setNumeroRuedas(int numeroRuedas) {
this.numeroRuedas = numeroRuedas;
}
public int getMasaMaximaSoportable() {
return masaMaximaSoportable;
}
public void setMasaMaximaSoportable(int masaMaximaSoportable) {
this.masaMaximaSoportable = masaMaximaSoportable;
}

© Alfaomega - Altaria 411


C urso a vanza do de Java

public int getVelocidadMaximaAicanzable() {


return velocidadMaximaAicanzable;
}
public void setVeloci dadMaximaAicanzable(int velocidadMaximaAicanzable) {
this.velocidadMaximaAicanzable =velocidadMaximaAicanzable;
}
}

• Clas e "Bicicleta.java ":


package dominio;

public class Bicicleta extends Vehicu lo {


protected int numeroVelocidades;
protected boolean sirveParaCompetir;
public int getNumeroVelocidades() {
return numeroVelocidades;
}
public void setNumeroVelocidades(int numeroVelocidades) {
this.numeroVelocidades = numeroVelocidades;
}
public boolean isSirveParaCompetir() {
return sirveParaCompetir;
}
public void setSirveParaCompetir(boolean sirveParaCompetir) {
this.sirveParaCompetir =sirveParaCompetir;
}
}

• Clase "Bicicletal n fantil.java ":


package dominio;
public class Bicicletalnfantil extends Bicicleta {}

• Clase "BicicletaAdulto.java ":


package dominio;
public class BicicletaAdulto extends Bicicleta {
protected boolean profesionaiAmateur; 11 true =profesional
public boolean isProfesionaiAmateur() {
return profesionaiAmateur;
}
public void setProfesionaiAmateur(boolean profesiona iAmateur) {
this.profesionaiAmateur = profesionaiAmateur;
}
}

142 © A lfaomega - Altaria


JEE. Manual práctico

• Clase "BicicletaCarretera.java ":


package dominio;
public class BicicletaCarretera extends BicicletaAdulto {}

• Clase principal. Creamos una instan cia concreta d e bicicleta d e carretera per-
teneciente a Ismael:
package principal;
import dominio.BicicletaCarretera;
public class Main {
public static void main(String[] args) {
//Definiéndolo de tipo BicicletaCarretera podemos ver que
11 tiene todos los métodos públicos de sus ancestros.
BicicletaCarretera m iBicicletaCarretera =new BicicletaCarretera();
miBicicletaCarretera.setVelocidadMaximaAicanza ble(90); 11 km/hora.
miBicicletaCarretera.setMasaMaximaSoportable(160); 11 kg.
mi Bicicleta Carretera .set Nu m e ro Ruedas( 2);
miBicicletaCarretera.setNumeroVelocidades(12); // 2 platos y 6 piñones.
miBicicletaCarretera.setSirveParaCompetir(false); //No es de competir.
miBicicletaCarretera.setProfesiona iAmateur(false); 11 Es de aficionado.
11 Mostramos los datos por consola.
System.out.println("Datos de la bicicleta de Ismael".);
System .out.println(" La velocidad máxima alcanzable es " +
m iBicicletaCarretera .getVelocidad MaximaAicanzable() + " km/hora" .);
System.out.println(" La masa máxima soportable es"+
mi BicicletaCarretera .getMasaM aximaSoportable() + " kg" .);
System.out.println(" EI número de ruedas es"+
mi BicicletaCarretera.getN umeroRuedas()+ '"' .);
System.out.println("EI número de velocidades de la bicicleta es"+
m iBicicletaCa rretera .getNumeroVelocidades() + " " .);
if( miBicicletaCa rretera. isSi rve Pa raCom pe ti r()) {
System.out.println("Sirve para competir" .);
} else {
System.out.println("No sirve para competir".);
}
if( mi Bicicleta Carretera. isProfesiona !Amateur()) {
System.out.println( "Es bicicleta profesional".);
} else {
System.out.println("Es bicicleta de aficionado".);
}
}
}

© Alfaomega - Altaria 431


Curso avanzado de Java

La salida por consola de la aplicación es la siguiente:


Datos de la bicicleta de Ismael.
La velocidad máxima alcanzable es 90 km/hora.
La masa máxima soportable es 160 kg.
El número de ruedas es 2.
El número de velocidades de la bicicleta es 12.
No sirve para competir.
Es bicicleta de aficionado.

En el ejemplo, hemos podido comprobar como todos los métodos públicos de los an-
cestros de la clase "BicicletaCarretera" están disponibles. Los atributos también están
disponibles porque Jos hemos definido como protected o protegidos.
La variable "miBicicletaCarretera" se ha definido como de tipo "BicicletaCarretera" .
Si la hubiéramos definido de una superclase superior, sólo podríamos acceder a los
métodos definidos hasta esa superclase superior. Por ejemplo, redefinamos la clase
principal d efiniendo "miBicicletaCarretera" de tipo "Vehículo":

package principa l;
im port do m inio.BicicletaCarretera;
import dominio.Vehiculo;
public class Main {
public static void main(String[] args) {
//Definiéndolo de tipo Vehículo vemos que sólo tiene
//los métodos públicos de "Vehiculo".
Vehiculo miBicicletaCarretera =new BicicletaCarretera();
m iBicicletaCarretera.setVelocidad MaximaAicanzable(90); 11 Km/hora.
m iBicicletaCarretera.setMasaMaximaSoportable(160); 11 Kg.
mi BicicletaCa rretera .setNu meroRu edas(2);
//Mostramos los datos por consola.
System.out.println("Datos de la bicicleta de Ismael".);
System.out.println("La velocidad máxima alcanzable es" +
miBicicletaCarretera.getVelocidadMaximaAicanzable() + " km/hora" .);
System.out.println("La masa máxima soportable es"+
miBicicletaCarretera.getMasaMaximaSoportable() + " kg" .);
System.out.println("EI número de ruedas es"+
miBicicletaCarretera.getN umero Ruedas()+ "" .);
}
}

144 © Alfaomega - Altaria


JEE. Manual práctic o

No tenemos acceso a los métodos definidos ni en la clase "Bicicleta" ni en la clase


"BicicletaAdulto". La salida por consola es la siguiente:

Datos de la bicicleta de Ismael.


La velocidad máxima alcanzable es 90 km/hora.
La masa máxima soportable es 160 kg.
El número de ruedas es 2.

3.5 Polimorfismo
El polimorfismo se define como la "cualidad de polimorfo" (RAE). Buscando polimorfo
en el diccionario de la RAE nos encontramos que es un adjetivo que define aquello "que
tiene o puede tener distintas formas". En el contexto de la programación (programación
orientada a objetos), se define como aquel código que, aun escrito igual, puede lanzar
ejecuciones diferentes de código. Esto lo conseguimos gracias a la herencia que hemos
estudiado en el apartado previo. Veamos un ejemplo para entenderlo más fácilmente.
Tenemos la siguiente estructura de clases:

Per sona

...
• "
~

+ ducharse O: void;

PersonaMadrugadora PersonaPerez osa

+ ducharse O: void; + ducharseQ: void;

En el ejemplo podemos ver como una superclase define un método público denomi-
nado ducharse. Digamos que las personas normales crearían instancias (objetos) de
dicha clase. Dichas personas se duchan a las ocho de la mañana, antes de ir al trabajo.
Pero tenemos una clasificación. Tenemos dos tipos de personas: personas madruga-
doras y personas perezosas. Las personas madrugadoras se levantan muy temprano
y se duchan a las siete de la mañana. Y las personas perezosas se van a l trabajo sin
ducharse y se duchan por la tarde cuando les apetece.
En nuestro código Java, cuando creemos una persona, definiremos el tipo en la
creación. A partir de este punto, el código se ejecutará dependiendo de la instancia que

© Alfaomega - Altaria 451


Curso avanzado de Java

hayamos creado. Veamos este ejemplo en forma de código Java. Nuestra estructura de
ficheros fuente es la siguiente:

éJ. .6 004000ha
' -
~ .. ~ Paquetes. de fuentes
i $133 dominio
¡ i r... @ iPersona.java
¡ ' r... ~ PersonaMadrugadora.java
. . L... ~ IPersonaPere<osa.java
8 . . 139 prindpal
l... ~ Main.java
é·(j Paquetes. de prueba
®-· (j Bibliotecas
@.. [i Bibliotecas de pruebas

• Clase "Persona.java":

package dominio;
public class Persona {
public void ducharse() {
System.out.println("Soy una persona norma l y me ducho a las ocho"
+" antes de ir al trabajo".);
}
}

• Clase "PersonaMadrugadora .java":


package dominio;
public class Persona Madrugadora extends Persona {
@Override
public void ducharse() {
System.out.println("Soy una persona madrugadora y como me despierto "
+ " temprano me aburro y me ducho a las siete" .);
}
}

• Clase "PersonaPer ezosa .java" :

package dominio;
public class Persona Perezosa exten ds Persona {
@Override
public void ducharse() {
System.out.println("Soy una persona perezosa. M e levanto tarde "
+"y me ducho cuando puedo".);
}
}

146 © A lfaomega - Altaria


JEE. Manual práctico

Y ahora el programa principal. Vamos a crear una persona normal:


package principal;
import dominio.Persona;
public class M ain {
public static void main(String[) args) {
Persona p = new Persona();
p.ducharse();
}
}

La salida por consola de esta aplicación es la siguiente:


Soy una persona normal y me ducho a las ocho antes de ir al trabajo.

En el anterior código hay una instrucción, en concreto "p.ducharse();", que puede


tomar múltiples formas, dependiendo del objeto al que hayamos enlazado dinámicamente
la referencia de "p" de la clase "Persona". Veamos otra versión del programa principa l:

package principal;
import dominio.Persona;
import dominio.PersonaMadrugadora;
public class M ain {
public static void main(String[] args) {
Persona p = new PersonaMadrugadora();
p.ducharse();
}
}

El "enlace dinámico" se realiza ahora a otro código. Veamos la salida por consola:
Soy una persona madrugadora y como me despierto temprano me aburro y me ducho a las
siete.

Veamos, por último, el polimorfismo de la citada instrucción con el método de la


clase "PersonaPerezosa":
package principal;
import dominio. Persona;
import dominio.PersonaPerezosa;
public class M ain {
public static void main(String[] args) {
Persona p = new PersonaPerezosa();
p.ducharse();
}
}

© Alfaomega - Altaria 471


Curso avanzado de Java

La salida por consola es la siguiente:

Soy una p ersona perezosa. Me levanto tarde y me ducho cuando puedo.

3.6 Clases abstractas


Una clase abstracta es aquella de la que nunca se va a crear nmguna instancia.
Sin embargo, se crearán instancias de sus clases heredadas. Existen clases en las que
no tiene sentido crear una instancia concreta (por ser conceptos abstractos, por no
requerirse en la funcionalidad de la aplicación ... , por las razones que se nos ocurran).

Las clases abstractas también pueden contener métodos abstractos . Son aquellos
métodos que deben poseer e implementar las clases que hereden de la clase abstracta.
Pero en la clase abstracta no damos la definición del método. Únicamente ofrecemos
la cabecera del método que deberán implementar forzosamente las clases derivadas.

Aclaremos todos estos conceptos con un ejemplo. Pensemos en las figuras geomé-
tricas. Si c reamos la clase "FiguraGeometrica", podemos proteger datos (atributos)
comunes a varios tipos de figuras. Del mismo modo podemos definir la cabecera de
métodos implementables por varios tipos de figuras geométricas. Pero únicamente definir
la cabecera. Evidentemente la clase "FiguraGeometrica" será una clase abstracta. Los
métodos cuya cabecera se defina en dicha clase abstracta serán métodos abstractos.

En este ejemplo vamos a hacer uso d e dos patrones d e diseño orientados a objetos.
Uno va a ser el método fáb rica (un método cuya función es crear objetos por nosotros,
sin tener que usar el cliente la instrucción new). El otro patrón será el patrón "Estra-
tegia" o "Strategy". En la que un componedor hará uso del polimorfismo para crear
un enlace dinámico a un tipo de objeto. En este caso unificaremos los patrones en el
mismo componedor. Veamos el diagrama de clases.

fiQtnaGeometrica
(Ct&se ~tac:ta)

..... Coo-r
a ea 0 ;$0: d(Ml!Q,
• Cll.W'a:OOUD!e·
(AtiMbs pro!lil~idos)
• mam(StlmB¡¡rgs); YOiO, .. CleoHFJQUia(tipoFtQUii. TW tii\Ha.b:ose: double, albJ ri~ . dolttle): F• ~r;a aGeomelrka.

.. areaO: <loubte;/1 a0s1rildo


a. P9-'ltmJ1roO llOVb!Qlf ;bt)ll;eto

Tlpofigllt'a (Enum) ,.....-

CUADr<.i\DO
RE CTA~tGULO
Cuadrado R.ectilnguto lr~
TRI!v'iG\llO

• a r<.~<~O . doubh¡o; ·a11:ta0:<1oub!e, • ¡u¡¡aO. dou'tie .


• RelfmelroO double, .. pe·ime:troo dotJb!e; • PCiimOOoQ:d(l'I.IUie.

148 © A lfaomega - Altaria


JEE. Manual práctico

La est ruc t ura de ficheros queda como sigue:

~.~
GH:lD Pawetes de fuentes
1 9.. fE <l<:md>io
¡1 ¡¡ '
t"-6
el\
Componedcr.java
! i ~~ Cu.cdrado.java
l r-~ F¡gurdGc:om:hico.jcva
!-@ Rectangulo.java
~~ lipJFigura.java
L@ lflangulo.¡ava
᧠!>'ná><J
! L~ N.:~in.j\IV.:I
dJ.. {j Pacpetes de P'Ueba
&J.. (i F.i>lo...,.•
'
8·· W bttcas de l)f'uebas

Veamos uno por uno los artefactos software de este proy ecto:

• Enumerado "TipoFigura.java":

package dominio;
public enum TipoFigura {
CUADRADO,
RECTANGU LO,
TRIANGULO
}

• Clase "Com ponedor.java":

package dominio;
public class Componedor {
public FiguraGeometrica crearFigura(TipoFigura tipoFigura,
double base, double altura) {
FiguraGeometrica fg = null;
if (tipoFigura == TipoFigura.CUADRADO) {
fg = new Cuadrado(base,altura);
} else if (tipo Figura == TipoFigura.RECTANGULO) {
fg = new Rectangulo(base,altura);
} else if (tipo Figura== TipoFigura.TRIANGULO) {
fg = new Triangulo(base,altura);
}
return fg;
}
}

• Clase "FiguraGeometrica.java" (clase abstracta):

package dominio;
public abstract class FiguraGeometrica {
protected double base;
protected double altura;

© Alfaomega - Altaria 491


C urso a vanza do de Java

public abstract double area();


public a bstract double perimetro();
}

• Clase "Cuadrado.java":
package dominio
public class Cuadrado extends FiguraGeometrica {
public Cuadrado(double base, double altura) {
this.base = base;
this.altura =a ltura;
}
@Override
public double area() {
return (double) this.base * this.altura;
}
@Override
public double perimetro() {
return (double) (2 * this.base) + (2 * this.altura);
}
}

• Clase "Rectangulo.java":
package dominio;
public class Rectangulo extends FiguraGeometrica {
public Rectangulo(double base, double altura) {
this.base = base;
this.altura =a ltura;
}
@Override
public double area() {
return (double) this.base * this.altura;
}
@Override
public double perimetro() {
return (double) (2 * this.base) + (2 * this.altura);
}
}

• Clase "Triangulo.java":
package dominio;
public class Triangu lo extends FiguraGeometrica {
public Triangulo(double base, double altura) {

ISO © A lfaomega - Altaria


JEE. Manual práctico

this.base = base;
this.altura =altura;
}
@Override
public double area() {
return {double) (this.base * this.altura) 1 2;
}
@Override
public double perímetro() {
11 Suponemos triángulo equilátero.
return (double) (3 * this.base);
}
}

• Finalmente, vemos la clase principal "Main":


package principal;
import dominio.Componedor;
import dominio.FiguraGeometrica;
import dominio.TipoFigura;
public class Ma in {
public static void main(String[] args) {
Componedor e= new Componedor();
FiguraGeometrica fg = c.crearFigura{TipoFigura.CUADRADO, 3, 3);
double area = fg.area();
double perímetro= fg.perimetro();
System.out.println("Datos del cuadrado".);
System.out.println("EI área es: "+area+"".);
System.out.println("EI perímetro es: "+perímetro+"".);
fg = c.crearFigura(TipoFigura.RECTANGULO, S, 3);
area = fg.area();
perímetro = fg.perimetro();
System.out.println("Datos del rectángulo".);
System.out.println("EI área es: "+area+"".);
System.out.println("EI perímetro es: "+perímetro+"".);
fg = c. crea rFigu ra(TipoFigura.TRIANG ULO, 4, 3);
area = fg.area();
perímetro = fg.perimetro();
System.out.println("Datos del triángulo".);
System.out.println("EI área es: "+area+'"'.);
System.out.println{"EI perímetro es: "+perímetro+"".);
}
}

© Alfaomega - Altaria 511


C urso a vanzado de Java

La salida por consola del programa es la siguiente:


Datos del cuadrado.
El área es: 9,0.
El perímetro es: 12,0.
Datos del rectángulo.
El área es: 15,0.
El perímetro es: 16,0.
Datos del triángulo.
El área es: 6,0.
El perímetro es: 12,0.

3.6.1 Métodos implementados en clases abstractas


En cualquier clase abstracta podemos dar una implementación de cualquier método
que puede ser sobrescrito en una clase heredada. Retomemos el ejemplo de la figura
geométrica. Podemos añadir un método público llamado descripción que indique el tipo
de figura geométrica sobre el que estamos trabajando.

FiguraGeometrica
(Clase Abstracta)

• base: double;
• altura: double;
(Atributos protegidos)

+ áescripciono: void; 11 no abstracto


+ areaQ: double; 11 abstra~:to
• perimetroQ: double 11 abstracto

Cuadrado Rectángulo Triángulo

+ ceseripciono void; + descripciono: void; + areaQ: double;


... are ao: double; ... areao: double; + perimetroO: double;
+ perimelroO: double; + perimetroO: double;

152 © Alfaomega - Altaria


JEE. Manual práctico

Vemos que en la clase abstracta podemos implementar el método descripción, que


puede ser sobrescrito en las clases heredadas (tal cual hemos dicho). En este ejemplo,
la clase "Triangulo" no sobrescribe el m étodo d es crip ción.
Veamos la implem enta ción de la s clases:
• Clase "FiguraGeometrica.java":
package dominio;
public abstract class FiguraGeometrica {
protected double base;
protected double altura;
public void descripcion() {
System.o ut. println("Soy una figura geométrica cualquiera".);
}
public abstract do u ble area();
public abstract double perímetro();
}

• Clase "Cuadrado.java":
package dominio;
public class Cuadrado extends FiguraGeometrica {
public Cuadrado(double base, double altura) {
this.base = base;
this.altura = altura;
}
@Override
public void descripcion() {
System.out.println("Soy un cuadrado".);
}
@Override
public double area() {
ret urn (double) this.base * this.altura;
}
@Override
public double perímetro() {
return (double) (2 * this.base) + (2 * th is.altura);
}
}

• Clase "Rectangulo.java" :
package dominio;
public class Rectangulo extends FiguraGeometrica {
public Rectangulo(double base, double altura) {
this.base = base;
this.altura = altura;
}

© Alfaomega - Altaria 531


C urso a vanza do de Java

@Override
public void descripcion() {
System.out.println("Soy un rectángulo".);
}
@Override
public double area() {
return (double) this.base * this.altura;
}
@Override
public double perímetro() {
return (double) (2 * this.base) + (2 * this.altura);
}
}

• La clase "Triangulo" permanece igual que la mostrada en el apartado previo.


Como podemos ver, si ejecutamos el método descripción sobre un objeto de la clase
"Triangulo", la implementación que se ejecutará será aquella de la superclase "Figura-
Geometrica". Veamos la implementación de la clase principal y la salida por consola
de la a plicación:
package principa l;
import dom inio.Componedor;
import dominio.FiguraGeometrica;
import dominio.TipoFigura;
public class Main {
public static void main(String[) args) {
Componedor e = new Componedor();
FiguraGeometrica fg = c.crearFigura(TipoFigura.CUADRADO, 3, 3);
double area = fg.area();
double perímetro= fg.perimetro();
fg .descri pcion ();
System.out.println("EI área es: "+area+"".);
System .out.println(" E1perímetro es: "+perímetro+"".);
fg = c.crearFigura(TipoFigura.RECTANGULO, S, 3);
area = fg.area();
perímetro= fg.perimetro();
fg .descri pcion ();
System .out.println("EI área es: "+area+'"'.);
System .out.printl n(" E1perímetro es: "+perímetro+"".);
fg = c.crearFigura(TipoFigura.TRIANGU LO, 4, 3);
area = fg.area();
perímetro = fg.perimetro();
fg.descri pcion ();
System.out.println("EI área es: "+area+"".);
System .out.printl n(" E1perímetro es: "+perímetro+"".);
}
}

154 © Alfaomega - Altaria


JEE. Manual práctic o

Ahora, cada tipo de objeto indica de qué clase es . El triángulo debe decir que es una
figura geométrica cualquiera. Veamos la salida por consola:
Soy un cuadrado.
El área es: 9,0.
El perímetro es: 12,0.
Soy un r ectángulo.
El área es: 15,0.
El perímetro es: 16,0.
Soy una figura geométrica cualquiera.
El área es: 6,0.
El perímetro es: 12,0.

3.7 Clases estáticas


Las clases estáticas son aquellas que no necesitan tener una instancia en memoria
(un objeto) para poder hacer uso de sus métodos o atributos. Suelen ser clases sin
estado en las que tendremos "métodos funcionalidad" (similares a los de la programa-
ción estructurada), constantes, enumerados ... Siempre sin estado de los objetos o con
estado compartido para toda la aplicación. El ejemplo más conocido es el de la clase
"Main" en la que se ubica el método "public static void main(String[] args)".

Es bueno señalar que el modificador static no se indica a nivel de clase, sino a nivel
de los métodos o atributos n ecesarios. Veamos un ejemplo. ¿Conocemos la función
factorial de un número? La función factorial de un número se define como una función
recursiva para los enteros positivos (números naturales) . Lo calculamos como el propio
número multiplicado por el factorial del entero inmediatamente menor. La recursión
acaba en el cero. El factorial de cero es uno. Veamos esta explicación matemáticamente:
n!=n*(n-1)!
O! = 1
Siguiendo la anterior definición, podemos calcular el factorial de cualquier número
entero positivo. Estudiemos el factorial del número 4.
4!=4 * 3!
3!=3*2!
2!=2*1!
1!=1*0!
O! = 1

Por lo tanto, el factorial de 4 es:


4! = 4 * 3 * 2 * 1 * 1 = 24
Estudiemos una clase estática que implementa la función factorial. Como hemos
dicho, será una clase sin estado.

© Alfaomega - Altaria 551


Curso avanzado de Java

·· 6 007Esl:aticaFactorial
~. .. [i
.
!Paquetes de fuentes.
l EJ. . tfl ¡prindpal
l ¡. ~ Main.java
L.. ~ Matematica.java
$. Qil¡ Paquetes de prueba
$· (i !Bibliotecas
@.. [j Bibliote~As de pruebas.

• Clase estática "Matematica.java":


package principa l;
public class M atematica {
public static long factorial(long n) {
if (n>O) {
return n * (M atematica.factorial(n-1));
} else if (n ==O) {
return 1;
} else {
return 1;
}
}
}

• Clase principal "Main.java" (que es otra clase estática):


package principa l;
public class Main {
public static void main(String[) args) {
System.out.println("Vamos a estudiar el factorial de 4" .);
long resultado= Matematica.factoria l(4);
System.out.println("EI factorial es:"+ resultado+'"'.);
}
}

Observamos que no es necesario realizar new de ningún obj eto. El método factorial
es un método de clase y podemos llamarlo invocando únicamente a la clase (clase es-
tática) . El resultado por consola es el siguiente:
Vamos a estudiar el factorial de 4.
El factorial es: 24.

156 © Alfaomega - Altaria


JEE. Manual práctico

3.8 Implementación de interfaces


Cuando en POO nos encontramos con un caso de herencia, dándose la situación de
que sólo necesitamos heredar métodos (no atributos) y no necesitamos implementa-
ciones que puedan reutilizarse en las clases heredadas, lo que precisamos, en vez de
heredar clases, es implementar interfaces.

Las interfaces únicamente exponen al público las cabeceras de los métodos que
deben ser implementados por las clases que "implementen las interfaces". Veamos un
ejemplo de un servicio de acceso a datos de usuarios. La interfaz definirá únicamente
los métodos que deben implementar aquellas clases que quieran implementar la interfaz.
En este caso definiremos la interfaz "DatosUsuario" .

...oatusUsuariu..

+ ge1AIIUsuariosQ: Ust<Usuario>;
+ findUsua riold(id: int): Usuario;
+ editUsuariold(id: int): boolean;
+ countusuariosQ: int;

Se define una interfaz con cuatro métodos de acceso a datos. Cualquier clase que
quiera implementar dicha interfaz debe implementar dichos métodos.
Para mostrar un ejemplo, imaginemos un acceso a datos hard coded, es decir, los
datos estarán en un fichero de código fuente.
Crearemos la siguiente estructura de clases:

ocOatosU~uario..

• aeWIUsuariosO: list<Usuario>;
... findUsuariold(id: inO: Usuario;
... editUsuariold(id: inO: boolean;
• countUsuariosO: in!;

-';'
••
Main •
••

•••
••
••
Usuario
HardCodedDatosUsuario
• id: int
. usuarios: üst<Usuario > · nombre: String
1 • • ema11: svrng
• métodos de la interfaz ·edad: int

• aetters & setters


+ muestraUsuar¡oO: void;

© Alfaomega - Altaria 571


Curso avanzado de Java

Para implementar una interfaz debemos usar la palabra reservada implements.

á ·b oosrmplementadonrnterfa<
$. Qi'¡ Paquetes de fuentes
1 ~J I!J dominio
~ ~ ~- PJ DatosUsuario.java
l l L. ~ HardCodedDatosusuario.java
l l L.. ~ rusuario.java
éJ. . EÉ) prindpal
L. .. [¡, .Main.java
$·- ('j Paquetes de prueba
$. (i Bibliotecas
9.. Bibliotecas de pruebas

• Clase "Usuario.java":

package dominio;
public class Usuario {
prívate int id;
prívate String nombre;
prívate String email;
prívate int edad;
public int getld() {
return id;
}
public void setld{int id) {
this.id = id;
}
public String getNombre() {
return nombre;
}
public void setNombre(String nom bre) {
this.nombre = nombre;
}
public String getEmail() {
return email;
}
public void setEmaii(String email) {
this.email = email;
}
public int getEdad() {
return edad;
}
public void setEdad(int edad) {
this.edad =edad;
}

ISB © A lfaomega - Altaria


JEE. Manual práctic o

public void muestraUsuario() {


System .out. p rintln ("Id: "+th is. id+"".);
System .out.println(" Nombre: "+this.nom bre+"" .);
System .out. println ("E m ai 1: "+th is.ema il+"". );
System .out. p rintln ("Edad: "+th is.edad+ '"'. );
}
}

• Interfaz "DatosUsuario.java":
package dominio;
import java.utii.List;
public interface DatosUsuario {
public List<Usuario> getAIIUsuarios();
public Usuario findUsuario(int id);
public boolean editUsuario(Usuario u);
public int countUsuarios();
}

• Clase "HardCodedDatosUsuario.java":
package dominio;
import java.utii.ArrayList;
import java.utii.List;
public class HardCodedDatosUsuario implements DatosUsuario {
private List<Usuario> usuarios;
public HardCodedDatosUsuario() {
this.usuarios = new ArrayList<Usuario>();
Usuario u= new Usuario();
u.setld(l);
u.setNom bre(" lsmael" );
u .setEma il(" is mae l@ ism ael.com" );
u.setEdad(33);
th is. usua rios.add (u);
u = new Usuario();
u.setld(2);
u.setNom bre(" Antonio");
u .setEma il( "a nton io@ anton io .com ");
u.setEdad(44);
th is. usua rios.add (u);
u = new Usuario();
u.setld(3);
u .setNom b re(" Luis");
u .setEma il( "1u is@ luis.com" );
u.setEdad(22);
th is. usua rios.add (u);
}

© Alfaomega - Altaria 591


Curso avanzado de Java

@Override
public List<Usuario> getAIIUsuarios() {
return this.usuarios;
}
@Override
public Usuario fi ndUsuario(int id) {
f or (Usuario u: th is.usuarios) {
if (id== u.get ld()) {
return u;
}
}
re t urn null;
}
@Override
public boolean editUsuario(Usuario u) {
for (Usuario us: t his.usuarios) {
if (u .getld() == us.getld()) {
us.setN o m bre( u .getNom bre() );
us.setEmail( u.get Ema il() );
us.setEdad (u .get Edad() );
return t rue;
}
}
return fa lse;
}
@Override
public int countUsuarios() {
re t urn t his.usuarios.size();
}
}

• C lase "Main.j ava":

package principa l;
impo rt dom inio.DatosUsuario;
im port do m inio.HardCodedDatosUsuario;
import dominio.Usuario;
impo rt java.utii.List;
public class Main {
public st atic void main(String[) args) {
DatosUsuario du = new HardCodedDatosUsuario();
int numeroUsuarios = du.countUsuarios();
System.out.println("EI número de usuarios es : "+numeroUsuarios+"" .);
System.out.println("*****Mostramos todos los usuarios*****");
List <Usuario> usuarios= du.getAIIUsuarios();
f or (Usuario u: usuarios) {

160 © A lfaomega - Altaria


JEE. Manual práctic o

System .out.println("** * * * * * * * * * * ** * * ** * ** * * * * * * * * ** ** * * *");


u.muestra Usuario();
}
System.out.println("*****EI usuario con id 2 es*****");
Usuario u= du.findUsuario(2);
u. muestra Usuario();
System.out.println(" *****Modificamos el usuario con id 2*****");
Usuario modificado= new Usuario();
mod ificado.setld (2};
mod ificado.setN o m bre(" Raúl");
m od ificado.setE m ai 1("rau 1@ra u l.com");
modificado. setEdad (20};
if (du.editUsuario(modificado)) {
System.out.println(" *****Mostramos todos los usuarios*****");
usuarios= du.getAIIUsuarios();
for (Usuario us: usuarios) {
System.out.println(" * ** * * * * * * * * * * * * * * ** * * * * * * * * * * ** * * * * * ");
us.m uestra Usua río();
}
}
}
}
La salida por consola del programa es la siguiente:
El número de usuarios es: 3.
*****Mostramos todos Jos usuarios*****
***** ************* ******************
Id: 1.
Nombre: Ismael.
Email: ismael@ismael.com.
Edad: 33.
************************************
Id: 2.
Nombre: Antonio.
Email: antonio@antonio.com.
Edad: 44.
************************************
Id: 3.
Nombre: Luis.
Email: luis@luis.com.
Edad: 22.
*****El usuario con id 2 es*****
Id: 2.
Nombre: Antonio.
Email: antonio@antonio.com.
Edad: 44.

© Alfaomega - Altaria 611


Curso avanzado de Java

*****Modificamos el usuario con id 2*****


*****Mostramos todos los usuarios*****
************************************
Id: l.
Nombre: Ismael.
Email: ismael@ismael.com.
Edad: 33.
************************************
Id: 2.
Nombre: Raúl.
Email: raul@raul.com.
Edad: 20.
********** **************** **********
Id: 3.
Nombre: Luis.
Email: luis@luis.com.
Edad: 22.

3.9 Excepciones
El control de los errores se lleva a cabo en el lenguaje Java a través de las excepcio-
nes. Las excepciones son un tipo especial de objeto. Al ser un tipo especial de objeto,
hereda de la clase "Object", que se encuentra en el paquete "java.lang". La clase "Object"
es aquella de la cual heredan todas las clases que existen en la plataforma Java, así
como todas aquellas clases que creemos en el desarrollo de nuestras aplicaciones . La
jerarquía de clases de la clase "Exception" es la siguiente:

j!MI.Iilng

Object

Throwable

Exception

162 © Alfaomega - Altaria


JEE. Manual práctic o

En nuestras aplicaciones, aparte de las excepciones que proporcione la plataforma


Java, crearemos nuestras propias excepciones. Una excepción típica es aquella de acceso
a datos en nuestra base de datos. Otro tipo de excepciones son las excepciones lógicas.
Empecemos por lo s imple y veamos cómo tratamos las excepciones de la propia
plataforma Java JSE. Veamos un pequeño programa que realiza una división entre
cero:
package principal;
public class M ain {
public static void main(String[J args) {
intd=9·
'
int result = d 1 O;
System.out.println("EI resultado es "+resu lt+"" .);
}
}
Al ejecutar el anterior código, la salida que tenemos por consola es la siguiente:
Exception in thread "main "java.lang.ArithmeticException: 1 by zero
at principai.Main.main(Main.java:7)

Vemos que la excepción que se ha lanzado es "ArithmeticException", que hereda de


la clase "Exception" vista anteriormente. La forma de evitar este tipo de errores que
se producen en tiempo real (normalmente usaremos para dividir variables en vez de
valores escritos en el código) es mediante el uso de las instrucciones "try ... catch".
Veamos cómo se hace:
package principal;
public class Ma in {
public static void main(String[] args) {
int d = 9;
try {
int result = d /O;
System.out.println("EI resultado es "+result+"".);
} catch(ArithmeticException e) {
System .out. printl n ("Error: "+e .getM essage()+" ". );
}
}
}

Ahora la salida por consola será la siguiente:

Error: 1 by zero.

La lógica es: situamos en un bloque "try" aquellas instrucciones susceptibles de


producir error o que deben ejecutarse secuencialmente si no se produce el error. En
el bloque "catch" hacemos indicación de la excepción que vamos a capturar, de cuyos

© Alfaomega - Altaria 631


C urso a vanzado de Java

métodos podremos hacer uso. Uno de dichos métodos es "getMessage()", que muestra
la causa del error. Si en vez de entre cero dividimos entre tres:
package principa l;
public class Main {
public static void main(String[) args) {
int d =9·1
try {
int result = d /3;
System.out .println("EI resultado es "+result+"".);
} catch(ArithmeticException e) {
System .out. p rintln (" Error: "+e .get M essage()+ "".);
}
}
}

La salida por consola es:

El resultado es 3.

Vemos que la aplicación no ha necesitado entrar en el bloque "catch" porque no se


ha producido la posible excepción.

Para ver un ejemplo más práctico, veamos cómo podemos lanzar y capturar una
excepción d e acceso a datos . Recordemos el ejemplo en el que implementa mos la in-
terfaz "Datos Usuario".

..oatosUsuario..

• geiAIIUsuarioso: List<Usuario>:
• findUsuarloid(ld: int): Usuario:
• editUsuariold(id: int): boolean;
• countUsuari osQ: int;

"T

Main

Usuario
HardCodedOatosUsuario

· usuarios: List<Usuari o> r · id: int


· nombre: String
' 1

' . email: String
• métodos de la inteñaz ·edad: int

• getters & setters


• mue straUsuarioO: void;

164 © A lfaomega - Altaria


JEE. Manual práctico

En dicho ejemplo en el que nuestro acceso a datos era hard coded, imaginemos que
la clave primaria de la "supuesta tabla" es el campo "id". Insertemos en la interfaz un
nuevo método "insertarUsuario(usuario: Usuario): boolean". Si la clave primaria ya existe
en la capa de datos, lanzaremos una nueva excepción "DatosUsuarioAccessException".
Veamos cómo queda el nuevo diseño.

OatoSUsuarioAccessException
itDatosllsuario-.

• insertarUsuario(usuano: Usuario): boolean throws OUAE


• getAIIUsuariosO: Lrst<Usu ano>; " OatosUsuarioAccessException(message: String); JI constructor
• findUsuariold(ld: rnl) Usuario;
• editusuariold(id: int) boolean;
• couniUsuariosO: mt;

"r
' DUAE: DatosUsuarioAccessException "
'
''
,_-- ----------.
''
''
'' Usuario
Marn '
''
'
HardCodedDatosUsuario • id: int
• • nombre: String
~ • email: String
- usuarios. Ust<Usuario> ·edad: int

• métodos de la interfaz
+ getters 8. setters
+ muestraUsuarioD: void;

La clase del modelo del dominio "Usuario" queda inalterada (ver apartado 3.8) . El
resto queda de la siguiente forma. Estructura de ficheros.
Podemos ver que hemos creado un paquete "errores", en el que hemos insertado la
clase de la excepción. Aparte, hemos modificado la interfaz, su implementación hard
coded, así como el método main. Veamos los cambios.

• Excepción "Datos UsuarioAccessException.java":


package errores;
public class DatosUsuarioAccessException extends Exception {
public DatosUsuarioAccessException() {}
pu blic DatosUsua rioAccessException (String msg) {
super(msg);
}
}

• Interfaz "DatosUsuario.java":
package dominio;
im port errores. DatosUsua rio Access Exception;
import java.utii.List;

public interface DatosUsuario {

© Alfaomega - Altaria 651


Curso avanzado de Java

public boolean insertarUsuario(Usuario usuario) throws DatosUsuarioAccessException;


public List<Usuario> getAIIUsuarios();
public Usuario findUsuario(int id);
public boolean editUsuario(Usuario u);
public int countUsuarios();
}

• "HardCodedDatosUsuario.java ":

package dominio;
import errores.DatosUsuarioAccessException;
import java.utii.Arraylist;
i m port java.util. List;
public class HardCodedDatosUsuario implements DatosUsuario {
private List<Usuario> usuarios;
public HardCodedDatosUsuario() {
this.usuarios = new Arraylist<Usuario>();
Usuario u = new Usuario();
u.setld(l);
u .setN ombre("lsmael");
u .setE m ai 1(" ism ael@ ismael.com" );
u.setEdad(33);
th is. usua rios.add (u);
u = new Usuario();
u.setld(2);
u. setN ombre ("Antonio");
u .setE m ai 1( "a ntonio@ anton io .com" );
u.setEdad(44);
th is. usua rios.add (u);
u = new Usuario();
u.setld(3);
u. setNombre ("Luis");
u .setE m ai 1( "1 u is@ 1u is.com " );
u.setEdad(22);
th is. usua rios.add (u);
}
@Override
public boolean insertarUsuario(Usuario usuario) throws
DatosUsuarioAccessException {
int id = usuario.getld();
boolean encontrado= fa lse;
for(Usuario u: this.usuarios) {
if (u .getld() == id) {
encontrado= true;
break;
}

166 © A lfaomega - Altaria


JEE. Manual práctico

}
if(encontrado) {
throw new DatosUsuarioAccessException("EI id"
+ "ya se encuentra en la base de datos");
} else {
Usuario nuevoUsuario = new Usua rio();
nuevo Usua río .setl d (u su arío.get 1d ());
nuevoUsua rio.setN o m b re( usua rio.getNom bre() );
nuevoUsua rio.setEma il( usu ario.getE m a il() );
nuevo Usu a río .setEd ad (u su ario. get Edad ());
th is.usua rios.add (nuevo Usuario);
}
return t rue;
}
@Override
public List<Usuario> getAIIUsuarios() {
return this.usuarios;
}
@Override
public Usuario findUsuario(int id) {
for (Usua rio u: this.usuarios) {
if (id == u.getld()) {
return u;
}
}
return null;
}
@Override
public boolean editUsuario(Usuario u) {
for (Usuario us: this.usuarios) {
if (u.getld() == us.getld()) {
us.setNombre(u.getNom bre());
us.setE m ai 1(u .getEmail() );
us.setEdad (u .getEdad() );
return true;
)
}
return false;
}
@Override
public int countUsuarios() {
return this.usuarios.size();
}
}

© Alfaomega - Altaria 671


Curso avanzado de Java

• Clase: "Main.java":
package principa l;
import dominio.DatosUsuario;
im port do m inio.HardCodedDatosUsuario;
import dominio.Usuario;
import errores.DatosUsuarioAccessE xception;
public class Main {
public static void main(String[] args) {
DatosUsuario du = new Ha rd CodedDatosUsuario();
11 El id 2 ya existe.
Usuario u = new Usuario();
u.setld(2);
u .setNom bre(" Nicolás");
u .setEm ai 1(" n icolas@ nicolas.com" );
u.setEdad(20);
try {
boolean correcto = du.insertarUsuario(u);
if (correcto) System.out.println("EI usuario se ha insertado bien ".);
} catch (DatosUsuarioAccessException ex) {
System .out. println(" Error: "+ex.getMessage()+"" .);
}
}
}
Al existir un usuario con id = 2 en la base de datos, se lanza la excepción y se cap-
tura en el método main.
Veamos la salida por consola:

Error: El id ya se encuentra en la base de datos.

Sin embargo, para el id = 4 (que no se h a usado), la salida del programa será la


siguiente:

El usuario se ha insertado bien.

Para finalizar con las excepciones, veamos cómo podemos tener métodos que captu-
ren excepciones y no tengan una cláusula "try ... catch", sino que directamente lancen
las excepciones al método que las ha llamado. Esta técnica es común para la capa de
servicio. Esto lo conseguimos con la cláusula "throws", sin necesidad de que la excep-
ción haya sido creada en el método. Para ver un caso práctico, imaginemos una capa
de servicio tonta, que lo único que haga sea llamar a la capa de datos hard coded que
hemos desarrollado.

168 © A lfaomega - Altaria


JEE. Manual práctico

Oatos.UsuarioAccassExctption
-.OatosUsuario"

+ inn r1arUs:u;rro(usu;rio: Usu;.rio): booltan itvows OUAE


+ gotAIIUsualiosO: List..:Usuario:.; • Dato :s U:svariaAcce:ssE~c eption(mes:saue; Sfrinol: 11 con$tructor
+ tindUsuaricld(ld: int]: Usu::Jrio;
+ editUsuaricld(ld: lnt]: bootean;
.. countLJsuariosO: in~
~

:' OUAE: O;tosUsu;;nnA.ccessException "

'
·--------------------.
''
'
'' lhouario
''
''
llardCodedOatosUsuario • id' inr
servlcet.llYI!r • · nom!HP.: Srnng
1¡ . ema11: String
l&lin ~ • usuarios: Lislo(Usuario' • eCI;;d: int

+ métodos de l a interfaz
.. métodos de la inlerfar
+ getters 8.
setters
+ muestraUsuarioO:wid;

En la clase "ServiceLayer" el métod o "insertarUsuario" la nza la excepción sin crearla .


Veamos el código reestructura d o.

éJ. .b 011Exception3
$. (j Paquetes de fuentes
l S Etl doiTÚnio
i ¡ ¡. .
[:01 DatosUsuario .java
¡. . I§J HardCodedDatosUsuario .java
L.~ Usuario.java
8 EJ3 errores
~ L.. ~ DatosUsuarioAccessException.java
$ tfl prindpal
L. ~ Main.java
a' . . Etl service
!' L .. ~ ServiceLayer.java

$·ri Paquetes d e prueba


$· lj Bibliotecas
á· lj Bibliotecas de pruebas

Las d os clases que n os interesan son las siguientes:

• Clase "ServiceLayer.java " (es una capa d e servicio tonta, n o h ace nada):
package service;
import dominio.DatosUsuario;
import dominio.HardCodedDatosUsuario;
import dominio.Usuario;
im port errores. DatosUsua rioAccessException;
import java.utii.List;
public class Servicel ayer {
private DatosUsua rio datosUsua rio;

© Alfaomega - Altaria 691


Curso avanzado de Java

public Servicelayer() {
this.datosUsuario = new HardCodedDatosUsuario();
}
public boolean insertarUsuario(Usuario usuario)
throws DatosUsuarioAccessException {
return th is.datosUsuario. in se rtarUsu ario( usuario);
}
public List<Usuario> getAIIUsuarios() {
return th is.datosUsuario.getAIIU su arios();
}
public Usuario findUsuario(int id) {
return th is.datosUsuario.find Usuario(id );
}
public boolean editUsuario(Usuario u) {
return th is.datosUsuario.editUsuario(u);
}
public int countUsuarios() {
return th is.datosUsuario.cou ntUsuarios();
}
}

• Clase "Main.java":
package principal;
import dominio.Usuario;
import errores.DatosUsuarioAccessException;
im port java.util. List;
im port service .Servicelayer;
public class Main {
public static void main(String[) args) {
Servicelayer si = new Servicelayer();
Usuario u = new Usuario();
u.setld(4);
u .setNom bre(" Nicolás");
u .setEm ai 1(" n icolas@ n icolas.com" );
u.setEdad(20);
try {
boolean correcto= sl.insertarUsuario(u);
if (correcto) System.out.println("EI usuario se ha "
+ " insertado bien".);
} catch (DatosUsuarioAccessException ex) {
System .out. println ("Error: "+ex.getM essage()+ '"'.);
}
List<Usuario> usuarios= sl.getAIIUsuarios();
for (Usuario us: usuarios) {
System .out. println(" * * ** ** * * * * * ");

170 © A lfaomega - Altaria


JEE. Manual práctico

us.muestraUsuario();
}
}
}

La salida por consola de la aplicación es la siguiente:


El usuario se ha insertado bien.
********** *
Id: l.
Nombre: Ismael.
Email: ismael@ismael.com.
Edad: 33.
********** *
Id: 2.
Nombre: Antonio.
Email: antonio@antonio.com.
Edad: 44.
******** ***
Id: 3.
Nombre: Luis.
Email: luis@luis.com .
Edad: 22.
***********
Id: 4.
Nombre: Nicolás.
Email: nicolas@nicolas.com.
Edad: 20.

3.10 Ejercicio 1
Se propone al lector implementar una aplicación de consola a modo de bloc de notas.
La aplicación constará de un menú en el que el usuario podrá elegir si introducir una
nota nueva o ver las notas existentes. Cuando introducimos una nota nueva, primero
introducimos la fecha (ya que el bloc funcionará a modo de calendario, con notas or-
denadas por fecha) y luego introducimos el texto. Si elegimos ver las notas, veremos
el listado de notas por orden cronológico. El esquema de clases y de paquetes que se
propone es el siguiente:

© Alfaomega - Altaria 711


Curso avanzado de Java

senñcio

dorrinio

tltlldaá

(cl::asP. P.SI~Uca)

libroNotm~:
.. mostrarUeru.u) ; Nota
~ tomarNolal): NoJ;¡:
notas: Ust<Nota,.:
+ mostra rN otas(no~s: Ltst<rJota>); - terlo strmg;
... mostrarTG).1C(t004:o: Silln;-); lf llama a Syslcm.out.¡:nrc;n_ 1 LIS! •
. recha. Calt r:.tJa:•
.. recogc rTe):to(\c:<toVost'~r: Stnng): Stnng; -+ UIJroNolaso:
.. recopeiV&Iof(m~n· m:. mil)( int tcidoi\tos1Tor:Suinp): mt .. goiWrs & sonors:
" recogerf'ech&(lel.1oVostrar Strino)· Calendllr, • a n t~cirNota{n: NoW): boclean. " NotaO:
.. ordenarNota.s0urt".d3fecl'la(, ctas· List.. l\l o~· ): U st<.i'\oía~o: .. get1ers & setr.e1s•
J1 Cuanl os mélodos ~ri-rado s se consideren n erc esari e>s .

tlfocnocas

BlocNol m;

- lii:HOI\1'012$ LitlrONO l~ S.
- Uiitla;a tt,

~; lnQ;

*Nota: En la clase estática usamos el método de ordenación burbuja.

172 © Alfaomega - Altaria


,
Instalación del CAPITULO

entorno servidor de
aplicaciones Oracle
WebLogic Server

Una vez estudiadas las nociones básicas de Java (plataforma JSE), vamos a comenzar
a instalar nuestro entorno de trabajo para la JEE. No nos confundamos. La versión
de NetBeans que hemos instalado en nuestra máquina nos da acceso al conjunto de
librerías y clases que componen la JEE. Pero nuestras aplicaciones empresariales (cuya
principal característica es la conectividad) deben correr (ejecutarse) en un servidor de
aplicaciones empresariales. Existen varios servidores de aplicaciones para la JEE. Entre
los de libre distribución nos encontramos: JOnAS, JBoss (últimas versiones conocidas
como WildFly), Geronimo, TornEE o GlassFish (de Oracle, instalada con nuestro Net-
Beans), entre otras. Entre los servidores de aplicaciones propietarios están WebSphere
(de IBM) y WebLogic (de Oracle) . El servidor de aplicaciones WebLogic, aun siendo
propietario, puede ser descargado bajo la licencia OTN (Oracle Technology Network
License Agreement). Es una versión completa pero en modo prueba, sólo disponible
para versiones de desarrollo. En el s itio web de Oracle, bajo las restricciones de Oracle
WebLogic, podemos leer lo siguiente (versión original en inglés):

"Oracle grants You a nonexclusive, nontransferable, /imited license to internally use the Pro-
grams, subject to the restrictions stated in this Agreement, only for the purpose of developing,
testing, prototyping, and demonstrating Your application and only as long as Your application
has not been used for any data processing, business, commercial, or production purposes, and
not for any other purpose ".

La traducción al castellano del anterior texto sería la siguiente:


"Oracle le concede una licencia ni exclusiva ni intransferible y limitada para utilizar interna-
mente los programas, con sujeción a las restricciones establecidas en este acuerdo, sólo con el
propósito de desarrollo, prueba, prototipo y demostración de su aplicación, y sólo durante el
tiempo en que su aplicación no sea utilizada para ningún propósito de procesamiento de datos,
negocios, comercial o de producción; y para ningún otro propósito" .

© Alfaomega - Altaria 731


Curso avanzado de Java

Digamos que cumplimos la restricción del acuerdo porque nuestro uso va a ser sólo
para desarrollo, aprendizaje y enseñanza de nuestro propio código. No vamos a usar
Oracle WebLogic Server para fines de producción de aplicaciones reales ni comerciales
(no vamos a vender software que se ejecute sobre Oracle WebLogic).

¿Por qué usamos WebLogic? La respuesta es simple: por su robustez. Es un servidor


de aplicaciones desarrollado desde el año 1997 por la compañía BEA Systems, adqui-
rida posteriormente por Oracle (es el servidor de aplicaciones propietario de Oracle). Al
ser un servidor propietario y empaquetado, va a ser muy sencillo tanto su uso como
el despliegue de nuestras aplicaciones empresariales . El lector, una vez conozca las
características de este servidor de aplicaciones, y sobre todo de la JEE, podrá migrar
sus aplicaciones o desarrollarlas para que se ejecuten en el servidor de aplicaciones
que estime conveniente.

Pero ¿qué es realmente un servidor de aplicaciones?

La tecnología de servidores de aplicaciones, sobre la que se basa el desarrollo de


aplicaciones empresariales de la JEE, nos proporciona un conjunto de funcionalidades
básicas:

• Centralización de la lógica de negocio, usando la API de Enterprise JavaBeans


(EJB). Los EJB harán uso de la Java Persistence API (JPA) o API de Persistencia
de Java para persistir los objetos. Gracias a la tecnología de EJB, tendremos
acceso a otro subconjunto de tecnologías que nos permitirán:
Tener facilidad para la conectividad basándonos en el desarrollo de compo-
nentes software que se intercomunican.
Acceso universal a l árbol de objetos, conexiones y componentes desplegado a
modo de árbol de directorios sobre el servidor. A dicho árbol lo denominamos
árbol JNDI (Java Naming and Directory Interface).
Olvidarnos en gran medida de los problemas arquitectónicos de la aplicación.

• Contenedor de servlets.
• Un largo etcétera, del cual no pretendemos ser exhaustivos.

Denominamos lógica de negocio al conjunto de datos y operaciones sobre esos datos


aplicables al dominio de un cliente concreto. Por ejemplo, para un cliente que necesite
una aplicación de facturación, tendríamos un conjunto de datos: productos, servicios,
totales ... , y un conjunto de operaciones sobre estos datos: calcular precio, calcular
regla de impuestos, calcular total, crear factura .. .

La JPA es una especificación JSR que permite crear una base de datos relacional (y
gestionarla) a partir de un diagrama de clases diseñado siguiendo los principios de la
programación orientada a objetos. Esto se consigue gracias a l patrón de mapeo objeto-
relacional (también denominado ORM: Object-Relational mapping).

174 © Alfaomega - Altaria


JEE. Manual práctico

Facilidad para la conectividad: el servidor de aplicaciones nos permite la conectividad


universal mediante un conjunto de tecnologías: servicios web (SOAP o RESTful), RMI,
CORSA, JMS ...
Acceso a los objetos, conexiones y artefactos desplegados sobre el servidor de aplica-
ciones mediante una búsqueda sobre el contexto de la aplicación y sobre el árbol JNDI,
estando en la misma máquina virtual o en una máquina virtual diferente .
El servidor de aplicaciones debe contener un dominio para el espacio de nombres.
En nuestro caso tendremos la configuración más simple:

MrSquirorc::.al
ioo:llhost

dominio: lc:»m

Para instalar Oracle WebLogic Server nos vamos a la siguiente URL: "http:/ jwww.
oracle.com/ technetwork / middleware/ weblogic/ downloads fwls -main-097127 . h tml" .
Debemos aceptar la licencia OTN y descargarnos la versión genérica de 794 MB que
contiene el instalador gráfico. La versión actual de We bLogic Server es la 12c.

o -
SÍQ'J INRegCim Help Co.rtry v Cc.rnuifn .., 1a'TI 4-•• v l •.nn! to. . v 1Se.,d\ Q.j
Products Solutions Oownloads Store Support Tralning Partners About 1OTN )

11,i,¡tnr•~M~Hone

O.Si"l~ lni:!IKI(<~
1.,.,,,... ll ....,"'" )1• - - 1' <nm••~ 11 """ J
Orecl• WebLoglc Server lnstallers
u$1llr.(%( lfli OTN ~IW o\11i+llt<HIIII t1t ll~OiiCI tl! t ~U
fl&d v~rg!on cl'/,l&~L.C:IOc Sorwr.
•P' LI OC!'tllt ~~etmt111 10 o.cw uun-:o~rocrncM
JRot.tl
Or.tcle v.t~Lo a ic Sti'Ytr U .t .1.1
II'Kiil11er• 111kfl Of•ele 'N:tJlOOOC Str.-H •lid O'~ COhereooe:
The O!JI~ hSHier Q$¡>1S 1\111 Jitta EE 1 o;..~IOO!'Mnl anCI !flt'-'CIU Oo'ade W~tt.OO~ ~~<~d
()r~t Cone~. Tlle tene.f!Ci•JU ,.,t!l$0if'IChldeS <ntte \WCLO(IlCS eNel $~\11 O'acJe
C~lwen te and 11 ~ds l'MRPk-' .,~ "mS9ll: hefp,~. Tbt f111<~ll M dd ~111e lñ::Nt:31;:e
"''''''" hlb lll:r fii~!IFII!IIO'!I M.:~lC'U.\lif'e Wt:cf uC:,.,.II fiC<IIArecl A!u(JR.FI I:r ~trQ
lrolJII!"n ~nl clong~ "'"lhrwull!~t;o C)a1"1:n$.. U,;~~~lng bw;~l ~;rla Ofad"VAibl<>JC Scnu
""'l!tn~nl. 01 wl;lll ~p b in~1tl M N • Qr~t F\.ISion Mielcl~art ~I OIJIItl. Oo.rnlofciJ" loO d
Qr.-clt V«!l~~ S!Ne~". 51101110 • o.-...ot'l@ f"u!Ocn Mllllllt ,., ~ tnll"tlltlr~Kt.rt ~§!'!' U!-.tS

stlt
'fl:\tlll~il ~ mtl.llllll Oradt W>H.o::.oc:~tf Cucnucu~AVJI~bll rli li! OIJ\1 cot.notOo.:""J«!M
o~:ne~(m)blel Ot lhe f 101011 ~\~11t••...,~ ht~Jtr~l'.ln¡ ln,btl ~l.

C:''t!C htla""
! C..non( fl'Ool MG.
Mtll~ Slrt•ruO()rJckl COf'ICt'Cfltt:

"lt•f'IC" lll•.c-1..-
fOf ln ltn..a:liOfll on uO'ro me GeM«- rWo'IJ~' Sl!ell!l8 40CIII!'Iíl!\t
ev!ftl "'~N!'.~
0CII:II I1151111k l UUienllcll lotCiratll' ~ SoeiVU MoCIOI•CIIe CC>hl:l cll«Cin'~
&,.,. ., a lnloligo<ou
foonolol..., M0.
om ! Q¡/0;:1 I0$1.1>.,.. lff 1,1.._ 0$.)(, V-.Wl- _.,....,_, Q(J9 N8) 1f@i(!ffifj
i SJ~ncnlll OUCII tl~l(225 11.9})1 U.""kftt

Una vez descargada, procedemos con la instalación . Se nos habrá descargado un


archivo comprimido ".zip". En nuestro caso "fmw_ l2.2 . l.O.O_wls_Diskl_ lofl.zip". Lo
descomprimimos (obtendremos un fichero ".jar" ejecutable) y podremos eliminar el
archivo ".zip" descargado.

© Alfaomega - Altaria 751


Curso avanzado de Java

*caF~ritos
~Cit9;~
O Nombre

ll/l0/2015 17:2.4 Archh·o JAA


1Tt m.3ñO
etll.63S KB
• &cñtorio
~ Sitios recientes

Acto seguido vamos a la consola de comandos y ejecutamos el fichero ".jar".

El instalador que hemos ejecutado desde la línea de comandos lanzará el instalador


gráfico.

Ora ele Fusion Middleware ·12c Weblogic


lnstaller

Generaci~n ele la secuencia del asistente en ()Urs~.

ClR.A.C.LE~
~~@ IS9S, 2015, Ora<>le y/o..,. iiiales. Todos md<-_....,'hos reservaO.. .

176 © A lfaomega - Altaria


JEE. Manual práctico

Vemos a continuación la sucesión de pantallas del instalador del servidor de apli-


cacwnes.

Bienvenido ORACLE'
FUSION MIDDLEWARE

Bienvenido

Actuaizaciones A utomáticas Bienvenido allnstaller de Oracle Fusion Middlewar e 12c (122.1.0.0) Weblogic Server y Coherence.

Ubicación de Instalación Utilice este lnstaller para crear un nuevo directorio raíz de Oracle que contenga el software Oracle
Webl ogic Server y Orade Coherence. Puede utilizar este software para configurar un donWiio de Weblogic
TJ?O de Instalación Server para el despliegue de apncaciones Java.
Co!!'C)robaciones de Reo uidos
Para obtener más infonnación, consutte la sección sobre ~sla!ación. aolicaci6n de oarenes y actuar!Zación
Resumen de Instalación en la biblioteca de documentación de Oracle Fusion MkkUeware.

Progreso de Instalación
Puede acceder a una ayuda en IÚlea sensible al contexto mediante el botón Ayuda.
lnslaiación Terminada

Copyright@ 1996, 2015, Oracle y/o sus filiales. Todos los derechos reservados.

< Atrás > Ierminar Cancelar

Pulsamos en siguiente, el conjunto de pasos que vamos a seguir serán los que se
muestran en la columna de la izquierda.

• Actualizaciones automáticas.

• Ubicación de la instalación.

• Tipo de instalación.
• Comprobaciones de requisitos.

• Resumen de instalación.

• Progreso de instalación.
• Fin de la instalación: instalación terminada.

Seleccionamos "omitir actualizaciones automáticas".

© Alfaomega - Altaria 771


Curso avanzado de Java

o~
ActualiZadones Automáticas ORACI E'
FUSION MIDDLIWAR&

Ofcnvc¡'!i:lo

?,1 ACCUJII1,aC;IOMS Automi11c.3S


®2•• Ac;:lu;slicocil.lne:> Au1uuú!iot4

Ub:S!!e'ó n do: lte1411clón.


O Sc."cce:Ot'lor pcrehe,3 ~c,:Kic el di-eetorio
!JbksciOI\: E~r.mtnl'lr
-non tl'f:: h~!!)!!t:rl!l 1 11 1
Co!rDc<lbac:onaa da Rgguiltos O a.ascar Acruat·zacones en mroraceSIQ>Ort
Be.s:·.tmm d~ ros.tela~ido y_~uarb. 1 1
Progres o dG 'ruialación ~«ht»eÁo: 1 1
ttseeiaei6n Terrrinacla Valores de Proxy
1 1 1 Probar Conex10n 1
~scer
1 1

11 Ay-<Jda 1 1 -< Airáa 11 11 I Gnrinar 11 CancGbr 1,

Indicamos el directorio en el que queremos instalar el servidor de aplicaciones.

ORACL..E"
Ubicación de Instaladón
r:tJSION MJX)~W~

DeoyetJI<!O
Oiredotio Ro~ de Qud::

Ubicación de l ns\ab.ción
Juegos~ Funcion@s lr.stabdo& e :a el Ohdorio Raíz de Oracle Seleccionad): 1.-'".:c..J

hal!ioción TEJrrrinada

Eldre;::b-b raizde Orscte s:013 pue.l~ co~;.ene; caracteres: altanum! riCO$, de sutnyado U . guiór. (-)o punto O y
d'ebe empe¡ar por un carácter alf;numéroo,

Cancelar

178 © A lfaomega - Altaria


JEE. Manual práctico

Seleccionamos el tipo de instalación: WebLogic Server. Coherence es una herramienta


de Oracle que permite fácilmente la escalabilidad de aplicaciones.

Tipo de lnstalaci<Sn ORACLE"


FUSION NIDDL,WARI:

n po de l nst,¡J~IÓn

Gl Or.te~ ru:sion Mkkll<:w•re 12c Yicblogic: ~rvcr y Cohe renc:e 1~,l.1 .0.0
8 ~k!Or Pri 'ICI;lil

Strveor d t A¡llesccntt 9rlfi(II)3112.2. 1.0.0


1\rC..,IYOSGO P:~c:tO d8 CO/lOI'ClftCO 1 .22:.1.0 .0
Servldord: f\.rbicacilo-Su$ct'lpcón en HTIP de WeO 2.0 122.1.0.0
Ylebt.ogi; SC4 12.2.1 00
Ar c~ivo:sJAAck Cfr.nk ~ Webi..QtJic. 12.2.1.0.0
8 Herramemasde Adlrb"s!lsdón
A ICliNOSde A}111~ C!ll? ldlomlAdJ:t male; de 18 COnsaia!)! Ann1Wacér.122.1.0.0
Q;nhjurQCI5n<loe \YlS ele tiE 1Z.Z. 1.0.0
e Soporte 41: So~~ de. O~CXJ
Contfo\1~ .l)G(! de Tc.t«:ro:l 12.2 1Oil
S C(HtfJOIIetl ltr,: di! o~ Sol.lrc:o:
5oporli: de tllt\.'é~ de ,U!CIOfl Vl:!k!~ WIIftl 12,2, 1.0.0

canoebf

Comprobación de requisitos del sistema.

ORACLE"
Comprobaciones de Requisitos
FUSION NIJDLJ;WAR(

Bienvenido

-
trP comproDafiOO vers.On ~e Java uutzatta oara lflletar 1:as1a1tt
c omproDatmes oe
Ac!ua!izaci:lnaa do Seauri:bd

Prog-eso (le lns.talliC'10n

IM!olooOn Tcrni1odo

<Atrás !crmhu

© Alfaomega - Altaria 791


Curso avanzado de Java

Deseleccionamos la opción de recibir actualizaciones de seguridad en nuestra cuenta


de Oracle.

Actualizaciones de Seguridad ORACLe·


FUSION MlOOLEWARE

PtoPOrtio~:re $J diu:cciUn de ooaeo e ~ o.ara rccbr inforrntci ~n .wbre b$ llf'Otiem$ de scguridod. cOro
nsttbr ee producto
yQÍIIK' irlic;Cr elye::shn d:: conliguroc'ún.~V:•:•:De:f:•:'""-
:::________,
Adaliweionc:a de;:
$_orreo E!eC11'6n'CO:

~==:~
t..t rfWtl88 mM r4cu ' ' unll::,e •• dlr!QOIÓt'l O! CMto !if:otrÓt.ICOY ti
IIO..,C de IY.I.lCII'lo d-0 My Or.lclo Support

¿ nsu•Bdón renrfi18Cia

Continuamos hacia la pantalla de resumen de la instalación.

Resume n de Instalación ORACU:


RJSK>N MIDot.EWMe

Oi:::nvcai:b l3 lnctabcrOracle Fusion Nlddleow.are t2~ Vteblogic Servery Coherence


EJ Ubteaeión <1& tnctalaeiOn
Ubie3c:ibn de1 Drte10f'O lb ir. M Oradt: C:t.Or&c:ltV.~icldltw8rt'IONioe_Hcw'De
Ubicac:il\n del Arc:hi16 t.ng: C:\!.ltltsrsmaefiAI'tl't0a18'Ltlelfi.Tt rnp'IOntlr&tt120'1&.0&..18_0S..l5-31Pl.tti!ls1
cll23 t &.OS..18_ "-35-31PIAJog
e Espacioon Di eco
H€1c:eaar0: 803 fJB
DBponibD: 6845.82 MB
Resumen ele In!Sial.actón 8 J uegos de fgncionn Que Se Van a lns13b r
C.,;ntroladores JOBC 1M Ter~rc;s 1221.0.0
SGporte 4• rJ8\•en d• F"son f.IW.ewJre 12.2.1.0.0
Strv.dor de A.picscic;ne..ot Pnncyal 12 2.1 .0.0
.t.rcnr.·o~ de Product3de CoJitrenat 12.2.1 o.o
Stt\I'.Mr de Pubii::!IC:ón•.S;JS~" M H1'11J dt V/~b 2.0 122.1,DO
W~~l.GgeSCA 12.2.1 0,0
Arcnwos JARoe Clenteoeweot ooc 12.2.1 .0.0
Arcnwos oe AYU08 <re 1310na Aalet>11aleS oe 13 consola o e A.omnstractón 1221.0 o
c onricuraciXI <Je \\'LS <le ct: 1221.0 o
A$pecto.s. Genera~cre Patafoclll! FUW 12.2.1.0.0
OPf teh 13.3.0.0.9

Para carrlllar 13$ opcbnes. anleiiOfes jnles de h'dar 13 lnstaac'ón. seec:::dOne 1¡ ClC)CI(!n (liJe resee carnlllar en el
Í2Ct\.rierdo o !l!il:ce el botOn AtrSs.

Cancelar

IBO © A lfaomega - Altaria


JEE. Manual práctico

El servidor de aplicaciones debe terminar de instalarse exitosamente.

y 81P.nV'll!'l~eo
de InstalaciOn ORACLE'
FUSlON MIDDLEWARE jjl
~ Acb.Joliz:ociones Auto:rit~

uru~:aodn Cle ns:talacón


.,j' Prep!lrar
Tipo de 1M-tola OOn
.,j' Copiar
~ ComproDaOCl-nes de REQlltul)S
~ Gener~nd'O fiiJio~!)
~ Aotuolitocion:3dc Seo:.~ri:SOO .,j' ReallzanOO ~dclnes ele Cadena.~
Y RecuCMn <ltla~ll016n
~ Enloco
de ln::s.tll!.oetón
~ CCin fiOIIfU
ln&t311@n Ter:nntdt .,j' Guart:tanM envt.:'tano
# SerPI do f.\:mr,abbciln

1ver!!..,,.,.. ¡ 0 :lorTcreoaComx:tu 1VerLo! )

~i;i ,....-~
Hardware and Software
E-vinee111d to Work Tog8Üier
- --- '-1
Axuda J 1 <6tróa D » 11 Iorrrinar Jj c..co~a. 1

Pulsa mos en "Siguiente". Observa mos que a pa rece la opción "Pasos siguientes " a l
fina l de la pantalla que debemos seleccionar para configurar nuestro dominio de trabajo.

I nstalación Terminada ORACLE'


RJSION MIOOlt"WAAii:
Sionvoni:lo 9 ln stalar Or~Je FU$101'1 ~ llklteware 1Zc Webloglc serverycooerence

Acb.loii-"' e<O~ AIA.o:ritC'4S Gl UbicliK'iÓn do lnetQiaciÓn


Ubiccoi&o <fd Oi'cclofio Roiz <.k Oracl:::: C:'IOuu:.I:'!IAUlkwcre\Orac:li:,.J1orrn:
urueaeón 4 ! 1ns.tstaeón
UOIC8CIXI OCt Afe11tl0 Loo: t \USff'$\!Sf11teAA,pp()t~ Ui\l.oc&:\Tetr.~\Or8in$l81~0 1~ 18_CJ&.35,J:1PM\'r'ISI
Tipo do h3tobdón o12:0 16-4&-18-((;.3S..31~. bg
Comptobl!cbnes<le Rcqtjisfos 6 LoiS J uego~ de runclonu se 1111n lí'lstaUI<SO correctamente
controlatl~res JIEC M Tereems 122.1 o.o
Soporte da 1&,¡\f'oo c!e FooioB Ni&lewara 12.2.1.0.0
Resuman do lnst>1lsciot1 SertOor d~ .64!fcotion~ Princip.!ll l 22. 1.0.0
A rchivOs dePfoel!dnCie Coherence 122. 1 o.o
Servclor de P\.ücaoón.Suscnpcibn e11 HTTP d! Web 2.0 12.2.1.0.0
ln$taiac:l6n Termtn tda
'lú:blog.C. stA 1 2.2.1 .0.0
P,f'(1l!VOS JAR 00 Clt lllt G~ We::!:UJtJC IZ.Z. 1,0,0
Aren!\« 09 A YIIC'* G• k:l!Otl:.a ACIICIQI"'31.C ot 11 ( O:!CO\t O. AGII'I~IIttf8C:IÓn 1:Z.2.1.0,0
Confgui"'Oc:iéndo \\ll$ do CE 12.2. 1.0.0
A$pcd.O$ G-:octab$ de FtiU.ftttlll:l f r.l'/1 12.2., 1.0 .0
0 PMC!I 13.3 .0.0.0

P UOS; Siguiente~t:

L•

© Alfaomega - Altaria 811


C urso a vanzado de Jav a

Un dominio de trabajo es aquel bajo el que vamos a configurar nuestro servidor de


aplicaciones. Una máquina real puede tener varios dominios, y cada dominio, a su
vez, varios servidores virtuales. Para trabajar en nuestra máquina real necesitamos al
menos un dominio y su servidor virtual por defecto "AdminServer". Vamos a crear un
dominio que se llame learn ("aprender"). Al final de la ruta indicamos el nombre del
dominio que queremos crear.

Tipo do Configuración ORACLE"


FUiiQN M IDDLi.WNii

~ c~ll f Domillio

tr -··
( yit!!jd¡! AstrírlJYidc!l

T N.;tfn &- Oqpitig y DI(

t C<rlfirurMOO Ayi¡lmda

i Re>ulr.lif"'de connQJrad6n

y Ft~w di: QlnPvtroOOn


0 0 e:atl'fl..t::'IO~I'io
v Frt cf(: COnfro~~aoSn
Q Ad1.;)1w OcnthO E;9sl:tntc

A continuación le indicamos que queremos seleccionar todas las plantillas para


conectividad del dominio.

~
Ph:mtillo~~s
ORACLE"
FUSION Mfi)DLEWAAI

f ::::.·~ ® Oeat Ootr.i'lio medMt<: 8~r~till:l.1 dt' f'fo!IJaD:


T dd Admcmca:doc
Q~
caugo:rt;e de Ftandla: lroo~ la:: A3n~ ;;)
T :r:
~kl<bde OO'l'lhkl JOK
Pbn..."'fus Cia:~o··ibla

f O:nfstrac:ión ov...n r~d!l


~ 8a>IC '1/ll:l:t.~c server OOII'Qh .. U.2.l ('-'b:rver) •
~ Wcbloo:c Ad\';:ntcd Vicb ScJ-v,co ftlr JAJ(~CExtcn3on • 12.2.l lor00e_a:mnonl
T R t!'tll!m d;: Ccnfinmain ª \Vebll:lfJit:Aih-l!f11:ed Vleb Selvice:fe.r lAX·\VSExm.,;o-o • 122. ~ Lo: ..de_~
'( Prcx;:rttlCI de Confr.:ur;ao:Sn ª·~vEbl.og~ coherence d~Ste:f EN.ter6i:n .. u.:t 1 (~'~~vtr]
v flh di Conflg.lrad6n ~~Vd>l..oa:<.JN<·VIS SON>lJMSExtcni10d'l · 12.2.1 ~or~g_ctfl'ln:ln~

O gear COII!~b rreda'lte Plontila Pef~


uttc:.;oon ck: Pi.Jntlltl: 1C:'Orvck\'-'~cnort'VJr.xf~"nC
~1 Ex;mNl
1

1 A1udo 1 1 < !U~ 11 •> 11 ! ewrur 11 C«Jul«

IB2 © Alfaomega - Altaria


JEE. Manual práctico

A continuación indicamos el usuario y la contraseña de administración del servidor


de aplicaciones. Será necesario para acceder al panel de gestión y para conectarnos a
él desde NetBeans.

C~1da dP.I Administrador


ORACLE"
FUSION M I)D~WARC

1 Cr~H Oornno

1 Plaon!as

f C•enta del Admi••iStr300r


~ Modo d! polfiQ!O Y JPK

Á'-~~*-

i1 •.
~·-<wr~
--~--·-- ~
~ Pn:lO'"c::!O dc Confiourodén
ü fin d: Conñou1odón Nombre

Contraseña

Confirmar Contraseña ~..:·c:.:


-c:.:•c:.:
••c:.:
-cc•'---------------------'

ser gB qiJe la~ la corroa:eña debe tener ;i :na"'3 S caracteres alfanurná-icos con un n{,.....,.o o

Instalamos el dominio en modo desarrollo y le indicamos al configurador dónde se


encuentra nuestra JDK.

l~odo d~ Oom;n;o y JI)K ORACLE"


RJSION MlJDlEWARE

't CJxninQ
s:Jl=a;:

1" ElooJ;Ilo:i. ® QeSñlfd lo


T '.HS!Lm dd Admh'slt_• l.Jt:iio boet pr~ p;;ra d notrbfe de ucuario y b ccntJ~ y eonde¡¡ IOls apicadoncs QIJI!: Ce;ft:qar.
~ Modo de: Domie:io y J OIC
O ~roducdón
~ Config..,...ción Avanroda
~"!la la intrOOOC<ión ~ un ncrnbre de usuario y una (l)llltróSe"'e y no~ las cPita<J()llfS OJe
j Reamen de Con~guraóCÍ'! de:::pjegar.

'( Progreoo de Cont\gt.raOOo


JDK
U Rn de: Configuraoón <!) Q"ade H01SOCt J.6.0J7C:'/'RQG<A.Nl\JaVa\JDKl8~J.OJ

O lb~de.o<cJeooos: '----------------' Qi..,h_D

!Ertrinar

© Alfaomega - Altaria 831


Curso avanzado de Java

En la pantalla "Configuración Avanzada" dejamos todas las opciones por seleccionar.

Conrigun1ción Avnnt .)da ORACLE'


FUSK)N M IDDI.E.WAAE

t Of:i!!'>rrrWn
t pt:nt1Ll~
O Servidor ele: AdminDtr•ciúa
Y.cdfi<ar Valores
j o wra M ertr1o15tr-;rtr
J: tt;rbdtCqn!oby 1)< O Gc>tor <k Nodo>
~ Coof.g•ritCiÓn Av~tlzada Confg...rar Gestor de Nodos

w Regl!!ffi dt coofarac!OO O Servido~ GotiQr~ Omtcn Y Colterc::nc.c


y1
Pt O(Irt:SO <fe ccnll'J.raclón 1\g"tg.;ar o Suprírni-JModifi;ar Ccr.fg.rad::n
v 1'11 de ccnro..racl:)o
O Doo&couc> v Scrvic:lo>
t'irigi ~ Sc:Ndorcs o Oucb:rc

0 A.hd.<:én de Archive» de lHS


Y.odfi.car Valores

Clr'«Jar

El configurador nos muestra un mensaje con la configuración del dominio. Es una


pa ntalla d e información, simplemente pulsamos en "Crear" pa ra crear el dominio con
la configuración que hemos ido indicando.

'f
Re,umen de Configuraci ón

O~:a CU'rinio Va: l~>«;~i<guc -1 -br·


itll ...,.,
ORACLE'
FUSION MIDDUiWAArl
fil
ts:tsat WetlOQicserver CO'T'an
O"ta IM1 dom'rio b¡cico de Webl.ogic S.
1' !!l!!l- C.S:aipdón
Orodc:<#pcrotion
re~
-~

T euenm ~ Admn~tr~or
B e AdrninServc:r
Ltlcac:l6n

lj Ml'!do dt rxmnrn v
Coofo.radón Avanzada
l!l(

Resunten de conflouracióll
a ~ser•,,ao
G !<rJVS
6 ¡¡:;. Ftbrica de COilellO!leS<!e JOIS
-br·
D::scripdál
AUtor
Weblogjc J,\.'(-\VS OON'/JHS
Exb:n:J en eJ1i$frg Wc:bLogc Servo de
o--ade Q:lrJI<:fanon
D Lbicadón
J
_,
Pfog1Eto de Conf:gJración a 12;7 ~,·idor :MS
u fía de confburCCó1 D WseeJaxw~~
D Vl:~c:clmsSavo -br•
Descripóón
Vfetlt..ooeo::ilererce duster
btrPa oo cb..Tinio existente de
D WseeSoa~)m;:Server CT.xk ~pc;r01tion
S (;3. Aju~t: Autom~tic:o t...tica::i:)n
s ¡¡:;. Gostor de 1tobaps
Nembre V'lebl.ogi:::Adv:~need \Vcb Sc:t'·icc:c fot
D Dc::$aipdéo Extcn:l en cxi:sf"9 Wc:bLogc Servo- de
D ~"' AUtor aacte COUI«anon
8 ~ O.q:>ieg..e
Lbica:i5n

_,
G (O- 1\Picoción
D
-br·
Descripóón
Vfebt.OOC:JidvanCEd Web 5er\'1Ces for
ExtErd en e~st"rg \Veblogc Server dl
Oradc: Coqx;r01tion

Cr~r !)\~ro wo::pt.:lr l013 opdorc:J a"tbctVc$ e iriówr l01 oox:i6n y confi~o:i:.in d: un nuc:vo dorrinio. p.,..,
ccnfigur~ ont:erior cnte3 6c: h 'Qcr&o <r0:1dón del dQ'flinio1 Yud,·c .ct &o~ rdev.::ntc: sc:lccc:ioNT~do,.,
en d ~nd de lo lzquic:rcb o 11'\Cdi)f'lte d botén Atnis.

1 Alu:h 1 <~R 11 Siguie!lte > ~1 ~leat


1• J

IB4 © Alfaomega - Altaria


JEE. Manual práctico

Una vez terminado el proceso, nos aparece la siguiente pantalla.

Progrt!$0 de Conf~g1.nación
ORACLE"
fUSION MIODUWARE

'1' OolrillO
Crc:QI

t Pl3ntll!ae lOO%

1 Cuenta del t.dllintuad!r Prepara!'\00...


f'lc<lo de DoninO y .l:lK Extrayerr.:b C001eoi:b d:f Dolini::)•••
Cre~ndo Info!'fl"o!!dón de Sc:o>rikld dc:l Dcmno .. ,
CO'"Ifguraoón 1\v-c.ruada, GUM'cla'ldo Infora"~aciSn d:t Oorini:l•..
AtnacEs"\andO In~ d:i Dolrinb.•.
Archivo:; ck eomno de Sus·!ib..óil de Cadena...
P.caliz;rdo Tarea:; ~dd S:;terna ~tati\'o , ..
Reó!lililn:lo TanMs Pos:tMotes a~ O"Nclón dt!-1 Ccml'llo..•
Oo-nirio Cn::odt:J C«tc<:ton:ntc-

<

Pulsando en "Siguiente", vemos la configuración del servidor.

r=in do Configuración ORACLE"


fUSION MIDDLEWARE

f Oear Dominio
./ Orade conRguraeión de Weblogie server correcto
f(' Pbnti)~s Creación de Mtlevo Dominio leam Correcto
y OJenta del AdmnstJalkr Ubi0ló6n de~
C:IOradeiM!I!de'ft.nJOrade Home/user moierts/dona!a;learn
( Modo de Oonnio y JDK URL del Serv:dor dt AdrW1istraOÓ'"I

>f' contlgU'aoón Avanzatta bt¡:~• tfJ?•ZOOI/ml.<Qfe

( ~rmend~ Confqur~

J PJogrc::so de: Contguroción

Q Finde ConfWJuraOón

>

© Alfaomega - Altaria 851


C urso a vanzado de Java

Bien. Hemos instalado nuestro servidor de aplicaciones Oracle WebLogic Server.


Veamos ahora cómo podemos desarrollar aplicaciones en nuestro IDE (NetBeans) y
desplegarlas en nuestro servidor de aplicaciones. El primer paso que debemos dar es
abrir NetBeans.

~~

~ B II
-
~"""'

.........
,_ _ _ ...,._.......

,.,_ _,
. .._.,4

En la columna de la izquierda (que, una vez abierto NetBeans, se encuentra vacía),


tenemos tres pestañas:

• Proyectos.
• Archivos.

• Prestaciones.
Vayamos a la pestaña de prestaciones y cliquemos en la opción "Servidores".

fJ
ArclhLvo Ver Debug Profi l~e Team Hecramiemtas: Ventana
••

••
• ~ 'El ~
Projects Files Pre staeiones X -
~o ,Bases de datos.
·•·····~
: ....;
DI .Servidores
S· 4 Cloud
$% IHudson Builders
$"·rta Task: Repositories
@Q¡ Selenium Server

IB6 © Alfaomega - Altaria


JEE. Manual práctico

Una vez que hemos hecho lo comentado, nos aparecerá el siguiente mensaje indi-
cando que NetBeans está activando las características de Java EE (este mensaje no
aparecería si NetBeans ya lo hubiera h echo).

Activating Java Web and EE

Gan<elar

A continuación se nos pregunta por el tipo de servidor que queremos enlazar d esd e
NetBeans. En nuestro caso y como bien sabemos, es Oracle WebLogic Server.

Pasos Selea:ionar servidor

L Sele<x:iona r servidor
2. . .. Servidor: r::~:,~or"iiToO.m;EiEEE-----------l

Nombre: larade Weblogic Server

[ < Atrás ~nc:l!lar 1LAyuda__=)

Nos pide la ruta de nuestro disco duro en la que tenemos instalado el servidor de apli-
caciones. Se la indicamos. En "Tipo de Registro de Dominio", indicamos "Local Domain".

© Alfaomega - Altaria 871


C urso a vanza do de Java

...... Serv~r Locatic>A

1. Selec:riomr !:l!rvi.ior
2.. .5erver Lcx:a t1or1
3. Inob ncc Propc:r'Seo You g:n dov1J"I0!2d lhc Ot-odc Wd1.csjs; Scrvc:r ol Oradc V\s:b:ils.

Ocm<lin R.cgBb'ati()n Type:


® l oo::JIDomcin
O Remate Oorn:!lin

Tennnar ~ 1 Cancela< 11 Ayuda

Por defecto, ha detectado el dominio "learn" en nuestra instalación. A continuación


le indicamos el usuario y la contraseña de administración del servidor, aquella que
indicamos cuando creamos el dominio.

Pasos lnstance Properties


l. Seleccionar servidor
2. Server l ocation
3. lnsblncc: Properties ro select an exls!lng doosah, seea lES nane from the d1op~wo 1st. If !he domain's
nane does not appearin the ist, you can type the path to the danain diecuy.

U!:lername: ~lw::•b=lo=g=c=::::::¡===~
Pm.sword: 1• •• ••••• •••1

8 Rc:gi::~b::r exi:;ling domain: k:orn

< Atrás 1[ Sígui8>1! > T<rrrinar 11 CMo!lar 11 Ayu:ja

Para finalizar la instalación del servidor con su dominio en NetBeans, pulsamos en


"Terminar". Si ahora acudimos a la pestaña de "Prestaciones" en la primera columna,
encontraremos el servidor "Oracle WebLogic Server" instalado.

IBB © A lfaomega - Altaria


JEE. Manual práctico

1±1··· ~ Bases de datos


S ~ Servicios Web
e~ Servidores


¡ ~-·<$ GlassFISh Server 4.1.1
ID·· Oel()(ade Weblog1c Server
.... ~ Reposítcrios de Maven
Cloud
S 'if:¡ Hudson &Jilder s
S fiiij¡ Tasi< Reposítories
éJ. . ~ JS Test Driver
ffi...¡j Selerilln Server

Para iniciar el servidor nos posicionamos encima de él y seleccionamos "Start"

Stott in ()d)u!J Modc:


Statt in Profile Mode

Stop
REfresh

Rename...
Remove

Vicw Admin Con.solc:


Vicn Sctvcr log

. . .
Pod remos ver en la salida del sistema el log de operaciones de l n l ClO del servid or.

-~ .... - . . . - - - .... Ooht ...... , _ _ ......,.,...

¡eri!t 11 ,. o.~ co • ~ · T 11 • · • · • ·

__ _
---·-u ..... _.., -..
.........
c.-•· ...._
•• --···
-··- - --··
---·-·_ -·-
........ ... .......... c.-:. ~- ....................... - · - - -

.._..... ·-·· - ·-· -·-- _,.,_ - ...·-·- __,-- .... t-•..· -·,
.....,_.._,
001-•000.. 10•0"'-M OI'J - ....... - ....... , • . 1 .1 · · · · - -~ ._,..,_,, •••- - · -_,.-.....-...;,. - • - olo ..__ _ _ _ _ , , 0,0•0_ _ .._

···-""'
_
,..,,.
.... ....... _ -·
_
..., .oloof-·
...._....._.
-·· ~ --··'"""" ---·-· ........-.•
-·-. ...-... . --.. -. ·-·· -too)--·--·-» ~--·
. -. .
···-""' ,,,..,. ,.._, . ----·-.
. . ............... ¡. . . .-.·-·. ._,._
..., -_._. . . ..-.. --.....·------
..._ _, · - ·· ...... - > - - _.,_....... - .... __..., · -..- · . - - ..... - - - · · - · - ..--··
....
·..· ---·-
~ ~· -~.:~

-..·-·""-'''"'.. ''' ,___


- . --- - ---
. .- . - - _ . _. . . .. -. . . . . . ._ . . . ., . . , _ . ., . , . . . . - -· e - ... . .- - · - · - · ·· . . .- .

-~ l+:ltOUIO·- · -.noU..--~--·::_••IA:Io•

-· ...
,,._,.., ,_ ... -.... -.................... - ··
- ""''.u"
~ ~- ·-··
......
.. - ...... ...••·--.......__ ,_,
····-·
•tt ~ Ml4 1 l.f.ll ....._ _ • - n"Oúooo -~-~- ...........-lt>o,_..OloWllw
..... -·--·---
,_........
~..,- ..... ...... -·· -·.. -·--- -
..- ...__ ................. ...·-· ........
_,

'* "'". .-. ,.,. ,.....


...,,,.
~ .-..:' ~ u ...a....,.:.o. ... ••tr·..U.-.o.-..
l ....,,..~-· - ..... .... ..,..,,....,, _ _ -u..wh..
-··· ~·~· ~
•U -1+11 111\Y ftr.• -~· .......... '""'""1111 • .,.,_ . ._ 1• ...,.,,.,,¡ IN"'
···-- ·.... - -···> q,....~ ........... ... - · -
"'lll ' o - " . .¡.,otC,. l ,t, , ....., hll-.!6 ·1 - ,..,., .... !oroU , H ltt.' IOJt · - I ( U -

. . . . . . . . _-. ... ..........


· · · · .. - ••- .... - ... ... 1 · ·- · ... · · - - -· · ..... . .. - - ..... .

··-·... __ ...... ................._.......-·w.-·.......... . .................... . . - ...-..-·. .·-.-···


••• Oto, . . . . . . . to .. _......... · - ......... - · - o t . l o l - - - .. - ....- . -.
, , _ _, ...,... .. ooo.O-••••-•·•...,_ ~ ._ - • _,.,..,,,..,.,. ,,.,.,-._...-.,
- Oto, Nll• 1 U'll .....'-""" - ....,,.... - o - o t . l d - . . - : . - . -...........c._
.._ ...
. . . Oto, . . . 1
~
u .......- ·- ·~...... _ _...,_----~·
•. . - -, ...-......-.......................... 1.090••......•· . . . ...-.,. . . ·-.-.. . J•lu<•- - -·
. .....-... - .....-............
ooo ~ Nll4 O'U"U • - - - ...,,_ -·-~---,::_- - · •....l ' -• ero...•- ,..,...., - · -

-....-- .......·_
• • - .._ _ _, - · .., ,,,_ ,......,,,_ ...u u - ._,_, _ _..,._.._..._..,.
u" .....- ·-·-·- _ _....,_ _ ,.,_..,141•-·•·
, _.,.
-..---
. . . Oto, . . . .
.-- -..-....
··- .._.._ _,_,_ --"'--..... -·. -;__,
... ~ Ml4

... »<•."
..-
-
~
,.,,.,u ___ .._----·-~,--

- - ..........
_. .
, ... ""._..............., ,._,.................._••_
nJ4 , .,,..,, • - . - . , . . _ .,..._ _ _.
-···-·~···- ..-:.-
. ............
·-·-
.. --~

• . . . _ ......_

......... .,...--..... _._-.........................


.-
.......- ••• . _ .... ..1"......._ ..

_
~ -·-.,<~-

..- .....__ ........ .,_.._ ,_,.,_, -·- · ... .., __...... I..JIW'I'"""'_'__....___. -··- ·· ...............
......_...... ·-··
,_. -....... --·_ ......·-·. .-·u---·...., ......._··- --..-.,..,.,u.-
·-·-..... ._ . ............
. . . -. . . ..-,•..........
. . . _.. . ~.
,.,._...,. ·~·· ar.o
_. -·--· -·-·
-•o:o• oe>oooo•• .....
....... __
~.,U<.........,,

"'ot'" - ¡ -·•uu• .. -
..._ .... '""'''- _.... ...,,_,. .....·-·U> -· ........- ··- .... - ......
" " - " " " ~~- _..... "hcooo.. ' " "
'fWoohiU' oo ,,.. , ,,,.,, .,..., _ , ,, - , _. , _...., ....,,,.,, - • ~...... _.. , . _ o.>, ''•• -

~ ··-lo--ol· ..........,.-....-- .-. . ...... .


~ * u•.•1•·•:"",..
...... · -
.,...
--. ,...,_.
· ..,,. •

...._ ,..... - -·- ·-..·-·-·- - ·-··--- _,...


• 1 0 -J+It l o . ! " " - -
... _ ...... , ..... -
· · .,.._, ··~-Hl._ -
_,_, - · - ·· . . ... .. ._., -
·.¡ -•oltloJ' ~ -
·· ~··•• 10)'
u..,...._., .-- t -
.. · - ···· - - - ........ .. ...... ...., -
..- • - " "
....
""'' - · - h< . . , _ , • - ol, 1-, - · ,..... •
·u-fololt IOCI<" ~· _...., . _ . .. _ . . . . . , ou- """ - .,-~•"' .,... - · ·
......

© Alfaomega - A ltaria 891


Curso avanzado de Java

La operación de arranque estará terminada una vez que veamos el siguiente mensaje:

"<Notice> <WeblogicServer> <BEA-000365> <Server state changed to RUNNING.>".

Pa ra poder gestiona r el s ervidor, de bemos seleccionar la opción "Ve r la Consola de


Administ ración".

Proj eds 1 Files 1 Prestaciones xl


lB·· lB Bases de datos.
$···a Servidos Web
$···~ Servidores
j $-<::; Glassfish Ser ver 4.1. 1
! lil-E
$· ~ Start
ffi...,¡,. Ooud Start in Debug M od e
ffi··'1& Hudson B1 Start in Profile Mode
$ fi1l TaskRepq
Restart
$· ·~ JS Test 0 1
Stop
lB···fl Selenium
Refresh

Renarme...
Re m ove

View Admi n Con sole


Vi"ew Server Log

Properties

En dicho caso accedemos a la ventana de administración del servidor de aplicaciones.


Como se ha comentado, nos encontramos en el dominio "learn". Nuestras aplicaciones,
conexiones y demás elementos de JEE se desplegará n sobre el servidor virtual "Adm-
inServer" d e dicho dominio.

. ___
....

. - ..- ~~. • ......,...._ .. .. _ , ... _ _ _.,¡, - ..- ' -... - ...... ........, ......

190 © A lfaomega - Altaria


JEE. Manual práctico

Para poder desarrollar aplicaciones empresariales, necesitamos conectarnos a una


base de datos. Vamos a crear una nueva base de datos llamada learndb y vamos a
enlazarla a nuestro servidor de aplicaciones para cuando nuestras aplicaciones hagan
uso de ella. Supongamos un gestor de base de datos SQL, en este caso MySQL. Ins-
talamos a n ivel local (localhost) tanto el SGBD como un cliente, en este caso el cliente
web "phpMyAdmin".

· $Wdo>"~ ~·•ICA~f>J
• ,_,, .... MJ$Ct
• W1't•oM <liJO)Tif'lll UlHt ~eo-,.,~*"''~ lU'tl
.· !.'>
v.nu _ ...
_,......,.. l!
........,,
. ~ ........ ,,.•• .u ....w... utr~1..Wo:>."' fAll
~ , ._ .~. 1tiH"•$,Wlll ·1
• T(floS:I ~nt ·1
. '-'> ttW<~a.-1 1.<'11 ·1
• ~.41{>-w)nt'l1.4)
j) Nio_.l:_ . , _
..._.,....,..,.
• """'t""'401d . . .t.~•- , ~·· ·
,,.~~·'l~W~u.tmt:ot.+» 5
~
..~ ~:~-1111-·lf·

·---
• ,t.¡ roo 1t ad • vt~•f•

.....,.... . . .. ,..,,.....

• C"o...-
. w·- ._.,
....,'1. •tdlo'l wlillf Al)'"'""' 1 '-•

• U.b<lo ........

Acudiendo al apartado de "Bases de datos", creamos una nueva base de datos "lear-
ndb".

o ?diJ= test&token= 7 d6806cd716358266891 c2bea2329b56

phpMyAdmin
ll!!l Usuarios
~~®) 1[) ~

(T'-a_bl_as_re_c_
L..:l ien_te--'
s)' -.._. --·-'1 Bases de datos
@ information_schema
@ mysql @ Crear base de datos ~
@ performance_schema Jleamdb 1 utf8_general_ci • J KCrear)

Creamos el usuario para la base de datos y le otorgamos todos los privilegios posibles .

© Alfaomega - Altaria 911


Curso avanzado de Java

Agregar usuario X

Agr egar usuario

r1 Informa ción de la cuenta


Nombre .de 1Use el campo de te • 1. 1admin
usuano: .

Servidor: 1use el campo de te · 1 llocalhost


Contraseña: 1Use el campo de te • 1 1........
Debe volver a
escribir: 1········
Gene~ar 1 Generar J
contrasena: 1 1

Ba se de dato s para el usuar io

O Ninguna
O Crear base de datos con el mismo nombre y otorgar todos los privilegios
O Otorgar todos los priv ilegios al nombra que contiene comodín (usorname\_O/o)
® Otor gar todos los pri v ilegios para la base de datos " learndb"

Privilegios g lobales ( Ma r car todos 1 Desm a r car todos)

Nota: l.Ds nombres de los privilegios de MySQL están expresados en inglés

Datos Est r uctura A d ministración

0 SELECT 0 CRE.\TE 0 GAANT


0 IIISERT 0 ALTER 0 WI'ER
{!) UP!>ATE @ n.mx (!) PFOCESS
0 DRETE @ oRO• 0 RELOAO
.f:ñ en e Wl CUITnn.IU

[ Agregar usuario J[ cancelar J

Acto seguido volvemos al servidor de aplicaciones y en la ventana "Estructura de Do-


minio", seleccionamos la opción "Servicios" y a su vez la subopción "Orígenes de Datos".

192 © Alfaomega - Altaria


JEE. Manual práctico

Estr uctur a de Dominio

t··[)es.pliegues
8-·SeiVÍCÍ GS

n.es Persistentes
t··J>r011ee•do"es de J NQJ Ajen O'S
¡--¡:~>nlte>d:os de Trabaj D
~--Re-gi~trO$' X~1L
}·-r:ochés de Entidades ><ML
i ·- ;, rn~t

Para enlazar con la base de datos que tenemos pensada debemos escoger "Origen
de Datos Genérico".

Origenes de Datos {Filtrado: E.x ísten ~l ás ColunVIas)

Nuevo v 1 [S<JJ)fri f
Origen de Datos Genérico
Topo
Origen de Qatos Qidünk

Origen de Qatos ~lúltipl e

Origen de Datos Proxy

Origen de Qatos UCP

En la siguiente ventana debemos coger el nombre ("JDBCMySQLLearn"), nombre


JNDI (la hemos llamado igual, "learndb") y el tipo de SGBD (en nuestro caso "MySQL").

ORACL..E' WcbL09fc S.rvcr ConDOIO II9/•d1TlW6ttc.lllon 1ll>


0 ~JIIIJI~ ~~ ft"tf~, liil tntlir l.flcb 1¡--
llt-....-..........-.._._,~.S.Oo;n.~~;>~r.--<lc to..,nJ >__,,.,.~.toO..JOoC
v.. c......;,• .-~
~.t N~t~tñ&!h ,.,.,""•.o5, m~.
~~ III;UMCOnlol05$c ~"\
, ..,¡......;¡;........... _.,. ........"". .
~~ft,J ~ o:.,MIIUC!llfi:~C.ee:u 6:1 ntllt .
~I)IH~ . . ~.Oolo~<te l 08C

..., ~~~~!!tU 11!10\ro,~ M rdli8'> "" H'(rl,!l'lur <el 111~ O!l¡r.• lit ...,.tt)ll:<"·
• l..touu.,,....,.._ ,¡...

.q..,¡ .......... d. . . _,. ..... _...,. ....... Xli)C)

<:m;:u Ol'i:l.n"' u cu.~,.. •U lCOC:

e·~· o-iJ~~ w()f)l.li;Jto:unUJI.


·-~

ra~<o•
Ull!tM

!Ab!~··
,I,Mf!~~
'""' C.:> . -~ --
Cuf.a~(l)

. . . . .
• •• l ' '
. ,. , •. . , . , , • ..,., , , ,, , , .,. , . . ,. , ..
-----------------------------------------------------------------------------
~, ,,,~·'•rw ,_, .,.,., ,. ~•• • •··~·-· · ~ ·

© Alfaomega - Altaria 931


C urso a vanza do de Jav a

En la siguiente ventana debemos escoger el driver o controlador que por defecto nos
ofrece el servidor para MySQL.

i Qué contrmdorte bu~ de dato~ desea utili:!:ar para a eu conWon ~s. de bate!: de datot} Not-a: '" ind1ca qge Oracle WebLogic Serversoporta E)pliOtamente el contro.!ador.

Controlador de 8ue de O¡¡tos:: 1MySOL"s Driver (Type 4 XA) VersiollS:using com.mysql.jdbc.jllx:2.optionai.MysqiXADataSourt:e •1

Le indicamos las opciones de conexión a la base de datos.

(í) Directorio Raíz De:s<Oile)lf00 Preferencias !iil Grabar Ayuda jf ][e.]


Ciredtrb ~ :>ltoam:l de P~-&ir;no do: Oomnio :>Reu:n=n de fnb no :> R~cn de ~te.J de D•tWo JO BC

Crcoc.:ión de rtucvo Origen de O. le» JOOC

~ 1S!!tt:~tnte 1 ji,..,..,. l jlcano.... ¡


... ............. "".............. " ................... ............. " ......... "'"".. " .............····-·.. ' ........................................................ ..-.- ........................................
·~·

Propioedadei: de la Conexi6n

D!fine l a~ propiiHiitdM de la c.o:'le:rióo.


_.,_, , , ................................................ ·- ....................... ......................··--···--·····-- ................ ......................·········----··-···· ................................
,_, '.' '.'

iCuál e$ el nombre d el~ bose de d~tos D lll que de.sca concctorse?

Nombr~ de la Base de Datos: l team<f>


.....................................................·---··--······· ...................... ' .............................-..............................
'Cv$1~s el norrDre o la direcotin JP 4 ~1 servidor de base de datos?

NOmbre del tton: ¡tocelh0$1

l Qaé '1uerto del sefvldOI' de beses d.? datos se ull!ln !)ílfa <ontctarse a f¡ base de datos'

Puerto: j3306 1
_........................................................- ................................... ............................. -..............................
iQt~é nombre .!e u.su;,;rio de cuent~ de li) bDSe de datos .!esco uti liz~r p.M<l ac,¡r cono.iones de b.ne dech1tos?

Nombr~ ck Usuario ck! Base de 0011tos: l admln 1


.................................................... .... .. ....................................................... . ' ............................
<.Q1.1t QOnCr.l~lia de cuent1 de la bu. dt datos st utilinrll pan~ crea; cont»OI'Its dt ba~ de datO$?

CO.ntraci!H.a:
1···..··· 1

confirmar contraseña: 1·....... 1


_.................. .....................................- ................................... ............................ -........................................................... ______ ....................................
' ''

¡..,;. ¡ 1Slgu¡e,nte 111Terrm:u 1)1 caneetar 1

Realizamos una conexión de prueba.

194 © Alfaomega - Altaria


JEE. Manual práctico

¿Qué nombre de tabla ·O sentenda SQLde.sea utilizar para, pr·obar las conexiones de base de datos?

Nombre-de Tabla de P.r ueba;


.S QL SELECT 1

1Probar ConfJQ,Ur ación 1 11Atrás 1 1SiijiJienle I I J Tenminar 1 I J CilllCelar 1

Es importante que en este paso tengamos una conexión correcta así como que
presionemos el botón "Siguiente", ya que debemos desplegar en la siguiente ventana.

Desplegamos sobre "AdminServer".

1..~~
l!lu ....Sc~·

Finalmente, veremos nuestro origen de datos desplegado sobre "AdminServer" .

~~ ~·T~

~~ *~(~">I.M(I:I!Ittt~~(o~-)

·"
'" ·-. . l- -. ,........ . ~t ~ I>Jel

~ - - lo l do l

© Alfaomega - Altaria 951


,

Arquitectura CAPITULO

de so tware

Al igual que ocurre con los edificios, las aplicaciones deben diseñarse a alto n ivel
para posteriormente ir definiendo el diseño de las partes o módulos que componen la
aplicación. A este diseño de alto nivel lo denominamos arquitectura de software. La ar-
quitectura de software no se centra en elementos tales como la tipología de las entradas
del usuario, ni se centra en el color de las ventanas de la aplicación. Digamos que esta
parte es más propia de un especialista en d iseño gráfico o experiencia de usuario que
de un arquitecto de software. La arquitectura se centra en diseñar a grandes rasgos
cómo serán las partes de la aplicación y la unión de todas estas partes para conseguir
un sistema completo. Un arquitecto de software puede seguir diseñando los módulos
que ha pensado en refinamientos sucesivos (bajando de nivel). Pero la arquitectura
compone la unión de grandes bloques que denominamos capas de la aplicación.

5.1 Patrón MVC


Para definir la arquitectura de una aplicación, lo común es encontrarnos con el
patrón de diseño arquitectónico MVC. MVC es una abstracción de casi cualquier ar-
quitectura, en la que el acceso coherente a datos se define como modelo, el corazón
de la aplicación (la que gestiona todo) es el controlador y aquello que ve el usuario (las
páginas web o las pantallas de las aplicaciones) se define como la vista. Aquí aparece
un pequeño esquema explicativo de cómo funciona d icho patrón .

© Alfaomega - Altaria 971


C urso a vanzado de Jav a

--------- -;::, M'ode1o L--

~~
1:.::::
,
muestras eg¡urn
.actuaJi za
··
1:.::::

i
eventos us ua~io

l ______________________ ~
Vista L .- - Contrdlad or
...
t:::
despacha
-
Este esquema, como hemos dicho, suele ser una abstracción, para cualquier len-
guaje y para cualquier arquitectura. En una aplicación empresarial Java, lo normal
es que el modelo de la aplicación esté desarrollado bajo la API de EJB. El controlador
está compuesto por servlets que responden a las peticiones de los usuarios; siendo las
vistas las páginas JSP que se lanzan al navegador. Pero la arquitectura, sobre todo en
el lado del back-end, suele ser más compleja. Lo normal, para una aplicación Java, es
tener las cinco capas que se muestran en la imagen s iguiente:

t
Servicio Acceso a Datos (DAO)
,
1

Modelo 1
o conexión mediante <tiver
adualizaModeloBD(Modelo) o
1 muestaVísta(Modelo) \

eventos usuano " Bas:e de datos


Controlador SQLoNoSQL
,___ -- ----.---.-----------------;; Mantiene a nivel de sesión
VISta o o e apncaclón 1os ooJetos
del dominio adualízados.

despacha

Vemos que el modelo se convierte en el conjunto de clases que sirven para comuni-
car el controlador con la vista y la capa de servicio. En la capa de servicio comienza el
back-end de la aplicación, donde las conexiones de datos ya serán con objetos propios
de la base de datos.

198 © Alfaomega - Altaria


JEE. Manual práctico

Previamente, en nuestro texto, hemos visto cómo instalar un servidor de aplica-


ciones (en concreto Oracle WebLogic Server, pero podría haber sido cualquier otro). La
filosofía de trabajo con servidores de aplicaciones viene a definir lo que denominamos
el desarrollo basado en componentes.

Según Wikipedia "un componente de software individual es un paquete de software,


un servicio web o un módulo que encapsula un conjunto de funciones relacionadas (o
de datos)".

En nuestro discurso (genérico, pero orientado aJEE), digamos que un componente


de software es una unidad de software que tiene un alto nivel de cohesión (porque
las partes del componente realizan una única tarea concreta) y un bajo nivel de aco-
plamiento (porque es fácil modificar la estructura interna del componente), ya que las
partes están claramente diferenciadas (siendo, por lo tanto, fácil de mantener). Todo
componente de software que se precie debe ser modular (módulos o partes internas
bien diferenciadas) y cohesivo (realizan una tarea concreta lo mejor posible). Los com-
ponentes de software en la JEE se definen por medio de la API de EJB.

5.2 Arquitectura de software y servidor de


aplicaciones
Enterprise JavaBeans es una API de JEE de acceso a la lógica de negocio de las
aplicaciones. Dicha API en su versión 3.0 está especificada en la JSR 220. El objetivo
de la API es ofrecer una colección de objetos del lado del servidor. Estos objetos son los
denominados EJB. Lo que se pretende con los EJB es que los programadores puedan
abstraerse de problemas genéricos de una aplicación empresarial (tales como concurren-
cia, persistencia, seguridad ... ). Todo ello basado en una arquitectura de componentes
(cada EJB es un componente). La API de EJB hace uso de un conjunto de tecnologías:

• RMI (Remote Method Invocation o Invocación Remota de Métodos) .

• JNDI (Java Naming and Directory Interface o Interfaz de Directorio y Nombrado de


Java}. Esta tecnología la usaremos para localizar los EJB, las conexiones y otros
componentes dentro de la estructura de directorios del servidor de aplicaciones.

• CORBA (Common Object Request Broker Architecture). Para la comunicación


remota. Es una tecnología que permite que diversos componentes escritos en
diferentes lenguajes de programación y desplegados en distintas máquinas pue-
dan acoplarse para desarrollar una aplicación empresarial.
• JMS (Java Message Service o Servicio de Mensajería de Java). Permite la co-
municación asíncrona entre componentes, mediante un sistema de eventos. Los
publicadores de eventos los envían a una cola punto a punto o !o suscriben a
un tema.

© Alfaomega - Altaria 991


C urso a vanzado de Java

No va a ser objetivo de nuestro manual estudiar CORBA, pero estudiaremos de forma


aplicada las otras tecnologías.

En el capítulo anterior hemos instalado un servidor de aplicaciones . Pues ahora co-


menzaremos a hacer uso de el. El servidor de aplicaciones está basado en la lógica de
contenedores, en la que el contenedor se encarga de gestionar los problemas genéricos
de una aplicación empresarial (tales como concurrencia o seguridad) . Recordemos el
diagrama del servidor de aplicaciones que hemos instalado:

Maquina rea l
localhost
AdminServer

dominio: lea m

Dentro de nuestro dominio, en el servidor que hayamos configurado o haya configu-


rado el instalador (en este caso "AdminServer"), habrá dos contenedores: el contenedor
de EJB y el contenedor de servlets. El contenedor de EJB se va a encargar de gestionar
todos los componentes EJB que residan en el servidor, mientras que el contenedor de
servlets se encargará de escuchar las peticiones web que se reciban mediante HTTP
(por método GET o POST) para gestionar la respuesta del sistema a las peticiones de
los usuarios e implementar el controlador de la aplicación, despachando las páginas
JSP que se precisen en cada momento. Actualmente están de moda los términos front-
end y back-end. El front-end hace referencia a la presentación de la aplicación y lo
podríamos entender como la unión de la capa de servicio con la interfaz de usuario
gracias al controlador. El back-end hace referencia a las capas de software existentes
desde la base de datos hasta que ofrecemos una API coherente como capa de servicio
al front-end de nuestras aplicaciones.

Como hemos dicho, en el dominio tendremos dos contenedores: uno de EJB (back-end)
y otro de servlets (front-end). Pero no olvidemos nunca que el código que escribamos
en un EJB, en un servlet o en una página JSP es código de servidor, distinto al código
de cliente que enviaremos a los dispositivos de los usuarios en el lenguaje JavaScript.

1100 © Alfaomega - Altaria


JEE. Manual práctic o

Contenedor de EJB

Contenedor de Servlets

oom1n10 del seMdor de ap11cac1ones

Una vez visto el esquema básico de la arquitectura de software de una aplicación,


veamos cómo se desplegarían en nuestro servidor de aplicaciones.

Servicio Acceso a Datos (OAOJ

,¡,
Modelo Base de datos
SQLo NoSOL
Contenedor seNiets 1
Contenedor EJB
Controlador
r ···-··-···········--)> Mantiene a niv~l de sesión
Vista o de aplicación los objetos
i'
. del dominio actualizados .

Los objetos del modelo no son en sí EJB. Son objetos serializables (implementan la
interfaz serializable) que permiten el paso de parámetros entre el controlador y la capa
de servicio. A su vez los podemos usar como JavaBeans para ser gestionados por la
capa de presentación (la vista).

Acudamos de nuevo a Wikipedia para leer la definición de serialización: "la seriali-


zación (o marshalling en inglés) consiste en un proceso de codificación de un objeto en
un medio de almacenamiento (como puede ser un archivo o un buffer de memoria)" .
Afortunadamente, Java nos aísla de este problema de bajo nivel haciendo que nuestros
objetos que puedan ser enviados o recibidos a través de la red o a otras máquinas
virtuales implementen la interfaz serializable.

© Alfaomega - Altaria 1011


Curso avanzado de Java

5.3 Capa de presentación


La capa de presentación también puede ser una abstracción de la arquitectura de la
aplicación. Podemos entender por capa de presentación únicamente a los artefactos de
código fuente que verán los usuarios (las vistas en sí, por ejemplo, las páginas JSP).
Pero también podemos llamar capa de presentación a l conjunto de módulos que forman
las vistas, el controlador y la interfaz proporcionada por la capa de servicio (front-end
completo). Si lo entendemos de esta segunda forma (y debemos hacerlo así), aparecen
dos tecnologías que tendrán su propio contenedor (el de serulets). Dichas tecnologías
son los sewlets y las páginas JSP.

5.3.1 Servlets
Los sewlets son la tecnología que implementa el controlador de la aplicación res-
pondiendo a peticiones HTTP. Como hemos visto anteriormente, un serulet residirá en
el contenedor de serulets del servidor de aplicaciones. Como ya hemos visto suficiente
teoría en lo que va de capítulo, vamos a comenzar con la práctica. Empecemos a definir
una aplicación empresarial que contenga un sewlet. Vayamos a NetBeans y creemos
un nuevo proyecto de tipo "Aplicación Empresarial".

L C~Pro.kd:
2. ... ~RW' L---------------------------------~

Creato o ncw cnb:rpriK appk.ñjon a (1 st:ard:rd projed. Ywcon also<RC~t:: "" EJ6 module:
proj!ct cnd Vleb app¡ca tion proje« .n the entErp;se ,Wication. A s.tr dad ptoject uses en
IDE-oc:ncrotcd A11t bi.A"\1 )Q"i;pl tob!..'\1 o-d run 'IQUr proj:d::s.

< Ab'át Termnar ~ 11 Ayuda

Seguidamente creamos un nuevo proyecto a desplegar en el servidor.

1102 © Alfaomega - Altaria


JEE. Manual práctico

Name and LocaboA

L Cl'<x>ooP<oj<d Pro.)!Ct Name: ,.,Fr~J!PI


a. Ncnte oud Loc:ation
3. Sl!r'ver Af'l!l Stttr9t ProjoctLoco5on' lc"'
r: ,\.b:>o
.,---,
•U
:-....,;
---:::c
'llo-CLrn
- c-nt:s""V,-
<c"'
lll-co-n"""i«t:s
-:-.:-,-------111 El-owsc ..

O lbe Ded~tl:d Fddcr fa Stuf":g Librllric:s


llr01 ie:s fo!dcr. L __ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ __ J a-o\\OS:.,.

(>'fferErltorersand oroJacts taoS'a'e thesneorn:ilatiOn lb-afies


{seetk'p for deta•l!:).

Concelor 11 A)V!b

Muy importante: elegimos el servidor en el que se desplegará y la versión actual de


J ava EE: la 7 . Podemos ver dos módulos: el de EJB y el web (cada módulo va a un
contenedor).

Pasos

1. Choose ProJect h:_..._ .. ;:·G······:····s··.··,··v·...··,·····························


2. Narre ard Locaticn Server: ~-R'l' n.~~·····································-···
3. Sewer and SeU.gs
Java EE Version: jJava EE 7 " 1

~ Create EJB M,.U.,

~ Crec.tr: Wcb Applitoion Moch.Jc: Ei_?rmcrc~

< Atr.!s s:guiente > Termar 11 caocelar 11 AI'J(l>

Finalmente, podremos observar en la sección de proyectos el proyecto creado (con


sus dos módulos).

© Alfaomega - Altaria 103 1


C urso a vanzado de Jav a

Configuration Ftlt~
Strvtr Rttourcec:
' ~ " A-mt--«jb
g..
(¡ Source Pad:aget
fb. {i li!:lrariet
ij).(i Enter¡rBe Beans
é·ti\ Configuration Rles
L. m Server R.ecources
11 B ·& fo1iPrineraApp-v.:ar
Wc.b Pagcs
Source Pad:agc.G
Libraritll
Conñgutoation Rl~

Para crear nuestro primer servlet hemos de ir a l proyecto WAR, crear un paquete y
dentro de él un servlet. Acto seguido nos preguntará si queremos añadirlo al servidor
de despliegue. La respuesta es afirmativa, ya que los servlets no los trabajaremos con
anotaciones como los EJB.

Ard'livo Editar Ver N.~tW!9""' FtJente Rtettrua urar Run De.bug Prcfile lum Hememientat Venun¡

· ti]¡ :bvo EE MociJco


(l\ Conf\g~.ra!:io,-. Fb

·CS ~vc; ~=~=~=«=·============~~=====;:;~-------------------------1


<
ID Carp«a ...

o.;Id 1!1 J~va Package...

ISP...
Oe.<~n Mld Build
Setv&el.•.
Ou n
Yeliy
~ JdYo Clas~...

GenE.1ate lavadoc B Java Interface.•.


~ Session Sean.-
Run lril Test for Existing Cl ;~u...
Dc¡¡loy ~ Se!>~ion Beans For Entity C l ass6. ~
Dc;>oror Cla)c c::ntidcd ...

··~
l. ChoOGe File Type
..~~--~-~-~,~~~~:.:;:::::::::::::::::::::::::::::::::::~
0 -M.S Nomc: 1PrimerSer...tet
1. Ka•te aOO LocaUOO ===-------------------------------------'
~-
3. CO'Ifgl.l"e Sc:r~Aet DcdO)ftmt
Projed: !MPrinele.App-wr 1
l«óltQ'l: [SoYrcc Pad:aoc:;
·1
Pact.9': n,>Act;;l

1104 © A lfaomega - Altaria


JEE. Manual práctic o

.....
l. Ch:lo~Nt TWt Rigi:W:I tht Sti•AU W:ft h ·~ICIIIilt'l by gMng dW Sic\'li:tM~I Nrnt (Servlit Nimt). 11len
2. N<nt MdLOGtton cpotcl(y pitlilnt é\atklin~ thi U:U.t: thil h\•o'» lhi Si:l:\'ii:t. SiP<nti múllplt p¡l~ w11h
'· eonn,ure k rvli:t
Ot-:ploytt~t
""···,g···¡;;;····¡¡;;;····= ···········¡¡;:····.·······(··;:¡;·=
~ f.......l!'! ....~ ....•.!P.~~~.t.. ...~.~. ~.....:~~!!.t

ctoos Na~ f!JerV'.et!.Ptlmerservt:t

Servet Na'l'le: [Prir:erSetvlet

URLP,:,ttan(s) : .. =============:
=
~!=#==· ScnA
~e=t=
Irili:Jiicotitn P~o-r-.cb:r:s;

,____..;.;,...... J
Sdt...

Vea mos ahora el código del servlet que hemos crea do:
package servlets;
import java.io.IOException;
import java.io.PrintWriter;
import javax.servlet.ServletException;
im port javax.servlet. http. HttpServlet;
im port javax.servlet. http. HttpServletReq uest;
im port javax.servlet. http. HttpServletResponse;
public class PrimerServlet extends HttpServlet {
protected void processRequest{HttpServletRequest request,
HttpServletResponse response)
throws ServletException, IOException {
response. setCo nte ntTyp e {"text/h tm 1; eh arset= UTF-8");
try (PrintWriter out = response.getWriter{)) {
out.println{"<! DOCTYPE htm 1>");
out.printl n{" <htm 1>" );
out.printl n{" <head>" );
out.println{"<title>Servlet PrimerServlet</title>");
out.printl n{" </h ead>");
out.printl n{"<body>" );
out.println{"<hl>Servlet PrimerServlet at" +
request.getContextPath{) + "</hl>");
out.printl n{"</body>" );
out.printl n{" </htm 1>" );
}
}

© Alfaomega - Altaria 1051


C urso a vanza do de Java

@Override
protected void doGet(HttpServletRequest request,
HttpServletResponse response)
throws ServletException, IOException {
processRequest(request, response);
}
@Override
protected void doPost(HttpServletRequest request,
HttpServletResponse response)
throws ServletException, IOException {
processRequest(request, response);
}
@Override
public String getServletlnfo() {
return "Short description";
}
}

Del código podemos hacer varias apreciaciones:


• La clase "PrimerServlet" hereda de la clase "HttpServlet".

• Todas las clases referenciadas se incluyen en el paquete "javax" (contiene las


API de la JEE) .

• Tenemos dos métodos: "doGet" y "doPost". Ambos pa ra atender las peticiones.


Unas mediante HTTP GET y otras mediante HTTP POST.

• Ambos métodos tratan dos parámetros: la petición del cliente y la respuesta que
se le envía al cliente.

• Ambos métodos llaman al método "processRequest", que es el método que pro-


cesa la petición HTTP.
• Ambos métodos lanzan las excepciones "ServletException" e "IOException".

• La salida al usuario se realiza mediante el objeto out que lo obtenemos con la


instrucción: "PrintWriter out = response.getWriter()".

Para echar a andar nuestra aplicación, hemos de compilarla y desplegarla en el


servidor. Para ello, la compilamos mediante la opción "Construir" del IDE (en nuestro
caso "Clean and Build"). Nota: hemos de hacerlo sobre el proyecto web.

1106 © Alfaomega - Altaria


JEE. Manual práctico

Archivo Editar Ver Navegar Fuente Reestructurar Roo

1Prestaciones ~ Prim
l!J·" MiPrimeraApp S...a!
$. .~ MiPrimeraAro-ei>
10
lB®
New •
Build
Clean and Build
Clean

Verify
Generate Javadoc

La salida del compilador es:


BUlLO SUCCESSFUL (total time: 5 seconds).

Para desplegarlo y ejecutarlo, seleccionamos la opción ejecutar ("Run") .

Archivo Editar Vtr Navegar fuente Reestructurar Run

IR··~ l~iPrineraApp Sou're


$... ~ l~iPrineraApp~
lO
dl@ I'11PrmeraAP0-'11ar


Build
Clean and Build
Clclln
Verify
Generate Javadoc

.Run

OqJuror

Profile

Y vemos la salida de la aplicación acudiendo a nuestro navegador y escribiendo la


siguiente URL: "http: 11 localhost:700 1 1MiPrimeraApp-war1PrimerServlet".

© Alfaomega - Altaria 1071


Curso avanzado de Java

Cuando tengamos nuestros componentes EJB creados, en vez de compilar y ejecutar


sobre el proyecto web, lo haremos sobre el proyecto completo (módulos EJB y WAR).

Por último, veamos el código del descriptor de despliegue del módulo web ("web.xml"
dentro de la carpeta "WEB-INF"):
<?xml version="l.O" encoding="UTF-8"?>
<web-app version="3 .1" xm lns="http://xm lns.jcp .org/xml/ns/javaee" xm lns:xsi="http ://www.
w3 .org/2001/XM LSch ema-i nst ance" xsi :schema location=" http ://xm lns .jcp.org/xm 1/ns/javaee
http://xm lns.jcp.org/xm I/ns/javaee/web-a pp_3 _1.xsd">
<servlet>
<servlet-name>PrimerServlet</servlet-name>
<se rvlet-el ass> se rvle ts.Prime rSe rvlet </se rvlet-class>
</servlet>
<servlet-mapping>
<servlet-name>PrimerServlet</servlet-name>
<u rl-patte rn >/Prime rSe rvlet</ u rl-parte rn >
</servlet-mapping>
<session-config>
<session-timeout>
30
</session-timeout>
</session-config>
</web-app>

Podemos personalizar la aplicación escribiendo en la salida (mediante el objeto out)


lo que queramos.
Imaginemos que tenemos un listado de tres personas. De cada persona únicamente
guardamos su nombre. Como es un diagrama de clases muy simple únicamente vamos
a mostrar el código. Todo el código lo escribiremos en el módulo WAR. Veamos cómo
queda la estructura del proyecto (mostrando la localización del descriptor de despliegue).

Projects X 1 Files 1 Prestaciones 1


@- ~ MiPnmeraApp
@. ~ MiPnmeraApp-ejb
é-@ MIPnmeraApp-war
$ Q1 WcbPogco
l ¡• .. ~ VIEB·INF
1 f· ~ web.xml
¡ j L. ~ weblogc.xnl
i L... I!J index.htmt
$ . (i Souro: Pc:doges
¡ $J.. !!l oomin'o
. ! ~···· ~ UstaPerson:.s.java
! 1 L.~ Persora.ja\'a
! éJ. I!l servlels
j L.. ~ Pl1me<sei'VIet.¡ava
m. (i TectPac:kag•c
ij¡. ~ libraries
$. (¡ TestLibraries
ffi . ~ Confguration f11es

1108 © Alfaomega - Altaria


JEE. Manual práctico

Hemos creado un paquete llamado dominio. Dicho paquete incluye la lista de per-
sonas.

• Clase "Persona .java ":


package dominio;
public class Persona {
private String nombre;

public String getNombre() {


return nombre;
}
public void setNombre(String nombre) {
this.nombre =nombre;
}
}

• Clase "ListaPers onas.java":


package dominio;
import java.utii.Arraylist;
import java.utii. List;
public class Lista Personas {
private List<Persona> personas;
public ListaPersonas() {
this.personas = new Arraylist<Persona>();
}
public List<Persona> getPersonas() {
return personas;
}
public void setPersonas(List<Persona> personas) {
this.personas = personas;
}
}

• Servlet:
package servlets;
import dominio.ListaPersonas;
import dominio.Persona;
import java.io.IOException;
import java.io.PrintWriter;
import java.utii.List;
import javax.servlet.ServletException;
im port javax.servlet. http. HttpServlet;
im port javax.servlet. http .HttpServletRequest;
im port javax.servlet. http. HttpServletResponse;

© Alfaomega - Altaria 1091


C urso a vanzado de Java

public class PrimerServlet extends HttpServlet {


private ListaPersonas listaPersonas;
protected void processRequest(HttpServletRequest request,
HttpServletResponse response)
throws ServletException, IOException {
th is.ca rga rPersonas();
response.setCo nte ntType ("text/ht m 1; eh arset= UTF-8");
try (PrintWriter out = response.getWriter()) {
out.println("<! DOCTYPE htm 1>");
out.println (11 <htm 1> 11 );
out. println (11 <head> 11 );
11
out.println ( <title>Servlet Pri m erSe rvlet</title>" );
11 11
out. printl n ( </head> );
out. println (11 <body> 11 );
out.println( 11 <hl>Servlet Prim erServlet at 11 +
request.getContextPath() + 11 </hl> 11 );
inti=l·
'
List<Persona> personas= this.listaPersonas.getPersonas();
for(Persona p : personas) {
11 11 11 11
out.println( <h2>Persona +i+ : "+p.getNom breO+ .</h2>" );
i++·
'
}
out.printl n (11 </body> 11 );
out. println (11 </htm 1> 11 );
}
}

@Override
protected void doGet(HttpServletRequest request,
HttpServletResponse response)
throws ServletException, IOException {
processRequest(request, response);
}

@Override
protected void doPost(HttpServletRequest request,
HttpServletResponse response)
throws ServletException, IOException {
processRequest(request, response);
}

@Override
public String getServletlnfo() {
11
return Short description";
}

1110 © A lfaomega - Altaria


JEE. Manual práctico

private void cargarPersonas() {


this.listaPersonas = new listaPersonas();
Persona p = new Persona();
p.setNom bre(" Ismael");
th is.lista Personas.getPerso nas(). add (p );
p = new Persona();
p .setNom bre(" Antonio");
th is.lista Personas.getPersonas().add (p );
p = new Persona();
p.setN o m bre("J avier");
th is.lista Personas.getPersonas() .a dd (p );
}
}

Y la salida en el navegador es la esperada:

Servlet PrimerServlet at /MiPrimeraApp-war


Persona 1: Ismael.
Persona 2: Antonio.

5 .3.2 Páginas JSP


Hemos visto que mediante la tecnología de servlets podemos crear contenido web
dinámico (haciendo uso de "PrintWriter out= response.getWriter())". Pero se nos antoja
un poco tedioso escribir de esta forma nuestras aplicaciones. Lanzando la salida en los
servlets, debemos escribir el código HTML en cadenas de texto. Para solucionar este
tedio se pensó en la tecnología de páginas JSP. Una página JSP es una página con
sintaxis HTML que puede incorporar código Java en forma de scriptlet para generar el
contenido dinámico. JSP es el acrónimo de Java Server Pages (Páginas del Servidor de
Java), siendo la última versión la 2. Una vez analizada la página JSP, su información
se añadirá al servlet.

Para poder incorporar el código Java dentro de la página JSP, se hace uso de la
siguiente sintaxis:

© Alfaomega - Altaria 1111


Curso avanzado de Java

• Expresión:"<%= .. . %>". Con dicho fragmento le decimos al intérprete de JSP que


debe escribir en la salida lo que indique la expresión. Indicar que la expresión
no debe acabar en punto y coma (;).
• Scriptlet: "<% ... %>". Dentro del scriptlet podemos incluir cualquier código Java.

• Declaraciones: "<%! ... %>". Sirve para declarar nuevas variables en la página JSP.

Pero para poder hacer uso de las variables de nuestra aplicación, necesitamos in-
cluir estas variables en forma de JavaBeans. No confundamos los JavaBeans con los
Enterprise JavaBeans. Los Enterprise JavaBeans son los componentes que residirán
en el contenedor de EJB en forma EJB, mientras que los JavaBeans son los objetos
del modelo que usarán las vistas para mostrar la información.
Llegados a este punto, es bueno indicar que vamos a tener dos tipos de JavaBeans:

• De sesión.
• De aplicación.

Los beans de sesión serán aquellos objetos que sólo tendrán vida a lo largo de la
sesión del usuario. Del mismo modo, tendremos una instancia por cada usuario que
esté logado en el sistema. Los beans de aplicación existirán durante todo el tiempo de
vida de la aplicación y tendremos una instancia para todos los usuarios del sistema. La
variable de sesión será de tipo "javax.servlet.http.HttpSession", mientras que la variable
de aplicación será de tipo "javax.servlet.ServletContext". Ambas variables funcionarán
a modo de mapa (HashMap), en el que setearemos los objetos de cada ámbito con el
identificador que queramos. Lo normal es ponerle a los objetos como identificador su
mismo nombre. Veamos nuestro primer servlet en el que se obtiene acceso a las varia-
bles de aplicación y de sesión:
package servlets;
import java.io.IOException;
import java.io.PrintWrite r;
i m port javax.servlet.ServletContext;
i m port javax.servlet.ServletException;
i m port javax.servlet.http. HttpServlet;
im port javax.servlet.http .HttpServletReq uest;
i m port javax.servlet. http. HttpServletResponse;
im port javax.servlet.http.HttpSession;
public class PrimerServlet extends HttpServlet {
protected void processRequest( HttpServletRequest request,
HttpServletResponse resp onse)
throws ServletException, IOException {
HttpSession session = request.getSession();
ServletContext context = getServletContext();
response .setCo nte ntType (" text/ht m 1; eh arset = UTF-8");
try (PrintWriter out= response.getWriter()) {

1112 © Alfaomega - Altaria


JEE. Manual práctic o

out.println("<! DOCTYPE htm 1>");


out.printl n(" <html>" );
out.printl n(" <h ead>");
out.println("<title>Servlet PrimerServlet</title>");
out.printl n("</h ead>");
out.printl n("<body>" );
out.println("<hl>Servlet SegundoServlet at "+ request.getContextPath() + "</hl>");
out.printl n(" </body> ");
out.printl n("</htm 1>" );
)
}

@Override
protected void doGet(HttpServletRequest request,
HttpServletResponse response)
throws ServletException, IOException {
processReq uest(request, response);
}

@Override
protected void doPost(HttpServletRequest request,
HttpServletResponse response)
throws ServletException, IOException {
processReq uest(request, response);
}

@Override
public String getServletlnfo() {
return "Información del servlet";
}
}

De este modo podremos setear en las variables session y context nuestras variables
de sesión y de aplicación respectivamente. Creemos un segundo servlet en el que se-
teemos nuestras variables con dos cadenas (una cadena a la variable de sesión y otra
cadena a la variable de aplicación):
package servlets;
import java.io.IOException;
import java.io.PrintWriter;
import javax.servlet.ServletContext;
import javax.servlet.ServletException;
im port javax.servlet. http. HttpServlet;
im port javax.servlet. http .HttpServletRequest;
im port javax.servlet. http. HttpServletResponse;
im port javax.servlet. http. HttpSession;

© Alfaomega - Altaria 1131


C urso a vanza do de Java

public class SegundoServlet extends HttpServlet {


private String variableSesion ="cadena 1";
private String variableAplicacion ="ca dena 2";
protected void processRequest(HttpServletRequest request,
HttpServletResponse response)
throws ServletException, IOException {
HttpSession session = request.getSession();
ServletContext context = getServletContext();
if (session.getAttribute("variableSesion") == null) {
session .setAttribute("va riableSesion ", varia bleSesion);
}
if (context.getAttribute("variableAplicacion") == null) {
eo ntext. setAtt ri bu te ("varia b leA pi ica cio n ", varia b 1eAp 1ica ci o n);
}
response .setCo nte ntType ("text/ht m 1; eh arset= UTF-8");
try (PrintWriter out= response.getWriter()) {
out.println("<! DOCTYPE htm 1>");
out. println (" <htm 1> ");
o ut .println (" <head> ");
o ut. printl n (" <title>Servlet Pri m erServlet </title>" );
out. println (" </head> ");
out. println (" <body> ");
out.println("<hl>Servlet SegundoServlet at" + request.getContextPath() + "</hl>");
out. println (" </body> ");
out.println (" </htm 1> ");
}
}

@Override
protected void doGet(HttpServletRequest request,
HttpServletResponse response)
throws ServletException, IOException {
processRequest(request, response);
}

@Override
protected void doPost(HttpServletRequest request,
HttpServletResponse response)
throws ServletException, IOException {
processRequest(request, response);
}

@Override
public String getServletlnfo() {
return "Información del servlet";
}
}

1114 © A lfaomega - Altaria


JEE. Manual práctico

De esta forma estamos asociando una cadena a la sesión y otra cadena a la aplicación.
Pero Jo normal será tener seteados objetos del modelo del dominio. Y estos objetos del
dominio son los conocidos JavaBeans. Dichos objetos son accesibles desde las páginas
JSP. Llamamos a un objeto JavaBean si cumple ciertas condiciones:

• Tienen un constructor sin argumentos.


• Implementan la interfaz "java.io.Serializable".

• Tienen los atributos privados.

• Accedemos a ellos a través de getters & setters. Para los booleanos los getters
comienzan con la palabra is.

Para poder hacer uso de ellos en las páginas JSP debemos hacer uso de la directiva
page y de la acción de "jsp useBean".

Las directivas de JSP son page, taglib e include. Con las directivas le indicamos al
motor de JSP información que afectará a la estructura del serolet generado (en tiempo
de compilación). La sintaxis de una directiva es "<%@directiva propiedad="valor">".

La directiva page le indica al motor JSP cierta información de la página que se está
tratando. Por ejemplo, le indicará el tipo de contenido de la página, si tendrá acceso a
la sesión o no, el listado de paquetes a importar .. .

La directiva taglib sirve para importar conjuntos de etiquetas que sean interpretables
por el motor de JSP. Una librería de etiquetas muy conocida es JSTL.
La directiva include le indica al motor de JSP que debe incluir en la página la infor-
mación del fichero indicado en la URL. Dicha inclusión se realiza en tiempo de compi-
lación y podemos incluir el contenido de otra página JSP.

Las acciones de JSP se lanzan en tiempo de ejecución y tienen diversos cometidos


como la transferencia de control, la inclusión de objetos o la inclusión de páginas (pero
en tiempo de ejecución). Comencemos mostrando el uso de la acción "useBean".
Después de haber realizado la lectura somera de la teoría sobre JSP, vamos a re-
escribir nuestro ejemplo del listado de personas usando ahora la tecnología de JSP.
Reescribimos Jos beans para que implementen la interfaz "Serializable", con construc-
tores sin argumentos y refactorizamos el servlet "PrimerServlet".
Los beans "Persona" y "ListaPersonas" quedan como siguen:

• Clase "Persona.java":
package dominio;
import java.io.Serializable;
public class Persona implements Serializable {
public Persona(){}
prívate String nombre;

© Alfaomega - Altaria 1151


Curso avanzado de Java

public String getNombre() {


return nombre;
}
public void setNombre(String nombre) {
this.nombre = nombre;
}
}

• Clase "ListaPersonas.java":

package dominio;
import java.io.Seri alizable;
import java.utii.Arraylist;
import java.utii.List;
public class ListaPersonas implements Seria liza ble {
prívate list<Persona> personas;
public listaPersonas() {
=
this.personas new Arraylist<Persona>();
}
public list<Persona> getPersonas() {
return personas;
}
public void setPersonas(list<Persona> personas) {
this.personas = personas;
}
}

Pensemos en un modelo en el cual accedemos a una página principal escrita en


JSP ("index.jsp"). Una vez que hayamos accedido a dicha página, mediante un botón
de formulario accederemos al servlet. Una vez en el servlet, se creará el listado de
personas y se seteará la variable "listaPersonas" como variable de aplicación. Poste-
riormente despacharemos en la respuesta la página "listadopersonas.jsp". El diagrama
de funcionamiento podria ser el siguiente:

JSP: lndex.jsp )

Usuario servlet Primerservlet

1116 © Alfaomega - Altaria


JEE.. Manual práctico

Veamos ahora cuál es la estructura del proyecto:

PrQjecls X Files Pre l!tacione s


$·"~ IMiPrimeraA¡lp
éJ ~ MiPrimeraApp-ejb
8@ M1PrimeraApp-war
El· [jl Web Pages
i $ (l} WEB-INF
; : (j i'ldelC.jsp

1 L....(jjl i stadopersonas .jsp


$··11 Source Packages.
i $·l.! domi'lio
i . ¡.... @ ListaPersonas.java
i L.. @ Persona .java
8 133 serv!ets
L.. ~ PrimerServlet.java
$· {i TestPa<kages
$· (i Ubraries
$ . (i Test Ubraries
ffi . ()!¡ Configtration Files

• Clase "PrimerServlet.java":
package servlets;
import dominio.ListaPersonas;
import dominio.Persona;
import java.io.IOException;
import javax.servlet.ServletContext;
import javax.servlet.ServletException;
im port javax.servlet. http. HttpServlet;
im port javax.servlet. http. HttpServletReq uest;
im port javax.servlet. http. HttpServletResponse;
im port javax.servlet. http. HttpSession;
public class PrimerServlet extends HttpServlet {
prívate ListaPersonas listaPersonas;
protected void processRequest(HttpServletRequest request,
HttpServletResponse response)
throws ServletException, IOException {
11 Sesión. No la usamos en este caso. Para los beans de sesión.
=
HttpSession session request.getSession();
11 Aplicación. Para los beans de aplicación .
ServletContext context =getServletContext();
th is.ca rga rPersonas();
if (context.getAttribute(" listaPersonas") null) { ==
context.setAttri bute(" 1ista Personas", th is.l ista Personas);
}
response .send Red irect(" 1ista dope rso nas.jsp" );
}

© Alfaomega - Altaria 1171


Curso avanzado de Java

@Override
protected void doGet(HttpServletRequest request,
HttpServletResponse response)
throws ServletException, IOException {
processRequest(request, response);
}

@Override
protected void doPost(HttpServletRequest request,
HttpServletResponse response)
throws ServletException, IOException {
processRequest(request, response);
}

@Override
public String getServletlnfo() {
return "Información del servlet";
}

prívate void cargarPersonas() {


this.listaPersonas = new ListaPersonas();
Persona p = new Persona();
p .setNombre ("Ismael");
th is.l ista Personas.getPersonas() .add( p );
p = new Persona();
p.setN ombre(" Antonio");
th is.l ista Personas.getPersonas() .add( p);
p = new Persona();
p .setNombre ("Javier");
th is.l ista Personas.getPersonas() .add( p );
}
}

• Página JSP "index.jsp":


<%@page contentType="text/html" pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
<head>
<meta http-eq uiv="Content-Type" content=" text/htm 1; cha rset=UTF -8">
<title>Página JSP</title>
</head>
<body>
<p>Sigue el enlace para ver el listado de personas ... </p>
<form action="PrimerServlet">

1118 © A lfaomega - Altaria


JEE. Manual práctico

<input type="submit" name="Enviar" />


</form>
</body>
</htm l>

• Página JSP "listadopersonas.jsp":


<%@page contentType="text/html" pageEncoding="UTF-8"
import="java.util. *,dominio.*" %>
<jsp:useBean id=" lista Personas" class="dominio.ListaPersonas" scope="a pplication" />
<!DOCTYPE htm l>
<htm l>
<head>
<meta http-equiv="Content-Type" content="text/htm 1; charset=UTF-8 ">
<title>Listado de personas</title>
</head>
<body>
<hl>Listado de personas</hl>
<%
List<Persona> personas = listaPersonas.getPersonas();
for(Persona p :personas) {
%>
<h2>
<%=p.getNombre()%>
</h2>
<%
}
%>
</body>
</htm l>

Podemos ver que en la propiedad action del formulario podemos indicar únicamente
el nombre del servlet. También podemos ver como hemos usado el bean de aplicación
mediante la acción "<jsp:useBean ... />" . Mediante la directiva page hemos indicado
el tipo de fichero de la salida, su codificación de caracteres y los paquetes que debe
importar (para poder hacer uso de las clases "List" y "Persona"). Veamos la salida en
el navegador:

rJ Página JSP X

~ ~ C [J localhost:7001/MiPrimeraApp-war/

Sigue el enlace para ver el listado de personas...

1 E rrtvi a:r 1

© Alfaomega - Altaria 1191


Curso avanzado de Java

~ l 4tt4ode pt~l X

~ -+ e IDloc.al00sl7001/MiPrimeraApp-war/li~1adopecsonas.jsp 'Crl 6 =
Listado d e pe1·sonas
t~ruael

Antonio
J avier

5.4 Enterprise JavaBeans


Nos centraremos a h ora en la tecnología de EJB para ver cómo podemos implementar
el back-end de nuestras aplicaciones de forma profesional (mediante componentes). Es-
tudiaremos los beneficios de usar EJB así como la implementación de cad a tipo de ellos.
Como hemos explicado anteriormente, los componentes que van a residir en nuestro
servidor de aplicaciones van a tener la forma de EJB. Estos componentes deben ofrecer
en forma de contrato una interfaz para aquellas otras aplicaciones o componentes que
quieran usarlos. En este punto debemos nombrar que existen dos tipos de interfaces
(si hablamos de la J EE):

• Interfaz local.
• Interfaz remota.

La interfaz local es aquella que usarán los componentes que están situados en el mis-
mo servidor de aplicaciones. La interfaz remota es aquella que usarán los componentes
que estén en servidores de aplicaciones diferentes, o aquellos fragmentos de código que,
aun ejecutándose en la misma máquina que el servidor, no estén desplegados en éste.
Los componentes susceptibles de ser enviados a otros servidores deben ser serializables.

Los EJB son componentes de software. Son accesibles desde el propio servidor de
aplicaciones o desde otros servidores (haciendo uso de RMI). Nosotros los usaremos
haciendo uso de la interfaz local o mediante la interfaz remota.
Desde la implementación de EJB 3 .0, siendo la actual la implementación 3.2, es muy
simple desplegar los beans en los contenedores, ya que no hace falta crear descriptores
de despliegue en XML, sino que lo realizamos por medio de anotaciones en el código
de nuestras clases. Por ejemplo, la interfaz local de un bean de sesión:

1120 © Alfaomega - Altaria


JEE. Manual práctico

package sessionbeans;
import javax.ejb.Local;
@Local
public interface ISessionBeanlocal {
11 M étodos de la interfaz local.
}

Del mismo modo, una interfaz remota sería la siguiente:

package sessionbeans;

import javax.ejb.Remote;

@Re mot e
public interface ISessionBeanRemote {
11 M étodos de la interfaz remota.
}

Los componentes de software son cada una de las piezas del puzle en la arquitectu-
ra orientada a servicios. Esta arquitectura no es más que un paradigma que permite
el desarrollo de sistemas altamente escalables y distribuidos en los que se facilita la
comunicación, el mantenimiento y la integración de aplicaciones menores.
Tenemos varios tipos de EJB:

• De entidad.
Persistencia gestionada por el contenedor.
Persistencia gestionada por el bean.

• De sesión.

Sin estado: stateful.


Con estado: stateless.
Instancia única: singleton.

• Orientados a mens ajes . Hacen uso de la JMS.

De cola: queue.
Tema: topic.

© Alfaomega - Altaria 1211


Curso avanzado de Java

5.4.1 Beans de entidad. Acceso usando JPA


Los beans de entidad (entity) son los relacionados con la persistencia de objetos en
nuestra base de datos. Usando la Java Persistence API (API de Persistencia de Java) en
su versión 2.1, las entidades serán los equivalentes directos a las tablas de nuestra base
de datos. En el capítulo de instalación de Oracle WebLogic Server vimos cómo conectar
una base de datos con el servidor. Nuestra base de datos de ejemplo se llamaba leamdb.

La Java Persistence APl es un estándar que debe ser implementado (definido mediante
JSR). Existen varias implementaciones de la JPA, entre ellas (recogido de Wikipedia):

• Hibernate.

• ObjectDB .
• TopLink .
• Coco Base .
• EclipseLink .

• OpenJPA .
• Kodo .
• DataNucleus.

• Amber.
En nuestro aprendizaje usaremos la versión de EclipseLink porque es la más fácil
de usar en nuestro entorno servidor (el servidor de aplicaciones ya la trae instalada) .
Usando JPA nos olvidamos de la estructura relacional de la base de datos. Cada bean
de entidad irá precedido de la anotación "@Entity". Sólo nos tendremos que preocupar
de definir el modelo de la base de datos a modo de diagrama de clases. Esto se con-
sigue gracias a l ORM (patrón de mapeo objeto-relacional) que convierte los atrib utos
entidades en claves ajenas.
Para modelar las relaciones en JPA, usamos las siguientes anotaciones en los atri-
butos:

• @OneToOne: uno a uno.

• @OneToMany: uno a muchos.

• @ManyToOne: muchos a uno.


• @ManyToMany: muchos a muchos (intentaremos evitarlas).

Comencemos con la práctica. Como ejemplos y ejercicios a lo largo del libro se va


a proponer crear una aplicación web completa usando JEE. Toda aplicación, a l igual
que un edificio, comienza desde los cimientos. Los cimientos en software son los da-
tos . Propongamos realizar, a lo largo del texto, una aplicación consistente en un blog

1122 © Alfaomega - Altaria


JEE. Manual práctico

que permita insertar comentarios a los posts de los distintos usuarios registrados. Del
mismo modo, se podrá chatear particularmente con cada uno de los usuarios.

El modelo de datos que se propone para este ejercicio es el siguiente (definido como
diagrama de clases):

Chat

· fechalnslante: Calen dar


l ChatActuat

· contenido: Stríng
1 ·usuario: Usuario
· enviadoPor: usuario
• enviadoA: Usuario • chateaCon: Usuarl o

• getters & s etters


• getters & setters 1

Usuario
1
· e mail: String
1 • elave: String 1
• n ombreusuario: Slring
• nombre: String Post
• apellidos: String escrito por •
1

• getters & setters · fechatnstante: Calenaar


· contenido: String
1 1_.., · autor. Usuario

• • getters & setters

ComentaPost
(Relílcíón)

· fechaln stante: Calendar •


· comentario: String
. usuario: Usuario
. post: Post

~ge tte rs & setters

En dicho ejercicio tenemos cinco entidades:


• "Usuario" .
• "Post" .

• "ComentaPost".
• "Chat" (un mensaje de chat).

• "ChatActual". Para ver quién está hablando particularmente con quién en cada
momento.

Las relaciones "muchos a muchos" las hemos modela do como una nueva entidad
("Chat" y "ComentaPost"). Como regla general evitaremos las relaciones "muchos a
muchos" en el diseño, ya que pueden acarrear problemas si no tenemos mucha pericia
en el uso de estas relaciones .

© Alfaomega - Altaria 1231


Curso avanzado de Java

La relación "ChatActual" es una relación "uno a muchos" con atributos (con el atributo
"Usuario"). Por ello mismo es necesario definirla como una nueva entidad.

Comencemos con la práctica. Creemos un nuevo proyecto de aplicación empresarial


en el que tengamos los módulos que se desplegarán: EJB y WAR. El proyecto se lla-
mará Blog. El módulo en el que escribiremos nuestros beans de entidad (y todos los
demás EJB) será el módulo EJB. Para poder persistir los objetos, debemos crear una
nueva unidad de persistencia en nuestro proyecto. Haciendo clic con el botón derecho
sobre el módulo EJB, podremos crear los distintos elementos que iremos estudiando.

Creemos una base de datos denominada Blog en nuestro gestor MySQL del mismo
modo que creamos la base de datos "learn" en el capítulo de instalación del servidor.
El nombre que le asignaremos en el servidor de aplicaciones será "JDBCMySQLBlog".
El nombre JNDI será "blogdb".

Creando la nueva unidad de persistencia. Podemos observar que el proveedor de la


unidad de persistencia es EclipseLink y que la fuente de datos es "blogdb" (nombre en
el árbol JNDI que le h emos asign ado) .

Pasos Proveedor y base de datos

1.
2.
Choose Rle Type
Proveedor y base de Nombre de IJridad de persistenda: ,_IBI...::og:__..:.~
:...-------------------J
datos Especificar el proveedor de persistend• y la base de datos para las dases entity.
Proveedonle ,....l:letenda: ledipscl.a (JPA 2.1)(po-cdctcnninodo)
Fuente Datos:

~ Utire APls de Java Transaction


Estrategia de generadón de tablas: @Crear O 8iminar y crear O Ningt.no

<Atrás 1[ SiglJie nte > JI Terminar 11 Cancelar 11 Ayuda

Acto seguido, vemos dónde queda ubicada la unidad de persistencia ("persistence.


xml").

1124 © Alfaomega - Altaria


JEE. Manual práctico

Prestaciones

Java EE Modules
"'""'"' Conligu-ation Files

Lilraries
..,.. ,,. Enterprise Beans
o·· t"' Conligu-ation Files
i·····l•~l MANJFEST.MF
persistence.xml
weblogic-ejb·jar .xml
Serv<r Resour~s
Blog-war

Código XML de la unidad de persistencia:


<?xm l version="l.O" eneoding="UTF-8"?>
<persistenee version="2.1" xm lns=" http://xm lns.jep.org/xml/ns/persistenee"
xm lns :xsi= "http://www.w3 .org/2001/XM LSehem a-insta nee"
xsi :sehem aLoeation=" http ://xm lns.jep.org/xm 1/ns/persistenee
http://xm 1ns.jcp .org/xm 1/ns/persistence/persist ence_ 2_ 1.xsd">
<persiste nee-u n it na me=" Blog-ej b PU" tra nsaction-type=" JTA ">
<jta-data-souree>b logdb</jta-data-souree>
<exelude-un 1isted -e1asses>fa lse</exe1u de-un 1isted -e1a sses>
<properties>
<property na m e= "javax. pe rsistenee .sehem a-gene ration.data base .aetion"
va 1u e=" create-o r -exte n d-t ab les"/>
</properties>
</persiste nee-u n it>
</persist enee>

Observamos con detenimiento que h emos modificado la estrategia de generación de


tablas: "create-or-extend-tables".

Acto seguido, comenzamos a crear nuestras entidades. Debemos indicar que por
defecto, según el procedimiento seguido, la persistencia será gestionada por el con-
tenedor. Podríamos indicar que la persistencia la gestionaríamos nosotros haciendo
uso de JDBC. Para el lector que quiera profundizar en el tema, puede investigar sobre
BMP (Bean-Managed Persistence). Se considera que el manual pretende dar el cono-
cimiento genérico sobre la JEE para desarrollar una aplicación completa de la forma
más fácil y rápida.
Para nuestras entidades, crearemos un paquete llamado data.

© Alfaomega - Altaria 1251


Curso avanzado de Java

B··A Blog
1 $· ij\ Java EE Modules
. $·· ~ Configuration Fies
L... {j8 Server Resources
$··~ Blog-ejb
! S· [:ilJ Source Pad(ages
i i 8 El9 data

1 1
! $· {11¡
L .,
.¡ ij).....
Test Packages
FU Libraries
é¡..(i Test Librarles
~·· {¡ Enterprise Beans
®-·lA\ Configuration ñles
. L. m Server Resources
®-··® Blog-war

Escribamos el código de las entidades "Post" y "Usuario".

• Entidad "Usuario.java":

package dat a;
import java.io.Serializable;
import javax.persistence.Entity;
im port javax.persistence.GeneratedValue;
i m port javax. persisten ce .GenerationType;
import javax.persistence.ld;

@Entity
public class Usuario implements Seri aliza ble {
private static fina l long seriaiVersionUID = ll;
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
private String email;
private String clave;
private String nombreUsuario;
private String nombre;
private String apellidos;
public Long getld() {
re t urn id;
}
public void setld(Long id) {
this.id = id;
}
public String getEmail() {
ret urn email;
}

1126 © A lfaomega - Altaria


JEE. Manual práctic o

public void setEma ii(String email) {


this.email = email;
}
pu blic String getCiave() {
return clave;
}
public void setCiave(String clave) {
this.clave =clave;
}
public String getNombreUsuario() {
return nombreUsuario;
}
public void setNombreUsuario(String nombreUsua rio) {
this.nombreUsuario = nombreUsuario;
}
public String getNombre() {
return nombre;
}
public void setNombre(String nombre) {
this.nombre =nombre;
}
pu blic String getApellidos() {
return apellidos;
}
public void setApellidos(String apellidos) {
this.apellidos = apellidos;
}
@Override
pu blic int hashCode() {
int hash =O;
hash +=(id != null ? id.hashCode() :O);
return hash;
}
@Override
public boolean equals(Object object) {
if (!(object instanceof Usuario)) {
return false;
}
Usua rio other = (Usuario) object;
if ((this.id == null && other.id != null) 11 (this. id != null && !this.id.equals(other.id))) {
return false;
}
return true;
}
@Override
pu blic String toString() {

© Alfaomega - Altaria 1271


Curso avanzado de Java

ret urn "data. Usuario [ id="+ id+" )";


}
}

• Entidad "Post.java" :

package dat a;
impo rt java.io.Serializable;
import java.utii.Calendar;
import javax.persistence.Entity;
i m port javax. persisten ce .GeneratedValu e;
im po rt javax.persistence .GenerationType;
import javax.persistence.ld;
import javax.persistence.Ma nyToOne;
@Entit y
public class Post implements Serializable {
private static fina l long seriaiVersionUID = ll;
@Id
@GeneratedValue(strat egy = GenerationType.AUTO)
private Long id;
private Calendar fecha Instante;
private String contenido;
@ManyToOne
private Usuario autor;
public Long getld() {
re t urn id;
}
public void setld(Long id) {
this.id = id;
}
public Calendar getFechalnstante() {
re t urn fecha 1nst ant e;
}
public void setFech alnstante(Calendar fec ha Inst ante) {
this.fechalnstan t e = fechalnstante;
}
public String getContenido() {
re t urn contenido;
}
public void setContenido(String contenido) {
this.contenido =contenido;
}

public Usuario getAutor() {


re t urn autor;
}

1128 © A lfaomega - Altaria


JEE. Manual práctico

public void setAutor(Usuario autor) {


this.autor = autor;
}
@Override
public int hashCode() {
int hash = O;
hash +=(id != null ? id.hashCode() :O);
return hash;
}
@Override
public boolean equa ls(Object object) {
if ( !(object instanceof Post)) {
return false;
}
Post other = (Post) object;
if ((this.id == null && other.id != null) 11 (this. id != null && !this.id.equals(other.id))) {
return false;
}
return true;
}
@Override
pu blic String toString() {
return "data.Post[ id="+ id+" ] ";
}
}

Comentarios:

• Las entidades implementan la interfaz serializable.


• Identificamos las entidades con la anotación "Entity". En EJB 3 .0 , desaparecen
los descriptores de despliegue para los EJB.
• El campo clave será el campo "id", de tipo "Long". La estrategia de generación
de claves será automática.
• El atributo "Usuario" en la entidad "Post" se define mediante la relación "muchos
a uno".
Al ser la persistencia gestionada por el contenedor, podemos indicar que todas las
operaciones sobre la base de datos que afecten a una sola tabla tendrán la conside-
ración de transacción. Imaginemos que queremos insertar cien usuarios mediante un
bucle. Al afectar sólo a la tabla de usuarios, el contenedor de EJB hará rollback si falla
alguna inserción (o inserta todos los usuarios o no inserta ninguno).

© Alfaomega - Altaria 1291


C urso a vanzado de Java

5.4.2 Ejercicio 2
Dado el esquema de la base de datos del ejemplo del Blog,

Chal ChatActual

- fecha nstante: Calendar 1 - usuario: Usuario
- contellic'o:Shing - chateaCon: Usuario
- emiadoPo1: V:sua1io
- emlaUoA: usuario •
+ oetters & sefters

• getters & setters 1


Usuario 1

1 1
- nombreUsuario: Stnng
11
• gatters & setters
Po5t
1 escr.to por •
- recnarnstame: carenaar
contenido: Sting
1 autor: Usuario
~

• • oetters & seuers

ComentaPost
(Relotión)

fechatnstante: Calendar
oomentario: Strino
usu-ario: Usu::.rio
-
post: Post

•geners & seners

y habiend o implementado durante el tema la entidad de "Post", se plantea al lector


refactorizar la entidad de "Usuario", para que sólo guardemos de él el nick ("nombreU-
suario") , y, ade m ás, terminar de implementar las entidades, en concreto :

• "ComentaPost".

• "Ch at".

• "ChatActual".

5.4.3 Beans de sesión


Tenemos tres tipos d e beans d e sesión:

• Sin estado: s tateless.

• Con estado: stateful.

• Instancia ú nica: singleton.

1130 © Al fa o mega - Altaria


JEE. Manual práctic o

Los beans de sesión con estado son aquellos componentes software del lad o d el
servidor que son capaces de mantener un estado conversacional con el usuario d e la
aplicación (es capaz de mantener el estado) . Por el contrario, los beans de sesión sin
estado están disponibles para cualquier usuario, ya que no guarda n el estado con-
versacional con el cliente. El singleton es un tipo de EJB que es único para todas las
peticiones sobre él (es compartido por toda la aplicación) . El ej emplo más común d e un
bean de s esión con estado es el del carrito d e la compra de un comercio electrónico .
Cada usuario n ecesita almacenar en sesión su carrito. Cada objeto EJB debe ser único
para cada usuario. Por el contrario, los beans de sesión sin estado son comunes . El
ejemplo má s típico es el b ean loca liza dor d e s ervicios (acceso coherente a l back-end).
La utilidad del singleton es que nos s irve para compartir información entre toda la
aplicación. ¿A qué nos recuerda esto? A los beans de aplicación .

5.4.3.1 Sin estado: stateless


Pa ra poder realiza r las operaciones d el CRUD (Crea te-Read-Update-D elete) sobre las
tablas de nuestra base de datos, necesitamos definir nuevos stateless session beans
para las clases "Entity ". Haciedo clic con el botón d erecho sobre el módulo EJB en-
contraremos esta opción . A estos beans d e s esión d e bajo n ivel d e a cceso a d atos les
antepond remos la palabra "Data_". Estos beans estarán disponibles únicamente d es de el
servidor de aplicaciones, por lo tanto, sólo de ben implementar la interfaz local. Veamos
cómo quedaría n los beans d e s esión sin esta do para la s operaciones d el CRUD d e las
entidades "Usuario" y "Post". La estructura del proyecto es la s iguiente:

Pr oj ecte X 1Files 1 Pre staciones 1

~·~ · ~ ~ P~g<:~
~·· cr:=ce
¡ ¡ .., _dota
1 1 ···· ~ Clut.iava
1 1 ··· ~ Onv.ct..ol.jo•- o
i ¡ ··· ~ CaneuaPost.java
i i ~ Post.;ava
!' ¡i
¡
···· ~ tmario.ja va
i ¡ d3tasesslJObeans
' ¡ ¡gj Data_Absttadfaca:le.java
1
¡ ¡i
¡ ;
'· ··I!!Jª'
.... Oat:a_ct.a b\dudFacadc.ja"a
oa~a_ChatAruaFocadeLocal.;¡>va
i l ~ oata_Chatfacade.java
1 1 ,.•• ~ ~-ChatfacadtLoal.java
! 1 .... @ oata_ccmentaPoslfacade.Java
¡ ¡ ··· ~ Data_ComentaPoslfacadeLocal.java
~ Oata_Pa:tfacade.java
¡i ¡i
, ¡ B) oata_Postfacadel occl.java
¡ ··· ~ Data_U&Ja-iofacade.j>va
l ! g, Oata_lkl.;rioFacadelocal.java
1 $ ()!¡ Tema<~<oges
¡ $· Qil Ft.entesg...,..adas (q¡-source<JUtpuij
l $· Cj l,ibrMOS

© Alfaomega - Altaria 1311


C urso a vanzado de Jav a

Observamos que hemos creado una clase "Data_AbstractFacade" genérica que im-
plementa la funcionalidad. Del mismo modo, hemos creado un par de artefactos por
cada entidad: la interfaz que funciona a modo de contrato y la implementación de
dicha interfaz. Para no repetir código, la funcionalidad se obtiene desde la clase "Abs-
tractFacade", usándose el tipo de dato genérico "<T>" que permite el lenguaje Java. A
continuación mostramos el diagrama de clases para las entidades "Usuario" y "Post".

Oata_U,uarioFacadoLocat

Data_Abstrac1facadc<T> ,. e~eate {e n1jy•Usuario): 'lO Id:


,. edil(entit( U suario)· void:
-----~ " remove(.entitr.Usuario):
· 'fOid;
• entitvCiass: T:
+ ~nd(t d : Ob~c tt Usuario:
• ñndl\110: List~u su-; ri o:.;
• absiract oet6ntiWanaoe:O:En:itrManaaer: + nndRang&{l angq: lm!J): ust<Usuano>;
<» UGa1G(Gn1it',•: T}: \'OCO;
... odlt{ontfly: 1): V'Oid;
.. removQ(9ntlt(: T}: YOl4 ;
r- • coun1Q: irJ,

,. rind(iá: OIJJEW) T;
"' findAI!O LiS\<T:10:
.. tíndRanoe(ranoe: int!I'IJst.. T~o:
.. eountO: int Interfaz. como eontJalo

lmplerTtEnla runcionali!JadJPQL
Uata_Pos:t:FacaCICLOcal

~_Usuar~Facado ... create(entity: Pos1)· li'Oid;


... edit(entity; Post): \'Oid;
· enütyManaaer: EnlityMenaaer: ; ·t: .. remove{entitv: Post): void;
'- ----- ' .. find{td: ObJetl): Post;
• nndAilQ: LlstcPost>;
" protettt<l gi!EOUI'fManagQrQ. E nU!yfll ~gar, • AnCJRange(range: ln!3). Us.tcP0$1>;
• O ab_Usuari oF~c a d &O . •• "couniQ: in~
••
••
••
!Interfaz como con!rsto
••

mplementa funcionalidad JPOL oara_PostFacade

-
protttltU giHEnUf,Van41gtrQ: Entllytllanagar:
.. D:t! a_Po~:tFaudeO:

La implementación de los métodos comunes se realiza con el lenguaje JPQL. Éste es


un lenguaje de consulta orientado a objetos definido como parte de la JPA.
Veamos ahora el código de los "Session Beans" propuestos:

• Interfaz local a modo de contrato del bean "Data_ UsuarioFacade" . Interfaz


"Data_UsuarioFacadeLocal":
package datasessionbeans;
import data.Usuario;
import java.utii.List;
import javax.ejb.Local;
@Local
public interface Data _UsuarioFacade l ocal {
void create(Usuario usuario);
void edit(Usuario usuario);

1132 © Alfaomega - Altaria


JEE. Manual práctico

void remove(Usuario usuario);


Usuario find(Object id);
List<Usuario> findAII();
List<Usuario> findRange(int[] range);
int count();
}

• Bean de sesión sin estado "Data_UsuarioFacade.java ":


package datasession be ans;
import data.Usuario;
import javax.ejb.Stateless;
import javax.persistence. EntityManager;
im port javax. persistence.PersistenceContext;
@Stateless
public class Data_UsuarioFacade extends Data_AbstractFacade<Usuario> implements Data
UsuarioFacadelocal {
=
@ PersistenceContext(unitName "Biog-ejbPU ")
private EntityManager em;
@Override
protected EntityManager getEntityManager() {
return em;
}
public Data _UsuarioFacade() {
su per(Usuario.class);
}
}

La anotación "@Stateless" define un bean de s esión sin estado. Podemos apreciar


el u so que se h ace d e la unid ad d e persistencia (etiqueta "@Persis ten ceContext") p ara
definir el objeto d e la clase "EntityMan age r".
El bean de sesión anterior tiene implementados los métodos de la interfaz por heredar
de la clase "Data_AbstractFacade" (como hemos visto anteriormente definimos el tipo
Ten la definición de la clase).

• Cla se "Data_Abs tractFacad e .java " (uso d e JPQL):


package datasessionbeans;
import java.utii.List;
import javax.persistence. EntityManager;
public abstract class Data _AbstractFacade<T> {
private Class<T> entityCiass;
public Data_AbstractFacade(Ciass<T> entityCiass) {
this.entityCiass = entityCiass;
}
protected abstract EntityManager getEntityManager();

© Alfaomega - Altaria 1331


Curso avanzado de Java

public void create(T entity) {


getEntityM anage r(). persist( entity );
}
public void edit(T entity) (
getEntityM anager(). me rge( entity );
}
public void remove(T entity) {
getEntityM anager(). remove(getEntityM anager(). merge (entity) );
}
public T find(Object id) {
return getEntityManager().find(entityCiass,( Long) id);
}
public List<T> findAII() {
javax.persistence.criteria.CriteriaQuery cq =
getE ntityM a nager(). getC rite ria Bu iId er(). ere ateQu e ry();
cq .select( cq .from (entityCiass) );
return getEntityM anage r() .createQue ry( cq) .getResu ltList();
}
public List<T> findRange(int[] range) {
javax.persistence.criteria.CriteriaQuery cq =
get EntityM a nager(). getCrite ria Bu iId er(). createQu ery();
cq .select( cq .from (entityCiass) );
javax.persistence.Query q = getEntityManager().createQuery(cq);
q.setMaxResults(range[l]- range[O] + 1);
q .setFirstResu lt( range [O]);
return q.getResultlist();
}
public int count() {
javax.persistence.criteria.CriteriaQuery cq =
getE ntity Manager().getCrite ria Bu iId e r(). createQ u e ry();
javax.persistence.criteria.Root<T> rt = cq.from(entityCiass);
cq .select(getEntityMa nager().getCriteriaBuilder() .count( rt) );
javax.persistence.Query q = getEntityManager().createQuery(cq);
return ((Long) q.getSingleResult()).intValue();
}
}

Esta clase "Data_AbstractFacade" implementa todos los métodos comunes para todas
las entidades. Para terminar con el ejemplo mostramos la interfaz (contrato) "Data_Post-
FacadeLocal" y su implementación "Data_PostFacade".

• Interfaz "Data_PostFacadeLocal.java":
package datasessionbeans;
import data.Post;
import java.utii.List;

1134 © A lfaomega - Altaria


JEE. Manual práctic o

import javax.ejb.Local;
@Local
public interface Data_PostFacadelocal {
void create(Post post);
void edit(Post post);
void remove(Post post);
Post find(Object id);
List<Post> findAII();
List<Post> findRange(int[] range);
int count();
}

• Bean de sesión "Data_PostFacade.java":

package datasession be a ns;


import data. Post;
import javax.ejb.Stateless;
import javax. persistence. EntityManager;
im port javax. persistence. PersistenceContext;
@Stateless
public class Data_PostFacade extends Data_AbstractFacade<Post> implements Data_PostFa-
cadelocal {
@ PersistenceContext(unitName = "Biog-ejbPU ")
prívate EntityManager em;
@Override
protected EntityManager getEntityManager() {
return em;
}
pu blic Data_PostFacade() {
super( Post.class);
}
}

Veamos de una forma simple cómo podemos empezar a probar nuestro código.
Podemos empezar a hacer uso de Jos servlets. Crearemos un servlet que hará uso del
EJB cuya interfaz es local "Data_UsuarioFacade". Para hacer uso de un EJB en un
servlet (o en cualquier otro lugar), en la definición del atributo hemos de indicar la
anotación "@EJB".
package servlets;
import data.Usuario;
import datasessionbeans.Data_UsuarioFacadelocal;
import java.io.IOException;
import java.io.PrintWriter;
import javax.ejb.EJB;
import javax.servlet.Servlet Exception;

© Alfaomega - Altaria 1351


Curso avanzado de Java

im port javax.servlet.http.HttpServlet;
i m port javax.servlet. http. HttpServletReq u est;
i m po rt javax.servlet. http. HttpServletResponse;
public class BlogServlet extends HttpServlet {
@EJB
private Data _UsuarioFacadelocal usuarioFacade;
private Usuario usuario;
protected void processRequest(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
usuario = new Usuario();
usua rio.setNom bre(" Ismael");
usuario.setApellidos("López Quintero");
usua rio.setNom breUsua rio(" leria no 7");
usuario .setCiave(" m iclave 123 ");
usua rio.setEmail("leriano 7@ leriano 7 .com");
try {
usua rioFacade .ere ate( usuario);
} catch(Exception e) {
System .out. println (e .getM essage() );
}
response .setCo nte ntType ("text/ht m 1; eh arset= UT F-8"};
try (PrintWriter out= response.getWriter()) {
/*TODO output your page here. You may use following sample code. */
out.println("<! DOCTYPE htm 1>");
out.println (" <htm 1> ");
out. println (" <head> " };
out. println (" <title>Servlet BlogServlet</title> ");
out. println (" </head> ");
out. println (" <body> "};
out.println("<hl>Servlet BlogServlet at "+ request.getContextPath() + "</hl>");
out. println (" </body> ");
out. println (" </htm 1> ");
}
}

@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
processRequest(request, response);
}

@Override
protected void doPost(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
processRequest(request, response);
}

1136 © A lfaomega - Altaria


JEE. Manual práctic o

@Override
public String getServletlnfo() {
return "Información del servlet";
}
}
Compilamos el proyecto completo y lo desplegamos sobre el servidor de aplicaciones.
Ejecutamos el servlet.

D Se·rvlet BlogServlet X

~ ~ C [l localhost7001/Biog-war/Bio.gServlet

Servlet BlogServlet at /Blog-lvar

Y vemos lo que ha ocurrido en la base de datos.

+ Opciones
¡ ~:¡-+ ...,.. ¡¡ID APELLIDOS CLAVE EMAIL NOMBRE NOMBREUSUARIO

O ¿Y Editar ;!-e Copiar O Borrar 1 López Quint ero miclaven23 leriano7@1eriano7.com lsrrnaell leriano7

t._ Marcar todos 1 Desmarcar todos P-ara Jos elementos que están marcados: &9 Cambiar o Borrar ~ Exportar

5 .4.3.2 Ejercicio 3
Habiéndose implementado en el texto las fachadas de acceso a datos mediante state-
less session bean de las entidades de "Post" y "Usuario", se propone al lector la imple-
mentación de las fachadas de acceso a datos de "ComentaPost", "Chat" y "ChatActual" .

5.4.3.3 Con estado: stateful


Estudiemos el caso de un bean de sesión con estado. El ejemplo más típico de uso
de un bean con estado es el del carrito de la compra en un comercio electrónico. Pero
gran cantidad de desarrolladores, la primera vez que se enfrentan a tratar este caso
de uso con los stateful session beans, cometen el error de esperar que se creen nuevos
EJB con cada sesión de usuario HTTP que recibe el servlet. Este razonamiento es falso,
ya que los beans stateful crean una nueva instancia por cada cliente remoto y dispo-
nible para él sólo durante el tiempo de vida de éste. Si implementamos una aplicación
web en la que el usuario haga uso de un servlet, no tendremos varios stateful session
beans, s ino uno sólo, propiedad del servlet. Por ello mismo es importante no delegar
la lógica de "un carrito por cad a usuario" a l EJB, sino hacer el discernimiento a nivel

© Alfaomega - Altaria 1371


Curso avanzado de Java

de sesión HTTP. Como el servlet siempre tendrá la misma instancia del EJB, podemos
usar el EJB para mantener el estado de los diversos carritos.

Creemos una aplicación web en la que cada cliente que se conecte al servidor vea
la siguiente pantalla:

~C.MIO ... Ioc.....-,- )(


- :. ><
... -+ e 1D 1~7001/~emploSes~Be~n·W.'J!i 't;1 o =

Su carrito de la compra

r'ISf!•le(ll!lonmre<~etplcY.J~ , - - - - - - - - - - - - - ,

Estado del Carrito

¡- . . . . .. 1PnCIO
n:ttt
¡ -~-- 0,00

En dicho ejemplo tendremos un formulario en el que los campos (de texto libre)
alimentarán a una tabla. Cada usuario que se conecte mediante HTTP a la aplicación
tendrá su propio carrito y, por ende, su propia tabla. Como hemos dicho, este discerni-
miento debemos hacerlo mediante la sesión HTTP. La solución que se nos ocurre para
este problema con los EJB stateful es guardar (en el stateful del servlet) un atributo de
tipo "Map" que conserve el "id" de la sesión de cada usuario unido a la información de
su carrito de la compra. El lector se estará preguntando si estamos usando el stateful
EJB como si fuera un singleton. En efecto, lo estamos haciendo, porque el valor del
stateful no lo encontramos en las llamadas a servlets a través de HTTP, sino a través de
RMI, con llamadas remotas (mediante la interfaz remota) del EJB. Vayamos por partes
y veamos cómo implementar el ejemplo del carrito con EJB stateful. La estructura de
carpetas del proyecto es la siguiente:

Visión general:

Project s X files 1 • •
. •

••
-
~ · ,;,;,;;: EjernploSessionBean
13... EjemploSessionBean-ejb
éJ..@ E,jemploSessionBean-war

1138 © Alfaomega - Altaria


JEE. Manual práctico

Módulo EJB:

~ Sou-re PaOOges
é ·· E§ domoi~
i t ·li!l C<ri11D,JaW
¡ L.~ Prodt:do.jilva
á· E§ se~
f···li!l C<nill>S.¡ava
f···gj IC.mblsl.<coi.iovo
L. GJ IC~cmob::.jiiva
Tc:~t Pcdo¡c:$

Ubrani!S
TEtt Librarie-e
Ent:c:rpri:c: Gc:!IM
conngurat!OOfles
Server Res<IU'<es

Módulo web (WAR):

Te31: Libr.:ri::s
&\tr:rprire ge:.m
Con~gut'il"tbn Fia
Sc;rver Reoo:rces

\Ve!>Page-e
\VES-~

~ web.xml
~ w~b'c:Qc.xrrf
1iles
~ (!$
!···'i;u bootsUao-~••me.n1n.css
L.,
¡.. :e~ boo!:sbcp.tr.in.C:iS

(» ¡,
PfC!Clio.CIS

¡... ~ bootsb~p.n:in.js
L..~ jqxrr·3.LO.mh j3
indcx.j:;p
{19 S01.1cc PaOOtg~.¡
$··ijl btan•
j ~.. ~ ~ntr6ta.."l.jav3
é .. ijl ""~"
t .. ~ c.am~.JMJJ
1'f'!:t PMcal)~t-t
ubrcYes
TestiJ>rcres

Para definir la vista vemos que hemos definido un JSP ("index.jsp"). Aparte, hemos
importado la librería de JavaScript jQuery y hemos importado las librerías de CSS y de
JavaScript del framework Bootstrap (framework de la capa de presentación). Todo ello
desde sus respectivos sitios web. Son de libre uso. También podemos ver un fichero
"propio .css". Con dicho fichero le damos el toque personal a nuestra vista en aquello
que lo necesitemos.
Para poder trabajar las vistas a nivel de sesión, hemos definido un bean de sesión
(no confundir con los EJB session beans), es decir, una clase con atributos privados,
getters & setters, constructor sin atributos y que implementa la interfaz serializable

© Alfaomega - Altaria 1391


Curso avanzado de Java

(como se explicó anteriormente). Veamos primero cómo podemos definir la vista que se
ha mostrado a modo de p rototipo:

• Clase "CarritoBean .java" (un J ava Bean d e sesión ):


package beans;
ímport domaínmodei.Producto;
ímport java.ío.Seríalízable;
ímport java.utii.List;
publíc class CarrítoBea n ímplements Serí alízable {
publíc CarrítoBean() {}
prívate List<Producto> productos;
prívate double precíoTotal;
publíc List<Producto> getProductos() {
return productos;
}
publíc voíd setProductos(List<Producto> productos) {
=
thís.productos productos;
}
publíc double getPrecíoTotal() {
return precíoTotal;
}
public voíd setPrecioTotal(double precíoTotal) {
=
thís.precíoTotal precíoTotal;
}
}

• Fichero CSS "propio.css":


dív.contaíner {
margín-top: lcm;
text-alígn: center;
}
dív.contaíner table {
margín: O auto;
t ext-alígn: center;
}
dív.contaíner table.form th:fírst-chíld,
dív.contaíner table .form td :fírst-chíld {
wídth: 30%;
text-alígn: rígh t;
}
dív.contaíner table.form td.ínput-fíeld {
text-alígn: left;
}
dív.contaíner table td .total {
text-alígn: ríght;
}

1140 © A lfaomega - Altaría


JEE. Manual práctic o

• Página "index.jsp":
<%@ page contentType="text/html" pageE ncod i ng= "UTF-8" session="true"
import=
"javax.ejb. *,javax.naming. *,sessionbea ns. * ,java.text. Decima 1Format,
java.util. *,be a ns. * ,domainmodel. *" %>
<jsp:useBean id="carritoBean" class="beans.CarritoBean" scope="session" />
<%!
DecimaiFormat decimales;
%>
<%
decimales= new DecimaiFormat("O.OO");
%>
<!DOCTYPE htm l>
<htm l>
<head>
<meta http-equiv="Content-Type" content="text/htm 1; charset=UTF-8">
<link rel="stylesheet" type="text/css"
href="fi les/css/bootstrap.min .css" />
<link rel="stylesheet" type="text/css"
h ref= "fi les/css/bootstra p-theme. m in .css" />
<link rel="stylesheet" type="text/css"
href="fi les/css/propio.css" />
<script type="text/javascript"
src= "fi les/js/jq uery-3 .1.0. m in .js "></scri pt>
<script type="text/javascript"
src= "fi les/js/bootstrap.m in .js "></scri pt>
<meta na me="viewport" content="width=device-width, in itial-scale=l.O">
<title>Ca rrito de la Com pra</title>
</head>
<body>
<div class="conta iner">
<hl>Su carrito de la compra</hl>
</div>
<div class="container">
<form action="CarritoServlet" method="post">
<table class="table table-bordered form">
<tbody>
<tr>
<td>lnserte el nombre del producto: </td>
<td class="input-field"><input type="text"
name="nombre" size="60" maxlength="lOO" /></td>
</tr>
<tr>
<td>lnserte el precio del producto: </td>

© Alfaomega - Altaria 1411


Curso avanzado de Java

<td class="input-fie ld"><input type="text"


na me=" precio" size="60" maxlength=" 10" /></td>
</tr>
<tr>
<td>lnserte el número de unidades: </td>
<td class="input-fie ld"><input type="text"
name="nunid" size="60" maxlength="lO" /></td>
</tr>
<tr>
<td> </td>
<td class="input-field"> <input type="submit" />
<input type="reset" /></td>
</tr>
</tbody>
</table>
</form>
</div>
<div class="container">
<hl>Estado del Carrito</hl>
</div>
<div class="container">
<ta ble class="table table -bordered ">
<thead>
<tr>
<th>Nombre de Producto</th>
<th>Precio</th>
<th>Número de Unidades</th>
<th>Precio Producto</th>
</tr>
</thead>
<tbody>
<%
if ((carritoBean != null) &&
(carritoBean.getProductos() != nu ll)) {
List<Producto> productos=
ca rritoBea n .getProd uctos();
for (Producto p : productos) {
double precioTotal =
p.getPrecio() * (double)p.getnUnidades();
%>
<tr>
<td>
<%=p.getNombre()%>
</td>
<td>
<%=decimales.format(p.getPrecio())%>

1142 © A lfaomega - Altaria


JEE. Manual práctico

</td>
<td>
<%=p.getnUnidades()%>
</td>
<td>
<%=decimales.format(precioTotal)%>
</td>
</tr>
<%
}
}
%>
<tr>
<td colspan="3" class="tota 1">Tota 1: </td>
<%
if ((carritoBean != null) &&
(carritoBean .getProductos() != null)) {
%>
<td>
<%=decimales.format( carrito Be an .getPrecioTotal())%>
</td>
<%
} else {
%>
<td><%=decim ales. form at(O.O )%></td>
<%
}
%>
</tr>
</tbody>
</table>
</div>
</body>
</htm l>

Antes de estudiar cómo trata el servlet la información de la sesión, veamos cómo


implementamos el módulo EJB.

Las clases del modelo del dominio están en el paquete que se llama domainmodel.
Represen tan la información de cada uno de los carritos con los que trabajará el EJB
stateful.
• Clase "Producto.java":
package domainmodel;
import java.io.Seria lizable;
public class Producto implements Serializable{

© Alfaomega - Altaria 1431


Curso avanzado de Java

private String nombre;


private double precio;
private int nUnidades;
public String getNombre() {
return nombre;
}
public void setNombre(String nombre) {
this.nombre =nombre;
}
public double getPrecio() {
return precio;
}
public void setPrecio(double precio) {
th is.precio = precio;
}
public int getnUnidades() {
return n Unidades;
}
public void setnUnidades(int nUnidades) {
this.nUnidades = nUnidades;
}
}

• Clase "Carrito.java":
package domainmodel;
import java.io.Serializable;
import java.utii.Arraylist;
import java.utii.List;
public class Carrito implements Serializable {
prívate List<Producto> productos;
prívate double valorTotal;
public Carrito() {
th is.productos = new Arraylist<Producto>();
}
public list<Producto> getProductos() {
return productos;
}
public void setProductos(List<Producto> productos) {
this.productos = productos;
}
public double getValorTotal() {
return valorTotal;
}
public void setValorTotal(double valorTotal} {
this.valorTotal = valorTotal;
}
public void anadirProductoCarrito(Product o p) {

1144 © Alfaomega - Aliaría


JEE. Manual práctico

if ((p != null) && (this.productos != null)) {


boolean encontrado= false;
inti=O;
for (Producto producto : this.productos) {
if (producto.getNombre().equals(p.getNombre())) {
encontrado = true;
break;
} else {
i++·
'
}
}
if (encontrado) {
Producto este Producto= this.productos.get(i);
este Prod ucto.setn Un ida des(esteProducto .getn Un ida des()
+ p.getnU n idades() );
} else {
this.productos.add(p );
}
}
}
public void eliminarProductoCarrito(Producto p) {
if ((p != null) && (this.productos != null)) {
boolean encontrado= false;
inti=O;
for (Producto producto : this.productos} {
if (producto.getNombre().equals(p.getNombre())) {
encontrado = true;
break;
} else {
i++·
'
}
}
if (encontrado) {
th is. p reductos. re m ove( i);
}
}
}
public double getPrecioTota iCarrito() {
if (this.productos != null) {
double total = 0.0;
for (Producto p : this.productos} {
total= total+ (p.getPrecio() * p.getnUnidades());
}
return total;
} else {
return 0.0;
}
}
}

© Alfaomega - Altaria 1451


Curso avanzado de Java

Estudiemos ahora el componente EJB sta teful "Carritos". Indicar que se implementa
una interfaz local y u na interfaz remota.

• Interfaz "ICarritos Local.java ":


package sessionbeans;
import domainmodei.Producto;
import java.utii. List;
import javax.ejb.Local;
@Local
public interface ICarritosloca l {
public void usarCarrito(String id); 11 Identificador de la sesión.
public void anadirProduct oCarrito(String id, Producto p);
public void eliminarProductoCarrito(String id, Producto p);
public double getPrecioTotaiCa rrito{St ring id);
public List<Producto> getProductos(String id);
public void remove(String id);
public void re m ove(); 11 Re m ove de Stateful Session Bean.
}

• Interfaz "ICarritosRemote.java ":


package sessionbeans;
import doma inmodei.Producto;
import java.utii.List;
import javax.ejb.Remote;
@Remote
public interface ICa rritosRemot e {
public void usarCarrito(String id); 11 Identificador de la sesión.
public void anadirProductoCarrito(String id, Producto p);
public void eliminarProductoCarrito(String id, Producto p);
public double getPrecioTotaiCa rrito(String id);
public List<Pro duct o> get Productos(St ring id);
public void rem ove(St ring id);
public void re m ove(); 11 Re m ove de Stateful Session Sean.
}

• EJB stateful "Ca rritos.j ava". Es el compon ente que implem en ta e l m a pa de datos:
package sessionbeans;
import domainmodei.Carrito;
impo rt doma inmodei.Producto;
import java.utii.Arraylist;
import java.utii. HashMap;
import java.utii.List;
import javax.ejb.Remove;
import javax.ejb .Stat eful;

1146 © A lfaomega - Altaria


JEE. Manual práctic o

@Statefu l
public class Carritos implements ICarritoslocal, ICarritosRemote {
prívate HashMap<String,Carrito> carritos;
public Carritos() {
this.carritos = new HashMap<>();
}
@Override
public void usarCarrito(String id) {
Carrito carrito = this.carritos.get(id);
if (carrito== nu ll) {
carrito= new Carrito();
this.ca rritos. put( id, carrito);
}
}
@Override
pu blic List<Producto> getProd uctos(String id) {
Carrito carrito = th is.ca rritos.get(id );
if (carrito== nu ll) {
return new Arraylist<Prod ucto>();
} else {
return ca rrito.getProductos();
}
}
@Override
public void anadirProductoCarrito(String id, Producto p) {
Carrito carrito = this.carritos.get(id);
if (carrito != null} {
carrito .a nad irProd uctoCa rrito(p);
}
}
@Override
public void eliminarProductoCarrito(String id, Producto p) {
Carrito carrito = this.carritos.get(id);
if (carrito != null} {
ca rrito. eli m in arProductoCa rrito(p };
}
}
@Override
public double getPrecioTota iCarrito(String id) {
Carrito carrito = this.carritos.get(id);
if (carrito != null) {
retu rn carrito.getPrecio Total Carrito();
} else {
return 0.0;
}
}

© Alfaom ega - Altaria 1471


C urso a vanzado de Java

@Override
@Re m ove
public void remove() {
this.carritos = nu ll;
}
@Override
public void remove(String id) {
Carrito carrito= this.carritos.get(id);
if (carrito != null) {
th is.ca rritos.remove(id);
}
}
}
Finalmente, volvemos a la vista (proyecto WAR) para ver cómo implementamos el
servlet:

• "CarritoServlet.java" :

package servlets;
import beans.CarritoBean;
import doma inmodei.Producto;
import java.io.IOException;
import javax.ejb.EJB;
i m port javax.se rvlet.ServletException;
im port javax.servlet.http.HttpServlet;
i m port javax.servlet. http. HttpServletReq uest;
i m port javax.servlet. http. HttpServletResponse;
im port javax.servlet.http.HttpSession;
im port sessionbea ns.ICarritoslocal;
public class CarritoServlet extends HttpServlet {
@EJB
prívate ICarritoslocal carritos;
prívate CarritoBean carritoBean;
protected void processRequest(HttpServletRequest request,
HttpServletResponse response)
throws ServletException, IOException {
synchronized (this) {
HttpSession session = request.getSession();
String idSesion = session.getld();
carritos. usa rCa rrito( idSesio n);
if (session.getAttribute("carritoBean") == null) {
carritoBean = new CarritoBean();
session .setAttri bu te( "carrito Be an ", carrito Be an );
} else {
carritoBean = (CarritoBean) session .getAttribute("carritoBean");
}

1148 © A lfaomega - Altaria


JEE. Manual práctico

String nombreProducto = request.getParameter("nombre");


String precioProducto = request.getParameter("precio");
String nUnidadesProducto = request.getParameter("nunid");
double precio = 0.0;
int nUnidades =O;
try {
precio= Double.parseDouble(precioProducto);
nUnidades = lnteger.parselnt(nUnidadesProducto);
} catch (Exception e) {}
if ((precio != 0.0} && (nUnidades !=O)) {
11 Los datos se han introducido bien, modificamos el EJB stateful.
Producto p = new Producto();
nombreProducto = new String(nombreProducto.getBytes{"IS0-8859-1"), "UTF-8");
p.setNombre(nombreProducto);
p.setPrecio(precio );
p.setnU nidades(nU nidades);
ca rritos.a nad irProd uctoCa rrito(idSesion,p);
System.out.println("EI carrito tiene "+
carritos.getProductos(idSesion).size() + " elementos".);
}
carrito Bean. set Productos( carritos. get Productos ( idSesio n));
ca rritoBean .setPrecioTota 1(ca rritos.getPrecio Tota ICa rrito( idSesion));
res pon se .sen d Red i re ct (" i nd ex. j sp");
}
}
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
processRequest(request, response);
}
@Override
protected void doPost{HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
processReq uest(request, response);
}
@Override
public String getServletlnfo() {
return "Descripción del servlet CarritoServlet";
}
}
Vemos cómo llamamos a l stateful session bean en la llamada a "usarCarrito". Se
añadirá un carrito al mapa por cada "id" de sesión. Ésta es la solución propuesta para
la aplicación web del carrito de la compra con stateful session beans (usándolo como si
fuera un singleton). Ya que lo llamamos siempre desde el servlet, éste tendrá inyectada
la misma insta ncia del EJB (siempre será el mismo cliente) . Veamos el funcionamiento
de la aplicación desarrollada.

© Alfaom ega - Altaria 1491


Curso avanzado de Java

.. .. e

Su carrito de la compra

t~~Ut!oe e l ecmbte: dei !>JCICI:.::!::I;

rlsette f l pteeio oel p-JNutt~

ln~eftt et n..merode 1:1'1\U~~

1"''"' 11•·..-1

Estado del Carrito

3 •••
S,QO 2

(l.ilO l:MO

..... 1,CO
•• 10.00

Los EJB stateful muestran su potencial en la llamada haciendo uso d e la interfaz


remota, a través de RMI. El estudio de RMI debe realizarse en tres estadios diferentes,
para entenderlo mejor:
• Implemantación de RMI en la JVM.

• Implementación de RMI en el servidor de aplicaciones.


• Implementación d e RMI mediante una interfaz de EJB remota.

5.4.3.3.1 RMI en la JVM local


RMI consiste, como su nombre bien indica, en la invocación remota de métodos.
Dicho estándar conforma una librería que es parte de la JSE. La tecnología es heredera
d e RPC (Remote Procedure Call), versión de RMI para la programación estructurada.
Lo que pretende es asociar a l registro local de la máquina un método en un punto de
red, que será visible por los demás equipos conectados a la misma red (en nuestro
caso la red local) .

Para implementar RMI debemos exponer una interfaz remota en una librería se-
parada d e la implementación de la interfaz. Evidentemente, para poder implementar
dicha interfaz d ebemos importar en el proyecto de implem entación el a rchivo ".ja r" de
la librería. La idea es la siguiente:

• Implementamos en nuestra máquina una interfaz remota que será pública.


• Creamos una aplicación Java que añadirá dicha implementación al registro.

1150 © Alfaomega - Altaria


JEE. Manual práctico

• Facilitamos a los clientes que quieran hacer uso de nuestra implementación el


nombre de acceso al registro de nuestra dirección IP.

• Los clientes se conectarán a nuestra máquina haciendo uso de la interfaz remota


y del nombre de acceso al registro que les habremos facilitado.

La idea subyacente es aislar el uso de los métodos de la implementación de éstos,


creando sistemas débilmente acoplados. El lector se estará preguntando si llevamos
una copia del objeto a cada cliente. La respuesta es sí y a la vez no. Lo que se traslada
al cliente es una imagen espejo de nuestro objeto (una imagen sincronizada), ya que
los cambios en la "copia" afectan directamente al originaL Lo que el cliente obtiene es
un proxy. Para implementar RMI en versiones anteriores de JSE debíamos compilar
la clase que accedía al registro, llamada skeleton, creando una clase stub. Esta forma
de implementar RMI está obsoleta y no es necesario llamar a rmic desde la línea de
comandos.

Veamos cómo implementar RMI con un sencillo ejemplo. El ejemplo que veremos será
el archiconocido "Hola Mundo" pero implementado mediante RMI. Como bien hemos
observado, RMI en sí es una característica de JSE. Por lo tanto, para programar este
ejemplo podemos apagar el servidor de aplicaciones. Todo lo que necesitamos es dos
aplicaciones Java (una de implementación y otra cliente), así como una librería que
expone la interfaz remota. O sea, tendremos tres proyectos Java.

Projects X f"des Prestacione s


El··· t;, Implementaáon
$···t:t
t±J ~ RMICftente

En el proyecto "Interfaz" colocamos la interfaz remota. Dicho proyecto es una librería


de clases . Los otros dos proyectos son aplicaciones Java JSE. El proyecto "Implemen-
tacion" es la implementación de la interfaz y el proyecto "RM ICliente" es el programa
que se ejecutará en cualquier equipo de nuestra red.
Veamos el código de cada uno de los proyectos:

• Código de la interfaz remota en la librería Interfaz:


packa ge ejemplo;
import java.rmi.Remote;
import java.rmi.RemoteException;
public interface Saludo extends Remote {
public Strin g saludar() throws RemoteException;
}

© Alfaomega - Altaria 1511


Curso avanzado de Java

• Código de la implementación de la interfaz. Debemos importar el fichero "Inter-


faz.jar":
package ejemplo;
import java.rmi.RemoteException;
im port java.rmi.server.U nicastRemoteObject;
public class lmplementacion extends UnicastRemoteObject implements Saludo {
protected lmplementacion() throws RemoteException {
super();
}

@Override
public String saluda r() throws RemoteException {
return "Hola Mu ndo"·1
}
}

Y del mismo modo vemos el programa principal que accede al regist ro, situando en
él una instancia de la clase "Implementacion":
package ejemplo;
im port java.rmi.registry. LocateRegistry;
i m po rt java. rm i.registry. Registry;
public class PrincipaiRMI {
public static void main(String[J args) {
try {
Saludo saludo = new lmplementacion();
Registry registro = LocateRegistry.createRegistry(9091);
registro. rebind ("saludo" saludo);
1

System.out.println("Añadimos al registro");
} catch (Exception ex) {
System .out. println (ex.getM essage());
}
}
}

De este modo, estamos situando la implementación de la interfaz remota en el


puerto d e red 9091 de la máquina local. Aquellos clientes que quieran hacer uso de la
implementación tendrán:

• Acceso a la librería que define la interfaz remota.


• Nuestra dirección !P.

• El puerto en el que hemos situado la implementación (9091) .

• El nombre que le hemos asignado en el registro de nuestra máquina ("saludo").

1152 © Alfaomega - Altaria


JEE. Manual práctico

La dirección IP de nuestra máquina es 192.1 68.0 . 101. Facilitando dicha información


y el puerto pueden escribir la aplicación cliente.

• La aplicación "RMICliente":
package rmicliente;
import ejemplo.Saludo;
import java .rm i. registry.LocateRegistry;
import java .rm i.registry.Registry;
public class RMICiiente {
public static void main(String[J args) {
try {
Registry registro = LocateRegistry.getRegistry(" 192.168.0.101 ", 9091);
Sa ludo saludo= (Saludo) registro.lookup("saludo");
System .out.println (sa 1udo .sal u dar());
} catch (Exception ex) {
System .out.printl n (ex.getM essage());
}
}
}

Ejecutando la aplicación cliente tenemos la salida esperada:

Hola Mundo

5.4.3.3.2 RMI en el servidor de aplicaciones

Una vez entendido el anterior ejemplo, nos disponemos a hacer lo mismo en Oracle
WebLogic Server. Damos el paso al servidor de aplicaciones pero aún no usamos EJB.
La idea es dejar la aplicación de implementación corriendo en el servidor. Para ello
hemos de setear dicha clase como una clase de arranque del servidor. Los clientes
se conectarán ahora a la implementación que residirá en el servidor de aplicaciones.
Seguimos teniendo tres proyectos JSE.

Proj>ecl:s X f ile:s Pre:stacio.ne:s


~... ,& CUente
$·ti
@.... Interfaz

• "Interfaz": interfaz remota.


• "Implem": proyecto a desplegar e n el servidor d e aplicaciones que incluye la
implementación.
• "Cliente": programa que ejecutaremos desde cualquier máquina conectada a la
red del servidor de aplicaciones.

© Alfaomega - Altaria 1531


Curso avanzado de Java

Para conectamos como clientes RMI a WebLogic usamos el protocolo T3 . Cada ser-
vidor de aplicaciones (JBoss, GlassFish, JOnAs ... ) tendrá su configuración y protocolo
para conectarse al servidor de aplicaciones. En nuestro caso, identificamos el registro
como e l contexto del servidor. La configuración para setear y leer información d e l con -
texto es la s iguiente:

String url = "t3://" + hostname + ":" + port;


Hashtable env =new Hashtable();
env. put( Context.l N ITIAL_ CO NTEXT_FACTO RY, "weblogic.j ndi .W ll n itia IContextFacto ry");
env.put(Context.PROVIDER_URL, url);
env.put("weblogic.j ndi.co n nectTim eout", new long( 15000));
env.put("weblogic.j ndi.response Re a dTi m e out", new Long( 15000));
env. put(Context.SECU RITY_ PRINCIPAL, "adm in");
env.put(Context.SECU RITY_ CREDENTIALS, "admin_ password" );
new lnitiaiContext(env);

Ésta es una nota técnica para saber cómo acceder al contexto en WebLogic. Este
código debe ajustarlo e l programador para su servidor elegido.

El código de la interfaz, situado en una librería, ya lo conocemos:


package ejemplo;
import java.rmi.Remote;
import java.rmi.RemoteException;
public interface Saludo extends Remate {
public String saluda r() throws RemoteException;
}
• Código de la implementación. Es una aplicación JSE con un método main. Hemos
situado en una sola clase la implementación de la interfaz y el método matn.

package implem;
import ejemplo.Sa ludo;
import java.rmi.RemoteException;
import java.utii.Hashtable;
import java.util.logging.Level;
import java.util.logging.logger;
import javax.naming.Context;
im port javax.naming.lnitiaiContext;
im port javax.naming.Nam ingException;
public class lmplem implements Saludo {
private static String hostname = "localhost";
private static int port =7001;
private static String url = "";
private static Context ctx = null;
private static Saludo saludo;

1154 © A lfaomega - Altaria


JEE. Manual práctico

@Override
public String saludar() throws RemoteExc eption {
return "Hola mundo";
}
public static void main(String[) args) {
try {
ctx = getlnitiaiContext();
saludo= new lmplem();
ctx.rebind("saludo", saludo);
} catch (Exception ex) {
Logger.getLogger(lm pie m .class.getNa me () ).log( Leve I.SEVE RE, nu 11, ex);
}
}
private static Context getlnitiaiContext() throws NamingException {
url = "t3 ://" + hostname + ":" + port;
Hashtable env = new Hashtable();
env.put(Context.IN ITIAL_ CONTEXT_ FACTORY,
" we blogic.jnd i .WLIn itiaIContextFactory" );
env.put(Context.PROVIDER_ URL, url);
env.put(" we blogic.jnd i.con nectTimeout", n ew Long( 15000) );
env. put(" w e blogic.jndi.response ReadTi m eout", new Long( 15000) );
env.put(Context.SECU RITY_PRIN CIPAL, "adm in");
env.put(Context.SECURITY_ CREDENTIALS, "adm in_password");
return new lnitiaiContext(env);
}
}
La idea es compilar esta clase y colocarla como una clase de arranque de WebLogic.
Veamos cómo hacerlo:

• Debemos crear un directorio en la ruta de nuestro dominio, a saber: "C:\Oracle\


Middleware \ Oracle_Home \u ser _projects \ domains \learn \ " para colocar el fichero
".jar" ejecutable que hemos generado al compilar el proyecto "Implem". Dicha
carpeta la llamamos, por ejemplo, jar. La ruta seria: "C:\Oracle\Middleware\
Oracle_Home \ user_projects \domains \learn \jar" .

• Copiamos en dicho fichero el ".jar": "Implem.jar".

• Ajustamos el classpath del servidor. Para ello acudimos al fichero: "C: \ Oracle \
Middleware \ Oracle_Home \ user_projects \ domains \learn \ bin \ star tWebLogic.
cmd". Justo a l final del fichero, antes de la sección echo (salida por consola),
insertamos la siguiente línea:

"CLASSPATH = %CLASSPATH%;%DOMAIN_HO M E%\jar\lmplem.j ar".

Creamos la clase de arranque. La localización de la clase será "implem.Implem".


Veamos algunas capturas de pantalla.

© Alfaomega - Altaria 1551


C urso a vanzado de Jav a

En la estructura del dominio "learn", seleccionamos la opción "Clases de Inicio y


Cierre".

Estructura de Oomínic>
; ··Grupos de Recursos
t··Piantillas efe Grupo de Recur.sos
r··r.1áq;uinas
':··Host s VirtU<ales
~--oestinO>S Vi rtuales
}··Gestores de Trabajos
t··Piantillas Simultáneas
t··Gestiólll de Recursos
~ .. Oasts de [nido y Cierre

r··[)on>ini•OS de Seguridad

Creamos nueva clase.

Clases de Inicio y Cierre { filtrado: Existen t l ás Colunmu)

1Nuevo 1 1Clonar 1 1Suprirnr 1

o Nombre A Tipo Normre de Clase

1Nuevo 1 1Clonar 1 1Suprimt 1

Indicamos el nombre que será tomado del classpath.

ConfiguRc:ión de Nucw <:Y se de Inicio o C~e.rrc

Lls .$iguJf!RU!S p:o;.edaclts se utilaarár. pilril it!eOO:ficar la dilse (JIC!: etá CCJnfigurando.
• Jr.dlca campos nec...">S.arios.

lQu@ nombre d~ uUiizar para identl fic<~r la dase1

1Loodlmplom

• t~nlllrc ck: Ouc:: [§plem.lmplem

La desplegamos en "AdminServer" del dominio.

1156 © A lfaomega - Altaria


JEE. Manual práctico

Configuración ele N 11eva Clase de Inicio o Cierre

1Atá> ll SiQuiente f l l Terminar 111cancearl


.selecciOnar Destinos

Putdt asigrtar esta nueva dase a watQUiera de ertos servidores o du.sters. Seleccione los destinos.

Dc:stinos:

¡ ~~-
~ Adn~nServer

Vemos la clase de inicio instalada.

Clases de I n icio Y' CieTre (Filtrado: Existen ~1ás <:olumnu)

1Nuevo 11Clo:nar 1 1Suprimir 1

o Nl>mbre A Tíipo Noni>re de Clase Orden de Des¡lfie9ue

o LoadlmQiem Inicio implem.lmpl em lOO~

1Nuevo 11Clonar 1 1Suprimir 1

Reiniciamos el servidor y vemos la variable classpath.

J~ 1es t1

J··ri Selenium Server 5ali<la • Oracle Webologic Ser.-er xl


•·~ .
.
JAVA Memor¡¡ argwnent;3: -Km3Z!56m - Klm<S 12m -XK:Compil·eThre3hol d-SOOO
~ .
a CLASSPAIH-c: \OI':ac:le\MI DDLE:•·1 \ORA<:LE-1 \ora.cle_ c:o""""n\modules\ <:0111. orac:lo . "
.
e PAIH=C:\Ora~la \MIDDLE:-1\0RACLE-1\0SE R_ P-1\domains\dvx\bin;;C: IMID
.
•••••••••••••••••••••••••••••••••••••••••••••••••••

.. To W-ebLoq.ic Serve r, use a userna.me and
~
s~.art

password assigned to en admin·- leve 1 use r _ For •


" gerve.r edminigtrati on,. uge the WebLoqi c Serve:r •
~
C003Dle at http:\\ho~tname:port\con~ole •
~·············-····~·······························
Startinq WLS v~eb l i-ne :
C:\PllO<:RA-1 \.Java\.JDR!.8·1.0_?\bin\java -server -Xm.s25Ga -lCmxS12., -vv -.r,

Y finalmente veremos el serv1c10 RMI del servidor colgando del árbol JNDI con el
nombre "saludo".

© Alfaomega - Altaria 157 1


Curso avanzado de Java

Estructura de: Árbol de JNDI


AdminServer
r··
_ WL_Gl obaiJa.vaApp
$-·_WLj nte rna1_yVSt73iobPk31IX.eelSLjfM!:xsKf•
S·com
'
B ·eis
'
1$1-·ejb
$-java :g'l oba 1
$·javax
'
:--jmx
'
:--mejbmejb_ja rMejb_EO
't--saludD
'
S·webl ogi e

Finalmente desarrollamos la aplicación cliente con el siguiente código:


package cliente;
import ejemplo.Sa ludo;
import java.utii.Hashtable;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.naming.Context;
im port javax.naming.lnitiaiContext;
im port javax.naming.Nam ingException;
public class Cliente {
prívate static String hostname = "192.168.0.101";
prívate static int port = 7001;
prívate static String url = "";
prívate static Context ctx = null;
public static void main{String[] args) {
try {
ctx = getlnitiaiContext();
Saludo saludo= (Saludo) ctx.lookup("saludo");
System .out. println (saludo.sa luda r());
} catch (Exception ex) {
Logger.getlogger( Cliente.class.getN a m e() ).log{Leve I.SEVE RE, n u 11, ex);
}

}
prívate static Context getlnitiaiContext() throws NamingException {
url = "t3://" + hostname + ":" + port;
Hashtable env = new Hashtable();
env. p ut( Context.l NITIAL_CONTEXT_FACTO RY, "weblogic.j nd i .W L1 n itia IContextFactory" );
env.put(Context.PROVI DER_U RL, url);
return new lnitiaiContext(env);
}
}
Hola Mundo

1158 © Alfaomega - Altaria


JEE. Manual práctico

5.4.3.3.3 RMI. Interfaz remota de stateful EJB

Hemos tenido una aproximación para ver cómo podemos implementar RMI en WebLo-
gic . Ahora veremos cómo acceder a un stateful EJB mediante interfaz remota, usando
precisamente RML De esta forma podremos entender en qué consisten las sesiones
(diferentes a las sesiones HTTP). Ahora sí podemos hablar de beans de sesión que con-
servan estado. Este estado es conservado durante la vida del cliente. Si recordamos el
carrito de la compra que implementamos con un EJB stateful, tendríamos un carrito
de la compra por cada cliente que se conecta al EJB mediante RML

Los stateful session beans pueden pasar a estado "pasivo" por tiempo de inactividad
del cliente. El estado pasivo indica que la imagen del bean pasa de memoria principal
a memoria secundaria (disco) del servidor. Por ello mismo, el objeto es sometido a un
proceso de marshalling o serialización. Todos los atributos del EJB deben ser tipos
primitivos o implementar la interfaz serializable. El bean vuelve a memoria principal
cuando el cliente vuelve a invocarlo. Si un cliente termina su ejecución, el EJB stateful
es destruido.

Para no repe tir el código del carrito, en gran parte igual, vamos a implementar otro
ejemplo. Cada bean de sesión guarda un entero. Este entero se incrementa en uno cada
vez que se consulta. Evidentemente, cada cliente tendrá su contador.

Crearemos tres proyectos:

• "InterfazRemota": define la interfaz remota del EJB stateful.


• "MiSessionBean-ejb": módulo contenedor en el que definiremos nuestro EJB.

• "ClienteEJBRemoto": Aplicación JSE que ocupará el rol de cliente.

Veamos cómo queda el código de los tres proyectos:


• "InterfazRemota". Interfaz "EjemploSessionBeanRemote.class":
package ejemplo;
import javax.ejb.Remote;
@Re mote
public interface EjemploSessionBeanRemote {
public int getContador();
}

• EJB "EjemploSessionBean":
package ejemplo;
import javax.ejb.Stateful;
@Statefu l
public class EjemploSessionBean implements EjemploSessionBeanRemote {
private int contador;
pu blic EjemploSession Be an() {

© Alfaomega - Altaria 1591


Curso avanzado de Java

contador= O;
}
@Override
public int getContador() {
cont ador++;
return contador;
}
}

Para pod er implementar el cliente d e bemos importar la librería "wlthint3client.j a r"


ubicad a e n "C: \Ora ele \Middleware \ Oracle_Hom e \ wlserver\server \ lib". Por supuesto,
también debemos importar la librería de la interfaz remo ta.

• "ClienteEJBRemoto.class":
package clienteejbremoto;
im port eje m plo.Ejem ploSession Be an Re mote;
import java.utii. Hashtable;
impo rt java.util. logging. Level;
import java.util.logging.Logger;
import javax.naming.Context;
im port javax.naming.lnitiaiContext;
im port javax.na ming.Nam ingException;
public class ClienteEJBRemoto {
private static String hostname = "192.168.0.101";
private static int port = 7001;
private static Context ct x = null;
public static void main(String[) args) {
try {
ctx = getlnitiaiContext();
EjemploSessionBeanRemote sbr = (EjemploSessionBeanRemote)
ctx.lookup("java :global. M iSession Be a n. M iSession Be an -ej b.Eje m ploSession Bean"
+ " !ejem plo.EjemploSession Bea nRemote");
int contador= -1;
while(contador < 1000) {
contador = sbr.getContador();
Thread .sleep( lOO);
System.out.println("EI va lor del contador del bean es: "+contador+"".);
}
} catch (Exception ex) {
System .out. println (ex.get M essage());
}
}
private static Context getlnitiaiContext() throws NamingException {
Context con t exto = null;
String url = "t3://" + hostname + ":" + port ;
Hashtable env = new Hashtable();

1160 © A lfaomega - Altaria


JEE. Man ual práctic o

env.put( Context.IN ITIAL_ CO NTEXT_FACTORY, "weblogic.jndi .W Lln itiaiContextFactory" );


env.put(Context .PROVIDER_URL, url);
contexto= new lnitiaiContext(env);
ret urn contexto;
}
}
La salida del programa desd e dos clientes diferentes es la siguiente:

,, Administrador: <Jmcl · Jírlll ·jill 0•enteEJ8Remoto.j~r .


~ .o c-()!lta r;~n 1'.'; : >1 ....11t1f"
, .;, c <mt .ru!c...- cT~i ¡,.; ,,.; .... : 104.
'" ' ,.'• ;
..,
<!·· 1 tl"'t .ukK de•! lw.m r·,:
" 1
1
'-'o<i<K
....., 1...
\•<11 • ..-
,¡.. ¡ • '"'t ...~... olr l ¡, .. ·'"
•!t·l 'ufll .uk)l" dt•l lw •.n
Jvt· l!d L<»lt.uJor dt!l b~:.1n 1::. :
.. ·. :
···.:
'• HL
., ¡ '1.
':!'lO.
)41. ""
\¡~lillf'
d•·l e 111!1 ...!nr dr•l
v,d (Jr J.:l I..VI\lá~\11 dt: 1
' 1 v:~ lo:"lr d• l rMt;uiN" d•l bf'ln
bt>o~n
bO!oln
.,.

11)).
10(, .
1C 11
1 \ ';J
' 1 \1:-tlt~r de l rnnt;u!M bi'Jin t'<;; IOf..
l l \ ·al eor d•l contador "'1 bean e5: • 1 V~tJc¡r· do• l e ont .ulclf" dr• 1 bí' ..U1
1 \·:-ti M dr 1 rontildor .... ,
hr;~n ro;: JH.
j4[,
' 1 \l<tlt)f' de 1 ( (loflt adc.or del b""'" .,.,.: 111. 111'1.
•'",. :; 110.
"'; ..... l,,. dt•l ( t>tllolllur d••l b .. ~n
1 \•:olnr dP J ( oot ,u!IJII" hr;w
""'
1 v;.o1 or d~ 1 l(>oflt.t.dc.- do1 hc;an •. : ' ..1'J.
';-1-1 . '1
., .....
"; 1 -..a\r<ll(lr\(lr ,,,
V.l.ll.ll" Jd
'"
,., ··~ .. llJ.
1..oo l.ul or 112.
~
~'
1 v ..ll>l· ..h-1 t out .tdo;~· d•·1 b ...1n 1" '• : '>4ó. ,,1 c.oot a.dor del
1 11.:11 VI" t.O!l t ....dul- del b i!J.n "-!.. : >4/.
'"
1 \"d 1V! dd 1-v:tl t .s.dul del b\!<1.11 . :,.48. ., " t'()f! t <lc!Cll"
• 1 ...., hw dl' l <¡mt .Hioor drl h~<tn
b~""
tH .
....
:
O"
11'i.
1 .._, l r.r rl~>l r M t ;¡ r1nor
,,,
""'
hP.!'In ';.4'}
'", ""1.,..- do 1 < <mt.uloo:r dr1 L ~;o;n ...'· : llL_
1 ''" l nor <!1'1 •oot.ulor
1 "" 1or dol <oat .ulor drl hr<~n
1 ''"1 or de 1 cm~Lulor do1 bran
hr<~n "'
f.'. :
.. ~ :
'i'iO.
•, ~ 1 .
' 1 v.tltJf· ·11·1 < ontddor

"
[J
v.,¡\lJr" Jd ;,.oolo;~.dor dt: 1 .....
d•1 b i'an :-.:

,., ...
..
e:.:
ll/-
llb .

"" ;,.ool.s.dOf" del Lun 119 .


',~7.
"'·
1' _•, :

..·. : \< 411.!1


1 \ •;¡) <:X" dt•l ' oo t .uJor di• 1 ¡,,.,¡n
1
1
\'4) VI
V~ 1r.r
\ '0' lr.r ~
,.,
'".., I..OOtddul-
r.-.nf!'ldor
d<l
... , bl!d!l
h~>!'ln ... ...
"'·
')'d.
,A,

;;G.
; 1 \r~lt'lr <• 1 roo t ;¡d(ll"
> 1 \rotl<:t· do• 1 <<mt o1Ck•r drl
• 1 v.tl<a· <1<>1 <
",,,.,.. '"'t
o~cl<>f" dv 1
,¡.. ¡ < <lflt m\,,.. del
h~>;¡n
h r;¡n
bn.a:n
b ...,n
..... :
..... :
..... :
1i'O
171.
1Jl.
1 r Ofl t a clor
"' h<'<ln r.o:
..•.
•.. ,,.,. ' , ..
.,
>1
.... 1; i.

"
,_.,.¡,K <!<· 1
1 V .1 ],;ot .!.-1
1 \•<11 <>1" <!•·1
1 \ '0: 1vt · Jd
UJOII t .uk¡or
( <>nt ... ~...
el!• 1
•t .. 1
ulflt .uk)l" dt•l ''"''"
lw .111

lll'.ln
I..Oflt.l.t)o,· dl!l bc.1n e:.
•··.
·,·,u.
>W.
n
"
".tleJI' .¡,. ¡ ¡ llfll .u!ur dt•l
1 \r:llt'lr
' 1 \r;'!]C'lr
,.,
,.,,_¡'-11 Jcl I..VIll Gd\11 del
,,,t'NitAriN"
(' nnt ;u!N" drl
,.,
b r.tn
L o:.o~n
bl'ln
bl'.:tn
...
,,,
1'";;
: 111 .
12'J.
1/f,
1n.

Evidentemente, cada cliente conserva su estado del bean, porque tiene una sesión
asociada y un EJB stateful propio .

5 .4 .3 .4 Instancia única: singleton


El desarrollo de software se hizo mucho más escalable y mantenible gracias al pa-
radigma de la programación orientada a objetos . Como se ha explicado a lo largo de
este manual, un objeto es una instancia de una clase. Pero junto con la programación
orientada a objetos aparecieron un conjunto de patrones de diseño de software. Si
queremos estudiar los patrones de diseño para que la arquitectura de nuestra aplica-
ción siga las soluciones más efectivas que han planteado los desarrolladores a lo largo
de los años, tenemos un compendio de ellos en el libro Patrones de diseño: elementos
de software orientado a objetos reutilizables (autores: Erich Gamma, Richard Helm,
Ralph Johnson y J ohn Vlissides). Al grupo de estos cuatro arquitectos de software se
les conoce como La banda de los cuatro (The Gang of Four o GoF). Cuando en textos de
software leamos que el diseño de la arquitectura sigue los principios de GoF, sabremos
que se siguen los patrones de diseño indicados en dicho libro .
Un patrón de diseño es aquel en el que queremos una instancia única de una clase.
El patrón singleton se encarga de este cometido. No queremos que los programadores
pued an crear tantas instancias de la clase como quieran haciendo uso d e la instrucción
new. Sólo debe haber una instancia de dicha clase en la aplicación (un solo objeto).
¿Cómo se consigue esto?:
• Mediante un constructor privado que sólo podrá usar la propia clase.
• Mediante un método estático "getlnstancia " que devolverá la única instancia de
la clase .

© Alfaomega - Altaria 1611


Curso avanzado de Java

Veamos cómo implementar el patrón de diseño singleton. Creemos un proyecto JSE.


Para probar el correcto funcionamiento del singleton crearemos una aplicación multi-
hilo en la que accederemos a un contador como el anterior. Pero esta vez el valor del
contador será el mismo para los distintos hilos de la aplicación.

Veamos el código de la clase singleton:


package singletonapp;
public class Singleton {
private int contador;
private static Singleton instancia = null;
private Singleton() {
this.contador =O;
}
public static Singleton getlnstancia() {
if (instancia == null) {
instancia = new Singleton();
}
return instancia;
}
public int getContador() {
contador++;
return contador;
}
}

Tendremos tres hilos en la aplicación que harán uso de la clase singleton. Veamos
el código de los hilos:
package singletonapp;
public class Hilo extends Thread {
private Singleton instancia;
private fina l static int MAX = 100;
public Hilo(String nombre) {
super(nombre);
this.instancia = Singleton.getlnstancia();
this.start();
}
public void run() {
try {
int valor= O;
while (valor<= MAX) {
System.out.println( 11 Valor obten ido por el hilo 11

+ t his.getName() + 11 : 11 +valor+ 1111 . ) ;


Thread .sleep( 500);
synchronized (instancia) {
valor = insta ncia.get Contador();

1162 © Alfaomega - Altaria


JEE. Manual práctico

}
}
} catch (Exception e) {
System.out.println("Se ha producido un error");
}
}
}

Y ahora veamos el código del programa principal que echa a andar la aplicación:
package singletonapp;
public class SingletonApp {
public static void main(String[) args) {
Hilo hilo1 =new Hilo("primero");
Hilo hilo2 =new Hilo("segundo");
Hilo hilo3 = new Hilo("tercero");
try {
hilol.join();
hilo2 .join();
hilo3.join();
} catch (Exception e) {
System.out.println("Se produjo un error");
}
}
}

Vemos que el programa crea tres hilos. Cada uno de estos hilos, en su constructor,
llama al método "getlnstancia" del singleton. En dicha instancia estará el valor del
entero que será compartido por toda la aplicación. Si en vez de llamar al método "ge-
tlnstancia()" llamáramos al constructor con new, cada hilo tendría su propio valor del
contador. Y esto es precisamente lo que no queremos. Veamos la salida del programa:
Va lor obtenido por el hilo tercero: O.
Va lor obtenido por el hilo segundo: O.
Va lor obtenido por el hilo primero: O.
Va lor obtenido por el hilo tercero: 3.
Va lor obtenido por el hilo primero : 2.
Va lor obtenido por el hilo segundo: l.
Va lor obtenido por el hilo segundo: 6.
Va lor obtenido por el hilo tercero: 4.
Va lor obtenido por el hilo primero: S.
Va lor obtenido por el hilo tercero: 7.
Va lor obtenido por el hilo segundo: 8.
Va lor obtenido por el hilo primero : 9.
Va lor obtenido por el hilo segundo: 10.
Va lor obtenido por el hilo tercero: 11.

© Alfaomega - Altaria 1631


C urso a vanza do de Java

Valor obtenido por el hilo primero: 12.


Valor obtenido por el hilo tercero: 14.
Valor obtenido por el hilo primero: 15.
Valor obtenido por el hilo segundo: 13.
Valor obtenido por el hilo segundo: 16.
Valor obtenido por el hilo primero: 17.
Valor obtenido por el hilo tercero: 18.
Valor obtenido por el hilo tercero: 19.
Valor obtenido por el hilo primero: 21.
Valor obtenido por el hilo segundo: 20.
Valor obtenido por el hilo segundo: 22.
Valor obtenido por el hilo tercero: 23.
Valor obtenido por el hilo primero: 24.
Valor obtenido por el hilo tercero: 26.
Valor obtenido por el hilo segundo: 27.
Valor obtenido por el hilo primero: 25.
Valor obtenido por el hilo segundo: 30.
Valor obtenido por el hilo tercero: 29.
Valor obtenido por el hilo primero: 28.
Valor obtenido por el hilo primero: 32.
Valor obtenido por el hilo segundo: 33.
Valor obtenido por el hilo tercero: 31.
Valor obtenido por el hilo tercero: 34.
Valor obtenido por el hilo primero: 35.
Valor obtenido por el hilo segundo: 36.
Valor obtenido por el hilo primero: 37.
Valor obtenido por el hilo segundo: 38.
Valor obtenido por el hilo tercero: 39.
Valor obtenido por el hilo tercero: 42.
Valor obtenido por el hilo primero: 41.
Valor obtenido por el hilo segundo: 40.
Valor obtenido por el hilo segundo: 43.
Valor obtenido por el hilo primero: 45.
Valor obtenido por el hilo tercero: 44.
Valor obtenido por el hilo segundo: 48.
Valor obtenido por el hilo primero: 47.
Valor obtenido por el hilo tercero: 46.
Valor obtenido por el hilo primero: 50.
Valor obtenido por el hilo tercero: 49.

Vemos que existe un pequeño desfase entre el valor obtenido por el hilo y el mo-
mento en que se muestra en pantalla. Pero ésta es precisamente una característica
de la programación concurrente. Cada hilo se ejecuta de forma independiente, y no
secuencialmente, compartiendo únicamente la memoria de la aplicación .

1164 © Alfaomega - Altaria


JEE. Manual práctico

El propio servidor de aplicaciones nos ofrece el patrón singleton implementado.


Dicho patrón no es más que un EJB. El EJB se anota con "@Singleton". Imaginemos
una aplicación web en la que queremos mantener a nivel de aplicación el número de
peticiones HTTP realizadas al servidor (mediante el método GET o el método POST).
Es un dato compartido por toda la aplicación. Por lo tanto, ¿por qué no implementarlo
con un singleton? Creemos una nueva aplicación empresarial.

CiD
éH!I '* ·~
""'"ses;IOfli:Eirls
t· ~ C.O:.,tz!dcYSinglc:ten.jovo
~···B ~taCo'SingleOOrLocal.java
(ji¡ T<SIP~
(ilhoñcs
(j Teot ü!><ar:e:
ti """"""aeans
[)A Cmfi1wcW'lfiles
· (i E'cYcr RC!WJ«:~
Sln!Jle1~ esar~
~ Wcl>P_.
So..Jrce P~ee
·(i
EJ..m•"""'"'
'···@ COOillá:rServ~tJava
!JI¡ TosiP~es
(j lhariec
(j Test tfofares
ll\ Cmfi1wct.tn files

En dicho proyecto debemos prestar atención al EJB "ContadorSingleton" y al servlet


"ContadorServlet". Veamos el singleton con su interfaz local:

package sessionbeans;
import javax.ejb.Local;
@Local
public interface ContadorSingletonloca l {
public void incrementaContador();
public int getContador();
}

Singleton implementando la interfaz:

package sessionbeans;
import javax.ejb.Singleton;
@Singleton
public class ContadorSingleton implements Contado rSingletonloca l {
prívate int contador;
public ContadorSingleton() {
this.contador =O;
}
@Overrid e
public void incrementaContador() {
this.contador++;

© Alfaomega - Altaria 1651


Curso avanzado de Java

}
@Override
public int getContador() {
return th is.contador;
}
}

Veamos el servlet (sin hacer uso de páginas JSP) para ver cómo usar el s ingleton.
La in stan cia en el s ervidor seguirá esta n do dis ponible a unque cerremos todas las ven-
tanas del navegador :
package servlets;
import java.io.IOException;
import java.io.PrintWriter;
import javax.ejb.EJB;
i m port javax.se rvlet.ServletException;
i m port javax.servlet. http. HttpServlet;
i m port javax.servlet. http. HttpServletReq uest;
i m po rt javax.servlet. http. HttpServletResponse;
im port sessionbea ns.ContadorSingleton Local;
public class ContadorServlet extends HttpServlet {
@EJB
prívate ContadorSingletonlocal contadorSingleton;
protected void processRequest(HttpServletRequest request,
HttpServletResponse response)
throws ServletException, IOException {
contadorSingleton.incrementaContador();
int contador= contadorSingleton.getContador();
response .setCo nte ntType ("text/ht m 1; eh arset= UTF-8"};
try (PrintWriter out = response.getWriter()) {
out.println("<! DOCTYPE htm 1>");
out. println (" <htm 1> ");
out. printl n (" <head> ");
out. println (" <title>Servlet ContadorServlet</title>");
out.println (" </head> ");
out.println (" <body> ");
out.println("<hl>Servlet ContadorServlet at " +
request.getContextPath() + "</hl>");
out.println("<p>EI número de veces que se ha accedido al"
+ " servidor es: "+contador + "</p>");
out. println (" </body> ");
out.println (" </htm 1>" );
}

1166 © A lfaomega - Altaria


JEE. Manual práctico

}
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
processReq uest(request, response);
}

@Override
protected void doPost(HttpServletRequest request,
HttpServletResponse response)
throws ServletException, IOException {
processReq uest(request, response);
}

@Override
public String getServletlnfo() {
return "Descripción del servlet";
}
}

Finalmente veamos cómo accedemos al valor del singleton desde una ventana de
Google Chrome y otra ventana de Mozilla Firefox:

• Google Chrome :

[J Servlet ContadorSeiVIet x

~ -+ C (i) loca lhost:7001JSingletonEmpresarial-war/ContadorServlet

Servlet ContadorServlet at /SingletonEmpresarial-war


El número de vece1.: que !ite b~ acc!!:dido al Sl!:ntidor e!O:: 6

• Mozilla Firefox:

Sf:rvlct Cont:~dotServlct )(

Servlet ContadorServlet at /SingletonEmpresarial-war


El número de veces que se ha accedido al servidor e ¡;;: 1

© Alfaomega - Altaria 1671


Curso avanzado de Java

¿Qué pasaría si implementamos el ejemplo del carrito que vimos en el apartado de


stateful session bean con un singleton? Pues ¡absolutamente nada! Porque, precisamen-
te, al ser siempre el mismo cliente, usamos dicho stateful como si fuera un singleton.
El EJB: "@Stateful".
public class Ca rritos implements ICa rritosl ocal, ICarritosRemote {

}
Lo redeclaramos de la siguiente forma:
@Singleton
public class Carritos implements ICarritosl ocal, ICarritosRemote {

}
El funcionamiento de la aplicación no se altera en absoluto.

5.4.4 Beans orientados a mensajes


El Servicio de Mensajería de Java (Java Messaging Service o JMS) es la solución
propuesta para las comunicaciones asíncronas en la JEE. Evidentemente, y como parte
de la JEE, se nos ofrece un conjunto de clases dentro de la API para poder trabajar
con dicho sistema. Los beans orientados a mensajes son la solución empresarial para
recibir los mensajes.

La a rquitectura de JMS propone dos esquemas de conexiones:

• Conexión punto a punto. La cual permite un canal de comunicaciones en la que


sólo puede haber un emisor y un receptor. El emisor debe configurarse según
los ajustes del servidor de aplicaciones. El receptor será el MDB que habremos
configurado para dicho canal.

• Conexión por tema/suscriptor. Este esquema permite múltiples receptores para


un mismo emisor, siempre que los receptores se hayan suscrito al tema para el
cual el emisor envía mensajes. Tendremos varios MDB por cada tema.

En el servidor de aplicaciones es necesaria una infraestructura concreta para poder


hacer uso de JMS. En concreto necesitamos dos factorías (fábricas) de conexiones.
Una fábrica para las conexiones punto a punto y otra fábrica para las conexiones por
tema. De dichas factorías obtendremos las sesiones que se enlazarán con los canales,
que tendremos que setear en el servidor de aplicaciones.
¿Qué viene ya seteado en Oracle WebLogic Server y qué hay que configurar? Recor-
demos siempre que WebLogic es el servidor de aplicaciones que se ha elegido en este
manual por su robustez, pero debemos estudiar las diferentes opciones en el servidor
que elijamos para nuestra aplicación. En Oracle WebLogic Server tenemos por defecto
creadas dos fac torías. Observemos el árbol JNDI de nuestro dominio:

1168 © Alfaomega - Altaria


JEE. Manual práctico

S·javax
S·jms
' .
'
:--QueueConnectionFactory
'
•--TopicConnectionFactory
1±1--tra m;a cti on

Por lo tanto, en WebLogic no será necesario crear dichas factorías. Tendremos que
crear los canales para los mensajes y casarlos con las factorías de conexiones, mediante
las sesiones que abriremos. Cada sesión tendrá su tipo de notificación. Normalmente
en nuestro desarrollo usaremos la notificación en la que el cliente sabrá cuándo debe
recibir un mensaje (cuando el mensaje exista) .
Vamos a crear los canales. Empecemos viendo un ejemplo de conexión punto a
punto. Accediendo a la consola de administración de WebLogíc, en concreto al panel de
administración de la estructura del dominio, seleccionamos la opción "Módulos JMS".

de Dominio

:··servi,dores m s
;--A,gerotes de Store-and-FoiWard

¡::~~~~de~IRut~ de Acceso
t··(lrig<,nes de Datos
;--JUm.acenes Persistente.s

Seleccionando el módulo "WseeJmsModule" (WebLogic Server Enterprise Edition JMS


Module}, veremos los canales disponibles. Queremos crear un canal para conexiones
punto a punto (tipo cola). Seleccionamos crear un nuevo canal (tipo de recurso), y luego
seleccionamos el de tipo "Cola ".

Cl'eaaóode Uue\10 Recurso de t-1óduJodcl Sistema ck JI'IS

kV._ c:l tapo 4e: teCJt~ qu-e .u:l«eo~~e, Ulle ~ohcta que l.vroclia ro •nft~tml!co&n !l:i~ fN~O"C:.M d teollluo, 1111,., u:cut~J p:t~!ll o ~~~ ck1:."J,o, C<U!:'Io col" ~ y lemM :mtÓnOII'on, t ~btu~~ le con.e<IOt"Jd, «~1:.' y
: ~ c!btl!lllldos. SCN 6orc' u: n~ y~tnos eSe S/..li ~ JMS, Uri)l~n u~de 3cxtdet ' o101n~ 6e<flreccon:~mtcnto ~ ~~ ' ele<O)Mr ~1110sde sen~ flor ;adecu;¡d(tll. 11mblt!n o~ ~:OO.ll'tt<~~ ~ble~
de 4etino <t1t1 dcjpllc91Jn ~wn.U ñ~ q11e n un ~.,;,o .v~ n.udo 11• r• •9• U~>•• ecc;•$':1$ ~ ~~lo )MS v leo;: mlcenbe ~W <:11 u:o.erw' de ,~<d4or.

Od"ine an.~de p.tiomcbos do: contigu1ación de ~ 1J1e s-.e utiliu n p;,r.


<Je6t co'"lbW!es l)olrla di entes de J NS. M5s 11\formwó::.-

Defino: 'Al. 5~o do: & stino & p.mto o: punto, q..t-: :;o: U'Sizo:. ~ ~o telliiiJniucio~s do:
peer .w·~ tl'l men,s.,)Je entreQ."~do D uno ull11 s.e dts:l,tw.-e sól~ a un
<onsurid:r. Mk Enfcunad6n_

O T~ Oefine \11'1 \l pode ~no ~ PUbli(a(4ón{W::;.cr1PO ón, Q'"Je s.e ..tlk<a para
<e>tnlrir.MJOQC:'iÓi M:tt as11KIOIW$. Un lllfiiSIÜe f!fltleo• • \111 Cm• se <h b ibv)oe
' . ..

Le asignamos un nombre y una ruta en el árbol JNDL En nuestro caso, lo situamos


junto a las factorías de conexiones ("javaxjjms").

© Alfaomega - Altaria 1691


Curso avanzado de Java

1J.rb 1 Ls,uer:cJ IITO'IImoVJ llCi11"1Ccllll l

1PuntoPunto

Hornb~ de J NDI:

1Ninguno ' 1

Elegimos el servidor JMS (depende del servidor concreto). En nuestro caso "Wsee-
JmsServer".
&e~dl:lll de t ... wo ~ ok l ...... okl ~l!ll•lllc JttS

W~>J ~ IL.~&es~l~
1'"' .-..:.-..-.,~ -w.... , -~~~ -II:.O.I.•Ioo_,_._doo"""""'" ... ··- · I M~.

••<0.,.. ..... · -"-


~il<• oor. ..... ,..,• ...,__• .,..,.... M tli••.. oooonJone ,...-, ••
~ ~ ,r.,.., l..,. do.a..,••'"'"'~-•• - -·•---';':,
Gnl'l" o u..• -clr ~""' ~· •
11 nlf..,r!\YIM - Mi 6r uteft,.~~t ~ ·
,:p;;rtc de ~t, $1 e. ~n" 1'~ Ctt"fo/ ' " " ' - rtnllllctUII«<IIf<nn lltar..clo dll eo ti! b~ ONo .,....
"'• _,.~ róelllt~ ~~ Ir. l')fllll ~ ~He'"" 6e d«:l:ei~ 1t«.nc- s 6el II'M5h !#!,..,,~.
------------
\i
$det00111!
-----~---------------------------·---------
~l!O)~~t< ~WOI'f l'aoMIIUl U r. SI MleanM (11t$J'Jt0\. N:J ~ ~fiP:>)Íitd~f:O-I'IIIIIt!J,

~n.)eQIOCI.~ f eEA JMS NOO UlE SVBOEPLOYIIEIH \9SEEJU.SS..-rtr .. Jc....-... -~~1

iQ..f ~'IH$ ~ ·fi)..... ~~~~W.,...<.hllt<>

.,.,........ :

s;.........._. ,"t
O w.;n,'•••O•,...~
1$ -.~

o ~,.."~

[ <>0.) ~ ILT~i ,,,__,

Aprovechando que estamos en la consola de administración, crearemos también el


canal p ara las conexiones de tipo tema o topic. Crearemos un tipo d e recurso "Tema".

Creación de Nuevo Recurso de f-1ócfulo del Sistema de JI-1S

I IIIJás 1 1Si¡uiente 111 Teoninar 111cancelad

Seleccione el tipo de recurso que desea crear.

utilice esta.s piai11as para crear rec\I'Sos en un módulo de si$tem~ de Jr-ts:. como colas. temas. plantillas yfá~ritas de co11exiones.
Seg1i~ t i tipo de r~cun: o que ~e!e«ione, le le ~ali eit111 que introduu.- inform,.ci6n b~.s-i c,. p1111111 Utlllr el rtc,¡rso, Par,. recur~M po.d bles de
lemat c!i~ribuido!:, ::etvidoret aj eno~ ydutinot de SAF de JMS, también puede aoeeder a páginH de di :e<don;~~miento para ::elecclonar
de dt~tino con dc~pli cguc$ !.:ecundarios, que es un mecilnir;rno 3-nruado p aro:t;agrup¡¡r rccui"!;Ol de 11\0dulo JP.IS y los rnembro$ en rccul"!;

O Fábrici de Conexiones Define.u


crear co

O colo Define u
Deer a$Í
con.sumi

Define u
corn.mic
a todot 1

O Cola Distribuida O!fine u


se pued
Informa

1170 © A lfaomega - Altaria


JEE. Manual práctico

Le asignamos un nombre y una ru ta JNDI.

Prop~dades de oesuoo de JMS

Las sigajentes propiadacles ~e utili2arán p;na id!!ltificcr el nue-10 Tema. 9 módulo ad:ual es \VseeJmsNodule.

* Nombre:

Nombre! de JNOl : 1jevex/jms/Temal


Plantilla:

Volvemos a elegir el servidor JMS. En n u estro caso "WseeJmsServer".

Creac:::i611 de lluewo RecurSO ck H6du.b del Sisten.:. de JHS

1~1r.i:d I~I II Tmnrw l llcwx:e~• l


..
Las slotliotlliles PfO~ se utilllatan ,.,,. dliriQiir el • uevo rec• rso de oiiSd:uto dt sistenla de JWS..

utilite l'$1 11 pi.gi~• pa;a ~decvunar un do~li egue ~I.J"'IIIIno IJ4 1&~"'c' o le rec:ur~u de mOdulo de J;l$lf'me.. lb doP'rttpJe -.ewmlonu le$ un meot:ti~mo mect~
dlngcn;, Un.) tn:;'!olll(&l ck $<Mdoo", dastero OlGCnte de SAF. 5I o~~. puede ucvr un nuevo d~!lll ~~uc..sc~no h.xlendo d i( en el botónO't~ r loiiJC'W'o t
a confla¡¡rar 11>$ 4~-,w.. fi ~lllit~IR ¡eo.md.Jr.o mo4 Jdd.ant~ mtel..,.tc '" o:ieiniol de ~6n ® d~pl ito\l t$ M~O$dd rn6Q.IIo orintip¡¡l,

Sd ctCtelll(' ~ do;¡li'~guc ~~lo •tllll!! rf<t.}eJ!I uU!o:.,r. ~ ~d f<OCIII•(•"~<:J"Ol. n.o J<! jltocliu::lfti lliii!JÚI'I do~~oonc.. e<1to,

t~'f!llei:IIX:I St:eund,nos: ( BEA JMS MODULE SUBDEPLOYME~IT WSEEJMSSer.•er • liC.,...otl"luovo {)O;e;J'II!?QUO~I

i Que de-i1inM dewa •"""'"' • d~ d~ph eg~;<e .Jen.mdlno}

Ot:stiiiOS :

Sc.rftd<tn:• JHS

O W~ a~W"WnnSc:I'Y'II:r

® w_&«) n,.sr_f"'ltt

O wseesoapJIRSJ~M:Strwr

! ;.uu J I~I II rumnar f llcwd.vl

Veamos cómo quedan los recursos JMS con los que tra baj aremos:

~ ~rScn.ital r ei:ta Tibliil

_..
~utnea llk AeoJrsos

1Hl.kiO 11St..ii)C'hit 1 Mosttandola<ld'~~ Aocenor l.s•a•u nte

ltl .,.......
--·
T. . ~delrtDI oes:p~teoue se~no

o Colo if\'a~l\lntol\lnto 92AJI'43_('()QJI.E_SUOD&lO'íKSfl_WSEEJrASSerYCr \'l~c.)ms$(1".'0"

o T..,. Te"' )W~~CmiJ f);A,.JMS_t«)QJlE_SU6l)BI'l()'lla1ff_VISEEJt-t>SerYCr W~cJINSCI"'II:'

o Wsc:eCollb.l6.Quc....: Colo '!'fCtiOOJI(,vtiCc:.Ocf\lultCallbockQucuc €EA_.IM3J«)I)Jt.E_St..IOD6"1.0n (8fr_VISEEJt~SerYCr W~cJINSc:r"ICI'

o \\'$td'le».3~JCQ•.euc: c... wcblog¡t.vtice.Ocf\lultQucue !I;VOfS.J«)I)JlE..$U)Qil>WI1<8fl'.YISI;E)1<6Sc" '' W$CeJIN$crvct

1~ 1 .....'""'1 M~ anclo 1 a .f e!• -1 AllteiiOt 1Sl gul~te


-

© Alfaomega - Altaria 1711


Curso avanzado de Java

Desarrollaremos una sencilla aplicación empresarial en la que mostraremos una


entrada de texto al usuario. El servlet que reciba la petición será el productor del men-
saje, que lo enviará a través del canal "PuntoPunto" que acabamos de crear y el MDB
suscrito al mismo canal será el que lo reciba. Los artefactos software de este ejemplo
son los siguientes:
• Página "index.jsp" en la que el usuario introducirá su entrada:
<%@ page contentType="text/h t m 1" pageEncod ing=" UTF-8"%>
<!DOCTYPE html>
<html>
<head>
<meta http-eq uiv="Content-Type" content=" t ext/htm 1; eha rset=UTF-8">
<title>JSP Page</title>
</head>
<body>
<form action="Pu ntoPu ntoServlet" method="post">
<table>
<tr>
<td>lnserte el texto:</td>
<td><input type="text" name="entra da"
size="40" maxlength="40" /></td>
<td><input type="submit" /></td>
</tr>
</table>
</form>
</body>
</html>

• "PuntoPuntoServlet.java". Recibe la entrada del usuario, genera el mensaje y lo


envía a través del canal, usando la factoría de conexiones:
package servlets;
import java.io.IOException;
import java.io.PrintWriter;
impo rt javax.annotation.Resource;
import javax.jms.Connection;
im port javax.jms.Connection Factory;
import javax.jms.J MSException;
import javax.jms.Message;
import javax.jms.MessageProducer;
import javax.jms.Queue;
import javax.jms.Session;
im port javax.jms.Text M essage;
i m port javax.se rvlet.ServletException;
im port javax.servlet.http.HttpServlet;
im port javax.servlet.http.HttpServletReq uest;

1172 © Alfaomega - Altaria


JEE. Manual práctic o

im port javax.servlet. http. HttpServletResponse;


public class PuntoPuntoServlet extends HttpServlet {
@Resource(mappedName = "javax.jms.PuntoPunto")
prívate Queue puntoPunto;
@ Resource(mappedName = "javax.jms.QueueConnectionFactory")
prívate ConnectionFactory connectionFactory;
prívate Connection connection = null;
prívate Session session = null;
prívate MessageProducer producer = null;
prívate Message message = null;
prívate boolean ok = false;
protected void processRequest(HttpServletRequest request,
HttpServletResponse response)
throws ServletException, IOException {
String entrada= request.getParameter("entrada");
try {
sendJ MSM essage ToPuntoPunto( entrada);
ok = true·
'
} catch(Exception e) {
System .out.println(e .getM essage() );
ok = false;
}
response. setCo nte ntTyp e ("text/h tm 1; eh arset= UTF-8");
try (PrintWriter out= response.getWriter()) {
out.println("<! DOCTYPE htm 1>");
out.printl n("<html>" );
out.printl n(" <h ead>" );
out.printl n(" <title>Servlet M iServlet</title>" );
out.printl n(" </head>");
out.printl n(" <body>" );
if (ok) {
out.println("<hl>EI mensaje "+entrada +
"se envió al MDB ... </hl>");
} else {
out.println("<hl>No se pudo enviar el mensaje"+ entrada+
"al MDB ...</hl>");
}
out.printl n("</body>");
out.printl n(" </htm 1>" );
}
}
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
processReq uest(request, response);
}

© Alfaomega - Altaria 1731


Curso avanzado de Java

@Override
protected void doPost(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
processRequest(request, response);
}
@Override
public String getServletlnfo() (
return "Información del servlet";
}
prívate void sendJMSMessageToPuntoPunto(String mensaje)
throws JMSException {
setM essageProd ucer();
message =createJMSStringMessage(session, mensaje);
prod ucer.send( m essage);
}
prívate Message createJMSStringMessage(Session session, String text)
throws JMSException {
TextMessage msg = session.createTextMessage();
msg.setText(text);
return msg;
}
prívate void setMessageProducer() throws JMSException {
if (producer == null) {
connection = connectionFactory.createConnection();
session =connection.createSession(false, Session.CLIENT_ACKNOWLEDGE);
producer = session.createProducer(puntoPunto);
}
}
}
• Con las líneas:
@ Resource(mappedName = "javax.jms.PuntoPunto" )
prívate Queue pu ntoPunto;
@Resource(mappedName = "javax.j ms.QueueConnection Factory")
prívate ConnectionFactory connectionFactory;

Realizamos la llamad a (inyección de d ependen cias) a n u estro canal. El código que


se en carga d e envia r el m en saje a la cola es el siguiente:
prívate void sendJMSMessageToPuntoPunto(String mensaje)
throws JMSException {
setMessageProducer();
message = createJMSStringMessage(session, mensaje);
producer.se nd( m essage );
}
prívate Message createJMSStringMessage(Session session, String text)
throws JMSException {

1174 © A lfaomega - Altaria


JEE. Manual práctic o

TextMessage msg = session.createTextMessage();


msg.setText(text);
return msg;
}
private void setMessageProducer() throws JMSException {
if (producer == null) {
connection = connectionFactory.createConnection();
session = connection.createSession(false, Session.CLIENT_ACKNOWLEDGE);
=
producer session.createProd ucer(pu ntoPu nto );
}
}

Para enviar el mensaje, primero creamos el productor, preparamos el mensaje y acto


seguido lo enviamos. En dicho código llama la atención la instrucción en la que creamos
la sesión: "session = connection.createSession(false, Session.CLIENT_ACKNOWLEDGE);".
A la instrucción "createSession" le pasamos dos parámetros. El primero sirve como una
instrucción begin de una transacción, en la que posteriormente se d eberá ejecutar com-
mit o rollback (true); o si el propio canal se encarga de administrar los mensajes (false).
La constante "Session.CLIENT_ACKNOWLEDGE" indica un método de acuse de recibo en
el que el cliente reconoce un mensaje consumido llamando al método "acknowledge()".

Los posibles valores para esta constante son:

• Session.AUTO_ACKNOWLEDGE.
• Session.CLIENT_ACKNOWLEDGE.
• Session.DUPS_OK_ACKNOWLEDGE.

• Session.SESSION_TRANSACTED.

Se deja al lector profundizar sobre este asunto.

• El bean "PuntoPuntoBean" queda como sigue:


package beans;
im port javax.ejb.ActivationConfigPro perty;
import javax.ejb.MessageDriven;
import javax.jms.Message;
import javax.jms.Messagelistener;
import javax.j ms.TextMessage;
@MessageDriven(activationConfig = {
@ActivationConfigProperty(propertyName ="destinationlookup",
propertyValue = "javax.jms.PuntoPunto"),
@ActivationConfigProperty(propertyName = "destinationType",
propertyValue = "javax.jms.Queue")
})
public class PuntoPuntoBean implements Messagelistener {
public PuntoPuntoBean() {}

© Alfaomega - Altaria 1751


Curso avanzado de Java

@Override
public void onMessage(Message message) {
TextMessage msg = (TextMessage) message;
String text = "";
try {
t ext = msg.get Text();
} catch (Exception e){}
System.out.println("En el bean tenemos: "+text+'"' .);
}
}
Vemos cómo se configura el bean mediante la anotación:
@MessageDriven(activationConfig ={
@ActivationConfigProperty(propertyName = "destinationlookup",
propertyVa lue = "javax.jms.PuntoPunto"),
@ActivationConfigProperty(propertyName "destinationType", =
propertyVa lue = "javax.jms.Queue")
})

La recepción se realiza en el método "onMessage", de forma asíncrona, ejecutable


cada vez que se establezca un mensaje en la cola.

D JSPPage x

C (j) localhost:7001/EjemploJMS-war/

Inserte el texto: j Er~v iando un mensa¡e al servidor...


¡ 11 Emviar 1

El mensaje Enviando un mensaje al servidor... se envió allVIDB...

La salida en la consola del s istema del servidor es la siguiente:

En el bean tenemos: enviando un mensaje al servidor...

Dejamos al lector que realice la prueba de poner otro bean a la escucha del canal de
tipo cola. Como nota, y recordando que dicho canal es para un emisor y un receptor,
mostramos la salida por consola:

En el otro bean tenemos: mensaje en la cola ...

La entrega del mensaje se realiza únicamente a un bean.

1176 © Alfaomega - Altaria


JEE. Manual práctico

Para finalizar con los MDB, y siguiendo la misma filosofía, veamos el código de un
bean de tipo topic ("Tema"). Pondremos un servlet emisor y dos beans suscritos a su
tema.
• Servlet "TemaServlet.java":
package servlets;
ímport java.ío.IOException;
ímport java .ío.PríntWríter;
ímport javax.annotation.Resource;
ímport javax.jms.Connection;
import javax.j ms.ConnectionFactory;
ímport javax.jms.JMSException;
ímport javax.jms.Message;
ímport javax.j ms.MessageProducer;
ímport javax.jms.Sessíon;
ímport javax.jms.TextMessage;
import javax.j ms.Topic;
import javax.servlet.ServletException;
ím port javax.servlet. http. HttpServlet;
ím port javax.servlet. http. HttpServletReq uest;
ím port javax.servlet. http. HttpServletResponse;
publíc class TemaServlet extends HttpServlet {
@ Resource(mappedName = "javax.jms.Tema")
private Topíc tema;
@ Resource(mappedName = "javax.jms.TopícConnectionFactory")
prívate ConnectionFactory connectionFactory;
prívate Connection connection = null;
prívate Sessíon sessíon = null;
prívate MessageProducer producer = null;
private Message message = null;
prívate boolean ok = false;
protected voíd processRequest(HttpServletRequest request,
HttpServletResponse response)
throws ServletException, IOException {
response. setCo nte ntTyp e ("text/htm 1; eh arset= UTF-8");
Stríng entrada= request.getParameter("entrada");
try {
se ndJ MS M essage To Tema (entrada);
ok = true·1
} catch (Exception e) {
ok = false;
}
try (PríntWríter out= response.getWríter()) {
out.príntln("<! DOCTYPE htm 1>");
out.príntl n("<htm 1>" );
out.príntl n("<head>" );

© Alfaomega - Altaría 1771


C urso a vanzado de Java

out. println (" <title>Servlet Te m aSe rvlet</title>" );


out. println (" </head> ");
out. println (" <body> ");
if( o k) {
out.println("<hl>EI mensaje " +entrada +
"se envió al MDB ... </hl>");
} else {
out.println("<hl>EI mensaje " +entrada +
"no se pudo enviar al MDB ... </hl>");
}
out. println (" </body> ");
out. println (" </htm 1> ");
}
}
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
processRequest(request, response);
}
@Override
protected void doPost(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
processRequest(request, response);
}
@Override
public String getServletlnfo() {
return "Información del servlet";
}
private void sendJMSMessageToTema(String mensaje) throws JMSException {
setM essageProd ucer();
=
message createJMSStringMessage(session, mensaje);
prod ucer.send( m essage);
}
private Message createJMSStringMessage(Session session,
String text) throws JMSException {
TextMessage msg = session.createTextMessage();
msg.setText(text);
return msg;
}
private void setMessageProducer() throws JMSException {
if (producer == null) {
connection = connectionFactory.createConnection();
session =connection.createSession(false, Session.CLIENT_ACKNOWLEDG E);
producer = session.createProducer(tema);
}
}
}

La filosofia es la misma. Veamos los dos beans que vamos a tener a la escucha:
• "TemaBean.java":

1178 © Alfaomega - Altaria


JEE. Manual práctico

package beans;
im port javax.ejb.ActivationConfigPro perty;
import javax.ejb.MessageDriven;
import javax.j ms.Message;
import javax.jms.Messagelistener;
import javax.jms.TextMessage;
@MessageDriven(activationConfig = {
@ActivationConfigProperty(propertyName = "clientld ",
propertyValue = "javax/jms/Tema"),
@ActivationConfigProperty(propertyName = "destinationlookup",
propertyValue = "javax/jms/Tema"),
@ActivationConfig Property( propertyNam e = "subscription Du rability",
propertyValue = "Du rabie"),
@ActivationConfigProperty(propertyName = "subscriptionName",
propertyValue = "javax/jms/Tema"),
@ActivationConfigProperty(propertyName = "destinationType",
propertyValue = "javax.jms.Topic")
})
public class TemaBean implements Messagelistener {
public TemaBean() {
}
@Override
public void onMessage(Message message) {
TextMessage msg = (TextMessage) message;
String text = '"';
try {
text = msg.getText();
} catch (Exception e) {}
System.out.println("En tema bean tenemos: "+text+"".);
}
}

• Bean "OtroTemaBean.java" :
package beans;
im port javax.ejb.ActivationConfigP roperty;
import javax.ejb.MessageDriven;
import javax.j ms.Message;
import javax.jms.Messagelistener;
import javax.jms.TextMessage;

@MessageDriven(activationConfig = {
@ActivationConfigProperty(propertyName = "clientld ",
propertyValue = "javax/jms/Tema2"),
@ActivationConfigProperty(propertyName = "destinationlookup",
propertyValue = "javax/jms/Tema"),
@ActivationConfig Property( pro pe rtyNa m e = "subscription Du rability",
propertyValue ="Durable"),
@ActivationConfigProperty(propertyName = "subscriptionName",
propertyValue = "javax/jms/Tema"),

© Alfaomega - Altaria 1791


C urso a vanzado de Java

@ActivationConfigProperty(propert yName = " destinationType",


propertyVa lue = "javax.jms.Topic")
})
public cla ss OtroTemaBean implements M essagelistener {
public OtroTema Bean() (
}
@Override
public void onMessage(Message message) {
TextMessage msg = (TextMessage) message;
String text = "";
try {
t ext = msg.getText();
} catch (Exception e){}
Syst em.out.println("En otro tema bean tenemos: "+text +"".);
}
}

La a n otación del bean se realiza con el s iguien te código:


@MessageDriven(activationC onfig = {
@ActivationConfigProperty(propertyN ame = "clientld",
propertyVa lue = "javax/jms/Tema"),
@ActivationConfigProperty( propertyName = "desti nation Looku p",
propertyVa lue = "javax/jms/Tema"),
@ActivationConfigProperty(propertyName = "su bscription Dura b ility",
propertyVa lue = "Durable"),
@ActivationConfigProperty(propertyName = "subscription Name",
propertyVa lue = "javax/jms/Tema"),
@ActivationConfigProperty(propertyName = "destinationType",
propertyVa lue = "javax.jms.Topic")
})

Dejamos aJ lector investigar sobre estos valores (aunque son bastante intuitivos). La
salida por consola es la sigu iente:
En tema bean tenemos: enviando mensaje a canal tipo tema...
En otro tema bean tenemos: enviando mensaje a canal tipo tema ...

5 .5 Ejercicio 4
Como ejercicio, se plantea desarrollar la capa de servicio de la aplicación para crear
consistencia en el back-end de nuestra aplica ción. La capa de servicio trabajará con los
s iguientes objetos del modelo del dominio (una capa superior al modelo d e las en tidades).

1180 © A lfaomega - Altaria


JEE. Manual práctico

BlogPoot
Chateacon
· id: Long;
• 1ecna1ns1ante: Slnng; usuario: Stnng:
• contenido: String:
• au~or : String;
con: Sumg;
- comemarios: List<ComentarioPost>;
• gQttGrs & sotters;
+ oetters 8 setters:

-
MensajeChat

ComontarioPoc1
· lecnalnslanle: String;
. tonten ~do : String;
- enviadoPor: StrL1g;
fechalnstanle: Strina:
· comentario: String;
· enviaaoA: StrL1g;
usuauo: Slnng;
• geners & seners:
a-get!ers &. setters;
J
Y para convertir dichos objetos del dominio en entidades d e la base d e d atos, n ecesi-
tamos una API en la capa de servicio. Dicha API queda diseñada de la s iguiente forma:

.,
l os métodos devuelven la excepció n ' BiogExce ption", indicando los errore s en la aplicación.

~IB iog"

• crearus u:ario(us u:ario: String): bool e:an;


• existeUsuario(usuario: String): Long; lf Devuelve id o · 1l.
• obtenerToclosUsuarioso: List<String>;
• crearPost(po st: BlogPost): Blog Post: 11 devuelve con id y con fecha
• obtenerPostPorl d(postld: long): BlogPost; 11 devuelve lo s campos seteados
• anadirComentarioPost(post: BlogPosl, comentario: Comentario Post): Comentario Post; 11 fechalnstante seteado
• abreC hatCon(chatea: ChateaCon): boolean;
• obtenerChatActuaiUsuario(usuario: String): String;
• envlaMensajeChat(mensaje: MensajeChat): MensajeC hat; lffech:alnstante seteado
• obtenerToclosLosPostsO: List<BiogPost>;
• oblenerTodoslosMensajes Chat(usuario1: Slring, usuario2: String): List<Mensaj eChat>;
,
1

«BiogS ef\IÍc eloe al» <<BiogServiceRemote>>

.... .,...
'
·--------- .. .-----------·'
''
BlogSeNice

Stateless Session Bean

Se plantea al lector la implementación de dicha capa de servicio, creando las clases


y métodos de utilidad que considere necesanos.

© Alfaomega - A ltaria 1811


,

Implementación CAPITULO

de MCV: JSF

JSF es una implementación propuesta para gestionar el patrón MVC en aplicaciones


web. ¿Qué ocurre si usamos servlets y páginas JSP para implementar el controlador
y las vistas de nuestra aplicación? No ocurre nada. Lo podemos usar perfectamente.
Pero, siempre que se desarrolla un framework para una tecnología, se pretende dotar
al desarrollador de un conjunto de herramientas que hagan más fácil y menos tediosa
la escritura del código, haciendo que se centre mayormente en la lógica de negocio de
su aplicación.
Si pretendemos desarrollar una aplicación web usando servlets y páginas JSP, debe-
mos estar pendientes de tener siempre correctamente seteados los JavaBeans de sesión
o de aplicación que vimos en el capítulo correspondiente. Cada acción del usuario que
afecte al modelo de nuestra aplicación debe enviarse a un servlet; éste será el encar-
gado de procesar la petición, hacer la llamada correspondiente a la capa de servicio,
tratar la respuesta del back-end (incluidas las excepciones) y setear correctamente los
J avaBeans antes de despachar la vista (página JSP) correspondiente. Ya que tenemos
componentes software en el back-end (EJB), ¿por qué no tenemos también componentes
de la capa de usuario que sean fáciles de administrar? Ésta es precisamente la idea
de JSF (acrónimo de Java Server Faces: Vistas del Servidor Java). Dicha tecnología
pretende que la interfaz de usuario esté compuesta por un conjunto de componentes.
Cada componente tiene lo que llamaríamos un bean de respaldo o bean gestionado (ma-
naged bean). La versión actual de JSF es la 2.2 e incluye una serie de características:
• Conjunto de componentes de la interfaz de usuario.
• Librerías de etiquetas para hacer más fácil la escritura y lectura del código JSP,
evitando los scriptlets.

• Beans administrados .

© Alfaomega - Altaria 1831


Curso avanzado de Java

• Modelo de eventos.

• Soporte para HTML5/CSS3.


Y muchas más características. El mundo de JSF requiere un manual en sí, por lo
tanto, nosotros nos centraremos en las características generales de JSF. Además, el
propio JSF tiene varias subimplementaciones, que hacen mucho más rico el conjunto
de componentes que podemos usar. Estas subimplementaciones del framework son
las siguientes:
• RichFaces.

• OpenFaces.

• PrimeFaces.
• jQuery4Jsf.

• lceFaces.

Cada cual le aporta su riqueza al framework. Nosotros nos centraremos principal-


mente en JSF en sí, pero es un maravilloso mundo en el que el lector puede adentrarse
para desarrollar interfaces de usuario profesionales de sus aplicaciones empresariales .
Centrémonos en estudiar las generalidades de JSF.
¿Por qué es JSF una implementación de MVC? Porque actúa como núcleo de dicho
patrón. El modelo estará representado por los stateless session beans que residirán en
el conte n edor de EJB. Con dichos EJB seremos capaces de crear una API sin estado
que podremos llamar desde el controlador. En la arquitectura de servlets + JSP, el
servlet es el controlador y las páginas JSP son las vistas. Dicha tupla servlet + JSP se
ha convertido ahora en la tupla componente + managed bean.

6.1 Managed beans


Los beans administrados son los que nos permiten setear las propiedades que quere-
mos mostrar al usuario, esto es, aquellos datos con los que trabajarán los componentes
UI (User Interface). Al igual que ocurría con los JavaBeans, los managed beans tienen
sus ámbitos de existencia o ciclos de vida (aquellos en los que las variables permanecen
inalteradas para el usuario). Veamos los ámbitos de estos beans:

• Dependent. Su ciclo de vida depende del ciclo de vida de otro bean (seteado con
la anotación "@ManagedProperty").

• View. El ciclo de vida del managed bean permanece mientras se muestre en


pantalla la misma página JSF.

• Application. Su ciclo de vida se corresponde con el de la aplicación. Estos beans


contendrán datos que serán iguales para todos los usuarios de la aplicación (di-

1184 © Alfaomega - Altaria


JEE. Manual práctico

ferentes sesiones). Su ámbito se corresponde con el de los Application JavaBeans


que vimos en su sección correspondiente.

• Request. Su ciclo de vida se equipara al ciclo de vida de la petición o request.


Esto es, desde que el cliente HTTP lanza la petición por el método GET o el
método POST hasta que la respuesta es enviada a l cliente. Estos tipos de ma-
naged beans nos pueden venir bien para setear campos de formularios que son
enviados al servidor.

• Sess ion. Su ciclo de vida se corresponde con el de la sesión. Serán los más uti-
lizados, ya que cada cliente HTTP tendrá su propio conjunto de variables propias
de la aplicación mientras dure la sesión del usuario.

• Conversation. El ciclo de vida del bean dura mientras se ejecute el inicio y el


fin de una conversación, bien definida con los métodos begin y end.

• Flow. Este tipo de beans se usa para la navegación entre las páginas.

En nuestros ejemplos estudiaremos de forma aplicada los beans cuyo ámbito de


acceso es la sesión (SessionScoped).

6.2 Librerías de etiquetas en las páginas JSF


Tenemos tres librerías de etiquetas. A cada una la referenciamos con un prefijo:

• "h:". Es el conjunto de etiquetas de HTML definido sobre JSF.


• "f: ". Core de JSF. Es un conjunto de etiquetas que ofrecen funcionalidad extra
al código JSF.

• "ui:". Librería de Facelets. Nos permite definir plantillas.


Veamos el conjunto de etiquetas de cada librería. No entraremos a estudiarlas una
por una, pero sí estudiaremos de forma aplicada aquellas que usemos en nuestro código.

Conjunto de etiquetas en la librería de HTML:


• h:commandButton .
• h :commandLink .
• h :dataTable .
• h:form .
• h:graphiclmage .
• h:inputFile .
• h:inputHidden .
• h:inputSecret .
• h:inputText .

© Alfaomega - Altaria 1851


Curso avanzado de Java

• h:inputTextarea .
• h:message .
• h:messages .
• h:button .
• h:link.
• h:body .
• h:doctype .
• h:outputFormat .
• h:head .
• h:outputLabel.
• h:outputLink.
• h:outputText.
• h:outputScript.
• h:outpu tStylesheet .
• h:pane!Grid .
• h:pane!Group .
• h:panelpassthrough.Element.
• h:selectBooleanCheckbox.
• h:selectManyCheckbox .
• h:selectManyListbox.
• h:selectManyMenu .
• h:selectOneListbox .
• h:selectOneMenu .
• h:selectOneRadio .
• h:column .
Conjunto de etiquetas en la librería core (aportan funcionalidad extra):
• f:actionListener.
• f:ajax.
• f:attribute.
• f:attributes.
• f:passThroughAttribute.
• f:passThroughAttributes.

1186 © Alfaomega - Altaria


JEE. Manual práctico

• f:converter.
• f:convertDateTime.
• f:convertNumber.
• f:event.
• f:facet.
• f:loadBundle.
• f:metadata.
• f:param .
• f:phaseListener.
• f:selectltem .
• f:selectltems.
• f:setPropertyActionListener.
• f:subview.
• f:validateDoubleRange.
• f:validateLength.
• f:validateLongRange.
• f:validateBean.
• f:validateRegex.
• f:validateRequired.
• f:validator.
• f:valueChangeListener.
• f:verbatim.
• f:view.
• f:viewParam.
• f:viewAction.
• f:resetValues .

Conjunto de etiquetas en la librería "ui" (Facelets, definición de plantillas):


• ui:component .
• ui:composition .
• ui:debug.
• ui:define .
• ui:decorate .

© Alfaomega - Altaria 1871


C urso a vanzado de Java

• ui:fragment .
• ui:include .
• ui:insert.
• ui:param .
• ui:repeat.
• u1:remove .

Referencia para ver las etiquetas: "http:/ / docs.oracle.com/javaee/7 /".

6.3 Renderizando componentes


Como hemos indicado, cada componente debe tener un bean de respaldo. Hay algunos
componentes que servirán para maquetar y diseñar nuestras vistas. Dichos componentes
deben tener, evidentemente, un bean de respaldo. El componente concreto usará un
booleano en el bean para saber si se renderiza (se muestra) o no.

Una página JSF no es más que una página JSP en la que importamos las librerías
que anteriormente hemos citado, haciendo uso de ellas con los prefijos indicados.
Veamos un primer ejemplo de página JSF, con una imagen en la que mostramos cómo
crear dicha página en NetBeans. A dicha página la llamamos index.xhtml (ésta es la
extensión de las páginas JSF).

Prestaciones Página de Inicio X

lliil Ca•peta...
!@ JSF Manag·ed &ean ...
B"i Id
Cle~" and Build
~ Jav~ Clas.~...

Clea"
m Jav.a P~cka.g e...

Verify
ffi ·
Generate Javadoc
~ Se>si:on Be.a, ...
~ Servf.et...

El código de nuestra página JSF es el s iguiente, en el que podemos ver cómo impor-
tamos las librerías del apartado anterior:
<?xml version='1 .0' encoding='UTF-8' ?>
<!DOCTYP E html PUBLIC "-//W3C//DTD XHTM L 1.0 Transitional//EN"
" http ://www. w3 .org/TR/xhtm 11/DTD/xhtm 11-tra nsiti o na l.dtd" >
<html xmlns="http:/ /www.w3.org/1999/xhtml"
xm lns: h=" http ://xm 1ns.jcp.org/jsf/htm 1"
xm lns:f=" http ://java .sun.com/jsf/e ore"
xm lns: ui=" http ://java .su n .com/jsf/fa celets " >

1188 © Alfaomega - Altaria


JEE. Manual práctico

<h:head>
<title>Página JSF</title>
</h:head>
<h:body>
<hl>Hola desde JSF</hl>
</h:body>
</htm l>

Empecemos a unir las páginas JSF con los beans administrados. Primero, veamos
cómo podemos introducir un managed bean:

Prestaciones Página de !nido X

131
JSF Sean ...
Build
Java Class ...
Clean and Build
Clean
l!l Java Package...
~ JSF Page...
Verify
@ Session Bean ...
Generate Javadoc

Ahora que ya sabemos cómo incluir en nuestro proyecto páginas JSF y beans ad-
ministrados, pensemos en una pequeña aplicación en la que tendremos un botón que
setee si se muestra un saludo o una despedida. El texto que muestra dicho saludo o
despedida será un componente en sí. De esta forma tan simple sabremos cómo rende-
rizar componentes. La imagen de la vista podría ser ésta:

~tx:i'tn d'! componente

HOL'I

1 tA~rar Despedida 1

Cada vez que pulsemos en el botón "Mostrar Saludo" o "Mostrar Despedida", se


modificará el estado del bean gestionado. En dicho bean tendremos dos booleanos
(aunque para este caso con uno nos bastaría). En el bean también guardamos el texto
que mostramos en el botón. Para este pequeño proyecto tendremos:

• Una hoja de estilos CSS para dar dicha composición a la pantalla.

• El bean administrado que incluirá el código de la acción de cambio de estado .


• La página JSF "index.xhtml" que incluye el componente.

© Alfaomega - Altaria 1891


Curso avanzado de Java

Veamos cuál sería la estructura del proyecto.

Prestaciones

cr· • ... css


· ~ bootstrap-theme .min.css
~ bootstrap.m.=
'ti:u, proopío.css
,~, .... ., js

~ Source IP.acl<ages
e. .~ beans.
L... ~ Composition.java
ti Tes.t l'acl<ages
t)i\ Libra ri es
ti Test ~ibr:aries
(¡\ Configuration Files

Los tres elementos los podemos ver en los ficheros "propio.css", "index.xhtml" y
"Composition.java" . Veamos cada uno de ellos:

• Hoja de estilos "propio.css":


htm l, body {
height: 100%;
}
div.container {
margin-top: lcm;
}
div.container div.header {
height: SOpx;
line-height: SOpx;
t ext-align: center;
border-left: lpx sol id black;
border-top: lpx solid black;
border-right: lpx solid black;
}
div.container div.panelcomponent {
height: lSOpx;
line-height: lSOpx;
border: lpx solid black;
text-align: center;
}
div.container div.formbutton {
tex t-align: center;
border-left: lpx sol id black;

1190 © A lfaomega - Altaria


JEE. Manual práctico

border-bottom: lpx sol id black;


border-right: lpx solid black;
margin-bottom: le m;
}
div.container div.formbutton input {
margin: 20px;
}

El código del bean administrado "Composition.java" es el siguiente:


package beans;
import javax.inject.Named;
im port javax.ente rp rise.context.SessionScoped;
import java.io.Serializable;
import javax.faces.bean.ManagedBean;
@ManagedBean
@Named(value = "composition")
@SessionScoped
public class Composition implements Serializable {
private boolean mostrarSaludo;
private boolean mostrarDespedida;
private String texto="";
public Composition() {
this.mostrarSaludo = true;
this.mostrarDespedida = false;
this.texto = " Mostrar Despedida";
}
public void cambia Estado() {
if(this.mostrarSaludo) {
this.mostrarSaludo = fa lse;
this.mostrarDespedida = true;
this.texto ="Mostrar Sa ludo";
} else {
this.mostrarSaludo = true;
this.mostrarDespedida = fa lse;
this.texto ="Mostrar Despedida";
}
}
public boolean isMostrarSaludo() {
return mostrarSaludo;
}
public void setMostrarSaludo(boolean mostrarSaludo) {
this.mostrarSaludo = mostrarSaludo;
}
public boolean isMostrarDespedida() {
return mostrarDespedida;
}

© Alfaomega - Altaria 1911


C urso a vanzado de Java

public void setMostrarDespedida(boolean mostrarDespedida) {


this.mostrarDespedida = mostrarDespedida;
}
public String getTexto() {
return texto;
}
public void setTexto(String texto) {
this.texto =texto;
}
}

Debemos pararnos a observar cómo hemos usado las anotaciones "@ManagedBean",


"@SessionScoped" y "@Named(value = "composition")". Con dichas anotaciones indica-
mos que es un bean administrado (de respaldo), que su ámbito es la sesión y que el
nombre con el que lo vamos a referenciar desde las páginas JSF es "composition". El
funcionamiento de la aplicación queda definido en el método "cambiaEstado()" que se
lanza desde la página JSF. El código de "index.xhtml" es el s iguiente:
<?xml version='l.O' encoding='UTF-8' ?>
<!DOCTYPE html PUBLIC "-/ /W3C//DTD XHTM L 1.0 Transitional//EN"
"http ://www. w3 .org/TR/xhtm 11/DTD/xhtm 11-tra nsiti o na l.dtd">
<html xmlns="http://www.w3.org/1999/xhtml"
xm 1ns: h=" http ://xm 1ns.jcp.org/jsf/htm 1"
xm 1ns :f=" htt p ://java .sun.com/jsf/core"
xm 1ns: ui=" http ://java .su n .com/jsf/facelets" >
<h:head>
<meta http-eq uiv="Content-Type" content="text/htm 1; eha rset=UTF -8" />
<link rel="stylesheet" type="text/css" href="files/css/bootstrap.min.css" />
<link rel="stylesheet" type="text/css" href="files/css/bootstrap-theme.m in.css" />
<link rel="stylesheet" type="text/css" href="files/css/propio.css" />
<script type= "text/javascript" src="fi les/js/jq uery-3.1.0. m in .js"></script>
<script type= "text/javascri pt" src="fi les/js/bootstra p.m i n.js "></script>
<meta name="viewport" content="width=device-width, initial-scale=l.O" />
<title>Renderizando en JSF</title>
</h:head>
<h:body>
<div class="container">
<div class="header col-md-12">
<h:outputText value="Selección de componente"/>
</div>
<div class="panelcomponent col -md-6">
<h:outputText value="Hola" rendered="#{ composition .mostrarSaludo }" />
</div>
<div class="panelcomponent col-md-6 second">
<h :outputText va lue=" Adiós" rendered=" #{ com position. m ostra rDesped id a}" />
</div>

1192 © A lfaomega - Altaria


JEE. Man ual prác tic o

<div class="formbutton col-md-12">


<h:form>
<h :command Button value="#{com position.texto }" type="submit"
action="#{ com position .cambia Estado()}" />
</h:form>
</div>
</div>
</h:body>
</htm l>

El lector se habrá percatado de que este bean de respaldo no se corresponde con


un componente definido mediante una etiqueta de JSF, sino que se corresponde con
la composición completa. Podríamos decir, no de forma del todo correcta, que se co-
rresponde con el "div" de la clase "container".

6.4 Ejemplo del carrito de la compra


Un buen ejemplo para empezar a practicar JSF puede ser el del carrito de la compra
que vimos en el capítulo de EJB . Recordemos la presentación (la vista) de la única
pantalla de esta aplicación:

o Coi!>)O,Oill ~ )(

of.. -> C 1C 11XalhOtl:100t./Ej~mploSHfiot.e.¡n•w.,¡il'ldtlljSp t:~I G =

Su carrito de la compra

1...... 11 " ''"'"""' 1

Estado del Carrito

'~'
1.50

·- ~.tll)
10:00

... 1,111)
" ,0,01)

TCOt 4 9,~0

Lo primero que tenemos que preguntamos es: ¿cuántos componentes vamos a tener?
Está claro que la respuesta a esta pregunta nos responde a otra: ¿cuántos beans de
respaldo vamos a tener? Pero, antes de todo, debemos preguntarnos: ¿la composición
general requiere un componente? La composición general requeriría un componente
si ésta fuera dinámica, es decir: la composición está compuesta por un formulario y
una tabla. ¿En algún momento dicha composición puede variar? La respuesta es no.

© Alfaomega - A ltaria 1931


Curso avanzado de Java

Por lo tanto, nuestra composición general no requiere de un componente. Bajando de


nivel nos encontramos dos componentes: un formulario y una tabla. De modo que ya
sabemos la respuesta a cuántos componentes vamos a tener: dos. Y, por lo tanto, ya
sabemos cuántos managed beans (beans de respaldo) vamos a tener: dos. Veamos la
estruc tura del proyecto:

6·· ~., EjcmplooJSF-c:Jb


~··tltl Sou-ce Paciages
E!l domoinnoclc:l
t··· ~
C..nib>.jova
L. ~ Producto.java
e ·~'13 sessoroeans
· ~ Garritos.java
~ IGaTito!Lo:a'.¡ova
~ !GaTito!Remote.java
Tcst Pockogcs
Libraries
líH6t Ted: Lbrarits:
l:lf"l/JI Enterpnse 8eons
tíHl\ Confi9JrllltiMFi~
5e(vel RE=St>u!CE:S
EiempiosJSF-war
{íla Vle.b Pog:s
é·ffi WEB·I.l\'f
~.. (» files
L..~ index.xhtoll
S llh So~.rce Pad:ages
e -~~ bears
~ C..nib>8ean.lova
~ Entrodoecwrito.j:tv:~
~ FonnBea>.ja\'a
Test Package'

Los dos beans de respaldo tendrán:


• El del formulario. Un listado de campos con las entradas del carrito.
• El del estado del carrito. Un listado de productos.
Por lo tanto, en cada managed bean habrá un listado. Necesitamos definir una clase
que tenga los tres campos con la entrada del usuario. Dicha clase podría ser "Entra-
daCarrito.java ".

• "EntradaCarrito.java":
package beans;
import java.io.Serializable;
public class EntradaCa rrito implements Serializable {
private String text;
private String value;
public EntradaCarrito(){
this.text="";
this.value = "";
}
public String getText() {
return text;

1194 © A lfaomega - Altaria


JEE. Manual práctic o

}
public void setText(String text) (
this.text = text;
}
public String getValue() (
return value;
}
public void setValue(String value) {
this.va lue = value;
}
}
Con el conocimiento que ya tenemos, podemos afirmar que es un JavaBean. ¿Y el
listado de productos? Podemos usar perfectamente el bean de "Producto" definido en
el módulo de EJB.
• "Producto.java":

package domainmodel;
import java.io.Serializable;
public class Producto implements Serializable{
private String nombre;
private double precio;
private int nUnidades;
private double precioTota l;
public String getNombre() {
return nombre;
}
public void setNombre(String nombre) {
this.nombre =nombre;
}
public double getPrecio() {
return precio;
}
public void setPrecio(double precio) {
this.precio = precio;
}
public int getnUnidades() {
return nUnidades;
}
public void setnUnidades(int nUnidades) {
this.nUnidades = nUnidades;
}
public double getPrecioTota l() {
return precioTota l;
}
public void setPrecioTotal(double precioTota l) {
this.precioTotal = precioTotal;
}
}

© Alfaomega - Altaria 1951


C urso a vanzado de Java

Debemos in dicar que hemos añadido, con respecto al ej emplo que presentamos en
el capítulo de EJB, el campo "precioTotal" qu e tiene el producto (multiplicación) por el
número de u nida d es d e dicho producto. ¿Por qué lo hacemos en el bean? Para evitar
hacer op eracion es en la págin a JSF. Por ello mis mo , modificamos la lógica d e n egocio
cada vez que añadimos un producto a l ca rrito, seteando dich o campo (ta mbién en el
módulo de EJB).
• "Carrito.java":
package domainmodel;
import java.io.Serializable;
import java.utii.Arraylist;
i m port java.util. List;
public class Carrito implements Serializable {
private List<Producto> productos;
private double valorTotal;
public Carrito() {
this.productos = new Arraylist<Producto>();
}
public List<Producto> getProductos() {
return productos;
}
public void setProductos(List<Producto> productos) {
this.productos = productos;
}
public double getValorTotal() {
return valorTotal;
}
public void setValorTotal(double valorTotal) {
this.valorTota l = valorTotal;
}
public void anadirProductoCarrito(Producto p) {
if ((p != null) && (this.productos != null)) {
boolean encontrado= false;
int i = o·,
for (Producto producto : this.productos) {
if (producto.getNombre().equals(p.getNombre())) {
encontrado = true;
break;
} else {
i++·,
}
}
if (encontrado) {
Producto esteProducto = this.productos.get(i);
esteProd ucto .setn Unidades( este Prod ucto.getn Unidades()
+ p.getnUnidades());

1196 © A lfaomega - Altaria


JEE. Manual práctic o

double estePrecioTotal =
(do u b le )esteProd ucto.getn Unidades() * este Prod ucto.getPrecio();
este Producto.setP recio Tota 1( estePrecio Tota 1);
} else {
double estePrecioTotal = (double)p.getnUnidades() * p.getPrecio();
p .set Precio Tota 1(este Precio Tota 1);
this.productos.add(p );
}
}
}
public void eliminarProductoCarrito(Producto p) {
if ((p != null) && (this.productos != nuil)) {
boolean encontrado = false;
inti=O·
'
for (Producto producto: this.productos} {
if (producto.getNombre().equals(p.getNombre())) {
encontrado = true;
break;
} else {
i++·
'
}
}
if (encontrado) {
th is. productos.remove( i};
}
}
}
public double getPrecioTota iCarrito() {
if (this.productos != nuil) {
double total = 0.0;
for (Producto p : this.productos) {
total = total+ (p.getPrecio() * p.getnUnidades());
}
return total;
} else {
return 0.0;
}
}
}

Excepto este ajuste en el módulo de EJB, todo el código de dicho módulo permanece
inalterado. Las interfaces locales y remotas permanecen igual y la implementación del
EJB singleton también permanece tal y como vimos en el capítulo de EJB. Pasemos
a estudiar nuestros managed beans en el módulo WAR (donde también definimos el
JavaBean "EntradaCarrito.java" ya visto). El bean que va a administrar las entradas
del usuario se llamará FormBean y será un bean de sesión.

© Alfaomega - Altaria 1971


Curso avanzado de Java

• "FormBean.java":
package beans;
import javax.inject.Named;
import java.io.Serializable;
import java.utii.Arraylist;
im port java.util.list;
im port javax.faces.bean. M a nagedBea n;
im port javax.faces.bean.SessionScoped;
@ManagedBean
@SessionScoped
@Named(value = "formBean")
public class FormBea n im plements Serializable {
prívate list<EntradaCarrito> entradas;
public FormBean() {
entradas= new Arraylist<EntradaCarrito>();
EntradaCarrito entrada = new EntradaCarrito();
entrada.setText("lnserte el nombre del producto: ");
entradas.add (entrada);
entrada = new EntradaCarrito();
entrada.setText("lnserte el precio del producto:");
e ntradas.add (entrada);
entrada = new EntradaCarrito();
entrada.setText("lnserte el número de unidades:");
entradas.add (entrada);
}
public list<EntradaCarrito> get Entradas() {
re t urn entradas;
}
public void setEntradas(list<EntradaCarrito> entradas) {
this.entradas = entradas;
}
public vo id limpia Entradas() {
f or (EntradaCarrito entrada : entradas) {
entrada.setValue("");
}
}
}

Hemos decidido setear en el bean también los textos que le mostraremos al usua-
rio . El dato que manejará esta vista será la lista de entradas del carrito (nombre del
producto, precio del producto y número de unidades) .

Veamos cómo resulta el bean administrado del carrito, "CarritoBean.java", también


bean de sesión:

package beans;

1198 © Alfaomega - Altaria


JEE. Manual práctic o

import domainmodei. Producto;


import javax.inject.Named;
import java.io.Serializable;
import java.utii.Arraylist;
import java.utii.List;
import javax.ejb.EJB;
import javax.faces.bean.ManagedBean;
import javax.faces. bean.ManagedProperty;
im port javax.faces.bea n .SessionScoped;
im port javax. faces.context. FacesContext;
im port javax.servlet. http. HttpSession;
import sessionbeans.ICarritosloca l;
@ManagedBean
@SessionScoped
@Named(value = "carritoBean")
public class CarritoBean implements Serializable {
@EJB
prívate ICarritoslocal carritos;
prívate List<Producto> productos;
prívate double precioTota l;
@ Managed Property(value="#{formBean}")
prívate FormBean formBean;
public CarritoBean() {
this.productos = new Arraylist<Producto>();
this.precioTotal = 0.0;
}
pu blic void anadeCa rrito() {
String sessionld = this.getSessionld();
carritos. usarCa rrito(session 1d);
List<EntradaCarrito> entradas= formBean.getEntradas();
String nombre= entradas.get(O).getValue();
String precioP = entradas.get(l).getValue();
String nUnidadesP = entradas.get(2) .getValue();
double precio = 0.0;
int nUnidades =O;
String nombreSinEspacios = '"';
try {
precio= Double.parseDouble(precioP);
nUnidades = lnteger.parselnt(nUnidadesP);
nombreSinEspacios = nombre.trim();
} catch (Exception e) {}
if (!nombreSinEspacios.equals("") && precio != 0.0 && nUnidades !=O) {
Producto p = new Producto();
p.setNom bre(nombre);
p.setPrecio(precio );
p.setn Unidades(n Unidades);

© Alfaomega - Altaria 1991


Curso avanzado de Java

carritos.anad i rP rod uctoCa rrito(session Id, p );


}
form Bean.li m pi aEntradas();

this.productos = carritos.get Productos(sessionld);


this.precioTotal =carritos.getPrecioTotaiCarrito(sessio n Id );
}
public void setFormBean(FormBean fo rmBean) {
this.formBean = formBean;
}
public l ist<Producto> getProductos() {
re t urn productos;
}
public void setProduct os(list<Producto> productos) {
this.productos = productos;
}
public double getPrecioTotal() {
re t urn precioTotal;
}
public void setPrecioTotal(double precio Total) {
this.precioTotal = precioTot al;
}
prívate String getSession ld() {
FacesContext fCtx = FacesContext.getCurrentlnstance();
HttpSession session = (HttpSession) fCtx.getExternaiCont ext().get Session(fa lse);
String sessionld =session .getld();
ret urn sessionld;
}
}

En este código debemos apreciar:


• Cómo inyectamos el bean singleton. Podemos hacer la inyección de EJB como
si fuera un servlet. Gracias a esta anotación, tendremos en el bean de respaldo
acceso al back-end de nuestra aplicación.
• Cómo inyectamos el bean del formulario. Necesitamos tener acceso al bean d el
formu lario para poder procesar los valores introducidos por el usuario. Indicar
que se necesita un setter del bean administrado.
• Cómo conseguimos el "id" de la sesión. Haciendo uso de la fachada "Faces-
Context" conseguimos la instancia actual de la página JSF y con ella podemos
tener acceso al "id" de la sesión en la forma indicada en el método "getSessionld".
Dicho "id" es necesario para enviárselo al EJB.

1200 © A lfaomega - Altaria


JEE. Manual práctico

Para finalizar, veamos cómo encaja toda nuestra aplicación en la página JSF "index.
xhtml". Como nota, indicamos que hacemos uso de los mismos ficheros CSS y Java-
Script que usamos en el apartado del carrito visto en el capítulo de EJB, incluyendo
el CSS "propio.css".
<?xm l version='l.O' encoding='UTF-8' ?>
<!DOCTYPE htm l PUBLIC "-//W3C//DTD XHTML 1.0 Transitionai//EN"
"http ://www. w3 .org/TR/xhtm 11/DTD/xhtm 11-tra nsiti o na l.dtd">
<htm l xmlns="http://www.w3.org/1999/xhtml"
xm lns:h="http ://xmlns.jcp.org/jsf/htm 1"
xm lns:f="http://java.sun .com/jsf/core"
xm 1ns:u i=" http ://java .su n.com/jsf/facelets" >
<h:head>
<meta http-equiv="Content-Type" content="text/htm 1; charset=UTF-8" />
<link rel="stylesheet" type="text/css" href="files/css/bootstrap.min.css" />
<link rel="stylesheet" type="text/css" href="files/css/bootstra p-theme.m i n.css" />
<link rel="stylesheet" type="text/css" href="files/css/propio.css" />
<script type="text/javascri pt" src="fi les/js/jq uery-3 .l. O. m in .js "></script>
<scri pt type="text/javascri pt" src="fi les/js/bootstra p. m in .js"></script>
<meta name="viewport" content="width=device-width, initial-scale=l.O" />
<title>Ca rrito de la compra</title>
</h:head>
<h:body>
<div class="container">
<h1>Su carrito de la compra</h1>
</div>
<div class="conta iner">
<h:form>
<h:dataTable value="#{formBean.entradas}" var="e"
styleCiass="table table-bordered form"
col u m nCiasses=", input-field"
foote rCiass=" in put-fie Id">
<h:column>
<h:outputText va lue="#{e.text}" />
</h:column>
<h:column>
<h:inputText value="#{e.value}" size="60" maxlength="100" />
<f:facet name="footer">
<h:commandButton value="Enviar" type="submit"
acti on="#{carritoBean.anadeCarrito }" />
<h:outputText value="&nbsp;&nbsp;" />
<h:commandButton value="Restablecer" type="reset" />
</f:facet>
</h:column>
</h:dataTable>
</h:form>
</div>

© Alfaomega - Altaria 2011


Curso avanzado de Java

<div class="container">
<hl>Estado del Carrito</hl>
</div>
<div class="container">
<h :dataTable va lue="#{carritoBean .productos}" var=" p"
styleCiass="ta ble ta ble-bordered">
<h:column>
<f:facet name="header">
<h:outputText value="Nombre de Producto"/>
</f:facet>
<h:outputText value="#{p.nombre}" />
</h:column>
<h:column>
<f:facet name="header">
<h:outputText value="Precio" />
</f:facet>
<h:outputText value="#{p.precio}" >
<f:convertNumber minFractionDigits="2" />
</h :outputText>
</h:column>
<h:column>
<f:facet name="header">
<h:outputText value="Número de Unidades"/>
</f:facet>
<h:outputText value="#{p.getnUnidades()}" />
<f:facet name="footer">
<h:outputText value=" Total:" />
</f:facet>
</h :col u m n>
<h:column>
<f:facet name="header">
<h:outputText value=" Precio Producto" />
</f:facet>
<h:outputText value="#{p.precioTotal}" >
<f:convertNumber minFractionDigits="2" />
</h :outputText>
<f:facet name="footer">
<h:outputText value="#{carritoBean.precio Tota 1}" >
<f:convertNumber minFractionDigits="2" />
</h:outputText>
</f:facet>
</h :col u m n>
</h:dataTable>
</div>
</h:body>
</html>

1202 © A lfaomega - Altaria


JEE. Manual práctico

Digno de mención:
• Maquetamos con los contenedores definidos en nuestro CSS.

• Los managed beans está n disponibles sin n ecesida d de importa rlos.

• Con las librerías de etiquetas <h:> y <f:> evitamos los scriptlets. Podemos ver
que hemos evitado el "for" del scriptlet para todos los elementos del carrito con
el componente de tipo "h:dataTable".

• No escribimos en la página directamente, sino a través de la etiqueta de HTML


"outputText". La conve rsión de form a to d e double la realiza mos con la etiqueta
del core de JSF "convertNumber".
• Escribimos la cabecera y el pie de página con la etiqueta <f:facet> .

Veamos finalmente la ejecución del proyecto del carrito en JSF:

~ cu~r;k <;¡(cnp-, X o ..:_1 • •• •


f. 4 C 1Q IOc~ltwx:t7001J~mi))O:ISF~&cC'1/lr.::l<tT.llhtml

Su carrito de la compra

1Eown 11 RulabOe«•J

Estado del Carrito

~C:IP Producto

..... 3,51) 10,50

• 7."'
TOt3 t

6.5 Plantillas con Facelets


La librería que nos permite crear diseños reutilizables que podremos elegir cada vez
que los necesitemos es Facelets. Podemos entender Facelets estudiando cuatro etiquetas
proporcionadas por su librería:
• <ui:insert>
• <ui:define>
• <ui:include >
• <ui:composition>

© Alfaomega - Altaria 2031


Curso avanzado de Java

Con <ui:insert> definimos bloques de la plantilla que podrán ser reemplazables en


posteriores definiciones (sobrescritos por el programador). Imaginemos el siguiente
ejemplo de fragmento de plantilla:
<h:body>
<div id="cabecera">
<ui:insert name="cabecera" >
Ésta es la cabecera por defecto
</ui:insert>
</d iv>
</h:body>

Si tuviésemos una página que heredase de la plantilla anterior (ya veremos cómo),
podríamos redefinir el texto de la cabecera de la siguiente forma (usando nuestra se-
gunda etiqueta <ui:define>):
<u i:define name="cabecera">
'
Esta es la cabecera sobrescrita
</u i:define>

Es decir, la etiqueta <ui:define> sirve para sobrescribir las partes de la plantilla que
queramos modificar. En nuestro ejemplo previo, el código que se enviaría al navegador
es el siguiente:
<h:body>
<div id="cabecera">
Ésta es la cabecera sobrescrita
</d iv>
</h:body>

Con <ui:include> podemos incluir el código que tengamos en otra página JSF. Si
tenemos el siguiente código JSF:
<h:body>
<div id="cabecera">
<ui:insert name="cabecera" >
<ui:include src="/cabecera.xhtml" />
</ui:insert>
</div>
</h:body>

1204 © Alfaomega - Altaria


JEE. Manual práctic o

Estamos creando un bloque "cabecera":

• Que puede ser sobrescrito con la etiqueta <ui:define>.

• Cuya definición, a menos que sea sobrescrita, se encuentra en el fichero "cabe-


cera.xhtml".
El bloque definido en un fichero externo ("cabecera.xhtml") enlaza con la etiqueta
que nos falta por ver: <ui:composition>. El contenido de dicho fichero puede tener esta
parte de definición:
<body>
<ui:composition>
Ésta es la cabecera
</ui:composition>
</body>

Todo lo que esté fuera del bloque <ui:composition> el motor de JSF no lo tiene en
cuenta. De tal forma que nuestro bloque de caso de estudio queda definido (se envía
al navegador) de la siguiente forma:
<h:body>
<div id="cabecera">

Esta es la cabecera
</div>
</h:body>

Siendo, además, el bloque "cabecera" reemplazable. La etiqueta <ui:composition> es la


que nos permite realizar las definiciones externas y la herencia de plantillas. Acabamos
de ver un ejemplo de cómo se realizaría una definición externa. ¿Cómo heredaríamos
la plantilla? Añadiendo la propiedad template a <ui:composition> . Veamos un ejemplo
de página que usa la plantilla definida en "plantilla .xhtml".
<?xm l version='l.O' encoding='UTF-8' ?>
<!DOCTYPE htm l PUBLIC "-//W3C//DTD XHTML 1.0 Transitionai//EN"
"http ://www. w3 .org/TR/xhtm 11/DTD/xhtm 11-tra nsiti o na l.dtd">
<htm l xmlns="http://www.w3.org/1999/xhtm l"
xm lns:h="http ://xmlns.jcp.org/jsf/htm 1"
xm lns:f="http://java.sun .com/jsf/core"
xm 1ns:u i=" http ://java .sun.com/jsf/facelets" >
<ui :com positio n te m plate= "/plantilla .xhtml">
</ui:composition>
</htm l>

© Alfaomega - Altaria 2051


C urso a vanzado de Jav a

Con este pequeño estudio de las cuatro etiquetas, tenemos el conocimiento suficiente
para crear una plantilla en JSF. Imaginemos la siguiente estructura:

TÍTULO DE LA PÁGINA ENVIADO DESDE EL CONTROLADOR

Texto dividido en pá rrafos. Tendremos el número de p iln afos y con e l conten ido Q".Je dicte el
1 &lb ce l
1 controlador.
Enlace 2
1 1
Fnbca ~
1 1
.
.
.
.

Pie de página. Cotnúnmente información de copyright.

Podríamos definir una plantilla con tres bloques:


• Bloque cabecera.

• Bloque pa rte central.

• Bloque pie de página.


A su vez, los bloques pueden estar definidos en ficheros independientes. Veamos
cómo podríamos plantear esta plantilla con Facelets. Una posible estructura del pro-
yecto es la siguiente:
Prestaciooes -

resourc:es
(B css
LH.' propío.css
cabecera .><html
indeK.xhbnl
partecentral.xhtml
piepagina.xhbnl
plantilla. xhtml
texto. xhbnl
titulo.xhtml
[j Source Ped<ages
(i Libr aries
~ Configura1ion Files

1206 © A lfaomega - Altaria


JEE. Manual práctico

La estructura en árbol de los artefactos JSF es la siguiente, definida mediante


<ui:include>:

• pla ntilla .xhtml.


titulo.xhtml.
cabecera.xh tml.
partecen tral. xh tml.
• texto.xhtml.
piepagina .xhtml.

Y la página "index.xhtml" hereda de la plantilla "plantilla.xhtml".

Desde la plantilla importamos un fichero CSS, "propio.css" . En esta ocasión hace-


mos uso de la etiqueta <h:outputStylesheet/> . Veamos los artefactos de código fuente:

• Fichero CSS "propio.css":


@charset "UTF-8";
body {
background-color: gray;
}
div {
border: lpx solid black;
}
div#contenedor {
width: 80%;
margin: O auto;
}
div#contenedor div#cabecera {
text -align: center;
font-size: xx-large;
padding: 20px O;
color: white;
background-color: red;
}
div#contenedor div#partecentral div#menu {
width: 20%;
float: left;
border: Opx;
border-right: 2px sol id black;
text-align: center;
}
div#contenedor div#partecentral div#menu a.boton,
div#contenedor div#partecentral div#menu a.boton:visited {
margin: 20px;
display: inline-block;

© Alfaomega - Altaria 2071


C urso a vanza do de Java

padding: 10px 15px;


border: 1px solid black;
text-decoration : none;
color: white;
background-color: silver;
}
div#contenedor div#partecentra l div#menu a.boton :hover,
div#contenedor div#partecentra l div#menu a.boton:active {
background-color: gray;
}
div#contenedor div#partecentra l div#contenido {
width: 79.3%;
float: left;
border: Opx;
height: auto;
}
d iv#contenedor div#partecentral div#contenido p {
margin: 10px;
text-align: justify;
color: black;
font-fam ily: Garamond, Baskerville, "Baskerville Old Face",
"Hoefler Text" "Times New Roman" serif-
' J J

text-indent: 30px;
}

div#contenedor div#piepagina {
text-align: center;
font-size: smaller;
padding: 10px O;
color: white;
background-color: red;
}
div.clear {
clear: both;
border: Opx;
}

• Código de la pla n tilla "pla n tilla .xhtml":


<?xml version='l.O' encoding='UTF-8' ?>
<!DOCTYPE html PUBLIC "-/ /W3C//DTD XHTM l1.0 Transitional//EN"
"http ://www. w3 .org/TR/xhtm 11/DTD/xhtm 11-tra nsitiona l.dtd" >
<html xmlns="http :/ /www.w3.org/1999/xhtml"
xm 1ns: h=" http ://xm 1ns.jcp.org/jsf/htm 1"
xm 1ns :f=" http ://java .sun.com/jsf/core"
xm 1ns: ui=" http ://java .su n .com/jsf/fa celets ">

1208 © A lfaomega - Altaria


JEE. Manual práctico

<h:head>
<h:outputStylesheet name="propio.css" library="css" />
<ui:insert name="title">
<ui :include src="/titulo.xhtml" />
</ui:insert >
</h:head>
<h:body>
<div id="contenedor">
<div id="cabecera">
<ui :insert name="cabecera" >
<ui:include src="/cabecera.xhtml" />
</ui:insert>
</div>
<div id="partecentral">
<ui:insert name="partecentral" >
<ui:include src="/partecentral.xhtml" />
</ui:insert>
</div>
<div id="piepagina">
<ui: insert name="piepagina" >
<ui :include src=" /piepagina.xhtml" />
</ui:insert>
</div>
</div>
</h :body>
</htm l>

Vemos cómo hacemos uso de la hoja de estilos y cómo definimos los bloques que
pueden ser sobrescritos en fich eros independientes . Veamos estos ficheros:

• Fich ero "titulo.xh tml":

<?xm l version="l.O" encoding="UTF-8" ?>


< !DOCTYPE htm l PUBLIC "-/ /W3C//DTD XHTM L 1.0 Transitional//EN"
"http ://www. w3 .org/TR/xhtm 11/DTD/xhtmll -t ra nsiti o na l.dtd ">
<htm l xmlns="http://www.w3 .org/1999/xhtm l"
xm lns:h="http ://xmlns.jcp.org/jsf/htm 1"
xm lns:f="http:/ /java.sun .com/jsf/core"
xm 1ns:u i=" http ://java.su n.com/jsf/facelets" >
<body>
<ui:composition>
<title> PI antillas</title>
</u i:composition>
</body>
</htm l>

© Alfaomega - Altaria 2091


C urso a vanzado de Java

• Fichero "cabecera.xh tml":


<?xml version="l.O" encoding="UTF-8"?>
<!DOCTYPE html PUBLIC "-/ /W3C//DTD XHTM l1 .0 Transitional//EN"
"http ://www. w3 .org/TR/xhtm 11/DTD/xhtm 11-tra nsitiona l.dtd" >
<html xmlns=" http :/ /www.w3.org/1999/xhtml"
xm lns: h="http://xm 1ns.jcp.org/jsf/htm 1"
xm 1ns :f=" http ://java .sun.com/jsf/core"
xm lns: ui=" http ://java .su n .com/jsf/facelets ">
<body>
<ui:composition>
Ésta es la cabecera
</ui:composition>
</body>
</html>

• Fichero "partecentral.x html":


<?xml version="l.O" encoding="UTF-8"?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTM L 1.0 Transitionai//EN"
"http ://www. w3 .org/TR/xhtm 11/DTD/xhtm 11-tra nsitiona l.dtd">
<html xmlns="http://www.w3.org/1999/xhtml"
xm 1ns: h= "http ://xm 1ns.jcp.org/jsf/htm 1"
xm lns :f=" http ://java.sun.com/jsf/core"
xm lns: ui=" http ://java .su n .com/jsf/facelets" >
<body>
<ui:composition>
<div id="menu">
<a class="boton" href="#">Enlace 1</a>
<a class="boton" href="#">Enlace 2</a>
<a class="boton" href="#">Enlace 3</a>
</div>
<div id="contenido">
<ui:insert name="texto">
<ui:include src="/texto.xhtml" />
</u i :insert>
</div>
<div class="clear"></div>
</ui:composition>
</body>
</html>

Apreciamos que se hace inclusión del fich ero "tex to.xhtml". Veamos el conten ido de
dic ho fic hero:

1210 © A lfaomega - Altaria


JEE. Manual práctic o

<?xm l version='l.O' encoding='UTF-8' ?>


< !DOCTYPE htm l PUBLIC "-/ /W3C//DTD XHTM L l. OTransitional//EN"
"http ://www.w3 .org/TR/xhtm 11/DTD/xhtm11-tra nsiti o na l.dtd">
<htm l xmlns="http://www.w3.org/1999/xhtml"
xm lns:h="http ://xmlns.jcp.org/jsf/htm 1"
xm 1ns:f="http://java.sun .com/jsf/core"
xm 1ns:u i=" http ://java .sun.com/jsf/facelets" >
<h:body>
<ui:composition>
<p>Lorem ipsum dolor sit amet.</p>
</u i:composition>
</h:body>
</htm l>

Y, finalmente, vemos el fichero del pie de página, "piepagina.xhtml":


<?xm l version="l.O" encoding="UTF-8"?>
< !DOCTYPE htm l PUBLIC "-//W3C//DTD XHTML l. OTransitional//EN"
"http ://www. w3 .org/TR/xhtm 11/DTD/xhtml1-tra nsiti o na l.dtd">
<htm 1xmlns="http://www.w3.org/1999/xhtm 1"
xm 1ns:h="http ://xmlns.jcp.org/jsf/htm 1"
xm lns:f="http://java.sun .com/jsf/core"
xm 1ns:u i=" http ://java .sun.com/jsf/facelets" >
<body>
<ui:composition>
Éste es el pie de página
</ui:composition>
</body>
</htm l>

Hemos de dejar claro que únicamente hemos definido la plantilla, pero nmguna
página. La página "index.xhtml" que h ará uso de la plantilla es :
<?xm l version='l.O' encoding='UTF-8' ?>
< !DOCTYPE htm l PUBLIC "-/ /W3C//DTD XHTML l. OTransitional//EN"
"http ://www. w3 .org/TR/xhtm 11/DTD/xhtm 11-tra nsitiona l.dtd">
<htm l xmlns="http://www.w3.org/1999/xhtm l"
xm lns:h="http ://xmlns.jcp.org/jsf/htm 1"
xm 1ns:f="http://java.sun .com/jsf/core"
xm 1ns:u i=" http ://java .sun.com/jsf/facelets" >
<ui :com positio n te m plate="1 plantilla .xhtml">
<ui:define name="texto">
<p>No escribimos Lorem ipsum. Escribimos texto en castellano
sobrescribiendo la plantilla.</p>
</u i :define>
</ui:composition>
</htm l>

© Alfaomega - Altaria 2111


Curso avanzado de Java

A la vez que estamos heredando la plantilla, estamos redefiniendo la sección de texto.


El código HTML que se enviaría al navegador es el siguiente:

<?xml version='l.O' encoding='UTF-8' ?>


<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml">
<head id="j_idt2">
<title>Pia ntillas</title>
<link type="text/css" rel="stylesheet"
h ref= "/Ejemplo Facelets/faces/javax. faces. res o urce/pro pio.css;
jsessionid=CehYbNqEKNMq-OP936XJdB-SyvjwiTWwbjSY-qB9XqenOHCH-h8
!1304982310?1n=css" />
</head>
<body>
<div id="contenedor">
<div id="cabecera">

Esta es la cabecera
</div>
<div id="partecentra l">
<div id="menu">
<a class="boton" href="#">Enlace 1</a>
<a class="boton" href="#">Enlace 2</a>
<a class="boton" href="#">Enlace 3</a>
</div>
<div id="contenido">
<p>No escribimos Lorem ipsum . Escribimos texto en castellano
sobrescribiendo la plantilla. </p>
</div>
<div class="clear"></div>
</div>
<div id="piepagina">
Ést e es el pie de página
</div>
</div>
</body>
</html>

Y en el navegador podemos ver:

1212 © A lfaomega - Altaria


JEE. Manual práctico

6.6 Ejercicio 5
Se propone al lector la maquetación mediante Facelets de la vista del ejercicio del
blog. Tendremos una única plantilla. Mediante un managed bean gestionaremos el
estado de dicha plantilla. El prototipo del ejercicio del blog es el siguiente:

• Vista del login (no se necesita registro):

81 JEE

© Alfaomega - Altaria 213 1


Curso avanzado de Java

• Vista del blog:

,. hr'

<
'

1~ 10-'lB

En la columna izquierda tendremos todos los usuarios que alguna vez han entrado
(tengan sesión activa o no). Bajo el nombre de cada usuario tendremos un botón para
indicar si en la columna derecha queremos chatear con él. En la parte central se mos-
trará la zona de posts y comentarios típica de cualquier blog. En la columna derecha
aparece una zona en la que podremos mantener una conversación de chat con otro
usuario registrado.

Se propone la implementación de la vista y el controlador (a fal ta de websockets


para gestionar envío de posts, de comentarios y de chats) mediante JSF. Tendremos:
• Un managed bean de ámbito de sesión.

• Varios fragmentos JSF que se renderizarán o no.


Plan tilla general: "plantillablog.hxtml".
• "nombreusuario.xhtml". Un "div" oculto en CSS con el nombre del usuario
registrado.
• "mensajeflash.xhtml". Indicará al usuario mensajes de información o de
error.
• "cabecera.xhtml".
• "logout.xhtml". Renderiza el botón de salida.
• "contenidologin.xhtml".
• "contenidoblog.xhtml".

La página "index.xhtml" heredará de "plantillablog.xhtml". Para renderizar compo-


nentes se plantea usar el componente de Facelets <ui: fragment>.

La p lantilla usada utiliza una hoja de estilos propia (a desarrollar por el lector), que
en el apartado de ejercicios resueltos se puede encontrar con el nombre de "style.css".

1214 © Alfaomega - Altaria


JEE. Manual práctico

También hace uso de una hoja de estilo de botones desarrollada por Valeriu Timbuc,
con licencia educacional Creative Commons y que podemos descargar desde "http:/ /
designmodo.com/futurico". Además, la plantilla es una adapta ción de la que podemos
en contra r en "http:/ fwww.free-css-templates.com/previewfGrey/", diseñ a da por Free
CSS Templates con agradecimiento a Web Design US.

Para que el lector tenga una idea de qué son los elementos ocultos de nombre de
usuario y de mensaje flash, los indicamos aquí:
• Nombre de usuario:
<div id="nombreusuario">
<!--Nombre del usuario set eado en el bean gestionado-->
</div>

• Mensaje flash:
<div id="mensajeflash">
<div class="error">
<!--Cont enido del mensaje de error-->
</div>
<div class="mensaje">
<!--Contenido del mensaje de información-->
</div>
</div>

Y los demás elementos gráficos son los siguientes:

lod:S I3

l ~omcn 0t usuaA« pon.c.o~

wntentdol<l(:Jn
~-....

© Alfaomega - Altaria 2151


Curso avanzado de Java

logout

vrc:tor

Usuario: ismael
contenidoblog fOCha: OtN0J2015 15:10:21
Contenkl« Hola es m1

>
- Ctl:llea 00111.3m./I.CI ~

u s.ano:VIct~r

f ec:bn:0 1J101201& 15:10:58

Se propone crear la hoja de estilos CSS, las páginas JSF y el managed bean de sesión
para conseguir dicha estructura.

1216 © Alfaomega - Altaria


,.

Servicios web CAPITULO

Cuando una máquina o un conjunto de ellas ejecutan una aplicación en forma de


clúster (balanceando la carga), podemos adoptar la implementación mostrada a lo largo
de este manual para crear una aplicación web. Pero ¿qué ocurre con las aplicaciones
distribuidas? Tenemos varias opciones:

• RMI.

• CORBA.

• Servicios web.
La solución RMI se ha estudiado someramente a lo largo de este manual y es buena
y eficiente. Pero debemos importar en nuestro código las librerías de interfaces propor-
cionadas por el implementador de los métodos.
CORBA se plantea como una solución muy buena, ya que nos permite intercomu-
nicar sistemas que se encuentran escritos en múltiples lenguajes de programación y
corriendo sobre diversas plataformas. El "problema" es que debemos añadir una capa
extra a nuestra aplicación para traducir nuestros objetos a la arquitectura de CORBA,
capa extra que a lo largo de los años no se ha visto carente de problemas a la hora de
su implementación. Además, si lo que queremos es enviar datos a aplicaciones cliente
o a aplicaciones escritas en nuestro mismo lenguaje, CORBA se plantea innecesario
(es matar una mosca a cañozanos) .
Los servicios web son la solución idónea para las implementaciones en las que
queremos enviar datos a clientes. Además, también permiten que los clientes estén
escritos en diversos lenguajes de programación . Los clientes sólo tienen que adaptarse
al protocolo del servidor.

© Alfaomega - Altaria 2171


C urso a vanza do de Java

7.1 RESTful vs. SOAP


Tradicionalmente, existen dos implementaciones para los servicios web:
• SOAP.
• RESTful.
SOAP es el acrónimo de Simple Object Access Protocol (Protocolo Simple de Acceso
a Objetos). Los servicios SOAP son servicios web implementables en JEE mediante la
librería JAX-WS. En SOAP usamos un conjunto de tecnologías para crear nuestros
SerVICIOS:

• SOAP. Es el protocolo en sí. Necesita envolver los mensajes (los datos que envia-
mos) en formato SOAP. Los clientes deben adaptarse al protocolo SOAP.
• W S DL. Son las siglas de Web Service Description Language (Lenguaje de Descrip-
ción de Servicios Web). El servidor pone a disposición de los clientes un contrato
en el que queda definido el servicio web en sí y la forma de acceder a él.

• UD DI. Es un repositorio de servicios web en forma de "diccionario" . Los clientes


pueden localizar los servicios web en el catálogo de registros. UDDI son las siglas
de Universal Description, Discovery and Integration (Integración, Descubrimiento
y Descripción Universal).

En el otro lado de la balanza de los servicios web tenemos los servicios RESTful. En
la JEE los podemos implementar usando la API JAX-RS. Dichos servicios consisten en
la creación de una API siguiendo los principios REST, que es el acrónimo de Representa-
tional State Transfer (Transferencia de Estado Representacional). El término fue acuñado
en el año 2000 por Roy Fielding, uno de los autores de la especificación HTTP. La idea
de RESTes crear un canal entre cliente y servidor con las siguientes características:

• Protocolo de comunicación sin estado. Sirviéndose de forma común de HTTP.


• Una URI para cada recurso.
• Operaciones bien definidas sobre los recursos, básicamente las operaciones del
CRUD (Create, Read, Update and Delete).
• Sintaxis universal sin necesidad de documentación.
• Uso del principio HATEOAS. HATEOAS es el acrónimo de Hypermedia As The
Engine Of Application State (hipermedia como motor del estado de la aplicación).
Esto quiere decir que en los propios mensajes enviaremos información sobre
cómo podemos seguir accediendo al servicio web.
En nuestro desarrollo, ¿cuál usaremos, SOAP o RESTful? La respuesta es RESTful,
debido a:

• No requiere de una capa de código adicional, como la definición del servicio web
(WSDL) .

1218 © Alfaomega - Altaria


JEE. Manual práctico

• No necesitamos adaptarnos al protocolo SOAP.


• Es más liviano para los dispositivos móviles, que por regla general serán los
clientes del back-end de nuestra aplicación.

• Está siendo adoptado por la comunidad internacional como el estándar de Jacto


para los servicios web. Facebook, Twitter, Google ... han adoptado el estándar
RESTful para sus servicios web .

7.2 Servicios web RESTful. JAX-RS y Jersey


Los servicios web RESTful no son más que la implementación de servicios web
siguiendo los principios de diseño REST. Nosotros usaremos la librería JAX-RS y su
implementación Jersey.

7.2.1 Métodos HTTP


HTTP es un protocolo sin estado. Esto quiere decir que los objetos de las operacio-
nes del protocolo no guardan el estado conversacional del cliente. El funcionamiento
de HTTP es simple: realizamos una petición (request}, es procesada por el servidor
y se devuelve una respuesta (response) al cliente. Pero en la operación en sí no hay
información del estado, sino que es en el cliente y en el servidor donde se guarda la
información d el estado de la conversación. En el clien te se guardan en las conocidas
cookies y en el servidor se guardan en los beans de sesión que venimos estudiando a
lo largo de este texto.
Ya hemos visto que el cliente envía una petición y el servidor devuelve una respuesta
al cliente. Pero ¿cómo se sabe la operación concreta que se debe realizar? Esta ope-
ración se conoce mediante las operaciones que permite el protocolo HTTP. Éstas son:

• GET. Para obtener información del recurso al cual estemos accediendo en el


servidor.
• POST. Con esta operación enviamos una nueva entrada del recurso. Es decir,
con POST creamos nuevas entradas de datos.

• PUT. Esta operación del método HTTP nos permite actualizar el estado del re-
curso concreto.

• DELETE. Operación que nos permite eliminar un recurso del back-end de la


aplicación.
La petición y la respuesta contendrán datos, ya sea en la cabecera o en el cuerpo.
Serán objetos HTTP, en los que el cuerpo será cualquier formato de datos. Para nosotros
los datos viajarán en formato JSON {JavaScript Object Notation).

© Alfaomega - Altaria 2191


C urso a vanzado de Jav a

7 .2.2 Diseño de la API


Imaginemos un diagrama ERO (datos) simple:

Clase Alum no

- id: Long; - id Long;


- nombreCurso: String;
., 1 .. - nombreAiumno: String;
- nombreProfesor: Slring; 1" - fechaNacimie nto: Calendar;
- numeroTel efono : String; - numeroTelefono: String;
- email: String; - email: Stri ng;
- clase: Clase;
+ gette rs &. setters;
+ getters & setters;

Lo que nos daría lugar a un diagrama d e clases tal cual mostramos:

Clase
Alumn o

- id: Long;
-id: Long;
- nombreCurso: String;
* .... - nombreAiumno: String;
- nombreProfesor: String;
- numeroTelefono: Slring;
r1
.. fech aNacimiento: String;
- numeroTelefono : String;
- email: String;
- email: String;
- alumnos: List<Aiumno>

+getters & setters;


+getters & setters;

Lo que a nivel de datos seria una clave ajena a nivel de dominio se convierte en un
listado. Por lo tanto, podemos diseñar una API RESTful de la siguiente forma:
• Obtener los datos de todas las clases (en el caso de que hubiera varias).
G ET: "1clase".
• Obtener los datos de una clase concreta (en el caso de que hubiera varias).
GET: "jclasej{id}".

• Crear un recurso de tipo "Clase".


POST: "/clase". En la request enviamos los datos de la clase.
• Actualizar un recurso de tipo "Clase".
PUT: "jclasef{id}" . En la requestenviamos los nuevos datos. El "id" no puede
cambiar porque es el campo clave.

1220 © A lfaomega - Altaria


JEE. Manual práctico

• Borrar un recurso de tipo "Clase".


DELETE: "/clase/{id}". Eliminaría la clase. Si la política de eliminación es en
cascada, también eliminar los alumnos de dicha clase .

• Obtener todos los alumnos de una clase.


G ET: "/clase1{id}1alumno".
• Obtener los datos de un alumno concreto.
GET: "/clase/ {id}/ alumno/ {id}" .
• Crear un recurso de tipo alumno.
POST: "/clasef{id}falumno". Enviamos los datos del alumno en el cuerpo de
la request.
• Actualizar un recurso de tipo "Alumno".
PUT: "/clasef{id}/alumno/ {id}" . En la request enviamos los nuevos datos del
alumno. El "id" no puede cambiar porque es el campo clave.
• Borrar un recurso de tipo "Alumno".
DELETE: "/clasef{id}/alumno/{id}" . Eliminaría al alumno concreto.
Éste sería el diseño de nuestra APl RESTful. Para ser RESTful pura debemos incluir
enlaces en las respuestas, siguiendo el principio HATEOAS. Imaginemos que realizamos
la siguiente request:
• GET: "/clase/ 1".
Con dicha petición estamos solicitando los datos de la clase cuyo "id" es l. En el
cuerpo de la respuesta (JSON) podríamos tener:
{
"id": 1,
"nombreCurso": "Curso de Patinaje",
"nombreProfesor" : "Ismael López Qui ntero",
"numeroTelefono" : "+34. 111 111111",
"ema il" : "info@cursodepatinaje.com",
"links" : [
{
"rel" : "a lum nos",
"href" : "/clase/1/alumno "
}
1
}
El principio HATEOAS se cumple al incluir información de los enlaces a las clases
referenciadas en el objeto JSON que devolvemos como respuesta.

© Alfaomega - Altaria 2211


Curso avanzado de Java

7.2.3 Códigos de estado HTTP


Los códigos de estado en HTTP son valores numéricos devueltos por el servidor en
la cabecera de la respuesta que sirven para indicar el estado del recién realizado pro-
ceso. Estos valores no pertenecen a la definición de servicios web, sino a la definición
del protocolo HTTP en sí. Tenemos varios tipos de códigos (las X son cualquier dígito):
• lXX: Información del servidor.

• 2XX: Éxito en la operación.

• 3XX: Redirecciones en la petición.


• 4XX: Error en el cliente.
• SXX: Error en el servidor.

Los códigos que enviaremos en nuestras respuestas serán los siguientes:

Operación Método Códlco Estado


' .
Obtener GET • Ex1to: 200.
• No encontrado: 404 .
• Error servidor: 500 .
Crear POST • Éxito creado: 200 o 201.
• Mala petición: 400 .
• Tipo no soportado: 4 15 .
• Error servidor: 500 .
' .
Borrar DELETE • Ex1to borrado: 200 o 204.
• No encontrado: 404 .
• Error servidor: 500 .
Editar PUT • Éxito: 200.
• Mala petición: 400 .
• No encontrado: 404 .
• Tipo no soportado: 415 .
• Error servidor: 500 .

Con el código en la cabecera de la respuesta seremos capaces de averiguar qué ha


ocurrido durante el proceso.

7.2.4 Desarrollo del back-end


Lo primero que debemos hacer es definir la base de datos. Creamos una base de datos
en MySQL llamada cursos, y accederemos a ella desde el servidor de aplicaciones. Acto
seguido creamos un proyecto que desplegamos sobre nuestro servidor de aplicaciones.
El proyecto se llama Cursos con sus respectivos módulos EJB y WAR. En el módulo

1222 © Alfaomega - Altaria


JEE. Man ual práctic o

EJB definimos la unidad de persistencia, las entidades y los stateless session beans
para dichas entidades (fachadas) .

• Unida d de persistencia "persistence.xml":


<?xm l version="l.O" encoding="UTF-8"?>
<persistence version="2.1" xmlns=" http://xmlns.jcp.org/xml/ns/persistence"
xm lns :xsi="http://www.w3 .o rg/2001/XM LSche m a-insta nce"
xsi :schem aLocation=" http ://xm lns.jcp.org/xm I/ns/persistence
http://xm 1ns.jcp .org/xm 1/ns/persistence/persiste nce_2_l.xsd ">
<persiste nce-u n it na me="Cursos-ej bP U" tra nsaction-type= "JTA">
< jta -data -so u re e >e u rsos</jta-d ata-so u ree>
<exelude-un 1i sted -e1a sses>fa lse</exelude-un 1isted -e 1a sses>
<properties>
<property na m e= "javax. pe rsistence .schem a-generation .data base .action"
value=" create-or-extend-ta bies"/>
</properties>
</persiste nce-u n it>
</persistence>

Las entidades se corresponden con el modelo que hemos visto anteriormente:

Clase Alumno

- id: Long; - id: Long;


- nombreCurso: String; ¡.. 1 * - nombreAiumno : String;
- nomb reProfesor: String; ro- - fechaNacimiento: Calendar;
- numeroTelefono: String; - numeroTelefono: String;
- email: String; - email: Stri ng;
- clase: Clase:
+ getters & sette rs;
+ getters & setters:

• Entida d "Clase.java":
package entities;
import java.io.Serializable;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
im port javax. persistence .Generati on Type;
import javax.persistence. ld;

© Alfaom ega - Altaria 2231


Curso avanzado de Java

@Entit y
public class Clase implements Serializable {
prívate static fina l long seriaiVersionUID = ll;
@Id
@GeneratedValue(strat egy = GenerationType.AUTO)
prívate Long id;
prívate String nombreCurso;
prívate String nombreProfesor;
prívate String numeroTelefono;
prívate String email;
public Long getld() {
re t urn id;
}
public void setld(Long id) {
this.id = id;
}
public String getNombreCurso() {
re t urn nombreCurso;
}
public void setNombreCurso(String nombreCurso) {
this.nombreCurso = nombreCurso;
}
public String getNombreProfesor() {
return nombreProfesor;
}
public void setNombreProf esor(String nombreProfesor) {
this.nombreProfesor = nombreProfesor;
}
public String getNumeroTelefono() {
return numeroTelefono;
}
public vo id setNu meroTelefono(St ring numeroTelefono) {
this.numeroTelefo no = numeroTelefono;
}
public String getEmail() {
return email;
}
public void setEmaii(St ring email) {
t his.ema il = email;
}
@Override
public int hashCode() {
int hash = O;
hash +=(id != null? id.hashCode() :O);
ret urn hash;
}

1224 © A lfaomega - Altaria


JEE. Manual práctic o

@Override
public boolean equals(Object object) {
if ( !(object instanceof Clase)) {
return false;
}
Clase other = (Clase) object;
if ((th is.id == null && other.id != null) 11
(this.id != null && !this.id.equa ls(other.id))) {
return false;
}
return true;
}
@Override
pu blic String toString() {
return "entities.Ciase[ id="+ id+")";
}
}

• Entidad "Alumno.java":

package entities;
import java .io.Serializable;
import java.utii.Calendar;
import javax.persistence.Entity;
im port javax. persistence .GeneratedVa 1ue;
im port javax. persistence .Generation Type;
import javax.persistence. ld;
import javax.persistence.ManyToOne;
@Entity
public class Alumno implements Serializable {
private static final long seriaiVersionU ID = ll;
@Id
@GeneratedValue(strategy = GenerationType .AUTO)
private Long id;
private String nombreAiumno;
private Calendar fecha Nacimiento;
private String numeroTelefono;
private String email;
@ManyToOne
private Clase clase;
pu blic Long getld() {
return id;
}
public void setld( Long id) {
this.id = id;
}

© Alfaomega - Altaria 2251


C urso a vanzado de Java

public String getNombreAiumno() {


return nombreAiumno;
}
public void setNombreAiumno(String nombreAiumno) {
this.nombreAiumno = nombreAiumno;
}
public Calendar getFechaNacim iento() {
return fecha Nacimiento;
}
public void setFechaNacimiento(Calendar fechaNacimiento) {
this.fechaNacimiento = fechaNacimiento;
}
public String getNumeroTelefono() {
return numeroTelefono;
}
public void setNumeroTelefono(String numeroTelefono) {
this.numeroTelefono = numeroTelefono;
}
public String getEmail() {
return email;
}
public void setEmaii(String email) {
this.email = email;
}

public Clase getCiase() {


return clase;
}
public void setCiase(Ciase clase) {
this.clase =clase;
}
@Override
public int hashCode() {
int hash =O;
hash +=(id != null? id.hashCode() :O);
return hash;
}
@Override
public boolean equals(Object object) {
if (!(object instanceof Alumno)) {
return false;
}
Alumno other =(Alumno) object;
if ((this.id == null && other.id != null) 11
(this.id != null && !this.id .equals(other.id))) {
return false;
}
return true;

1226 © A lfaomega - Altaria


JEE. Manual práctico

}
@Override
public String toString() {
return "entities.Aiumno[ id=" +id + " ]";
}
}
Y del mismo modo definimos las fachadas en el paquete "entitiesfacades".
• Clase "AbstractFacade.java". Implementa la funcionalidad. La misma que hemos
estudiado a lo largo del texto:
package entitiesfacades;
import java.utii.List;
import javax.persistence. EntityManager;
public abstract class AbstractFacade<T> {
prívate Class<T> entityCiass;
pu blic AbstractFacade( Class<T> entityCiass) {
this.entityCiass = entityCiass;
}
protected abstract EntityManager getEntityManager();
public void create(T entity) {
getE ntityMa nager(). persist( e ntity);
}
public void edit(T entity) {
getE ntityMa nager(). m e rge( e ntity);
}
public void remove(T entity) {
getE ntityMa nager(). re m ove(getE ntityM an age r(). m e rge( entity));
}
public T find(Object id) {
return getEntityManager().find(entityCiass,(long) id);
}
public List<T> findAII() {
javax.persistence.criteria .CriteriaQuery cq =
get Entity Manager(). getC rite ria Bu i 1de r(). crea teQu e ry();
cq .select( cq .from (e ntityCiass) );
re tu rn getEntityM a nager() .createQue ry( cq ).getResu ltlist();
}
public List<T> findRange(int[J range) {
javax.persistence.criteria .CriteriaQuery cq =
get Entity Manager() .getC rite ria Bu i 1de r(). ere ateQu e ry();
cq .select( cq .from (entityCiass) );
javax.persistence.Query q = getEntityManager().createQuery(cq);
q.setMaxResults(range[l) - range[O) + 1);
q .setFirstResult( ra nge [O));
return q.getResu ltlist();
}
public int count() {
javax.persistence.criteria .CriteriaQuery cq =

© Alfaomega - Altaria 2271


C urso a vanzado de Java

getE ntityM a nage r(). getCrite ria Bu i Id er(). createQu e ry();


javax.persistence.criteria.Root<T> rt = cq .from(entityCiass);
cq. se 1ect(getE ntityM a nage r() .getCrite ri aBu i Id e r(). co unt (rt));
javax.persistence.Query q = getEntityManager().createQuery(cq);
return (( Long) q.getSingleResult()).intValue();
}
}

• Interfaz local de clase "ClaseFacadeLocal.java":


package entitiesfacades;
import entities.Ciase;
import java.utii.List;
import javax.ejb.Local;
@Local
public interface ClaseFacadelocal {
void create(Ciase clase);
void edit(Ciase clase);
void remove(Ciase clase);
Clase find(Object id);
List<Ciase> findAII();
List<Ciase> findRange(int[J range);
int count();
}
• Interfaz local de a lumno "AiumnoFacad eLocal.java":
package entitiesfacades;
import entities.Aiumno;
import java.utii.List;
import javax.ejb.Local;
@Local
public interface AlumnoFacadeLocal {
void create(Aiumno alumno);
void edit(Aiumno alumno);
void remove(Aiumno alumno);
Alumno find(Object id);
List<AIu m no> fi ndAII();
List<Aiumno> findRange(int[] range);
int count();
11 Añadido.

public List<Aiumno> getAiumnosByCiaseld(Long claseld);


}
Observamos el método "getAlumnosByClaseld" añadido. A continuación, la implemen-
tación d e las interfaces (prácticamente toda la funcionalidad está en "AbstractFacade.
java"):
• "ClaseFacade.java":

1228 © A lfaomega - Altaria


JEE. Manual práctico

package entitiesfacades;
import entities.Ciase;
import javax.ejb.Stateless;
import javax. persistence. EntityManager;
im port javax. persistence.PersistenceContext;
@Stateless
public class ClaseFacade extends AbstractFacade<Ciase> implements ClaseFacadelocal {
@ PersistenceContext(unitName = "Cu rsos-ejbPU")
private EntityManager em;
@Override
protected EntityManager getEntityManager() {
return em;
}
public ClaseFacade() {
super( Clase .class);
}
}
• "AlumnoFacad e.j ava":
package entitiesfacades;
import entities.Aiumno;
import java.utii.List;
import javax.ejb.Stateless;
import javax.persistence. EntityManager;
im port javax. persistence .PersistenceContext;
import javax.persistence.Query;
@Stateless
public class AlumnoFacade extends AbstractFacade<Aiumno>
implements AlumnoFacadelocal {
@ PersistenceContext(unitName = "Cu rsos-ejbPU")
private EntityManager em;
@Override
protected EntityManager getEntityManager() {
return em;
}
public AlumnoFacade() {
su per(Aiu m no .class);
}
@Override
public List<Aiumno> getAiumnosByCiaseld(Long claseld) {
Query querySt ring = this.getEntityManager().createQuery("SELECT a FROM Alum no "
+"a WHERE a.clase.id = \""+claseld+"\" ",entities.Aiumno.class);
List<Aiumno> alumnos= queryString.getResultlist();
return alumnos;
}
}
Tenemos métodos de acceso a la base de datos. Ahora, veamos cómo implementar
la APl.

© Alfaomega - Altaria 2291


C urso a vanzado de Jav a

7.2.5 CRUD con un recurso


Llamamos recurso o recurso de nivel 1 a aquel en el que no necesitamos identificar
recursos de orden superior para poder acceder a ellos. En nuestro caso el recurso de
orden 1 es la clase. El subrecurso sería el alumno porque en nuestra lógica de nego-
cio hemos decidido filtrar los alumnos por clase. Si queremos definir las operaciones
del CRUD para el recurso, debemos definir las siguientes operaciones de nuestra API:
• Obtener los datos de todas las clases (en el caso de que hubiera varias).
G ET: "1clase".

• Obtener los datos de una clase concreta (en el caso de que hubiera varias).
GET: "/clasej{id}".
• Crear un recurso de tipo "Clase".
POST: "1 clase". En la request enviamos los datos de la clase .

• Actualizar un recurso de tipo "Clase".


PUT: "/clase/ {id}". En la request enviamos los nuevos datos . El "id" no puede
cambiar porque es el campo clave.

• Borrar un recurso de tipo "Clase".


DELETE: "/ clasej {id}". Eliminaría la clase. Si la política de eliminación es en
cascada, también eliminar los alumnos de dicha clase.

Para ello, vamos a definir en el módulo web nuestros obj etos del dominio que serán
los que se traduzcan a objetos hipermedia (XML o JSON). Básicamente, lo que haremos
será eliminar las claves ajenas. El diagrama es el que vimos en un apartado previo.

Clase
Alumno

- id: Long;
- id: Long;
- nombreCurso: String;
- nombreP rofesor: String ; i1 * ..... - nombreAiumno: String;
, - fechai\Jacimiento: String;
- numeroTe lefono : String;
- numeroTelefono: String;
- email: String;
- email: String;
- alumnos: List<Aiumno>

+getters & setters;


+getters & setters;

Esto es, cada "Clase" contendrá un listado de alumnos, pero dicho listado no
lo plasmaremos a nivel de bean, sino a nivel de subrecurso. Veamos cómo quedan de-

1230 © Alfaomega - Altaria


JEE. Manual práctico

finidos nuestros beans del dominio en el módulo WAR. Les anteponemos la letra "D"
como nota para indicar que son entidades del dominio, en concreto del acceso a datos.

• Clase "DClase.ja va":


package domain;
import java.io.Serializable;
import java.utii.ArrayList;
import java.utii.List;
im port javax.xm l. bi nd .a n notation .Xm 1RootEiem ent;
@XmiRootEiement
public class OCiase implements Serializable {
prívate Long id;
prívate String nombreCurso;
prívate String nombre Profesor;
prívate String numeroTelefono;
prívate String email;
prívate List<Enlace> en laces;
pu blic OCiase() {
this.enlaces = new ArrayList<Enlace>();
}
pu blic Long getld() {
return id;
}
public void setld( Long id) {
this.id = id;
}
public String getNombreCurso() {
return nombreCurso;
}
public void setNombreCurso(String nombreCurso) {
this.nombreCurso = nombreCurso;
}
public String getNombreProfesor() {
return nombreProfesor;
}
public void setNombreProfesor(String nombre Profesor) {
this.nombreProfesor = nombreProfesor;
}
public String getNumeroTelefono() {
return numeroTelefono;
}
public void setNumeroTelefono(String numeroTelefono) {
this.numeroTelefono =numeroTelefono;
}
public String getEmail() {
return email;
}

© Alfaomega - Altaria 2311


Curso avanzado de Java

public void setEmaii(String email) {


this.email = email;
}
public List<Enlace> getEnlaces() {
return enlaces;
}
public void setEnlaces(List<Enlace> enlaces) {
this.enlaces =enlaces;
}
public void anadirEn lace(String link, String rel) {
Enlace esteEn lace = new En lace( link,rel);
th is.en laces.add (este Enlace);
}
}

• Clase "DAlumno.java":
package domain;
import java.io.Serializable;
import java.utii.Arraylist;
import java.utii.List;
im port javax.xml.bind .annotation .XmiRootEiement;
@XmiRootEiement
public class DAiumno implements Serializable {
private Long id;
private String nombreAiumno;
private String fecha Nacimiento;
private String numeroTelefono;
private String email;
private List<Enlace> enlaces;
public DAiumno(){
this.enlaces = new Arraylist<En lace>();
}
public Long getld() {
return id;
}
public void setld(Long id) {
this.id = id;
}
public String getNombreAiumno() {
return nombreAiumno;
}
public void setNombreAiumno(String nombreAiumno) {
this.nombreAiumno = nombreAiumno;
}
public String getFechaNacimiento() {
return fecha Nacimiento;
}
public void setFechaNacimiento(String fechaNacimiento) {

1232 © A lfaomega - Altaria


JEE. Manual práctico

this.fechaNacimiento =fecha Nacimiento;


}
public String getNumeroTelefono() {
return numeroTelefono;
}
public void setNumeroTelefono(String numeroTelefono) {
this.numeroTelefono = numeroTelefono;
}
public String getEmail() {
return email;
}
public void setEma ii(String email) {
this.email = email;
}
public List<Enlace> getEnlaces() {
return enlaces;
}
public void setEnlaces(List<Enlace> en laces) {
this.en laces =enlaces;
}
public void anadirEnlace(String link, String rel) {
Enlace esteEnlace = new Enlace(link,rel);
th is.en laces.add( este En lace);
}
}
La clase "Enlace" qued a implem en tada d e la siguien te forma :
package domain;
public class Enlace {
prívate String link;
prívate String rel;
public Enlace() {}
public Enlace(String link, String rel) {
this.link = link;
this.rel = rel;
}
pu blic String getlink() {
return link;
}
public void setlink(String link) {
this.link = link;
}
pu blic String getRel() {
return rel;
}
public void setRei(String rel) {
this.rel = rel;
}
}

© Alfaomega - Altaria 2331


C urso a vanzado de Jav a

Llega el momento de crear nuestro serv1c10 web. Debemos añadir al proyecto WAR
un nuevo servicio web s iguiendo estos pasos. Creamos "RESTful Web Services from
Patterns":

Clrtoosc File: Typc:

L
2.
Choose File Type
...
Pro~t: [fi Cursos- ... ¡
Q. "'""'
Ale Typec:
C•b!~: 1t 1~sn:-
------,
web ser"1ce
· 1)1 Pc...Otcn:i:> " Web Sf!I'\'ICII! frorn VISI,X.
·-·Cil Gr«Nv Q.ES'T~I \Veb Ser\lict~ &cm Entity Clac!:e::
····ft. Hbc:rratc
:-·~ Web Ser\·ices: RESTfi..J! Wc:b Servicrs !Tan Oehlbc.sc
:-·1)1 XML RESTful Java Olent
@, RESTful .lavaSa'pt C;mt
'-·Cil Gla"Fish
@, CroS3-0rign Resource ~ Fi!ter
~.~ W.blog;c
@J JAX-RS 2.0 Ft~
:...ijl arras V
@:, JAX-RS z.o rnterceola
< > wm Se-\'ice a~ V

Crtates R.eSTtíll web serv1ces US1'001l::' ofl.ttt fcllowrto oatteu"$: Slf)Qie:m, COfllailtr-ltern.
dent<onb'olled contare<-it.em.

< Ab"ás Terrrincr Ayuda

Elegimos "Simple Root Resource":

Pasos Seled: Pattern


1. Choose Fle Type Sdcct .:J RESTful 'NCb :;c:r'llia: ck-3gn~ttc-n:
2. Sdtct: Pattem
3. Sp:Oft Rc:sourcc: Clossc::t @ ~~::~~~::~~:Q:~.~
O Ccnbliner ~Ittm
O Cli:nt<onrolled conlal>eNten

C eate a RESTful rcot reeource da!t witñ CET and PUf me!hod!: ulling lava API fi:r RESTiU! Vleb
Service OSR-311}. This pattem is us;elí.!l fa creati1o a simple HS:IOV\'crtd senlee and wrapper
:scr'licc:s fcr- imokhg WSDL~ web scrvkes.

en ttte rext oaoe vou wil be ~ dass reme~ LRl~ and represet~saticn tyoe of the reso~.rce .

< AlTos 11 Sioienb! > Terminar

Y escribimos el nombre de la clase, en este caso "ClaseResource". La ruta desde la


que accederemos será "/clase".

1234 © A lfaomega - Altaria


JEE. Manual práctico

Pasos Spec::ify Resoooroe Classes

l. Choose Füe Type Project: leursos-war


2. Select Pan.m 1
3. Spe<:rfy Resouroe Classes Locauon: 1source Packages -- 1
Res:ource Padca!)e: lres:ources:l
--1
Path: clase

Class Narne: ldasEResource 1

MiME Type: 1~================~


application/xml v1

Represen121tion0ass: ~li•=va~.l~an~g.~str§in~g~~~~~~~~~~~~~~~~¡-;I ~Selert


~=...~~

<Atrás Siguienli'! > Cancelar 11 A)Uda

A la hora de crear el servicio web debemos saber lo siguiente:

• La forma de indicar que una clase es un recurso de un servicio web es mediante


la anotación "@Path" , que contiene la ruta desde la cual es accesible el recurso.

• Cada método consume y produce tipos de datos. A cada método hay que indicarle
el tipo que produce y consume, o bien indicarlo de manera global a toda la clase.
En nuestro caso lo indicamos de manera global. Lo realizamos con anotaciones.
• La variable que seteemos con "@Context" contendrá el contexto de la petición al
servicio y podremos obtener de ella información de la petición.

• A cada método le antepondremos una notación con el método HTTP con el que
accederemos a él. Del mismo modo, a cada método le podremos ampliar la ruta
global para toda la clase.
• Haremos uso de los códigos de estado vistos para devolver, mediante el manejo
de excepciones, el código completo.

Al realizar los pasos anteriores se habrá creado una clase de configuración de nues-
tros servicios web. Tal clase se llama "ApplicationConfig.java" y su código queda tal
cual sigue:
package resources;
import java.utii.Set;
im port javax.w s. rs.core .Application;
@ javax. w s.rs.Ap p lication Path( " w ebresou rce s")
public class ApplicationConfig extends Application {

© Alfaomega - Altaria 2351


C urso a vanzado de Java

@Override
public Set<Ciass<?» getCiasses() {
Set<Ciass<?» resources = new java.utii.HashSet<>();
try {
Class jacksonProvider =
Cla ss. fo rN am e ("o rg. cod eha u s. j aek son. jaxrs .J aek so nJ son Pro vid er");
res o u re es .add (jackson Provider);
} catch (CiassNotFoundException ex) {
java. util.logging. Logger.getlogge r(getCiass() .getNa m e() ).log
(java.util.logging.Levei.SEVERE, null, ex);
}
ad d RestResou rceCiasses( re so u re es);
return resources;
}
prívate void add RestResourceCiasses(Set<Ciass<?» res o u re es) {
resources.add( re so u rces.CiaseResou re e .class );
}
}
Sin m ás, pasamos a ver cómo quedaría la API que hemos diseñado para un recurso
(el recurso " / clase/").

package resources;
import domain.DCiase;
import entities.Ciase;
i m port entitiesfacades.Ciase Facadeloca 1;
im port java.io.Unsupported EncodingException;
import java.utii.Arraylist;
import java.util.list;
im port javax.naming.lnitiaiContext;
im port javax.naming.Nam ingException;
import javax.ws.rs.core.Context;
im port javax.ws.rs.core. U rilnfo;
import javax.ws.rs.Produces;
import javax.ws.rs.Consumes;
import javax.ws.rs.DELETE;
import javax.ws.rs.GET;
import javax.ws.rs.POST;
import javax.ws.rs.PUT;
import javax.ws.rs.Path;
import javax.ws.rs.PathParam;
import javax.ws.rs.WebApplicationException;
im port javax.ws.rs.core. M ediaType;
im port javax.ws.rs.core. Response .Status;

@ Path("/clase")
@Consumes(MediaType.APPLICATION_JSON)

1236 © A lfaomega - Altaria


JEE. Manual práctic o

@Produces(MediaType.APPLICATION_JSON)
public class ClaseResource {
ClaseFacadeLocal claseFacade = lookupCiaseFacadeLocal();
@Context
private Urilnfo context;
public ClaseResource() {}
@GET
public List<OCiase> getCiases() {
List<OCiase> dCiases = new ArrayList<>();
try {
List<Ciase> clases= claseFacade.findAII();
for (Clase clase :clases) {
OCiase nueva OCiase = this.convertCiaseFromOBToOomain(clase);
nueva OCiase= this.anadirEnlaces(nuevaOCiase);
dCiases.add (nueva OCiase);
}
} catch (Exception e) {
th row new WebAppl ication Exception (Status.INTE RNAL_SERVE R_ ERRO R);
}
ret urn dCiases;
}
@POST
public OCiase crearCiase(OCiase dCiase) {
if (dCiase == null) {
throw new WebApplicationException(Status.BAO_ REQUEST);
}
try {
Clase clase= null;
clase= this.convertCiaseFromOomainToOB(dCiase);
clase Faca de .create( clase);
dCiase = this.convertCiaseFromOBToOomain(clase);
dCiase = this.anadirEnlaces(dCiase);
} catch (Exception ex) {
throw new WebApplicationException(Status.INTERNAL_SERVER_ ERROR);
}
return dCiase;
}
@GET
@ Path("/{claseld}")
public OCiase getCiaseByld(@PathParam("claseld") String claseld) {
Long id= null;
if ((claseld == null) 11 (claseld.trim()).equals("")) {
throw new WebApplicationException(Status.BAO_REQUEST);
}
try {
id = Long.valueOf(clase ld);

© Alfaomega - Altaria 2371


Curso avanzado de Java

} catch (Exception e) {
throw new WebApplicationException(Status.BAO _REQU EST);
}
Clase clase = null;
OCiase dCiase = null;
clase= claseFacade.find(id);
if (clase== nu ll) {
throw new WebApplicationException(Status.NOT_ FOUNO);
}
try {
dCiase = this.convertCiaseFromOBToOomain(clase);
dCiase = this.anadirEnlacesCon ld(dCiase);
} catch (Exception ex) {
throw new WebApplicationException(Status.INTERNAL_SERVER_ ERROR);
}
return dCiase;
}
@PUT
@Path("/{claseld}")
public OCiase updateCiaseByld(@PathParam("claseld") String claseld, OCiase dCiase) {
Long id = null;
if ((claseld == null) 11 (claseld.trim()).equals("")) {
throw new WebApplicationException(Status.BAO_REQUEST);
}
try {
id= Long.va lueOf(claseld);
} catch (Exception e) {
throw new WebApplicationException(Status.BAO_ REQUEST);
}
if (dCiase == null) {
throw new WebApplicationException(Status.BAO _REQU EST);
}
Clase clase= null;
Clase claseEnBO = null;
claseEnBO = claseFacade.find(id);
if (claseEnBO == null) {
throw new WebApplicationException(Status.NOT_FOUNO);
}
try {
clase= this.convertCiaseFromOomainToOB(dCiase);
clase .setld( Long.val ueOf( clase Id));
clase Faca de .ed it( clase);
dCiase = this.convertCiaseFromOBToOomain(clase);
dCiase = this.anadirEnlacesConld(dCiase);
} catch (Exception e) {
throw new WebApplicationException(Status.INTERNAL_SERVER_ ERROR);
}
return dCiase;
}

1238 © A lfaomega - Altaria


JEE. Manual práctic o

@OELETE
@ Path{"/{claseld}"}
public OCiase removeCiaseByld{@PathParam{"claseld") String claseld) {
Long id= null;
if {{claseld == null) 11 {claseld.trim{)).equals{"")) {
throw new WebApplication Exception{Status.BAO _RE QU EST);
}
try {
id = Long.valueOf{clase ld);
} catch {Exception e) {
throw new WebApplicationException{Status.BAO_REQUEST);
}
Clase clase = n u 11;
OCiase dCiase = null;
clase = claseFacade.find{id);
if {clase == null) {
throw new WebApplicationException{Status.NOT_FOUNO);
}
try {
dCiase = this.convertCiaseFromOBToOomain{clase);
dCiase = this.anadirEnlacesConld{dCiase);
clase Faca de. remove{ clase);
} catch {Exception e) {
th row new WebAppl ication Exception {Status.INTE RNAL_ SERVER_ ERROR);
}
return dCiase;
}
@ Path{"/{claseld}/alumno")
public AlumnoResource getAiumnoResource{) {
return new AlumnoResource{);
}
prívate Clase FacadeLocallookupCiaseFacadeLoca 1{) {
try {
javax.naming.Context e= new lnitiaiContext{);
re tu rn {Clase Faca de Local) c.loo ku p{" java :globai/Cu rsos/Cursos -ej b/"
+ "Clase Facade !entitiesfacades.Ciase Faca de Local");
} catch {NamingException ne) {
Syste m .out.println {ne .getMessage{));
throw new RuntimeException{ne);
}
}
prívate OCiase convertCiaseFromOBToOomain{Ciase clase) {
OCiase dCiase = new OCiase{);
dCiase .setld {clase .getld {) );
dCiase .setN o m breCu rso{ clase .getN o m breCurso{));
dCiase .setNom breProfesor{ clase.getNombre Profesor{));
dCiase .setN u m ero Te lefono{ clase .getN u m ero Telefono{) );
dCiase .setEm ail{ clase .getEm ai 1{));
return dCiase;
}

© Alfaomega - Altaria 2391


C urso a vanza do de Java

prívate Clase convertCiaseFromOomainToOB(OCiase dCiase)


throws UnsupportedEncodingException {
Clase clase = new Clase();
clase .setld( dCiase .getld () );
clase.setNombreCurso(new String(dCiase.getNombreCurso()
.getBytes(" IS0-8859- 1"), " UTF-8") );
clase .setNom breProfesor( new Stri ng( dCiase .getN o m b re Profesor()
.getBytes(" IS0-8859- 1"), " UTF-8"));
clase .setN u m ero Telefono( new String( dCiase .getN u mero Telefono()
.getBytes(" IS0-8859-1"), " UTF-8") );
clase .setEm ai 1( new Stri ng(dCiase .getE mail()
.getBytes(" IS0-8859- 1"), " UTF-8") );
return clase;
}
prívate OCiase anad irEnlaces(OCiase dCiase) {
long claseld = dCiase.getld();
String rel ="alumnos";
String link= context.getAbsolutePathBuilder()
.path( clase Id .toString())
.pat h("/alu m no")
.build() .toString();
dCiase.anadirEnlace(link, rel);
return dCiase;
}
prívate OCiase anadirEnlacesConld(OCiase dCiase) {
long claseld = dCiase.getld();
String rel ="a lum nos";
String link= context.getAbsolutePathBu ilder()
.path(" /al u m no")
.build() .toString();
dCiase.anadirEnlace(link, rel);
return dCiase;
}
}

Como ejemplo de funcionamiento, realicemos un conjunto de llamadas sobre la API


con el recurso desarrollado. Usaremos el cliente "Postman", que forma parte de Google
WebStore (de esta forma podemos testar nuestra API sin necesidad de programar un
cliente).

• Petición GET.
URL: "http: / / localhost:700 1/ Cursos-war jwebresources /clase" .
Datos: (vacío).
Respuesta: [].

1240 © Alfaomega - Altaria


JEE. Manual práctic o

• Petición POST.
URL: "http:/ /localhost:700 1 /Cursos-war fwebresources/ clase".
Datos:
{
"ema il": "info@cursodepatinaje .com ",
"nombreCurso": "Curso de Patinaje",
"nombreProfesor": "Javier Hernández Rodríguez",
"numeroTelefono": "+34. 111111111"
}
Respuesta:
{
"email": "info@ cursodepatinaje.com ",
"enlaces": (
{
"link": "http:// localhost:7001/Cu rsos-war/webresou rces/clase/1/alu m no",
"rel": "alumnos"
}
L
"id": 1,
"nombreCurso": "Curso de Patinaje",
"nombreProfesor": "Javier Hernández Rodríguez",
"numeroTelefono": "+34. 111111111"
}
Añadimos, del mismo modo, mediante operación POST los siguientes cursos a nues-
tra base de datos:

• Curso:
{
"ema il": "i nfo@ cursodeingles.com ",
"nombreCurso": "Curso de Inglés",
"nombreProfesor": "Alberto López Méndez",
"numeroTelefono" : "+34. 222 22 22 22"
}
• Curso .
{
"email": "info@cursodebuzo.com ",
"nombreCurso": "Curso de Buzo",
"no m breProfesor" : "Antonio Cruz Pérez",
"numeroTelefono": "+34. 333 33 33 33"
}
• Petición GET.
URL: "h ttp: f f localhost:700 1 f Cursos-warf webresourcesf clase".
Datos: (vacío).

© Alfaomega - Altaria 2411


Curso avanzado de Java

Respuesta:
[
{
"email": "info@cursodepatinaje .com ",
"enlaces": [
{
"1in k": "http ://localhost: 7001/Cu rsos -wa r/webresources/clase/1/a 1u m-
no"
'
"rel": "alumnos"
}
L
"id": 1,
"nombreCurso" : "Curso de Patinaje",
"nombreProfesor": "Javier Hernández Rodríguez",
"numeroTelefono": "+34. 111111111"
},
{
"e m ail": "info@ cu rsodei ngles.com ",
"enlaces": [
{
"link": "http://localhost:7001/Cursos -war/webresources/clase/2/a lum-
no"
'
"rel": "alumnos"
}
L
"id" : 2,
"nombreCurso": "Curso de Inglés",
"no m breProfesor": "Alberto López M éndez",
"numeroTelefono" : "+34. 222 22 22 22"
},
{
"email": "info@cursodebuzo .com",
"enlaces": [
{
"link": "http://localhost:7001/Cursos -war/webresources/clase/3/a lum-
no",
"rel": "alumnos"
}
L
"id": 3,
"nombreCurso": "Curso de Buzo",
"nombreProfesor": "Antonio Cruz Pérez",
"numeroTelefono": "+34. 333 33 33 33"
}
1

1242 © Alfaomega - Altaria


JEE. Manual práctic o

• Petición GET.

URL: "http: / / Jocalhost:700 1/ Cursos-war / webresources /clase / 2 ".

D atos : (vacío) .

Respuesta:

{
"email": "info@ cu rsodei ngles.com ",
"enlaces": [
{
"link": " http:// localhost:7001/Cu rsos-war/webresou rces/clase/2/alu m no",
"rel": "alumnos"
}
L
"id": 2,
"nombreCurso": "Curso de Inglés",
"nombreProfesor": "Alberto López Méndez",
"numeroTelefono": "+34. 222 22 22 22"
}

• Petición PUT (para act ualizar).

U RL: "http: / f localhost:700 1/Cursos-warjwebresourcesfclase / 1".

Datos :

{
"ema il" : "info@cursodepatinajeavanzado.com ",
"nombreCurso": "Curso de Patinaje Avanzado",
"nombreProfesor": "Javier Hernández Rodríguez",
"numeroTelefono": "+34. 111111111"
}

Respuesta:
{
"email": "info@ cu rsodepati najeava nzado.com ",
"enlaces": [
{
"link": "http:// localhost:7001/Cu rsos-war/webresou rces/clase/1/alumno",
"rel" : "alumnos"
}
L
"id" : 1,
"nombreCurso": "Curso de Patinaje Avanzado",
"nombreProfesor": "Javier Hernández Rodríguez",
"numeroTelefono": "+34. 111111111"
}

© Alfaomega - Altaria 2431


C urso a vanzado de Java

• Petición DELETE.
URL: "http: / / localhost:700 1 / Cursos-war jwebresources / clase/2".
Datos: (vacío) .
Respuesta:
{
"e ma il" : "info@cursodeingles.com",
"enlaces": [
{
"link": "http://localhost: 7001/Cursos-war/webresources/clase/2/a lum no",
"rel": "alumnos"
}
L
"id" : 2,
"nombreCurso": "Curso de Inglés",
"nombreProfesor" : "Alberto López Méndez",
"n umeroTelefono": "+34. 222 22 22 22"
}

• Petición GET (vemos como ya no está el curso de inglés y el curso de patinaje


es avanzado).
URL: http:/ 1localhost:700 1 /Cursos-war jwebresources/ clase.
Datos: (vacío).
Respuesta:
[
{
"email": "info@cursodepatinajeavanzado.com ",
"enlaces": [
{
"link": "http ://localh ost :7001/Cu rso s-wa r/ w ebresources/clase/1/a 1u m no",
"rel" : "alumnos"
}
L
"id": 1,
"nom breCurso": "Curso de Patinaje Ava nzado",
"nom breProfeso r": "Javier Hern ández Rod ríguez",
"numeroTelefono": "+34. 1111111 11"
},
{
"email": "info@cursodebuzo.com ",
"enlaces": [
{

1244 © Alfaomega - Altaria


JEE. Manual práctico

"link": "http ://loca lhost:7001/Cu rsos-wa r/we bresou rces/clase/3/alu m no",


"rel": "a lumnos"
}
L
"id": 3,
"nombreCurso": "Curso de Buzo",
"nombreProfesor" : "Antonio Cruz Pérez",
"numeroTelefono": "+34. 333 33 33 33"
}
1

Veamos el estado de nuestra tabla de cursos en el cliente MySQL:

.. Opciones
~"Ji ..... ... 10 EMAII. NOMBRf CURSO IIOMBREPROFf SOR HIIUMf ROTELfFONO
O .J Edtar ¡¡.; Copiar O Borrar 1 rnlo@cursodepalinajeovanzado.com Curso de Patrno¡e Avanzado Jovíer Hernández Rodríguez~ • 34. 11111 11 11
0 oJ Edll<lf !..! Copiar 0 Borrar 3 mlo@curso<lebuzo com Curso el• Bu>.o , AAiooio Crtrz e~rez ~ +34 333 33 33 33
t_ M.:ucar todos 1 Oésmarc:ar todos Para los e./otr)(;nfos quo csfán marcados: ~ Cambiar O Borrar (&¡ l:>cportnr

7.2.6 CRUD con subrecursos


Un subrecurso es enlazable desde un recurso principal. El lector entenderá perfecta-
mente el concepto d e subrecurso si piensa en el a lumno de nuestro ejemplo. No tiene
sentido obtener todos los alumnos de todos los cursos (aunque también los podríamos
obtener). Las consultas tendrán sentido si obtenemos los a lumnos de un curso. En
el recurso de "clase" hemos enlazado con el subrecurso de "alumno" con el siguiente
método :
@ Path("/(clase ld}/alum no")
public AlumnoResource getAiumnoResource() {
return new AlumnoResource();
}
De hecho, los métodos que queremos implementar ahora son:

• Obtener todos los alumnos de una clase.


GET: "1clase / {id}/ alumno".

• Obtener los datos de un alumno concreto .


GET: "1clase/ {id}/ alumno/ {id}" .
• Crear un recurso de tipo "Alumno".
POST: "/clase / {id}/alumno". Enviamos los datos del alumno en el cu erpo d e
la request.

© Alfaomega - Altaria 2451


Curso avanzado de Java

• Actualizar un recurso de tipo "Alumno".


PUT: "/clase/ {id}/ alumno/ {id}". En la request enviamos los nuevos datos del
alumno. El "id" no puede cambiar porque es el campo clave.

• Borrar un recurso de tipo "Alumno".


DELETE: /clasef{id}falumno/{id}. Eliminaría al alumno concreto.
Para ello, nuestro nuevo recurso debe estar definido con la ruta "@Path("/ ")", ya
que no será accesible desde la ruta principal, sino a través de la ruta de un recurso
de nivel l .

Añadimos el recurso "Alumno", y nuestra clase "ApplicationConfig" queda tal cual


sigue:
package resources;
import java.utii.Set;
im port javax.ws.rs.core.Application;
@javax .ws. rs.Application Path ("webresou re es")
public class ApplicationConfig extends Application {
@Override
public Set<Ciass<?» getCiasses() {
Set<Ciass<?>> resources = new java.utii.HashSet<>();
try {
Class jacksonProvider =
Cla ss. fo rN am e ("o rg. cod eha u s. j ackso n. jaxrs .J a ek so nJ son Pro vid er");
resou rces.add (jackson Provider);
} catch (CiassNotFoundException ex) {
java. util.logging. Logger.getlogge r(getCiass() .getNa m e()) .lag
(java.util.logging.Levei.SEVERE, null, ex);
}
add RestResou rceCiasses( re so u re es);
return resources;
}
prívate void addRestResourceCiasses(Set<Ciass<?>> reso urces) {
resources.add( re so u rces.AI u m noResou re e .class );
res o u rces.add( re so u rces.CiaseResou re e .class );
}
}

Vemos que en el método "addRestResourceClasses" se ha añadido la linea: "resour-


ces.add(resources.AlumnoResource.class);". Veamos la implementación de la clase
"AlumnoResource.java":
package resources;
import domain.DAiumno;
import entities.Aiumno;
import entities.Ciase;
import entitiesfacades.AiumnoFacadelocal;

1246 © A lfaomega - Altaria


JEE. Manual práctic o

im port entitiesfacades.Ciasefacade Loca 1;


import java.utii.Arraylist;
import java.utii.Calendar;
import java .utii.Gregoria nCalendar;
import java.utii.List;
import javax.naming. l nitiaiContext;
import javax.naming.NamingException;
import javax.ws.rs.core.Context;
import javax.ws.rs.core.Urilnfo;
import javax.ws.rs.Consumes;
import javax.ws.rs.DELETE;
import javax.ws.rs.Produces;
import javax.ws.rs.GET;
import javax.ws.rs.POST;
import javax.ws.rs.PUT;
import javax.ws.rs.Path;
import javax.ws.rs.PathParam;
import javax.ws.rs.WebApplicationException;
import javax.ws.rs.core.MediaType;
im port javax.ws. rs.core. Response .Status;
@Path("/")
@Consumes(MediaType.APPLICATION_JSON)
@Produces( MediaType .APP LICATION _JSO N)
public class AlumnoResource {
ClaseFacadeLocal claseFacade = lookupCiaseFacade Local();
AlumnoFacadeLocal alumnoFacade = lookupAiumnoFacadeLocal();
@Context
prívate Urilnfo context;
public AlumnoResource() {}
@GET
pu blic List<DAium no> getAiu m nosByCiaseld (@ Path Para m ("claseld") String clase Id) {
Long ICiaseld = null;
List<DAium no> dAiumnos = new Arraylist<DAium no>();
if ((claseld == nul l) 11 ((claseld.trim()).equals('"'))) {
throw new WebApplicationException(Status.BAD_REQUEST);
}
try {
ICiaseld = Long.valueOf(claseld);
} catch(Exception e) {
throw new WebApplicationException(Status.BAD_REQUEST);
}
try {
List<Aiumno> alumnos= alumnoFacade.getAiumnosByCiaseld(ICiaseld);
for(A iumno a: alumnos) {
DAiumno dA= this.convertAiumnoFromDBToDomain(a);
dAiumnos.add(dA);
}

© Alfaomega - Altaria 2471


Curso avanzado de Java

} catch(Exception e) {
throw new WebApplicationException(Status.INTERNAL_SERVER_ERROR);
}
return dAiumnos;
}
@POST
public DAiumno crearAiumno(@PathParam("claseld") String claseld, DAiumno dAiumno) {
Long ICiaseld = null;
if (dAiumno == null) throw new WebApplicationException(Status.BAD_REQUEST);
if ((claseld == null) 11 ((claseld .trim()).equals(""))) {
throw new WebApplicationException(Status.BAD_REQUEST);
}
try {
ICiaseld = Long.valueOf(claseld);
} catch(Exception e) {
throw new WebApplicationException(Status.BAD _REQUEST);
}
Clase clase= claseFacade.find(ICiaseld);
if (clase==null) {
throw new WebApplicationException(Status.NOT_FOUND);
}
try {
Alumno alumno = this.convertAiumnoFromDomainToDB(dAiumno);
al um no.setCiase( clase);
a1u m noFacad e .create( alumno);
dAiumno = this.convertAiumnoFromDBToDomain(alumno);
} catch(Exception e) {
throw new WebApplicationException(Status.INTERNAL_SERVER_ERROR);
}
return dAiumno;
}
@GET
@Path("/{alum nold}")
public DAiumno getAium noByld(@PathParam( "claseld ") String claseld,
@PathParam("alumnold" ) String alumnold) {
Long ICiaseld = null;
Long IAiumnold = null;
DAiumno dA = null;
if ((claseld == null) 11 ((clase ld .trim()).equals("")) 11
(a lumnold == null) 11 ((alumnold.trim()).equals(""))) {
throw new WebApplication Exception(Status.BAD _REQU EST);
}
try {
ICiaseld = Long.valueOf(claseld);
IAiumnold = Long.va lueOf(alumnold);
} catch(Exception e) {
throw new WebApplicationException(Status.BAD_REQUEST);
}

1248 © A lfaomega - Altaria


JEE. Manual práctico

Alumno a= alumnoFaeade.find(IAiumnold);
if (a== null) throw new WebApplieationExeeption(Status.NOT_FOUND);
Clase e= a.getCiase();
if (e.getld().equals(ICiaseld)) {
dA= this.eonvertAiumnoFromDBToDomain(a);
} else {
throw new WebApplieation Exeeption(Status.BAD_REQU EST);
}
return dA;
}
@PUT
@ Path("/{alum nold}")
publie DAiumno updateAiumnoByld(@PathParam("elaseld") String elaseld,
@PathParam("alumnold") String alumnold, DAiumno dAiumno) {
Long ICiaseld = null;
Long IAiumnold = null;
DAiumno dA= null;
if ((claseld == null) 11 ((claseld.trim()).equals("")) 11
(alumnold == null) 11 ((alumnold.trim()).equals(""))) {
throw new WebApplieationExeeption(Status.BAD_REQUEST);
}
try {
ICiaseld = Long.valueOf(elaseld);
IAiumnold = Long.valueOf(alumnold);
} eateh(Exeeption e) {
throw new WebApplieationExeeption(Status.BAD_REQUEST);
}
if (dAiumno == null) {
throw new WebApplieationExeeption(Status.BAD_REQUEST);
}
Alumno a= alumnoFaeade.find(IAiumnold);
if (a== null) throw new WebApplieationExeeption(Status.NOT_FOU ND);
Clase e= a.getCiase();
if (e.getld().equals(ICiaseld)) {
try {
Alumno alumno= this.eonvertAiumnoFromDomainToDB(dAiumno);
a1u m no.setld (IAium nold );
a1u m no.setCiase(e);
alumnoFaeade.edit(a lum no);
dA= this.eonvertAiumnoFromDBToDoma in(alum no);
} eateh(Exeeption e) {
throw new WebApplieationExeeption(Status.INTERNAL_ SERVER_ERROR);
}
} else {
throw new WebApplicationExeeption(Status.BAD _REQU EST);
}
return dA;
}

© Alfaomega - Altaria 2491


C urso a vanza do de Java

@DELETE
@Path("/{a lum nold}")
public DAiumno deleteAiumnoByld(@PathParam("claseld") String claseld,
@PathParam("alumnold") String alumnold) {
Long ICiaseld = null;
Long IAiumnold = null;
DAiumno dA= null;
if ((claseld == null) 11 ((clase ld.trim()).equa ls("")) 11
(alumnold == null) 11 ((alumnold.trim()).equals(""))) {
throw new WebApplicationException(Status.BAD _REQU EST);
}
try {
ICiaseld = Long.valueOf(claseld);
IAiumnold = Long.valueOf(alumnold);
} catch(Exception e) {
throw new WebApplicationException(Status.BAD _REQU EST);
}
Alumno a= alumnoFacade.find(IAiumnold);
if (a== nu ll) throw new WebApplicationException(Status.NOT_FOUND);
Clase e = a.getCiase{);
if (c.getld{).equals(ICiaseld)) {
dA= this.convertAiumnoFromDBToDomain(a);
al um noFacade.remove(a);
} else {
throw new WebApplicationException(Status.BAD _REQU EST);
}
return dA;
}
private DAiumno convertAiumnoFromDBToDomain(Aiumno alumno) {
DAiumno dAiumno = new DAiumno{);
dAiu m no.setld (a lum no.getld{) );
dAiu m no.setNom breAiu m no( a lum no.getN o m breAium no());
Calendar fecha= alumno.getFechaNacimiento{);
int ano = fecha.get(Cale ndar.YEAR);
int mes= fecha.get(Calendar.MONTH) + 1;
int dia = fecha.get(Calendar.DAY_ OF _MONTH);
String diaString = (dia < 10) ? "O" + dia : ""+dia;
String mesString = (mes< 10) ? "O" + mes : ""+mes;
String anoString = "" +ano;
String fechaNacim iento = diaString + "/" + mesString + "/" + anoString;
dAiu m no.setFecha Nacim iento(fecha Nacim ie nto );
dAiu m no .setE m ai 1( a 1um no .getE m a il{)};
dAiu m no.setN u m eroTelefono( a lum no.getNu mero Telefono{));
return dAiumno;
}
private Alumno convertAiumnoFromDomainToDB(DAiumno dAiumno)
throws Exception {
Alumno alumno= new Alumno{);
alu m no.set ld (dAium no .getld () );

1250 © A lfaomega - Altaria


JEE. Manual práctico

alumno.setNombreAiumno(new 5tríng(dAiumno .getNombreAiumno()


.getBytes(" 150-8859-1"), "UTF-8" ));
Ca len dar fecha N aci m íento = thís.getCalenda rF rom5trí ng( dAiu m no .getFecha Nací miento());
a lum no.setFechaNacim íento( fecha N acím iento );
a lu m no.setN umeroTelefono( new 5tring( dAIu m no .getN u mero Telefono()
.getBytes(" 150-8859-1"), "UTF-8" ));
alumno.setEmaíl(new 5tríng( dAiumno.get Ema íl()
.getBytes(" 150-8859-1" ), "UTF-8" ));
return alumno;
}
prívate Calendar getCalendarFromStríng(Stríng fecha) throws Exception {
Stríng[] numeres= fecha.split("/");
íf (numeros.length == 3) {
5tríng día5tring = numeros[O);
5tríng mes5tríng = numeros(1];
5tríng ano5tríng = numeros[2);
try {
ínt día= lnteger.parselnt(díaStríng);
int mes = lnteger.parselnt(mes5tríng)- 1;
ínt ano= lnteger.parselnt(anoStríng);
return new GregoríanCalendar(ano,mes,día);
} catch(Exception e) {
throw e;
}
} else {
throw new Exception();
}
}
prívate AlumnoFacadeLocal lookupAiumnoFacadeLocal() {
try {
javax.namíng.Context e= new lnítiaiContext();
re tu rn (Al u m noFacadeLoca 1) e.lookup("java :globai/Cu rsos/Cu rsos -ej b/"
+ "Al u m no Faca de! e ntitiesfaca des.AIum noFacadeLoca 1" );
} catch (NamíngException ne) {
5ystem .out.príntln (ne .getM essage() );
throw new RuntimeException(ne);
}
}
prívate ClaseFacadeLocallookupCiaseFacadeLocal() {
try {
javax.namíng.Context e= new lnítiaiContext();
re tu rn (Clase Faca de Loca 1) c.looku p("java :globai/Cu rsos/Cu rsos-ej b/"
+ "Clase Faca de !entitiesfacades.Ciase Faca de Local" );
} catch (NamíngException ne) {
5ystem .out.príntln (ne .getM essage() );
throw new RuntimeException(ne);
}
}
}
Con este código queda definido el servicio web completo de clases y alumnos.

© Alfaomega - Altaria 2511


Curso avanzado de Java

7.3 Ejercicio 6
Se pla ntea al lector la implem enta ción d e una APl RESTful usando JAX-RS para el
eje rcicio d el blog, e n concreto para los modelos de "BlogPost" y d e "Comenta rioPost".

BlogPost
ComentarioPost
- id: Long;
- fechalnstante: String;
- contenido: String; - fechalnstante: Strin g;
- autor: String;
,.... comentari o: String;
- comentari os: List<ComentarioPost>; - usuario String;

+ getters & setters; +getters & setters;

El recurso de nivel 1 es "BlogPost" y el subrecurso es "ComentarioPost". En concreto,


la APl debe tener los siguientes métodos:

• Obtener todos los posts.


G ET: "/post".
• Obten e r los d a tos d e un post concreto.
G ET: "/post/ {id}".
• Crear un recurso de tipo "Post". El usuario debe existir en la base de datos. Si
no existe, se debe devolver un error de tipo "Bad Request".
POST : "/post". E n la request envia mos los datos d el post en formato JSON.

Y para el subrecurso:

• Obtener todos los comentarios de un post.


G ET: "1post/ {id}1comentario".
• Crear un recurso de tipo "Comentario".
POST: "/post/ {id}/ comen tario". En la request envia mos los da tos d el come n -
tario en formato JSON.
No pondremos enlaces a los comentarios siguiendo el principio HATEOAS, ya que
nos conlleva modificar el modelo del dominio y es un ejemplo simple.

1252 © A lfaomega - Altaria


,

Websockets CAPITULO

Los websockets son la solución del protocolo TCP (subprotocolo WS) que permite
una comunicación asíncrona entre el navegador del usuario y el servidor web que le ha
despachado la página. Con los websockets conseguimos soluciones óptimas en tiempo
real, como chats y redes sociales. No necesitamos peticiones HTTP para comunicar en
segundo plano el navegador con el servidor (ni peticiones HTTP tradicionales ni vía
AJAX).

8.1 Lado del cliente


JavaScript de forma nativa permite la ejecución de websockets. La clase "WebSocket"
está disponible en el navegador y podemos crear instancias de ella con la siguiente
instrucción (vemos código JavaScript en el lado del cliente):

var webSocket = new WebSocket ("ws://localhost/DireccionServidorWebSocket ");

Evidentemente, hemos de sustituir localhost por la dirección IP concreta o por el


dominio que apunte a nuestro servidor web.

Es fácil enviar mensajes de texto al servidor con el websocket abierto. La nomencla-


tura que usaremos para enviar mensajes a l servidor es la siguiente:
var mensaje= 'mensaje de texto';
swebSocket.send(mensaje);

© Alfaomega - Altaria 2531


Curso avanzado de Java

Para poder tener más potencia en el envío de información, recordamos que cualquier
mensaje XML o JSON se puede "codificar" en una cadena de texto. Imaginemos que
queremos enviar al servidor el siguiente mensaje:
var operacion = {
definicion: "sa ludo",
contenido: "Hola a todos"
};
var mensaje= JSON.stringify(operacion);
11 Suponemos que ya tenemos creado el websocket.
webSocket.send(mensaje);

Como se puede ver, la clase JSON, que también tenemos disponible de forma nativa
en JavaScript, permite "codificar" cualquier objeto JavaScript en una cadena de texto.

Es tan simple el uso de websockets en JavaScript que sólo nos queda por estudiar
dos operaciones más: una la que se lanza (evento) cuando se recibe un mensaje desde
el servidor y otra la que lanza el navegador cuando quiere cerrar el socket. Suponiendo
que tenemos la variable "webSocket" con el socket abierto.
webSocket.onmessage = function(mensaje) {
};

El mensaje que recibimos también vuelve a ser una cadena de texto, pero podemos
usar la función de "jQuery $ .parseJson(mensaje)" para obtener el objeto JSON a partir
de la cadena.

La otra operación es la que cierra el socket:


webSocket.close();

De todas formas, el socket también se puede cerrar en caso de inactividad, por eso
es bueno enviar cada cierto tiempo al servidor una señal indicando que el cliente s igue
vivo, por ejemplo:
fu nction setKeepAiive () {
setlnterval(function () {
var operacion = {
11 definición de la operación
};
var mensaje= JSON.stringify(operacion);
w ebSocket.send(mensaje);
}, 4000);
};
Dicho fragmento de código le envía al servidor una señal cada cuatro segundos. Con
ello, conseguimos que el servidor tenga constancia de la vida del cliente.

1254 © Alfaomega - Altaria


JEE. Manual práctico

8.2 Lado del servidor


Dependiendo del lenguaje, en el lado del servidor tendremos una implementación
u otra. En Java, y formando parte de JEE, tenemos la solución "ServerEndpoint",
disponible en el paquete "javax.websocket.server". Hemos visto que las comunicacio-
nes mediante websockets las maneja el cliente, excepto en el caso en que el servidor
considere que un cliente ha expirado por inactividad. Veamos cómo implementar una
clase "ServerEndpoint" de forma que actúe como singleton para recibir las llamadas
websocket de la aplicación:
package socket;
import java.io .IOException;
import java.utii.HashMap;
import java.utii.Map;
import javax.websocket. *;
im port javax.websocket.server.ServerE nd poi nt;
import socket.modelo.Operacion;
import utii.JSONMa nager;
@Serve rE nd poi nt( "/we bsoc ketse rve r")
public class WebSocketServer {
prívate static M ap<String, Session> sesiones= new HashMap<String, Session>();
@OnOpen
pu blic void abrirSocket(Session ses ion Usuario) {
sesiones. put(sesion Usuario .getld (), sesionUsua rio);
}
@OnMessage
public void recibeMensaje(String mensaje, Session sesionUsuario)
throws IOException {
Object operacion = JSONManager.generateTOfromJson(mensaje, Operacion.class);
11 Tratamos la información que nos ha llegado.
Respuesta r = new Respuesta();
String mensajeTexto = JSON Manager.generateJson(r);
ses ion Usua rio.getBasicRemote() .se ndText( mensaje Texto);
}
@OnCiose
pu blic void cerrarSocket(Session sesionUsua rio) {
sesiones. re m ove( sesion U su ario.getl d () );
}
}

Si queremos conectarnos a dicho punto de servidor desde un cliente JavaScript,


suponiendo que el servidor esté a lojado en localhost, haríamos la siguiente llamada
(desde JavaScript):

var webSocket = new WebSocket("ws://localhost/websocketserver ");

© Alfaomega - Altaria 2551


Curso avanzado de Java

Con el "Map" conseguimos tener un listado de los clientes que se encuentran co-
nectados mediante websocket, para hacer una comunicación multicast, llegado el caso.

Hemos h echo uso de la cla se "JSONMa n a ge r" con m é todos está ticos . Dicha clase
queda codificada ta l y como podre mos ve r a continua ción. Indicar que hace uso de la
librería GSon, de libre distribución (la podemos descargar desde repositorio Git). Con
ella conseguimos convertir objetos de Java en objetos JSON codificados en cadenas de
texto, y viceversa.

La cla s e "JSONManager" queda tal cual sigue:


package util;
import com.google.gson.Gson;
public final class JSONManager {
prívate static Gson gson = new Gson();
public static String generateJson(Object to) {
return null == to? 1111 : gson.toJson(to);
}
public static Object generateTOfromJ son(St ring json, Class<?> classl ) {
return gson.fromJson (json, classl);
}
}

8.3 Implementación de una sala de chat


Mostremos el código de servidor y de cliente de una sala de chat. El código de ser-
vidor es el siguiente:
package socket;
import java.io.IOException;
import java.io.StringWriter;
import java.utii.Collection;
im port java.util. HashM ap;
import java.util.lterator;
im port java.util. Map;
import javax.json.Json;
import javax.json.JsonObject;
import javax.json.JsonWriter;
import javax.websocket.OnCiose;
import javax.websocket.OnMessage;
import javax.websocket.Onüpen;
import javax.websocket.Session;
im port javax.we bsocket.server.ServerEnd point;
@ ServerEnd poi nt( 11 /chatsocket 11 )
public class ChatSocket {
prívate static Map<String,Session> usuarios= new HashMap<String,Session>();
@Onüpen

1256 © A lfaomega - Altaria


JEE. Manual práctico

pu blic void abrirSocket(Session ses ion Usuario) {


usuarios. put( sesion Usua río .getld (), ses ion Usuario);
}
@OnMessage
public void recibeMensaje(String mensaje, Session sesionUsuario)
throws IOException {
String nom bre= {St ring) sesionUsuario.getUserProperties().get{"nom bre");
if (nombre == null) {
ses ion Usua rio.getU serProperties(). put(" nombre", mensaje);
ses ion U su a rio.getBasicRem ote()
.sendText(mensajeJson("Sistema","Te has conectado como"+ mensaje));
} else {
Collectio n<Session> sesiones = usuarios.values();
lterator it = sesiones.iterator();
while(it.hasNext()} {
Session estaSesion = (Session) it.next();
estaSes ion. get Ba sicRe m ote ()
.sendText( mensajeJson (nombre, mensaje));
}
}
}
@OnCiose
pu blic void cerrarSocket(Session sesionUsua río) {
usu aríos. re m ove( sesio n Usuario. ge t id ());
}
public String mensajeJson(String nombre, String mensaje) {
StringWriter sTringWriter = new StringWriter();
JsonWriter jsonWriter = Json .createWriter(sTringWriter);
JsonObject jSON = Json.createObjectBu ilder()
.add("mensaje", nombre+":"+ mensaje+'"' .).build();
json Writer.w rite(jSO N);
return sTringWriter.toString();
}
}

El método "mensajeJson(String nombre, String mensaje)" propuesto envía al cliente:


"mensaje" = {
nombre : mensaje
}

En el lado del cliente tendremos un fichero "index.html" (HTML estándar) con el


siguiente contenido:
<htm l>
<head>

© Alfaomega - Altaria 2571


C urso a vanzado de Java

<title>Chatsocket</tit le>
<meta charset="UTF-8">
<meta na m e= "viewport" content="width=device-width, in itia 1-sca le= l. O">
<script type= "text/javascri pt" src=" js/jq uery-3 .1.1. m in .js"></scri pt>
<script type="text/javascript">
var websocket = new WebSocket("ws://localhost:7001/Chat-war/chatsocket");
websocket.onmessage = function procesarRespuesta(mensaje) {
var datosJson = $.parseJSON(m ensaje.data);
if (datosJson.mensaje!==null) {
mensajetextoarea.value =
mensajetextoarea.va lue += datosJson.mensaje + "\n";
}
};
function enviar() {
websocket.send{mensajetexto.value);
mensajetexto.value = "";
}
</script>
</head>
<body>
<t extarea id="mensajetextoarea"
readonly=" readon ly" rows=" 10" cols=" 45 "></textarea> <br />
<input type="text" id="mensajetexto" size="SO" />
<input type="button" value="Enviar" onCiick="enviar();" />
</body>
</html>

Una buena práctica para el lector es implementar este ejemplo en un proyecto web
(W AR) llamado Chat-war. *
*Este ejercicio es d e dominio público y s e pued e encontra r en diversos blogs y ej em -
plos en videotutoriales. En nuestro caso, el lector puede seguir un videotutorial con
este ejemplo en "https:f fwww.youtube.comjwatch?v=BikL52HYaZg".

8.4 Ejercicio 7
Se propone al lector la finalización de la aplicación web d el blog, implementando
mediante websockets las operaciones de:

• Escribir un post.
• Aña dir un comentario a un post.

• Establecer una conversación d e chat con un usua rio.


• Enviar mensajes de chat.

1258 © A lfaomega - Altaria


JEE. Manual práctico

De modo que mediante JSF realizaremos la gestión de plantillas y la entrada o salida


del blog (login/ logout).
Los mensajes que se enviarán serán los siguientes:

• Del servidor al cliente.


Mensaje de flash con información o error.
Mensaje con el estado del cliente.

• Del cliente al servidor.


Mensaje descriptivo d e la operación del usuario.

En el servidor, tendremos las siguientes clases:


• Para enviar mensaje flash:
package estadoclíente;
ímport java.ío.Seríalízable;
publíc class Mensaje Flash ímplements Seríalízable {
prívate Stríng operacíon;
prívate Stríng titulo;
prívate Stríng error;
prívate Stríng mensaje;
prívate Stríng nombreUsuarío;
publíc MensajeFiash() {
th ís.operacíon="mensajefl ash ";
}
publíc Stríng getOperacíon() {
return operacíon;
}
publíc voíd setOperacion(Stríng operacíon) {
thís.operacíon = operacíon;
}
public Stríng getTítu lo() {
return titulo;
}
publíc voíd setTítu lo(Stríng titulo) {
thís.titulo =titulo;
}
pu blíc Stríng getError() {
return error;
}
publíc voíd setError(Stríng error) {
thís.error = error;
}
publíc Stríng getMensaje() {
return mensaje;
}

© Alfaomega - Altaria 2591


Curso avanzado de Java

public void setMensaje(String mensaje) {


this.mensaje =mensaje;
}
public String getNombreUsuario() {
return nombreUsuario;
}
public void setNombreUsuario(String nombre Usuario) {
this.nombreUsuario = nombreUsuario;
}
}

Los campos que recibirá el cliente serán tratados con GSon. O sea, cuando se envíe
un mensaje flash al cliente le llegará así:
var mensaje= {
"operacion" : "mensajeflash",
"titulo" : "El mejor blog del mundo",
"error" :"Mensaje de error obtenido desde el back-end",
"mensaje" : "Información de cualquier tipo no errónea",
"nombreUsuario" :"nombre del usuario logado"
};

La clase que plasmará el estado de un usuario será la siguiente:


package estadocliente;
i m port java.util. List;
public class EstadoCiiente {
private String operacion;
private String nombreUsuario;
private List<Usuario> usuarios;
private List<Post> posts;
private String chatActivo; 11 nombreUsuario.
private List<MensajeChat> chats; 11 Listado de chats con el usua rio activo.
public EstadoCiiente(){
this.operacion ="actualiza";
}
public String getOperacion() {
return operacion;
}
public void setOperacion(String operacion) {
this.operacion =operacion;
}
public String getNombreUsuario() {
return nombreUsuario;
}
public void setNombreUsuario(String nombreUsuario) {
this.nombreUsuario = nombreUsuario;
}

1260 © Alfaomega - Altaria


JEE. Manual práctico

public List<Usuario> getUsuarios() {


return usuarios;
}
public void setUsuarios(List<Usuario> usuarios} {
this.usuarios = usuarios;
}
pu blic List<Post> getPosts() {
return posts;
}
public void setPosts(List<Post> posts} {
this.posts = posts;
}
pu blic String getChatActivo() {
return chatActivo;
}
public void setChatActivo(String chatActivo) {
this.chatActivo = chatActivo;
}
public List<MensajeChat> get Chats() {
return chats;
}
public void setChats(List<M ensajeChat> chats) {
this.chats =chats;
}
}

Todas son autodescriptivas menos la de usuario. La de usuario contendrá el nombre,


si es el propio usuario de la sesión (booleano), y si se está chateando con él (booleano).
La clase quedaría tal cual sigue:
package estadocliente;
import java.io .Serializable;
public class Usuario implements Serializable {
prívate String nombre;
prívate boolean yoM ismo;
prívate boolean chateando;
public Usuario(){}
public String getNombre() {
return nom bre;
}
public void setNombre(String nombre) {
this.nombre = nombre;
}
public boolean isYoMismo() {
return yoMismo;
}

© Alfaomega - Altaria 2611


C urso a vanza do de Java

public void setYoMismo(boolean yoMismo) {


this.yoMismo = yoMismo;
}
public boolean isChateando() {
return chateando;
}
public void setChateando(boolean chateando) {
this.chateando =chateando;
}
}

Cada vez que ocurra un evento registrado por el servidor, se enviará a cada cliente
la información de su estado. Al mismo tiempo, cada cliente enviará al servidor cada
cuatro segundos una señal indicando que está vivo.

Del cliente al servidor enviaremos el siguiente objeto:


var operacion = {
definicion: "",
nombreUsuario: "",
idPost: "",
contenidoTexto : "",
usuarioChat: ""
};

Donde definición puede tomar a lguno de los siguientes valores:


• keepalive .
• login .
• escribePost .
• escribeComen tario .
• chateacon .
• enviachat .

Y los demás campos toman valores según la definición de la operación. Se propone


al lector terminar de implementar el blog con toda la funcionalidad especificada:

• Login (JSF).

• Escribir un post (websockets).


• Añadir un comentario a un post (websockets).
• Establecer una conversación de chat con un usuario (websockets).
• Enviar mensajes de chat (websockets).
• Logout (JSF).

1262 © Alfaomega - Altaria


,

Ejercicios CAPITULO

resueltos

9.1 Ejercicio 1
La estructura de carpetas del proyecto es la siguiente:

Projects X Files Prestaciones


El ·fjp BlocNotas
' .
j $· (i Paquetes de fuentes
¡. .! .$···~. bl001otas.
~
:
:
:
: •....
:
:
:
Bloc:Notas..java
'
i :

$ . 99 dominio
: :
! :
: :
! :
~ l.JbroNotas..java
: :
'~ ' '
i
;
;
;
¡ L:~ Nota.java
!' !' 8·133 servicio
!' ;
¡' i L. ~ Utilidad.java
' '
''
'' á~ Paquetes de prueba
¡'' 8· (j Bibliotecas
¡'
' ffi . fli Bibliotecas de pruebas
'

• Modelo del dominio. Clase "Nota.java":


packa ge dominio;
import java.utii.Calendar;
public class Nota {
pu blic Nota(){}
prívate Strin g texto;
prívate Calendar fecha;
public String getTexto() {
return texto;
}

© Alfaomega - Altaria 2631


Curso avanzado de Java

public void setTexto(String texto) {


this.texto =texto;
}
public Calendar getFecha() {
re t urn fecha;
}
public void setFecha(Calendar fecha) {
this.fecha =fecha;
}
}

• Clase "LibroNotas.j ava":


package dominio;
import java.utii.Arraylist;
import java.utii. List;
import servicio.Utilidad;
public class libro Notas {
private list< Nota> notas;
public libroNot as() {
this.notas = new Arraylist<>();
}
public List<Nota> getNotas() {
return notas;
}
public void setNotas(list<Nota> notas) {
this.notas = notas;
}
public boolean anad irNota(Nota nota) {
th is. notas.add( nota);
this.notas = Utilidad.ordenarNotasBurbuja Fecha(this. notas);
re t urn t rue;
}
}
• Paquete de servicio. Clase "Utilidad.java":
package servicio;
import dom inio.Nota;
import java.io.Buffered Reader;
import java.io.lnputSt reamReader;
impo rt java.utii.Calendar;
im po rt java.utii.GregorianCalenda r;
import java.utii. List;
public class Utilidad {
private static BufferedReader br = null;

1264 © A lfaomega - Altaria


JEE. Manual práctico

pu blic static void mostrarM en u() {


System .out. p rintln (" 1.- 1nsertar nota".);
System.out.println("2.- Ver notas".);
System.out.println("3.- Salir".);
System .out. p rintln (" ** * *");
}
public static Nota tomarNota() {
m ostra rTexto(" --NUEVA NOTA--");
=
Calendar fecha recogerFecha(" -FECHA-");
mostra rTexto("-TEXTO-");
String texto= recogerTexto("Nota");
Nota n = new Nota();
n .setFecha( fecha);
n.setTexto(texto );
return n;
}
public static void mostrarNotas(List<Nota> notas) {
if ( (notas!=null) && (!notas.isEmpty())) {
System.out.println("--LISTADO DE NOTAS--");
inti=l·
'
for(Nota n :notas) {
Calendar fecha= n.getFecha();
String texto = n .getTexto();
String fechaCadena = convertirFechaCadena(fecha);
System.out.println("Nota "+ i +".Fecha:"+ fechaCadena
+ ". Nota: " + texto+"".);
i++·
'
}
} else {
mostrarTexto(": No existen notas que mostrar".);
}
}
public static void mostrarTexto(String textoMostrar) {
System .out. p rintln (texto Mostrar);
}
public static String recogerTexto(String textoMostrar) {
if (br == null) br = new BufferedReader(new lnputStreamReader(System.in));
String entrada =null;
while (entrada == null) {
System.out .print(textoMostra r + ": ");
try {
entrada = br.readline();
} catch(Exception e) {}
}
return entrada;
}

© Alfaomega - Altaria 2651


C urso a vanza do de Java

public static int recogerVa lor(int min1 int max1 String textoMostrar) {
if (br == null) br = new BufferedReader(new lnputStreamReader(System.in));
String entrada;
int value = -1000;
while ((va lue <m in) 11 (value > max)) {
System.out.print(textoMostrar + ": ");
try {
entrada= br.readline();
value = lnteger.parselnt(entrada);
) catch (Exception e) {}
}
return value;
}
public static Calendar recogerFecha(String textoMostrar) {
if (br == null) br = new BufferedReader(new lnputStreamReader(System.in));
System .out.printl n(textoMostra r);
mostrarMeses();
int mes = recogerValor(1 12 "Seleccione mes");
1 1

int ano= recogerValor(2016 2030 "Seleccione año entre 2016 y 2030");


1 1

int dia = O·1


if ((mes== 1) 11 (mes== 3) 11 (mes== S) 11 (mes== 7) 11 (mes== 8}
11 (mes== 10) 11 (mes== 12)) {
dia = recogerValor(1 31 "Seleccione el día entre 1 y 31");
1 1

} else {
if (mes== 2) {
if(esBisiesto(ano)) {
dia = recogerValor(1 1 291 "Seleccione el día entre 1 y 29");
} else {
dia = recogerValor(1 1 281 "Seleccione el día entre 1 y 28");
}
} else {
dia = recogerValor(1 30 "Seleccione el día entre 1 y 30");
1 1

}
}
mes= mes - 1·1

return new GregorianCalendar(anolmes dia); 1

}
private static String convertirFechaCadena(Calendar fecha) {
int ano= fecha.get(Calendar.YEAR);
int mes = fecha.get(Calendar.MONTH) + 1;
int dia = fecha.get(Calendar.DAY_ OF _MONTH);
String anoString = "" +ano;
String mesString = (mes< 10) ? "O" + mes : "" + mes;
String diaString = (dia < 10) ? "O" + dia : "" + dia;
String fechaString = diaString + "/" + mesString + "/" + anoString;
return fechaString;
}

1266 © A lfaomega - Altaria


JEE. Manual práctico

private static void mostrarMeses() {


System.out.println("Los meses del año"); 11 Días
System.out.println("l.- Enero".); 11 31
System.out.println("2.- Febrero".); 11 28 o 29
System.out.println("3.- Marzo".); 1/ 31
System.out.println(" 4.- Abril".);// 30
System.out.println("S.- Mayo" .);// 31
System.out.println("6.- Junio".); 11 30
System.out.println("7.- Julio".); 11 31
System.out.println("8.- Agosto".); 11 31
System.out.println("9.- Septiembre".);// 30
System.out.println("10.- Octubre".);/1 31
System.out.println("ll.- Noviembre".); 11 30
System.out.println("12.- Diciembre".); 11 31
}
private static boolean esBisiesto(int ano) {
boolean bisiesto= fa lse;
int mod4 = ano%4;
int mod100 = ano%100;
int mod400 = ano%400;
if ( (mod4 ==O) && ( (!(mod100==0)) 11 (mod400 ==O)) ) {
bisiesto= true;
}
return bisiesto;
}
public static List<Nota> ordenarNotasBurbujaFecha(List<Nota> listaNotas) {
if ((listaNotas != null) && (listaNotas.size() >= 2)) {
int nNotas = listaNotas.size();
for( int i = 1; i < nNotas; i++) {
int max = nNotas- i;
for(int j =O; j < max; j++) {
Nota notaJ = listaNotas.get{j);
Nota notaJl = listaNotas.get{j+1);
Ca lendar fechaJ = notaJ.get Fecha();
Ca lendar fechaJl = notaJ1.getFecha();
int comp = fechaJ.compareTo(fechaJl);
if (comp >O) {
listaNotas.set{j, notaJ1);
listaNotas.set{j+1, notaJ);
}
}
}
}
return listaNotas;
}
}

© Alfaomega - Altaria 2671


Curso avanzado de Java

• Paquete "blocnotas". Clase "BlocNotas.java ". Método "main()".


package blocnotas;
import dominio.LibroNotas;
import dominio.Nota;
import servicio.Util idad;
public class BlocNotas {
public static void main(String[J args) {
int valor;
LibroNotas libro= new LibroNotas();
Utilidad.mostrarTexto("BLOC DE NOTAS");
do {
Uti 1idad. m ostra rMenu ();
va lor= Utilidad.recogerValor(l, 3, "Seleccione una opción");
if (valor== 1) {
Notan= Utilidad.tomarNota();
libro.a nad irNota( n );
} else if (valor== 2) {
Utilidad. m ostra rN otas(libro .getNotas());
}
} while (va lor != 3);
}
}

9.2 Ejercicio 2
Nuestro paquete de datos "data" en el proyecto del blog queda de la siguiente forma:

PrQLects X f iJes Prestacione s -


Blog

Source Pawges.
H -·1+1 data
. · ~ Chat.java
· ~ ChatActual.java
ComentaPostjava
Post. java
Usuario .java

El código de las entidades "Usuario", "ComentaPost", "Chat" y "ChatActual" se mues-


tran a continuación.

• "Usua rio.java ":


package data;
import java.io.Seri alizable;

1268 © A lfaomega - Altaria


JEE. Manual práctic o

import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
im port javax. persistence .Generation Type;
import javax.persistence. ld;
@Entity
public class Usuario implements Serializable {
prívate static final long seriaiVersionU ID = ll;
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
prívate Long id;
prívate String nombreUsuario;
pu blic Long getld() {
return id;
}
public void setld(Long id) {
this.id = id;
}
public String getNombreUsuario() {
return nombreUsuario;
}
public void setNombreUsuario(String nombreUsuario) {
this.nombreUsuario = nombreUsuario;
}
@Override
pu blic int hashCode() {
int hash =O;
hash +=(id != null ? id.hashCode() :O);
return hash;
}
@Override
public boolean equals(Object object) {
if (!(object instanceof Usuario)) {
return false;
}
Usuario other =(Usuario) object;
if ((th is.id == null && other.id != null)
11 (this.id != nu ll && !this.id.equals(other.id))) {
return false;
}
return true;
}
@Override
pu blic String toString() {
return "data.Usuario[ id="+ id+")";
}
}

© Alfaomega - Altaria 2691


Curso avanzado de Java

• "ComentaPost.java " :

package data;
import java.io.Serializable;
import java.utii.Calendar;
im port javax.persistence. Entity;
i m port javax. persistence .GeneratedValu e;
import javax.persistence.GenerationType;
impo rt javax.persistence.ld;
import javax.persistence.Ma nyToOne;
@Entity
public class Comenta Post implements Serializable {
prívate static fina l long seriaiVersionU ID = ll;
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
prívate Long id;
prívate Calendar fechalnstante;
prívate String comentario;
@ManyToOne
prívate Usuario usuario;
@ManyToOne
prívate Post post;
public Long getld() {
return id;
}
public void setld(Lo ng id) {
this.id = id;
}
public Calendar getFechalnsta nte() {
return fecha 1nstante;
}
public void setFech alnstante(Calendar fecha Inst ante) {
this.fechalnsta nte = fechalnstante;
}
public String getComentario() {
return comentario;
}
public void setComentario(String comentario) {
this.comentario =co mentario;
}
public Usuario getUsuario() {
return usuario;
}
public void setUsuario(Usuario usuario) {
this.usua rio =usuario;
}

1270 © A lfaomega - Altaria


JEE. Manual práctic o

pu blic Post getPost() {


return post;
}
public void setPost(Post post) {
this.post = post;
}
@Override
pu blic int hashCode() {
int hash =O;
hash +=(id != null ? id.hashCode() :O);
return hash;
}
@Override
public boolean equals(Object object) {
if ( !(object instanceof Comenta Post)) {
return false;
}
Comenta Post other = (ComentaPost) object;
if ((this.id == null && other.id != null) 11
(this .id != null && !this.id.equals(other.id))) {
return false;
}
return true;
}
@Override
pu blic String toString() {
return "data.ComentaPost[ id="+ id+")";
}
}

• "Chat.java":

package data;
import java.io.Serializable;
import java.utii.Calendar;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
im port javax. persistence .Generation Type;
import javax.persistence. ld;
import javax.persistence.ManyToOne;
@Entity
public class Chat implements Serializable {
prívate static final long seriaiVersionU ID = lL;
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
prívate Long id;

© Alfaomega - Altaria 2711


Curso avanzado de Java

prívate Calendar fecha Instante;


prívate String contenido;
@ManyToOne
prívate Usuario enviadoPor;
@ManyToOne
prívate Usuario enviadoA;
public Long getld() {
return id;
}
public void setld(Long id) {
this.id = id;
}
public Calendar getFechalnstante() {
return fecha 1nstante;
}
public void setFechalnstante(Calendar fecha Instante) {
this.fechalnstante = fechalnstante;
}
public String getContenido() {
return contenido;
}
public void setContenido(String contenido) {
this.contenido =contenido;
}
public Usuario getEnviadoPor() {
return enviadoPor;
}
public void setEnviadoPor(Usuario enviadoPor) {
this.enviadoPor = enviadoPor;
}
public Usuario getEnviadoA() {
return enviadoA;
}
public void setEnviadoA(Usuario enviadoA) {
this.enviadoA = enviadoA;
}
@Override
public int hashCode() {
int hash =O;
hash +=(id != null? id.hashCode() :O);
return hash;
}
@Override
public boolean equals(Object object) {
if (!(object instanceof Chat)) {
return false;
}

Chat other = (Chat) object;


if ((this.id == null && other.id != null)

1272 © A lfaomega - Altaria


JEE. Manual práctico

11 (this.id != nul l && !t his.id.equals(other.id))) (


return false;
}
return true;
}
@Override
pu blic String toString() {
return " data.Chat[ id="+ id +")";
}
}
• "ChatActual.j ava":
package data;
import java.io.Serializable;
import javax.persistence.Entity;
import javax.persist ence.Generat edValue;
im port javax.persist ence .Generatio nType;
import javax.persistence. ld;
import javax.persistence .ManyToOne;
import javax.persistence.OneToOne;
@Entity
public class ChatActual implement s Serializable {
private static fi nal long seriaiVersionUID = ll;
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
@OneToOne
private Usuario usuario;
@M anyToOne
private Usuario chat eaCon;
pu blic Long getld() {
return id;
}
public void setld( Long id) {
this.id = id;
}
public Usuario getUsuario() {
return usuario;
}
public void setUsuario(Usuario usuario) {
this.usuario = usuario;
}

public Usuario getChateaCon() {


return chateaCon;
}
public void setChateaCon(Usuario chateaCon) {
this.chateaCon = chateaCon;
}
@Override
pu blic int hashCode() {

© Alfaomega - Altaria 2731


C urso a vanzado de Java

int hash =O;


hash +=(id != null? id .hashCode() :O);
return hash;
}
@Override
public boolean equals(Object object) {
if (!(object instanceof ChatActual)) {
return false;
}
ChatActual other = (ChatActua l) object;
if ((this.id == null && other.id != null)
11 (this.id != null & & !this.id.eq uals(other.id))) {
return false;
}
re t urn t rue;
}
@Override
public String toString() {
return "data.ChatActual[ id="+ id+"]";
}
}

9.3 Ejercicio 3
La estructura de l proyecto con los paquetes "data" y "datasessionbeans".
Presl8ciones

data
Chat..java
ChatAdual.java
Coment:aPost.java
Post. java
Usuario .java
EH'I"' datasessionbeans
~ Dat:a_Abstradfacade .java
~ Data_ChatAc11JaFa<ade.java
· g¡ Dat:a_ChatA<11JaFacadeLocal.java
~ Da121_Chatfacade.java
I:!J Dat:a_Chatfacadel.ocal.j:ava
~ Darn_ComenrnPostfacade.java
I:!J Data_ComentaPostfacadelocal.java
I!J Data_Postfacade.java
I:!J Dat:a_Postfacadele<:al.java
~ Dat:a_UsuarioFa<ade.java
gj Oat:a_U:suarioFacadel.o<al .java
¡j Test Pac:kagl!s.
(i Fuentes generadas (ap.g:)Ufce-output)
ti libraries

1274 © A lfaomega - Altaria


JEE. Manual práctic o

• Int erfaz "Dat a_ComentaPost FacadeLocal.jav a":

package datasession be a ns;


import data .ComentaPost;
im port java. uti l. List;
import javax.ejb.Local;
@Local
public interface Data_ComentaPostFacadeLocal {
void create(ComentaPost comentaPost);
void edit(ComentaPost comentaPost);
void remove(ComentaPost comentaPost);
ComentaPost find(Object id);
List<ComentaPost> findAII();
List<ComentaPost> findRange(int[] range);
int count();
}

• Sta teless "Data_Com en taPostFacad e.java" :

package datasessionbeans;
import data.ComentaPost;
import javax.ejb.Stateless;
import javax.persistence. EntityManager;
im port javax.persistence.PersistenceContext;
@Stateless
public class Data_ComentaPostFacade extends Data_AbstractFacade<ComentaPost>
implements Data_ComentaPostFacadeLocal {
@ PersistenceContext(unitName = "Biog-ejbPU ")
private EntityManager em;
@Override
protected EntityManager getEntityManager() {
return em;
}
public Data_ ComentaPostFacade() {
super( Comenta Post.class );
}
}

• Interfaz " D ata_ChatFacadeLocal.java" :

package datasessionbeans;
import data.Chat;
import java.utii.List;
import javax.ejb.Local;
@Local
public interface Data_ChatFacadeLocal {
void create(Chat chat);

© Alfaomega - Altaria 2751


Curso avanzado de Java

void edit(Chat chat);


void remove(Chat chat);
Chat find(Object id);
List<Chat> findAII();
List<Chat> findRange(int[] range);
int cou nt();
}

• Stateless "Data_ChatFacade.java":
package datasessionbeans;
import data.Chat;
import javax.ejb.Stateless;
im port javax.persistence .EntityManager;
im port javax.persistence. PersistenceContext;
@Stateless
public class Data_ChatFacade extends Data_AbstractFacade<Chat>
implements Data_ChatFacadelocal {
@PersistenceContext(unitName = "Biog-ejbPU")
prívate EntityManager em;
@Override
protected EntityManager getEntityManager() {
return em;
}
public Data_ChatFacade() {
super( Chat.class);
}
}

• Interfaz "Data_ChatActualFacadeLocal.java" :

package datasessionbeans;
import data.ChatActua l;
import java.utii.List;
import javax.ejb.Local;
@Local
public interface Data_ChatActuaiFacadelocal {
void create(ChatActual chatActual);
void edit(ChatActual chatActual);
void remove(ChatActual chatActual);
ChatActual find(Object id);
List<ChatActual> findAII();
List<ChatActual> findRange(int[] range);
int count();
}
• Stateless "Data_ChatActua!Facade.java" :
package datasessionbeans;
import data.ChatActua l;
import javax.ejb.Stateless;

1276 © A lfaomega - Altaria


JEE. Manual práctic o

import javax.persistence. EntityManager;


im port javax. persistence. PersistenceContext;
@Stateless
public class Data_ChatActuaiFacade extends Data_AbstractFacade<ChatActual>
implements Data_ChatActuaiFacadelocal {
@ PersistenceContext(unitName = "Biog-ejbPU ")
prívate EntityManager em;
@Override
protected EntityManager getEntityManager() {
return em;
}
public Data_ ChatActuaiFacade() {
super( ChatActua l.class);
}
}

9.4 Ejercicio 4
En los apartados y ejercicios previos hemos implementado las clases de los paque-
tes "data" y "datasessionbeans". Aun así, tendremos que modificar algunas fachadas
de acceso a datos para adaptarlas a las necesidades del servicio . Veamos el contenido
de los paquetes "domainmodel", "exceptions", "util", de las fachadas de acceso a datos
modificadas, y del paquete "service".

PrOjects X Prestaciones
lB ,! Blog
e\\
·a

1 1
Slogl>ost.java
1 1 ChateaCon.java
ComentarioPost.java
MensajeChat.java
8 f:¡q exceptions
L... ~ Blogfxception.java

1 BlogServke.java
' 1' BlogServkelocai.J•v•
1
lllogServi<eRemote ,java
1 1 IBiog.java
8 E§ uti
. L ~ SeMceVti6ty.java
$ (i TestPackages
$·ti Fuentes generadas (ap-source-<>utput)
$ (» librañes
~. Of Test Lilraries
W·Ol Ent...poiseBeons
W·~ Config<ration Files
L. (j8 Server Resou'ces

© Alfaomega - Altaria 2771


Curso avanzado de Java

Paquete "domainmodel":
• Clase "BlogPost.java":
package domainmodel;
import java.io.Serializable;
i m port java.util. List;
public class BlogPost implements Serializable {
private Long id;
private String fecha Instante;
private String contenido;
private String autor;
private List<ComentarioPost> comentarios;
public BlogPost() {}
public Long getld() {
return id;
}
public void setld(Long id) {
this.id = id;
}
public String getFecha lnstante() {
return fecha 1nstante;
}
public void setFecha lnstante(String fechalnstante) {
this.fechalnstante = fechalnstante;
}
public String getContenido() {
return contenido;
}
public void setContenido(String contenido) {
this.contenido =contenido;
}
public String getAutor() {
return autor;
}
public void setAutor(String autor) {
this.autor = autor;
}
public List<ComentarioPost> getComentarios() {
return comentarios;
}
public void setComentarios(List<ComentarioPost> comentarios) {
this.comentarios = comentarios;
}
}

1278 © A lfaomega - Altaria


JEE. Manual práctico

• Clase "ComentarioPost .java ":


package domainmodel;
import java .io.Serializable;
public class ComentarioPost implements Serializable {
private String fecha Instante;
private String comentario;
private String usuario;
public ComentarioPost() {}
public String getFechalnstante() {
return fechalnstante;
}
public void setFechalnstante(String fecha Instante) {
this.fechalnstante = fechalnstante;
}
public String getComentario() {
return comentario;
}
public void setComentario(String comentario) {
this.comentario =comentario;
}
public String getUsuario() {
return usuario;
}
public void setUsuario(String usuario) {
this.usuario = usuario;
}
}
• Clase "ChateaCon.java ":
package domainmodel;
import java .io.Serializable;
public class ChateaCon implements Serializable {
private String usuario;
private String con;
public ChateaCon() {}
public String getUsuario() {
return usuario;
}
public void setUsuario(String usuario) {
this.usuario = usuario;
}
pu blic String getCon() {
return con;
}
public void setCon(String con) {
this.con =con;
}
}

© Alfaomega - Altaria 2791


Curso avanzado de Java

• Clase "MensajeChat .java ":


package domainmodel;
import java.io.Serializable;
public cla ss MensajeChat implements Serializable {
private String fecha Instante;
private String contenido;
private String enviadoPor;
private String enviadoA;
public MensajeChat() {}
public String getFecha lnstante() {
return fecha 1nstante;
}
public void setFecha lnstante(String fec ha instante) {
this.fechalnstante = fechalnstante;
}
public String getContenido() {
return contenido;
}
public void setContenido(String contenido) {
this.contenido =contenido;
}
public String getEnviadoPor() {
return enviadoPor;
}
public void setEnviadoPor(String enviadoPor) {
this.enviadoPor = enviadoPor;
}
public String getEnviadoA() {
return enviadoA;
}
public void setEnviadoA(String enviadoA) {
this.enviadoA = enviadoA;
}
}

Paquete "exceptions":
• Excep ción "BlogExcep tion.ja va ":
package exceptions;
public class BlogException extends Exception {
public BlogException() {}
public BlogException(String msg) {
super(msg);
}
}

1280 © A lfaomega - Altaria


JEE. Manual práctico

Paquete "util". En él implementamos una clase con métodos de utilidad:


• Clase "ServiceUtility.java":
package util;
import data.Chat;
import java.utii.Calendar;
import java .utii.Gregoria nCalendar;
import java.utii.List;
public class ServiceUtility {
public static String getFechalnstanteCadena(Ca lendar fecha) {
int ano= fecha.get(Calendar.YEAR);
int mes= fecha.get(Calendar.MONTH) + 1;
int dia = fecha.get(Calendar.DAY_OF _MO NTH);
int hora= fecha.get(Calendar.HOUR_OF _DAY);
int minutos= fecha.get(Calendar.MINUTE);
int segundos= fecha.get(Calendar.SECOND);
String anoString = "" +ano;
String mesString = (mes< 10) ? "O" +mes : "" + mes;
String diaString = (dia < 10)? "O"+ dia : "" + dia;
111 1
String horaString = (hora < 10) ? "O" +hora : + hora;
String minutosString =(m inutos< 10)? "O" + minutos: ""+minutos;
String segundosString =(segundos< 10)? "O"+ segundos:""+ segundos;
String fechaString = diaString + "/" + mesString + "/" + anoString
+" "+ horaString + ":" + minutosString + ":" + segundosString;
return fechaString;
}
public static Calendar getFechalnstanteCalendar(String f echa Instante) {
String[) partes= fecha lnstante.split(" ");
String fecha= partes[O);
String instante= partes[l);
String[) partesFecha = fecha.split("/");
String[] parteslnstante = instante.split(":");
String diaString = partesFecha[O);
String mesString = partesFecha(l];
String anoString = partesFecha[2);
String horaString = parteslnstante[O);
String minutosString = parteslnstante[l);
String segundosString = parteslnstante[2];
int dia = lnteger.parselnt(diaString);
int mes= lnteger.parselnt(mesString);
int ano = lnteger.parselnt(anoString);
int hora = lnteger.parselnt(horaString);
int minutos= lnteger.parselnt(minutosString);
int segundos= lnteger.parselnt(segundosString);
Calendar flnstante = new GregorianCalendar(ano, mes, dia,
hora, minutos, segundos);
return flnstante;
}

© Alfaomega - Altaria 2811


C urso a vanza do de Java

public static List<Chat> ordenarChatsBurbuja(List<Chat> chats) {


int nChats = chats.size();
for(int i=1;i<nChats;i++) {
int max = nChats-i;
for(int j=O;j<max;j++) {
Chat chatJ = chats.get(j);
Chat chatJl = chats.get(j+ 1);
Calendar fechalnstanteJ = chatJ.getFechalnstante();
Calendar fechalnstanteJ1 = chatJ1.getFechalnstante();
int comp = fechalnstanteJ.compareTo(fechalnstanteJ1);
if (comp<O) {
chats.set(j, chatJ1);
cha t s.set(j+1, chatJ);
}
}
}
return chats;
}
}

Veamos ahora las fachadas de acceso a datos que hemos modificado para poder
tener acceso a los datos que necesitamos desde la capa de servicio.

• Interfaz "Data_UsuarioFacadeLocal.java":
package datasessionbeans;
import data.Usuario;
import java.utii.List;
import javax.ejb.Local;
@Local
public interface Data_UsuarioFacade Local {
void create(Usuario usuario);
void edit(Usuario usuario};
void remove(Usuario usuario);
Usuario find(Object id);
List<Usuario> findAII();
List<Usuario> findRange(int[] range);
int count();
11 Añad ido.
public Long encontrar UsuarioporNombre(String nombreUsuario};
}

1282 © Alfaomega - Altaria


JEE. Manual práctico

Hemos añadido un método para encontrar el "id" de un usuario por su nombre. Si


no existe se devuelve -1. A continuación, la implementación:

• Clase "Data_UsuarioFacade.java":
package datasessionbeans;
import data.Usuario;
import java.utii.List;
import javax.ejb.Stateless;
import javax. persistence. EntityManager;
im port javax. persistence.Pe rsistenceContext;
import javax.persistence.TypedQuery;
@Stateless
public class Data_UsuarioFacade extends Data_Abst ractFacade<Usuario>
implements Data_UsuarioFacadelocal {
@ PersistenceContext(unitName = "Biog-ejbPU ")
private EntityManager em;
@Override
protected EntityManager getEntityManager() {
return em;
}
public Data _UsuarioFacade() {
super( Usua rio.class );
}
@Override
public Long encontrarUsuarioporNombre(String nombreUsuario) {
TypedQuery<Usuario> query = this.getEntityManager().createQuery(
"SELECT u FROM Usuario u WHERE u.nombreUsuario = :nombreUsuario",
data. Usu a rio.class);
query.setPa rameter("nombre Usuario", nombre Usuario);
List<Usuario> usuarios = query.getResultlist();
if (usuarios.isEmpty() 11 usuarios.size() > 1) {
return null;
} else {
return usuarios.get(O).getld();
}
}
}

• Interfaz "Data_ComentaPostFacadeLocal.java":
package datasessionbeans;
import data.ComentaPost;
import java.utii.List;
import javax.ejb.Local;
@Local
public interface Data_ComentaPostFacadelocal {

© Alfaomega - Altaria 2831


C urso a vanzado de Java

void create(ComentaPost comenta Post);


void edit(ComentaPost comenta Post);
void remove(ComentaPost comenta Post);
Comenta Post find(Object id);
List<ComentaPost> findAII();
List<ComentaPost> findRange(int[] range);
int cou nt();
11 Añad ido.
public List<ComentaPost> obtenerlosComentariosdePost(Long postld);
}

Hemos añadido un método para obtener los comentarios de un post:


• Clase "Data_ComentaPostFacade.java":
package datasessionbeans;
import data.ComentaPost;
import java.utii.List;
import javax.ejb.Stateless;
im port javax.persistence.EntityManager;
i m port javax. persisten ce. Pe rsistenceContext;
import javax.persistence.TypedQuery;
@Stateless
public class Data_ComentaPostFacade extends Data_AbstractFacade<ComentaPost>
implements Data _ComentaPostFacadeloca l {
@PersistenceContext(unitName = "Biog-ejbPU")
prívate EntityManager em;
@Override
protected EntityManager getEntityManager() {
return em;
}
public Data_ Comenta PostFacade() {
super( Comenta Post. class );
}
@Override
public List<ComentaPost> obtenerlosComentariosdePost(Long postld) {
TypedQuery<ComentaPost> query = this.getEntityManager().createQuery(
"SELECT cPost FROM Comenta Post cPost WHERE cPost.post.id ="
+ postld +" ORDER BY cPost.fechalnstante DESC",
data .Comenta Post.class);
List<ComentaPost> comentaPosts =query.getResultlist();
return comentaPosts;
}
}

1284 © A lfaomega - Altaria


JEE. Manual práctico

• Interfaz "Data_ChatActualFacadeLocal.java":
package datasession be a ns;
import data.ChatActual;
im port java. uti l. List;
import javax.ejb.Local;
@Local
public interface Data_ChatActuaiFacadeLocal {
void create(ChatActual chatActua l);
void edit(ChatActua l chatActua l);
void remove(ChatActual chatActual);
ChatActual find(Object id);
List<ChatActual> findAII();
List<ChatActual> findRange(int[] range);
int count();
11 Añadido.
public Long existeChatUsuario(Long idUsuario);
}

Hemos añadido un método que devuelve el "id" de un "ChatActual" del usuario cuyo
"id" se pasa por parámetro. La implementación es la siguiente:
• Clase "Data_ChatActualFacade.java":
package datasessionbeans;
import data.ChatActual;
import java.utii.List;
import javax.ejb.Stateless;
im port javax. persistence. EntityM anage r;
im port javax. persistence. Pe rsistenceContext;
im port javax. persistence .TypedQuery;
@Stateless
public class Data_ChatActuaiFacade extends Data_AbstractFacade<ChatActual>
implements Data_ChatActuaiFacadeLocal {
@ PersistenceContext(unitName = "Biog-ejbPU ")
private EntityManager em;
@Override
protected EntityManager getEntityManager() {
return em;
}
public Data_(hatActuaiFacade() {
super( ChatActua l.class);
}
@Override
public Long existeChatUsuario(Long idUsuario) {
TypedQuery<ChatActual> query =this.getEntityManager().createQuery(
"SELECT chact FROM ChatActual chact WHERE chact .usuario.id = "+ idUsuario,

© Alfaomega - Altaria 2851


Curso avanzado de Java

data .ChatActua l.class);


=
List<ChatActua l> chats query.getResultlist();
if (chats.isEmpty() 11 chats.size() > 1) {
return null;
} else {
return chats.get(O).getld();
}
}
}

• Interfaz "Data_ChatFacadeLocal.java":

package datasessionbeans;
import data.Chat;
import java.utii.List;
import javax.ejb.Local;
@Local
public interface Data _ChatFacadelocal {
void create(Chat chat);
void edit(Chat chat);
void remove(Chat chat};
Chat find(Object id);
List<Chat> findAII();
List<Chat> findRange(int[] range};
int count();
11 Añadido.
public List<Chat> chatsDeUsuarioAOtro(Long idUsuariol, Long idUsuario2);
}

Añadimos un método para obtener un listado de chats (mensajes) de un usuario a


otro (de forma unidireccional, enviados desde usuario 1 a usuario 2). La implementación
a continuación:

• Clase "Data_ChatFacade.java":

package datasessionbeans;
import data.Chat;
im port java.util. List;
import javax.ej b.Stateless;
im port javax.persistence. EntityManager;
i m port javax. persisten ce. Pe rsistenceContext;
import javax.persistence.TypedQuery;
@Stateless

1286 © Alfaomega - Altaria


JEE. Manual práctic o

public class Data_Chatfacade extends Data_AbstractFacade<Chat>


implements Data_ChatFacadeLoca l {
@ PersistenceContext(unitName = "Biog-ejbPU ")
prívate EntityManager em;
@Override
protected EntityManager getEntityManager() {
return em;
}
public Data_Chatfacade() {
su per(Chat.class);
}
@Override
public List<Chat> chatsDeUsuarioAOtro(Long idUsuariol, Long idUsuario2) {
TypedQuery<Chat> query =this.getEntityManager()
.createQuery("SELECT cht FROM Chat cht WHERE"
+ "cht.enviadoPor.id = "+ idUsuariol + "AND"
+ "cht.enviadoA.id ="
+ idUsuario2 +
"ORDER BY cht.fecha lnstante DESC", data.Chat.class);
List<Chat> chats = query.getResultlist();
return chats;
}
}

Finalmente, acudimos a la capa de servicio, paquete "service". En d ich a capa acabará


el back-en d de nuestra a plicación (si n o ten emos en cu en ta los s ervicios web).
• Interfaz "IBlog.java ":
package service;
import domainmodel. *;
import exceptions.BiogException;
import java.utii.List;
public interface IBiog {
public boolean crearUsuario(String usuario) throws BlogException;
public Long existeUsuario(String usuario) throws BlogException;
public List<String> obtenerTodosUsuarios() throws BlogException;
public BlogPost crearPost(BiogPost post) throws BlogException;
public BlogPost obtenerPostPorld(Long postld) throws BlogException;
public ComentarioPost anadirComentarioPost(BiogPost post,
ComentarioPost comentario) throws BlogException;
public boolean abreChatCon(ChateaCon chatea) throws BlogException;
public String obtenerChatActuaiUsuario(String usuario) throws BlogException;
public MensajeChat enviaMensajeChat(MensajeChat chat) throws BlogException;
public List<BiogPost> obtenerTodosLosPosts() throws BlogException;
public List<MensajeChat> obtenerTodosLosMensajesChat(String usuariol,
String usuario2) throws BlogException;
}

© A lfaom e g a - Altaria 2871


C urso a vanzado de Java

• Interfaz "BlogServiceLocal.java":
package service;
import javax.ejb.Local;
@Local
public interface BlogServicel ocal extends IBiog {}

• Interfaz "BlogServiceRemote.java ":


package service;
import javax.ejb.Remote;
@Re mote
public interface BlogServiceRemote extends IBiog {}

Finalmente , y para acabar el ejercicio, vemos la clase que implementa la capa de


servicio con un s tateless session bean, implementando las interfaces local y remota.
• Clase EJB stateless "BlogService.java ":
package service;
import data.*;
import datasessionbeans.*;
impo rt doma inmodel.*;
im port exceptions.BlogException;
import java.utii.Arraylist;
import java.utii.Calendar;
im port java.utii.Gregor ianCalenda r;
i m port java.util. List;
import javax.ejb .EJB;
impo rt javax.ejb.Stat eless;
import utii.ServiceUtility;
@Stateless
public class BlogService implements BlogServicelocal, BlogServiceRemote {
@EJB
private Data _Usuario Facadeloca l data_UsuarioFacade;
@EJB
private Data_PostFacadelocal data_PostFacade;
@EJB
priva te Data_ ComentaPostFacadeloca 1data_ ComentaPostFacade;
@EJB
private Dat a_ ChatFacadelocal dat a_ Chat Facade;
@EJB
private Data _ChatActuaiFacadeloca l data_ChatActuaiFacade;
@Override
public boolean crearUsua rio(String usuario) t hrows BlogException {
if (usuario== null 11 usuario.equals{'"')) {
throw new BlogException(" BiogService -crear usuario: "
+ " No se ha pasado usuario a insertar en la BD");

1288 © A lfaomega - Altaria


JEE. Manual práctico

}
Usuario u = new Usuario();
u .setNom b re Usuario( usuario);
try {
this.data_ Usu arioFacade .create( u);
} catch(Exception e) {
throw new BlogException("BiogService - crear usuario:"
+"El nombre del usuario ya existe en la BD");
}
return true;
}
@Override
public Long existeUsuario(String usuario) throws BlogException {
if (usuario== null 11 usuario.equals("")) {
throw new BlogException("BiogService - existe usuario: "
+"No se ha pasado usuario a recuperar de la BD");
}
Long usuariold = th is.data_ Usua rioFacade.encontrarUsuarioporNom bre(usua rio );
if (usuario Id == null) {
return -lL;
} else {
return usuariold;
}
}
@Override
public List<String> obtenerTodosUsuarios() throws BlogException {
List<Usuario> usuarios= this.data_UsuarioFacade.findAII();
if (usuarios== null) throw new BlogException("BiogService - obtener usuarios:"
+ "No se pudieron obtener todos los usuarios de la BD");
=
List<String> listado new Arraylist<>();
for(Usuario u: usuarios) {
String esteUsuario = u.getNombreUsuario();
listado .ad d( este Usuario);
}
return listado;
}
@Override
public BlogPost crearPost(BiogPost post) throws BlogException {
if (post==nul l) {
throw new BlogException("BiogService - crear post: "
+ "No se ha pasado post a introducir de la BD");
}
=
String autor post.getAutor();
String contenido= post.getContenido();
if (autor==nu ll 11 autor.equals('"')) throw
new BlogException("BiogService - crear post: "

© Alfaomega - Altaria 2891


C urso a vanzado de Java

+ "No se ha pasado autor del post a introducir de la BD");


if (contenido== null 11 contenido.equals("")) throw
new BlogException("BiogService - crear post: "
+ "No se ha pasado contenido del post a introducir de la BD");
Long autorld = this.existeUsuario(autor);
if (autorld == -ll) throw new BlogException("BiogService - crear post: "
+ "El autor del post no existe en la base de datos");
Usuario u = data_UsuarioFacade.find(autorld);
Post p = new Post();
p.setAutor(u);
p. setContenido( contenido);
p.setFechalnstante(new GregorianCalendar() );
try {
th is.data_PostFacade .create(p );
} catch(Exception e) {
throw new BlogException("BiogService - crear post: "
+ " No se pudo crear el post en la BD");
}
Calendar fechalnstante = p.getFecha lnstante();
String flnsta nte = ServiceUtility.getFechalnsta nteCadena(fecha 1nsta nte );
post.setFecha 1nsta nte(flnsta nte );
post.setld ( p .getld ());
post.setComentarios(new Arraylist<ComentarioPost>());
return post;
}
@Override
public BlogPost obtenerPostPorld(Long postld) throws BlogException {
if (postld == null) {
throw new BlogException("BiogService - obtener post por id: "
+ "No se ha pasado correctamente el id");
}
Post p = null;
try {
p = this.data_PostFacade.find(postld);
} catch(Exception e) {
throw new BlogException(" BiogService - obtener post por id: "
+ " No se pudo obtener el post de la BD");
}
BlogPost bPost = new BlogPost();
bPost.setAutor( p .getAutor() .getNom b re Usu arío());
b Post. setConte nido( p .getCo ntenido ());
bPost.setld( p.getld () );
bPost.setFecha 1nstante(Service Uti lity

1290 © Alfaomega - Altaria


JEE. Manual práctic o

.getFecha 1nsta nteCade na (p .getFecha 1nsta nte()));


List<ComentaPost> comentaPosts = null;
try {
comentaPosts = this.data_ComentaPostFacade
.obten e rLosCo m e nta riosd e Post( post 1d);
} catch(Exception e) {
throw new BlogException("BiogService- obtener post por id:"
+ "No se pudo obtener el post de la BD");
}
List<ComentarioPost> comentarios= new Arraylist<>();
for (ComentaPost cPost: comentaPosts) {
ComentarioPost comentario = new ComentarioPost();
comenta rio.setComenta rio( cPost.getCom enta rio());
comenta rio.setFecha 1nsta nte(ServiceUtility
.getFecha Insta nteCadena (cPost.getFecha 1nsta nte()));
comenta rio.setUsua rio( cPost.getU su ario() .getN o m bre Usuario());
comenta rios.add( comentario);
}
b Post.setCo m e nta rios (comentarios);
return bPost;
}
@Override
public ComentarioPost anadirComentarioPost(BiogPost post,
ComentarioPost comentario) throws BlogException {
if (post== null)
throw new BlogException("BiogService - añadir comentario post:"
+"No se ha pasado post existente en la BD");
if (comentario== null)
throw new BlogException("BiogService - añadir comentario post:"
+ "No se ha pasado comentario a añadir a la BD");
String contenido= comentario.getComentario();
if (conten ido==n ull 11 contenido.equals('"')) {
throw new BlogException("BiogService - añadir comentario post:"
+ "No se ha pasado comentario a añadir a la BD");
}
Long postld = post.getld();
Post p = data_PostFacade.find(postld);
if (p == null) {
throw new BlogException("BiogService- añadir comentario post:"
+"No se ha pasado post existente en la BD");
}
String autorComentario = comentario.getUsuario();
Long usuariold = existeUsuario(autorComentario);
if (usuariold == -ll) {
throw new BlogException("BiogService- añadir comentario post:"
+"No se ha pasado autor del comentario existente en la BD");

© Alfaomega - Altaria 2911


Curso avanzado de Java

}
Usuario u = data_UsuarioFacade.find(usuariold);
if (u == null) {
throw new BlogException(" BiogService- añadir comentario post: "
+ " No se ha pasado autor del comentario existente en la BD");
}
ComentaPost cp = new ComentaPost();
cp.setPost(p};
cp.setUsuario( u);
cp.setFechalnsta nte(new GregorianCalenda r());
cp .setComentario( con te nido);
try {
th is.data_ Comenta PostFacade .create( cp );
} catch(Exception e) {
throw new BlogException(" BiogService - añadir comentario post: "
+ " No se pudo añadir el comentario al post");
}
Calendar fecha Instante = cp.getFechalnstante();
String flnsta nte = ServiceUtility.getFechalnsta nteCadena(fecha 1nsta nte );
comentario .setFechalnsta nte(flnsta nte );
return comentario;
}
@Override
public boolean abreChatCon(ChateaCon chatea) throws BlogException {
if (chatea == null) {
throw new BlogException(" BiogService - actualiza chat destino: "
+ " el objeto no se pasó bien");
}
String usuariol = chatea.getUsuario();
String usuario2 = chatea.getCon();
if (usuariol == null 11 usuariol.equals('"'))
throw new BlogException(" BiogService - actualiza chat destino: "
+ " el usuario activo no se pasó correctamente");
if (usuario2 == null 11 usuario2.equals(""))
throw new BlogException(" BiogService - actualiza chat destino: "
+ " el usuario pasivo no se pasó correctamente");
long usuariolld = existeUsuario(usuariol);
long usuario21d = existeUsuario(usua rio2);
if ((usuariol ld == -l l ) 11 (usuario21d == -ll)) {
throw new BlogException(" BiogService - actualiza chat destino: "
+ " alguno de los usuarios no existe en la BD");
}
Usuario ul = null;
Usuario u2 = null;
try {
ul = t his.data_UsuarioFacade.find(usuariolld);

1292 © A lfaomega - Altaria


JEE. Manual práctico

u2 =this.data_UsuarioFacade.find(usuario21d);
} catch(Exception e) {
throw new BlogException("BiogService - actualiza chat destino: "
+ "alguno de los usuarios no existe en la BD");
}
if (ul == null 11 u2 == null) {
throw new BlogException("BiogService - actualiza chat destino: "
+"alguno de los usuarios no existe en la BD");
}
ChatActua l chatAct = new ChatActual();
chatAct.setUsuario( u 1);
chatAct.setCh ateaCon (u2 );
Long chatActualld = this.data _ ChatActuaiFacade .exist eChatUsuario(usuariolld);
try {
if (chatActualld == null) {
th is.data_ChatActua 1Facade .e reate( eh atAct);
} else {
chatAct. setld (chatActu alid );
th is.data_ ChatActua 1Faca de. edit( chatAct);
}
} catch(Exception e) {
throw new BlogException("BiogService - actualiza chat destino: "
+"no se pudo llevar a cabo la operación");
}
return true;
}
@Override
public String obtenerChatActuaiUsuario(String usuario) throws BlogException {
if ( (usuario == null) 11 (usuario.equals(" ")) ) {
throw new BlogException("BiogService- obtener chat actua l usuario:"
+ "no se pasó bien el nombre del usuario");
}
Long usuario Id =existeUsua rio( usuario);
if (usuariold == -ll) {
throw new BlogException("BiogService - obtener chat actua l usuario:"
+ "no se pasó bien el nombre del usuario");
}
Long chatActualld = th is.data_ ChatActuaiFacade .existeChatUsua rio( usuariold);
if (chatActualld == null) {
re tu rn "" 1·
}
String nombreUsuario =null;
ChatActua l chatAct = null;
try {
chatAct = this.data_ ChatActuaiFacade.fi nd(chatActualld);
no m breUsuario =chatAct.getUsuario().getNom breUsua rio();

© Alfaomega - Altaria 2931


Curso avanzado de Java

} catch(Exception e) {
throw new BlogException("BiogService -obtener chat actual usuario: "
+ "no se pudo obtener");
}
if (! nombreUsuario.eq ua ls( usuario)) {
throw new BlogException("BiogService -obtener chat actual usuario: "
+ "no se pudo obtener");
}
return chatAct.getChateaCon().getNombreUsuario();
}
@Override
public MensajeChat enviaMensajeChat(MensajeChat chat) throws BlogException {
if (chat == null) {
throw new BlogException("BiogService -envía mensaje chat: "
+"el mensaje no se seteó correctamente");
}
String conten idoChat = chat.getContenido();
String usua rio! = chat.getEnviadoPor();
String usuario2 = chat.getEnviadoA();
if ((contenidoChat == null) 11 (contenidoChat.equals(""))
11 (usuario!== null} 11 (usuario l.equals(""))
11 (usuario2 == null) 11 (usuario2.equals(""))) {
throw new BlogException("BiogService -envía mensaje chat: "
+"el mensaje no se seteó correctamente");
}
Long usuariolld = existeUsuario(usua riol);
Long usuario21d = existeUsuario(usuario2);
if ((usuariol ld == -ll) 11 (usuario21d == -lL)) {
throw new BlogException("BiogService -envía mensaje chat: "
+"alguno de los usuarios no existe en la BD");
}
Usuario ul = null;
Usuario u2 = null;
try {
ul = this.data_UsuarioFacade.find(usuariolld);
u2 = th is.data_ UsuarioFacade.fi nd( usuario21d);
} catch(Exception e) {
throw new BlogException("BiogService- envía mensaje chat: "
+ "alguno de los usuarios no existe en la BD");
}
if (ul == null 11 u2 == null) {
throw new BlogException("BiogService - envía mensaje chat: "
+"alguno de los usuarios no existe en la BD");
}
Chat mensaje = new ChatO;
mensaje.setContenido( conten idoChat);

1294 © Alfaomega - Altaria


JEE. Manual práctico

mensaje .setE nviado Por( u 1 );


mensaje .setE nviadoA( u 2);
mensaje .setFecha Instante( new GregorianCa lenda r() );
try {
this.data_ ChatFacade .e reate( mensaje);
} catch(Exception e) {
throw new BlogException("BiogService- envía mensaje chat: "
+ "no se pudo registrar el mensaje en la BD");
}
Calendar fechalnstante = mensaje.getFechalnstante();
String flnstante = ServiceUtility.getFecha lnstanteCadena(fecha Instante);
chat.setFecha 1nsta nte(fl nsta nte );
return chat;
}
@Override
public List<BiogPost> obtenerTodoslosPosts() throws BlogException {
List<Post> posts = this.data_PostFacade.findAII();
int n Posts = posts.size();
List<BiogPost> blogPosts = new Arraylist<>();
11 Ordenamos al revés para poner el último al com ienzo.
for( int j=nPosts-l;j>=O;j--) {
Post este Post= posts.get(j);
BlogPost esteBiogPost = new BlogPost();
esteBiogPost.setAutor(estePost.getAutor() .getN o m breU su ario());
este BlogPost.setConten ido( estePost.getContenido());
este Blog Post .set Fe eh a1nsta nte (Service Uti 1ity
.getFecha 1nsta nteCadena{estePost.getFecha Instante()));
Long postld = estePost.getld(};
esteBiogPost.setld (post Id);
List<ComentaPost> comentaPosts = null;
try {
comentaPosts =
this.data_ Comenta PostFacade .obtenerlosCom e nta riosd ePost( postld);
} catch(Exception e) {
throw new BlogException("BiogService- obtener los comentarios de post:"
+ "no se pudieron obtener");
}
List<ComentarioPost> comentarios= new Arraylist<>();
for(ComentaPost cPost: comentaPosts) {
ComentarioPost cp = new ComentarioPost();
cp .setUsua rio( cPost.getUsuario().getNom breUsua río());
ep. set Fe eh a 1nsta nte (Service Uti 1ity
.get Fe eh a1nsta nteCa den a(cPost.get Fe eh a 1nsta nte ()));
cp .setComentario( cPost.getCome nta rio());
comentarios.add(cp );

© Alfaomega - Altaria 2951


Curso avanzado de Java

}
este Blog Post .setCo m en ta ríos( comentarios);
blogPosts.ad d (este BlogPost);
}
return blogPosts;
}
@Override
public List<MensajeChat> obtenerTodosLosMensajesChat(String usuariol,
String usuario2) throws BlogException {
if ((usuariol == null) 11 (usuariol.equals('"'))
11 (usuario2 ==null) 11 (usuario2.equals('"'))) {
throw new BlogException(" BiogService -obtener todos los mensajes de chat: "
+ " los usuarios no se setearon correctamente");
}
Long usuariolld = existeUsuario(usuariol);
Long usuario21d = existeUsuario(usuario2);
if ((usuariol ld == -ll) 11 (usuario21d == -lL)) {
throw new BlogException(" BiogService -obtener todos los mensajes de chat: "
+ " los usuarios no se setearon correctamente");
}
Usuario ul = null;
Usuario u2 = null;
try {
ul =this.data_UsuarioFacade.find(usuariolld);
u2 =th is.data_UsuarioFacade.find(usuario21d);
} catch(Exception e) {
throw new BlogException(" BiogService - obtener todos los mensajes de chat: "
+ "alguno de los usuarios no existe en la BD");
}
if (ul == null 11 u2 == null) {
throw new BlogException(" BiogService -obtener todos los mensajes de chat: "
+ "alguno de los usuarios no existe en la BD");
}
List<Chat> todosChats =null;
try {
List<Chat> chatsl =
th is.data_ ChatFacade .chatsDe Usua rioAOtro( usuario lid, usuario21d );
List<Chat> chats2 =
th is.data_ ChatFacade .chatsDe Usua rioAOtro( usua rio21d, usuario lid);
todosChats = chatsl;
todosChats.addAII(chats2);
todosChats = Se rviceUtil ity.ordena rChats Burbuja( todosChats );
} catch(Exception e) {
throw new BlogException(" BiogService -obtener todos los mensajes de chat: "
+ " no se pud ieron obtener los mensajes de chat");

1296 © A lfaomega - Altaria


JEE. Manual práctic o

}
List<Mensajeehat> mensajes = new Arraylist<Mensajeehat>();
for(ehat esteehat : todosehats) {
Mensajeehat mensaje =new Mensajeehat();
m ensaje.seteonten ido( esteehat.geteonten ido());
m e nsa je. set Fe eh a 1nsta nte (Se rvice U ti 1ity
.getFecha Insta nteeadena(esteehat.getFecha 1nstante()) );
m e nsa je. set Enviad o Por( este e hat.getE nvi ado Por(). get N o m bre Usuario ());
m e nsa je. set Enviad o A( este eh at. get Enviad o A () .get N o m bre Usuario());
m ensajes.add( mensaje);
}
return mensajes;
}
}

9.5 Ejercicio 5
Comenzamos a realizar la implementación del módulo web (WAR). La estructura de
carpetas es la siguiente:

á @ Blog-war
i $~ Web Pages
i (E ·m WEB·lNF
1 $-m es•
¡1 ¡ ¡ '
! L'
$·Q1 img
style.=
buttons.=

i ¡ ¡¡¡¡j bg.pg
1 La heaoo .jpg
$-~ js
1 1··· ~ Vlsi200M.js
! !···· ~ blog.js
i L~ jquery·3.l.l.min.js
¡....~jj cabecera.xhtml
¡....ljl contenidoblog.xhtml
! ·~ conteridologín. xhtm'l
! -~ índex.xhtml
i .ljl logout.J<html
i··liJ
mensajellash.xhtml
1···.. 00
nombreusuario .xhtml
. L .~ planlilablog.l<html
8 ·~ Se>urce Packages
G Qí¡ Test Padcages
...
${i Libr~es
G{i Testlbr~
.+,,<5 .

En este apartado veremos la implementación propuesta de la hoja de estilos CSS


propia (la de botones no la hemos desarrollado nosotros). Veremos el bean administra-
do situado en el paquete "managedbeans" (del módulo WAR) y todas las páginas JSF.

© Alfaomega - Altaria 2971


Curso avanzado de Java

Comencemos viendo la hoja de estilos propia:

• Hoja de estilos "style .css":


@CHARSET "UTF-8";
/* Hoja de estilos para el ejercicio del blog *1
*{
margin: O;
padding: O;
}
htm l {
height: 100%;
}
html body {
background: #fff uri(/Biog-war/img/bg.jpg) repeat-y top center;
font-family: Arial, Helvetica, sans-serif;
font-size: 12 px;
line-height: 17px;
color: #222;
height: 100%;
}
htm l body div#nombreusuario {
display : none;
}
htm l body div#mensajeflash {
position: absolute;
left: 50%;
top: 120px;
transform: translate( -50%, -50%);
-webkit-transform: translate( -50%, -50%);
z-index: 10;
}
htm l body div#mensajeflash div {
padding: 10px;
font-weight: bold;
-webkit- border-radius: 10px;
-moz-border- radius: 10px;
border-radius: 10px;
}
htm l body div#mensajeflash div.oculto {
display: none;
}
htm l body div#mensajeflash div.visible {
display: block;
}
htm l body div#mensajeflash div.mensaje {

1298 © A lfaomega - Altaria


JEE. M a nua l práctico

background-color: green;
color: white;
border: 2px solid white;
}
html body div#mensajeflash div.error {
background-color: red;
color: yellow;
border: 2px sol id yellow;
}
html body div#contenedor {
margin: O auto;
width: 800px;
height: 100%;
}
html body div#contenedor div#cabecera {
height: 90px;
margin: O;
background: #fff uri{/Biog-war/img/header.jpg) no-repeat;
}
html body div#contenedor div#cabecera h1 {
font-size: 25px;
letter-spacing: -1px;
padding: 25px O O 20px;
color: #fff;
}
html body div#contenedor div#cabecera h2 {
font-size: 18px;
color: #336699;
padding: 3px O O 20px;
letter-spacing: -1px;
font-weight: 100;
}
html body div#contenedor div#cabecera div#muestranombreusuario {
display: block;
margin-left: 20px;
}
html body div#contenedor div#cabecera div#logout {
float: right;
display: inline-block;
margin-right: 40px;
}
html body div#contenedor div#cabecera div.clear {
clear: both;
}
html body div#contenedor div#contenido {
height: 100%;
padding: 20px;

© Alfaomega - Altaria 2991


Curso avanzado de Java

}
htm l body div#contenedor div#contenido d iv#login,
htm l body div#contenedor div#contenido div#registro {
height: 100%;
}
htm l body div#contenedor div#conten ido div#login table,
html body div#contenedor div#conten ido div#registro table {
border: 2px solid gray;
-webkit- border-radius: 10px;
-moz-border-radius: 10px;
border-radius: 10px;
margin: lSOpx auto;
}
html body div#contenedor div#conten ido div#login table tr td,
htm l body div#contenedor div#contenido div#registro table tr td {
padding: lOpx;
}
html body div#contenedor div#contenido div.red {
float: left;
height: 100%;
border: 1px solid gray;
}
htm l body div#contenedor div#contenido div#izquierdo {
width: 150px;
}
htm l body div#contenedor div#contenido div#izquierdo div#titulousuarios {
padding-left: lOpx;
line-height: 26px;
background: #898989;
color: #fff;
font-size: 13px;
font-weight: bold;
}
html body div#contenedor div#contenido d iv#izquierdo div#listadousuarios {
height: 95%;
overflow: scroll;
}
html body div#contenedor div#contenido div#izquierdo div#listadousuarios div.usuario {
border: 1px solid black;
color: gray;
text-align: center;
padding: 3px;
}
htm l body div#contenedor div#contenido div#centra l {
width: 400px;
}

1300 © A lfaomega - Altaria


JEE. Manual práctic o

html body div#contenedor div#contenido div#central div.tituloposts {


text-align: center;
line-height: 26px;
background: #898989;
color: #fff;
font-size: 16px;
font-weight: bold;
}
html body div#contenedor div#contenido div#central div#listadoposts {
height: 95%;
overflow: scroll;
}
html body div#contenedor div#contenido div#central div#listadoposts div.escribepost,
html body div#contenedor div#contenido div#central div#listadoposts div.post {
border: lpx sol id black;
color: gray;
text-align: justify;
padding: 3px 3px 3px Spx;
}
html body div#contenedor div#contenido div#central div#listadoposts div.post div.escribeco-
mentariopost {
padding: O lOpx O lOpx;
border: lpx solid gray;
}
html body div#contenedor div#contenido div#centra l div#listadoposts div.post div.comentar-
iopost {
padding-left: 20px;
border: lpx solid gray;
}
html body div#contenedor div#contenido div#derecho {
width: 200px;
}
html body div#contenedor div#contenido div#derecho div.titulo {
text-align: center;
background: #B9B9B9;
color: #fff;
font-weight: bold;
}
html body div#contenedor div#contenido div#derecho div#contenedorchats {
height: 50%;
overflow: scroll;
}
html body div#contenedor div#contenido div#derecho div#contenedorchats div.mensajechat {
color: gray;
padding: 3px;
text-align: left;

© Alfaomega - Altaria 3011


Curso avanzado de Java

}
htm l body div#contenedor div#contenido div#derecho div#enviochat {
height: 30%;
}
html body div#contenedor div#contenido div#derecho div#enviochat span#usuariochat {
display: none;
}
html body div#contenedor div#conten ido div#derecho div#enviochat textarea {
width: 99%;
height: 50%;
}
html body div#contenedor div#contenido div div.clear {
clear: both;
}
textarea {
resize: none;
width: 100%;
}

• Bean gestionado en el paquete "managedbeans". Los métodos que gestionarán la


entrada y la salida del usu ario del sistema son "accesoUsuario() " y "salidaUsua-
rio()". Cuando realiza mos el login y la con sulta y se crea el usuario en la base
de d atos, es necesaria la inserción d el EJB BlogService.
package managedbeans;
im port exceptions. BlogException;
import javax.inject.Named;
import java.io.Serializable;
import javax.ejb.EJB;
im port javax.faces.bean.Ma nagedBea n;
i m port javax.faces.bea n .SessionScoped;
import service.BiogServicelocal;
@ManagedBean
@Named(value ="usuarioBean")
@SessionScoped
public class UsuarioBean implements Seria lizable {
@EJB
private BlogServicelocal blogService;
private String titulo;
private String nombreUsuario;
private String mensaje;
private String error;
private boolean enviaNombreUsuario;
private boolean envia DatosFiash;
private boolean enviaCabecera;
private boolean inicio;

1302 © A lfaomega - Altaria


JEE. Manual práctic o

private boolean blog;


public UsuarioBean() {
this.titulo ="El mejor blog del mundo";
this.nombreUsuario = "";
this.mensaje = '"';
this.error = "";
this.enviaDatosFiash = true;
this.enviaCabecera = true;
this.inicio = true;
this.enviaNombreUsuario = true;
this.blog = false;
}
public void accesoUsuario() {
try {
String nombreSinEspacios = this.nombreUsuario.trim();
if (!nombreSinEspacios.equals("")) {
long usuariold = blogService.existeUsuario(this.nombreUsuario);
boolea n exito = false;
if(usuariold.equals(-ll)) {
11 Hay que crear el usuario.
exito = blogService.crea rUsuario(this.nombreUsuario );
} else {
exito = true;
}
if(exito) {
11 Hemos accedido. Procedemos a ajusta r los valores del bea n.
this.mensaje ="Bienvenido";
this.error = "";
this.inicio = false;
this.blog = true;
}
} else {
this.nombreUsuario = "";
this.mensaje = "";
throw new BlogException("Error al introducir el nombre");
}
} catch (BiogException ex} {
this.error = ex.getMessage();
}
}
public void salidaUsuario() {
this.nombreUsuario = "";
this.mensaje = "";
this.error = "";
this.enviaDatosFiash = true;
this.enviaCabecera = true;

© Alfaomega - Altaria 3031


Curso avanzado de Java

this.inicio = t rue;
this.envia NombreUsuario = true;
this.blog = fa lse;
}
public String getTitulo () {
return tit ulo;
}
public void setTitulo{St ring titulo) {
this.titulo =titulo;
}
public String getNombreUsuario() {
re t urn nombreUsuario;
}
public void setNombreUsuario{St ring nombreUsuario) {
this.nombreUsuario = nombreUsuario;
}
public String getMensaje() {
re t urn mensaje;
}
public void setM ensaje(String mensaje) {
this.mensaje = mensaje;
}
public String getError() {
return error;
}
public void setError(String error) {
this.error = error;
}
public boolean isEnvia DatosFiash () {
return envia DatosFiash;
}
public void setEnviaDatosFiash(boolean enviaDatosFiash) {
this.enviaDatosFiash = enviaDatosFiash;
}
public boolean isEnviaCabecera() {
return enviaCabecera;
}
public void setEnviaCabecera(boolean enviaCabecera) {
t his.enviaCa becera = env iaCabecera;
}
public boolean islnicio() {
return inicio;
}
public void setln icio(boolean inicio) {
this.inicio = inicio;

1304 © A lfaomega - Altaria


JEE. Manual práctic o

}
public boolean isEnviaNombreUsuario() {
return enviaNombreUsuario;
}
public void setEnviaNombreUsuario(boolean enviaNombreUsuario) {
this.enviaNombreUsuario = enviaNombreUsuario;
}
pu blic boolean isBiog() {
return blog;
}
public void setBiog(boolea n blog) {
this.blog = blog;
}
}
Y las páginas JSF planteadas al lector son las siguientes:
• "plan tillablog.xh tml":
<?xm l version='l.O' encoding='UTF-8' ?>
<!DOCTYPE htm l PUBLIC "-/ /W3C//DTD XHTML 1.0 Transitional//EN"
"http ://www.w3 .org/TR/xhtm 11/DTD/xhtm 11-tra nsiti o na l.dtd ">
<htm l xmlns="http://www.w3.org/1999/xhtml"
xm lns:h="http ://xmlns.jcp.org/jsf/htm 1"
xm lns:f="http://java.su n .com/jsf/core"
xm 1ns:u i=" http ://java .sun.com/jsf/facelets" >
<ui:composition>
<h:head>
<title>Biog</title>
<meta charset="UTF-8" />
<scri pt type= "text/javascript" src="js/jq uery-3 .1.1. mi n.js "></scri pt>
<script type="text/javascript" src="js/VistaDOM .js"></script>
<scri pt type= "text/javascript" src="js/blog.js "></script>
<link rel ="stylesheet" type="text/css" href="css/style.css" />
<link rel="stylesheet" type="text/css" href="css/buttons.css" />
</h :head>
<h:body>
<u i :fragm ent rendered= "#{ usua rioBea n .envía No m breUsua rio}" >
<ui:insert name="nom breusuario">
<ui:include src="/nombreusuario.xhtml" />
</ui:insert>
</ui:fragment>
<u i :fragm ent rendered= "#{ usua rioBea n .envía DatosFiash }">
<ui:insert name="mensajeflash">
<ui:include src="/mensajeflash.xhtm l" />
</ui:insert>
</ui:fragment>
<div id="contenedor">

© Alfaomega - Altaria 3051


Curso avanzado de Java

<ui :fragme nt rendered="#{ usuario Be an. enviaCa becera }">


<ui:insert name="cabecera">
<ui:include src="/cabecera.xhtml" />
</ui:insert>
</ui:fragment>
<ui :fragment rendered ="#{ usuarioBean.in icio }">
<ui :insert name="contenidologin">
<ui:include src="/cont enidologin.xhtml" />
</ui:insert>
</ui:fragment>
<ui :fragment ren dered=" #{usuario Be an .blog}">
<ui:insert name="contenidoblog">
<ui:include src="/contenidoblog.xhtml" />
</ui:insert>
</u i :fragment>
</div>
</h:body>
</ui:composition>
</html>

• "nombreusuario.xhtml" (elemento que permanecerá oculto mediante CSS):

<?xml version='l.O' encoding='UTF-8' ?>


<! DOCTYPE html PUBLIC "-//W3C//DTD XHTM L 1.0 Transitional//EN"
" http ://www. w3 .org/TR/xhtm 11/DTD/ xhtm 11-tra nsitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml"
xm lns :h=" http ://xm 1ns.jcp.org/jsf/htm 1"
xm 1ns :f=" http ://java .sun.com/jsf/core"
xm lns :u i=" http ://java .su n .com/jsf/facelets ">
<body>
<ui:composition>
<div id="nombreusuario">
<h:outputText value="#{usua rioBean.nombreUsu ario }" />
</div>
</ui:composition>
</body>
</html>

• "mensajeflash.xhtml" (elemento que ocultaremos o mostraremos con JavaScript):

<?xml version='l.O' encoding='UTF-8' ?>


<!DOCTYPE html PUBLIC "-//W3C//DTD XHTM L 1.0Transitionai//EN"
" http ://www. w3 .org/TR/xhtm 11/DTD/ xhtm 11-tra nsitiona l.dtd" >
<html xmlns="http://www.w3.org/1999/xhtml"
xm 1ns: h=" http ://xm 1ns.jcp.org/jsf/htm 1"
xm 1ns :f=" http ://java .sun.com/jsf/ core"

1306 © A lfaomega - Altaria


JEE. Manual práctico

xm 1ns:u i=" http ://java .sun.com/jsf/facelets" >


<body>
<ui:composition>
<div id="mensajeflash">
<div class="error">
<h:outputText va lue="#{usuarioBean.error}" />
</div>
<div class="mensaje">
<h:outputText va lue="#{usuarioBean.mensaje}" />
</div>
</div>
</ui:composition>
</body>
</htm l>

• "cabecera.xhtml":
<?xm l version='l.O' encoding='UTF-8' ?>
<!DOCTYPE htm l PUBLIC "-/ /W3C//DTD XHTML 1.0 Transitional//EN"
"http ://www.w3 .org/TR/xhtm 11/DTD/xhtm11-tra nsitiona l.dtd">
<htm l xmlns="http://www.w3.org/1999/xhtml"
xm lns:h="http://xmlns.jcp.org/jsf/htm 1"
xm lns:f="http://java.sun .com/jsf/core"
xm 1ns:u i=" http ://java .sun.com/jsf/facelets" >
<body>
<ui:composition>
<div id="cabecera">
<ui :fragm ent rendered=" #{ usuarioBea n. blog}" >
<ui:insert name="logout">
<u i:include src="/logout.xhtml" />
</ui:insert>
</ui:fragment>
<h1>
<h:outputText va lue="Biog JEE" />
</h1>
<h2>
<h :outputText va lue="#{usuarioBean.titulo }" />
</h2>
<div class="clear"></div>
</div>
</u i:composition>
</body>
</htm l>

© Alfaomega - Altaria 3071


Curso avanzado de Java

• "logout.xhtml":
<?xml version='l.O' encoding='UTF-8' ?>
< !DOCTYPE html PUBLIC "-//W3C//OTO XHTML l. OTransitional//EN"
"http://www.w3 .org/TR/xhtmi1/DTD/xhtm 11-transitiona l.dtd ">
<html xm lns="http://www.w3.org/1999/xhtm l"
xm 1ns: h=" htt p ://xm lns.jcp .org/jsf/htm 1"
xmlns:f="http://java .su n .com/jsf/core"
xm 1ns: u i=" http ://java .sun .com/jsf/facelets">
<body>
<ui :com position>
<div id="logout">
<h:form>
<h:comma nd link class="button gray" value="Logout"
acti on= "#{usuarioBea n .salida Usuario()}" />
</h:form>
</div>
</ui:composition>
</body>
</html>

• "contenidologin.xhtml":
<?xml version='l.O' encoding='UTF-8' ?>
< !DOCTYPE html PUBLIC "-//W3C//DTD XHTML l.O Transitional//EN"
"http://www.w3 .org/TR/xhtmll/DTD/xhtm 11-transitiona l.dtd ">
<html xm lns="http://www.w3.org/1999/xhtml"
xm lns: h=" http ://xm lns.jcp .org/jsf/htm 1"
xmlns:f="http://java .su n .com/jsf/core"
xm 1ns: u i=" http://java .sun .com/jsf/facelets">
<body>
<ui :com position>
<div id="contenido">
<div id="login">
<h:form>
<h:paneiGrid id="login" columns="2">
<h :outputlabel value="Nombre de usuario:"/>
<h:inputText id="username" label="username"
va lue="#{usua rioBean. no m breUsuario }" />
<h:commandButton class="button gray" value="Enviar"
type="subm it" action="#{ usua rioBean.accesoUsuario()}" />
<h:commandButton class="button gray"
value="Restablecer" t ype="reset" />
</h :paneiG rid>
</h:form>
</div>

1308 © Alfaomega - Altaria


JEE. Manual práctico

</div>
</ui:composition>
</body>
</htm l>

• "con tenido blog.xhtml":


<?xm l version='l.O' encoding='UTF-8' ?>
< !DOCTYPE htm l PUBLIC "-/ /W3C//DTD XHTMll.O Transitional//EN"
"http ://www.w3 .org/TR/xhtm 11/DTD/xhtm11-tra nsiti o na l.dtd">
<htm 1xmlns="http://www.w3.org/1999/xhtm 1"
xm lns:h="http ://xmlns.jcp.org/jsf/htm 1"
xm 1ns:f="http://java.sun .com/jsf/core"
xm 1ns:u i=" http ://java .sun.com/jsf/facelets" >
<body>
<ui:composition>
<div id="contenido">
<div id="izquierdo" class="red">
<div id="titulousuarios">
<h:outputText value="Usuarios" />
</div>
<div id="listadousuarios"></div>
</div>
<div id="central" class="red">
<div class="tituloposts">
<h:outputText value="Posts" />
</div>
<div id="listadoposts">
<d iv class="tituloposts">
<h:outputText value="Escribe un Post"/>
</div>
<d iv class="escribepost">
<h:outputText value="¿En qué estás pensando?"/>
<br />
<h:form>
<h :inputTextarea id="escribepost" la bel="escribepost">
</h:inputTextarea>
</h:form>
<br />
<h:link class="escribepost button gray" value="Enviar" />
</div>
<d iv class="tituloposts">
<h:outputText value="Posts tuyos y de los demás usuarios"/>
</div>
</div>
</div>

© Alfaomega - Altaria 3091


C urso a vanza do de Java

<div id="derecho" class="red">


<div class="titulo">
<h:outputText value="Chat" />
</div>
<div id="contenedorchats"></d iv>
<div id ="enviochat">
<div class="titulo">
<h:outputText va lue="Área de Chat" />
</div>
<h:form>
<h :inputTexta re a id="cam pochat" ></h :in putTextarea>
</h:form>
<br />
<span id="usuariochat" nombreusuario="" />
<h:link id="enviarchat" class="button gray" value="Enviar" />
</div>
</div>
</div>
</ui:composition>
</body>
</html>

9.6 Ejercicio 6
Debemos implementar las clases "PostResource.java" y "ComentarioResource.java".

• Clase "PostResource.java ":


package webservices;
import domainmodei.BiogPost;
im port exceptions. BlogException;
import java.utii.Hashtable;
import java.utii.List;
im port javax.naming.lnitiaiContext;
im port javax.naming.Nam ingException;
import javax.ws.rs.core.Context;
import javax.ws.rs.core.Urilnfo;
import javax.ws.rs.Consumes;
import javax.ws.rs.Produces;
import javax.ws.rs.GET;
import javax.ws.rs.POST;
im port javax.ws.rs. Path;
import javax.ws.rs.PathParam;
im port javax.ws.rs.WebApplication Exception;
im port javax.ws.rs.core. M ediaType;
im port javax.ws.rs.core. Response .Status;

1310 © A lfaomega - Altaria


JEE. Manual práctico

import service.BiogServiceRemote;
@Path("/post")
@Consumes(MediaType.APPLICATION_JSON)
@Produces( MediaType .APPLICATION_J SO N)
public class PostResource {
prívate BlogServiceRemote blogService = nu ll;
@Context
prívate Urilnfo context;
public PostResource() {
if (blogService == null) {
try {
blogService = obtenerReferenciaBiogService();
} catch (NamingException ex) {
System.out.println("Error crítico. Sin acceso al back-end");
}
}
}
@G ET
pu blic List<BiogPost> getPosts() {
try {
re tu rn blogService .obtenerTodoslosPosts();
} catch (BiogException ex) {
throw new WebApplicationException(Status.INTERNAL_SERVER_ERROR);
}
}
@POST
public BlogPost addPost(BiogPost post) {
if (post== null) {
throw new WebApplication Exception(Status.BAD_REQU EST);
}
String autor = post.getAutor();
String contenido = post.getContenido();
if (autor== null 11 autor.eq uals("") 11 contenido== null 11
contenido.equals("")) {
throw new WebApplicationException(Status.BAD_REQUEST);
}
Long autorld = null;
try {
autorld = blogService.existeUsuario(autor);
} catch(Exception e) {
th row new WebAppl ication Exception (Status.INTE RNAL_SERVER_ ERROR);
}
if (autorld == -ll) {
throw new WebApplicationException(Status.BAD_REQUEST);
}
try {

© Alfaomega - Altaria 3111


Curso avanzado de Java

post= blogService.crearPost(post);
} catch(Exception e) {
throw new WebApplicationException(Status.INTERNAL_SERVER_ERROR);
}
return post;
}
@GET
@Path("/{postld}")
public BlogPost getPostByld(@ PathParam("postld") String post Id) {
if (postld == null 11 postld.equals('"')) {
throw new WebApplicationException(Status.BAD_REQUEST);
}
Long postldl = null;
try {
postldl = Long.valueOf(postld);
} catch(Exception e) {
throw new WebApplicationException(Status.BAD_REQUEST);
}
BlogPost post = null;
try {
post= blogService.obtenerPostPorld (postldl);
if (post== null) throw new WebApplicationException(Status.NOT_FOUND);
} catch(Exception e) {
throw new WebApplicationException(Status.INTERNAL_SERVER_ERROR);
}
return post;
}
@Path ("/ {postld }/comentario")
public ComentarioResource getComentarioResource() {
return new ComentarioResource();
}
private BlogServiceRemote obtenerReferenciaBiogService()
throws NamingException {
Hashtable hash = new Hashtable();
hash .put(javax.na m i ng.Context.l NITIAL_CONTEXT _FACTO RY,
"we blogic.jnd i.W Llnitia IContextFactory" );
hash.put(javax.naming.Context.PROVI DER_ URL, "t3 ://loca lhost:7001 ");
javax.naming.Context ctx = new lnitiaiContext(hash);
BlogServiceRemote bsr = (BiogServiceRemote)
ctx.looku p("java :global. Blog. Blog-ejb. BlogService !"
+ "service.BiogServiceRemote");
return bsr;
}

1312 © A lfaomega - Altaria


JEE. Manual práctico

}
Y para el subrecurso tenemos:

• Clase "ComentarioResource.java":
package webservices;
import domainmodei. BiogPost;
import domainmodei.ComentarioPost;
import java.utii.Hashtable;
import java.utii.List;
import javax.naming. l nitiaiContext;
import javax.naming.NamingException;
import javax.ws.rs.core.Context;
import javax.ws.rs.core.Urilnfo;
import javax.ws.rs.Consumes;
import javax.ws.rs.Produces;
import javax.ws.rs.GET;
import javax.ws.rs.POST;
import javax.ws.rs.Path;
import javax.ws.rs.PathParam;
import javax.ws.rs.WebApplicationException;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;
import service.BiogServiceRemote;
@Path("/")
@Consumes{MediaType.APPLICATION_JSON)
@Produces(MediaType.APPLICATION_JSON)
public class ComentarioResource {
prívate BlogServiceRemote blogService = nu ll;
@Context
prívate Urilnfo context;
public ComentarioResource() {
if (blogService == null) {
try {
blogService = obtenerReferenciaBiogService();
} catch {NamingException ex) {
System.out.println("Error crítico. Sin acceso al back-end");
}
}
}
@GET
pu blic List<ComentarioPost> getComentarios(@ Path Pa ra m ("postld") String post Id) {
if (postld==null 11 postld.equals("")) {
throw new WebApplication Exception(Response.Status.BAO _REQU EST);
}
l ong postldl = null;
try {

© Alfaomega - Altaria 3131


C urso a vanzado de Java

postldl = Long .valueOf(postld);


} catch(Exception e) {
throw new WebApplicationException(Response.Status.BAO _REQU EST);
}
BlogPost post = null;
try {
post= blogService.obtenerPostPorld(postldL);
if (post== null)
throw new WebApplicationException(Response.Status. NOT_FOUNO);
} catch(Exception e) {
throw new WebApplicationException(Response.Status.INTERNAL_SERVER_ ERROR);
}
return post.getComenta rios();
}
@POST
public ComentarioPost anadeComentarioPost(@ PathParam("postld")
String postld, ComentarioPost comentario) {
if (postld==null 11 postld.equals("")) {
th row new WebAppl ication Excepti on (Response .Status. BAO _REQU EST);
}
Long postldl = nu ll;
try {
postldl = Long.valueOf(postld);
} catch(Exception e) {
th row new WebAppl ication Excepti on( Response .Status. BAO _REQU EST);
}
BlogPost post = null;
try {
post= blogService.obtenerPostPorld(postldL);
if (post == null)
throw new WebApplicationException(Response.Status.NOT_ FOUNO);
} catch(Exception e) {
throw new WebApplicationException(Response.Status.INTERNAL_SERVER_ ERROR);
}
try {
comentario= blogService.anadirComentarioPost(post, comentario);
} catch(Exception e) {
throw new WebApplicationException(Response.Status.INTERNAL_SERVER_ ERROR);
}
return comentario;
}
private BlogServiceRemote obtenerReferenciaBiogService()
throws NamingException {
Hashtable hash = new Hashtable();
hash .put(javax.na mi ng.Context.l NITIAL_ CONTEXT_ FACTO RY,
"we blogic.jnd i .W Llnitia IContextFactory" );

1314 © Alfaomega - Altaria


JEE. Manual práctico

hash.put(javax.naming.Context.PROVIDER_ U RL, "t3 ://localhost:7001 ");


j avax.naming.Context ctx = new lnitiaiContext(hash);
BlogServiceRemote bsr = (BiogServiceRemote)
ctx.looku p( "java :global. Blog.Biog-ej b. BlogServi ce!"
+ "service.BiogServiceRemote");
return bsr;
}
}

9.7 Ejercicio 7
Se nos propone dar toda la funcionalidad al ejercicio del blog. Tenemos un punto de
acceso al back-end en el EJB stateless BlogService que implementa las interfaces local
y remota. Veamos cómo queda nuestro módulo WAR, en el cual hemos implementado
ya las páginas JSF y el bean gestionado de sesión "UsuarioBean". La estructura de
carpetas del módulo WAR es la siguiente:

$···@ Blog-war $ (j So urce Pac:kages


S (a Web Pages éJ..-133 est;¡¡dodiente
$ ffi WEB-INF !-··~ Comentario.java
$ ffi css ¡_H i! EstadoCiiente.java
$··tn img f-··1!! MensajeChat.java
$··ln l ¡. .~ MensajeFiash.java
¡ ¡.H. ~ Vis1a00M .jS !····~ Post. java
; f· ~ blog.js LH ~ usuario.java
. l. ~ j(Jlery-3.l.l.min.js. 8-··133 managedbeans
!·I!J abecera.xhtml ' L..~ UsuarioBean.java
!-I!J contenidoblog.Khtml 8···133 servidodiente
!·· · I!J contenidologin. xhtml . L.. ~ ServidoCiiente.java
!···I!J indeK.xhtml éJ.' .. f33. ser.,.lets
! liJ
.H logout xhtnil
! L.. ~ BlogServlet.java
!·I!J mens.ajeflash. Kh11lil
$ [33 socket
!·iiJ nombreusuario.Khtrnl
: L.. ~ BlogSocket. java
, L [¡) piantil ablog.Khtrril
$ [33 socket.modelo
$ (i Source Padages
LHi! Operadon.java
1 B··EJ3 estadodiente
S·· l33 util
: ¡ f·· ~
! ! '

Comentario.java
!
L.!---'..-'.
' __;·~
!n> _ . , _lie.
"Jlip__,._,,.__ _ __ J
l....@ JSONManager .java

En el planteamiento del ejercicio hemos visto cómo quedan las clases:


• MensajeFlash.java.

• EstadoCliente.java .

• Usua rio.ja va .

• JSONManager.java.

© Alfaom ega - Altaria 3151


Curso avanzado de Java

Todas del paquete "estadocli ent e". Dicho paquete es el que con tiene la definición en
el l ado del servidor de las v i stas d e cad a uno d e los clientes (cada clien te tendrá una
vista p ar t icul ar). El rest o de clases que componen el paquete "estadocliente" son las
sigu iente s:

• "Post.java":
package estadocliente;
import java.io.Serializable;
import java.utii.List;
public class Post implements Serializable {
private String id;
private String fecha Instante;
private String autor;
private String contenido;
private List<Comentario> comentarios;
public Post(){}
public String getld() {
return id;
}
public void setld(String id) {
this.id = id;
}
public String getFecha lnstante() {
return fecha 1nstante;
}
public void setFecha lnstante(String fecha Instante) {
this.fechalnstante = fechalnstante;
}
public String getAutor() {
return autor;
}
public void setAutor(String autor) {
this.autor = autor;
}
public String getContenido() {
return contenido;
}
public void setContenido(String contenido) {
this.contenido =contenido;
}
public List<Comentario> getComentarios() {
return comentarios;
}
public void setComentarios(List<Comentario> comentarios) {
this.comentarios = comentarios;
}

1316 © A lfaomega - Altaria


JEE. Manual práctic o

}
• "Comentario.java":
package estadocliente;
import java.io.Serializable;
public class Comentario implements Serializable {
private String nombreUsuario;
private String fecha Instante;
private String contenido;
public Comentario(){}
public String getNombreUsuario() {
return nombreUsuario;
}
public void setNombreUsuario(String nombreUsua rio) {
this.nombreUsuario = nombreUsuario;
}
public String getFechalnstante() {
return fechalnstante;
}
public void setFechalnstante(String fechalnstante) {
this.fechalnstante = fechalnstante;
}
public String getContenido() {
return contenido;
}
public void setConten ido(String contenido) {
this.contenido =conten ido;
}
}

• "Mensaj eCha t.java ":


package estadocliente;
import java.io.Serializable;
public class MensajeChat implements Serializable {
private String nombreAutor;
private String fecha Instante;
private String contenido;
pu blic M ensajeChat(){}
public String getNombreAutor() {
return nombreAutor;
}
public void setNombreAutor(String nombreAutor) {
this.nombreAutor = nombreAutor;
}
public String getFechalnstante() {
return fechalnstan t e;

© Alfaomega - Altaria 3171


Curso avanzado de Java

}
public void setFecha lnstante(String fec halnstante) {
this.fechalnstante = fechalnstante;
}
public String getContenido() {
return contenido;
}
public void setContenido(String contenido) {
this.contenido =contenido;
}
}

Necesitamos una clase que genere los objetos que se envían al cliente: "MensajeFlash"
y "EstadoCliente". Dicha clase es la que da servicio al cliente y está compuesta por mé-
todos estáticos. Se encuentra en el paquete "serviciocliente" y se llama ServicioCliente.

• Clase "ServicioCliente.java":
package serviciocliente;
import domainmodei.BiogPost;
import domainmodei.ComentarioPost;
import estadocliente.Comentario;
im port estadocliente. EstadoCiiente;
import estadocliente. M ensajeChat;
im port estadocliente. M ensajeFiash;
import estadocliente.Post;
import estadocliente.Usuario;
import exceptions.BiogException;
import java.utii.Arraylist;
import java.utii.Hashtable;
import java.utii.List;
import javax.naming.Context;
im port javax.naming.lnitiaiContext;
im port javax.naming.Nam ingException;
import service.BiogServiceRemote;
public class ServicioCiiente {
prívate static BlogServiceRemote blogService = nu ll;
public static EstadoCiiente obtenerEstadoCiiente(String nombreUsuario)
throws BlogException {
EstadoCiiente estadoCiiente = null;
try {
if (blogService == null) {
blogService = obtenerReferenciaBiogService();
}
estadoCiiente = new EstadoCiiente();
estadoCiiente .setNom bre Usua río( no m breUsuario);
estadoCiiente.setUsua rios(getUsua riosRelaciona dos Usu ario(nom breUsua río));

1318 © A lfaomega - Altaria


JEE. Manual práctic o

estadoCI iente .setPosts(getTodoslosPosts() );


String chatActivoCon = getNombreUsuarioChatActivo(nombreUsuario);
estadoCiiente.setChatActivo(chatActivoCon);
if (chatActivoCon.equals("")) {
estadoCiiente.setChats(new Arraylist<MensajeChat>());
) else {
estadoCiiente.
setChats(getMensajesChatActivo(nombreUsuario,chatActivoCon));
)
} catch(NamingException 1 BlogException e) {
if (e instanceof BlogException) {
t hrow new BlogException(e.getMessage());
} else if (e instanceof NamingException){
throw new BlogException("Error interno");
}
}
return estadoCiiente;
}
public static MensajeFiash obtenerMensajeFiash(String nombreUsuario,
String mensaje, String error) {
MensajeFiash mensajeF = new MensajeFiash();
mensaje F.setN o m breU su ario(nombre Usuario);
mensajeF.setTitulo("EI mejor blog del mundo");
mensajeF.setMensaje(mensaje);
mensaje F.setError( error);
return mensajeF;
}
prívate static BlogServiceRemote obtenerReferenciaBiogService()
throws NamingException {
Hashtable hash = new Hashtable();
hash .put( Context.IN ITIAL_ CONTEXT_FACTORY,
" we blogic.jnd i .WLI n iti aiContextFactory" );
hash.put(Context.PROVIDER_ URL, "t3 ://localhost:7001 ");
Context ctx = new ln itiaiContext(hash);
BlogServiceRemote bsr = (BiogServiceRemote)
ctx.looku p( "java :global. Blog. Blog-ej b. BlogService!"
+ "service.BiogServiceRemote");
return bsr;
}
prívate static List<Usuario> getUsuariosRelacionadosUsuario
(String nombreUsuario) throws BlogException {
List<Usuario> usuariosSistema = new Arraylist<>();
List<String> nombresUsuarios = blogService.obtenerTodosUsuarios();
String chatActivo = blogService.obtenerChatActua iUsuario(nombreUsuario);
for(String esteUsuario : nombresUsuarios) {
Usuario u = new Usuario();

© Alfaomega - Altaria 3191


Curso avanzado de Java

u .setNom bre(esteUsuario );
if (esteUsuario.equals(nombreUsuario)) {
u.setYoM ismo(true);
)
if (esteUsuario.equals(chatActivo)) {
u .setCh atea ndo(true );
)
usua riosSiste m a. add (u);
)
return usuariosSistema;
}
private static list<Post> getTodoslosPosts() throws BlogException {
list<BiogPost> postsDomin io =blogService.obtenerTodoslosPosts();
List<Post> postsSistema = new Arraylist<>();
for (BiogPost estePost: postsDominio) {
Post p = new Post();
p.setAutor( estePost.getAutor() );
p.setFecha 1nsta nte(estePost.getFecha 1nsta nte() );
p .setConte nido( este Post.getConten ido());
p .setl d (este Post. getl d ().toS tri ng ());
list<ComentarioPost> comentariosPost =estePost.getComentarios();
list<Comentario> cPost = new Arraylist<>();
for(ComentarioPost esteComentario : comentariosPost} {
Comentario e = new Comentario();
c.setConte nido( esteComenta rio .getCome nta rio() );
c.setFecha Instante( esteCome nta rio .getFecha 1nsta nte());
c.setN o m breUsua rio( esteCom enta rio .getUsu ario());
cPost.add(c);
}
p.setComentarios( ePost);
postsSistema .add(p );
)
return postsSistema;
}
private static String getNombreUsuarioChatActivo(String nombreUsuario)
throws BlogException {
String usuarioChatActivo = blogService.obtenerChatActuaiUsuario(nombreUsuario);
return usuarioChatActivo; 11 Contiene el nombre o una cadena vacía.
}
private static List<Mensaj eChat> getMensajesChatActivo(String nombreUsuariol,
String nombreUsuario2) throws BlogException {
List<domainmodei.MensajeChat> mensajes =
blogService.obtenerTodoslosM ensajesChat( no m breU su ario 1, no m breUsuario2);
List<MensajeChat> mensajesUsuarios = new Arraylist<>();
for(doma inmodei.MensajeChat esteMensaje: mensajes) {
MensajeChat chat = new MensajeChat();

1320 © A lfaomega - Altaria


JEE. Manual práctic o

chat.setNom breAutor( este Mensaje .getE nviado Por());


chat.setConten ido( este M e nsaje .getConte nido());
chat.setFecha 1nsta nte( esteMensaje.getFecha 1nsta nte() );
m ensajesUsua rios.add (chat);
}
return mensajesUsuarios;
}
}

Dicha clase representa la generación de la información que se envía al cliente, pero ¿y


la información que se recibe desde éste? Dicha información debe ser recibida y mapeada
por una clase Java. Como vimos en el planteamiento del ejercicio, la información que
se envía del cliente al servidor es un objeto JSON de la siguiente forma:
var operacion ={
definicion: "",
no m breU su ario: 1111,
idPost: "",
contenidoTexto: "",
1111
usuarioChat:
};

Donde definición puede tomar alguno de los siguientes valores:

• keepalive .
• login .
• escribePost.
• escribeComen tario .
• chateacon .
• enviachat.
La clase Java que mapea dicha información es la siguiente:
• Clase "operacion.java":
package socket.modelo;
import java.io.Serializable;
public class Operacion implements Serializable{
prívate String definicion;
prívate String nombreUsuario;
prívate String id Post;
prívate String contenidoTexto;
prívate String usua rioChat;
public Operacion() {}
public String getDefinicion() {
return definicion;

© Alfaomega - Altaria 3211


Curso avanzado de Java

}
public void setDefinicion(String definicion) {
this.definicion =definicion;
}
public String getNombreUsuario() {
return nombreUsuario;
}
public void setNombreUsuario(String nombreUsuario) {
this.nombreUsuario = nombreUsuario;
}
public String getldPost() {
return id Post;
}
public void setldPost(String id Post) {
=
this.idPost idPost;
}
public String getContenidoTexto() {
return contenidoTexto;
}
public void setContenidoTexto(String conten idoTexto) {
t his.contenidoTexto = con t enidoTexto;
}
public String getUsuarioChat() {
return usuarioChat;
}
public void setUsuarioChat(String usuarioChat) {
this.usuarioChat = usua rioChat;
}
}

La clase "BlogServlet" que se muestra en la estructura de archivos no contiene in-


formación relevante. De hecho, dicho servlet no se usa y podría eliminarse.

Ya sólo nos queda ver cómo encajan tod as las piezas del puzle en el lado del cliente
y en el lado del servidor.

En el lado del cliente tenemos:

• La ú ltima versión de la librería jQuery.

• La aplicación que se ejecuta cuando se entra o se sale del blog (cuando se recarga
la página JSF): "blog.js".

• La aplicación que se ejecuta una vez dentro del blog, y que maneja los eventos
del usuario y actualiza el estado de la página: "VistaDOM.js".

1322 © Alfaomega - Altaria


JEE. Manual práctico

Veamos el código:

• "JavaScript blog.js":
=
var vista DOM null;
var nombreUsuario = null;
S(doeu m ent). ready(function(){
vistaDOM = new VistaDOM();
vista DOM. mensajeFiash();
this.nombreUsuario = vistaDOM.obtenerNombreUsuario();
if (this.nombreUsuario !== ") vistaDOM.setWebSocket(this.nombreUsuario);
} );
El corazón del cliente del blog es "VistaDOM.js".

• JavaScript "VistaDOM.js":
var VistaDOM = function () {
this.webSocket = null;
this.nombreUsuario = null;
};
VistaDOM.prototype.obtenerNombreUsuario =function () {
var $divNombreUsuario =$('div#nombreusuario');
var nombreUsuario = $divNombreUsuario.html();
var nombreUsuarioSinEspacios = nombreUsuario.trim();
if (nombreUsuarioSinEspacios !== ") {
nombreUsuario = nombreUsuario.trim();
this.nombreUsuario = nombreUsuario;
return nombreUsuario;
} else {
return "·
'
}
};
VistaDOM.prototype.setWebSocket =function (nombreUsuario) {
var sThis = this;
th is.asocia Eventos();
this.webSocket = new WebSocket("ws://localhost:7001/Biog-war/blogsocket");

setTimeout(function () {
sThis.webSocket.onmessage = function (mensaje) {
sTh is. recibe Mensaje( mensaje);
};
sTh is.setKeepAI ive();
11 Espera para enviar el nombre de usuario.
var operacion = {
definicion: "login",
nombreUsuario: nombreUsuario,
idPost: "",

© Alfaomega - Altaria 3231


C urso a vanzado de Java

contenidoTexto: "",
usuarioChat: ""
};
var mensaje= JSON.stringify(operacion);
sThis. webSocket.send (mensaje);
}, 2000);
};
VistaDOM.prototype.setKeepAiive = function O {
var sThis = this;
setlnterval(function O {
var operacion = {
defi nicion: "keepalive",
nombreUsuario: sThis.nombreUsuario,
idPost: '"',
contenidoTexto: "",
usuarioChat: ""
};
var mensaje= JSON.stringify(operacion);
sThis. webSocket.send (mensaje);
}, 4000);
};
VistaDOM.prototype.recibeMensaje = function (mensaje) {
var mensajeJson = $.parseJSON(mensaje.data);
if (mensajeJson.operacion === 'mensajeflash') {
th is.setea DatosFiash (mensajeJson);
th is. mensajeFiash 0;
} else if (mensajeJson.operacion === 'actualiza') {
th is.actua liza Modelo( m ensajeJson );
}
};
VistaDOM.prototype.seteaDatosFiash = function(datos) {
var $divMensajeFiash = $('div#mensajeflash');
var $divMensaje = $divMensajeFiash.find('div.mensaje');
var $divMensajeError = $divMensajeFiash.find('d iv.error');
$d ivMensaje.emptyO;
$d ivMensajeError.em pty0;
var mensajelnfo = datos.mensaje;
var mensajeError = datos.error;
var mensajelnfoSinEspacios = mensajelnfo.trimO;
var mensajeErrorSinEspacios = mensajeError.trimO;
if (mensajelnfoSinEspacios !== ") {
$divM ensaje .htm 1( mensajelnfo );
}
if (mensajeErrorSinEspacios !== ") {
$d ivM ensajeError.htm 1( mensaje Error);
}
};

1324 © A lfaomega - Altaria


JEE. Manual práctico

VistaDOM.prototype.mensajeFiash = function () {
var $divMensajeFiash = $('div#mensajeflash');
$divM ensaje Flash .css('d isplay'/ block');
var $divMensaje = $divMensajeFiash.fínd('div.mensaje');
var $divMensajeError = $divMensajeFiash.fínd('div.error');
var contenido Mensaje= $d ivMensaje.html();
var contenidoMensajeSinEspacios = contenidoMensaje.trim();
var contenidoMensajeError = $divMensajeError.html();
var contenidoMensajeErrorSinEspacios = contenidoMensajeError.trim();
if (contenidoMensajeSinEspacios === ") {
$divM en saje. re m oveCiass('visible').addCia ss( 'oculto');
} else {
$divM ensaje. re m oveCiass('ocu lto') .addCiass( 'visible');
}
if (contenidoMensajeErrorSinEspacios === ") {
$divM ensaje Error. removeCiass('visible') .addCiass( 'ocu lto');
} else {
$ d ivM en saje Error. re m ove e1ass ('oculto' ).ad d Cla ss( 'vi si b 1e');
}
setTimeout(function () {
$divMensajeFiash.fadeOut("slow", function () {});
}, 4000);
};
VistaDOM.prototype.actualizaModelo = function (estado) {
th is.actual iza M odeloU su ario( estado. nombre Usuario);
th is.actualiza M odeloU su arios( estado.usua rios );
th is.actualiza M odeloPosts( estado.posts);
th is.actua liza M odeloChatActivo( estado.chatActivo );
th is.actu aliza M odeloM ensajes( estado.chats);
th is.desasocia Eventos();
th is.asocia Eventos();
};
Vista DOM .prototype.desasociaEventos = function () {
$('a'). un bi nd ('click');
$( 'textarea#cam pochat') .unbind( 'keypress');
};
Vista DOM .prototype.asociaEventos = function () {
var sThis = this;
$('a.escribepost').on('click', function (event) {
eve nt.preventDefa u lt();
var $textArea = $('div.escribepost textarea');
if ($textArea.length >O) {
var contenidoPost = $textArea.val();
$textArea .val(");
var contenidoSinEspacios = contenidoPost.replace(' ', ");
if (contenidoSinEspacios !== ") {
var operacion = {

© Alfaomega - Altaria 3251


C urso a vanzado de Java

definicion: "escribePost",
nombreUsuario: sThis.nombreUsuario,
idPost: '"',
contenidoTexto: contenidoPost,
usuarioChat: ""
};
var mensaje= JSON .stringify(operacion);
sTh is.webSocket.send(me nsaje );
}
}
} );
$('a.envcomentario').on('click', function (event) {
eve nt. preventDefa u lt();
var $this = $(this);
var idPostComentario = $this.attr('idpost');
var $textArea = $('div.escribecomentariopost textarea[idpost='"
+ idPostComentario + '"]');
if ($textArea.length >O) {
var contenidoComentario = $textArea.va l();
$textArea .va 1(");
var conten idoSinEspacios = contenidoComentario.replace(' ', ");
if (conten idoSinEspacios !== ") {
var operacion = {
definicion: "escribeComentario",
nombreUsuario: sThis.nombreUsuario,
idPost: idPostComentario,
contenidoTexto: contenidoComentario,
usuarioChat: ""
};
var mensaje= JSON .stringify(operacion);
sTh is.webSocket.send( mensaje);
}
}
} );
$('a.chatear').on('click', function (event) {
eve nt. preventDefa u lt();
var $enlace= $(event.target);
var nombreUsuarioChat = $enlace.attr('nombreusuario');
var operacion = {
definicion: "chateacon",
nombreUsuario: sThis.nombreUsuario,
idPost: "",
contenidoTexto: "",
usuarioChat: nombreUsuarioChat
};
var mensaje= JSON.stringify(operacion);
sThis. webSocket.send (mensaje);
} );

1326 © A lfaomega - Altaria


JEE. Manual práctico

$('a#enviarchat').on('click', function (event) {


event.preventDefa u lt();
var $contenidoMensaje = $('div#enviochat textarea');
var conten idoMensaje = $contenidoMensaje.va l();
$con te nidoM e nsaje .va 1(" );
var contenidoSinEspacios = contenidoMensaje.replace('', ");
if (conten idoSinEspacios !== ") {
var $spa n Usua rioChat = $('span#usuariochat');
var nombreUsuarioChat = $spanUsuarioChat.attr('nombreusuario');
var operacion = {
definicion: "enviachat",
nombreUsuario: sThis.nombreUsuario,
idPost: "",
contenidoTexto: contenidoMensaje,
usuarioChat: nombreUsuarioChat
};
var mensaje= JSON.stringify(operacion);
sTh is.we bSocket.se nd (m e nsaje);
}
});
$('div#enviochat textarea').on('keypress', function (event) {
if (event.which === 13) {
event.preventDefa u lt( );
var $contenidoMensaje = $('div#enviochat textarea');
var contenidoMensaje = $contenidoMensaje.val();
$contenido Mensaje .va 1( ");
var contenidoSinEspacios = contenidoMensaje.replace(' ', ");
if (contenidoSinEspacios !== ") {
var $spanUsuarioChat = $('span#usuariochat');
var nombreUsuarioChat = $spanUsuarioChat.attr('nombreusuario');
var operacion = {
de fin icion: "enviachat",
nombreUsuario: sThis.nombreUsuario,
idPost: "",
contenidoTexto: conten idoMensaje,
usuarioChat: nombreUsuarioChat
};
var mensaje= JSON.stringify(operacion);
sThis.webSocket.send (mensaje);
}
}
});
};
Vista DOM .prototype.actualizaModeloUsuario = function (nombreUsuario) {
if ((nombreUsuario) && (typeof (nombreUsuario) === 'string')
&& (nombreUsuario.length >O)) {

© Alfaomega - Altaria 3271


C urso a vanzado de Java

var $divNombreUsuario = $('div#nombreusuario');


$divNombre Usua rio.em pty();
$divNom bre Usuario. htm 1( no m breU su ario);
}
};
VistaDOM.prototype.actualizaModeloUsuarios = function (listadoUsuarios) (
var $divlistadoUsuarios = $('div#listadousuarios');
$d ivlistadoUsua rios.em pty();
var nUsuarios = listadoUsuarios.length;
for (var i =O; i < nUsuarios; i++) {
var esteUsuario = listadoUsuarios[i);
var soyYo = esteUsuario.yoMismo;
var chateando = esteUsuario.chateando;
var nombre= esteUsuario.nombre;
var $usuario = $('<div class="usuario"></div>');
var $saltolinea = $('<br />');
$usuario .a ppe nd (nombre);
$usuario .a ppe nd ($sa ltoli nea );
if ((!soyYo) && (!chateando)) {
var $botonChatear =$('<a class="chatear button gray" nombreusuario="'
+ nombre+ '" href="#">Chatear</a>');
$usuario.append($botonChatear);
}
$divlistadoUsua rios.a ppe nd($usua rio );
}
};
VistaDOM.prototype.actualizaModeloPosts = function (listadoPosts) {
var $areaPost = $('div.escribepost textarea');
$a re aPost. va 1( ");
if ((listadoPosts) && (listadoPosts instanceof Array)) {
var $1istadoPostsDOM = $('div#listadoposts');
var $divsPostsCabecera = $1istadoPostsDOM
.ti nd( 'd iv.ti tu loposts .ca be cera');
var $divsPosts = $1istadoPostsDOM.find('d iv.post');
$divsPostsCabecera.remove();
$divsPosts. re m ove();
var nPosts = listadoPosts. length;
for (var i =O; i < nPosts; i++) {
var estePost = listadoPosts[i];
var idPost = estePost.id;
var fechaPost = estePost.fechalnstante;
var nombreUsuario = estePost.autor;
var contenido= estePost.contenido;
var comentarios= estePost.comentarios;
var $tituloPostDOM = $('<div class="titu loposts cabecera">Post</div>');
var $postDOM = $('<d iv class="post"></div>');

1328 © A lfaomega - Altaria


JEE. Manual práctico

var $tituloUsuario = $('<b>Usuario : </b>');


var $tituloFecha = $('<b>Fecha: </b>');
var $tituloContenido = $('<b>Contenido: </b>');
var $divNombreUsuario = $('<div></div>')
.a ppend ($titulo Usuario). append (nombre Usuario);
var $divFecha = $('<div></div>')
.a ppend ($titu loFech a) .append (fecha Post);
var $divContenido = $('<div></div>')
.a ppend ($titu loContenido) .append (contenido);
var $divEscribeComentario = $('<div class="escribecomentariopost"></div>');
var $escribeTuComentario = $('<div>Escribe tu comentario</div>');
var $textareaComentario = $('<textarea id="escribecomentario" idpost="'
+id Post+ "' name="escribecomentario"></textarea>');
var $enlaceEnviaComentario =
$('<a href="#" class="envcomentario button gray" idpost="'
+ idPost + '">Enviar</a>');
$d ivEscribeCome ntario.a ppen d ($escribe TuCo menta rio );
$d ivEscri beCo me ntario.appen d ($texta reaCom entario);
$d ivEscri beComentario.a ppend ($en lace EnviaComentario );
$postDOM .a p pend( $divNom breUsua rio );
$postDOM .a p pend ($divFecha );
$postDO M .a p pe nd ($divCo nten ido);
$postDOM .a p pe nd($divEscribeComenta rio );
var nComentarios = comentarios.length;
for (var j =O; j < nComentarios; j++) (
var esteComentario = comentarios[j);
var nombreUsuarioComentario = esteComenta rio .nombreUsuario;
var fechaComentario = esteComentario.fechalnstante;
var contenidoComentario = esteComentario.contenido;
var $divUsuario = $('<div><b>Usuario: </b></div>');
$d ivU su ario .a ppend (no m breUsua rioCom enta rio);
var $divFechaComentario = $('<div><b>Fecha: </b></div>');
$d ivFechaCo menta rio .a ppend (fechaCome nta rio );
var $divContenidoComentario = $('<div><b>Comentario: </b></div>');
$d ivContenidoComenta rio .a ppe nd (conten idoCom enta rio );
var $divComentario = $('<div class="comentariopost"></div>');
$d ivCom enta rio.a ppe nd($d ivUsua rio );
$d ivCom enta rio.a ppend ($divFechaComenta rio );
$divCom enta rio.a ppend ($d ivContenidoCom entario );
$postDO M .a ppend($d ivComentario);
}
$listado PostsDOM .a ppen d ($tituloPostDO M);
$listado PostsDOM .a ppen d ($ postDO M);
}
}
};

© Alfaomega - Altaria 3291


C urso a vanza do de Java

Vista DOM .prototype .actualiza ModeloChatActivo = function (chatActivo) {


if ((chatActivo) && (typeof (chatActivo) === 'string') && (chatActivo.length >O)) {
var nombreUsuarioChat = chatActivo;
var $divTituloChat = $('div#enviochat>div.titulo');
$divTituloCh at.em pty();
if (nombreUsuarioChat !== ") {
$divTituloChat.append('Chatea con'+ nombreUsuarioChat);
} else {
$divTituloChat.append('Espacio para el chat');
}
var $spanUsuarioChat = $('span#usuariochat');
$spa n Usua rioChat. attr( 'no m breusua rio', chatActivo );
}
};
VistaDOM.prototype.actualizaModeloMensajes = function (mensajesChatActivo) {
if ((mensajesChatActivo) && (mensajesChatActivo instanceof Array)) {
var $divContenedorChats = $('div#contenedorchats');
$divContenedorChats.empty();
var nMensajes = mensajesChatActivo.length;
for (var i =O; i < nMensajes; i++) {
var esteMensaje = mensajesChatActivo[i);
var nombreUsuario = esteMensaje.nombreAutor + ': ';
var contenido= esteMensaje.contenido;
var $divMensaje = ${'<div class="mensaje"></div>');
va r $usuario= $('<b></b>');
$u su ario.a ppend (nombre Usuario);
$divM en saje .a ppend ($usuario);
$divM en saje .a ppend( canten ido);
$divContenedorCh ats.a ppen d ($divM e nsaje);
}
}
};

Y sólo nos queda por ver el corazón de la aplicación en el servidor.


• Clase "BlogSocket.java":
package socket;
import doma inmodei.BiogPost;
import domainmodei.ChateaCon;
import doma inmodei.ComentarioPost;
import doma inmodei.MensajeChat;
im port estadocliente. EstadoCiiente;
im port estadocliente. M ensajeFiash;
im port exceptions. BlogException;
import java.io.IOException;
import java.utii.Collection;

1330 © A lfaomega - Altaria


JEE. Manual práctic o

import java.utii.HashMap;
import java.utii.Hashtable;
import java.util.lterator;
import java.utii.Map;
im port javax. na mi ng.Context;
import javax.naming. l nitiaiContext;
import javax.naming.NamingException;
import javax.websocket.OnCiose;
import javax.websocket.OnMessage;
import javax.websocket.OnOpen;
import javax.websocket.Session;
im port javax.websocket.server.ServerEndpoi nt;
import service.BiogServiceRemote;
import socket.modelo.Operacion;
import utii.JSONManager;
im port serviciocliente .ServicioCiiente;
@ServerE nd point( 11 /blogsocket 11 )
public class BlogSocket {
prívate BlogServiceRemote bsr = null;
prívate static Map<String, Session> usuarios= new HashMap<String, Session>();
@OnOpen
pu blic void abrirSocket(Session ses ion Usuario) {
usuarios. put(sesion Usuario .getld (), ses ion Usuario);
}
@OnMessage
public void recibeMensaje(String mensaje, Session sesionUsuario)
throws IOException {
synchronized (this) {
String nombreUsuario = 1111 ;
try {
if (bsr == null) {
bsr = obtenerReferenciaBiogService();
}
Operacion operacion = (Operacion) JSONManager
.generateTOfromJson (mensaje, Opera eion .class);
if (operacion.getDefinicion() .equa ls( 11 logi n11 )) {
nombreUsuario = operacion.getNombreUsuario();
se si o n Usu arío .getU se rP ro pe rti es()
.put ( 11 nombreUsuario 11 , nom breUsuario );
} else if (operacion.getDefi nicion().equals( 11 escribePost11 )) {
nombreUsuario = operacion.getNombreUsuario();
String contenidoPost = operacion.getContenidoTexto();
BlogPost post= new BlogPost();
post.setAutor(nombreUsuario);
post. setCo nten id o (e o nte nido Post);
bsr.crea rPost ( post);

© Alfaomega - Altaria 3311


Curso avanzado de Java

} else if (operacion.getDefinicion(}.equa ls("escribeComentario")) {


nombreUsuario = operacion.getNombreUsuario(};
String idPost = operacion.getldPost(};
String comentario = operacion.getContenidoTexto(};
BlogPost post= bsr.obtenerPostPorld(Lon g.valueOf(idPost));
ComentarioPost comentarioPost = new ComentarioPost(};
comenta rioPost.setCom enta rio( comentario);
comenta rioPost.setUsuario( nom breU su ario);
bsr.a na di rComenta rioPost(post, comentario Post);
} else if (operacion.getDefinicion(} .equa ls("chateacon")) {
nombreUsuario = operacion.getNombreUsuario(};
String usuarioChateaCon = operacion.getUsuarioChat(};
ChateaCon chatea = new ChateaCon(};
chatea .setUsuario( no m breUsua rio);
chatea .setCon (usua rioChateaCon);
bsr.a breChatCon (chatea);
} else if (operacion.getDefinicion(}.equa ls("enviachat")) {
nombreUsuario = operacion.getNombreUsuario(};
String usua rioChateaCon = operacion.getUsuarioChat(};
String mensajeCht = operacion.getContenidoTexto(};
MensajeChat mensajeChat = new M ensajeChat(};
mensajeChat.setConten ido( m ensajeCht);
mensajeChat.setE nviadoPor( no m breUsuario);
mensajeChat.setE nviad oA( u su a rioChateaCon);
bsr.enviaMensajeChat(mensajeChat);
}
if ( !operacion.getDefinicion(}.equals("keepalive")) {
Collection<Session> sesiones= usuarios.values(};
lterator it = sesiones.iterator(};
while (it.hasNext()) {
Session estaSesion = (Session) it.next(};
String nombreEsteUsuario = (String) estaSesion
.getUse rProperties(}.get(" nombre Usuario");
EstadoCiiente eCiiente
= ServicioCiiente .obtenerEstadoCiiente( nom breEsteUsua rio);
String estado= JSONManager.generateJson(eCiiente);
estaSesion .getBasicRem ote(} .sendText( estado);
}
}
} catch (BiogException e) {
M ensajeFiash mensajeFiash = ServicioCiiente
.obtenerMensajeFiash(nom breUsuario, '"', e .getMessage());
String mensajeF = JSONManager.generateJson(mensajeFiash);
ses ion Usu a rio.getBasicRemote() .sendText( mensaje F);
} catch (NamingException e) {
MensajeFiash mensajeFiash = ServicioCiiente

1332 © Alfaomega - Altaria


JEE. Manual práctico

.obtenerMensajeFiash(nombreUsuario, "","Error interno");


String mensajeF = JSONManager.generateJson(mensajeFiash);
se si o nU su ario.getBa si eRe m o te ().sen dText( m en saje F);
)
}
}
@OnCiose
public void cerrarSocket(Session sesionUsuario) {
usuarios. re m ove( sesio n Usuario. getld ());
}
private BlogServiceRemote obtenerReferenciaBiogService() throws NamingException {
Hashtable hash =new Hashtable();
hash. put(Context.IN ITIAL_ CONTEXT_ FACTORY,
" we b logic.jnd i .WLI n itiaiContextFactory");
hash.put(Context.PROVIDER_U RL, "t3://localhost:7001 ");
Context ctx = new lnitiaiContext(hash);
BlogServiceRemote bsr = (BiogServiceRemote)
ctx.looku p( "java :global. Blog. Blog-ej b.BlogService!"
+ "servi ce.BiogServiceRemote");
return bsr;
}
}

© Alfaomega - Altaria 3331


Curso avanzado de Java

Bibliografía y fuentes de información


• Keogh, Jim. J2EE: Manual de referencia. McGraw-Hill/Interamericana de España.
ISBN 9788448139803.

• Gamma Erich, Helm, Richard, Johnson, Ralph y Vlissides, John. Patrones de


Diseño: elementos de software orientado a objetos reutilizables. Addison-Wesley.
ISBN 9788478290598.

• Gulabana, Sunil. Developing RESTful Web Services with Jersey 2.0. Packt Pu-
blishing. ISBN 9781783288298.

• Ceballos, Francisco Javier. Java. Inte rfaces Gráficas y Aplicaciones para Internet.
Ra-Ma. ISBN 9788499645223.

1334 © Alfaomega - Altaria

También podría gustarte