apostila javaee 5 componentes distribuídos ejb 3 e jboss

76

Click here to load reader

Upload: gilberto-holms

Post on 19-Jul-2015

255 views

Category:

Software


17 download

TRANSCRIPT

Page 1: Apostila JavaEE 5 Componentes Distribuídos EJB 3 e JBoss

1

Apostila JavaEE 5 Componentes Distribuídos EJB 3 e JBoss

Autor: Gilberto Augusto T. de A. Holms [email protected] http://gibaholms.wordpress.com/ Última revisão: 03/08/2009

Page 2: Apostila JavaEE 5 Componentes Distribuídos EJB 3 e JBoss

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

Page 3: Apostila JavaEE 5 Componentes Distribuídos EJB 3 e JBoss

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

Page 4: Apostila JavaEE 5 Componentes Distribuídos EJB 3 e JBoss

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.

Page 5: Apostila JavaEE 5 Componentes Distribuídos EJB 3 e JBoss

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/

Page 6: Apostila JavaEE 5 Componentes Distribuídos EJB 3 e JBoss

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.

Page 7: Apostila JavaEE 5 Componentes Distribuídos EJB 3 e JBoss

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.

Page 8: Apostila JavaEE 5 Componentes Distribuídos EJB 3 e JBoss

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:

Page 9: Apostila JavaEE 5 Componentes Distribuídos EJB 3 e JBoss

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.

Page 10: Apostila JavaEE 5 Componentes Distribuídos EJB 3 e JBoss

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; };

Page 11: Apostila JavaEE 5 Componentes Distribuídos EJB 3 e JBoss

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

Page 12: Apostila JavaEE 5 Componentes Distribuídos EJB 3 e JBoss

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.

Arquivo IDL

Cliente

Código Cliente

Stubs

Servidor

Skeletons

Implementação do Objeto

Compilador

idl2Java

Rede TCP/IP - Protocolo IIOP

ORB

ORB

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

Page 13: Apostila JavaEE 5 Componentes Distribuídos EJB 3 e JBoss

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.

Page 14: Apostila JavaEE 5 Componentes Distribuídos EJB 3 e JBoss

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.

Page 15: Apostila JavaEE 5 Componentes Distribuídos EJB 3 e JBoss

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.

Page 16: Apostila JavaEE 5 Componentes Distribuídos EJB 3 e JBoss

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

Page 17: Apostila JavaEE 5 Componentes Distribuídos EJB 3 e JBoss

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.

Page 18: Apostila JavaEE 5 Componentes Distribuídos EJB 3 e JBoss

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):

Page 19: Apostila JavaEE 5 Componentes Distribuídos EJB 3 e JBoss

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.

Page 20: Apostila JavaEE 5 Componentes Distribuídos EJB 3 e JBoss

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

Page 21: Apostila JavaEE 5 Componentes Distribuídos EJB 3 e JBoss

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

Page 22: Apostila JavaEE 5 Componentes Distribuídos EJB 3 e JBoss

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

Page 23: Apostila JavaEE 5 Componentes Distribuídos EJB 3 e JBoss

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:

Page 24: Apostila JavaEE 5 Componentes Distribuídos EJB 3 e JBoss

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”

Page 25: Apostila JavaEE 5 Componentes Distribuídos EJB 3 e JBoss

25

“Variáveis do Sistema” > “Nova”

Digitar a variável “JBOSS_HOME”, informando o diretório onde foi

descompactado o JBoss

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.

Page 26: Apostila JavaEE 5 Componentes Distribuídos EJB 3 e JBoss

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

“Window” > “Preferences”

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

Page 27: Apostila JavaEE 5 Componentes Distribuídos EJB 3 e JBoss

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”

Page 28: Apostila JavaEE 5 Componentes Distribuídos EJB 3 e JBoss

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

Page 29: Apostila JavaEE 5 Componentes Distribuídos EJB 3 e JBoss

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

javax.ejb.Local

• É utilizada quando desejamos acessar o bean da mesma JVM

• É a interface mais lightweight • É realizada uma chamada de método local,

como uma classe normal • A passagem de parâmetros e retorno de

objetos é feita por referência, como o padrão do java

javax.ejb.Remote

• É utilizada quando desejamos acessar o bean remotamente, via CORBA ou RMI

• Realiza uma chamada de método remota, por CORBA, RMI-JRMP ou RMI-IIOP (configurável de acordo com o container utilizado)

Page 30: Apostila JavaEE 5 Componentes Distribuídos EJB 3 e JBoss

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

javax.ejb.MessageDriven • É utilizada quando queremos que o MOM

acesse o bean via mensageria (mais detalhes no capítulo sobre JMS)

javax.jws.WebService

• É utilizada quando desejamos acessar o bean via 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);

Page 31: Apostila JavaEE 5 Componentes Distribuídos EJB 3 e JBoss

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();

}

Page 32: Apostila JavaEE 5 Componentes Distribuídos EJB 3 e JBoss

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

Page 33: Apostila JavaEE 5 Componentes Distribuídos EJB 3 e JBoss

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

Page 34: Apostila JavaEE 5 Componentes Distribuídos EJB 3 e JBoss

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”

Page 35: Apostila JavaEE 5 Componentes Distribuídos EJB 3 e JBoss

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

