4. El servicio web J2EE de citas
Volvamos a la arquitectura de la aplicación que vamos a desarrollar:
![]() |
En esta sección nos enfocamos en la construcción del servicio web J2EE [1] que se ejecuta en un servidor Sun / Glassfish.
4.1. La base de datos
La base de datos, a la 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 que administra 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 fila en la tabla. Este número se incrementa en 1 cada vez que se realiza una modificación en la fila.
- NOM: el apellido del médico
- PRENOM: su nombre
- TITRE: su título (Srta., Sra., Sr.)
4.1.2. La tabla [CLIENTS]
Los clientes de los distintos médicos se registran en la tabla [CLIENTS]:
![]() | ![]() |
- ID: número de identificación del cliente —clave primaria de la tabla
- VERSION: número que identifica la versión de la fila en la tabla. Este número se incrementa en 1 cada vez que se realiza una modificación en la fila.
- NOM: el nombre del cliente
- PRENOM: su nombre
- TITRE: su tratamiento (Srta., Sra., Sr.)
4.1.3. La tabla [CRENEAUX]
Enumera los horarios en los que son posibles los RV:
![]() |
![]() |
- ID: número que identifica el horario —clave primaria de la tabla (línea 8)
- VERSION: número que identifica la versión de la fila en la tabla. Este número se incrementa en 1 cada vez que se realiza una modificación en la fila.
- ID_MEDECIN: número que identifica al médico al que pertenece este horario – clave externa en la columna MEDECINS (ID).
- HDEBUT: hora de inicio del horario
- MDEBUT: minutos de inicio del horario
- HFIN: hora de finalización del horario
- MFIN: minutos de fin de la franja
La segunda línea de la tabla [CRENEAUX] (véase [1] más arriba) indica, por ejemplo, que el horario n.º 2 comienza a las 8:20 y termina a las 8:40, y pertenece a la doctora n.º 1 (la Sra. Marie PELISSIER).
4.1.4. La tabla [RV]
Enumera los RV asignados a cada médico:
![]() |
- ID: número que identifica de manera única al RV – 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 en cuestión.
- ID_CLIENT: número del cliente para quien se realiza la reservación – clave externa en el campo [ID] de la tabla [CLIENTS]
Esta tabla tiene una restricción de unicidad en la 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 registraron dos RV al mismo tiempo para el mismo médico. Desde el punto de vista de la programación en Java, el controlador JDBC de la base de datos inicia un SQLException cuando ocurre este caso.
La línea de id igual a 3 (véase [1] más arriba) significa que se reservó un RV para el horario n.º 20 y el cliente n.º 4 el 23/08/2006. La tabla [CRENEAUX] nos indica que el horario n.º 20 corresponde al intervalo de 16:20 a 16:40 y pertenece a la doctora n.º 1 (la 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
Crea la base de datos MySql [dbrdvmedecins] con la herramienta que prefieras. Para crear las tablas y llenarlas, puedes usar el script [createbd.sql] que se te 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 se va a desarrollar:
![]() |
Del lado del servidor, la aplicación estará compuesta 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 diagrama 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 a los clientes a ser clientes Java. Un servicio web utiliza un protocolo de comunicación estandarizado que implementan diversos lenguajes: .NET, PHP, C++, etc. Esto es lo que queremos mostrar aquí utilizando un cliente .NET.
Para una breve introducción a los servicios web, se puede consultar 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 en el servidor GlassFish
Dependiendo de su versión, es posible que el servidor Glassfish V2 que viene con NetBeans no cuente con las bibliotecas de Hibernate que requiere la capa JPA/Hibernate. Si, al continuar con el tutorial, descubres que GlassFish no te ofrece una implementación de JPA/Hibernate o que, al implementar los servicios, aparece una excepción indicando que no se encuentran las bibliotecas de Hibernate, debes agregar las bibliotecas en la carpeta [<glassfish>/domains/domain1/lib/ext] y luego 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
- Ejecuta SGBD y MySQL 5 para que BD esté disponible
- crear una conexión de NetBeans a la base de datos [dbrdvmedecins]
![]() |
- en la pestaña [Files], en la rama [Databases] [1], seleccionar el controlador JDBC MySQL [2]
- y luego seleccione la opción [3] «Connect Using», que le permite crear una conexión con una base de datos MySQL
- en [4], ingresa la información que se te solicita
- y, a continuación, confirme en [5]
![]() |
- en [6], se crea la conexión. Aquí se ven 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], elige una carpeta para el proyecto y en [5], asígnale un nombre; luego, finaliza el asistente
- en [6] el proyecto generado
Agregar un recurso JDBC al servidor Glassfish
Vamos a agregar un recurso JDBC al servidor Glassfish.
![]() |
![]() |
- En la pestaña [Services], inicie el servidor Glassfish [2, 3]
- en la pestaña [Projects], haz clic con el botón derecho del ratón en el proyecto EJB y, en [5], selecciona la opción [New / Other] que permite agregar un elemento al proyecto.

