7. Versão 3: Portabilidade da aplicação PAM para um servidor de aplicações Glassfish
Propõe-se colocar os EJB das camadas [metier] 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 [metier].
Testámos dois contextos de execução: local e distant. Neste último modo, a camada [ui] era cliente da camada [metier], camada implementada por EJB. Para funcionar no modo cliente/servidor, no qual o cliente e o servidor são executados em dois JVM diferentes, vamos colocar as camadas [metier, DAO, jpa] no servidor Java Glassfish EE. Este servidor é fornecido com o NetBeans.
A implementação a construir com o servidor Glassfish
![]() |
- a camada [ui] será executada num ambiente Java SE (Standard Edition)
- As camadas [metier, 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. As trocas de dados em rede são transparentes 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 estas trocas denomina-se RMI (Remote Method Invocation), um protocolo que só pode ser utilizado entre duas aplicações Java.
- A implementação JPA utilizada no servidor Glassfish será EclipseLink.
7.1. A parte « » do servidor da aplicação cliente/servidor PAM
7.1.1. A arquitetura da aplicação
Analisamos aqui a parte do servidor que será alojada pelo contentor EJB3 do servidor Glassfish:
![]() |
Trata-se de fazer uma migração para o servidor Glassfish do que já foi feito e testado com o contentor OpenEJB. É aí que reside o interesse do OpenEJB e, em geral, dos contentores EJB incorporados: permitem-nos testar a aplicação num ambiente de execução simplificado. Quando a aplicação tiver sido testada, basta portá-la para um servidor de destino, neste caso, o servidor Glassfish.
7.1.1.1. O projeto NetBeans
Comecemos por criar um novo projeto NetBeans:
![]() |
- em [1], novo projeto
- em [2], selecionar a categoria Maven e, em [3], o tipo EJB Módulo. Trata-se, de facto, de criar um projeto que será alojado e executado por um contentor EJB, o do servidor Glassfish.
![]() |
- com o botão [4a], selecione a pasta pai da 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 selecionado aqui é um dos que aparecem 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 clássico em alguns aspetos:
- é criada automaticamente uma ramificação [Other Sources] [2]. Esta irá conter, nomeadamente, o ficheiro [persistence.xml] que configura a camada JPA,
- se compilarmos o projeto (Build), surge uma dependência [javaee-api-6.0] do [3]. É do tipo provided, uma vez que é fornecida em tempo de execução pelo contentor EJB do Glassfish.
7.1.1.2. Configuração da 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.
![]() |
Pode-se proceder da seguinte forma. Em primeiro lugar, no separador [Runtime / Databases], irá criar-se uma ligação à base de dados MySQL5 / dbpam_eclipselink:
![]() |
Feito isto, podemos passar à criação do recurso JDBC utilizado pelo módulo EJB:
![]() |
- no [1], criar um novo ficheiro – certifique-se de que o projeto EJB está selecionado antes de realizar esta operação
- em [2], o projeto EJB
- em [3], selecione a categoria [Glassfish]
- em [4], pretende-se criar um recurso JDBC
![]() |
- em [5], indicar que o recurso JDBC irá utilizar um novo conjunto de ligações. Recorde-se que um conjunto de ligações é um conjunto de ligações abertas que serve para acelerar as trocas de dados entre a aplicação e a base de dados.
- em [6], atribuir o nome JNDI ao recurso JDBC criado. Este nome pode ser qualquer um, mas tem frequentemente o formato jdbc/nom. Este nome, JNDI, será utilizado no ficheiro [persistence.xml] para designar a fonte de dados que a implementação JPA deve utilizar.
- No [7], atribua um nome (que pode ser qualquer um) ao conjunto de ligações que vai ser criado
- na lista suspensa [8], selecione a ligação JDBC criada anteriormente com base em MySQL / dbpam_eclipselink.
- Em [9], é apresentado um resumo das propriedades do conjunto de ligações — não é necessário alterar nada
![]() |
- em [10], é possível especificar várias propriedades do conjunto de ligações — mantêm-se os valores predefinidos
- em [11], após a conclusão do assistente de criação de um recurso JDBC para o módulo EJB, foi criado um ficheiro [glassfish-resources.xml] no ramo [Other Sources]. O conteúdo deste ficheiro é 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>
O ficheiro [glassfish-resources.xml] é um ficheiro XML que contém todos os dados recolhidos pelo assistente. Será utilizado pelo NetBeans para, aquando da implementação do módulo EJB no servidor GlassFish, solicitar a criação do recurso JDBC de que este módulo necessita.
Agora já é possível criar o ficheiro [persistence.xml], que irá configurar a camada JPA do módulo EJB:
![]() |
- em [1], criar um novo ficheiro – certifique-se de que o projeto EJB está selecionado antes de realizar esta operação
- no [2], o projeto EJB
- em [3], seleciona-se a categoria [Persistence]
- em [4], pretende-se criar uma unidade de persistência
![]() |
- em [5], atribuir um nome à unidade de persistência
- em [6], são propostas várias implementações JPA. Aqui, escolheremos [EclipseLink]. Outras implementações podem ser utilizadas, desde que as bibliotecas que as implementam sejam colocadas 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], indicar que as transações são geridas pelo contentor EJB
- em [9], indicar que não deve ser realizada nenhuma operação na fonte de dados durante a implementação do módulo EJB no servidor. Com efeito, o módulo EJB irá utilizar uma base de dados [dbpam_eclipselink] já 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"?>
<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>
- linha 3: o nome da unidade de persistência [mv-pam-ejb-metier-dao-eclipselinkPU] e o tipo de transações (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, ...) utilizada não está indicado. Neste caso, o Glassfish v3 utiliza, por predefinição, EclipseLink.
7.1.1.3. Inserção das camadas [jpa, DAO, metier]
Agora que o ficheiro [persistence.xml] foi definido, podemos passar à inserção no projeto das camadas [metier, dao, jpa] da aplicação empresarial [pam]:
![]() |
Estas três camadas são idênticas às que existiam no OpenEJB. É possível efetuar uma simples operação de copiar/colar entre os dois projetos. É isso que vamos fazer agora:
![]() |
- no [1], o resultado da cópia dos pacotes [jpa, dao, metier, exception] do projeto [mv-pam-openejb-eclipselink] para o módulo EJB [mv-pam-ejb-metier-dao-jpa-eclipselink]
7.1.1.4. Configuração do servidor Glassfish
Resta-nos configurar o servidor Glassfish em dois aspetos:
- a camada JPA é implementada por EclipseLink. Temos de garantir que o servidor Glassfish dispõe das bibliotecas desta implementação JPA.
- a fonte de dados é uma base de dados MySQL. Temos de garantir que o servidor Glassfish dispõe do controlador JDBC para esta base de dados SGBD.
É possível detetar a ausência destas bibliotecas durante a implementação do módulo EJB. Aqui está uma das várias formas de proceder para adicionar as bibliotecas em falta ao servidor Glassfish:
![]() |
- em [1], visualize as propriedades do servidor Glassfish
- em [2], anote a pasta dos domínios do servidor. Anotamos-a a seguir como <domains>
- na pasta <domains>\domain1\lib, coloque as bibliotecas em falta. No exemplo, foram adicionadas as bibliotecas do Hibernate (lib / hibernate-tools) e o controlador JDBC de MySQL (lib / divers). Por predefinição, o servidor Glassfish já possui as bibliotecas de EclipseLink. Por isso, apenas será adicionado o controlador JDBC de MySQL.
![]() |
- em [1], no separador [Services], iniciamos o servidor Glassfish v3
- em [2], que está ativo
7.1.1.5. Implantação do módulo EJB
Implantamos agora o módulo EJB no servidor Glassfish:
![]() |
- em [1], o módulo EJB é implementado
- em [2], a árvore de diretórios 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 tínhamos definido no parágrafo 7.1.1.2.
Durante a implementação, o servidor Glassfish regista na consola algumas informações interessantes:
Repare-se nas linhas
- 3, 6, 8 e 11 os nomes portáteis JNDI dos EJB implementados. O Java EE 6 introduziu o conceito de nome portátil JNDI. Isto significa que 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 utilizado.
- 4, 7, 9, 12: os nomes JNDI dos EJB implementados de uma forma específica para o 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. Consola do cliente - versão 1
Agora que implementámos a parte do servidor da nossa aplicação cliente/servidor, passamos a analisar a parte do cliente [1]:
![]() |
7.2.1. O projeto do cliente
Criamos um novo projeto Maven do tipo [Java Application] denominado [mv-pam-client-ejb-metier-dao-eclipselink]:
![]() |
- no [1], o projeto do cliente
No ficheiro [pom.xml], adicionamos as seguintes dependências:
<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>
- linhas 31-35: a dependência da biblioteca [gf-client], que permite que um cliente Classfish comunique com um servidor remoto,
- linhas 36-41: a dependência do projeto Maven do módulo EJB. Pretendemos aqui recuperar as definições das entidades JPA e das várias interfaces, bem como a da classe de exceção [PamException],
A partir do projeto [mv-pam-openejb-eclipselink], copiamos a classe [MainRemote]:
![]() |
A classe [MainRemote] deve obter uma referência à classe EJB da camada [metier]. O código da classe [MainRemote] é alterado da seguinte forma:
// Está tudo bem — já podemos pedir a folha de vencimento
FeuilleSalaire feuilleSalaire = null;
IMetierRemote metier = null;
try {
// contexto JNDI do servidor Glassfish
InitialContext initialContext = new InitialContext();
// instanciação da camada de negócio
metier = (IMetierRemote) initialContext.lookup("java:global/istia.st_mv-pam-ejb-metier-dao-eclipselink_ejb_1.0-SNAPSHOT/Metier!metier.IMetierRemote");
// cálculo da folha de pagamento
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 6: inicialização do contexto JNDI do servidor Glassfish.
- linha 8: solicita-se a este contexto JNDI uma referência à interface remota da camada [metier]. De acordo com os registos do Glassfish, sabe-se que a interface remota da camada [metier] tem dois nomes possíveis:
Linha 1: o nome JNDI, utilizável 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, que é portátil.
- O resto do código não sofre alterações
![]() |
Em [1], configuramos o projeto para que execute a classe [MainRemote] com argumentos. Se tudo correr bem, a execução do projeto produz o seguinte resultado:
Se, nas propriedades, for introduzido um número de segurança social incorreto, 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 a partir de um ficheiro [jndi.properties] que se encontrava algures nos arquivos do projeto. O seu conteúdo por predefinição é o seguinte:
# acesso JNDI ao Sun Application Server
java.naming.factory.initial=com.sun.enterprise.naming.SerialInitContextFactory
java.naming.factory.url.pkgs=com.sun.enterprise.naming
# É necessário adicionar um javax.naming.spi.StateFactory para o CosNaming que
# suporta o 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
As linhas 7 e 8 indicam o servidor do serviço JNDI e a porta de escuta do mesmo. Este ficheiro não permite consultar um servidor JNDI que não seja o localhost ou que funcione numa porta diferente da porta 3700. Se pretender alterar estes dois parâmetros, pode criar o seu próprio ficheiro [jndi.properties] ou utilizar uma configuração Spring. Apresentamos esta segunda técnica.
Começamos por criar um novo projeto a partir do 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:
<?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">
<!-- função -->
<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>
Utilizamos aqui uma baliza <jee> (linha 14) introduzida com o Spring 2.0. A utilização desta baliza requer a definição do esquema ao qual pertence, nas linhas 4, 10 e 11.
- linha 14: a baliza <jee:jndi-lookup> permite obter a referência de um objeto junto 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] passa a ser o conteúdo da baliza <jee:environment> (linha 15), que serve para definir os parâmetros de ligação ao serviço JNDI.
A classe principal [MainRemote] evolui da seguinte forma:
Nas linhas 7-8, é solicitada ao Spring a referência de tipo [IMetierRemote] na camada [metier]. Esta solução confere flexibilidade à nossa arquitetura. Com efeito, se o EJB da camada [metier] se tornasse local, c.a.d. executado na mesma JVM que o nosso cliente [MainRemote], o código deste último não se alteraria. Apenas o conteúdo do ficheiro [spring-config-client.xml] mudaria. Encontraríamos então uma configuração análoga à arquitetura Spring / JPA analisada no parágrafo 5.11.
Convidamos o leitor a testar esta nova versão.
7.4. O cliente Swing
Vamos agora construir o cliente swing da nossa aplicação cliente/servidor EJB.
![]() |
O ficheiro [pom.xml] deve ter a dependência necessária para as aplicações Swing:
<dependency>
<groupId>org.swinglabs</groupId>
<artifactId>swing-layout</artifactId>
<version>1.0.3</version>
</dependency>
Acima, a classe [PamJFrame] tinha sido inicialmente 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] do projeto, altere a forma como o método [doMyInit] (ver parágrafo 5.12.4) da classe [PamJFrame] para obter uma referência à camada [metier], que agora se encontra remota.

