Page 36: Apostila JavaEE 5 Componentes Distribuídos EJB 3 e JBoss

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

Pacote Sigla Depl. Descriptor Descrição

WAR Web ARchive web.xml Roda no Container Web, suporta as especificações Servlets e JSP

EJB-JAR EJB Java ARchive ejb-jar.xml

Roda no Container EJB, suporta as especificações EJB e JPA (incluir persistence.xml)

EAR Enterprise ARchive application.xml Roda no Container EJB

• Estrutura do WAR

Page 37: Apostila JavaEE 5 Componentes Distribuídos EJB 3 e JBoss

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.

Page 38: Apostila JavaEE 5 Componentes Distribuídos EJB 3 e JBoss

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();

Page 39: Apostila JavaEE 5 Componentes Distribuídos EJB 3 e JBoss

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.

Page 40: Apostila JavaEE 5 Componentes Distribuídos EJB 3 e JBoss

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

Page 41: Apostila JavaEE 5 Componentes Distribuídos EJB 3 e JBoss

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:

Page 42: Apostila JavaEE 5 Componentes Distribuídos EJB 3 e JBoss

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();

Page 43: Apostila JavaEE 5 Componentes Distribuídos EJB 3 e JBoss

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.

Page 44: Apostila JavaEE 5 Componentes Distribuídos EJB 3 e JBoss

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.

Page 45: Apostila JavaEE 5 Componentes Distribuídos EJB 3 e JBoss

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.

Page 46: Apostila JavaEE 5 Componentes Distribuídos EJB 3 e JBoss

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>( );

... }

Page 47: Apostila JavaEE 5 Componentes Distribuídos EJB 3 e JBoss

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

Page 48: Apostila JavaEE 5 Componentes Distribuídos EJB 3 e JBoss

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

Page 49: Apostila JavaEE 5 Componentes Distribuídos EJB 3 e JBoss

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>

Page 50: Apostila JavaEE 5 Componentes Distribuídos EJB 3 e JBoss

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.

Page 51: Apostila JavaEE 5 Componentes Distribuídos EJB 3 e 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;

Page 52: Apostila JavaEE 5 Componentes Distribuídos EJB 3 e JBoss

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();

}

Page 53: Apostila JavaEE 5 Componentes Distribuídos EJB 3 e JBoss

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.

Page 54: Apostila JavaEE 5 Componentes Distribuídos EJB 3 e JBoss

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:

Page 55: Apostila JavaEE 5 Componentes Distribuídos EJB 3 e JBoss

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.

Page 56: Apostila JavaEE 5 Componentes Distribuídos EJB 3 e JBoss

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

Page 57: Apostila JavaEE 5 Componentes Distribuídos EJB 3 e JBoss

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.

Page 58: Apostila JavaEE 5 Componentes Distribuídos EJB 3 e JBoss

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

Page 59: Apostila JavaEE 5 Componentes Distribuídos EJB 3 e JBoss

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

Page 60: Apostila JavaEE 5 Componentes Distribuídos EJB 3 e JBoss

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.

Page 61: Apostila JavaEE 5 Componentes Distribuídos EJB 3 e 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).

Page 62: Apostila JavaEE 5 Componentes Distribuídos EJB 3 e JBoss

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");

Page 63: Apostila JavaEE 5 Componentes Distribuídos EJB 3 e JBoss

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) {

Page 64: Apostila JavaEE 5 Componentes Distribuídos EJB 3 e JBoss

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:

Page 65: Apostila JavaEE 5 Componentes Distribuídos EJB 3 e JBoss

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.

Page 66: Apostila JavaEE 5 Componentes Distribuídos EJB 3 e JBoss

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.

Page 67: Apostila JavaEE 5 Componentes Distribuídos EJB 3 e JBoss

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.

Page 68: Apostila JavaEE 5 Componentes Distribuídos EJB 3 e JBoss

68

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

Operação 1

Operação 2

Operação N

[sucesso]

BEGIN TRANSACTION

ROLLBACK COMMIT[erro]

...

� 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;

Page 69: Apostila JavaEE 5 Componentes Distribuídos EJB 3 e JBoss

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:

Page 70: Apostila JavaEE 5 Componentes Distribuídos EJB 3 e JBoss

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.

Page 71: Apostila JavaEE 5 Componentes Distribuídos EJB 3 e JBoss

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.

Page 72: Apostila JavaEE 5 Componentes Distribuídos EJB 3 e JBoss

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.

Page 73: Apostila JavaEE 5 Componentes Distribuídos EJB 3 e JBoss

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.

Page 74: Apostila JavaEE 5 Componentes Distribuídos EJB 3 e JBoss

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

Exceções de

Sistema

- java.lang.RuntimeException - javax.ejb.EJBException - javax.rmi.RemoteException

- O container empacota a transação como EJBException; - O container reverte a transação; - O container mata a instância do bean (sai do pool); - O container registra log.

Exceções de

Aplicativo Qualquer outra

- O container não empacota, repassa do cliente da forma como foi lançada; - O container tenta comitar; - 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.

Page 75: Apostila JavaEE 5 Componentes Distribuídos EJB 3 e JBoss

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

Page 76: Apostila JavaEE 5 Componentes Distribuídos EJB 3 e JBoss

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