- en [6], selecciona la categoría [Glassfish] y en [7] indica que quieres 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], asigna un nombre al grupo de conexiones
- en [13], elige la conexión de 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 tomado de las de la conexión de NetBeans [dbrdvmedecins] creada anteriormente
- en [16], pasa al siguiente paso
![]() |
- en [17], mantenga los valores predeterminados propuestos
- en [18], finaliza el asistente. Este crea el archivo [sun-resources.xml] [19], cuyo contenido es el siguiente:
El archivo anterior recoge toda la información ingresada en el asistente en formato XML. Será utilizado por IDE NetBeans para solicitarle 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, ...) 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], selecciona la categoría [Persistence] y luego, en [4], indica que deseas crear una unidad de persistencia JPA
![]() |
- en [5], asigne un nombre a la unidad de persistencia creada
- en [6], elija [Hibernate] como implementación de JPA
- en [7], selecciona el recurso de GlassFish «jdbc/dbrdvmedecins» que acabas de crear
- en [8], indique que no se debe realizar ninguna acción en la base de datos al instanciar la capa JPA
- finalizar el asistente
- en [9], el archivo [persistence.xml] creado por el asistente
Su contenido es el siguiente:
Una vez más, 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 indicarle a Hibernate el tipo de SGBD que se debe manejar. Esto se hará más adelante.
Creación de las entidades JPA
![]() |
![]() |
![]() |
- En [1], haz clic con el botón derecho del ratón sobre el proyecto y, en [2], selecciona la opción [New / Other]
- en [3], seleccione la categoría [Persistence] y luego, 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], asignar un nombre al paquete de las entidades JPA
- en [12], elegir el tipo de Java que encapsulará las listas de objetos devueltas por la capa JPA
- finalizar el asistente
- en [13], las cuatro entidades JPA generadas, una por 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], haz clic con el botón derecho del ratón en el proyecto y, en [2], selecciona la opción [New / Other]
- en [3], selecciona la categoría [Persistence] y luego, en [4], el tipo [Session Beans for Entity Classes]
![]() |
- en [5], las entidades JPA creadas anteriormente aparecen
- en [6], selecciónalas todas
- en [7], ya están seleccionadas
- en [8], continuar con el asistente
![]() |
- en [9], asignar un nombre al paquete de los EJB que se generarán
- en [10], indicar que los EJB deben implementar tanto una interfaz local como una remota
- finalizar el asistente
- en [11], los EJB generados
A continuación se muestra, por ejemplo, el código del EJB que administra el acceso a la entidad [Rv], es decir, a la tabla [rv] de la base de datos [dbrdvmedecins]:
Como ya se mencionó, 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 de NetBeans del módulo EJB
Creamos un nuevo módulo EJB en blanco (véase el párrafo 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 continuación, suponemos que el lector ha seguido todos los pasos del párrafo 4.5. Deberá repetir algunos de ellos.
4.6.1. Configuración de la capa JPA
Recordemos la arquitectura de nuestra aplicación cliente/servidor:
![]() |
El proyecto de 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 describió en el párrafo 4.5.
- La generación del archivo [persistence.xml] se describió en el párrafo 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 de JPA/Hibernate. Para ello, se ha agregado la biblioteca Hibernate al servidor GlassFish (véase el párrafo 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 agregarse manualmente. Le indica a Hibernate que el SGBD utilizado es MySQL5.
La fuente de datos «jdbc/dbrdvmedecins» está configurada 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 de usuario y contraseña). La base de datos MySQL dbrdvmedecins es la descrita en el párrafo 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 de 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 esa técnica, sino que definiremos las entidades nosotros mismos. Sin embargo, estas tomará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). Será la clase padre de las entidades. La anotación @MappedSuperClass indica esta situación.
La entidad [Client] encapsula las filas de la tabla [clients]. Deriva de la clase anterior [Personne]:
- 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 filas de la tabla [creneaux]:
- las filas 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 filas 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 filas 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 manejarla 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 produjo en una excepción de tipo [EjbException] y la vuelve a lanzar. Es posible que no se desee esta encapsulación y se prefiera que el contenedor Ejb3 devuelva una excepción de tipo [RdvMedecinsException]. Esto es lo que permite la anotación @ApplicationException. Además, el atributo (rollback=true) de esta anotación le indica al contenedor EJB3 que, si la excepción de tipo [RdvMedecinsException] ocurre dentro de un método ejecutado en una transacción con un SGBD, esta debe ser revertida. En términos técnicos, esto se conoce como 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 simplemente deriva de la interfaz anterior [IDao]:
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 administrada 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 administra el acceso al contexto de persistencia. Al instanciar la clase, el contenedor EJB inicializará este campo mediante 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 similar 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 específico
- 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 presentar 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 genera 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, verifica las opciones de ejecución [1].
- En [2], el nombre del servidor en el que se implementará el EJB
- en la pestaña [Services] [3], se inicia [4].
![]() |
- en [5], el servidor GlassFish una vez iniciado. Aún no cuenta con 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 párrafo 4.5.
- En la pestaña [Projects] [6], se implementa el módulo EJB [7]: es necesario que SGBD y MySQL5 estén en ejecución para que se pueda acceder al recurso JDBC «jdbc/dbrdvmedecins» que utiliza el EJB.
- En [8], el EJB desplegado aparece en el árbol de directorios del servidor GlassFish
![]() |
- En [9], se elimina el EJB desplegado
- En [10], el EJB ya no aparece en el árbol de directorios del servidor GlassFish.
4.8. Despliegue del EJB de la capa [dao] con GlassFish
Aquí mostramos cómo implementar un EJB en el servidor GlassFish 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 párrafo 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, administrada 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 por eliminar el recurso para poder volver a crearlo. Lo hacemos desde NetBeans:
![]() |
- en [1], los recursos JDBC del servidor GlassFish
- a [2], el recurso «jdbc/dbrdvmedecins» de nuestro EJB
- a [3], el grupo de conexiones de este recurso JDBC
![]() |
- en [4], se elimina 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], se han eliminado el recurso JDBC y el grupo de conexiones.
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 luego 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 y cierres de conexiones con un SGBD. Al iniciar el servidor, N —un número definido por la configuración— conexiones se abren con el SGBD. Estas conexiones abiertas se ponen luego a disposición de los EJB que las solicitan para realizar una operación con el SGBD. Tan pronto como esta operación finaliza, el EJB devuelve la conexión al grupo. La conexión nunca se cierra. Se comparte entre los distintos hilos 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], pasa al siguiente paso
![]() |
- En [11], el atributo «Connection Validation Required» hace que, antes de proporcionar una conexión, el grupo verifique que esté operativa. Si no es así, crea una nueva. Esto permite que una aplicación siga funcionando después de una interrupción momentánea con el SGBD. Durante la interrupción, ninguna conexión está disponible y se envían excepciones al cliente. Cuando finaliza la interrupción, los clientes que siguen solicitando conexiones las obtienen nuevamente: gracias al atributo «Connection Validation Required», todas las conexiones del grupo se volverán a crear. 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 grupo gestiona las conexiones
- en [16], el usuario será root
- en [17], agregue una propiedad
- en [18], agrega la propiedad «Password» con el valor () en [19]. Aunque la captura de pantalla de [19] no lo muestre, 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, ingrese esa 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. Haz 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 sale bien, aparecerá un mensaje indicando que la conexión se realizó con éxito
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], selecciona el archivo JAR del EJB [serveur-ejb-dao-jpa-hibernate.jar] que te habrán 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 usarse.
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, de:
- el archivo JAR del EJB de la capa [dao] [3] (véase el párrafo 4.6.4).
- las bibliotecas de 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 párrafo 4.6.4).
Si todo sale 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 párrafo 14, página 111 de [ref1].
Volvamos a la arquitectura del servidor de nuestra aplicación cliente/servidor:
![]() |
Nos interesa aquí 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 comunicarse 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 empresarial 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 desarrollando.
Vamos a desarrollar este proyecto empresarial de dos maneras.
4.10.1. Proyecto de NetBeans - Versión 1
En primer lugar, creamos un proyecto de NetBeans del tipo «Aplicación web»:
![]() |
- En [1], creamos un nuevo proyecto en la categoría «Java Web» [2] del tipo «Aplicación web» [3].
![]() |
- en [4], se le da un nombre al proyecto y en [5] se especifica la carpeta en la que se debe generar
- en [6], se configura 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 desarrollando utilizará el EJB del proyecto anterior [10]. Por lo tanto, necesita hacer referencia al archivo .jar del módulo EJB [10].
- En [11], agregamos un proyecto de 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 agregado 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 manera:
- línea 3: la anotación @WebService convierte a 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 de esta manera. 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 que no esté 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 simplemente llama 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 de 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 desarrollar
- [2]: el módulo EJB que creamos en un paso anterior y del cual 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 elige 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 y aplicación cliente. En este caso, el proyecto se crea sin ningún componente. Estos se agregará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 agrega un nuevo módulo
- en [10], solo se muestran los módulos de NetBeans que están abiertos actualmente 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 agregados al proyecto empresarial.
Ahora nos queda implementar esta aplicación empresarial en el servidor GlassFish. A continuación, se debe iniciar el SGBD y el MySQL para que se pueda acceder a la fuente de datos JDBC «jdbc/dbrdvmedecins» que utiliza el módulo EJB.
![]() |
- En [1], se inicia el servidor Glassfish
- si el módulo EJB [serveur-ejb-dao-jpa-hibernate] está implementado, se desinstala [2]
- en [3], se implementa la aplicación de empresa
![]() |
- en [4], ya está implementada. Se observa 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 cuenta con el código fuente del módulo EJB, sino solo con su archivo .jar.
El nuevo proyecto de NetBeans del servicio web será el siguiente:
![]() |
Los elementos destacados del proyecto son los siguientes:
- [1]: el servicio web está implementado mediante un proyecto de NetBeans del tipo [Web Application].
- [2]: el servicio web está implementado mediante la clase [WsDaoJpa], ya analizada
- [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 implementar el servicio web:
![]() |
- [1], creamos una aplicación empresarial [ea-rdvmedecins], inicialmente sin ningún módulo.
- En [2], se agrega el módulo web anterior [serveur-webservice-ejb-dao-jpa-hibernate]
- en [3], el resultado.
Tal como está, la aplicación empresarial [ea-rdvmedecins] no se puede implementar en el servidor Glassfish desde NetBeans. Se produce un error. Por lo tanto, es necesario 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 genera a partir de un simple Build y [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, seguimos los pasos que se mostraron al implementar el archivo EJB [serveur-ejb-dao-jpa-hibernate.jar] en el párrafo 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 «desinstalar» la aplicación empresarial implementada en el párrafo 4.10.1:
![]() |
- [1]: seleccione la rama [Enterprise Applications] del servidor Glassfish
- en [2], seleccione la aplicación empresarial que desea descargar; luego, 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], seleccione 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 cuenta con 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 le ha agregado 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í devolvió la lista de clientes. La captura de pantalla nos muestra que envía su respuesta en un formato XML.
Un servicio web se describe por completo 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], sigue el enlace [View WSDL]
![]() |
- a [3]: la URI del archivo WSDL. Es importante conocer esta información, ya que 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 de NetBeans para «ejecutar» las pruebas que ya se realizaron con un cliente EJB, pero esta vez con un cliente para el servicio web recientemente implementado. Seguimos aquí un procedimiento similar al descrito en el párrafo 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. Cabe recordar 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 diagrama 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 TCP y el protocolo de transporte es HTTP. La capa [S], que representa el servicio web, está implementada mediante un servlet de 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] como una capa proxy del servicio web remoto, ya que el término proxy se refiere a 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 implementado.
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], agrega un nuevo elemento al proyecto Java
- en [2], selecciona la rama [Web services]
- en [3], selecciona [Web Service Client]
![]() |
- en [4], ingrese la URI del archivo WSDL del servicio web. Esta URI se presentó en el párrafo 4.10.2.
- en [5], deje el valor predeterminado [JAX-WS]. El otro valor posible es [JAX-RPC]
- Tras validar el asistente de creación del proxy del servicio web, el proyecto de NetBeans se ha ampliado 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 ha agregado código fuente Java [8]. Este corresponde al proxy en C generado.
- En [9] se muestra el código de una de las clases. Allí se observa [10] que se han colocado en un paquete [rdvmedecins.ws]. No comentaremos el código de estas clases, ya que nuevamente es bastante complejo.
Para el cliente Java que estamos desarrollando, el proxy en 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 en C. De esta manera, el cliente Java llama a métodos locales (ejecutados en la misma JVM) y, de manera transparente para él, estas llamadas locales se traducen en llamadas remotas.
Ahora solo nos queda saber cómo llamar a los métodos M del proxy en C. Volvamos a nuestra clase de prueba JUnit:
![]() |
En [1], la clase de prueba [MainTestsDaoRemote] es la misma que ya se utilizó al probar el EJB de la capa [dao]:
- En la línea [13], la prueba test1 se mantiene tal cual.
- En la línea [9], se ha eliminado el contenido del método [init].
En este momento, 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.
![]() |
- Selecciona en [1] el método [getAllClients] del servicio web con el ratón y arrastra 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 en 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 en C generado localmente.
- El acceso al servicio web remoto podría 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 que se comprende 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í:
Ahora estamos listos para las pruebas:
![]() |
En [1], se ejecuta la prueba JUnit. En [2], la prueba se realiza con éxito. Si observamos los mensajes 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
Del lado del servidor, la entidad [Client] cuenta con un método toString que muestra los distintos campos de un objeto de tipo [Client]. Durante la generación automática del proxy en C, las entidades se crean en el proxy en C, pero solo con los campos privados acompañados de sus métodos get / set. Por lo tanto, el método toString no se generó en la entidad [Client] del proxy en C. Esto explica lo que se vio anteriormente. Esto no afecta en nada a la prueba JUnit: se ha realizado con éxito. A partir de ahora se considerará que contamos con un servicio web operativo.






























































































