7. Versão 3: Portar a aplicação PAM para um servidor de aplicações GlassFish
Propomos colocar os EJBs das camadas [business] e [DAO] da arquitetura OpenEJB/EclipseLink no contentor de um servidor de aplicações GlassFish.
A implementação atual com OpenEJB / EclipseLink
![]() |
Acima, a camada [ui] utiliza a interface remota da camada [business].
Testámos dois contextos de execução: local e remoto. No último modo, a camada [ui] atuou como cliente da camada [business], que é implementada por EJBs. Para operar no modo cliente/servidor, em que o cliente e o servidor são executados em duas JVMs separadas, iremos implementar as camadas [business, DAO, JPA] no servidor Java EE GlassFish. Este servidor está incluído no NetBeans.
A implementação a ser construída com o servidor GlassFish
![]() |
- a camada [ui] será executada num ambiente Java SE (Standard Edition)
- As camadas [lógica de negócio, DAO, JPA] serão executadas num ambiente Java EE (Enterprise Edition) num servidor GlassFish v3
- O cliente comunicará com o servidor através de uma rede TCP/IP. A comunicação de rede é transparente para o programador, embora este deva estar ciente de que o cliente e o servidor trocam objetos serializados para comunicar, e não referências a objetos. O protocolo de rede utilizado para esta comunicação denomina-se RMI (Remote Method Invocation), um protocolo utilizável apenas entre duas aplicações Java.
- A implementação JPA utilizada no servidor GlassFish será o EclipseLink.
7.1. A parte server- e da aplicação cliente/servidor PAM
7.1.1. A arquitetura da aplicação
Aqui, estamos a examinar o componente do lado do servidor que será hospedado pelo contentor EJB3 do servidor GlassFish:
![]() |
O objetivo é portar para o servidor GlassFish o que já foi desenvolvido e testado com o contentor OpenEJB. Esta é a vantagem do OpenEJB e, de forma mais geral, dos contentores EJB incorporados: permitem-nos testar a aplicação num ambiente de execução simplificado. Depois de a aplicação ter sido testada, basta portá-la para um servidor de destino, neste caso o servidor GlassFish.
7.1.1.1. O Projeto NetBeans
Vamos começar por criar um novo projeto NetBeans:
![]() |
- em [1], novo projeto
- Em [2], selecione a categoria «Maven» e, em [3], selecione o tipo de módulo «EJB». O objetivo é compilar um projeto que será hospedado e executado por um contentor EJB, nomeadamente o servidor GlassFish.
![]() |
- Utilizando o botão [4a], selecione a pasta pai para a pasta do projeto ou digite o seu nome diretamente em [4b].
- Em [5], atribua um nome ao projeto
- Em [6], selecione o servidor de aplicações no qual será executado. O servidor selecionado aqui é um dos que estão visíveis no separador [Runtime / Servers], neste caso o GlassFish v3.
- Em [7], selecione a versão do Java EE.
![]() |
- Em [1], o novo projeto. Este difere de um projeto Java padrão em alguns aspetos:
- é criada automaticamente uma ramificação [Outras Fontes] [2]. Esta irá conter, em particular, o ficheiro [persistence.xml] que configura a camada JPA,
- Se compilar o projeto (Build), surge uma dependência [javaee-api-6.0] [3]. É do tipo «provided» porque é fornecida em tempo de execução pelo contentor EJB Glassfish.
7.1.1.2. Configurar a camada de persistência
Por «configuração da camada de persistência», entendemos a criação do ficheiro [persistence.xml], que define:
- a implementação JPA a utilizar
- a definição da fonte de dados utilizada pela camada JPA. Esta será uma fonte JDBC gerida pelo servidor GlassFish.
![]() |
Podemos proceder da seguinte forma. Primeiro, no separador [Runtime / Databases], iremos criar [1] uma ligação à base de dados MySQL5 / dbpam_eclipselink:
![]() |
Depois de fazer isso, podemos passar à criação do recurso JDBC utilizado pelo módulo EJB:
![]() |
- Em [1], crie um novo ficheiro — certifique-se de que o projeto EJB está selecionado antes de realizar esta operação
- Em [2], selecione o projeto EJB
- Em [3], selecione a categoria [Glassfish]
- em [4], selecione «Criar um recurso JDBC»
![]() |
- em [5], especifique que o recurso JDBC utilizará um novo conjunto de ligações. Note que um conjunto de ligações é um conjunto de ligações abertas utilizado para acelerar as interações da aplicação com a base de dados.
- em [6], atribua um nome JNDI ao recurso JDBC criado. Este nome pode ser qualquer coisa, mas geralmente assume a forma jdbc/nome. Este nome JNDI será utilizado no ficheiro [persistence.xml] para designar a fonte de dados que a implementação JPA deve utilizar.
- Em [7], atribua qualquer nome ao conjunto de ligações que será criado
- Na lista suspensa [8], selecione a ligação JDBC criada anteriormente para a base de dados MySQL / dbpam_eclipselink.
- Em [9], um resumo das propriedades do conjunto de ligações — deixe tudo como está
![]() |
- Em [10], pode especificar várias propriedades do conjunto de ligações — mantenha os valores predefinidos
- Em [11], após concluir o assistente para criar um recurso JDBC para o módulo EJB, foi criado um ficheiro [glassfish-resources.xml] na ramificação [Outras Fontes]. O conteúdo deste ficheiro é o seguinte:
O ficheiro [glassfish-resources.xml] é um ficheiro XML que contém todos os dados recolhidos pelo assistente. Será utilizado pelo NetBeans para solicitar a criação do recurso JDBC necessário a este módulo ao implementar o módulo EJB no servidor GlassFish.
Pode agora criar o ficheiro [persistence.xml], que irá configurar a camada JPA do módulo EJB:
![]() |
- Em [1], crie um novo ficheiro — certifique-se de que o projeto EJB está selecionado antes de realizar esta operação
- em [2], selecione o projeto EJB
- em [3], selecione a categoria [Persistência]
- em [4], crie uma unidade de persistência
![]() |
- em [5], atribua um nome à unidade de persistência
- Em [6], são apresentadas várias implementações JPA. Aqui, selecione [EclipseLink]. Podem ser utilizadas outras implementações, desde que inclua as bibliotecas que as implementam juntamente com as do servidor GlassFish.
- Na lista suspensa [7], selecione a fonte de dados JDBC [jdbc/dbpam_eclipselink] que acabou de ser criada.
- Em [8], especifique que as transações são geridas pelo contentor EJB
- Em [9], especifique que nenhuma operação deve ser realizada na fonte de dados ao implementar o módulo EJB no servidor. Isto porque o módulo EJB utilizará uma base de dados [dbpam_eclipselink] que já foi criada.
- No final do assistente, foi criado um ficheiro [persistence.xml] [10]. O seu conteúdo é o seguinte:
<?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>
- linha 3: o nome da unidade de persistência [mv-pam-ejb-metier-dao-eclipselinkPU] e o tipo de transação (JTA para um contentor EJB)
- linha 5: o nome JNDI da fonte de dados utilizada pela camada de persistência: jdbc/dbpam_eclipselink
- linha 6: as entidades JPA não estão especificadas. Serão procuradas no classpath do módulo EJB.
- O nome da implementação JPA (Hibernate, EclipseLink, etc.) utilizada não está especificado. Neste caso, o GlassFish v3 utiliza o EclipseLink por predefinição.
7.1.1.3. Inserção das camadas [JPA, DAO, Business]
Agora que o ficheiro [persistence.xml] foi definido, podemos prosseguir com a inserção das camadas [business, DAO, JPA] da aplicação empresarial [pam] no projeto:
![]() |
Estas três camadas são idênticas às que existiam no OpenEJB. Podemos simplesmente copiar e colar entre os dois projetos. É isso que estamos a fazer agora:
![]() |
- em [1], o resultado da cópia dos pacotes [jpa, dao, business, exception] do projeto [mv-pam-openejb-eclipselink] para o módulo EJB [mv-pam-ejb-business-dao-jpa-eclipselink]
7.1.1.4. Configurar o servidor GlassFish
Ainda precisamos de configurar o servidor GlassFish em duas áreas:
- A camada JPA é implementada pelo EclipseLink. Temos de garantir que o servidor GlassFish possui as bibliotecas necessárias para esta implementação JPA.
- A fonte de dados é uma base de dados MySQL. Temos de garantir que o servidor GlassFish possui o controlador JDBC para este sistema de gestão de bases de dados.
Poderá verificar que estas bibliotecas estão em falta ao implementar o módulo EJB. Aqui está uma das várias formas de adicionar as bibliotecas em falta ao servidor GlassFish:
![]() |
- Em [1], visualize as propriedades do servidor GlassFish
- Em [2], anote a pasta de domínios do servidor. Vamos referir-nos a ela como <domains>
- Na pasta <domains>\domain1\lib, coloque as bibliotecas em falta. No exemplo, foram adicionadas as bibliotecas Hibernate (lib/hibernate-tools) e o controlador JDBC do MySQL (lib/misc). Por predefinição, o servidor GlassFish inclui as bibliotecas EclipseLink. Por isso, iremos adicionar apenas o controlador JDBC do MySQL.
![]() |
- Em [1], no separador [Serviços], iniciamos o servidor GlassFish v3
- Em [2], ele está ativo
7.1.1.5. Implementação do módulo EJB
Vamos agora implementar o módulo EJB no servidor GlassFish:
![]() |
- Em [1], o módulo EJB é implementado
- Em [2], a árvore do servidor GlassFish é atualizada
- em [3], após a implementação, o módulo EJB aparece no ramo [Applications] do servidor GlassFish
- em [4], o recurso JDBC [jdbc / dbpam_eclipselink] foi criado no servidor GlassFish. Recorde-se que o definimos na Secção 7.1.1.2.
Durante a implementação, o servidor GlassFish regista as seguintes informações na consola:
<?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>
Note que as linhas
- 3, 6, 8 e 11: os nomes JNDI portáteis dos EJBs implementados. O Java EE 6 introduziu o conceito de nomes JNDI portáteis. Isto denota um nome JNDI reconhecido por todos os servidores Java EE 6. Com o Java EE 5, os nomes JNDI são específicos do servidor que está a ser utilizado.
- 4, 7, 9, 12: os nomes JNDI dos EJBs implementados num formato específico do GlassFish v3.
Estes nomes serão úteis para a aplicação de consola que vamos escrever para utilizar o módulo EJB implementado.
7.2. Cliente de Consola - Versão 1
Agora que implementámos o lado do servidor da nossa aplicação cliente/servidor, vamos examinar o lado do cliente [1]:
![]() |
7.2.1. O projeto do cliente
Criamos um novo projeto Maven do tipo [Aplicação Java] com o nome [mv-pam-client-ejb-metier-dao-eclipselink]:
![]() |
- em [1], o projeto cliente
No ficheiro [pom.xml], adicionamos as seguintes dependências:
- linhas 31–35: a dependência da biblioteca [gf-client], que permite que um cliente Glassfish comunique com um servidor remoto,
- linhas 36–41: a dependência do projeto Maven do módulo EJB. Aqui, pretendemos recuperar as definições das entidades JPA e das várias interfaces, bem como a da classe de exceção [PamException],
Do projeto [mv-pam-openejb-eclipselink], copiamos a classe [MainRemote]:
![]() |
A classe [MainRemote] deve obter uma referência ao EJB na camada [business]. O código da classe [MainRemote] é alterado da seguinte forma:
<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>
- Linha 6: Inicialização do contexto JNDI do servidor GlassFish.
- linha 8: este contexto JNDI é consultado para obter uma referência à interface remota da camada [business]. De acordo com os registos do GlassFish, sabemos que a interface remota da camada [business] tem dois nomes possíveis:
// it's OK - we can ask for the payslip
FeuilleSalaire feuilleSalaire = null;
IMetierRemote metier = null;
try {
// context JNDI of Glassfish server
InitialContext initialContext = new InitialContext();
// business layer instantiation
metier = (IMetierRemote) initialContext.lookup("java:global/istia.st_mv-pam-ejb-metier-dao-eclipselink_ejb_1.0-SNAPSHOT/Metier!metier.IMetierRemote");
// wage sheet calculation
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;
}
Linha 1: o nome JNDI que pode ser utilizado com qualquer servidor de aplicações Java EE 6. Linha 2: o nome JNDI específico do GlassFish. No código, na linha 9, utilizamos o nome JNDI portátil.
- O resto do código permanece inalterado
![]() |
Em [1], configuramos o projeto para executar a classe [MainRemote] com argumentos. Se tudo correr bem, a execução do projeto produz o seguinte resultado:
Se for introduzido um número de segurança social incorreto nas propriedades, obtém-se o seguinte resultado:
7.3. Consola do cliente - versão 2
Nas versões anteriores, o ambiente JNDI do servidor Glassfish era configurado utilizando um ficheiro [jndi.properties] localizado algures nos arquivos do projeto. O seu conteúdo padrão é o seguinte:
As linhas 7 e 8 especificam a máquina do serviço JNDI e a sua porta de escuta. Este ficheiro não permite consultar um servidor JNDI que não seja o localhost ou que esteja a ser executado numa porta diferente da porta 3700. Se pretender alterar estas duas definições, pode criar o seu próprio ficheiro [jndi.properties] ou utilizar uma configuração Spring. Iremos demonstrar esta última técnica.
Começamos por criar um novo projeto com base no projeto inicial [pam-client-metier-dao-jpa-eclipselink].
![]() |
- Em [1], o novo projeto
- em [2], o ficheiro de configuração do Spring [spring-config-client.xml]. O seu conteúdo é o seguinte:
O ficheiro de configuração do Spring é o seguinte:
# accès JNDI à Sun Application Server
java.naming.factory.initial=com.sun.enterprise.naming.SerialInitContextFactory
java.naming.factory.url.pkgs=com.sun.enterprise.naming
# Required to add a javax.naming.spi.StateFactory for CosNaming that
# supports dynamic RMI-IIOP.
java.naming.factory.state=com.sun.corba.ee.impl.presentation.rmi.JNDIStateFactoryImpl
org.omg.CORBA.ORBInitialHost=localhost
org.omg.CORBA.ORBInitialPort=3700
Aqui estamos a utilizar uma tag <jee> (linha 14) introduzida no Spring 2.0. A utilização desta tag requer a definição do esquema ao qual pertence, linhas 4, 10 e 11.
- Linha 14: A tag <jee:jndi-lookup> é utilizada para obter uma referência de objeto a partir de um serviço JNDI. Aqui, associamos o bean denominado «metier» ao recurso JNDI associado ao EJB [Metier]. O nome JNDI utilizado aqui é o nome portátil (Java EE 6) do EJB.
- O conteúdo do ficheiro [jndi.properties] torna-se o conteúdo da tag <jee:environment> (linha 15), que é utilizada para definir os parâmetros de ligação para o serviço JNDI.
A classe principal [MainRemote] é alterada da seguinte forma:
<?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">
<!-- business -->
<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>
Linhas 7–8: Solicita-se ao Spring a referência do tipo [IMetierRemote] na camada [business]. Esta solução confere flexibilidade à nossa arquitetura. De facto, se o EJB na camada [business] se tornasse local — ou seja, executado na mesma JVM que o nosso cliente [MainRemote] —, o código do cliente permaneceria inalterado. Apenas o conteúdo do ficheiro [spring-config-client.xml] mudaria. Teríamos então uma configuração semelhante à arquitetura Spring/JPA discutida na Secção 5.11.
Convidamos os leitores a testar esta nova versão.
7.4. O Cliente Swing
Vamos agora criar o cliente Swing para a nossa aplicação cliente/servidor EJB.
![]() |
O ficheiro [pom.xml] deve incluir as dependências necessárias para aplicações Swing:
Acima, a classe [PamJFrame] foi originalmente escrita para ser executada num ambiente Spring/JPA:
![]() |
Agora, esta classe deve tornar-se o cliente remoto de um EJB implementado no servidor Glassfish.
![]() |
Exercício prático: Seguindo o exemplo do cliente de consola [ui.console.MainRemote] no projeto, modifique a forma como o método [doMyInit] (ver Secção 5.12.4) na classe [PamJFrame] é utilizado para obter uma referência à camada [business], que agora é remota.

























