7. Versión 3: Migración de la aplicación PAM a un servidor de aplicaciones Glassfish
Nos proponemos 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 funcionar en modo cliente/servidor, en el que el cliente y el servidor se ejecutan en dos JVM diferentes, vamos a colocar las capas [metier, DAO, jpa] en el servidor Java Glassfish EE. Este servidor viene incluido con NetBeans.
La implementación que hay que crear 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, aunque 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 denomina RMI (Remote Method Invocation), un protocolo que solo puede utilizarse 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 se alojará en el contenedor EJB3 del servidor Glassfish:
![]() |
Se trata de realizar una migración al servidor Glassfish de 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 probada la aplicación, solo queda trasladarla a un servidor de destino, en este caso el servidor Glassfish.
7.1.1.1. El proyecto de NetBeans
Empecemos 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], seleccione la carpeta principal de la carpeta del proyecto o escriba su nombre directamente en [4b].
- En [5], asigna un nombre al proyecto
- en [6], selecciona el servidor de aplicaciones en el que se ejecutará. El elegido aquí es uno de los que aparecen en la pestaña [Runtime / Servers], en este caso Glassfish v3.
- En [7], selecciona la versión de Java EE.
![]() |
- En [1], el nuevo proyecto. Se diferencia de un proyecto Java clásico en varios aspectos:
- se crea automáticamente una rama [Other Sources] [2]. Contendrá, entre otros, el archivo [persistence.xml] que configura la capa JPA,
- si se compila el proyecto (Build), aparece una dependencia [javaee-api-6.0] de [3]. Es de tipo provided, ya que 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 va a utilizar
- la definición de la fuente de datos utilizada por la capa JPA. Esta será una fuente JDBC gestionada 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], crear un nuevo archivo; nos aseguraremos 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 [Glassfish]
- en [4], se desea crear un recurso JDBC
![]() |
- en [5], se indica que el recurso JDBC utilizará un nuevo grupo de conexiones. Recordemos 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], aparece un resumen de las propiedades del grupo de conexiones; no hay que modificar nada
![]() |
- en [10], se pueden especificar varias de las propiedades del grupo de conexiones; se dejan los valores por defecto
- 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, durante la implementación del 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; hay que asegurarse 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 que 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 gestionadas por el contenedor EJB
- En [9], indique que no debe realizarse ninguna operación en la fuente de datos durante la implementación del 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 ambos proyectos. Eso es lo que vamos a hacer 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
Ahora nos queda configurar el servidor Glassfish en dos aspectos:
- la capa JPA está implementada por EclipseLink. Debemos asegurarnos de que el servidor Glassfish disponga de las bibliotecas de esta implementación JPA.
- La fuente de datos es una base de datos MySQL. Debemos asegurarnos de que el servidor Glassfish disponga del controlador JDBC para esta base de datos SGBD.
La falta de estas bibliotecas se puede detectar al desplegar el módulo EJB. A continuación se muestra una de las formas de añadir las bibliotecas que faltan al servidor GlassFish:
![]() |
- en [1], visualiza las propiedades del servidor GlassFish
- en [2], anota la carpeta de dominios del servidor. A continuación, la denominaremos <domains>
- en la carpeta <domains>\domain1\lib, colocar las bibliotecas que faltan. En el ejemplo, se han añadido 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 añadirá 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. Despliegue del módulo EJB
Ahora desplegamos el módulo EJB en el servidor Glassfish:
![]() |
- en [1], se ha implementado 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 apartado 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 JNDI portátil. 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 escribir para utilizar el módulo EJB desplegado.
7.2. Consola de cliente - versión 1
Ahora que hemos desplegado 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] denominado [mv-pam-client-ejb-metier-dao-eclipselink]:
![]() |
- en [1], el proyecto del cliente
En el archivo [pom.xml], añadimos 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],
Desde el 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 JNDI, que es portátil.
- El resto del código no cambia
![]() |
En [1], se configura el proyecto para que ejecute la clase [MainRemote] con argumentos. Si todo va bien, la ejecución del proyecto da el siguiente resultado:
Si en las propiedades se introduce un número de la Seguridad Social incorrecto, se obtiene el siguiente resultado:
7.3. Consola de cliente - 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 añadir 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 el equipo del servicio JNDI y el puerto de escucha del mismo. Este archivo no permite consultar un servidor JNDI que no sea localhost o que funcione en un puerto distinto del 3700. Si se desea modificar 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.
Empezamos 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 denominado «metier» al recurso JNDI, asociado 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] pasa a ser 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-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 análoga a la arquitectura Spring / JPA analizada en el apartado 5.11.
Se invita al lector a probar esta nueva versión.
7.4. El cliente Swing
Ahora vamos a crear 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 desplegado en el servidor Glassfish.
![]() |
Ejercicio práctico: siguiendo el ejemplo del cliente de consola [ui.console.MainRemote] del proyecto, modifica la forma en que el método [doMyInit] (véase el apartado 5.12.4) de la clase [PamJFrame] para obtener una referencia a la capa [metier], que ahora es remota.

























