7. Versión 3: Migración de la aplicación PAM a un servidor de aplicaciones Glassfish
Se propone colocar los EJB de las capas [metier] y [DAO] de la arquitectura OpenEJB / EclipseLink en el contenedor de un servidor de aplicaciones Glassfish.
La implementación actual con OpenEJB / EclipseLink
![]() |
En el ejemplo anterior, la capa [ui] utiliza la interfaz remota de la capa [metier].
Hemos probado dos contextos de ejecución: local y distant. En este último modo, la capa [ui] era cliente de la capa [metier], capa implementada por EJB. Para operar en modo cliente/servidor, en el que el cliente y el servidor se ejecutan en dos JVM diferentes, colocaremos las capas [metier, DAO, jpa] en el servidor Java Glassfish EE. Este servidor viene incluido con NetBeans.
La implementación que se va a construir con el servidor Glassfish
![]() |
- la capa [ui] se ejecutará en un entorno Java SE (Standard Edition)
- Las capas [metier, DAO, JPA] se ejecutarán en un entorno Java EE (Enterprise Edition) en un servidor GlassFish v3
- El cliente se comunicará con el servidor a través de una red TCP/IP. Los intercambios de red son transparentes para el desarrollador, salvo que debe tener en cuenta que el cliente y el servidor intercambian objetos serializados para comunicarse, y no referencias a objetos. El protocolo de red utilizado para estos intercambios se llama RMI (Remote Method Invocation), un protocolo que solo se puede utilizar entre dos aplicaciones Java.
- La implementación JPA utilizada en el servidor GlassFish será EclipseLink.
7.1. La parte del servidor de la aplicación cliente/servidor PAM
7.1.1. La arquitectura de la aplicación
Aquí analizamos la parte del servidor que será alojada por el contenedor EJB3 del servidor Glassfish:
![]() |
Se trata de portar al servidor Glassfish lo que ya se ha hecho y probado con el contenedor OpenEJB. Ahí radica el interés de OpenEJB y, en general, de los contenedores EJB integrados: nos permiten probar la aplicación en un entorno de ejecución simplificado. Una vez que la aplicación ha sido probada, solo queda portarla a un servidor de destino, en este caso el servidor Glassfish.
7.1.1.1. El proyecto de NetBeans
Comencemos por crear un nuevo proyecto de NetBeans:
![]() |
- en [1], nuevo proyecto
- en [2], selecciona la categoría Maven y, en [3], el tipo EJB Módulo. De hecho, se trata de crear un proyecto que será alojado y ejecutado por un contenedor EJB, el del servidor Glassfish.
![]() |
- Con el botón [4a], elija la carpeta principal de la carpeta del proyecto o escriba su nombre directamente en [4b].
- En [5], asigne un nombre al proyecto
- en [6], elige el servidor de aplicaciones en el que se ejecutará. El que se elige aquí es uno de los que aparecen en la pestaña [Runtime / Servers], en este caso Glassfish v3.
- En [7], elige la versión de Java EE.
![]() |
- En [1], el nuevo proyecto. Se diferencia de un proyecto de Java clásico en algunos aspectos:
- se crea automáticamente una rama [Other Sources] [2]. Esta contendrá, entre otros, el archivo [persistence.xml] que configura la capa JPA;
- si se compila el proyecto (Build), aparece una dependencia de [javaee-api-6.0] llamada [3]. Es de tipo provided porque el contenedor EJB de GlassFish la proporciona en tiempo de ejecución.
7.1.1.2. Configuración de la capa de persistencia
Por configuración de la capa de persistencia entendemos la creación del archivo [persistence.xml], que define:
- la implementación JPA que se utilizará
- la definición de la fuente de datos utilizada por la capa JPA. Esta será una fuente JDBC administrada por el servidor Glassfish.
![]() |
Se puede proceder de la siguiente manera. En primer lugar, en la pestaña [Runtime / Databases], se creará una conexión a la base de datos MySQL5 / dbpam_eclipselink:
![]() |
Una vez hecho esto, podemos pasar a la creación del recurso JDBC utilizado por el módulo EJB:
![]() |
- En [1], crea un nuevo archivo; asegúrate de que el proyecto EJB esté seleccionado antes de realizar esta operación
- en [2], el proyecto EJB
- en [3], selecciona la categoría [Glassfish]
- en [4], se desea crear un recurso JDBC
![]() |
- en [5], se indica que el recurso JDBC utilizará un nuevo grupo de conexiones. Cabe recordar que un grupo de conexiones es un conjunto de conexiones abiertas que sirve para acelerar las comunicaciones de la aplicación con la base de datos.
- En [6], asigne el nombre JNDI al recurso JDBC creado. Este nombre puede ser cualquiera, pero suele tener el formato jdbc/nom. Este nombre, JNDI, se utilizará en el archivo [persistence.xml] para designar la fuente de datos que debe utilizar la implementación JPA.
- En [7], asigne un nombre cualquiera al grupo de conexiones que se va a crear
- En la lista desplegable [8], seleccione la conexión JDBC creada anteriormente en la base de datos MySQL / dbpam_eclipselink.
- En [9], se muestra un resumen de las propiedades del grupo de conexiones; no se modifica nada
![]() |
- en [10], se pueden especificar varias de las propiedades del grupo de conexiones; se dejan los valores predeterminados
- en [11], al finalizar el asistente de creación de un recurso JDBC para el módulo EJB, se ha creado un archivo [glassfish-resources.xml] en la rama [Other Sources]. El contenido de este archivo es el siguiente:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE resources PUBLIC "-//GlassFish.org//DTD GlassFish Application Server 3.1 Resource Definitions//EN" "http://glassfish.org/dtds/glassfish-resources_1_5.dtd">
<resources>
<jdbc-resource enabled="true" jndi-name="jdbc/dbpam_eclipselink" object-type="user" pool-name="dbpamEclipselinkConnectionPool">
<description/>
</jdbc-resource>
<jdbc-connection-pool allow-non-component-callers="false" associate-with-thread="false" connection-creation-retry-attempts="0" connection-creation-retry-interval-in-seconds="10" connection-leak-reclaim="false" connection-leak-timeout-in-seconds="0" connection-validation-method="auto-commit" datasource-classname="com.mysql.jdbc.jdbc2.optional.MysqlDataSource" fail-all-connections="false" idle-timeout-in-seconds="300" is-connection-validation-required="false" is-isolation-level-guaranteed="true" lazy-connection-association="false" lazy-connection-enlistment="false" match-connections="false" max-connection-usage-count="0" max-pool-size="32" max-wait-time-in-millis="60000" name="dbpamEclipselinkConnectionPool" non-transactional-connections="false" pool-resize-quantity="2" res-type="javax.sql.DataSource" statement-timeout-in-seconds="-1" steady-pool-size="8" validate-atmost-once-period-in-seconds="0" wrap-jdbc-objects="false">
<property name="URL" value="jdbc:mysql://localhost:3306/dbpam_eclipselink"/>
<property name="User" value="root"/>
<property name="Password" value=""/>
</jdbc-connection-pool>
</resources>
El archivo [glassfish-resources.xml] es un archivo XML que recoge todos los datos recopilados por el asistente. NetBeans lo utilizará para, al implementar el módulo EJB en el servidor GlassFish, solicitar la creación del recurso JDBC que necesita dicho módulo.
Ahora podemos crear el archivo [persistence.xml], que configurará la capa JPA del módulo EJB:
![]() |
- En [1], crear un nuevo archivo; asegúrate de que el proyecto EJB esté seleccionado antes de realizar esta operación
- en [2], el proyecto EJB
- en [3], se selecciona la categoría [Persistence]
- en [4], queremos crear una unidad de persistencia
![]() |
- en [5], asignar un nombre a la unidad de persistencia
- en [6], se proponen varias implementaciones JPA. Aquí elegiremos [EclipseLink]. Se pueden utilizar otras implementaciones siempre y cuando se incluyan las bibliotecas que las implementan junto con las del servidor Glassfish.
- En la lista desplegable [7], seleccione la fuente de datos JDBC [jdbc/dbpam_eclipselink] que acaba de crearse.
- En [8], indique que las transacciones son administradas por el contenedor EJB
- En [9], indique que no se debe realizar ninguna operación en la fuente de datos al implementar el módulo EJB en el servidor. De hecho, el módulo EJB utilizará una base de datos [dbpam_eclipselink] ya creada.
- Al finalizar el asistente, se ha creado un archivo [persistence.xml] a partir de [10]. Su contenido es el siguiente:
<?xml version="1.0" encoding="UTF-8"?>
<persistence version="2.0" xmlns="http://java.sun.com/xml/ns/persistence" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/persistence http://java.sun.com/xml/ns/persistence/persistence_2_0.xsd">
<persistence-unit name="mv-pam-ejb-metier-dao-eclipselinkPU" transaction-type="JTA">
<jta-data-source>jdbc/dbpam_eclipselink</jta-data-source>
<exclude-unlisted-classes>false</exclude-unlisted-classes>
<properties/>
</persistence-unit>
</persistence>
- línea 3: el nombre de la unidad de persistencia [mv-pam-ejb-metier-dao-eclipselinkPU] y el tipo de transacciones (JTA para un contenedor EJB)
- línea 5: el nombre JNDI de la fuente de datos utilizada por la capa de persistencia: jdbc/dbpam_eclipselink
- línea 6: las entidades JPA no se especifican. Se buscarán en el Classpath del módulo EJB.
- No se indica el nombre de la implementación JPA (Hibernate, EclipseLink, ...) utilizada. En este caso, Glassfish v3 utiliza por defecto EclipseLink.
7.1.1.3. Inserción de las capas [jpa, DAO, metier]
Ahora que se ha definido el archivo [persistence.xml], podemos pasar a insertar en el proyecto las capas [metier, dao, jpa] de la aplicación empresarial [pam]:
![]() |
Estas tres capas son idénticas a las que había con OpenEJB. Se puede realizar un simple copiar y pegar entre los dos proyectos. Eso es lo que haremos ahora:
![]() |
- en [1], el resultado de copiar los paquetes [jpa, dao, metier, exception] del proyecto [mv-pam-openejb-eclipselink] al módulo EJB [mv-pam-ejb-metier-dao-jpa-eclipselink]
7.1.1.4. Configuración del servidor Glassfish
Nos queda configurar el servidor Glassfish en dos aspectos:
- la capa JPA está implementada por EclipseLink. Debemos asegurarnos de que el servidor Glassfish cuente con las bibliotecas de esta implementación JPA.
- La fuente de datos es una base de datos MySQL. Debemos asegurarnos de que el servidor Glassfish cuente con el controlador JDBC para esta base de datos SGBD.
Es posible detectar la falta de estas bibliotecas al implementar el módulo EJB. A continuación se presenta una de las formas de proceder para agregar las bibliotecas que faltan al servidor GlassFish:
![]() |
- en [1], revise las propiedades del servidor GlassFish
- en [2], anota la carpeta de dominios del servidor. La denominaremos a partir de ahora <domains>
- en la carpeta <domains>\domain1\lib, coloca las bibliotecas que faltan. En el ejemplo, se agregaron las bibliotecas de Hibernate (lib / hibernate-tools) y el controlador JDBC de MySQL (lib / diversos). Por defecto, el servidor GlassFish ya cuenta con las bibliotecas de EclipseLink. Por lo tanto, solo se agregará el controlador JDBC de MySQL.
![]() |
- en [1], en la pestaña [Services], iniciamos el servidor Glassfish v3
- en [2], ya está activo
7.1.1.5. Implementación del módulo EJB
Ahora implementamos el módulo EJB en el servidor Glassfish:
![]() |
- en [1], se implementa el módulo EJB
- en [2], se actualiza el árbol de directorios del servidor Glassfish
- en [3], tras el despliegue, el módulo EJB aparece en la rama [Applications] del servidor Glassfish
- en [4], se ha creado el recurso JDBC [jdbc / dbpam_eclipselink] en el servidor GlassFish. Recordemos que lo habíamos definido en el párrafo 7.1.1.2.
Durante el despliegue, el servidor Glassfish registra en la consola información interesante:
Obsérvese en las líneas
- 3, 6, 8 y 11 los nombres portables JNDI de los EJB desplegados. Java EE 6 introdujo el concepto de nombre portátil JNDI. Esto indica un nombre JNDI reconocido por todos los servidores Java EE 6. Con Java EE 5, los nombres JNDI son específicos del servidor utilizado.
- 4, 7, 9, 12: los nombres JNDI de los EJB desplegados en un formato específico para Glassfish v3.
Estos nombres serán útiles para la aplicación de consola que vamos a desarrollar para utilizar el módulo EJB implementado.
7.2. Consola de cliente - versión 1
Ahora que hemos implementado la parte de servidor de nuestra aplicación cliente/servidor, pasamos a estudiar la parte de cliente [1]:
![]() |
7.2.1. El proyecto del cliente
Creamos un nuevo proyecto Maven de tipo [Java Application] llamado [mv-pam-client-ejb-metier-dao-eclipselink]:
![]() |
- en [1], el proyecto del cliente
En el archivo [pom.xml], agregamos las siguientes dependencias:
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>istia.st</groupId>
<artifactId>mv-pam-client-ejb-metier-dao-eclipselink</artifactId>
<version>1.0-SNAPSHOT</version>
<packaging>jar</packaging>
<name>mv-pam-client-ejb-metier-dao-eclipselink</name>
<url>http://maven.apache.org</url>
<repositories>
<repository>
<url>http://download.eclipse.org/rt/eclipselink/maven.repo/</url>
<id>eclipselink</id>
<layout>default</layout>
<name>Repository for library Library[eclipselink]</name>
</repository>
<repository>
<url>http://repo1.maven.org/maven2/</url>
<id>swing-layout</id>
<layout>default</layout>
<name>Repository for library Library[swing-layout]</name>
</repository>
</repositories>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
<dependencies>
<dependency>
<groupId>org.glassfish.appclient</groupId>
<artifactId>gf-client</artifactId>
<version>3.1.1</version>
</dependency>
<dependency>
<groupId>${project.groupId}</groupId>
<artifactId>mv-pam-ejb-metier-dao-eclipselink</artifactId>
<version>${project.version}</version>
<type>ejb</type>
</dependency>
<dependency>
<groupId>org.swinglabs</groupId>
<artifactId>swing-layout</artifactId>
<version>1.0.3</version>
</dependency>
</dependencies>
</project>
- líneas 31-35: la dependencia de la biblioteca [gf-client], que permite a un cliente Classfish comunicarse con un servidor remoto,
- líneas 36-41: la dependencia del proyecto Maven del módulo EJB. Aquí queremos recuperar las definiciones de las entidades JPA y las de las diferentes interfaces, así como la de la clase de excepción [PamException],
Del proyecto [mv-pam-openejb-eclipselink], copiamos la clase [MainRemote]:
![]() |
La clase [MainRemote] debe obtener una referencia a la clase EJB de la capa [metier]. El código de la clase [MainRemote] cambia de la siguiente manera:
// Ya está, podemos solicitar la nómina
FeuilleSalaire feuilleSalaire = null;
IMetierRemote metier = null;
try {
// contexto JNDI del servidor Glassfish
InitialContext initialContext = new InitialContext();
// instanciación de la capa de negocio
metier = (IMetierRemote) initialContext.lookup("java:global/istia.st_mv-pam-ejb-metier-dao-eclipselink_ejb_1.0-SNAPSHOT/Metier!metier.IMetierRemote");
// cálculo de la nómina
feuilleSalaire = metier.calculerFeuilleSalaire(args[0], nbHeuresTravaillées, nbJoursTravaillés);
} catch (PamException ex) {
System.err.println("L'erreur suivante s'est produite : "
+ ex.getMessage());
return;
} catch (Exception ex) {
System.err.println("L'erreur suivante s'est produite : "
+ ex.toString());
return;
}
- línea 6: inicialización del contexto JNDI del servidor Glassfish.
- línea 8: se solicita a este contexto JNDI una referencia a la interfaz remota de la capa [metier]. Según los registros de Glassfish, sabemos que la interfaz remota de la capa [metier] tiene dos nombres posibles:
Línea 1: el nombre JNDI, que se puede utilizar con cualquier servidor de aplicaciones: JAVA, EE, 6. Línea 2: el nombre JNDI, específico de GlassFish. En el código, en la línea 9, utilizamos el nombre portátil JNDI.
- El resto del código no cambia
![]() |
En [1], se configura el proyecto para que ejecute la clase [MainRemote] con argumentos. Si todo sale bien, la ejecución del proyecto arroja el siguiente resultado:
Si en las propiedades se ingresa un número de seguro social incorrecto, se obtiene el siguiente resultado:
7.3. Cliente de consola - versión 2
En las versiones anteriores, el entorno JNDI del servidor Glassfish se configuraba a partir de un archivo [jndi.properties] que se encontraba en algún lugar de los archivos del proyecto. Su contenido por defecto es el siguiente:
# Acceso a Sun Application Server mediante JNDI
java.naming.factory.initial=com.sun.enterprise.naming.SerialInitContextFactory
java.naming.factory.url.pkgs=com.sun.enterprise.naming
# Es necesario agregar un javax.naming.spi.StateFactory para CosNaming que
# admite RMI-IIOP dinámico.
java.naming.factory.state=com.sun.corba.ee.impl.presentation.rmi.JNDIStateFactoryImpl
org.omg.CORBA.ORBInitialHost=localhost
org.omg.CORBA.ORBInitialPort=3700
Las líneas 7 y 8 indican la máquina del servicio JNDI y el puerto en el que este escucha. Este archivo no permite consultar un servidor JNDI que no sea localhost o que opere en un puerto distinto al 3700. Si se desea cambiar estos dos parámetros, se puede crear un archivo propio o utilizar una configuración de Spring. A continuación, mostramos esta segunda técnica.
Comenzamos creando un nuevo proyecto a partir del proyecto inicial [pam-client-metier-dao-jpa-eclipselink].
![]() |
- en [1], el nuevo proyecto
- en [2], el archivo de configuración de Spring [spring-config-client.xml]. Su contenido es el siguiente:
El archivo de configuración de Spring es el siguiente:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:tx="http://www.springframework.org/schema/tx"
xmlns:jee="http://www.springframework.org/schema/jee"
xsi:schemaLocation="
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-2.0.xsd
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx-2.0.xsd
http://www.springframework.org/schema/jee
http://www.springframework.org/schema/jee/spring-jee-2.0.xsd">
<!-- profesión -->
<jee:jndi-lookup id="metier" jndi-name="java:global/istia.st_mv-pam-ejb-metier-dao-eclipselink_ejb_1.0-SNAPSHOT/Metier!metier.IMetierRemote">
<jee:environment>
java.naming.factory.initial=com.sun.enterprise.naming.SerialInitContextFactory
java.naming.factory.url.pkgs=com.sun.enterprise.naming
java.naming.factory.state=com.sun.corba.ee.impl.presentation.rmi.JNDIStateFactoryImpl
org.omg.CORBA.ORBInitialHost=localhost
org.omg.CORBA.ORBInitialPort=3700
</jee:environment>
</jee:jndi-lookup>
</beans>
Aquí utilizamos una etiqueta <jee> (línea 14) que apareció con Spring 2.0. El uso de esta etiqueta requiere la definición del esquema al que pertenece, líneas 4, 10 y 11.
- Línea 14: la etiqueta <jee:jndi-lookup> permite obtener la referencia de un objeto de un servicio JNDI. Aquí, se asocia el bean llamado «metier» al recurso JNDI, que a su vez está vinculado a EJB y [Metier]. El nombre JNDI utilizado aquí es el nombre portátil (Java EE 6) del EJB.
- El contenido del archivo [jndi.properties] se convierte en el contenido de la etiqueta <jee:environment> (línea 15), que sirve para definir los parámetros de conexión al servicio JNDI.
La clase principal [MainRemote] evoluciona de la siguiente manera:
En las líneas 7 y 8, se solicita a Spring la referencia de tipo [IMetierRemote] en la capa [metier]. Esta solución aporta flexibilidad a nuestra arquitectura. De hecho, si el EJB de la capa [metier] pasara a ser local, c.a.d. se ejecutara en la misma JVM que nuestro cliente [MainRemote], el código de este último no cambiaría. Solo cambiaría el contenido del archivo [spring-config-client.xml]. De este modo, se obtendría una configuración similar a la arquitectura Spring / JPA analizada en el párrafo 5.11.
Se invita al lector a probar esta nueva versión.
7.4. El cliente Swing
Ahora construiremos el cliente swing de nuestra aplicación cliente/servidor EJB.
![]() |
El archivo [pom.xml] debe tener la dependencia necesaria de las aplicaciones Swing:
<dependency>
<groupId>org.swinglabs</groupId>
<artifactId>swing-layout</artifactId>
<version>1.0.3</version>
</dependency>
Anteriormente, la clase [PamJFrame] se había escrito inicialmente para ejecutarse en un entorno Spring / JPA:
![]() |
Ahora esta clase debe convertirse en el cliente remoto de un EJB implementado en el servidor Glassfish.
![]() |
Ejercicio práctico: siguiendo el ejemplo del cliente de consola [ui.console.MainRemote] del proyecto, modifique la forma en que el método [doMyInit] (véase el párrafo 5.12.4) de la clase [PamJFrame] para obtener una referencia a la capa [metier], que ahora es remota.

























