Documentos de Académico
Documentos de Profesional
Documentos de Cultura
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
ISBN: 978-607-622-853-1
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.
Indice general
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.
El manual que tiene ante sí se centra en estudiar de forma generalista las principales
características de JEE.
© 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.
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
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.
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.
.... 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
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.
carpeta de Destino
<Atrás
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
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
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
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 •.•
Summary
Cick Instan to start the insllllation.
' t.' ' . . •
111
Completamos la instalación.
Setup Complete
Ckk Finish b:> flnish the Netseans lOE setup.
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.
• 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...........................
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).
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. Choose Prciect
2. ... Q, '"""" L-------------------------------------~
C . -ies:
:~ Java
i--{)1 JavaFX Java Class Lbrary
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.
Pre staciones
8·-
Gener.a te Javadoc
Run
New •
Build
Clean ami Build
Clean
Rul!l
Profile
Test Alt+F6
Hola Mundo
• Clases y objetos .
• Atributos y métodos .
• Método main. Archivo Manifest.
• Herencia .
• Polimorfismo .
• Clases abstractas .
• Clases estáticas .
• Implementación de interfaces .
• Excepciones .
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.
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.
+teñir el pelo.
+comprar gafas de otro color.
+aprender un nuevo idioma .
+perfecci onar un idioma.
+ir al alergólogo
-ducharse .
• 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:
Persona
• colorPelo: enum;
• colorGafas: enum;
-idiomas; Nillelldioma
-alergias ;
~ 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
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;
}
package dominio;
public enum Alergias {
ACAROS, LACTOSA, POLEN, GRAMINEAS;
}
• 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();
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 );
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.
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".
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").
Aceptar 11 Car<~G91ar
V'alor de la varia'ble;:
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
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
~
l'
1
lnfl'lnlil
re Adulo ;¡-
• Número de ruedas.
• Máxima velocidad que puede alcanzar.
• ¿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.
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;
}
• 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".);
}
}
}
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()+ "" .);
}
}
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;
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
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".);
}
}
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".);
}
}
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.
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.
CUADr<.i\DO
RE CTA~tGULO
Cuadrado R.ectilnguto lr~
TRI!v'iG\llO
~.~
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
}
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;
}
}
package dominio;
public abstract class FiguraGeometrica {
protected double base;
protected double altura;
• 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) {
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);
}
}
FiguraGeometrica
(Clase Abstracta)
• base: double;
• altura: double;
(Atributos protegidos)
• 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;
}
@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);
}
}
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.
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
·· 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.
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.
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
á ·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;
}
• 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);
}
@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();
}
}
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) {
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
Error: 1 by zero.
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()+ "".);
}
}
}
El resultado es 3.
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
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-.
"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.
• Interfaz "DatosUsuario.java":
package dominio;
im port errores. DatosUsua rio Access Exception;
import java.utii.List;
• "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;
}
}
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();
}
}
• 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:
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.
Oatos.UsuarioAccassExctption
-.OatosUsuario"
'
·--------------------.
''
'
'' 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;
é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
• 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;
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(" * * ** ** * * * * * ");
us.muestraUsuario();
}
}
}
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:
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;
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 ".
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).
• Contenedor de servlets.
• Un largo etcétera, del cual no pretendemos ser exhaustivos.
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).
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
*caF~ritos
~Cit9;~
O Nombre
ClR.A.C.LE~
~~@ IS9S, 2015, Ora<>le y/o..,. iiiales. Todos md<-_....,'hos reservaO.. .
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.
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.
o~
ActualiZadones Automáticas ORACI E'
FUSION MIDDLIWAR&
Ofcnvc¡'!i:lo
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
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
canoebf
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
IM!olooOn Tcrni1odo
<Atrás !crmhu
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
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
y 81P.nV'll!'l~eo
de InstalaciOn ORACLE'
FUSlON MIDDLEWARE jjl
~ Acb.Joliz:ociones Auto:rit~
~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.
P UOS; Siguiente~t:
L•
~ c~ll f Domillio
tr -··
( yit!!jd¡! AstrírlJYidc!l
t C<rlfirurMOO Ayi¡lmda
i Re>ulr.lif"'de connQJrad6n
~
Ph:mtillo~~s
ORACLE"
FUSION Mfi)DLEWAAI
1 Cr~H Oornno
1 Plaon!as
Á'-~~*-
i1 •.
~·-<wr~
--~--·-- ~
~ Pn:lO'"c::!O dc Confiourodén
ü fin d: Conñou1odón Nombre
Contraseña
ser gB qiJe la~ la corroa:eña debe tener ;i :na"'3 S caracteres alfanurná-icos con un n{,.....,.o o
't CJxninQ
s:Jl=a;:
!Ertrinar
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
Clr'«Jar
'f
Re,umen de Configuraci ón
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.
Progrt!$0 de Conf~g1.nación
ORACLE"
fUSION MIODUWARE
'1' OolrillO
Crc:QI
t Pl3ntll!ae lOO%
<
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
( ~rmend~ Confqur~
Q Finde ConfWJuraOón
>
~~
~ B II
-
~"""'
.........
,_ _ _ ...,._.......
,.,_ _,
. .._.,4
• 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
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).
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.
L Sele<x:iona r servidor
2. . .. Servidor: r::~:,~or"iiToO.m;EiEEE-----------l
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".
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.
U!:lername: ~lw::•b=lo=g=c=::::::¡===~
Pm.sword: 1• •• ••••• •••1
~á
¡ ~-·<$ 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
Stop
REfresh
Rename...
Remove
. . .
Pod remos ver en la salida del sistema el log de operaciones de l n l ClO del servid or.
¡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)--·--·-» ~--·
. -. .
···-""' ,,,..,. ,.._, . ----·-.
. . ............... ¡. . . .-.·-·. ._,._
..., -_._. . . ..-.. --.....·------
..._ _, · - ·· ...... - > - - _.,_....... - .... __..., · -..- · . - - ..... - - - · · - · - ..--··
....
·..· ---·-
~ ~· -~.:~
-~ l+:ltOUIO·- · -.noU..--~--·::_••IA:Io•
-· ...
,,._,.., ,_ ... -.... -.................... - ··
- ""''.u"
~ ~- ·-··
......
.. - ...... ...••·--.......__ ,_,
····-·
•tt ~ Ml4 1 l.f.ll ....._ _ • - n"Oúooo -~-~- ...........-lt>o,_..OloWllw
..... -·--·---
,_........
~..,- ..... ...... -·· -·.. -·--- -
..- ...__ ................. ...·-· ........
_,
-....-- .......·_
• • - .._ _ _, - · .., ,,,_ ,......,,,_ ...u u - ._,_, _ _..,._.._..._..,.
u" .....- ·-·-·- _ _....,_ _ ,.,_..,141•-·•·
, _.,.
-..---
. . . Oto, . . . .
.-- -..-....
··- .._.._ _,_,_ --"'--..... -·. -;__,
... ~ Ml4
... »<•."
..-
-
~
,.,,.,u ___ .._----·-~,--
- - ..........
_. .
, ... ""._..............., ,._,.................._••_
nJ4 , .,,..,, • - . - . , . . _ .,..._ _ _.
-···-·~···- ..-:.-
. ............
·-·-
.. --~
• . . . _ ......_
_
~ -·-.,<~-
..- .....__ ........ .,_.._ ,_,.,_, -·- · ... .., __...... I..JIW'I'"""'_'__....___. -··- ·· ...............
......_...... ·-··
,_. -....... --·_ ......·-·. .-·u---·...., ......._··- --..-.,..,.,u.-
·-·-..... ._ . ............
. . . -. . . ..-,•..........
. . . _.. . ~.
,.,._...,. ·~·· ar.o
_. -·--· -·-·
-•o:o• oe>oooo•• .....
....... __
~.,U<.........,,
"'ot'" - ¡ -·•uu• .. -
..._ .... '""'''- _.... ...,,_,. .....·-·U> -· ........- ··- .... - ......
" " - " " " ~~- _..... "hcooo.. ' " "
'fWoohiU' oo ,,.. , ,,,.,, .,..., _ , ,, - , _. , _...., ....,,,.,, - • ~...... _.. , . _ o.>, ''•• -
La operación de arranque estará terminada una vez que veamos el siguiente mensaje:
Renarme...
Re m ove
Properties
. ___
....
. - ..- ~~. • ......,...._ .. .. _ , ... _ _ _.,¡, - ..- ' -... - ...... ........, ......
· $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".
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 .
Agregar usuario X
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"
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".
Nuevo v 1 [S<JJ)fri f
Origen de Datos Genérico
Topo
Origen de Qatos Qidünk
..., ~~~~!!tU 11!10\ro,~ M rdli8'> "" H'(rl,!l'lur <el 111~ O!l¡r.• lit ...,.tt)ll:<"·
• l..touu.,,....,.._ ,¡...
ra~<o•
Ull!tM
!Ab!~··
,I,Mf!~~
'""' C.:> . -~ --
Cuf.a~(l)
. . . . .
• •• l ' '
. ,. , •. . , . , , • ..,., , , ,, , , .,. , . . ,. , ..
-----------------------------------------------------------------------------
~, ,,,~·'•rw ,_, .,.,., ,. ~•• • •··~·-· · ~ ·
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.
Propioedadei: de la Conexi6n
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?
CO.ntraci!H.a:
1···..··· 1
¿Qué nombre de tabla ·O sentenda SQLde.sea utilizar para, pr·obar las conexiones de base de datos?
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.
1..~~
l!lu ....Sc~·
~~ ~·T~
~~ *~(~">I.M(I:I!Ittt~~(o~-)
·"
'" ·-. . l- -. ,........ . ~t ~ I>Jel
~ - - lo l do l
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.
~~
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) \
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.
Maquina rea l
localhost
AdminServer
dominio: lea m
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.
Contenedor de EJB
Contenedor de Servlets
,¡,
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).
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.
Concelor 11 A)V!b
Pasos
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¡
ISP...
Oe.<~n Mld Build
Setv&el.•.
Ou n
Yeliy
~ JdYo Clas~...
··~
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
.....
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
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>" );
}
}
@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";
}
}
• Ambos métodos tratan dos parámetros: la petición del cliente y la respuesta que
se le envía al cliente.
1Prestaciones ~ Prim
l!J·" MiPrimeraApp S...a!
$. .~ MiPrimeraAro-ei>
10
lB®
New •
Build
Clean and Build
Clean
Verify
Generate Javadoc
•
Build
Clean and Build
Clclln
Verify
Generate Javadoc
.Run
OqJuror
Profile
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>
Hemos creado un paquete llamado dominio. Dicho paquete incluye la lista de per-
sonas.
• 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;
@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";
}
Para poder incorporar el código Java dentro de la página JSP, se hace uso de la
siguiente sintaxis:
• 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()) {
@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;
@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";
}
}
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:
• 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.
• Clase "Persona.java":
package dominio;
import java.io.Serializable;
public class Persona implements Serializable {
public Persona(){}
prívate String 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;
}
}
JSP: lndex.jsp )
• 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" );
}
@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";
}
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/
1 E rrtvi a:r 1
~ 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
• 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:
package sessionbeans;
import javax.ejb.Local;
@Local
public interface ISessionBeanlocal {
11 M étodos de la interfaz local.
}
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.
De cola: queue.
Tema: topic.
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:
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
· contenido: Stríng
1 ·usuario: Usuario
· enviadoPor: usuario
• enviadoA: Usuario • chateaCon: Usuarl o
ComentaPost
(Relílcíón)
• "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 .
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.
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".
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:
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
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.
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
• 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;
}
• 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;
}
Comentarios:
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
Usuario 1
1 1
- nombreUsuario: Stnng
11
• gatters & setters
Po5t
1 escr.to por •
- recnarnstame: carenaar
contenido: Sting
1 autor: Usuario
~
ComentaPost
(Relotión)
fechatnstante: Calendar
oomentario: Strino
usu-ario: Usu::.rio
-
post: Post
• "ComentaPost".
• "Ch at".
• "ChatActual".
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 .
~·~ · ~ ~ 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
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
,. 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
-
protttltU giHEnUf,Van41gtrQ: Entllytllanagar:
.. D:t! a_Po~:tFaudeO:
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;
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();
}
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;
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);
}
@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
+ 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" .
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:
Su carrito de la compra
r'ISf!•le(ll!lonmre<~etplcY.J~ , - - - - - - - - - - - - - ,
¡- . . . . .. 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
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
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
(como se explicó anteriormente). Veamos primero cómo podemos definir la vista que se
ha mostrado a modo de p rototipo:
• 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>
</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>
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{
• 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) {
Estudiemos ahora el componente EJB sta teful "Carritos". Indicar que se implementa
una interfaz local y u na interfaz remota.
• 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;
@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;
}
}
@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");
}
.. .. e
Su carrito de la compra
1"''"' 11•·..-1
3 •••
S,QO 2
(l.ilO l:MO
..... 1,CO
•• 10.00
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:
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.
@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());
}
}
}
• 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());
}
}
}
Hola Mundo
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.
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:
É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.
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;
@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:
• 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:
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
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.
1Loodlmplom
Putdt asigrtar esta nueva dase a watQUiera de ertos servidores o du.sters. Seleccione los destinos.
Dc:stinos:
¡ ~~-
~ Adn~nServer
J~ 1es t1
Y finalmente veremos el serv1c10 RMI del servidor colgando del árbol JNDI con el
nombre "saludo".
}
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
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.
• EJB "EjemploSessionBean":
package ejemplo;
import javax.ejb.Stateful;
@Statefu l
public class EjemploSessionBean implements EjemploSessionBeanRemote {
private int contador;
pu blic EjemploSession Be an() {
contador= O;
}
@Override
public int getContador() {
cont ador++;
return contador;
}
}
• "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();
"
[J
v.,¡\lJr" Jd ;,.oolo;~.dor dt: 1 .....
d•1 b i'an :-.:
,., ...
..
e:.:
ll/-
llb .
;;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 .
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
}
}
} 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.
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 .
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
package sessionbeans;
import javax.ejb.Local;
@Local
public interface ContadorSingletonloca l {
public void incrementaContador();
public int getContador();
}
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++;
}
@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>" );
}
}
@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
• Mozilla Firefox:
Sf:rvlct Cont:~dotServlct )(
}
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.
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
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.
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
' . ..
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~.
.,.,........ :
s;.........._. ,"t
O w.;n,'•••O•,...~
1$ -.~
o ~,.."~
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 colo Define u
Deer a$Í
con.sumi
Define u
corn.mic
a todot 1
Las sigajentes propiadacles ~e utili2arán p;na id!!ltificcr el nue-10 Tema. 9 módulo ad:ual es \VseeJmsNodule.
* Nombre:
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,
Ot:stiiiOS :
Sc.rftd<tn:• JHS
O W~ a~W"WnnSc:I'Y'II:r
® w_&«) n,.sr_f"'ltt
O wseesoapJIRSJ~M:Strwr
Veamos cómo quedan los recursos JMS con los que tra baj aremos:
_..
~utnea llk AeoJrsos
ltl .,.......
--·
T. . ~delrtDI oes:p~teoue se~no
@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;
• Session.AUTO_ACKNOWLEDGE.
• Session.CLIENT_ACKNOWLEDGE.
• Session.DUPS_OK_ACKNOWLEDGE.
• Session.SESSION_TRANSACTED.
@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")
})
D JSPPage x
C (j) localhost:7001/EjemploJMS-war/
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:
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>" );
La filosofia es la misma. Veamos los dos beans que vamos a tener a la escucha:
• "TemaBean.java":
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"),
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).
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"
.... .,...
'
·--------- .. .-----------·'
''
BlogSeNice
Implementación CAPITULO
de MCV: JSF
• Beans administrados .
• Modelo de eventos.
• OpenFaces.
• PrimeFaces.
• jQuery4Jsf.
• lceFaces.
• Dependent. Su ciclo de vida depende del ciclo de vida de otro bean (seteado con
la anotación "@ManagedProperty").
• 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.
• Flow. Este tipo de beans se usa para la navegación entre las páginas.
• 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.
• 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 .
• ui:fragment .
• ui:include .
• ui:insert.
• ui:param .
• ui:repeat.
• u1:remove .
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).
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 " >
<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:
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:
HOL'I
1 tA~rar Despedida 1
Prestaciones
~ 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:
o Coi!>)O,Oill ~ )(
Su carrito de la compra
'~'
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.
• "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;
}
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;
}
}
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());
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.
• "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) .
package beans;
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=" " />
<h:commandButton value="Restablecer" type="reset" />
</f:facet>
</h:column>
</h:dataTable>
</h:form>
</div>
<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>
Digno de mención:
• Maquetamos con los contenedores definidos en nuestro CSS.
• 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".
Su carrito de la compra
1Eown 11 RulabOe«•J
~C:IP Producto
• 7."'
TOt3 t
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>
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>
Con este pequeño estudio de las cuatro etiquetas, tenemos el conocimiento suficiente
para crear una plantilla en JSF. Imaginemos la siguiente estructura:
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
.
.
.
.
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
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;
}
<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:
Apreciamos que se hace inclusión del fich ero "tex to.xhtml". Veamos el conten ido de
dic ho fic hero:
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>
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:
81 JEE
,. 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.
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".
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>
lod:S I3
wntentdol<l(:Jn
~-....
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
Se propone crear la hoja de estilos CSS, las páginas JSF y el managed bean de sesión
para conseguir dicha estructura.
• 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.
• 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.
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:
• No requiere de una capa de código adicional, como la definición del servicio web
(WSDL) .
• PUT. Esta operación del método HTTP nos permite actualizar el estado del re-
curso concreto.
Clase Alum no
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>
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}".
EJB definimos la unidad de persistencia, las entidades y los stateless session beans
para dichas entidades (fachadas) .
Clase Alumno
• 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;
@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;
}
@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;
}
}
@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 =
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.
• 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 .
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>
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-
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 "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) {
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":
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.
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 .
• 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 {
@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)
@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);
} 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;
}
@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;
}
• Petición GET.
URL: "http: / / localhost:700 1/ Cursos-war jwebresources /clase" .
Datos: (vacío).
Respuesta: [].
• 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).
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
• Petición GET.
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"
}
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"
}
• 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"
}
.. 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
} 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);
}
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;
}
@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 () );
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;
Y para el subrecurso:
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).
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.
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.
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.
<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.
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"
};
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.
• Login (JSF).
Ejercicios CAPITULO
resueltos
9.1 Ejercicio 1
La estructura de carpetas del proyecto es la siguiente:
$ . 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
'
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
} 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
}
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;
}
9.2 Ejercicio 2
Nuestro paquete de datos "data" en el proyecto del blog queda de la siguiente forma:
Source Pawges.
H -·1+1 data
. · ~ Chat.java
· ~ ChatActual.java
ComentaPostjava
Post. java
Usuario .java
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+")";
}
}
• "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;
}
• "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;
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
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 );
}
}
package datasessionbeans;
import data.Chat;
import java.utii.List;
import javax.ejb.Local;
@Local
public interface Data_ChatFacadeLocal {
void create(Chat chat);
• 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;
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
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;
}
}
Paquete "exceptions":
• Excep ción "BlogExcep tion.ja va ":
package exceptions;
public class BlogException extends Exception {
public BlogException() {}
public BlogException(String msg) {
super(msg);
}
}
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};
}
• 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 {
• 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,
• 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);
}
• 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
• Interfaz "BlogServiceLocal.java":
package service;
import javax.ejb.Local;
@Local
public interface BlogServicel ocal extends IBiog {}
}
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: "
}
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);
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();
} 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);
}
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");
}
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 .
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;
}
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;
}
}
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%;
}
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;
}
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">
• "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>
• "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>
</div>
</ui:composition>
</body>
</htm l>
9.6 Ejercicio 6
Debemos implementar las clases "PostResource.java" y "ComentarioResource.java".
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 {
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;
}
}
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 {
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:
Comentario.java
!
L.!---'..-'.
' __;·~
!n> _ . , _lie.
"Jlip__,._,,.__ _ __ J
l....@ JSONManager .java
• EstadoCliente.java .
• Usua rio.ja va .
• JSONManager.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;
}
}
• "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;
}
}
}
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));
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();
• 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;
}
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;
}
}
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.
• 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".
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: "",
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);
}
};
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 = {
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);
} );
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);
• 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.