4. El servicio web J2EE de citas
Volvamos a la arquitectura de la aplicación que vamos a construir:
![]() |
En esta parte nos centramos en la construcción del servicio web J2EE [1] ejecutado en un servidor Sun / Glassfish.
4.1. La base de datos
La base de datos, que llamaremos [dbrdvmedecins] , es una base de datos MySQL5 con cuatro tablas:

4.1.1. La tabla [MEDECINS]
Contiene información sobre los médicos gestionados por la aplicación [RdvMedecins].
![]() | ![]() |
- ID: número que identifica al médico - clave primaria de la tabla
- VERSION: número que identifica la versión de la línea en la tabla. Este número se incrementa en 1 cada vez que se realiza una modificación en la línea.
- NOM: el apellido del médico
- PRENOM: su nombre
- TITRE: su tratamiento (Srta., Sra., Sr.)
4.1.2. La tabla [CLIENTS]
Los clientes de los distintos médicos se registran en la tabla [CLIENTS]:
![]() | ![]() |
- ID: n.º de identificación del cliente - clave primaria de la tabla
- VERSION: número que identifica la versión de la línea en la tabla. Este número se incrementa en 1 cada vez que se realiza una modificación en la línea.
- NOM: el nombre del cliente
- PRENOM: su nombre
- TITRE: su tratamiento (Srta., Sra., Sr.)
4.1.3. La tabla [CRENEAUX]
Enumera los intervalos horarios en los que son posibles los RV:
![]() |
![]() |
- ID: número que identifica el intervalo horario - clave primaria de la tabla (línea 8)
- VERSION: número que identifica la versión de la línea en la tabla. Este número se incrementa en 1 cada vez que se realiza una modificación en la línea.
- ID_MEDECIN: número que identifica al médico al que pertenece este intervalo de tiempo – clave externa en la columna MEDECINS (ID).
- HDEBUT: hora de inicio de la franja horaria
- MDEBUT: minutos de inicio del horario
- HFIN: hora de fin de la franja
- MFIN: minutos de fin de franja
La segunda línea de la tabla [CRENEAUX] (véase [1] más arriba) indica, por ejemplo, que la franja n.º 2 comienza a las 8:20 y termina a las 8:40 y pertenece al médico n.º 1 (Sra. Marie PELISSIER).
4.1.4. La tabla [RV]
Enumera los RV asignados a cada médico:
![]() |
- ID: n.º que identifica el RV de forma única – clave primaria
- JOUR: día del RV
- ID_CRENEAU: franja horaria del RV – clave externa en el campo [ID] de la tabla [CRENEAUX] – determina tanto la franja horaria como el médico correspondiente.
- ID_CLIENT: n.º del cliente para el que se realiza la reserva – clave externa en el campo [ID] de la tabla [CLIENTS]
Esta tabla tiene una restricción de unicidad en la e e sobre los valores de las columnas unidas (JOUR, ID_CRENEAU):
Si una fila de la tabla [RV] tiene el valor (JOUR1, ID_CRENEAU1) para las columnas (JOUR, ID_CRENEAU), este valor no puede aparecer en ningún otro lugar. De lo contrario, significaría que se han tomado dos RV al mismo tiempo para el mismo médico. Desde el punto de vista de la programación Java, el controlador JDBC de la base de datos lanza un SQLException cuando se produce este caso.
La línea de id igual a 3 (véase [1] más arriba) significa que se ha reservado un RV para la franja n.º 20 y el cliente n.º 4 el 23/08/2006. La tabla [CRENEAUX] nos indica que la franja n.º 20 corresponde al horario de 16:20 a 16:40 y pertenece al médico n.º 1 (Sra. Marie PELISSIER). La tabla [CLIENTS] nos indica que el cliente n.º 4 es la Srta. Brigitte BISTROU.
4.2. Generación de la base de datos
Cree la base de datos MySql [dbrdvmedecins] con la herramienta que prefiera. Para crear las tablas y rellenarlas, puede utilizar el script [createbd.sql] que se le proporcionará. Su contenido es el siguiente:
4.3. Los elementos de la arquitectura del lado del servidor
Volvamos a la arquitectura de la aplicación que vamos a construir:
![]() |
En el lado del servidor, la aplicación estará formada por:
- una capa JPA que permite trabajar con la BD mediante objetos
- un EJB encargado de gestionar las operaciones con la capa JPA
- un servicio web encargado de exponer a los clientes remotos la interfaz del EJB en forma de servicio web.
Los elementos (b) y (c) implementan la capa [dao] representada en el esquema anterior. Se sabe que una aplicación puede acceder a un EJB remoto a través de los protocolos RMI y JNDI. En la práctica, esto limita los clientes a clientes Java. Un servicio web utiliza un protocolo de comunicación estandarizado que implementan diversos lenguajes: .NET, PHP, C++, ... Esto es lo que queremos mostrar aquí utilizando un cliente .NET.
Para una breve introducción a los servicios web, puede consultarse el curso [ref1], párrafo 14, página 109.
Un servicio web se puede implementar de dos maneras:
- mediante una clase anotada con @WebService que se ejecuta en un contenedor web
![]() |
- mediante un EJB anotado con @WebService que se ejecuta en un contenedor EJB
![]() |
![]() |
Aquí utilizaremos la primera solución:
En el curso [ref1], párrafo 14, página 109, se encuentra un ejemplo que utiliza la segunda solución.
4.4. Configuración de Hibernate para el servidor Glassfish
Dependiendo de la versión, es posible que el servidor Glassfish V2 incluido con NetBeans no cuente con las bibliotecas Hibernate que necesita la capa JPA/Hibernate. Si, al continuar con el tutorial, descubre que Glassfish no le ofrece una implementación JPA/Hibernate o que, al implementar los servicios, una excepción indica que no se encuentran las bibliotecas de Hibernate, debe añadir las bibliotecas en la carpeta [<glassfish>/domains/domain1/lib/ext] y, a continuación, reiniciar el servidor Glassfish:
![]() |
|
Las bibliotecas de Hibernate se encuentran en el archivo zip que acompaña al tutorial.
4.5. Las herramientas de generación automática de NetBeans
Volvamos a la arquitectura que debemos construir:
![]() |
Con NetBeans, es posible generar automáticamente la capa [JPA] y la capa [Ejb] que controla el acceso a las entidades JPA generadas. Es interesante conocer estos métodos de generación automática, ya que el código generado ofrece valiosas indicaciones sobre cómo escribir entidades JPA o el código EJB que las utiliza.
A continuación describimos algunas de estas herramientas de generación automática. Para comprender el código generado, es necesario tener buenos conocimientos sobre las entidades JPA, [ref1] y las EJB, [ref2].
Creación de una conexión de NetBeans a la base de datos
- Ejecutar SGBD y MySQL 5 para que BD esté disponible
- crear una conexión NetBeans a la base de datos [dbrdvmedecins]
![]() |
- en la pestaña [Files], en la rama [Databases] [1], seleccionar el controlador Jdbc MySQL [2]
- y, a continuación, seleccione la opción [3] «Connect Using», que permite crear una conexión con una base de datos MySQL
- en [4], introduzca la información que se le solicita
- y, a continuación, confirme en [5]
![]() |
- en [6], se crea la conexión. Se pueden ver las cuatro tablas de la base de datos conectada.
Creación de un proyecto EJB
![]() |
- en [1], crear una nueva aplicación, un módulo EJB
- en [2], elija la categoría [Java EE] y en [3] el tipo [EJB Module]
![]() |
- en [4], elija una carpeta para el proyecto y, en [5], asígnele un nombre; a continuación, finalice el asistente
- en [6] el proyecto generado
Añadir un recurso JDBC al servidor Glassfish
Vamos a añadir un recurso JDBC al servidor Glassfish.
![]() |
![]() |
- En la pestaña [Services], iniciar el servidor Glassfish [2, 3]
- en la pestaña [Projects], haga clic con el botón derecho del ratón en el proyecto EJB y, en [5], seleccione la opción [New / Other] que permite añadir un elemento al proyecto.

