Você está na página 1de 76

1

Apostila JavaEE 5 Componentes Distribuídos


EJB 3 e JBoss

Autor:
Gilberto Augusto T. de A. Holms

gibaholms85@gmail.com

http://gibaholms.wordpress.com/

Última revisão: 03/08/2009


2

Sumário

1. Ferramentas Utilizadas ......................................................................................... 4


1.1. Java JDK ....................................................................................................... 4
1.2. Eclipse IDE.................................................................................................... 4
1.3. JBoss Application Server ............................................................................... 5
1.4. MySQL .......................................................................................................... 7
2. Computação Distribuída ........................................................................................ 7
2.1. Por que utilizar sistemas distribuídos ? ......................................................... 7
2.2. O que são sistemas distribuídos ? ................................................................. 8
2.3. Rede IP e Sockets ......................................................................................... 8
2.4. CORBA ....................................................................................................... 10
2.4.1. Arquitetura CORBA ............................................................................. 10
2.5. RMI ............................................................................................................. 14
2.6. Modelo de Programação Distribuída ........................................................... 15
2.7. Visão Geral ................................................................................................. 16
3. Serviço de Nomes JNDI ...................................................................................... 16
3.1. O que é JNDI ?............................................................................................ 16
3.2. Serviços de Nomes ..................................................................................... 16
3.3. Serviços de Diretório ................................................................................... 17
3.4. Principais conceitos de JNDI ....................................................................... 18
4. Introdução à Plataforma JEE .............................................................................. 20
4.1. O que é J2EE ou JEE ?............................................................................... 20
4.2. Modelo de Programação JEE ...................................................................... 20
4.3. O Servidor de Aplicação .............................................................................. 21
4.4. O Container EJB ......................................................................................... 22
4.5. Definição de Enterprise JavaBeans ............................................................. 23
5. Introdução ao JBoss Application Server .............................................................. 23
5.1. Instalação .................................................................................................... 24
5.2. Estrutura de Diretórios JBoss 5.x ................................................................ 25
5.3. Server Configurations .................................................................................. 25
5.3.1. Estrutura de Diretórios do Server Configuration ................................... 26
5.4. Configurando um Servidor JBoss no Eclipse ............................................... 26
6. Enterprise JavaBeans 3.0 ................................................................................... 28
6.1. Session Beans ............................................................................................ 29
6.1.1. Identificando os Beans ........................................................................ 29
6.1.2. Interfaces de Negócio .......................................................................... 29
6.1.3. Session Beans – Stateless .................................................................. 30
6.1.4. Session Beans – Statefull .................................................................... 31
6.1.5. Ciclo de Vida dos Session beans......................................................... 32
6.2. Distribuindo EJBs ........................................................................................ 36
6.3. EJB Timer Service ....................................................................................... 37
7. Java Persistence API (JPA) ................................................................................ 39
7.1. Entity Manager ............................................................................................ 40
7.2. Ciclo de Vida ............................................................................................... 41
7.3. Contexto de Persistência ............................................................................. 43
7.4. Configurando uma Aplicação JPA ............................................................... 45
7.5. Mapeando Relacionamentos ....................................................................... 46
7.5.1. Um-Para-Um Unidirecional .................................................................. 46
7.5.2. Um-Para-Um Bidirecional .................................................................... 46
7.5.3. Um-para-Muitos Unidirecional.............................................................. 46
7.5.4. Muitos-Para-Um Unidirecional ............................................................. 47
7.5.5. Um-Para-Muitos / Muitos-Para-Um Bidirecional ................................... 47
7.5.6. Muitos-Para-Muitos Unidirecional ........................................................ 47
7.5.7. Muitos-Para-Muitos Bidirecional .......................................................... 47
3

7.6. Cascade Type ............................................................................................. 47


7.7. Lazy Loading ............................................................................................... 48
7.8. CRUD com JPA........................................................................................... 49
7.9. Data Sources no JBoss ............................................................................... 49
8. Principais Design Patterns JEE ........................................................................... 51
8.1. Singleton ..................................................................................................... 51
8.2. Service Locator ........................................................................................... 52
8.3. Business Delegate ...................................................................................... 53
8.4. Session Facade........................................................................................... 54
8.5. Data Transfer Object (DTO) ........................................................................ 56
9. JMS e Message Driven Beans ............................................................................ 56
9.1. O que é Mensageria ? ................................................................................. 56
9.2. MOM – Message Oriented Middleware........................................................ 57
9.3. JMS ............................................................................................................. 58
9.4. Modelos de Mensagens .............................................................................. 59
9.4.1. Topic (Tópico) ...................................................................................... 59
9.4.2. Queue (Fila)......................................................................................... 59
9.5. Implementando Topics e Queues no JBoss ................................................ 59
9.6. Entendendo a JMS API ............................................................................... 61
9.7. Publicando e Consumindo Mensagens JMS ............................................... 62
9.7.1. Mensagens Tópico – Topic .................................................................. 62
9.7.2. Mensagens Fila – Queue ..................................................................... 63
9.8. JMS e EJB .................................................................................................. 64
9.8.1. EJB – Publicando Mensagens ............................................................. 64
9.8.2. EJB – Message Driven Beans ............................................................. 65
10. Controle Transacional na Plataforma JEE ........................................................... 66
10.1. O que são Transações ? ............................................................................. 67
10.2. Níveis de Isolamento de Transações ........................................................... 68
10.3. Transações e EJB 3.0 ................................................................................. 70
10.3.1. Transações CMT ................................................................................. 70
10.3.2. Transações e Message Driven Beans ................................................. 73
10.3.3. Revertendo Transações....................................................................... 74
10.3.4. Transações BMT ................................................................................. 74
11. Referências Bibliográficas ................................................................................... 75
4

1. Ferramentas Utilizadas
1.1. Java JDK
Para desenvolver aplicações Java Enterprise Edition, não precisamos de nenhum
ferramental especial. É necessário apenas algum JDK (Java Development Kit) e um
Servidor de Aplicação (Application Server) compatível com o JDK.

No momento em que foi criada esta apostila, a versão mais recente é a JDK 6 Update
13, disponível no link:
http://java.sun.com/javase/downloads/index.jsp

Note que a Sun também disponibiliza para download um pacote chamado JDK 6
Update 13 with Java EE. Este pacote é apenas um facilitador para iniciantes, pois já
vem com um servidor de aplicação, o GlassFish Enterprise Server, e também alguns
exemplos de mini-aplicativos JEE e tutoriais. O GlassFish é um Application Server
completo, disponibilizado pela Sun como open-source.

O GlassFish é um ótimo servidor de aplicação, porém não tem muita força no


mercado. Atualmente, dentro do nicho dos servidores de aplicação open-source, o
JBoss destaca-se como o mais robusto e utilizado pela comunidade, portanto, é ele
que utilizaremos neste curso.

1.2. Eclipse IDE


Como ferramenta de desenvolvimento, utilizaremos o Eclipse IDE. Trata-se de um
ambiente de desenvolvimento integrado open-source, o mais conhecido e utilizado
pela comunidade Java.
5

No momento em que foi criada esta apostila, a versão mais recente é o Eclipse
Ganymede SR1 – versão 3.4, disponível no link:
http://www.eclipse.org/home/categories/index.php?category=enterprise

O Eclipse IDE é disponibilizado para download em várias distribuições diferentes. Para


este curso utilizaremos a distribuição Eclipse IDE for Java EE Developers, pois ela já
incorpora diversos plugins direcionados ao desenvolvimento JEE, inclusive o WTP
(Web Tools Platform).

1.3. JBoss Application Server


Como servidor de aplicação e web container, utilizaremos o JBoss Application Server.
Ele é um servidor de código aberto, atualmente mantido pela empresa Hed Hat e
usado em produção em muitas empresas.

No momento em que foi criada esta apostila, a versão mais recente é o JBoss
Application Server 5.0.1.GA, disponível no link:
http://www.jboss.org/jbossas/downloads/
6

Note que dentro de uma mesma versão são disponibilidadas para download diversas
distribuições do JBoss, em diversos formatos, ou com JDK6 incluso. Para o curso nós
utilizaremos a versão “pura”, disponível no formato zip, sem nenhum adicional.
7

1.4. MySQL
Para os exercícios e projetos do curso, utilizaremos a base de dados MySQL (agora
pertencente à Sun Microsystems). Atualmente, ela é disponibilizada em duas versões:
Enterprise e Community.

No momento em que foi criada esta apostila, a versão mais recente é o MySQL
Community Server 5.1, disponível no link:

Optamos pela versão Community, pois ela é open-source.

2. Computação Distribuída
2.1. Por que utilizar sistemas distribuídos ?
o Dados são distribuídos
o Processamento é distribuído
o Usuários são distribuídos

Dados são distribuídos


Os dados que uma aplicação precisa acessar podem existir em muitos computadores,
por motivos administrativos, de segurança, de infra-estrutura ou até mesmo
geográficos. O responsável pelos dados ou processos pode permitir acesso apenas
remoto, mas não permitir que esses dados sejam armazenados localmente por um
cliente.
8

Processamento é distribuído
Uma aplicação pode executar de forma distribuída para ter vantagem de utilizar
múltiplos processadores, em diferentes máquinas ou servidores, ou até mesmo tirar
proveito de recursos específicos de uma determinada máquina ou sistema
operacional. Com isso, são garantidas escalabilidade e alta disponibilidade do sistema.

Usuários são distribuídos


Uma aplicação pode executar de forma distribuída, pois os usuários da aplicação
podem interagir entre si, compartilhando processos, cada usuário sendo responsável
por executar um pedaço do aplicativo, em diferentes sistemas, para a realização de
um fim comum.

2.2. O que são sistemas distribuídos ?


Segundo a definição de Tanembaum, um sistema distribuído é:
“Uma coleção de computadores independentes que se apresenta ao usuário
como um sistema único e consistente.”

Ou seja, um sistema distribuído é capaz de ter seu processamento compartilhado em


diversas máquinas distintas, interligadas entre uma rede, que se comunicam entre si
através de algum protocolo, independente do sistema operacional em que residem.
Tudo isso transparente ao usuário, que tem a impressão de estar acessando um único
sistema.

Cada pedaço da aplicação pode estar presente em uma máquina, e estes pedaços
podem ser reutilizados e orquestrados de forma a compor uma aplicação.

2.3. Rede IP e Sockets


Historicamente, a primeira abordagem existente para o desenvolvimento de aplicações
distribuídas foi através do uso de sockets, que é a forma mais “nativa” possível de
transferir dados através de uma rede de computadores.

Arquitetura geral de uma rede de computadores baseada em IP:

Vamos examinar rapidamente os componentes dessa arquitetura:


9

o Camada Física
É a camada que efetivamente transfere os bits, onde podemos pensar
em placas de rede, roteadores e elementos transmissores.
o Enlace de Dados
É a camada que tem diversas características de gerência e controle
sobre os dados trafegados, que realiza multiplexações, detecção,
notificação e recuperação de erros.
o Protocolo IP
A sigla IP significa “Internet Protocol”, por ser o protocolo no qual é
baseada a rede mundial de computadores (Internet). Nada mais é que
um protocolo, único e padronizado mundialmente, que permite que
dados sejam transferidos pela rede através de pacotes de dados com
uma estrutura bem definida, com informações de roteamento,
endereçamento, headers e outras informações.
o TCP / UDP
Aqui começamos a nos atentar. Existem duas maneiras (protocolos) de
se transferir dados em uma rede IP, e cada uma tem suas
características e utilização apropriada:
 Protocolo UDP
• Não orientado à conexão
• Não confiável
• Não garante entrega ao destinatário
• Utilizado para comunicação em tempo real (VoIP, Vídeo, etc)
 Protocolo TCP
• Orientado à conexão
• Confiável
• Garante entrega ao destinatário
o Camada de Aplicação
É onde residem os aplicativos. Nesta camada temos os protocolos de
software. Diversos aplicativos conhecidos utilizam serviços de rede.
Entre eles, podemos citar: browsers web (protocolo HTTP),
gerenciadores de email (protocolos SMTP e POP3).

Dando um zoom na nossa Camada de Aplicação, visualizamos um componente de


software vital para a comunicação em rede:

Os SOCKETS !

Sockets são um conjunto de funções (API) do sistema operacional que permitem que
os softwares utilizem um canal de comunicação sobre IP. E, logicamente, cada
linguagem de programação fornece o seu conjunto de APIs para manipulação dos
sockets do sistema operacional.
10

2.4. CORBA
Como pudemos observar nos exercícios apresentados em sala de aula, disponibilizar
objetos distribuídos nativamente através de sockets não é uma tarefa simples. Em
muitas linhas de código, onde não há transparência de localização, o cliente precisa
conhecer o servidor. Além de que implementar infra de segurança, alta disponibilidade
e pool de recursos demandaria muito esforço de desenvolvimento. Vimos também que
é muito difícil obter interoperabilidade, pois não há um padrão definido de protocolo de
comunicação, cada desenvolvedor de sistema poderia implementar de sua maneira.
Também não há transparência de linguagem de programação, ou seja, um objeto
distribuído Java seria acessado apenas por clientes Java, pois são utilizados os
recursos de serialização de objetos da própria linguagem para trafegá-los pela rede.

CORBA, ou “Common Object Request Broker Architecture”, é uma especificação de


arquitetura para criação de sistemas distribuídos orientados a objeto padronizados,
independente de plataforma ou linguagem de programação.

O Object Management Group (OMG) é responsável pela definição da arquitetura


CORBA. A OMG é um órgão formado por mais de 700 empresas fornecedoras de
tecnologia, responsável por definir e manter alguns dos principais padrões de
mercado, como por exemplo o UML.

A arquitetura CORBA define o paradigma de “Objetos Distribuídos”, que permite que


um objeto (C++, Delphi, Java, etc) em uma máquina possa interagir com um objeto de
outra máquina.

2.4.1. Arquitetura CORBA


IDL
É a definição da interface ou contrato das operações que o objeto distribuído expõe
aos clientes. Ela é criada em uma linguagem específica definida e mantida pela OMG.

Exemplo de IDL:
module LocadoraObjects {

struct InformacoesFilme {
string nome;
string genero;
double preco;
};
11

exception LocadoraException{};

interface Filme {
InformacoesFilme getInformacoesFilme() raises(LocadoraException);
void setInformacoesFilme(in InformacoesFilme info);
};

};

A partir da IDL, diversos fabricantes desenvolvem compiladores para gerar o código


em uma linguagem específica, como Java ou C++.

A plataforma Java fornece no JDK um compilador IDL para a geração de Skeletons e


Stubs CORBA. É o aplicativo idlj.exe, localizado na pasta “bin” do JDK.

Exemplo:
idlj –server –client teste.idl

ORB
Camada provedora de serviços (API) para implementação da arquitetura CORBA. Ela
é a intermediadora entre o objeto cliente e o objeto distribuído. Quando um objeto
cliente faz uma requisição a um objeto distribuído, ela localiza o objeto na rede, envia
a requisição ao objeto distribuído (através do protocolo IIOP), aguarda a resposta e a
repassa ao objeto chamador.

Um dos serviços importantes providos pela camada ORB é o serviço de nomes


(Naming Service), que permite transparência de localização dos objetos distribuídos.

Skeletons
São os objetos gerados automaticamente pelo compilador IDL para servirem
requisições no lado do servidor. Eles são responsáveis pelo marshalling e
unmarshalling dos objetos para que o ORB possa enviá-los pela rede. Desta forma, o
desenvolvedor preocupa-se apenas em implementar suas operações de negócio, sem
se preocupar com serviços de infra estrutura.

Stubs
São os objetos gerados automaticamente pelo compilador IDL para enviarem
requisições por parte do cliente. Eles são responsáveis pelo marshalling e
unmarshalling dos objetos para que o ORB possa enviá-los pela rede. Da mesma
forma, o desenvolvedor não precisa se preocupar com serviços de infra estrutura para
acessar os objetos distribuídos.

IIOP
12

O “Internet Inter Orb Protocol” é o protocolo de comunicação da arquitetura CORBA. É


um protocolo de rede (TCP/IP) que, através de uma estrutura de bytes pré-definida,
transporta as requisições e respostas através da rede.

Cliente

Código Cliente

Stubs
Compilador
idl2Java
ORB

Rede TCP/IP - Protocolo IIOP

Arquivo IDL

Servidor ORB
Skeletons

Implementação do Objeto

POA
O “Portable Object Adapter” (POA) é um artefato evolutivo na arquitetura CORBA. Ele
fica entre a implementação do ORB e o objeto distribuído. Desta forma, com o POA
pode-se fazer com que os ORBs de diversos fabricantes rodem objetos distribuídos da
mesma forma, sem nenhuma mudança no código dos objetos. Além disso, o POA
fornece alguns serviços de controle de ciclo-de-vida, segurança, multithreading e
transiência/persistência.

COS Naming Service


O CORBA COS(Common Object Services) Naming Service é uma especificação. Sua
implementação deve fornecer um sistema de armazenamento de nomes em formado
de “árvore”, pronto para armazenar referências de objetos CORBA. Sua estrutura é
muito semelhante a um LDAP:

• Binding
13

É uma associação nome-objeto, onde uma referência a um objeto é gravada


associada a um nome, que funciona como uma chave em uma estrutura de Map.
Um binding é criado sempre em relação a um Naming Context; não existe um nome de
binding absoluto.

• Naming Context
Um conjunto único de bindings (nomes), onde não pode haver bindings com nomes
repetidos. O Naming Context é um objeto, portanto, também pode ser “bindado” ao
Naming Service, criando assim uma hierarquia de naming contexts (Naming Graph).

• Naming Graph
É quando criamos Naming Context dentro de outro Naming Context, criando assim
uma árvore de contextos.

No diretório “bin” do JDK são fornecidas duas implementações de COS Naming


Service: ORBD e TNAMESERV.

• ORDB (Object Request Broker Daemon)


É uma implementação de COS Naming Service disponibilizada de duas maneiras:
“Transient Naming Service” e “Persistent Naming Service”.

o Persistent Naming Service – indica que os registros são gravados de


forma persistente (em disco ou base de dados), permitindo que os
bindings sobrevivam a restarts e quedas do sistema.

Orb.resolve_initial_references("NameService")

o Transient Naming Service – indica que os registros são gravado em


memória, ou seja, são perdidos quando o servidor finaliza.

//o nome TNameService é proprietario do ORBD,


//o padrão do CORBA é sem o T
Orb.resolve_initial_references("TNameService")

Ferramenta SERVERTOOL – também disponível no diretório “bin” do JDK. É


um aplicativo utilitário para monitorar o ORBD, permitindo visualizar os servidores
ativos, localizar ORBs, entre outras funções.

• TNAMESERV (Transient Naming Service)


Como o próprio nome indica, é uma implementação de COS Naming Service, porém
disponível apenas na forma transiente.

Orb.resolve_initial_references("NameService")

O COS Naming Service é disponibilizado através de um objeto distribuído no ORB,


que também foi gerado a partir de um IDL, portanto é preciso obter sua referência e
fazer o narrow a partir da classe helper.

Object ctxRef = orb.resolve_initial_references("NameService");


NamingContextExt ctx = NamingContextExtHelper.narrow(ctxRef);

Nomeando Referências à Objetos:

• Interoperable Object References (IOR)


É uma referência a um objeto em forma de string, em um formato conhecido pelo
ORB.
14

Orb.object_to_string(objRef)

• Interoperable Naming Service (INS)


É uma referência a um objeto em forma “Human-Readable”, em forma de strings,
URLs, “corbaloc” ou “corbaname”.

corbaloc:iiop:1.2@localhost:1050/Locadora

2.5. RMI
O RMI (“Remote Method Invocation”) é uma API criada como uma iniciativa para que
desenvolvedores pudessem escrever sistemas distribuídos com a mesma semântica
de sistemas comuns, com o intuito de agilizar e facilitar o desenvolvimento.

A princípio, ela foi criada para suprir as necessidades do desenvolvimento de sistemas


distribuídos baseados em Java, ou seja, server Java e client Java, através do protoclo
JRMP. Porém, logo depois foi evoluída para suportar também o protocolo IIOP,
visando a interoperabilidade de sistemas.

Existem dois tipos de implementação RMI:


- Baseada em JRMP (“Java Remote Method Protocol”)
- Baseada em IIOP (“Internet Inter Orb Protocol”)

JRMP
É um protocolo nativo e específico para linguagem Java, ou seja, objetos distribuídos
via RMI-JRMP podem ser consumidos apenas por clientes Java (JVM to JVM).

• RMIREGISTRY (RMI Registry)


Disponível na pasta “bin” do JDK, é o Naming Service do RMI, que funciona
analogamente ao COS Naming Service do CORBA, porém específico para bindar e
localizar objetos RMI.

• RMIC (RMI Compiler)


É um aplicativo localizado na pasta “bin” do JDK utilizado para gerar os Skeletons e
Stubs de objetos remotos RMI. A geração explícita de Skeletons e Stubs é necessária
apenas nas versões 1.4 e anteriores do JDK, pois nas versões mais recentes esta
geração é feita e disponibilizada à JVM automaticamente pelo RMIRegistry.

Exemplo:
rmic br.curso.LocadoraImpl

IIOP
Como já estudamos no tópico sobre CORBA, o IIOP é o protocolo de comunicação da
arquitetura CORBA. Objetos distribuídos sobre RMI-IIOP possuem a vantagem de
executar sobre um ORB, assim sendo independentes de linguagem de programação.
Com o intuito de trazer para o Java a facilidade de programação do RMI fundida à
interoperabilidade do CORBA, a Sun, em conjunto com a IBM, criaram o RMI sobre
IIOP.

Além de prover interoperabilidade com sistemas CORBA, o protocolo RMI-IIOP foi


idealizado para garantir também interoperabilidade com o RMI-JRMP, para que os
sistemas mais antigos construídos nesse protocolo pudessem interagir com o novo
protocolo sem necessidade de mudança de código.
15

Para atender esses requisitos, compilador RMIC foi modificado, adicionando a opção
de geração de Skeletons e Stubs para protocolo IIOP.

Exemplo:
rmic –iiop br.curso.LocadoraImpl

Conceito de “codebase”
Para qualquer chamada remota, vimos que é necessário ter no classpath do cliente o
Stub, tal Stub que referencia a classe de implementação do objeto distribuído. Porém,
nem sempre é desejável ter este Stub localmente, pois a qualquer mudança na
implementação do objeto remoto, teríamos que recompilar todos os clientes com o
novo Stub que seria gerado.
Para evitar esse problema, ao rodar o nosso servidor java podemos setar um atributo
de JVM chamado “codebase”, indicando um local para a JVM do cliente localizar os
Stubs automaticamente em tempo de execução. Quando bindamos um objeto remoto
no RMI Registry, este local é gravado e enviado aos aplicativos que fizerem lookup do
objeto.

Exemplos:
java -Djava.rmi.server.codebase=http://server/public/stubs.jar
java -Djava.rmi.server.codebase=http://server/stubs/

A ordem em que o cliente procura as classes Stub é primeiro em seu classpath e, se


não achar, procura no codebase do Servidor.

Obs.: este conceito é válido apenas para RMI-JRMP. Não faz sentido um cliente IIOP
fazer download automático de Stubs, pois um cliente IIOP pode ser escrito em
qualquer linguagem de programação, e cada linguagem terá o seu Stub específico.

2.6. Modelo de Programação Distribuída


Quando trabalhamos com Java puro localmente sabemos que, ao invocarmos um
método local, tipos primitivos são passados como cópia e, para os objetos, é passada
uma cópia da referência.
Quando trabalhamos com objetos distribuídos, não há como trafegar referências de
objetos na rede, mesmo porquê o sistema do outro lado pode não necessariamente
ser Java.
16

Na chamada de métodos remotos sempre trabalhamos com cópias serializadas


(sequência de bytes) dos objetos, tanto os passados como argumento quanto os
retornados de métodos. Portanto, todos os objetos que irão trafegar na rede
(parâmetros e retornos de métodos) devem ser declarados como serializáveis.

Na linguagem Java um objeto é declarado como serializável quando ele implementa a


interface java.io.Serializable:

public class Filme implements Serializable {

2.7. Visão Geral


CLIENT \ SERVER CORBA RMI – JRMP RMI – IIOP
CORBA X X
RMI – JRMP X X
RMI – IIOP X X X

3. Serviço de Nomes JNDI


3.1. O que é JNDI ?
A sigla JNDI significa Java Naming and Directory Interface. O JNDI é uma API da
plataforma Java SE que permite ao desenvolvedor manipular serviços de nomes e
diretórios.

3.2. Serviços de Nomes


A principal função de um serviço de nomes (ou Naming Service) é permitir a
associação de um nome a recursos computacionais, como:
o Endereços de memória
o Objetos
o Referências a objetos
o Arquivos
o Códigos em geral

Exemplos de serviços de nomes:


o File System – liga um caminho a um bloco de memória física
o Sistema DNS – liga um domínio a um endereço IP
o Sistema LDAP – liga um nome a um usuário ou grupo de usuários
o CORBA COS Naming Service – liga um nome lógico a uma referência
de objeto remoto CORBA
17

o RMI Registry – liga um nome lógico a uma referência de objeto remoto


RMI-JRMP

3.3. Serviços de Diretório


A principal função de um serviço de diretório (ou Directory Service) é permitir o
agrupamento de recursos computacionais de acordo com contextos ou hierarquias. O
serviço de diretório complementa o serviço de nomes, pois este agrupamento pode
ser feito logicamente a partir dos nomes. O serviço de diretório permite também
trabalhar com atributos, para guardar informações importantes sobre os objetos.

Exemplos de serviços de diretórios:


o File System – uma pasta é um agrupamento de arquivos, e arquivos
têm atributos
o Sistema DNS – um domínio é um agrupamento de sub-domínios ou
sites
o Sistema LDAP – um grupo é um agrupamento de usuários, e ambos
podem ter atributos (nome, endereço, senha)
o CORBA COS Naming Service e RMI Registry – um contexto de nomes
é um agrupamento de objetos remotos relacionados

De maneira macro percebemos que todos os exemplos de serviços de nomes e


diretório listados possuem muito em comum.
• Geralmente um serviço de nomes caminha acompanhado de um serviço de
diretórios (mas não é obrigatório).
• Como dito na própria definição do JNDI, todos eles associam nomes a recursos
computacionais.
• Eles podem guardar atributos para descrever estes recursos (mas não é
obrigatório).
• Todos eles também se organizam em forma de árvore, hierarquizada, ou seja,
um nome pode ser um contexto para agrupar outros nomes.
18

Devido a este comportamento comum, a Sun teve a brilhante idéia de desenvolver


uma API genérica, desacoplada, capaz de gerenciar qualquer serviço de nomes que
seguisse esta mesma lógica. Lógico que cada serviço de nomes tem seu protocolo
específico e sua particularidade de implementação, porém a JNDI fornece uma
interface comum para essas implementações.

O JNDI está para os “serviços de nomes e diretórios” assim como o JDBC está
para as “bases de dados”.

Com o JNDI, no que tange aos serviços de nomes e diretórios, temos os mesmos
benefícios que o JDBC fornece no que tange as bases de dados, onde a principal
delas é a independência de implementação.

3.4. Principais conceitos de JNDI


Service Provider
É a implementação (provedor do serviço) que o JNDI irá utilizar. Cada serviço de
nomes e diretórios específico possui a sua classe de implementação, conhecida como
“Service Provider”, e sua “factory”, responsável por criar a instância e atribuí-la à
interface do JNDI. Ela deve estar no classpath da aplicação e sua factory deve ser
definida no properties através da seguinte constante:

Context.INITIAL_CONTEXT_FACTORY

Initial Context
É o contexto inicial do serviço de nomes e diretórios. É a “raíz” do sistema, o “root”, o
“ponto de partida” para a navegação entre os nós da árvore de nomes.
A nível de código, ele é representado como uma classe em que seu construtor recebe
um java.util.Properties como parâmetro. É através dele que configuramos a
implementação que desejamos utilizar.

Exemplo:
Properties prop = new Properties();
prop.put(Context.INITIAL_CONTEXT_FACTORY, "org.jnp.interfaces.NamingContextFactory");
prop.put(Context.PROVIDER_URL, "jnp://127.0.0.1:1099");

InitialContext ctx = new InitialContext(prop);

Exemplos de implementações (service providers):


19

LDAP:
Properties prop = new Properties();
prop.put(Context.INITIAL_CONTEXT_FACTORY, "com.sun.jndi.ldap.LdapCtxFactory");
prop.put(Context.PROVIDER_URL, "ldap://localhost:389");
*incluso no JDK

COS Naming Service:


Properties prop = new Properties();
prop.put(Context.INITIAL_CONTEXT_FACTORY, "com.sun.jndi.cosnaming.CNCtxFactory");
prop.put(Context.PROVIDER_URL, "iiop://localhost:1050");
*incluso no JDK

RMI Registry:
Properties prop = new Properties();
prop.put(Context.INITIAL_CONTEXT_FACTORY,"com.sun.jndi.rmi.registry.RegistryContextFactory");
prop.put(Context.PROVIDER_URL, "rmi://localhost:1099");
*incluso no JDK

DNS:
Properties prop = new Properties();
prop.put(Context.INITIAL_CONTEXT_FACTORY, "com.sun.jndi.dns.DnsContextFactory");
prop.put(Context.PROVIDER_URL, "dns://server1.sun.com/java.sun.com");
prop.put(Context.AUTHORITATIVE, "true");
*incluso no JDK

File System:
Properties prop = new Properties();
Prop.put(Context.INITIAL_CONTEXT_FACTORY,"com.sun.jndi.fscontext.RefFSContextFactory");
*não incluso no JDK

Download de outros Service Providers (dns, novell, registro do windows, etc):


http://java.sun.com/products/jndi/serviceproviders.html

Obs.: para SPIs que requerem usuário e senha, utilizamos os seguintes parâmetros:
prop.put(Context.SECURITY_PRINCIPAL, "usuario");
prop.put(Context.SECURITY_CREDENTIALS, "xxxxxxx");

Context
É qualquer contexto do serviço de nomes e diretórios. Como ilustrado no capítulo
sobre COS Naming Service, um contexto pode conter sub-contextos. A característica
principal de um contexto é que ele não pode possuir nomes repetidos para
associações de recursos.
E isso faz todo o sentido, pois um recurso deve ser identificado de forma única.
Logicamente uma mesma pasta não pode conter dois arquivos com o mesmo nome,
ou um domínio não pode conter dois sub-domínios diretos com o mesmo nome.
Toda e qualquer operação de manuseio de registros de nomes é feita de forma relativa
a um contexto.

Bind
Operação de gravar uma associação nome-objeto em um contexto. Se o nome já
existir, é lançada uma javax.naming.NamingException.

Rebind
Operação de regravar uma associação nome-objeto em um contexto. A diferença para
o Bind é que, se o nome já existir, ele é sobrescrito.

Unbind
Operação de remover uma associação nome-objeto de um contexto.
20

Lookup
Operação de localizar uma associação nome-objeto em um contexto. Lembrando que
será feito sempre relativo a um contexto, pois em JNDI não existem “nomes
absolutos”.

ListBindins
Operação de listar todas as associações nome-objeto de um contexto.

4. Introdução à Plataforma JEE


4.1. O que é J2EE ou JEE ?
O termo JEE significa Java Enterprise Edition. Como o próprio nome já diz através do
termo “Enterprise”, o JEE é uma plataforma baseada em Java, desenvolvida para o
desenvolvimento de sistemas corporativos.

Na literatura atual podemos encontrar os termos J2EE ou JEE. Os duas


nomenclaturas referem-se à plataforma Java Enterprise Edition, onde a letra “2” fazia
menção à versão 2 do Java, referente ao JDK 1.4. Nas versões mais recentes da
plataforma, a letra “2” foi removida, falando-se apenas em JEE ou Java Enterprise
Edition.

O JEE não é um software nem nenhuma ferramenta especial. O JEE é um conjunto de


especificações desenvolvidas para atender as necessidades vitais de sistemas
corporativos, visando a componentização e a facilidade na implementação desses
sistemas.

A partir desse conjunto de especificações, cada fabricante implementa as


funcionalidades especificadas da forma desejada, como for mais viável e menos
custoso.

Isto torna a tecnologia JEE independente de fornecedor e de plataforma, pois a


especificação garante que um sistema desenvolvido seguindo a especificação terá o
mesmo comportamento em qualquer plataforma de qualquer fabricante (na prática não
é “tão maravilhoso assim”, mas através de algumas técnicas e boas práticas
conseguimos minimizar o impacto da dependência do fabricante).

Todas estas especificações são abertas, mantidas pela Sun e pelo Java Community
Process (JCP). O JCP é uma comunidade composta pelos principais fabricantes de
tecnologia em plataforma Java, que definem as especificações (JSRs) para as
principais tecnologias java.
Dentre este plantel de fabricantes encontram-se IBM, Oracle, HP, Lucent, Hed Hat e
todos os demais “barões” da tecnologia atual.

Todas as JSRs e suas revisões são disponibilizadas para download no site do Java
Community Process. As JSRs são documentos fabulosos para se ter como
documentação das principais APIs do JEE:
http://www.jcp.org/

4.2. Modelo de Programação JEE


Toda aplicação corporativa de missão crítica possui requisitos em comum, como
comunicação remota, componentes no lado do servidor com tecnologias de objetos
21

distribuídos, sistemas de mensageria assíncrona, segurança centralizada,


escalabilidade, pool de recursos, controle transacional, entre diversos outros
requisitos.

Desenvolver um sistema distribuído não é simples:


• O que acontecerá com a performance quando aumentar o volume de acessos
?
• Quais são os custos para se aplicar a capacidade de processamento ?
• Qual a integridade dos dados e das transações se alguma chamada falhar ?
• Quem garante a segurança e o controle de acesso ?
• E se o sistema remoto sair do ar ?

O modelo de programação JEE é baseado no reuso de componentes focando a


minimização do tempo de desenvolvimento de um sistema corporativo de larga escala.

Com o JEE o desenvolvedor não precisa se preocupar com particularidades de


infra-estrutura, como segurança, comunicação TCP-IP, pool de recursos ou
transações. O “Container JEE” fornece essas funcionalidades de forma segura e
confiável, permitindo assim que o desenvolvedor se concentre apenas na lógica
de negócio.

4.3. O Servidor de Aplicação


O Servidor de Aplicação (Application Server) é o componente macro da especificação
JEE, encarregado por implementar todos os serviços básicos de infra-estrutura,
seguindo a especificação JEE.

Ele é subdividido conceitualmente em duas partes, ou “containers”:

• Container Web
22

Responsável por disponibilizar e gerenciar os componentes de infra-estrutura


referentes a camada web (“Web Tier”), como as especificações Servlet e JSP.

• Container EJB
Responsável por disponibilizar e gerenciar os componentes de infra-estrutura
da camada de negócio (“Business Tier”), como as especificações EJB e JPA.

No mercado, atualmente, existem servidores de aplicações que disponibilizam


somente o Container Web (ex.: Tomcat e Jetty) e existem, também, os chamados
servidores de aplicação completos, que disponibilizam tanto o Container Web quanto
o Container EJB (ex.: JBoss, Sun GlassFish, IBM WebSphere, Oracle/BEA WebLogic).

4.4. O Container EJB

Um Container JEE precisa fornecer os seis serviços básicos:


o Concorrência
o Transação
o Persistência
o Distribuição de Objetos
o Atribuição de Nomes (JNDI)
o Segurança

Serviços secundários:
o Mensageria Assíncrona
o Temporização

Para um Container EJB ser completamente compatível com a especificação EJB 3.0,
ele precisa implementar as seguintes APIs:
o EJB 3.0
o JTA 1.1
o JPA 2.0
o JMS 1.1
o JavaMail 1.4 (apenas envio de email)
o JAF 1.1
o JAXP 1.2
o JAXR 1.0
o JAX-RPC 1.1
o JAX-WS 2.0
o JAXB 2.0
o SAAJ 1.3
o JAAS
o Connector 1.5
23

o Web Services 1.2


o Web Services Metadata 2.0
o Common Annotations 1.0
o StAX 1.0

APIs fornecidas pelo próprio runtime JSE sob o container:


o JDBC
o RMI-IIOP
o JNDI

4.5. Definição de Enterprise JavaBeans


Os Enterprise JavaBeans (EJBs) são componentes Java criados seguindo a
especificação JEE, que são gerenciados pelo servidor de aplicação. Os EJBs
representam a lógica de negócio do sistema (“aluguel de filmes”, “agente de viagens”,
“autenticador”, etc).
Com a junção dos EJBs e o Container, nos preocupamos em desenvolver apenas a
lógica do negócio, e o Container cuida do ciclo-de-vida dos beans, gerenciamento de
pool, segurança, controle de transações e todos os demais serviços básicos.

Definição da Sun para a Arquitetura EJB:

“A arquitetura Enterprise JavaBeans é uma arquitetura de componentes para o


desenvolvimento e a implantação de aplicativos de negócio distribuídos
baseados em componentes. Aplicativos escritos utilizando a arquitetura
Enterprise JavaBeans são escalonáveis, transacionais e seguros com multi-
usuários. Esses aplicativos podem ser escritos uma vez e então implantados em
qualquer plataforma de servidor que suporta a especificação Enterprise
JavaBeans.”

Atenção: “Enterprise JavaBeans” não são simples “JavaBeans”. O termo “JavaBeans”


propriamente dito refere-se a classes Java que seguem o padrão de nomenclatura
JavaBeans (classe encapsulada com “getters” e “setters” padronizados, ex. getAtributo
e setAtributo). Quando falamos “Enterprise JavaBeans”, esses sim são os EJBs, os
objetos de negócio distribuídos gerenciados pelo container.

Objetos Remotos vs. Objetos Distribuídos

Os Enterprise JavaBeans não representam objetos remotos (não implementam


java.rmi.Remote), e sim objetos distribuídos.

Eles podem ser “distribuídos” de diversas formas (localmente, remotamente, via


CORBA, via WebServices), porém quem faz o trabalho de distribuir os objetos é o
container. O container que implementa um Skeleton RMI ou uma interface de
WebService intercepta a requisição e a “delega” ao EJB.

5. Introdução ao JBoss Application Server


O JBoss Application Server é um servidor de aplicação JEE completo, atualmente
mantido pela empresa Red Hat. Ele é disponibilizado em duas versões: Community e
Enterprise.

A versão Community é totalmente open-source, destinada primeiramente a


desenvolvedores, mas pode ser utilizada para qualquer fim:
24

http://www.jboss.org/

Já a versão Enterprise é disponibilizada para fins comerciais, mantendo a metodologia


da Red Hat de não cobrar pelo produto, mas sim pelo suporte. Ela vem nomeada de
Application Platform, geralmente contendo pacotes adicionais como IDE de
Desenvolvimento com plugins proprietários para agilizar o desenvolvimento e outras
ferramentas e frameworks.
http://www.jboss.com/

5.1. Instalação
Após o download do pacote zip contendo o JBoss Application Server, como indicado
no capítulo 1.3 da apostila, podemos começar o procedimento de instalação.

Por ser baseado em java e assim portável, ele não possui instalador específico para
um sistema operacional. O JBoss é o conteúdo do arquivo zip.

Passos para instalação:


o Extrair o conteúdo do arquivo zip para um diretório qualquer. Para evitar
inconvenientes e incompatibilidades sobre formatos de nomes de diferentes
sistemas operacionais, devemos evitar um caminho muito comprido e
devemos evitar também o caractere “espaço” no caminho da pasta do
JBoss.
o Agora devemos apenas setar a variável de ambiente JBOSS_HOME
indicando o caminho onde o JBoss foi instalado, para que os aplicativos
que precisarem poderem localizar seu classpath

“Meu Computador” > “Propriedades”

“Avançado” > “Variáveis de Ambiente”


25

Digitar a variável “JBOSS_HOME”,


informando o diretório onde foi
descompactado o JBoss

“Variáveis do Sistema” > “Nova”

Pronto. Tendo feito isso, o JBoss estará pronto para rodar.

5.2. Estrutura de Diretórios JBoss 5.x


Para conhecermos um pouco melhor o nosso servidor de aplicação, vamos dar uma
analisada geral em sua estrutura de diretórios:

• bin – todos executáveis e scripts necessários para


ligar e desligar o servidor
• client – todos os JARs necessários para comunicar
com o JBoss a partir de aplicações client standalone
de várias naturezas
• docs – documentações do fornecedor
• lib e common – todos os JARs necessários para o
rodar o core do servidor
• server – todas as Server Configurations

5.3. Server Configurations


Cada pasta dentro do diretório server representa um “Server Configuration”, que é
uma instância do servidor.

A distribuição do JBoss fornece cinco templates de configuration: all, default, minimal,


standard e web. Cada template tem suas características e serviços configurados.
26

Atenção
Nunca carregue diretamente os templates, pois eles não foram criados para ser
usados. Eles servem como modelo para criarmos a nossa configuration
personalizada.

Para criar uma configuration, basta replicar o template (criar uma cópia da pasta) que
desejarmos e modificar o nome da pasta.

5.3.1. Estrutura de Diretórios do Server Configuration


Todos os templates possuem uma estrutura semelhante, vamos analisá-la:

• conf – configurações gerais de funcionamento do


servidor, são carregadas apenas uma vez quando o
server inicia (qualquer mudança requer restart do
server)
• deploy – diretório onde se faz o deploy de aplicações
(basta copiar nele o EAR ou o WAR e o deploy é feito
automaticamente)
• deployers – contém os serviços responsáveis por
efetuar o deploy dos arquivos no servidor (existe um
deployer para cada tipo de serviço – web, ejb3, etc)
• lib – qualquer JAR presente nessa pasta será
compartilhado e visível para todas as aplicações
deployadas no servidor

Além desses diretórios padrão, são gerados também em run-time mais quatro pastas:
• data – utilizada para todos os serviços do JBoss que precisam guardar dados
persistentes no disco (ex.: Naming Service) – Hypersonic SQL Database
(HSQLDB)
• log – onde são gerados os arquivos de log do servidor
• tmp – qualquer tipo de dado temporário necessário pelos serviços do JBoss
• work – utilizado pelo Web Container do JBoss (baseado em Tomcat), para
guardar JSPs compiladas e outros arquivos temporários necessários para web

5.4. Configurando um Servidor JBoss no Eclipse

“Server” > “Runtime Environments” > “Add...”


“Window” > “Preferences”
27

Selecione “JBoss v5.0” e clique em “Next” Escolha o JRE desejado e localize a pasta onde
reside o diretório raiz do JBoss

Na aba “Servers”, clique com o botão


direito do mouse e crie um novo server

Selecione o run-time environment criado


anteriormente marcando “JBoss v5.0” e clique em
“Next”
28

Em “Server Configuration”, digite o nome da sua configuração personalizada e finalize


Atenção: nunca utilize os templates (all, default, minimal, etc), crie sempre uma configuração
personalizada e a utilize.

6. Enterprise JavaBeans 3.0


A especificação EJB 3.0 veio a partir do JDK 1.5, com o intuito de facilitar o
desenvolvimento de EJBs substituindo os milhares de XMLs de configuração
necessários na versão EJB 2.1 pelas Annotations (recurso adicionado a partir do JDK
1.5). Além dessa evolução, o modelo de programação ficou mais simplificado
demandando menos código para implementar o mesmo beans, e houve a criação da
JPA (Java Persistence API) para substituir os antigos Entity Beans da velha
especificação (detalhes da especificação EJB 2.1 não são escopo deste curso).

Obs.: os EJBs 3.0 possuem total interoperabilidade com os EJBs 2.1, ficando quase
que transparente para o desenvolvedor caso precise integrá-los.

O capítulo 4.5 introduziu os conceitos da arquitetura de componentes Enterprise


JavaBeans. Vimos que é uma especificação que define um padrão de construção de
componentes modularizados e reutilizáveis que rodam em um servidor de aplicação,
mais especificamente em um container EJB, que lhes fornece serviços indispensáveis
de infra-estrutura distribuída (escalonáveis, transacionais e seguros com multi-
usuários).

Tipos existentes de EJBs:


• Session Beans
o Stateless Session Beans
o Statefull Session Beans
• Message Driven Beans
29

Obs.: até a versão EJB 2.1 também haviam os “Entity Beans”, porém na versão 3.0
eles foram substituidos pela JPA, e com bons motivos (veremos com mais detalhes no
capítulo sobre JPA).

6.1. Session Beans


6.1.1. Identificando os Beans
Quando modelamos um aplicativo empresarial, nos deparamos com dois tipos de
artefatos: entidades e processos. O termo “Entidade” se refere a artefatos com um
estado bem definido, geralmente se apresentam em forma de substantivos ou nomes
(ex.: “Filme”, “Usuário”, “Conta”). As entidades por si só são auto-suficientes, possuem
uma representação lógica. Já o termo “Processo” refere-se a uma tarefa, uma regra de
negócio, que não tem um estado bem definido. Geralmente os processos se
apresentam em forma de verbos ou agentes (ex.: “AgenteDeViagens”), artefatos que
não são auto-suficientes, muito pelo contrário, eles dependem das entidades e
efetuam operações com as mesmas.

Na arquitetura EJB, os Session Beans representam os processos, as tarefas, as


regras de negócio. Podem possuir ou não um estado, porém quando possuem estado,
este é um estado intermediário, transiente (em memória), que não faz sentido persistí-
lo em uma base de dados.

Quando pensamos em “sem estado”, podemos imaginar uma classe sem atributos,
composta apenas de métodos. Na prática é exatamente isso. Por exemplo, um
Session Bean de “Login” não possui estado, ele não precisa armazenar informações
entre diferentes requisições, ele não precisa “lembrar do usuário atual”, ele apenas
fornece a tarefa de efetuarLogin, e esta tarefa é auto-suficiente. Por outro lado, um
Session Bean “CarrinhoDeCompras” precisa ter um estado, pois ele não representa
uma tarefa atômica, ele precisa “lembrar do usuário atual” e armazenar seus itens de
compra, até que finalmente o usuário decida efetuar a transação.

6.1.2. Interfaces de Negócio


Construir EJBs é bem parecido com a construção de objetos remotos que vimos nos
capítulos anteriores, pois o bean pode se “apresentar como um objeto remoto”.
Primeiramente definimos a Interface de Negócio do bean, que representa o seu
contrato, as operações que o bean irá expôr aos clientes.

Interface Características
• É utilizada quando desejamos acessar o bean
da mesma JVM
• É a interface mais lightweight
• É realizada uma chamada de método local,
javax.ejb.Local
como uma classe normal
• A passagem de parâmetros e retorno de
objetos é feita por referência, como o padrão
do java
• É utilizada quando desejamos acessar o bean
remotamente, via CORBA ou RMI
javax.ejb.Remote • Realiza uma chamada de método remota, por
CORBA, RMI-JRMP ou RMI-IIOP (configurável
de acordo com o container utilizado)
30

• A passagem de parâmetros e retorno de


objetos é feita por valor
• Devido ao item anterior, os parâmetros e
retornos que são objetos precisam ser
serializáveis
• É utilizada quando queremos que o MOM
javax.ejb.MessageDriven acesse o bean via mensageria (mais detalhes
no capítulo sobre JMS)
• É utilizada quando desejamos acessar o bean
via WebService
javax.jws.WebService
• Disponível apenas para beans Stateless (pois
WebService não guarda estado)

Cada bean pode implementar quantas interfaces de negócio forem necessárias, e o


container EJB fará o trabalho de distribuí-lo das diversas formas solicitadas.

6.1.3. Session Beans – Stateless


Estes são os tipos mais comuns de beans, os que não apresentam estado. Os
Session Beans do tipo Stateless fornecem operações auto-suficientes, onde uma
única requisição do cliente é suficiente para efetuar o processo desejado.

Exemplo de EJB Stateless:

• Interface de Negócio Local


@Local
public interface CalculadoraLocal {
public double somar(double a, double b);
public double subtrair(double a, double b);
public double multiplicar(double a, double b);
public double dividir(double a, double b);
}

• Implementação
@Stateless
public class CalculadoraEJB implements CalculadoraLocal {
public double somar(double a, double b) {
return a + b;
}
public double subtrair(double a, double b) {
return a - b;
}
public double multiplicar(double a, double b) {
return a * b;
}
public double dividir(double a, double b) {
return a / b;
}
}

Como vimos no exemplo acima, para criar um EJB basta definir sua interface de
negócio, anotá-la da maneira desejada e implementar o bean.

Se desejarmos expor o bean também remotamente, criamos uma interface remota:

• Interface de Negócio Remota


@Remote
public interface CalculadoraRemote {
public double somar(double a, double b);
public double subtrair(double a, double b);
public double multiplicar(double a, double b);
public double dividir(double a, double b);
31

• Implementação
@Stateless
public class CalculadoraEJB implements CalculadoraLocal, CalculadoraRemote {
public double somar(double a, double b) {
return a + b;
}
public double subtrair(double a, double b) {
return a - b;
}
public double multiplicar(double a, double b) {
return a * b;
}
public double dividir(double a, double b) {
return a / b;
}
}

No exemplo acima, o nosso bean LocadoraEJB será exposto das duas formas,
localmente e remotamente. É permitido utilizar a interface remota para fazer chamadas
da mesma JVM, porém esta chamada será feita de forma remota (os objetos serão
serializados e passados por valor), desperdiçando assim a melhoria de performance
oferecida na chamada local.

Note também que as chamadas locais e remotas não precisam necessariamente


possuir as mesmas operações. Podemos definir operações diferentes para serem
expostas localmente e remotamente, basta definí-las nas respectivas interfaces de
negócio.

Obs.: se desejarmos fornecer exatamente as mesmas operações, podemos melhorar


o código evitando as duplicações das interfaces, da seguinte forma:
public interface CalculadoraOperations {
public double somar(double a, double b);
public double subtrair(double a, double b);
public double multiplicar(double a, double b);
public double dividir(double a, double b);
}
@Local
public interface CalculadoraLocal extends CalculadoraOperations { }
@Remote
public interface CalculadoraLocal extends CalculadoraOperations { }

6.1.4. Session Beans – Statefull


Estes são os tipos mais pesados de beans, os que apresentam estado. Eles são
pesados pois são únicos para cada cliente que os acessa, portanto o container precisa
criar uma instância do bean por requisição (veremos com mais detalhes quando
analisarmos o ciclo-de-vida dos EJBs).
Os Session Beans do tipo Statefull são capazes de guardar estado, portanto podem
ter variáveis de instância e utilizá-las no decorrer dos diversos métodos. Pode ser feita
uma analogia ao HttpSession da tecnologia web, pois é criada uma espécie de
“sessão” para cada cliente que executa o bean.

Exemplo de EJB Statefull:

• Interface de Negócio Local


@Local
public interface CarrinhoDeComprasLocal {
public void adicionarNoCarrinho(double preco);
public double obterTotal();
public void finalizarCompra();
}
32

• Implementação
@Stateful
public class CarrinhoDeComprasEJB implements CarrinhoDeComprasLocal {
private double totalCarrinho;

public void adicionarNoCarrinho(double preco) {


totalCarrinho += preco;
}
public double obterTotal() {
return totalCarrinho;
}
@Remove
public void finalizarCompra() {
System.out.println("Compra finalizada: " + totalCarrinho);
}
}

Uma sessão é criada para um determinado cliente quando o mesmo faz o primeiro
request ao bean, e é finalizada quando o cliente chama o método anotado com
@Remove, ou então quando a sessão expira (este tempo de expiração pode ser
configurado no container).

6.1.5. Ciclo de Vida dos Session beans


Session Beans – Stateless
Os session beans do tipo Stateless possuem um ciclo de vida muito simples, mas
primeiramente devemos falar sobre um serviço muito importante que o container EJB
oferece: o Pooling de Instâncias.

Uma das principais características de uma aplicação empresarial em produção é sofrer


um volume muito grande de acessos, e ela deve estar preparada para isso. Os EJBs
são objetos custosos, pesados. Portanto, como garantir a performance do sistema em
um caso crítico de acessos simultâneos? Com o intuito de otimizar o desempenho da
aplicação, o container fornece o serviço de Pooling de Instâncias. Este serviço faz
parte da especificação EJB e todo container é obrigado a implementá-lo.

O pooling de instâncias é implementado para os dois tipos de EJB que não guardam
estado: o Stateless e o MDB. O serviço funciona da seguinte forma: para cada
Stateless Session Bean e MDB deployados no servidor, o container cria um pool em
memória contendo um determinado número de instâncias desses beans. Então,
quando um cliente solicita um método de bean, uma instância aleatória é escolhida no
pool para servir à solicitação em particular. Quando a chamada do método finaliza, a
instância volta ao pool para posteriormente servir a outra solicitação.

O container não precisa ter a carga de instanciar uma nova instância do bean a cada
requisição, elas já estão prontas no pool. De acordo com a implementação do
container, ele pode decidir aumentar o pool e criar mais instâncias ou até mesmo
destruir instâncias de acordo com o número de requisições média ou memória
disponível.

Atenção
Ao contrário dos Servlets, os EJBs não executam em multithreading:
Servlets  uma instância por aplicação e uma nova thread por requisição
EJBs  uma instância por requisição (no caso de Stateless e MDB, a mesma
instância pode ser reutilizada por diversas requisições)

Note que isto é possível apenas para os beans Stateless e MDB, que não guardam
informações de estado e, desta forma, duas requisições subsequentes de um mesmo
33

cliente ao mesmo bean podem ser atendidas por instâncias diferentes do bean, sem
causar efeitos colaterais na aplicação.

Sendo assim, o ciclo de vida dos beans Stateless é muito simples, onde existem
apenas dois estados: “Não Existente” e “Pronto no Pool”

Session Beans – Stateful


34

Os session beans do tipo Stateful possuem um ciclo de vida um pouco mais complexo,
mas primeiramente devemos falar sobre um serviço muito importante que o container
EJB oferece: o Mecanismo de Ativação.

Para entender o motivo do mecanismo de ativação, devemos entender o


funcionamento do bean Stateful. O bean Stateful, por ter a característica de manter um
estado referente ao cliente que o chamou, não pode ter suas instâncias reutilizadas
entre clientes diferentes, pois cada instância só diz respeito a um cliente. Por exemplo,
o meu carrinho de compras mantém os valores das minhas compras, e não das
compras do João ou do Pedro. Devido a esta característica, o bean Stateful pode ter
apenas uma instância por cliente, e não pode haver pool pois elas não podem ser
reusadas.

Agora imagine um cenário empresarial, onde há milhares de clientes efetuando acesso


simultâneo a diversos beans Stateful. Como o container irá gerenciar todo esse uso de
memória ? Para isso existe o mecanismo de ativação.

Este mecanismo funciona da seguinte forma: quando o cliente termina de invocar um


método de negócio, o container persiste o estado do bean (variáveis de instância) em
um armazenamento secundário (disco ou banco de dados) e o remove da memória.
Esse processo se chama apassivação. Quando o mesmo cliente solicita novamente
outro método de negócio, o container instancia um novo bean, recupera o estado do
bean do armazenamento e refaz a instância como ela era anteriormente. O processo
de “volta” se chama ativação.

Obs.: o objeto Stub não perde a conexão durante esse processo

Desta forma, o ciclo de vida dos beans Stateful possui um estado a mais que o
Stateless e sem utilização de pool, resultando em três estados: “Não Existente” ,
“Pronto” e “Apassivado”
35

Curiosidade – Entendendo o Mecanismo Remoto Do Container


O papel do container é fazer com que o desenvolvedor esqueça os serviços primários
de infra-estrutura e se concentre em resolver a lógica de negócio através de EJBs.
Porém, vale a pena discutir como os modelos de computação distribuída estudados no
início do curso contribuíram para a especificação e funcionamento bem sucedido do
modelo EJB. Vamos entender como o container EJB trabalha !

O mecanismo de invocação de métodos remota do container é baseado em RMI,


portanto, baseando-se nos capítulos iniciais, vimos que ele precisa ter três coisas:
• Skeleton
• Stub
• Protocolo de comunicação remota
• Serviço de Nomes (Naming Service)

Os beans que implementamos não são objetos remotos, porém o container, através do
design pattern do GOF chamado Proxy, cria um “objeto proxy” que recebe as
requisições e as redireciona para os nossos beans.

Proxy Pattern

Fazendo um comparativo da figura que ilustra o pattern e a arquitetura do container


EJB, temos o bean real que codificamos, que implementa a interface de negócio.
Então, em tempo de deploy, através de reflection, o container gera uma classe proxy
36

que funciona como o Skeleton da arquitetura remota (note que o proxy também
implementa a interface de negócio). Esta classe que é responsável por receber a
invocação remota e “delegar” a chamada à instância do EJB através do container. O
container por sua vez gerencia as questões de pooling, transação e segurança da
chamada.

Vimos também que todo sistema distribuído trabalha em conjunto com um Serviço de
Nomes. Na arquitetura EJB, cada container implementa o serviço de nomes que
preferir. No caso do JBoss, o serviço de nomes padrão é o Java Naming Provider
(JNP), que é uma implementação substituta do RMI Registry. O protocolo de
comunicação padrão de distribuição de EJBs do JBoss é o RMI-JRMP.

Na camada cliente, vimos também que é necessário um Stub para realizar as


chamadas remotas. Porém, nós vimos que o lookup é feito somente com a interface
de negócio. Isto é possível devido ao conceito de codebase (capítulo 2.5), pois ao
fazer o lookup do EJB, o Naming Service informa à JVM cliente o endereço do Stub,
que é baixado e carregado automaticamente em tempo de execução pelo cliente e
desta forma a chamada remota é realizada.

6.2. Distribuindo EJBs


Depl.
Pacote Sigla Descrição
Descriptor
Roda no Container Web, suporta as
WAR Web ARchive web.xml
especificações Servlets e JSP
Roda no Container EJB, suporta as
EJB Java
EJB-JAR ejb-jar.xml especificações EJB e JPA (incluir
ARchive
persistence.xml)
Enterprise
EAR application.xml Roda no Container EJB
ARchive

• Estrutura do WAR
37

• Estrutura do EJB-JAR

• Estrutura do EAR

6.3. EJB Timer Service


Em sistemas empresariais é muito comum nos depararmos com funcionalidades de
agendamento de tarefas, como por exemplo a geração de relatórios de tempos em
tempos ou expiração de acesso depois de algum tempo de inatividade de um
determinado usuário.
38

Um sistema de agendamento é um sistema que executa algum tipo de atividade


automatizada, de acordo com um intervalo de tempo.

A especificação EJB 3.0 define um serviço de temporização, chamado de EJB Timer


Service. Ele permite a criação de tarefas agendadas, de forma transacional e segura.
Os temporizadores criados dessa forma sobrevivem a quedas e restarts do servidor de
aplicação.

Como a definição do EJB Timer Service é relativamente recente (foi incluída a partir da
versão EJB 2.1), ele ainda é um pouco engessado e pobre de funcionalidades. Porém,
para implementações simples, acaba tornando-se a ferramenta mais recomendada.
Para implementações de temporizadores mais robustos, existe um framework de
mercado chamado Quartz, que é muito mais completo e poderoso que o EJB Timer
Service, além do fato de poder ser utilizado em aplicações Java SE simples, sem a
necessidade de um container EJB (o estudo do framework Quartz não é escopo desse
curso).

Curiosidade: o JBoss Application Server já é distribuído com uma versão do


Quartz acoplada e implementa seus serviços de EJB Timer Service através do
Quartz.

Os temporizadores fornecidos pelo EJB Timer Service podem ser de dois tipos:

• Ação Única
Os temporizadores de ação única disparam apenas uma vez, depois de um certo
intervalo de tempo ou em uma data específica.

• Intervalo
Os temporizadores de intervalo disparam várias vezes, de acordo com um período
de tempo.

Para criar um temporizador, basta obter uma referência ao TimerService do container


e “inscrever” o EJB para ser o timer. Um timer é criado sempre através de um EJB, e
quando o mesmo for disparado, será executado o método que tiver a anotação
@Timeout.

Cuidado
Apenas os beans do tipo Stateless e MDB podem ser timers.

Obtendo a referência ao TimerService do container:


@Resource
private TimerService timerService;

Inscrevendo o bean para ser um timer:


@Stateless
public class TesteTemporizador implements TesteTemporizadorLocal {

@Resource
private TimerService timerService;

public void criarTemporizador() {


Calendar dataDeDisparoAsCalendar = Calendar.getInstance();
//soma 10 dias na data atual
dataDeDisparoAsCalendar.add(Calendar.DATE, 10);

Date dataDeDisparo = dataDeDisparoAsCalendar.getTime();


39

//cria um timer para executar na data informada


timerService.createTimer(dataDeDisparo, null);
}

@Timeout
public void temporizador(Timer timer) {
System.out.println("DISPAROU!");
}

Métodos da interface TimerService:

public Timer createTimer(Date expiration, Serializable info)


• Cria timer de Ação Única
• Expira na data especificada

public Timer createTimer(long duration, Serializable info)


• Cria timer de Ação Única
• Expira depois de passado o tempo especificado em milisegundos

public Timer createTimer(Date initialExpiration, long intervalDuration, Serializable info)


• Cria timer de Intervalo
• Expira na data especificada e subsequentemente a cada intervalo
especificado em milisegundos

public Timer createTimer(long initialDuration, long intervalDuration, Serializable info)


• Cria timer de Intervalo
• Expira depois de passado o tempo initialDuration e subsequentemente a cada
intervalo especificado em intervalDuration

public Collection getTimers()


• Retorna uma coleção contendo todos os timers agendados para o bean em
questão

7. Java Persistence API (JPA)


Como vimos no capítulo sobre Session beans, ao projetar um sistema empresarial
geralmente identificamos dois tipos de artefatos: entidades e processos, em que
“Entidade” se refere a artefatos com um estado bem definido e persistente (ex.:
“Filme”, “Usuário”, “Conta”).

Na versão EJB 2.1 existia o conceito de Entity Beans, que também eram componentes
EJB muito parecidos com os Session Beans, com interface de negócio e distribuídos
remotamente, gerando Proxies (ou Skeletons) e Stubs. Ou seja, os Entity Beans eram
pesados, faziam chamadas remotas e dependiam de um container EJB e muita
configuração XML.

Observando o grande sucesso dos frameworks ORM (Object Relational Mapping),


como por exemplo o Hibernate, a partir da versão EJB 3.0 a Sun também decidiu
especificar um framework ORM, e então foi criada a especificação JPA.
40

Note a diferença de implementação para especificação: o Hibernate é um produto, um


framework proprietário desenvolvido e mantido por alguma entidade (atualmente a
Red Hat), enquanto a JPA, assim como o EJB, é uma especificação, estudada e
desenvolvida pela JCP (Java Community Process). A vantagem de ser uma
especificação da JCP é que ele acaba definindo uma “padronização de mercado”, uma
“referência”, definida em consenso com as maiores empresas fornecedoras de
produtos Java, tornando o uso da tecnologia independente de fornecedor, ou seja,
estamos livres para usarmos a implementação que desejarmos ou que revele a melhor
performance (e sempre através da mesma interface de programação).

A JPA é leve, o modelo de desenvolvimento é baseado em POJOs (Plain Old Java


Objects), classes simples, que são manipuladas pelo framework através de
introspecção. Para a configuração das entidades é permitido optar entre configurações
via XML ou o uso das Annotations. Seu uso é bem simplificado, e suas
funcionalidades são todas encapsuladas dentro de um artefato chamado Entity
Manager.

Outra grande vantagem da especificação JPA é que ela foi criada para atuar também
em aplicações simples Java SE, não sendo necessário um servidor de aplicação para
gerenciar as entidades, todas as implementações da JPA devem fornecer uma
implementação do Entity Manager independente de container EJB.

7.1. Entity Manager


É através do Entity Manager que trabalhamos com nossas entidades; ele é o
gerenciador de entidades. Toda e qualquer operação de sincronização com a base de
dados que realizamos nas nossas entidades JPA é gerenciada pelo entity manager.
Ele que é responsável por resolver o mapeamento ORM, criar as conexões com o
banco, gerar os sql statements nativos específicos do banco e efetuar as operações
de persistência (desde que ele esteja associado a um Contexto de Persistência).

Obs.: toda operação realizada com o Entity Manager é transacional, de acordo com os
conceitos de Contexto de Persistência e Entidade Gerenciada que veremos adiante.

Principais operações oferecidas pelo Entity Manager:

persist(Object entity)
• Enfileira a entidade para criação no banco (não representa o momento real do
insert)
• É possível chamar persist fora de uma transação apenas se o contexto for
EXTENDED; nesse caso, a inserção é enfileirada até o contexto ser associado
com uma transação
• Se o parâmetro não for uma entidade, lança IllegalArgumentException
• Se for invocado fora do contexto de transação e for tipo TRANSACTION, lança
uma TransactionRequiredException
41

find(Class<T> entityClass, Object pk) : Entidade


getReference(Class<T> entityClass, Object pk) : Entidade
• Retornam uma entidade a partir de sua chave primária
• O find, se não encontrar retorna null, e utiliza as configurações de lazy-loading
• O getReference, se não encontrar lança EntityNotFoundException
• Se o contexto de persistência estiver ativo ela é acoplada, se não, ela é
desacoplada

createQuery
createXXXQuery
• Executam querys EJB-QL e consultas nativas (retornam um objeto Query)
• Entidades retornadas permanegem gerenciadas enquanto o contexto de
persistência estiver ativo

flush()
• Sincroniza as atualizações no banco antes de terminar a transacao

merge(Object entity) : Entidade


• Atualiza uma entidade desacoplada e retorna uma cópia dela acoplada
• ATENÇÃO: o objeto de parâmetro nunca é gerenciado pelo EntityManager, e
sim sua cópia que retorna. Se ele já estiver gerenciado a mesma instancia, ele
a atualiza e retorna sua referencia.

remove(Object entity)
• Remove a entidade da base e a torna desacoplada

refresh(Object entity)
• Atualiza a entidade acoplada com os dados da base
• Se ela não for acoplada ao próprio EntityManager que invoca o método, é
lançada IllegalArgumentException
• Se o objeto não estiver mais no banco devido a outra Thread ou Processo te-
lo removido, será lançado EntityNotFoundException

contains(Object entity) : Boolean


• Retorna true se a entidade estiver acoplada, false caso contrario.

clear()
• Desacopla todas as entidades atuais gerenciadas pelo EntityManager
• Suas modificações são perdidas, portanto é prudente chamar flush() antes.

7.2. Ciclo de Vida


As entidades JPA apresentam um ciclo-de-vida em relação ao Entity Manager,
mostrado na figura abaixo:
42

• New / Nova

Aqui a entidade ainda não tem um estado persistente, ela acabou de ser instanciada

Ex.:
Pessoa p = new Pessoa();

• Managed / Gerenciada / Acoplada

Aqui a entidade está gerenciada pelo Entity Manager. Isto significa que ela possui um
Id Persistente, ou seja, ela representa ativamente um registro da tabela e qualquer
mudança de seus atributos refletirá automaticamente na tabela após o flush do Entity
Manager ou no final da transação atual.

Ex.:
Pessoa p = entityManager.find(Pessoa.class, 2);
//neste momento, p está “Gerenciada”
p.setNome(“Gilberto Holms”);
p.setIdade(24);
entityManager.flush();

No trecho de código apresentado, após o comando find o Entity Manager irá retornar
uma entidade gerenciada, que representa um registro ativo no banco de dados.

• Detached / Desacoplada

Ocorre quando a entidade perde o sincronismo com o banco. Isso pode acontecer nas
seguintes ocasiões:
o Acabou o Contexto de Persistência
o Foi executado o método clear do Entity Manager
o A entidade foi removida do Contexto de Persistência (método remove)
o A entidade foi enviada ao cliente (retorno do método de negócio de um
EJB)

Ex.:
Pessoa p = entityManager.find(Pessoa.class, 2);
//neste momento, p está “Gerenciada”
p.setNome(“Gilberto Holms”);
p.setIdade(24);
entityManager.flush();
43

entityManager.clear();
//neste momento, p está “Desacoplada”

• Removed / Removida

Uma entidade torna-se removida quando é deletada do banco. Isto é feito através do
método remove do Entity Manager. Após ser removida, ela é automaticamente
desacoplada do Entity Manager.

Ex.:
Pessoa p = entityManager.find(Pessoa.class, 2);
//neste momento, p está “Gerenciada”
p.setNome(“Gilberto Holms”);
p.setIdade(24);
entityManager.flush();

entityManager.remove(p);
//neste momento, p está “Removida”

• Persisted / Persistida

Representa a entidade persistida no banco de dados. Com a abstração do JPA,


podemos entender uma linha de uma tabela como uma instância de um objeto Java.
Ela será retornada ao aplicativo após a execução de qualquer método de consulta do
Entity Manager em que ela atenda os requisitos da busca.

7.3. Contexto de Persistência


O Contexto de Persistência é um conceito da JPA, que significa “o tempo em que as
entidades estão gerenciadas pelo Entity Manager”.
Quando o Contexto de Persistência acaba, as entidades tornam-se desacopladas.

Na arquitetura EJB é possível utilizar dois tipos de contextos de persistência:


Transaction e Extended.

• Transaction

O tipo “Transaction” pode ser utilizado em qualquer tipo de EJB. Significa que o
contexto de persistência irá durar o tempo que durar a transação atual, ou seja, ao
terminar a transação atual, as entidades tornam-se desacopladas.
44

• Extended

O tipo “Extended” foi criado para ser utilizado apenas para os EJBs do tipo Stateful.
Significa que o contexto de persistência irá durar o tempo que durar a sessão do bean
Stateful, ou seja, uma entidade pode-se manter gerenciada entre diferentes
transações e métodos de negócio. Utilizando esse tipo de contexto de persistência,
podemos interagir com uma entidade mesmo fora de uma transação, pois as
alterações realizadas serão enfileiradas e efetivadas quando for iniciada a próxima
transação para aquele entity manager.
45

7.4. Configurando uma Aplicação JPA


Um conjunto de entidades JPA pode ser empacotado em um pacote EJB-JAR, como
ilustrado no capítulo 6.2. Ela será caracterizada por conter um arquivo obrigatório na
pasta META-INF, chamado “persistence.xml”.

Exemplo de persistence.xml:
<persistence>
<persistence-unit name="curso">
<jta-data-source>java:/meuDataSource</jta-data-source>
<properties>
<property name="org.hibernate.hbm2ddl"
value="update" />
</properties>
</persistence-unit>
</persistence>

Um arquivo persistence.xml pode conter várias “Persistence Unit” (Unidade de


Persistência). Uma Unidade de Persistência representa um conjunto de entidades
gerenciadas que residem no mesmo Data Source.
@Stateless
public class MyBean implements MyBusinessInterface {
@PersistenceContext(unitName="curso", type=PersistenceContextType.TRANSACTION)
private EntityManager manager;
...
}

Na prática, uma Unidade de Persistência representa um Entity Manager. Cada Entity


Manager trabalha com uma Unidade de Persistência. Em sistemas mais complexos,
onde haja mais de uma base de dados, é possível trabalhar com múltiplas unidades de
persistência.

Tags importantes:
<persistence-unit name="curso"> Declara a unidade de persistência, informando um
nome (obrigatório)
<jta-data-source> Declara um Data Source transacional (API JTA,
padrão para sistemas JEE), indicando o
“endereço JNDI” do Data Source dentro do
container
<properties> Declara propriedades específicas do fabricante da
implementação JPA utilizada

Tendo configurado o persistence.xml, basta criar as entidades e mapeá-las. O


mapeamento ORM pode ser feito de três maneiras:
• Via Annotations – mais simples, com anotações nas próprias classes de
entidade
• Via XML – é criado um arquivo chamado orm.xml, também dentro de META-
INF, contendo os mapeamentos das entidades
• Annotations com XML – caso utilizados juntos, o XML sobrescreve
parcialmente as Annotations

Obs.: os mapeamentos via XML não serão abordados nesse curso, utilizaremos
sempre as Annotations pelo fato de simplificarem o desenvolvimento e a manutenção
do sistema, porém isso não descarta a citação de trechos do XML quando for
interessante.
46

7.5. Mapeando Relacionamentos


Todos os tipos de mapeamento de entidades podem ser realizados com duas
variações: Unidirecional e Bidirecional. Quanto a sua estrutura de schema físico
(base de dados) eles não diferem em nada. Isto é relevante apenas para o
desenvolvimento na camada Java:
• Unidirecional – é quando queremos pesquisar partindo apenas de um lado do
relacionamento. Por exemplo, Pessoa vs. Enredeço: queremos saber qual é o
endereço de uma pessoa, mas não precisamos saber qual pessoa pertence a
um endereço.
pessoa.getEndereco(); //OK
endereco.getPessoa(); //NÃO EXISTE

• Bidirecional – é quando queremos pesquisar partindo de ambos os lados do


relacionamento. Por exemplo, Pessoa vs. Enredeço: queremos saber qual é o
endereço de uma pessoa e também precisamos saber qual pessoa pertence a
um endereço.
pessoa.getEndereco(); //OK
endereco.getPessoa(); //OK

7.5.1. Um-Para-Um Unidirecional


@Entity
public class Customer {
...
@OneToOne(cascade={CascadeType.ALL})
private Address address;
...
}

7.5.2. Um-Para-Um Bidirecional


@Entity
public class Customer {
...
@OneToOne(cascade={CascadeType.ALL})
private CreditCard creditCard;
...
}

@Entity
public class CreditCard {
...
@OneToOne(mappedBy="creditCard")
private Customer customer;
...
}

7.5.3. Um-para-Muitos Unidirecional


@Entity
public class Customer {
...
@OneToMany(cascade={CascadeType.ALL})
private Collection<Phone> phoneNumbers = new ArrayList<Phone>( );
...
}
47

7.5.4. Muitos-Para-Um Unidirecional


@Entity
public class Cruise {
...
@ManyToOne
private Ship ship;
...
}

7.5.5. Um-Para-Muitos / Muitos-Para-Um Bidirecional


@Entity
public class Reservation {
...
@ManyToOne
private Cruise cruise;
...
}

@Entity
public class Cruise {
...
@OneToMany(mappedBy="cruise")
private Collection<Reservation> reservations = new ArrayList<Reservation>( );
...
}

7.5.6. Muitos-Para-Muitos Unidirecional


@Entity
public class Reservation {
...
@ManyToMany
private Set<Customer> customers = new HashSet<Customer>( );
...
}

7.5.7. Muitos-Para-Muitos Bidirecional


@Entity
public class Reservation {
...
@ManyToMany
private Set<Customer> customers = new HashSet<Customer>( );
...
}

@Entity
public class Customer {
...
@ManyToMany(mappedBy="customers")
private Collection<Reservation> reservations = new ArrayList<Reservation>( );
...
}

7.6. Cascade Type


Um conceito importante é o de atividades em cascata. Existem os seguintes tipos de
cascata:
• ALL
• PERSIST
48

• MERGE
• REMOVE
• REFRESH

Por padrão, quando temos um relacionamento entre entidades, as operações do Entity


Manager são realizadas apenas para a entidade “superior” do relacionamento.

Por exemplo:
Endereco e = new Endereco();
...
pessoa.setEndereco(e);
entityManager.persist(pessoa)

No caso mostrado acima, se não tiver sido configurado nenhum tipo de cascata para o
relacionamento, ao persistir a entidade pessoa, o endereço não será persistido, a JPA
irá subentender que ele já existe na base de dados (e caso não exista, lançará
exception).
Se colocássemos um CascadeType.PERSIST, a JPA iria persistir automaticamente o
endereço (caso ele não exista na base) antes de persistir a pessoa.

Note que os nomes dos Cascade Types são idênticos a alguns métodos do Entity
Manager (exceto ALL, que indica “faça todos”). Em resumo, cada tipo de cascata
indica ao Entity Manager para “também realizar a respectiva operação para a
entidade relacionada”.

7.7. Lazy Loading


Outro conceito importante é o de Lazy Loading, que significa “trazer dados sob
demanda”. Quando setamos um relacionamento como LAZY, significa que a JPA irá
trazer as entidades relacionadas apenas quando o código java precisar acessá-las.
Este recurso é útil quando possuimos uma entidade com muitos relacionamentos.

Por exemplo:

Entidade:
@Entity
public class Customer {
...
@OneToMany(cascade={CascadeType.ALL})
private Collection<Phone> phoneNumbers = new ArrayList<Phone>( );
...
}

FetchType.LAZY
Customer c = entityManager.find(Customer.class, 2);
//neste momento o objeto c não contém a lista de telefones

c.getPhoneNumbers().size();
//apenas nesse momento a JPA carrega a lista de telefones

FetchType.EAGER
Customer c = entityManager.find(Customer.class, 2);
//neste momento o objeto c já contém a lista de telefones
Atenção
49

O carregamento sob demanda funciona apenas se a entidade ainda estiver


gerenciada pelo container. Quando a entidade torna-se desacoplada, qualquer
tentativa de carregamento LAZY resulta em exception.

7.8. CRUD com JPA


Inserir:
Pessoa p = new Pessoa();
p.setNome("Gilberto");
p.setIdade(24);
...
entityManager.persist(p);

Atualizar:
p.setNome("Holms");
...
p = entityManager.merge(p);

Excluir:
entityManager.remove(p);

Buscar (por Primery Key):


Pessoa p = entityManager.find(Pessoa.class, 2);
//procura um registro através da Primery Key da tabela
//se não encontrar, retorna null

Listar (por EJB-QL):


Query query = entityManager.createQuery(
"SELECT p FROM Pessoa AS p WHERE p.nome = :nome"
);
query.setParameter("nome", "Gilberto");

List pessoas = query.getResultList();

7.9. Data Sources no JBoss


Para a plataforma Java Enterprise, o termo “Data Sources” (Fonte de Dados)
representa um ponto de acesso a um sistema de armazenamento secundário,
geralmente um banco de dados.

Nos aplicativos JEE, devido à capacidade do container de injetar dependências e


gerenciar recursos, esse Data Source é configurado em algum arquivo de
configurações ou console administrativo do servidor, e disponibilizado para a aplicação
via JNDI Global.

Tendo esse Data Source configurado, qualquer aplicativo pode fazer seu “lookup” e
utilizá-lo para abrir uma sessão com a base de dados. Quando isso ocorre em um
servidor JEE, ele ainda pode gerenciar todo o controle transacional (através de JTA –
Java Transaction API) peculiar da especificação.

Reduzindo ao nosso mundo, no escopo da utilização da JPA, vimos anteriormente que


devemos configurar um Data Source no persistence.xml, e é através desse Data
Source que a JPA irá persistir os objetos.

Veja novamente um trecho de um arquivo persistence.xml:


<persistence>
50

<persistence-unit name="curso">
<jta-data-source>java:/meuDataSource</jta-data-source>
</persistence-unit>
</persistence>

Quando utilizamos a tag <jta-data-source> estamos definindo o endereço JNDI


do Data Source que a nossa unidade de persistência irá utilizar.

Obs.: note que podemos ter outra unidade de persistência com um Data Source
diferente, e utilizar um Entity Manager para cada unidade.

Nos diversos servidores de aplicação disponíveis no mercado, cada um tem sua forma
específica de configurar Data Sources. Em servidores mais poderosos, como por
exemplo o Oracle WebLogic, essa configuração é feita via console administrativo.

Infelizmente o console administrativo do JBoss ainda não chegou nesse nível de


maturidade, o que torna necessária a configuração de Data Sources via arquivo XML.

No JBoss Application Server podemos criar Data Sources no escopo do servidor ou no


escopo da aplicação.
• Criar Data Sources no JBoss, escopo de servidor:
o Criar um arquivo de final “-ds.xml” com as configurações do DS (ex.:
curso-ds.xml)
o Copiar o JAR do Driver JDBC da base de dados configurada para a
pasta “lib” do seu Server Configuration
o Copiar o arquivo “-ds.xml” para a pasta “deploy” do seu Server
Configuration
o Pronto, o DS estará disponível para qualquer aplicação
• Criar Data Souces no JBoss, escopo de aplicação:
o Criar um arquivo de final “-ds.xml” com as configurações do DS (ex.:
curso-ds.xml)
o Copiar o JAR do Driver JDBC da base de dados configurada para a
pasta “APP-INF/lib” do seu EAR
o Copiá-lo para a pasta “META-INF” do seu EAR
o O DS estará disponível para esta aplicação específica

Exemplo de –ds.xml:
<datasources>
<local-tx-datasource>
<jndi-name>cursoDS</jndi-name>
<connection-url>jdbc:mysql://localhost:3306/curso</connection-url>
<driver-class>com.mysql.jdbc.Driver</driver-class>
<user-name>root</user-name>
<password>root</password>
<exception-sorter-class-name>
org.jboss.resource.adapter.jdbc.vendor.MySQLExceptionSorter
</exception-sorter-class-name>
<metadata>
<type-mapping>mySQL</type-mapping>
</metadata>
</local-tx-datasource>
</datasources>

Atenção
As configurações de data source são específicas para cada fornecedor de base
de dados, porém o JBoss já vem com um exemplo de configuração para as
principais bases de dados, basta modificá-los e utilizá-los. Estes exemplos
residem na pasta “docs/examples/jca”, a partir do diretório raiz JBoss.
51

8. Principais Design Patterns JEE


Em 1994, um grupo de quatro experientes desenvolvedores e arquitetos de software
(Erich Gamma, Richard Helm, Ralph Johnson e John Vlissides), após reunirem suas
experiências sobre sistemas orientados a objeto, acabaram criando o livro “Design
Patterns: Elements of Reusable Object-Oriented Software”, que é o mais
renomado livro sobre design patterns conhecido até os dias de hoje. Este grupo foi
nomeado como “Gang of Four” (GoF).

Após a publicação deste livro é que se teve o conceito de Design Patterns bem
definido e adotado mundialmente. Design patterns são padrões de desenvolvimento
bem sucedidos e reutilizáveis, criados para resolver os problemas arquiteturais mais
comuns do desenvolvimento orientado a objetos através de uma solução testada e
bem sucedida nos diversos aspectos. O livro da GoF define 23 patterns que resolvem
grande parte dos problemas estruturais de sistemas orientado a objeto.

Porém, quando lançada a arquitetura Java EE, os próprios arquitetos da Sun


identificaram na sua estrutura algumas limitações e pontos de atenção que, se não
estudados devidamente, poderiam causar muitos problemas para as aplicações
baseadas no modelo JEE. Após esse estudo, foi lançado o conceito de “J2EE
Patterns”, que são os padrões de projeto específicos para um desenvolvimento
correto sobre a estrutura JEE. Este conceito foi introduzido no livro “Core J2EE
Patterns: Best Practices and Design Strategies”, escrito pelos mais renomados
arquitetos de software da Sun Microsystems, desenvolvedores do JEE (Deepak Alur,
John Crupi e Dan Malks).

8.1. Singleton
O Design pattern “Singleton” não é um pattern JEE, mas sim um pattern do GoF. O
estudo dos design patterns da GoF não é escopo deste livro, porém este pattern em
especial é utilizado como parte de diversos J2EE Patterns, portanto é importante o seu
entendimento.

Como o próprio nome do pattern indica, o seu objetivo é manter apenas uma instância
de um objeto na memória, e todas as classes que utilizarem este objeto estarão
utilizando a mesma instância dele.

Ele traz diversos benefícios, como menor consumo de memória garantindo que existe
apenas uma instância do objeto, fornecimento de um único ponto de acesso ao objeto
e maior flexibilidade que métodos estáticos, pois permite polimorfismo.

Procedimentos para implementação:


• Criar um atributo privado e estático do tipo da própria classe
• Criar um construtor privado, para não permitir que outras classes instanciem
seu objeto
• Cria um método estático getInstance, que irá retornar a única instância que
existirá

Exemplo de Implementação:
public class Configuracoes {

//atributo com a unica instância existente


private static Configuracoes configuracoes;
52

//Construtor privado
private Configuracoes(){
// qualquer código de inicialização da instância
}

//método que retorna a instância singleton


public static Configuracoes getInstance() {
if (configuracoes == null) {
configuracoes = new Configuracoes();
}
return configuracoes;
}

8.2. Service Locator


Aqui iniciamos os J2EE patterns. Com o uso de sistemas distribuídos e serviços de
nomes, tornou-se muito comum em sistemas J2EE a necessidade de localização de
recursos na rede ou no disco, tais recursos que poderiam constantemente ser
trocados de endereço ou domínio.

Nos capítulos sobre RMI e EJB vimos constantemente a necessidade de lookup de


recursos no JNDI, porém não é uma boa solução deixar esse serviço dentro da lógica
do negócio, mesmo porque ele não representa uma questão de negócio, e sim uma
questão de infra-estrutura.

Para resolver esta questão arquitetural foi desenvolvido o pattern “Service Locator”,
que torna-se uma camada responsável por localizar recursos e serviços, abstraindo a
localização física dos mesmos.

Procedimentos para implementação:


• Criar uma classe para representar o Service Locator, utilizando o padrão
Singleton
• Fornecer um método para retornar cada tipo de recurso desejado, através de
alguma identificação do mesmo

Exemplo de Implementação:
public class ServiceLocator {

//atributo de instância singleton


private static ServiceLocator serviceLocator;
private Context initialContext;

//construtor singleton
private ServiceLocator() throws ServiceLocatorException{
Properties properties = new Properties();
properties.put(Context.INITIAL_CONTEXT_FACTORY,
"org.jnp.interfaces.NamingContextFactory");
properties.put(Context.PROVIDER_URL, "jnp://127.0.0.1:1099");

try {
this.initialContext = new InitialContext(properties);
} catch (NamingException e) {
throw new ServiceLocatorException(e);
}
}

//método que retorna a instância singleton


public static ServiceLocator getInstance() throws ServiceLocatorException {
if (serviceLocator == null) {
serviceLocator = new ServiceLocator();
}
53

return serviceLocator;
}

//método que retorna um servico do jndi configurado (um EJB, DataSource, etc...)
public Object getService(String jndiName, Class clazz) throws
ServiceLocatorException {
try {
Object ref = initialContext.lookup(jndiName);
return PortableRemoteObject.narrow(ref, clazz);
} catch (NamingException e) {
throw new ServiceLocatorException(e);
}
}

Com isso, garantimos que os recursos que dependem de localização serão criados
através de um único ponto do sistema, deixando o negócio transparente da localização
física dos recursos, abstraindo as chamadas de lookup e particularidades de JNDI.

Poderíamos ainda incrementar nosso Service Locator com arquivos “properties”


associando beans a endereços, ou ainda implementar um mecanismo de cache.

8.3. Business Delegate


O papel do Business Delegate é encapsular as chamadas aos objetos de negócio. O
Business Delegate pode localizar o recurso na rede, obter o Stub e invocar suas
operações, podendo também tratar error de i/o remoto.

O Business Delegate remove o acoplamento entre a camada cliente e os objetos de


negócio. Desta forma, a camada cliente não precisa se preocupar com localização de
objetos, pode reduzir o tráfico na rede agrupando chamadas a objetos e permite ainda
a implementação de algum mecanismo de cache.
54

Procedimentos para implementação:


• Criar uma classe para representar o Business Delegate, contendo como
variável de instância o objeto de negócio que ele encapsulará.
• Fornecer um construtor público, que irá localizar o recurso através do Service
Locator e guardá-lo na variável de instância.
• Fornecer um método para encapsular cada método de negócio que será
chamado (não precisa ser necessariamente um-para-um, sempre que possível
pode-se, por exemplo, encapsular duas chamadas ao objeto de negócio em
apenas uma chamada ao Business Delegate).

Exemplo de Implementação:
public class LocadoraDelegate {

private Locadora locadora;

private static final Class clazz = Locadora.class;

public LocadoraDelegate() throws DelegateException {


try {
this.locadora =
(Locadora)ServiceLocator.getInstance().getService("java:/Locadora", clazz);
} catch(ServiceLocatorException e) {
throw new DelegateException(e);
} catch(RemoteException e) {
throw new DelegateException(e);
}
}

public int alugarFilme(int id) throws DelegateException {


try {
int protocolo = locadora.alugarFilme(id);
return protocolo;
} catch(RemoteException e) {
throw new DelegateException(e);
}
}
}

8.4. Session Facade


Quando distribuímos objetos pela rede, expomos suas interfaces de negócio ao
cliente, consequentemente criando uma granularidade muito alta de serviços. Isso
força o cliente a realizar muitas chamadas remotas a um objeto distribuído para
realizar uma determinada operação, aumentando o tráfego na rede.

Além do problema da rede, temos também uma falta de padronização nas chamadas
de negócio provenientes de diferentes clientes e alto acoplamento entre o cliente e os
objetos distribuídos.

Com a criação de um Session Facade, transformamos muitas chamadas remotas


complexas em uma única chamada remota, de grossa granularidade, a qual encapsula
as chamadas finas que se tornarão locais, diminuindo assim o tráfego na rede.

• Sem Session Facade:


55

• Com Session Facade:

Como observamos nos diagramas acima, antes da criação do Session Facade, um


determinado cliente realiza três chamadas remotas para realizar uma determinada
funcionalidade. Após a criação do Session facade, que também é um objeto remoto (o
nome “Session” vem de “Session Bean”), é realizada uma única chamada remota, de
grossa granularidade e baixa complexidade, onde o Session Facade realiza três
chamadas que tornam-se locais, pois ele vive “no mesmo local” que os objetos
remotos.

Obs.: existem dois tipos de Session Facade: Stateless e Stateful, que analogamente
são criados a partir dos respectivos Session Beans, para funcionalidades de negócio
sem estado e com estado respectivamente.
56

8.5. Data Transfer Object (DTO)


Na especificação EJB 2.1 e anteriores, havia o conceito de Entity Beans, onde
entidades persistentes eram implementadas como componentes de negócio EJB,
possuindo interfaces de negócio, consequentemente Stub, Skeleton e operação
remota. Devido a essa arquitetura, quando um cliente solicitava uma entidade ao
container EJB, esta entidade era retornada como um objeto remoto, e cada interação
com a mesma resultava em uma chamada custosa ao servidor, deteriorando a
performance do sistema.

Para resolver este problema foi criado o pattern Data Transfer Object (conhecido
também por outros nomes como Transfer Object ou Value Object). O pattern consiste
na criação de um POJO contendo os atributos da entidade ou qualquer outro conjunto
de informações, o qual será trafegado para o cliente ao invés do Entity Bean. Desta
forma o cliente recebe um objeto java simples, preenchido com os dados relevantes da
chamada realizada, sem precisar realizar mais chamadas remotas.

A partir da criação da versão EJB 3.0, com a introdução da Java Persistence (JPA),
muitos autores discutem sobre a necessidade do uso desse pattern, pois como agora
as entidades já são POJOs, o servidor pode retorná-las diretamente ao cliente como
entidades desacopladas, ou seja, elas já seriam o DTO.

Porém, ainda assim há cenários em que seria relevante o uso do pattern DTO.
Imagine que você tem uma entidade JPA com oitenta campos, porém sua tela
necessita apenas de três deles, ou ainda que sua tela precise de informações
provenientes de três entidades distintas, ou então você não deseja nenhum
acoplamento entre sua tela e as entidades de negócio. Reflita sobre o uso do DTO
nesses casos.

9. JMS e Message Driven Beans


Ao decorrer deste livro estudamos alguns componentes da especificação JEE, como
Stateless Session Beans, Stateful Session Beans e Entidades JPA. Porém, todos eles
têm uma característica em comum: são síncronos. Quando executamos uma operação
da interface de negócio de um Session Bean, nós ficamos aguardando a resposta.
Outra característica é o forte acoplamento, pois um cliente precisa conhecer bem o
componente remoto que está invocando, pois ele é dependente de sua interface de
negócio.

Porém, em alguns casos seria muito interessante possuir um sistema assíncrono e


desacoplado para troca de informações e este é o papel dos Message Driven Beans.

9.1. O que é Mensageria ?


Dentro de uma aplicação, existem diversas de se trocar informações entre
componentes. Por exemplo, no RMI a troca de mensagens é feita via chamadas de
métodos sobre algum protocolo de rede. O componente cliente precisa conhecer cada
componente remoto que invoca e aguardar sua resposta para prosseguir o
processamento.

O componente cliente é completamente dependente do recipiente, pois ele só


funcionará se o recipiente estiver online e pronto para responder a chamada. Podemos
fazer uma analogia a uma ligação telefônica: quando ligamos para uma pessoa e esta
57

pessoa atende, estamos em uma chamada síncrona, ótimo. Porém, e se o destinatário


não estiver disponível ? Não seria ótimo deixar uma mensagem na secretária
eletrônica ? Alguns requisitos não esperam que você simplismente perca a ligação.
Você gostaria que seu site de comércio eletrônico deixasse de efetuar vendas apenas
porquê o servidor do financeiro caiu ?

Assim como no exemplo citado, precisamos de um elemento intermediário para


guardar a mensagem e entregá-la ao recipiente assim que ele estiver disponível (no
caso acima, a “secretária eletrônica”, no caso dos sistemas de mensageria, o “MOM”),
bem como a garantia de que a mensagem será entregue.

9.2. MOM – Message Oriented Middleware


Como exemplificado no tópico anterior, o MOM faz o papel da secretária eletrônica.
Ele é o software responsável por receber as mensagens do produtor e disponibilizá-las
ao consumidor. Ele garante que não haverá perda de mensagens, pois se o receptor
não estiver disponível, ele guarda a mensagem até que o mesmo volte à ativa e
consuma a mensagem.

O MOM é independente de protocolo, a forma com que ele recebe ou envia as


mensagens não é importante. Podemos, por exemplo, dizer que um servidor de email
é um MOM, pois ele recebe emails (via SMTP) e os disponibiliza para os destinatários
(via POP3 ou IMAP). Cada mensagem possui sua estrutura específica, composta por
headers, metadados e conteúdo propriamente dito.

Existem no mercado diversos produtos de MOMs provenientes de diversos


fabricantes:
• JBossMQ (ou JBoss Messaging – versão 5.x)
• IBM MQSeries
• Tibco Rendezvous
• SonicMQ
• ActiveMQ
• outros...

A partir da especificação EJB 3.0, todos os servidores de aplicação são obrigados a


fornecer um MOM embutido e sua implementação (Service Provider – SPI) para
acessá-lo através da JMS API.
58

9.3. JMS
A sigla JMS significa Java Messaging Services. O JMS é uma API da plataforma Java
EE que permite ao desenvolvedor manipular MOMs.

De maneira macro percebemos que todos os MOMs possuem muito em comum.


• Um mecanismo para recepção de mensagens.
• Um mecanismo para armazenamento e endereçamento da mensagem.
• Um mecanismo para entrega (delivery) das mensagens.

Até mesmo a secretária eletrônica se aplica a esse padrão!

Devido a este comportamento comum, a Sun teve a brilhante idéia de desenvolver


uma API genérica, desacoplada, capaz de manipular qualquer sistema MOM. Lógico
que cada MOM tem seu protocolo específico e sua particularidade de implementação,
porém a JMS fornece uma interface comum para essas implementações.

Obs.: texto muito parecido com o do capítulo sobre JNDI não é mesmo!

O JMS está para os “MOMs” assim como o JNDI está para os “serviços de
nomes e diretórios” e assim como o JDBC está para as “bases de dados”.

Principais características da JMS:


• Baixo acoplamento (através do MOM)
• Mensageria é assíncrona
• Transacional
• Distribuída
• Garantia de entrega
• Auditoria
• Métrica
• Load Balance
• Escalabilidade
• À prova de falha
• Anonimicidade (não importa “quem”, “de onde” nem “quando”)
• Suporte para dois modelos de mensagem:
o Publish/Subscribe (pub/sub) – push (cliente recebe via listener) – Topic
o Point-to-Point (p2p) – pull (cliente solicita recebimento) – Queue
59

9.4. Modelos de Mensagens


9.4.1. Topic (Tópico)
O modelo Topic funciona na arquitetura publish/subscribe, onde a mensagem é
enviada para um canal virtual e todos que assinam este canal recebem a mensagem
(one-to-many).

Principais características:
• Um canal por ter muitos assinantes
• Todos assinantes do canal recebem uma cópia da mensagem
• Um assinante não recebe as mensagens enviadas antes de sua assinatura
• Podem ser implementados ambos os modelos PUSH e PULL.

Obs.: a especificação não garante a ordem em que as mensagens serão consumidas


(uma mensagem que foi enviada depois pode ser consumida antes que uma
mensagem que foi enviada primeiro).

9.4.2. Queue (Fila)


O modelo Queue funciona na arquitetura P2P, onde a mensagem é enviada para um
destino virtual e entregue para quem solicitar um recebimento deste destino.
Diferentemente do topico, a fila entrega a mensagem para apenas um consumidor
(one-to-one).

Principais características:
• Uma fila retém as mensagens até que algum consumidor as retire (ou expirem)
• Apenas um consumidor pode retirar cada mensagem
• Um consumidor pode receber mensagens anteriormente armazenadas na fila
• Podem ser implementados ambos os modelos PUSH e PULL.

Obs.: a especificação não garante a ordem em que as mensagens serão consumidas


(uma mensagem que foi enviada depois pode ser consumida antes que uma
mensagem que foi enviada primeiro).

9.5. Implementando Topics e Queues no JBoss


Nos tópicos anteriores vimos que para utilizar um serviço de mensageria precisamos
dos seguintes recursos:
• MOM
60

• JMS API, para acessar o MOM via Java


• Destino JMS, que pode ser uma Queue ou um Topic

Traduzindo para o “mundo JBoss application Server”, o MOM significa JBoss


Messaging. Desde as primeiras versões até a 4.x o provedor de mensageria
implementado era o chamado JBossMQ. Porém, a partir das versões 5.x foi criado um
novo provedor de mensageria, com algumas melhorias de performance, chamado
JBoss Messaging.

Assim como os Data Sources, os Destinos JMS são criados via arquivos de
configuração XML, que tornam-se artefatos deploiáveis no servidor de aplicações.

No JBoss Application Server podemos criar Destinos JMS no escopo do servidor ou no


escopo da aplicação.
• Criar Destinos JMS no JBoss, escopo de servidor:
o Criar um arquivo de final “-service.xml” com as configurações do
destino (ex.: curso-service.xml)
o Copiar o arquivo “-service.xml” para a pasta “deploy” do seu Server
Configuration
o Pronto, o Destino JMS estará disponível para qualquer aplicação
• Criar Destinos JMS no JBoss, escopo de aplicação (aplicações EAR):
o Criar um arquivo de final “-service.xml” com as configurações do
destino (ex.: curso-service.xml)
o Copiá-lo para a pasta “META-INF” do seu EAR
o O Destino JMS estará disponível para esta aplicação específica

Exemplo de topic–service.xml:
<server>
<mbean code="org.jboss.jms.server.destination.TopicService"
xmbean-dd="xmdesc/Topic-xmbean.xml"
name="curso.jms:service=Topic,name=TesteTopic">
<depends optional-attribute-name="ServerPeer">
jboss.messaging:service=ServerPeer
</depends>
<depends>
jboss.messaging:service=PostOffice
</depends>
</mbean>
</server>

Exemplo de queue–service.xml:
<server>
<mbean code="org.jboss.jms.server.destination.QueueService"
xmbean-dd="xmdesc/Queue-xmbean.xml"
name="curso.jms:service=Queue,name=TesteQueue">
<depends optional-attribute-name="ServerPeer">
jboss.messaging:service=ServerPeer
</depends>
<depends>
jboss.messaging:service=PostOffice
</depends>
</mbean>
</server>

Atenção
O JBoss já vem com um exemplo de destinos JMS ilustrando diversas
possibilidades de configurações, basta modificá-los e utilizá-los. Este exemplo
reside no caminho “docs/examples/jms/example-destinations-service.xml”, a
partir do diretório raiz JBoss.
61

Realizados estes passos, o Destino JMS torna-se disponível para a aplicação JEE
através do JNDI Global. Por padrão do JBoss versões 5.x, localizamos as filas no
namespace “java:/queue/<name>” e os tópicos no namespace “java:/topic/<name>” –
ex: ctx.lookup(“java:/queue/TesteQueue”).

9.6. Entendendo a JMS API


A JMS API fornece uma interface de programação Java comum para trabalhar com os
diversos MOMs. Esta API é organizada da seguinte forma:

O objeto inicial que precisamos ter em mãos é a javax.jms.ConnectionFactory. Ela


utiliza o design pattern de “Factory”, pois ela é responsável por fabricar conexões com
o MOM, porém o modo como essas conexões serão criadas depende do fabricante do
MOM e do servidor em questão, portanto a implementação desta fábrica é diferente
para cada caso.

A maioria dos servidores de aplicação JEE (inclusive o JBoss) fornecem o objeto de


implementação da ConnectionFacoty no seu JNDI Global no momento em que o
servidor inicia, portanto basta fazer o “lookup” da fábrica e já temos uma referência
válida à implementação da ConnectionFactory fornecida pelo servidor de aplicações.

Outro objeto específico do MOM que precisamos ter em mãos é o “Destino JMS” do
qual iremos consumir ou publicar mensagens, que pode ser um Topic ou Queue.

javax.jms.Destination
|- javax.jms.Topic
|- javax.jms.Queue

A implementação deste objeto também é específica do fornecedor do MOM, portanto


todos os servidores JEE fornecem um modo de configurá-lo através de parâmetros
para depois disponibilizá-los no JNDI Global. No caso do JBoss, como ilustrado no
capítulo anterior, definimos as configurações via XML e habilitamos no servidor como
artefatos deploiáveis.

Com a fábrica de conexões em mãos, podemos então criar uma conexão com o MOM,
a javax.jms.Connection. A JMS foi desenhada para ser totalmente segura quanto à
integridade dos dados e operações de envio e recebimento de mensagens, portanto,
assim como uma conexão JDBC, ela funciona com os conceitos de “conexões” e
“sessões”, possuindo todo o suporte transacional, até mesmo two-phase-commit
(dependendo do fabricante).
62

Através da conexão, criamos uma sessão – javax.jms.Session. Neste momento


setamos alguns parâmetros como suporte a transações e modo de notificação de
recebimento (“acknowledge-mode”).

Finalmente podemos então criar produtores (javax.jms.Producer) ou consumidores


(javax.jms.Consumer) de mensagens, através da sessão.

9.7. Publicando e Consumindo Mensagens JMS


Uma aplicação não precisa ser JEE para utilizar os recursos da JMS !

Basta ter disponível um MOM e sua implementação da JMS, então qualquer aplicação
poderá consumir e receber mensagens.

Veja abaixo alguns exemplos de código de publicadores e consumidores JMS


standalone, utilizando o JBoss Application Server apenas como provedor do MOM.

Obs.: para obter acesso às classes de implementação do JBoss Messaging em uma


aplicação standalone, precisamos adicionar os seguintes arquivos JAR no “Build
Path”: jbossall-client.jar e jboss-javaee.jar

9.7.1. Mensagens Tópico – Topic


• Publisher
public class Publicar {

public static void main(String[] args) {


try {
Context ctx = new InitialContext();
ConnectionFactory connectionFactory =
(ConnectionFactory)ctx.lookup("java:/ConnectionFactory");
Destination topic = (Topic)ctx.lookup("java:/topic/TesteTopic");

Connection connection = connectionFactory.createConnection();


Session session = connection.createSession(false,
Session.AUTO_ACKNOWLEDGE);

MessageProducer producer = session.createProducer(topic);

Message msg = session.createObjectMessage("Minha mensagem !");


producer.send(msg);

producer.close();
session.close();
connection.close();

System.out.println("Mensagem enviada.");

} catch (Exception ex) {


ex.printStackTrace();
}
}

• Consumer – modelo pull (consumidor é notificado da mensagem)


public class Consumir implements MessageListener {

public static void main(String[] args) {


try {
Context ctx = new InitialContext();
ConnectionFactory connectionFactory =
(ConnectionFactory)ctx.lookup("java:/ConnectionFactory");
63

Destination topic = (Topic)ctx.lookup("java:/topic/TesteTopic");

Connection connection = connectionFactory.createConnection();


Session session = connection.createSession(false,
Session.AUTO_ACKNOWLEDGE);

MessageConsumer consumer = session.createConsumer(topic);


consumer.setMessageListener(new Consumir());

connection.start();

System.out.println("Escutando Topic...");

while(true) Thread.sleep(1000);

} catch (Exception ex) {


ex.printStackTrace();
}
}

@Override
public void onMessage(Message msg) {
try {
ObjectMessage objectMsg = (ObjectMessage)msg;
System.out.println("Mensagem Recebida: " +
objectMsg.getObject().toString());
} catch (JMSException e) {
e.printStackTrace();
}
}

9.7.2. Mensagens Fila – Queue


• Publisher
public class Publicar {

public static void main(String[] args) {


try {
Context ctx = new InitialContext();
ConnectionFactory connectionFactory =
(ConnectionFactory)ctx.lookup("java:/ConnectionFactory");
Destination queue = (Queue)ctx.lookup("java:/queue/TesteQueue");

Connection connection = connectionFactory.createConnection();


Session session = connection.createSession(false,
Session.AUTO_ACKNOWLEDGE);

MessageProducer producer = session.createProducer(queue);

Message msg = session.createObjectMessage("Minha mensagem !");


producer.send(msg);

producer.close();
session.close();
connection.close();

System.out.println("Mensagem enviada.");

} catch (Exception ex) {


ex.printStackTrace();
}
}

• Consumer – modelo push (consumidor solicita mensagem)


public class Consumir {

public static void main(String[] args) {


64

try {
Context ctx = new InitialContext();
ConnectionFactory connectionFactory =
(ConnectionFactory)ctx.lookup("java:/ConnectionFactory");
Queue queue = (Queue)ctx.lookup("java:/queue/TesteQueue");

Connection connection = connectionFactory.createConnection();


Session session = connection.createSession(false,
Session.AUTO_ACKNOWLEDGE);

MessageConsumer consumer = session.createConsumer(queue);

connection.start();

System.out.println("Aguardando Mensagens...");

while(true) {
Message msg = consumer.receive();
ObjectMessage objectMsg = (ObjectMessage)msg;
System.out.println("Mensagem Recebida: " +
objectMsg.getObject().toString());
}

} catch (Exception ex) {


ex.printStackTrace();
}
}

9.8. JMS e EJB


No capítulo anterior vimos que uma aplicação não precisa ser JEE para utilizar os
recursos da JMS. Porém, ao utilizar o JMS em uma aplicação JEE, conseguimos muito
mais facilidade e robustez, pois possuímos o nosso grande aliado: o Container!
Graças a ele, conseguimos diversos recursos importantes, como por exemplo:
• Suporte transacional no envio da mensagem: se a transação corrente do
EJB que está mandando a mensagem falhar, o envio da mensagem é
revertido.
• Suporte transacional no recebimento da mensagem: se a transação do
MDB que recebe a mensagem falhar, o recebimento da mensagem é revertido.
• Message Driven Beans: temos possibilidade de criar um novo tipo de bean,
cuja única finalidade é receber mensagens JMS.
• Injeção de Dependência e Inversão e Controle: o container injeta os
recursos de ConnectionFactory e Destination necessários e controla os
mesmos.

9.8.1. EJB – Publicando Mensagens


Para publicar mensagens JMS em um aplicativo JEE, utilizamos os famosos Session
Beans (Stateless ou Stateful). Desta forma podemos utilizar os recursos transacionais
e injeção de dependência do container.

Ao utilizar da injeção de dependência para criar uma ConnectionFactory e um


Destination, o adaptador JMS automaticamente participará do contexto de transação
atual do EJB. A partir de então, qualquer operação de envio de mensagem (send) será
transacional no contexto de transação atual. Como conseqüência disso, qualquer
mensagem enviada será revertida caso tenha ocorrido rollback na transação do EJB.

Atenção
Na implementação do MOM fornecida pelo JBoss, é disponibilizado no JNDI Global
duas referências ao javax.jms.ConnectionFactory:
65

• Referência com Local-Transaction:


@Resource(mappedName="java:/ConnectionFactory")
private ConnectionFactory connectionFactory;

...

Session session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE);

O objeto disponibilizado em “java:/ConnectionFactory” suporta apenas “Local-


Transaction”, ou seja, apenas a transação manipulada pela próprio objeto
javax.jms.Session de forma explicita através de session.commit() e session.rollback().
Isto significa que ele NÃO participará da transação corrente gerenciada pelo container.

• Referência com JTA-Transaction:


@Resource(mappedName="java:/JmsXA")
private ConnectionFactory connectionFactory;

...

Session session = connection.createSession(true, Session.AUTO_ACKNOWLEDGE);

Quando utilizamos EJB, o container controla as transações distribuídas através da


JTA, que é propagada para cada resource adapter que suporta transações JTA. O
objeto disponibilizado em “java:/JmsXA” suporta “JTA-Transaction”, isto significa que
ele participará da transação corrente gerenciada pelo container.

Portanto, para usufruir dos recursos de suporte transacional no envio e recebimento


da mensagem gerenciado pelo container, deve-se utilizar a factory “java:/JmsXA”.

Exemplo de envio de mensagens JMS via Session Bean (com JTA-Transaction):


@Stateless
public class TesteJMS implements TesteJMSRemote {

@Resource(mappedName="java:/JmsXA")
private ConnectionFactory connectionFactory;

@Resource(mappedName="queue/TesteQueue")
private Destination testeQueue;

public void enviarMensagem(String msg) throws JMSException {


Connection connection = connectionFactory.createConnection();
Session session = connection.createSession(true,
Session.AUTO_ACKNOWLEDGE);

MessageProducer producer = session.createProducer(testeQueue);

Message mensagem = session.createObjectMessage(msg);


producer.send(mensagem);

producer.close();
session.close();
connection.close();

System.out.println("Mensagem enviada via EJB !");


}

9.8.2. EJB – Message Driven Beans


Para receber mensagens JMS em um aplicativo JEE, utilizamos um componente
especial chamado “Message Driven Bean”, ou de forma abreviada “MDB”. Os MDBs
são componentes EJB especiais muito parecidos com os Stateless Session Beans,
porém são criados especialmente para consumir mensagens de filas JMS.
66

Atenção
É terminantemente proibido consumir mensagems JMS através de um Session
Bean utilizando os códigos do capítulo 9.7 ! Os métodos consumer.receive()
bloqueam a thread até que uma mensagem apareça, e é proibido interferir na
thread ou criar threads em EJBs, pois o container é quem os controla. Se o
desenvolvedor altera o comportamento da thread de um EJB, o container fica
impossibilitado de controlar o ciclo de vida do bean e garantir o seu correto
funcionamento. Não invente ! ☺

Todo MDB trabalha no modelo PULL, ou seja, ele é executado sempre que chega uma
nova mensagem na fila. Quanto ao ciclo de vida do MDB, da mesma forma que o
Stateless Session Bean, o container trabalha com o “Pool de Instâncias”. Portanto, a
cada nova mensagem que chega na fila, o container elege uma instância do MDB para
consumi-la.

Atenção
A ordem de consumo das mensagens não é garantida! Por exemplo, uma
mensagem enviada antes pode ser consumida posteriormente a uma mensagem
enviada depois dela. Portanto, a lógica implementada nos MDBs não pode
contar com um comportamento seqüencial de mensagens.

Exemplo de Message Driven Bean:


@MessageDriven(
activationConfig = {
@ActivationConfigProperty(propertyName = "destinationType",
propertyValue = "javax.jms.Queue"),
@ActivationConfigProperty(propertyName="destination",
propertyValue="queue/CursoQueue"),
@ActivationConfigProperty(propertyName="acknowledgeMode",
propertyValue="Auto-acknowledge")
}
)
public class TesteMDB implements MessageListener {

public void onMessage(Message message) {


ObjectMessage objectMsg = (ObjectMessage)message;
try {
System.out.println("Mensagem Recebida: " +
objectMsg.getObject().toString());
} catch (JMSException ex) {
ex.printStackTrace();
}
}

Após consumir e processar a mensagem, a instância do MDB retorna ao pool para ser
utilizada posteriormente em uma nova solicitação do container. Sim, o container
também é capaz de solicitar a várias instâncias do MDB o consumo simultâneo de
duas ou mais mensagens que chegaram na fila (exatamente por isso a ordem de
consumo das mensagens não é garantida).

10. Controle Transacional na Plataforma JEE


O controle transacional é um dos recursos mais importantes de qualquer sistema de
grande porta, e conhecê-lo corretamente é um requisito essencial para o bom
programador.
67

10.1. O que são Transações ?


Primeiramente, devemos ter em mente um bom entendimento sobre o que são
transações. No mundo dos softwares, transações nada mais são que um conjunto de
operações que formam uma “unidade de trabalho”. Um exemplo claro é em um
sistema de caixa eletrônico: quando se deseja transferir dinheiro de uma conta para
outra, o sistema precisa retirar o dinheiro da conta de origem e em seguida depositar
na conta de destino. A unidade de trabalho, ou transação, só é considerada concluída
com sucesso se estas duas operações forem bem sucedidas. Se ocorrer uma falha de
sistema durante o depósito na conta do destinatário, o dinheiro não pode ser retirado
do cedente, senão os dados se tornarão inconsistentes.

Toda e qualquer transação precisa ser A.C.I.D.:


 Atômica: “Ou executa tudo ou não executa nada”. Se ocorrer um erro em
qualquer uma das etapas, então todas as anteriores devem ser revertidas.
 Consistente: “Não se pode retirar dinheiro de uma conta e não depositar
na outra”. Os dados que uma transação manipula devem se manter
íntegros, consistentes, sem efeitos colaterais.
 Isolada: “Uma transação não pode influenciar na outra”. Os dados que uma
transação manipula não devem ser afetados por outra transação ou
processo até que a primeira termine.
 Durável: “Se o sistema quebrar durante uma transação, não acontecerá
nenhum efeito colateral”, ou seja, nenhum dado será corrompido, perdido
ou alterado parcialmente.
68

Fluxograma básico de uma transação computacional:

BEGIN TRANSACTION

Operação 1

Operação 2
...
Operação N

[erro] [sucesso]
ROLLBACK COMMIT

 BEGIN – marca o início da transação


 COMMIT – indica que tudo correu bem, e as alterações podem ser
refletidas nos respectivos dispositivos (base de dados, fila JMS, etc)
 ROLLBACK – indica que ocorreu algum erro, e desfaz todas as
alterações realizadas até então

10.2. Níveis de Isolamento de Transações


Quando citamos os atributos A.C.I.D. das transações, nos deparamos com um atributo
particularmente especial, o “I” – Isolada. Vimos que uma boa transação deve ser
isolada, porém este é um comportamente decisivo no que tange à performance do
sistema, pois se a transação for totalmente isolada, então sempre teremos que
terminar uma transação para que outra comece ? Imagine um sistema de grande
acesso, em que 100.000 usuários efetuam transações simultaneamente, não seria
viável que cada um esperasse o outro acabar a transação para iniciar outra. Para
minimizar este impacto, existe o conceito de Níveis de Isolamento da transação.

Estes níveis de isolamento, são caracterizados por três efeitos colaterias conhecidos
por Dirty Reads, Repeatable Reads e Phantom Reads.
 Dirty Reads – traduzidos como “leitura suja”, acontece quando uma
transação lê dados que ainda não foram comitados por outra
transação. Exemplo:
-- usuário 1:
INSERT INTO PUB.state VALUES ('SP', 'São Paulo', 'South');

-- usuário 2, seleciona estados do Sul:


SELECT * FROM PUB.state WHERE Region = 'South';

-- usuário 1 lembrou que SP não é da região sul:


ROLLBACK;
69