- en [6], seleccione la categoría [Glassfish] y en [7] indique que desea crear un recurso JDBC seleccionando el tipo [JDBC Resource]
- en [8], indicar que este recurso JDBC utilizará su propio grupo de conexiones
- en [9], asigne un nombre al recurso JDBC
- en [10], pasar al siguiente paso
![]() |
- en [11], se definen las características del grupo de conexiones del recurso JDBC
- en [12], asigne un nombre al grupo de conexiones
- en [13], seleccione la conexión NetBeans [dbrdvmedecins] creada anteriormente
- En [14], pasa al siguiente paso
- en [15], normalmente no hay nada que cambiar en esta página. Las propiedades de la conexión a la base de datos MySQL [dbrdvmedecins] se han extraído de las de la conexión Netbeans [dbrdvmedecins] creada anteriormente
- en [16], pasar al siguiente paso
![]() |
- en [17], mantenga los valores predeterminados propuestos
- en [18], finalice el asistente. Este crea el archivo [sun-resources.xml] [19] cuyo contenido es el siguiente:
El archivo anterior recoge toda la información introducida en el asistente en formato XML. Será utilizado por IDE Netbeans para solicitar al servidor Glassfish que cree el recurso «jdbc/dbrdvmedecins» definido en la línea 4.
Creación de una unidad de persistencia
La unidad de persistencia [persistence.xml] configura la capa JPA: indica la implementación JPA utilizada (Toplink, Hibernate, etc.) y la configura.
![]() |
![]() |
- en [1], haz clic con el botón derecho del ratón en el proyecto EJB y selecciona [New / Other] en [2]
- en [3], seleccione la categoría [Persistence] y, a continuación, en [4], indique que desea crear una unidad de persistencia JPA
![]() |
- en [5], asigne un nombre a la unidad de persistencia creada
- en [6], elija [Hibernate] como implementación JPA
- en [7], seleccionar el recurso Glassfish «jdbc/dbrdvmedecins» que se acaba de crear
- en [8], indique que no se debe realizar ninguna acción en la base de datos al instanciar la capa JPA
- finalice el asistente
- en [9], el archivo [persistence.xml] creado por el asistente
Su contenido es el siguiente:
De nuevo, recoge en un formato XML la información proporcionada en el asistente. Este archivo no es suficiente para trabajar con la base de datos MySQL5 «dbrdvmedecins». Deberíamos indicar a Hibernate el tipo de SGBD que hay que gestionar. Esto se hará más adelante.
Creación de las entidades JPA
![]() |
![]() |
![]() |
- en [1], hacer clic con el botón derecho del ratón sobre el proyecto y en [2] seleccionar la opción [New / Other]
- en [3], seleccione la categoría [Persistence] y, a continuación, en [4], indique que desea crear entidades JPA a partir de una base de datos existente.
![]() |
- en [5], seleccione la fuente JDBC «jdbc/dbrdvmedecins» que hemos creado
- en [6], las cuatro tablas de la base de datos asociada
- en [7,8], incluirlas todas en la generación de entidades JPA
- en [9], continuar con el asistente
![]() |
- en [10], las entidades JPA que se van a generar
- en [11], asigne un nombre al paquete de las entidades JPA
- en [12], elegir el tipo Java que encapsulará las listas de objetos devueltas por la capa JPA
- finalizar el asistente
- en [13], las cuatro entidades JPA generadas, una para cada tabla de la base de datos.
A continuación se muestra, por ejemplo, el código de la entidad [Rv], que representa una fila de la tabla [rv] de la base de datos [dbrdvmedecins].
Creación de la capa EJB de acceso a las entidades JPA
![]() |
![]() |
- en [1], haga clic con el botón derecho del ratón en el proyecto y en [2], seleccione la opción [New / Other]
- en [3], seleccione la categoría [Persistence] y, a continuación, en [4], el tipo [Session Beans for Entity Classes]
![]() |
- en [5], las entidades JPA creadas anteriormente se muestran
- en [6], selecciónalas todas
- en [7], ya han sido seleccionadas
- en [8], continuar con el asistente
![]() |
- en [9], asigne un nombre al paquete de los EJB que se van a generar
- en [10], indicar que los EJB deben implementar tanto una interfaz local como una remota
- cerrar el asistente
- en [11], los EJB generados
A continuación se muestra, por ejemplo, el código del EJB que gestiona el acceso a la entidad [Rv], es decir, a la tabla [rv] de la base de datos [dbrdvmedecins]:
Como se ha dicho, la generación automática de código puede ser muy útil para iniciar un proyecto y familiarizarse con las entidades JPA y EJB. A continuación, reescribiremos las capas JPA y EJB con nuestro propio código, pero el lector encontrará en ellas información que acabamos de ver en la generación automática de las capas.
4.6. El proyecto NetBeans del módulo EJB
Creamos un nuevo módulo EJB en blanco (véase el apartado 4.5):
![]() |
- el paquete [rdvmedecins.entites] agrupa las entidades de la capa JPA
- el paquete [rdvmedecins.dao] implementa el EJB de la capa [dao]
- el paquete [rdvmedecins.exceptions] implementa una clase de excepción específica de la aplicación
A partir de ahora, damos por hecho que el lector ha seguido todos los pasos del apartado 4.5. Tendrá que repetir algunos de ellos.
4.6.1. Configuración de la capa JPA
Recordemos la arquitectura de nuestra aplicación cliente/servidor:
![]() |
El proyecto NetBeans:
![]() |
La capa [JPA] se configura mediante los archivos [persistence.xml] y [sun-resources.xml] mencionados anteriormente. Estos dos archivos son generados por los asistentes que ya hemos visto:
- la generación del archivo [sun-resources.xml] se ha descrito en el apartado 4.5.
- La generación del archivo [persistence.xml] se ha descrito en el apartado 4.5.
El archivo [persistence.xml] generado debe modificarse de la siguiente manera:
- línea 3: el tipo de transacciones es JTA: las transacciones serán gestionadas por el contenedor EJB3 de Glassfish
- línea 4: se utiliza una implementación JPA/Hibernate. Para ello, se ha añadido la biblioteca Hibernate al servidor Glassfish (véase el apartado 4.4).
- línea 5: la fuente de datos JTA utilizada por la capa JPA tiene el nombre JNDI «jdbc/dbrdvmedecins».
- línea 8: esta línea no se genera automáticamente. Debe añadirse manualmente. Indica a Hibernate que el SGBD utilizado es MySQL5.
La fuente de datos «jdbc/dbrdvmedecins» se configura en el siguiente archivo [sun-resources.xml]:
- líneas 8-10: las características JDBC de la fuente de datos (URL de la base de datos, nombre y contraseña del usuario). La base de datos MySQL dbrdvmedecins es la descrita en el apartado 4.1.
- línea 7: las características del grupo de conexiones asociado a esta fuente de datos
4.6.2. Las entidades de la capa JPA
Recordemos la arquitectura de nuestra aplicación cliente/servidor:
![]() |
El proyecto NetBeans:
![]() |
El paquete [rdvmedecins.entites] implementa la capa [Jpa].
En el apartado 4.5 vimos cómo generar automáticamente las entidades JPA de una aplicación. Aquí no utilizaremos esta técnica, sino que definiremos nosotros mismos las entidades. No obstante, estas retomarán gran parte del código generado en el apartado 4.5. En este caso, queremos que las entidades [Medecin] y [Client] sean clases hijas de una clase [Personne].
La clase Persona se utiliza para representar a los médicos y a los clientes:
- línea 3: cabe señalar que la clase [Personne] no es en sí misma una entidad (@Entity). Va a ser la clase padre de entidades. La anotación @MappedSuperClass designa esta situación.
La entidad [Client] encapsula las filas de la tabla [clients]. Deriva de la clase [Personne] anterior:
- línea 3: la clase [Client] es una entidad JPA
- línea 4: está asociada a la tabla [clients]
- línea 5: deriva de la clase [Personne]
La entidad [Medecin], que encapsula las filas de la tabla [medecins], sigue el mismo patrón:
La entidad [Creneau] encapsula las líneas de la tabla [creneaux]:
- las líneas 15-17 modelan la relación «uno a varios» que existe entre la tabla [creneaux] y la tabla [medecins] de la base de datos.
La entidad [Rv] encapsula las filas de la tabla [rv]:
- las líneas 15-17 modelan la relación «uno a varios» que existe entre la tabla [rv] y la tabla [clients] de la base de datos, y las líneas 18-20 la relación «uno a varios» que existe entre la tabla [rv] y la tabla [creneaux]
4.6.3. La clase de excepción
![]() |
La clase de excepción [RdvMedecinsException] de la aplicación es la siguiente:
- línea 6: la clase deriva de la clase [RuntimeException]. Por lo tanto, el compilador no obliga a gestionarla con try / catch.
- línea 5: la anotación @ApplicationException hace que la excepción no sea «absorbida» por una excepción de tipo [EjbException].
Para entender la anotación @ApplicationException, volvamos a la arquitectura utilizada en el lado del servidor:
![]() |
La excepción de tipo [RdvMedecinsException] será lanzada por los métodos del EJB de la capa [dao] dentro del contenedor EJB3 y será interceptada por este. Sin la anotación @ApplicationException, el contenedor EJB3 encapsula la excepción que se ha producido en una excepción de tipo [EjbException] y la relanza. Es posible que no se desee esta encapsulación y se quiera dejar que salga del contenedor Ejb3 una excepción de tipo [RdvMedecinsException]. Esto es lo que permite la anotación @ApplicationException. Por otra parte, el atributo (rollback=true) de esta anotación indica al contenedor EJB3 que, si la excepción de tipo [RdvMedecinsException] se produce dentro de un método ejecutado en una transacción con un SGBD, esta debe ser revertida. En términos técnicos, esto se denomina realizar un rollback de la transacción.
4.6.4. El EJB de la capa [dao]
![]() |
![]() |
La interfaz Java [IDao] de la capa [dao] es la siguiente:
La interfaz local [IDaoLocal] del EJB se limita a derivar la interfaz [IDao] anterior:
Lo mismo ocurre con la interfaz remota [IDaoRemote]:
El EJB [DaoJpa] implementa ambas interfaces, la local y la remota:
- la línea 3 indica que el EJB remoto se llama «rdvmedecins.dao»
- la línea 4 indica que todos los métodos del EJB se ejecutan dentro de una transacción gestionada por el contenedor EJB3.
- La línea 5 muestra que el EJB implementa las interfaces local y remota.
El código completo del EJB es el siguiente:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 | |
- línea 8: el objeto EntityManager que gestiona el acceso al contexto de persistencia. Al instanciar la clase, este campo será inicializado por el contenedor EJB gracias a la anotación @PersistenceContext de la línea 7.
- línea 15: consulta JPQL que devuelve todas las filas de la tabla [clients] en forma de una lista de objetos [Client].
- línea 22: consulta análoga para los médicos
- línea 32: una consulta JPQL que realiza una unión entre las tablas [creneaux] y [medecins]. Se configura mediante el ID del médico.
- línea 43: una consulta JPQL que realiza una unión entre las tablas [rv], [creneaux] y [medecins] y que tiene dos parámetros: el ID del médico y el día de la cita.
- Líneas 55-57: creación de una cita y posterior almacenamiento de la misma en la base de datos.
- línea 67: eliminación de una cita de la base de datos.
- línea 76: realiza una consulta en la base de datos para encontrar a un cliente determinado
- línea 85: lo mismo para un médico
- línea 94: lo mismo para una cita
- línea 103: lo mismo para un horario
- Todas las operaciones con el contexto de persistencia em de la línea 9 pueden encontrar un problema con la base de datos. Por lo tanto, todas están rodeadas por un try / catch. La posible excepción se encapsula en la excepción «propia» RdvMedecinsException.
Una vez compilado, el módulo EJB da lugar a un archivo .jar de tipo « »:
![]() |
4.7. Implementación del EJB de la capa [dao] con NetBeans
NetBeans permite implementar fácilmente en el servidor GlassFish el EJB creado anteriormente.
![]() |
- En las propiedades del proyecto EJB, compruebe las opciones de ejecución [1].
- En [2], el nombre del servidor en el que se va a implementar el EJB
- en la pestaña [Services] [3], se inicia [4].
![]() |
- en [5], el servidor Glassfish una vez iniciado. Todavía no tiene ningún módulo EJB.
- Inicie el servidor MySQL y asegúrese de que la base de datos [dbrdvmedecins] está en línea. Para ello, puede utilizar la conexión de NetBeans creada en el apartado 4.5.
- En la pestaña [Projects] [6], se implementa el módulo EJB [7]: es necesario que se ejecute SGBD MySQL5 para que el recurso JDBC «jdbc/dbrdvmedecins» utilizado por el EJB sea accesible.
- En [8], el EJB desplegado aparece en el árbol del servidor Glassfish
![]() |
- En [9], se elimina el EJB desplegado
- En [10], el EJB ya no aparece en el árbol del servidor Glassfish.
4.8. Implementación del EJB de la capa [dao] con Glassfish
Aquí mostramos cómo implementar en el servidor Glassfish un EJB a partir de su archivo .jar.
- Inicie el servidor MySQL y asegúrese de que la base de datos [dbrdvmedecins] está en línea. Para ello, puede utilizar la conexión de NetBeans creada en el apartado 4.5.
Recordemos la configuración JPA del módulo EJB que se va a implementar. Esta configuración se realiza en el archivo [persistence.xml]:
La línea 5 indica que la capa JPA utiliza una fuente de datos JTA, c.a.d, gestionada por el contenedor EJB3, denominada «jdbc/dbrdvmedecins».
En el apartado 4.5 vimos cómo crear este recurso JDBC desde NetBeans. Aquí mostramos cómo hacerlo directamente con GlassFish. Seguimos el procedimiento descrito en el apartado 13.1.2, página 79 de [ref1].
Comenzamos eliminando el recurso para poder volver a crearlo. Lo hacemos desde NetBeans:
![]() |
- en [1], los recursos JDBC del servidor Glassfish
- en [2], el recurso «jdbc/dbrdvmedecins» de nuestro EJB
- en [3], el grupo de conexiones de este recurso JDBC
![]() |
- en [4], eliminamos el grupo de conexiones. Esto tendrá como efecto la eliminación de todos los recursos JDBC que lo utilizan, es decir, el recurso «jdbc/dbrdvmedecins».
- En [5] y [6], el recurso JDBC y el grupo de conexiones han sido eliminados.
Ahora utilizamos la consola de administración del servidor Glassfish para crear el recurso JDBC e implementar el EJB.
![]() |
- en la pestaña [services] [1] de NetBeans, inicie el servidor Glassfish [2] y, a continuación, acceda a su consola de administración
- en [4], inicie sesión como administrador (contraseña: adminadmin si no la ha cambiado durante la instalación o posteriormente).
![]() |
- en [5], seleccione la rama [Connection Pools] de los recursos de Glassfish
- en [6], cree un nuevo grupo de conexiones. Recuerde que un grupo de conexiones es una técnica para limitar el número de aperturas/cierres de conexiones con un SGBD. Al iniciar el servidor, N, un número definido por configuración, se abren conexiones con el SGBD. Estas conexiones abiertas se ponen a disposición de los EJB que las solicitan para realizar una operación con el SGBD. Tan pronto como esta finaliza, el EJB devuelve la conexión al grupo. La conexión nunca se cierra. Se comparte entre los diferentes subprocesos que acceden al SGBD
- en [7], asigne un nombre al grupo
- en [8], la clase que modela la fuente de datos es la clase [javax.sql.DataSource]
- en [9], el SGBD que contiene la fuente de datos es aquí MySQl.
- en [10], pase al siguiente paso
![]() |
- En [11], el atributo «Connection Validation Required» hace que, antes de proporcionar una conexión, el grupo compruebe que está operativa. Si no es así, crea una nueva. Esto permite que una aplicación siga funcionando tras una interrupción momentánea con SGBD. Durante la interrupción, ninguna conexión es utilizable y se envían excepciones al cliente. Cuando finaliza la interrupción, los clientes que siguen solicitando conexiones las obtienen de nuevo: gracias al atributo «Connection Validation Required», se volverán a crear todas las conexiones del grupo. Sin este atributo, el grupo detectaría que las conexiones iniciales se han interrumpido, pero no intentaría crear otras nuevas.
- En [12], se solicita el nivel de aislamiento «Read Committed» para las transacciones. Este nivel garantiza que una transacción T2 no pueda leer datos modificados por una transacción T1 hasta que esta última haya finalizado por completo.
- En [13], se solicita que todas las transacciones utilicen el nivel de aislamiento especificado en [12]
![]() |
- En [14] y [15], especifique la URL de BD cuyo pool gestiona las conexiones
- en [16], el usuario será root
- en [17], añada una propiedad
- en [18], añada la propiedad «Password» con el valor () en [19]. Aunque la captura de pantalla [19] no lo muestra, no se debe poner la cadena vacía, sino () (paréntesis de apertura, paréntesis de cierre) para indicar una contraseña vacía. Si el usuario root de su SGBD MySQL tiene una contraseña que no está vacía, introduzca dicha contraseña.
- en [20], finalice el asistente de creación del grupo de conexiones para la base de datos MySQL [dbrdvmedecins].
![]() |
- en [21], el grupo se ha creado. Haga clic en su enlace.
- En [22], el botón [Ping] permite crear una conexión con la base de datos [dbrdvmedecins]
- en [23], si todo va bien, aparecerá un mensaje indicando que la conexión se ha establecido correctamente
Una vez creado el grupo de conexiones, se puede crear un recurso Jdbc:
![]() |
- en [1], se selecciona la rama [JDBC Resources] del árbol de objetos del servidor
- en [2], se crea un nuevo recurso JDBC
- en [3], se le da un nombre al recurso JDBC. Este debe coincidir con el nombre utilizado en el archivo [persistence.xml]:
- en [4], se especifica el grupo de conexiones que debe utilizar el nuevo recurso JDBC: el que acabamos de crear
- en [5], se finaliza el asistente de creación
![]() |
- en [6] el nuevo recurso JDBC
Ahora que se ha creado el recurso JDBC, podemos implementar el archivo jar del EJB:
![]() |
- en [1], seleccione la rama [Enterprise Applications]
- en [2], con el botón [Deploy], indique que desea implementar una nueva aplicación
- en [3], indique que la aplicación es un módulo EJB
- en [4], seleccione el archivo JAR del EJB [serveur-ejb-dao-jpa-hibernate.jar] que se le habrá proporcionado para el TP.
- En [5], puede cambiar el nombre del módulo EJB si lo desea
- En [6], finalice el asistente de implementación del módulo EJB
![]() |
- En [7], se ha implementado el módulo EJB. Ya está listo para su uso.
4.9. Pruebas del EJB de la capa [dao]
Ahora que se ha implementado el EJB de la capa [dao] de nuestra aplicación, podemos probarlo. Lo haremos mediante el siguiente cliente Java:
![]() |
La clase [MainTestsDaoRemote] [1] es una clase de prueba JUnit 4. Las bibliotecas en [2] constan, por un lado:
- el archivo JAR del EJB de la capa [dao] [3] (véase el apartado 4.6.4).
- las bibliotecas Glassfish [4] necesarias para los clientes remotos de los EJB.
La clase de prueba es la siguiente:
- línea 13: cabe destacar la instanciación del proxy del EJB remoto. Se utiliza su nombre JNDI «rdvmedecins.dao».
- Los métodos de prueba utilizan los métodos expuestos por el EJB (véase el apartado 4.6.4).
Si todo va bien, las pruebas deben pasar:
![]() |
Ahora que el EJB de la capa [dao] está operativo, podemos pasar a su exposición pública a través de un servicio web.
4.10. El servicio web de la capa [dao]
Para una breve introducción al concepto de servicio web, consulte el apartado 14, página 111 de [ref1].
Volvamos a la arquitectura del servidor de nuestra aplicación cliente/servidor:
![]() |
Nos centramos aquí en el servicio web de la capa [dao]. La única función de este servicio es poner a disposición la interfaz del EJB de la capa [dao] a clientes multiplataforma capaces de interactuar con un servicio web.
Recordemos que hay dos formas de implementar un servicio web:
- mediante una clase anotada con @WebService que se ejecuta en un contenedor web
![]() |
- mediante un EJB anotado con @WebService que se ejecuta en un contenedor EJB
![]() |
Aquí utilizamos la primera solución. En NetBeans, debemos crear un proyecto de empresa con dos módulos:
- el módulo EJB que se ejecutará en el contenedor EJB: el EJB de la capa [dao].
- el módulo web que se ejecutará en el contenedor web: el servicio web que estamos creando.
Vamos a crear este proyecto empresarial de dos maneras.
4.10.1. Proyecto NetBeans - Versión 1
En primer lugar, creamos un proyecto NetBeans de tipo «Aplicación web»:
![]() |
- En [1], creamos un nuevo proyecto en la categoría «Java Web» [2] de tipo «Aplicación web» [3].
![]() |
- en [4], se le da un nombre al proyecto y en [5] se especifica la carpeta en la que debe generarse
- en [6], se establece el servidor de aplicaciones que ejecutará la aplicación web
- en [7], se establece el contexto de la aplicación
- en [8], se valida la configuración del proyecto.
![]() |
- en [9], el proyecto generado. El servicio web que estamos creando utilizará el EJB del proyecto anterior [10]. Por lo tanto, necesita hacer referencia al archivo .jar del módulo EJB [10].
- En [11], añadimos un proyecto NetBeans a las bibliotecas del proyecto web [12]
![]() |
- en [13], se selecciona la carpeta del módulo EJB en el sistema de archivos y se confirma.
![]() |
- En [14], el módulo EJB se ha añadido a las bibliotecas del proyecto web.
En [15], implementamos el servicio web con la siguiente clase [WsDaoJpa]:
- En la línea 4, la clase [WsdaoJpa] implementa la interfaz [IDao]. Recordemos que esta interfaz se define en el archivo EJB de la capa [dao] de la siguiente forma:
- línea 3: la anotación @WebService convierte la clase [WsDaoJpa] en un servicio web.
- Líneas 6-7: la referencia del EJB de la capa [dao] será inyectada por el servidor de aplicaciones en el campo de la línea 7. Recordemos que siempre es la implementación local (IDaoLocal en este caso) la que se inyecta. Esta inyección es posible porque el servicio web se ejecuta en la misma JVM que el EJB.
- Todos los métodos del servicio web están etiquetados con la anotación @WebMethod para que sean visibles para los clientes remotos. Un método no etiquetado con la anotación @WebMethod sería interno al servicio web y no visible para los clientes remotos. Cada método M del servicio web se limita a llamar al método M correspondiente del EJB inyectado en la línea 7.
La creación de este servicio web se refleja en una nueva rama del proyecto NetBeans:
![]() |
En [1] vemos el servicio web WsDaoJpa y en [2] los métodos que expone a los clientes remotos.
Recordemos la arquitectura del servicio web en desarrollo:
![]() |
Los componentes del servicio web que vamos a implementar son:
- [1]: el módulo web que acabamos de construir
- [2]: el módulo EJB que creamos en una etapa anterior y del que depende el servicio web
Para implementarlos juntos, hay que reunir ambos módulos en un proyecto de NetBeans denominado «empresarial»:
![]() |
En [1] creamos un nuevo proyecto empresarial [2, 3].
![]() |
- En [4,5], se le da un nombre al proyecto y se establece su carpeta de creación
- En [6], se selecciona el servidor de aplicaciones en el que se implementará la aplicación empresarial
- en [7], un proyecto empresarial puede tener tres componentes: aplicación web, módulo EJB, aplicación cliente. Aquí, el proyecto se crea sin ningún componente. Estos se añadirán posteriormente.
![]() |
- En [8], la aplicación empresarial recién creada.
![]() |
- en [9], haz clic con el botón derecho en [Java EE Modules] y añade un nuevo módulo
- en [10], solo se muestran los módulos de NetBeans actualmente abiertos en IDE. Aquí seleccionamos el módulo web [serveur-webservice-1-ejb-dao-jpa-hibernate] y el módulo EJB [serveur-ejb-dao-jpa-hibernate] que hemos creado.
- En [11], los dos módulos añadidos al proyecto empresarial.
Ahora nos queda implementar esta aplicación empresarial en el servidor Glassfish. A continuación, hay que iniciar SGBD y MySQL para que sea accesible la fuente de datos JDBC «jdbc/dbrdvmedecins» utilizada por el módulo EJB.
![]() |
- en [1], se inicia el servidor Glassfish
- si el módulo EJB [serveur-ejb-dao-jpa-hibernate] está implementado, se descarga [2]
- en [3], se implementa la aplicación de empresa
![]() |
- en [4], ya está implementada. Se ve que contiene los dos módulos: Web y EJB.
4.10.2. Proyecto NetBeans - versión 2
Ahora mostramos cómo implementar el servicio web cuando no se dispone del código fuente del módulo EJB, sino solo de su archivo .jar.
El nuevo proyecto NetBeans del servicio web será el siguiente:
![]() |
Los elementos destacados del proyecto son los siguientes:
- [1]: el servicio web se implementa mediante un proyecto NetBeans de tipo [Web Application].
- [2]: el servicio web se implementa mediante la clase [WsDaoJpa] ya estudiada
- [3]: el archivo EJB de la capa [dao], que permite a la clase [WsDaoJpa] acceder a las definiciones de las diferentes clases, interfaces y entidades de las capas [dao] y [jpa].
A continuación, creamos el proyecto empresarial necesario para la implementación del servicio web:
![]() |
- [1], creamos una aplicación empresarial [ea-rdvmedecins], inicialmente sin ningún módulo.
- En [2], añadimos el módulo web [serveur-webservice-ejb-dao-jpa-hibernate] anterior
- en [3], el resultado.
Tal cual, la aplicación empresarial [ea-rdvmedecins] no se puede implementar en el servidor Glassfish desde NetBeans. Se produce un error. Por lo tanto, hay que implementar manualmente el archivo EAR de la aplicación [ea-rdvmedecins]:
![]() |
- El archivo [ea-rdvmedecins.ear] se encuentra en la carpeta [dist] [2] de la pestaña [Files] de NetBeans.
- En este archivo [3] se encuentran los dos elementos de la aplicación empresarial:
- el archivo del EJB [serveur-ejb-dao-jpa-hibernate]. Este archivo está presente porque formaba parte de las bibliotecas a las que hacía referencia el servicio web.
- el archivo del servicio web [serveur-webservice- ejb-dao-jpa-hibernate].
- El archivo [ea-rdvmedecins.ear] se crea a partir de un simple Build [4] de la aplicación empresarial.
- En [5], la operación de implementación falla.
Para implementar el archivo [ea-rdvmedecins.ear] de la aplicación empresarial, procedemos tal y como se mostró al implementar el archivo EJB [serveur-ejb-dao-jpa-hibernate.jar] en el apartado 4.2. Volvemos a utilizar el cliente web de administración del servidor Glassfish. No repetiremos los pasos ya descritos.
En primer lugar, comenzaremos por «descargar» la aplicación empresarial implementada en el apartado 4.10.1:
![]() |
- [1]: seleccione la rama [Enterprise Applications] del servidor Glassfish
- en [2], seleccione la aplicación empresarial que desea descargar y, a continuación, en [3], descárguela
- en [4], la aplicación empresarial se ha descargado
![]() |
- en [1], elija la rama [Enterprise Applications] del servidor Glassfish
- en [2], implemente una nueva aplicación empresarial
- en [3], seleccione el tipo [Enterprise Application]
- en [4], especifique el archivo .ear del proyecto NetBeans [ea-rdvmedecins]
- en [5], implemente este archivo
![]() |
- en [6], la aplicación se ha implementado
- en [7], el servicio web [WsDaoJpa] aparece en la rama [Web Services] del servidor Glassfish. Lo seleccionamos.
- En [8], se dispone de diversa información sobre el servicio web. La más interesante para un cliente es la información [9]: la URI del servicio web.
- En [10], se puede probar el servicio web
![]() |
- En [11], la URI del servicio web a la que se ha añadido el parámetro ?tester. Esta URI muestra una página de prueba. Todos los métodos (@WebMethod) expuestos por el servicio web se muestran y pueden probarse. Aquí se prueba el método [13], que solicita la lista de clientes.
![]() |
- En [14], solo mostramos una vista parcial de la página de respuesta. Pero se puede ver que el método getAllClients sí ha devuelto la lista de clientes. La captura de pantalla nos muestra que envía su respuesta en formato XML.
Un servicio web se describe íntegramente mediante un archivo XML denominado archivo WSDL:
![]() |
- en [1] en la herramienta de administración web del servidor Glassfish, seleccione el servicio web [WsDaoJpa]
- en [2], siga el enlace [View WSDL]
![]() |
- en [3]: la URI del archivo WSDL. Es importante conocer esta información. Es necesaria para configurar los clientes de este servicio web.
- en [4], la descripción XML del servicio web. No comentaremos este contenido complejo.
4.10.3. Pruebas JUnit del servicio web
Creamos un proyecto NetBeans para «ejecutar» las pruebas ya realizadas con un cliente EJB, esta vez con un cliente para el servicio web recientemente implementado. Seguimos aquí un procedimiento análogo al descrito en el apartado 14.2.1, página 115 de [ref1].
![]() |
- en [1], un proyecto Java clásico
- en [2], la clase de prueba
- en [3], el cliente utiliza el archivo del EJB para acceder a las definiciones de la interfaz de la capa [dao] y de las entidades JPA. Recordemos que este archivo se encuentra en la subcarpeta [dist] de la carpeta del módulo EJB.
Para acceder al servicio web remoto, es necesario generar clases proxy:
![]() |
En el esquema anterior, la capa [2] [C=Client] se comunica con la capa [1] [S=Serveur]. Para comunicarse con la capa [S], el cliente [C] debe establecer una conexión de red con la capa [S] y comunicarse con ella siguiendo un protocolo específico. Las conexiones de red son conexiones TCP y el protocolo de transporte es HTTP. La capa [S], que representa el servicio web, está implementada por un servlet Java ejecutado por el servidor Glassfish. No hemos escrito este servlet. Su generación está automatizada por Glassfish a partir de las anotaciones @Webservice y @WebMethod de la clase [WsDaoJpa] que hemos escrito. Del mismo modo, vamos a automatizar la generación de la capa [C] del cliente. A veces se denomina a la capa [C], una capa proxy del servicio web remoto, ya que el término proxy designa un elemento intermediario en una cadena de software. En este caso, el proxy C es el intermediario entre el cliente que vamos a escribir y el servicio web que hemos desplegado.
Con NetBeans 6.5, el proxy C se puede generar de la siguiente manera (para continuar, es necesario que el servicio web esté activo en el servidor GlassFish):
![]() |
- en [1], añadir un nuevo elemento al proyecto Java
- en [2], seleccionar la rama [Web services]
- en [3], seleccione [Web Service Client]
![]() |
- en [4], introduzca la URI del archivo WSDL del servicio web. Esta URI se ha presentado en el apartado 4.10.2.
- en [5], dejar el valor por defecto [JAX-WS]. El otro valor posible es [JAX-RPC]
- tras validar el asistente de creación del proxy del servicio web, el proyecto NetBeans se ha enriquecido con una rama [Web Service References] [6]. Esta rama muestra los métodos expuestos por el servicio web remoto.
![]() |
- En la pestaña [Files] [7], se han añadido códigos fuente Java [8]. Estos corresponden al proxy C generado.
- En [9], el código de una de las clases. Se puede ver [10] que se han colocado en un paquete [rdvmedecins.ws]. No comentaremos el código de estas clases, que vuelve a ser bastante complejo.
Para el cliente Java que estamos construyendo, el proxy C generado actúa como intermediario. Para acceder al método M del servicio web remoto, el cliente Java llama al método M del proxy C. De este modo, el cliente Java llama a métodos locales (ejecutados en la misma JVM) y, de forma transparente para él, estas llamadas locales se traducen en llamadas remotas.
Nos queda por ver cómo llamar a los métodos M del proxy C. Volvamos a nuestra clase de prueba JUnit:
![]() |
En [1], la clase de prueba [MainTestsDaoRemote] es la que ya se utilizó durante la prueba del EJB de la capa [dao]:
- En la línea [13], la prueba test1 se mantiene tal cual.
- línea [9], se ha eliminado el contenido del método [init].
En este punto, el proyecto presenta errores porque el método de prueba [test1] utiliza las entidades [Client], [Medecin], [Creneau], [Rv], que ya no se encuentran en los mismos paquetes que antes. Ahora están en el paquete del proxy C generado. Se eliminan las instrucciones import afectadas y se regeneran mediante la operación «Fix Imports».
![]() |
Volvamos al código de la clase de prueba [MainTestsDaoRemote]:
El método [init] de la línea 10 debe inicializar la referencia de la capa [dao] de la línea 7. Necesitamos saber cómo utilizar el proxy C generado en nuestro código. NetBeans nos ayuda en este proceso.
![]() |
- Seleccionar en [1] el método [getAllClients] del servicio web con el ratón y, a continuación, arrastrar este método para colocarlo dentro del método [init] de la clase de prueba.
Se obtiene el resultado [2]. Este esqueleto de código nos muestra cómo utilizar el proxy C generado:
- La línea [5] nos muestra que el método [getAllClients] es un método del objeto de tipo [WsDaoJpa] definido en la línea 3. El tipo [WsDaoJpa] es una interfaz que presenta los mismos métodos que los expuestos por el servicio web remoto.
- En la línea [3], el objeto [WsDaoJpa port] se obtiene a partir de otro objeto de tipo [WsDaoJpaService] definido en la línea 2. El tipo [WsDaoJpaService] representa el proxy C generado localmente.
- El acceso al servicio web remoto puede fallar, por lo que todo el código está rodeado por un try / catch.
- Los objetos del proxy C se encuentran en el paquete [rdvmedecins.ws]
Una vez entendido este código, se ve que la referencia local del servicio web remoto se puede obtener mediante el código:
El código de la clase de prueba JUnit queda entonces así:
Ya estamos listos para las pruebas:
![]() |
En [1], se ejecuta la prueba JUnit. En [2], se supera. Si observamos los mensajes que aparecen en la consola de NetBeans, encontramos líneas como las siguientes:
Liste des clients :
rdvmedecins.ws.Client@1982fc1
rdvmedecins.ws.Client@676437
rdvmedecins.ws.Client@1e4853f
rdvmedecins.ws.Client@1e808ca
En el lado del servidor, la entidad [Client] tiene un método toString que muestra los diferentes campos de un objeto de tipo [Client]. Durante la generación automática del proxy C, las entidades se crean en el proxy C, pero solo con los campos privados acompañados de sus métodos get / set. Por lo tanto, el método toString no se ha generado en la entidad [Client] del proxy C. Esto explica la visualización anterior. Esto no resta valor a la prueba JUnit: se ha superado. A partir de ahora se considerará que se dispone de un servicio web operativo.






























































