 Non-Repeatable Reads – ocorrem quando são feitas duas leituras


dentro da mesma transação, sendo que entre estas duas leituras, outra
transação alterou algum dado desta leitura, então são retornados
dados diferentes nas duas leituras. Ela difere da Dirty Read pois
aqui são lidos dados comitados. Exemplo:
-- usuário 1, retorna "AL - Alabama - South":
SELECT * FROM PUB.state WHERE Region = 'South';

-- usuário 2 atualiza nome do estado AL p/ Alagoas:


UPDATE PUB.state SET "State-Name" = 'Alagoas' WHERE State = 'AL';
COMMIT;

-- usuário 1, retorna "AL - Alagoas - South":


SELECT * FROM PUB.state WHERE Region = 'South';

 Phantom Reads – ocorre quando são feitas duas leituras dentro da


mesma transação, sendo que entre estas duas leituras, outra
transação inseriu novos dados, então são retornados mais registros
nas leituras subseqüentes. Exemplo:
-- usuário 1, retorna "AL - Alabama - South":
SELECT * FROM PUB.state WHERE Region = 'South';

-- usuário 2 insere um novo estado na região sul:


INSERT INTO PUB.state VALUES ('PR', 'Paraná', 'South');
COMMIT;

-- usuário 1 retorna "Alabama" e "Paraná":


SELECT * FROM PUB.state WHERE Region = 'South';

Conhecidos estes efeitos colaterais, todo dispositivo que suporta transações deve
trabalhar com um determinado nível de isolamento pré-definido ou até permitir que o
desenvolvedor escolha o nível de isolamento desejado. O que determina maior
isolamento é a quantidade e duração dos LOCKS que o dispositivo fará nos seus
dados. Consequentemente, quanto maior a utilização de LOCKS, menos transações
poderão trabalhar nos mesmos dados simultaneamente, fazendo com que a
performance do sistema caia em função disso.

Tipos de LOCKS implementados nos dispositivos transacionais:


 Read Locks – define que enquanto uma transação lê um registro,
nenhuma outra transação poderá alterá-lo. Previne Non-Repeatable
Reads.
 Write Locks – são usados para atualização, onde antes de alterar
um registro, o sistema solicita um lock deste registro, não permitindo
que outra transação altere o mesmo até que a primeira termine. A
outra transação pode ler, mas não pode alterar.
 Exclusive Write Locks – são parecidos com o Write Lock, com a
diferença que as outras transação não podem sequer ler os registros
antes que a primeira termine.
 Snapshots – quando uma transação inicia, é fornecida uma
visualização congelada dos dados. Pode causar problemas, pois no
momento em que o snapshot é tirado, os dados já não estão
sincronizados em tempo-real.

Conhecendo os diversos tipos de LOCKS que um dispositivo pode utilizar para garantir
o isolamento das transações, podemos então estudar os níveis de isolamento
disponíveis nos dispositivos transacionais:
70

 Read Uncommited – a transação pode acessar dados ainda não


comitados por outra transação. Permite todos os problemas
descritos anteriormente.
“Lerei todo e qualquer dado !”.
 Read Commited – a transação pode acessar apenas dados
comitados. Previne Dirty Reads, porém ainda permite Non-
Repeatable Reads e Phantom Reads.
“Lerei apenas dados comitados !”.
 Repeatable Read – uma transação não pode alterar dados que
estão sendo lidos por outra transação. Previne Dirty Reads e Non-
Repeatable Reads, porém ainda permite que ocorram Phantom
Reads.
“Lerei sempre os mesmos dados !”.
 Serializable – uma transação não pode gravar nem ler dados
acessados por outra transação. Previne todos os problemas citados,
e é o nível mais restritivo.
“Enquanto eu leio ninguém lê nem mexe !”.

Não é obrigatório que todos os dispositivos transacionais suportem todos os quatro


níveis de isolamento, cabe ao fornecedor informar na documentação este suporte.

10.3. Transações e EJB 3.0


Na plataforma JEE, todo o controle transacional é cuidado pelo container. Não
precisamos nos preocupar em realizar o “Begin”, nem fazer rollback, nem commit e
muito menos controlar conexões e sessões de acesso a base de dados. Basta utilizar
as operações fornecidas pelo Entity Manager, que já vem provido disso tudo, graças a
harmoniosa união da JPA com a especificação EJB 3.0.

O desenvolvedor precisa apenas demarcar as transações, que então o container fará


da maneira que ele solicitou. Estas demarcações de transação definem o tipo de
propagação da transação entre diferentes chamadas de métodos entre os EJBs.

Este tipo de controle transacional controlado inteiramente pelo container é chamado


de CMT (Container Managed Transaction). Podemos utilizar o controle manual de
transação, o chamado BMT (Bean Managed Transaction), porém não é a solução ideal
para a maioria dos casos, pois a idéia da especificação EJB é que o container tenha a
maioria das responsabilidades de infra estrutura. Somente em casos muito específicos
devemos optar pelo controle manual das transações.

10.3.1. Transações CMT


A especificação EJB define seis tipos distintos de propagação de transações
gerenciadas pelo container:

 NOT_SUPPORTED
A transação do chamador não é propagada e o bean é executado
sempre sem transação.
71

 SUPPORTS
Se o chamador tiver transação, ela é propagada. Caso contrário, o bean
é executado sem transação. "Acompanha o chamador".

 REQUIRED
Se o chamador tiver transação, ela é propagada. Caso contrário, o bean
cria uma transação própria. "Bean sempre tem uma transação".

 REQUIRES_NEW
O bean sempre cria uma transação própria, independentemente do
chamador ter ou não transação.
72

 MANDATORY
Bean só executa se o chamador tiver transação. Caso contrário, lança
EJBTransactionRequiredException.

 NEVER
Bean só executa se o chamador NÃO tiver transação. Caso contrário,
lança EJBException.
73

Por padrão, toda e qualquer operação da interface de negócio de um EJB possui


controle transacional do tipo REQUIRES_NEW, ou seja, se a operação não tiver
anotações de transação, ela será REQUIRES_NEW por default. Veja os exemplos
abaixo:

Obs.: recomendo agora que o leitor volte ao capítulo 7.3 e leia novamente o tópico
sobre “Contextos de Persistência”, com certeza tudo fará mais sentido agora ☺.

Exemplo de demarcação de transação:


@Stateless
public class Teste implements TesteRemote {

@PersistenceContext()
private EntityManager em;

@TransactionAttribute(TransactionAttributeType.REQUIRES_NEW)
public void testar() {
Empresa e = new Empresa();
e.setNome("Holms");
em.persist(e);
}

No exemplo acima, o método testar declara um controle de transação gerenciada pelo


container (CMT) do tipo REQUIRES_NEW, o que significa que independente de quem
o invocar, ele sempre criará uma nova transação. Vale lembrar que podemos
demarcar controle transacional apenas para operações da interface de negócio do
bean. Métodos utilitários não podem desfrutar deste recurso.

10.3.2. Transações e Message Driven Beans


Para os beans ouvidores de filas JMS, o controle transacional também é bem
suportado pelo container. O método listener onMessage pode ser demarcado para
fazer parte de uma transação e ser gerenciado pelo container, porém devemos nos
atentar para algumas restrições:
• Quem é o cliente de um MDB ? Um MDB é assíncrono e não deve conhecer
quem enviou a mensagem, portanto nenhum contexto de transação nem
segurança pode ser propagado de quem enviou a mensagem para o MDB. O
único “cliente” (ou pseudo-cliente) de um MDB é o próprio mecanismo do
MOM.
• Devido ao item acima, no método onMessage podemos utilizar apenas os tipos
REQUIRED ou NOT_SUPPORTED:
o REQUIRED: o método onMessage cria uma transação e executa dentro
de seu contexto. Se a transação reverte (rollback), o consumo da
mensagem também é revertido.
o NOT_SUPPORTED: não habilita controle de transação CMT para o
método onMessage.

Importante
Para os MDBs, quando utilizamos transações gerenciadas pelo container (CMT), a
transação é iniciada antes da chamada do método onMessage, o que permite que, em
caso de rollback, o consumo da mensagem também seja revertido, fazendo com que o
MOM tente entregá-la novamente.
74

Porém, quando utilizamos transações gerenciadas pelo bean (BMT), não é possível
iniciá-la antes da chamada onMessage, portanto neste caso o consumo da mensagem
não poderá ser revertido em caso de falha.

10.3.3. Revertendo Transações


Você deve estar se perguntando, como reverter transações ? Onde eu invoco o
rollback ? Como o próprio nome já diz: CMT – Container Managed Transaction, ou
seja, controlada pelo container. O container sabe que quando ocorre uma exception,
talvez deva reverter a transação.

Citamos “talvez”, pois depende do tipo de exception:

Categoria Exceção Comportamento


- O container empacota a transação como
-
EJBException;
Exceções java.lang.RuntimeException
- O container reverte a transação;
de - javax.ejb.EJBException
- O container mata a instância do bean (sai
Sistema -
do pool);
javax.rmi.RemoteException
- O container registra log.
- O container não empacota, repassa do
Exceções cliente da forma como foi lançada;
de Qualquer outra - O container tenta comitar;
Aplicativo - Não reverte a transação nem mata o
bean.

Quando criamos nossas próprias exceções de negócio, realizando herança na classe


Exception, ela entra na categoria de Exceções de Aplicativo, e como vimos na tabela
anterior, ela não causa rollback da transação. Porém, podemos querer forçar com que
ela cause.
@ApplicationException(rollback = true)
public class SaldoInsuficienteException extends Exception {
public SaldoInsuficienteException() {
super();
}
public SaldoInsuficienteException(String msg) {
super(msg);
}
}

Realmente não queremos que um cliente sem saldo consiga transferir o dinheiro que
não tem. Nestes casos utilizamos a anotação @ApplicationException(rollback =
true), que irá dizer ao container:
“Ok, eu sei que é uma exceção de aplicativo, porém eu quero que ela cause
rollback !”.

10.3.4. Transações BMT


Algumas vezes precisamos ter um controle mais minucioso sobre as transações, onde
um contexto transacional a nível de operação não é suficiente. Podemos ter a
necessidade de comitar ou reverter uma transação no meio de um método, ou então
criar diferentes transações ou mais que uma transação dentro do mesmo método.

Para estes casos, podemos utilizar o controle manual de transações. Logicamente


será mais trabalhoso e propenso a erros, porém necessidade não se escolhe.
75

Todo container fornece no seu JNDI um objeto javax.transaction.UserTransaction, que


nada mais é do que o objeto principal da API implementadora da JTA (Java
Transaction API). Ao obter uma instância deste objeto, ficamos livres para demarcar
programaticamente as barreiras das nossas transações.

Obtendo UserTransaction via uma aplicação JSE cliente:


Context jndiCntx = new InitialContext( );
UserTransaction utx = (UserTransaction)
jndiCntx.lookup("java:comp/UserTransaction");
utx.begin( );
...
...
...
utx.commit( );

Obtendo UserTransaction dentro de um EJB:


@Stateless
@TransactionManagement(TransactionManagementType.BEAN)
public class Teste implements TesteRemote {

@PersistenceContext()
private EntityManager em;

@Resource
UserTransaction utx;

public void testar() {


utx.begin();

try {
Empresa e = new Empresa();
e.setNome("Holms");
em.persist(e);

utx.commit();
} catch (Exception ex) {
utx.setRollbackOnly();
}
}
}

Cuidado
Não confunda, é proibido usar begins e commits de outras APIs (EntityManager,
Connection, etc). Apenas o objeto UserTransaction realiza uma transação distribuída.

11. Referências Bibliográficas


Sun – Introduction to CORBA
http://java.sun.com/developer/onlineTraining/corba/corba.html

Sun – Java RMI Over IIOP


http://java.sun.com/products/rmi-iiop/

Sun – Example of Java IDL


http://java.sun.com/j2se/1.3/docs/guide/idl/jidlExample.html

JavaNB – Portable Object Adapter POA


76

http://doc.javanb.com/javasdk-docs-6-0-en/technotes/guides/idl/POA.html

Sun – RMI-IIOP Programmers Guide


http://java.sun.com/j2se/1.3/docs/guide/rmi-iiop/rmi_iiop_pg.html

EJB3 In Action – Manning


Debu Panda, Reza Rahman, Derek Lane

JBoss In Action – Manning


Javid Jamae, Peter Johnson

Enterprise JavaBeans 3.0, 5th Ed – OReilly


Bill Burke, Richand Monson-Haefel

Core J2EE Patterns: Best Practices and Design Strategies, 2nd Ed. – Prentice
Hall
Deepak Alur, Dan Malks, John Crupi

The Java EE 5 Tutorial, 3rd Ed. – Addison Wesley


Kim Haase

Você também pode gostar