JNDI
Um bean de sessão pode ser empacotado em um arquivo ejb-jar ou dentro de um módulo da aplicação web (.war). Um deployment descriptor opcional para EJBs chamado “ejb-jar.xml” que fornece informações adicionais sobre o deployment pode ser empacotado em um “ejb-jar” ou “.war”.
O arquivo “ejbjar.xml” pode ser empacotado em “WEB-INF/ejb-jar.xml” ou “META-INF/ejb-jar.xml” dentro de um dos arquivos JAR “WEB-INF/lib”, mas não em ambos.
Um bean local ou sem interface que está empacotado num arquivo “.war” é acessível apenas a outros componentes dentro do mesmo arquivo “.war”. No entanto, um bean marcado com @Remote é acessível remotamente independente do seu empacotamento.
O arquivo “ejb-jar” pode ser implantado por si só ou empacotado dentro de um arquivo ".ear". Os beans empacotados neste “ejb-jar” podem ser acessados remotamente.
Podemos acessar este EJB utilizando um nome JNDI global com a seguinte sintaxe:
java:global[/<app-name>]
/<module-name>
/<bean-name>
[!<fully-qualified-interface-name>]
Onde se aplica apenas se o bean de sessão for empacotado com um arquivo ".ear". é o nome do módulo na qual o bean de sessão está empacotado. é o ejb-name do bean empresarial.
Se o bean expõe apenas uma interface de cliente (ou, alternativamente, tem apenas uma visualização sem interface), o bean também está exposto com um nome JNDI adicional usando a seguinte sintaxe:
java:global[/<app-name>]/<module-name>/<bean-name>
O bean sem estado de sessão também está disponível através dos namespaces “java:app” e “java:module”.
Se o ContaSessionBean está empacotado em um arquivo banco.war, então as entradas JNDI são expostas da seguinte forma:
java:global/banco/ContaSessionBean
java:global/banco/ContaSessionBean!org.exemplo.ContaSessionBean
java:app/ContaSessionBean
java:app/ContaSessionBean!org.exemplo.ContaSessionBean
java:module/ContaSessionBean
java:module/ContaSessionBean!org.exemplo.ContaSessionBean
Transações
Um bean pode usar transação programática diretamente no código do bean, na qual é chamado de “transação gerenciada por bean”. Como alternativa, também podemos utilizar uma transação declarativa, dessa forma as transações são gerenciadas automaticamente pelo container, na qual é chamado de “transação gerenciada pelo container”.
Por padrão, transações em um bean são gerenciadas pelo container. A anotação @TransactionManagement é utilizada para declarar se o bean de sessão ou o Message-Driven Bean usa uma transação gerenciada por bean ou por container. O valor desta anotação pode ser CONTAINER, que é utilizada por padrão, ou BEAN.
Uma transação gerenciada por bean requer especificarmos a anotação @TransactionManagement(BEAN) na classe e utilizarmos a interface javax.transaction.UserTransaction. Dentro do método de negócio, uma transação é iniciado com "UserTransaction.begin" e confirmada (comitted) com "UserTransaction.commit". Segue na Listagem 1 um exemplo.
Listagem 1. Exemplo de como iniciar e confirmar transações.
@Stateless
@TransactionManagement(BEAN)
public class ContaSessionBean {
@Resource javax.transaction.UserTransaction tx;
public float depositar() {
//. . .
tx.begin();
//. . .
tx.commit();
//. . .
}
}
Acima verificamos a presença da anotação @Resource. Esta anotação é utilizada para declarar uma referência para um recurso, como por exemplo, um dado, um enterprise bean ou uma entrada de ambiente. A anotação pode ser especificada em uma classe, um método ou um campo. O Container tem a responsabilidade de injetar referências para recursos declarados com @Resource e mapear esta propriedade em um recurso JNDI. Caso haja mais de um recuro sendo utilizado com anotação @Resource deve-se agrupar estas declarações em uma anotação @Resources. Portanto, a anotação @Resource apenas indica que deve-se realizar a injeção de dependência na classe.
Transação gerenciada por Container é o padrão e não requer especificarmos qualquer anotação adicional na classe. O Container EJB implementa todos os protocolos de transação de baixo nível, como por exemplo o "Procotolo de Confirmação em Duas Fases" (Two-Phase Commit Protocol) entre um gerenciador de transações e um sistema de banco de dados ou o provedor de mensagens.
Uma classe bean utilizando uma transação gerenciada por container é semelhante ao código da Listagem 2.
Listagem 2. Exemplo de um bean com transação gerenciada por Container.
@Stateless
public class ContaSessionBean {
public float depositar() {
//. . .
}
}
Não existem anotações adicionais especificadas na classe do bean ou no método.
As alterações aos recursos subjacentes são todos confirmados (Committed) ou revertidos (Rolled Back).
Um bean sem estado de sessão utilizando uma transação gerenciada por container pode usar a anotação @TransactionAttribute para especificar atributos de transação na classe do bean ou no método.
Especificando o TransactionAttribute na classe do bean significa que ela se aplica a todos os métodos aplicáveis do bean. A ausência de TransactionAttribute na classe bean é equivalente à especificação de TransactionAttribute(REQUIRED) no bean.
Os valores de @TransactionAttribute e o significado deles são definidos na Tabela 1.
Valor |
Descrição |
MANDATORY |
Sempre chamado no contexto de transação do cliente. Se o cliente chama com um contexto de transação, então ele se comporta como REQUIRED. Se o cliente chamar sem um contexto de transação, então o container lança uma javax.ejb.EJBTransactionRequiredException. |
REQUIRED |
Se o cliente chamar com um contexto de transação, então ele é propagado para o bean. Caso contrário, o contêiner inicia uma nova transação antes de delegar a chamada para o método de negócio e tenta confirmar (commit) a transação, quando o processo de negócio foi concluído. |
REQUIRES_NEW |
O container sempre inicia um novo contexto de transação antes de delegar a chamada para o método de negócio e tenta confirmar (commit) a transação, quando o processo de negócio foi concluído. Se o cliente chama com um contexto de transação, a transação suspensa é retomada após a nova transação ter sido confirmada (commit). |
SUPPORTS |
Se o cliente chamar com um contexto de transação, então ele se comporta como REQUIRED. Se o cliente chamar sem um contexto de transação, então ele se comporta como NOT_SUPPORTED. |
NOT_SUPPORTED |
Se o cliente chamar com um contexto de transação, então o container suspende e retoma a associação do contexto da transação antes e depois do método de negócio ser chamado. Se o cliente chamar sem um contexto de transação, então nenhum contexto de transação novo é criado. |
NEVER |
O cliente é obrigado a chamar sem um contexto de transação. Se o cliente chamar com um contexto de transação, então o container então ele se comporta como NOT_SUPPORTED. |
Tabela 1. Conjunto de valores permitidos para @TransactionAttribute.
Além das anotações, o elemento "container-transaction" no Deployment Descriptor pode ser utilizado para especificar os atributos da transação. Os valores especificados no Deployment Descriptor sobrescrevem ou complementam os atributos de transação especificados nas anotações.
Apenas os atributos de transação NOT_SUPPORTED e REQUIRED podem ser utilizados para o Message-Driven Bean. Uma mensagem JMS é entregue ao destino final após a transação ser confirmada (commited), assim o cliente não receberá a resposta na mesma transação.
Por padrão os métodos designados com @PostConstruct, @PreDestroy, @PrePassivate, e @PostActivate são executados em um contexto transacional não especificado. O EJB 3.2 especifica que para um bean com estado de sessão gerenciado por container com demarcação de transação, seus métodos podem ter os atributos REQUIRES_NEW e NOT_SUPPORTED.
Invocação Assíncrona
Cada método de um bean de sessão é invocado de forma síncrona, ou seja, o cliente é bloqueado até que o processamento no lado servidor esteja completo e o resultado seja retornado. Um bean de sessão pode marcar um método para invocação assíncrona, e um cliente pode então invocar aquele método de forma assíncrona. Isto retorna o controle para o cliente antes do container enviar a instância para um bean.
As operações assíncronas devem possuir um tipo de retorno “void” ou “Future”. Os métodos com um tipo de retorno “void” são usados para um padrão chamado “fire-and-forget”. A outra versão permite que o cliente recupere um valor do resultado, mas devemos verificar se há exceções, ou a tentativa de cancelar qualquer invocação em andamento.
A anotação @Asynchronous é utilizada para marcar um método especifico (em nível de método) ou todos os métodos (em nível de classe) do bean como assíncrono. Segue um exemplo na Listagem 3de um bean sem estado de sessão que está marcado como assíncrono em nível de classe.
Listagem 3. Exemplo de um bean marcado como Assíncrono em nível de classe.
@Stateless
@Asynchronous
public class MeuBeanAssincrono {
public Future<Integer> adicionaNumeros(int n1, int n2) {
Integer resultado = n1 + n2;
return new ResultadoAssincrono(resultado);
}
}
A assinatura do método retorna Future e o tipo de retorno é AsynchronousResult(Integer). AsynchronousResult é uma nova classe introduzida no EJB 3.1 que envolve o resultado de um método assíncrono num objeto Future. No entanto, por trás das cenas, o valor é retornado e enviado para o cliente.
Adicionando qualquer método novo nesta classe automaticamente os faremos assíncronos também.
Este bean de sessão pode ser injetado e invocado em qualquer componente Java EE conforme mostrado na Listagem 4.
Listagem 4. Exemplo de acesso ao bean criado.
@EJB MeuBeanAssincrono meuBeanAssincrono;
Future<Integer> future = meuBeanAssincrono.adicionaNumeros(10, 20);
Os métodos na API Future são usados para consultar a disponibilidade de um resultado, que é feito através do método “isDone”, ou então cancelar a execução com “cancel(boolean podeInterromperSeExecutando)”.
O contexto de transação do cliente não se propaga para o método de negócio assíncrono. Isto significa que as semânticas dos atributos de transação REQUIRED em um método assíncrono são exatamente os mesmos como REQUIRES_NEW.
No entanto, a propagação do contexto de segurança se comporta da mesma forma para execução do método síncrono e assíncrono.
Temporizadores
O serviço EJB Timer é um serviço gerenciado por container que permite retornos de chamadas serem agendados para eventos baseados no tempo. Estes eventos são agendados de acordo com um cronograma com base no calendário, após um período, ou a intervalos regulares.
Eventos baseados no tempo podem ser agendados de múltiplas formas: através de temporizadores automáticos baseados nos metadados (especificado com @Schedule), programaticamente usando TimerService, métodos marcados com @Timeout e Deployment Descriptors.
O primeiro passo para executar métodos baseados no tempo é marcando qualquer método do bean com @Schedule. Segue na Listagem 5 um exemplo.
Listagem 5. Marcando métodos do bean com @Schedule.
@Stateless
public class MeuTemporizadorExemplo {
@Schedule(hour="*", minute="*", second="*/10")
public void imprimeTempo() {
System.out.println(“Executando método.”)
}
}
Neste código, o método imprimeTempo é chamado todo décimo segundo de todos os minutos e todas as horas. @Schedule também tem campos “year” e “month”, com um valor padrão "*" indicando para executar esse método a cada mês de todos os anos.
O Container EJB lê a anotação @Schedule e automaticamente cria o temporizador.
A Tabela 2 mostra alguns exemplos e seus significados que podem ser especificados através de @Schedule.
@Schedule |
Significado |
hour="1,2,20" |
1 hora da madrugada, 2 horas da madrugada, e 10 horas da noite em todos os dias do ano. |
dayOfWeek="Mon-Fri" |
Segunda, terça, quarta, quinta e sexta-feira, à meia-noite (com base nos valores padrão de hora, minuto e segundo). |
minute="30", hour="4", timezone="America/Los_Angeles" |
Toda manhã às 4:30 de acordo com o horário de Los Angeles. |
dayOfMonth="-1,Last" |
Um dia antes do último dia do mês, à meia-noite. |
Tabela 2. Exemplos e significados para @Schedule.
@Schedules pode ser usado para especificar múltiplos temporizadores.
Podemos notar que não existe a necessidade para uma anotação @Startup aqui, assim como métodos do ciclo de vida não são obrigatórios.
Cada redeploy da aplicação automaticamente deletará e recriará todos os temporizadores.
Podemos facilmente criar temporizadores de intervalo usando os métodos ScheduleExpression.start e ScheduleExpression.end. Também podemos facilmente criar o temporizador de ação única, especificando valores fixos para cada campo conforme ilustra o exemplo abaixo:
@Schedule(year="A", month="B", dayOfMonth="C", hour="D", minute="E", second="F")
Temporizadores não são para tempo real, visto que o container intercala as chamadas para um método de retorno de chamada.
Assim, o método de tempo limite (timed-out) não pode ser invocado exatamente no tempo especificado na criação do temporizador.
O serviço Timer permite a criação programática e cancelamento de temporizadores. Podemos criar temporizadores programáticos usando os métodos “createXXX” no TimerService. O método que será invocado no tempo agendado pode ser o método “ejbTimeout” do objeto TimedObject. Segue na Listagem 6um exemplo.
Listagem 6. Criando temporizadores programaticamente.
@Singleton
@Startup
public class MeuTemporizador implements TimedObject {
@Resource TimerService ts;
@PostConstruct
public void initTimer() {
if (ts.getTimers() != null) {
for (Timer timer : ts.getTimers()) {
timer.cancel();
}
}
ts.createCalendarTimer(new ScheduleExpression().hour("*").minute("*").second("*/10"),
new TimerConfig("meuTemporizador", true));
@Override
public void ejbTimeout(Timer timer) {
//. . .
}
}
}
O método initTimer é um método de retorno de chamada do ciclo de vida que limpa quaisquer temporizadores criados anteriormente e, em seguida, cria um novo temporizador que dispara a cada 10 segundos. O método ejbTimeout, implementado pela interface TimedObject, é invocado toda vez que o limite de tempo (timeout) ocorre. O parâmetro "timer" neste método pode ser usado para cancelar o temporizador, obter informações sobre quando o próximo tempo limite irá ocorrer, obter informações sobre o próprio timer, e recolher outros dados relevantes.
Podemos notar que os temporizadores são criados nos métodos de callback do ciclo de vida, garantindo assim que eles estão prontos antes de qualquer método de negócio no bean seja invocado.
O EJB 3.2 introduz uma novidade na API que é o Timer.getAllTimers, na qual retorna todos os temporizadores ativos associados com os beans no mesmo módulo em que o chamador do bean está empacotado. Estes incluem tanto os temporizadores criados programaticamente e os temporizadores criados automaticamente.
A terceira forma de criar temporizadores é um método que tem as seguintes assinaturas: void () { . . . } ou void (Timer timer) { . . . }. Outra forma seria anotar o método com @Timeout, conforme demonstra o exemplo da Listagem 7.
Listagem 7. Anotando o método com @Timeout.
public class MeuTemporizador {
//. . .
@Timeout
public void timeout(Timer timer) {
//. . .
}
}
A quarta forma de criar temporizadores é marcando um método para execução em uma expiração do temporizador usando o arquivo ejb-jar.xml. Por exemplo, dado o método da Listagem 8.
Listagem 8. Exemplo de um método que será tornado um temporizador.
public class MeuTemporizador {
public void timeout(Timer timer) {
//. . .
}
}
Podemos converter o método "timeout" acima em um método temporizador (timer) adicionando o seguinte fragmento no ejb-jar.xml (Listagem 9).
Listagem 9. Convertendo o método anterior em temporizador.
<enterprise-beans>
<session>
<ejb-name>MeuTemporizador</ejb-name>
<ejb-class>exemplo.MeuTemporizador</ejb-class>
<session-type>Stateless</session-type>
<timer>
<schedule>
<second>*/10</second>
<minute>*</minute>
<hour>*</hour>
<month>*</month>
<year>*</year>
</schedule>
<timeout-method>
<method-name>timeout</method-name>
<method-params>
<method-param>javax.ejb.Timer</method-param>
</method-params>
</timeout-method>
</timer>
</session>
</enterprise-beans>
Temporizadores podem ser criados em beans sem estado de sessão, beans Singleton, e Message-Driven Beans, mas não em beans com estado de sessão. Esta funcionalidade poderá ser adicionada a futuras versões da especificação, mas até a recente versão 3.2 isto não é permitido.
Temporizadores são persistentes por padrão e podem ser feitos não persistentes programaticamente. Segue na Listagem 10 um exemplo.
Listagem 10. Tornando um temporizador como não persistente.
ts.createCalendarTimer(new ScheduleExpression().hour("*").minute("*").second("*/10"),new TimerConfig("meuTemporizador", true));
Alternativamente, “timerConfig.setPersistent(false);” pode ser usado para tornar o temporizador não persistente.
Temporizadores definidos através anotações também podem ser tornados como não persistentes. Segue na Listagem 11um exemplo.
Listagem 11. Criando temporizadores não persistentes utilizando anotações.
@Schedule(hour="*", minute="*", second="*/10", persistent="false")
public void imprimeTempo() {
//. . .
}
Os eventos baseados em temporizadores podem apenas ser agendados nos beans sem estado de sessão e nos beans Singletons.
EJB Lite
O conjunto completo de funcionalidades EJB não precisa ser obrigatório para todos os aplicativos de uma empresa. Como explicado anteriormente, o Web Profile oferece uma pilha razoavelmente completa, composto de APIs padrão, e é capaz de tratar uma ampla variedade de aplicações web.
Os aplicativos voltados para Web Profiles precisarão utilizar transações, segurança e outras funcionalidades definidas na especificação EJB. Dessa forma, o EJB Lite foi criado para atender a essa necessidade.
EJB Lite é um conjunto mínimo de funcionalidades EJB que por sua vez são completas. Nenhuma nova funcionalidade é definida como parte do EJB Lite. O EJB Lite é apenas um subconjunto próprio de todas as funcionalidades do EJB completo. Isso permite que a API EJB possa ser utilizada em aplicações que sejam menores, diferente de uma típica aplicação Java EE.
A Tabela 3 mostra as diferenças entre o EJB 3.2 Lite e o EJB 3.2 completo.
EJB 3.2 |
Lite EJB 3.2 |
Full API |
Session beans |
x |
X |
Message-driven beans |
- |
x |
Java Persistence 2.0 |
x |
X |
Interface de Negócio Local /Sem-Interface |
x |
x |
3.x Remote |
- |
x |
2.x Remote/Home component |
- |
x |
2.x Local/Home component |
- |
x |
JAX-WS Web Service endpoint |
- |
x |
EJB Timer Service Não Persistente |
x |
x |
EJB Timer Service Persistente |
- |
x |
Invocações Assincronas a Session Bean Local |
x |
x |
Invocações Assincronas a Session Bean Remoto |
- |
x |
Interceptors |
x |
x |
Interoperabilidade RMI-IIOP |
- |
x |
Transações gerenciadas por Bean e gerenciadas por Container |
x |
x |
Segurança Declarativa e Programática |
x |
x |
Embeddable API |
x |
x |
Tabela 3. Diferenças entre EJB Lite e EJB Completo.
Invocações a beans de sessão assíncronos e ao EJB Timer Service não persistente são as novas adições do EJB 3.2 Lite.
Um servidor de aplicativos completamente compatível com Java EE é necessário para implementar o conjunto completo de funcionalidades do EJB. Entre esses servidores temos o Oracle Weblogic, JBoss e Glassfish.
Criando uma Aplicação com EJB Lite
Primeiramente devemos baixar um servidor que suporte o EJB Lite. Um desses servidores é o TomEE.
O TomEE é uma mistura do Tomcat com o JavaEE. Com TomEE temos Tomcat com EJB adicionado e integrado, pronto para ser utilizado. O TomEE suporta as seguintes funcionalidades:
- CDI (Apache OpenWebBeans);
- EJB (Apache OpenEJB);
- JPA (Apache OpenJPA);
- JSF (Apache MyFaces);
- JSP (Apache Tomcat);
- JSTL (Apache Tomcat);
- JTA (Apache Geronimo Transaction);
- Servlet (Apache Tomcat);
- Javamail (Apache Geronimo JavaMail);
- Bean Validation (Apache BVal).
Para baixar o TomEE basta visitar o site http://tomee.apache.org/downloads.html ou baixar diretamente no link http://www.apache.org/dyn/closer.cgi/tomee/tomee-1.6.0.2/apache-tomee-1.6.0.2-webprofile.zip.
Após baixar o TomEE basta descompactarmos em alguma pasta do computador.
Agora já podemos ir para o Eclipse. Para isso, basta abrir o Eclipse e criar um projeto do tipo “Dynamic Web Project”, conforme ilustrado nas Figuras 1 e 2.
Figura 2. Escolhendo um projeto do tipo Dynamic Web Project.
Após isso, devemos criar um novo servidor do tipo TomEE. Para isso, clique em “New Runtime”, conforme a Figura 3.
Figura 3. Selecionando “New Runtime” para criar um novo servidor.
Após isso, devemos selecionar um projeto do tipo “Apache Tomcat v7.0”, conforme ilustra a Figura 4.
Figura 4. Selecionando um projeto do tipo “Apache Tomcat v7.0”.
Em seguida, selecionamos em “Tomcat installation directory” o diretório de instalação do TomEE e finalizamos através da opção “Finish” (Figura 5).
Figura 5. Selecionando o TomEE instalado.
Por fim, na aba “Servers” adicionamos o servidor que acabamos de criar (TomEE) para que possamos implantar as aplicações e iniciar, parar ou reiniciar o servidor. Para isso, clique com o botão direito na aba “Servers” e clique em “New” e “Server”, conforme mostra a Figura 6.
Figura 6. Adicionando novo servidor na aba Servers.
Na próxima tela basta selecionar o servidor TomEE criado anteriormente e escolher um nome para o servidor, conforme ilustra a Figura 7.
Figura 7. Selecionando o servidor TomEE criado.
Para exemplificar a utilização do EJB e do servidor TomEE que suporta o EJB Lite vamos criar um projeto de uma conta conforme abaixo.
Primeiramente devemos criar um pacote ejb e um pacote servlet em “src”. No pacote EJB criaremos a classe Conta que está implementada na Listagem 12.
Listagem 12. Exemplificando a classe EJB Conta que contém informações referentes a Conta.
package ejb;
import javax.ejb.Stateful;
@Stateful(mappedName = "Conta")
public class Conta {
float balanco = 0;
public void depositar(float valor) {
balanco += valor;
}
public void sacar(float valor) {
balanco -= valor;
}
public float getBalanco() {
return balanco;
}
}
A classe Controller será o controlador da nossa classe que é responsável por chamar o EJB e guardar informações na seção. Segue na Listagem 13o código da classe Controller.
Listagem 13. Exemplificando o controlador da aplicação.
package servlets;
import java.io.IOException;
import javax.ejb.NoSuchEJBException;
import javax.naming.Context;
import javax.naming.NamingException;
import javax.servlet.RequestDispatcher;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.naming.*;
import java.util.*;
import exemplo.ejb.Conta;
public class Controller extends HttpServlet {
private static final long serialVersionUID = 1L;
private static final String CONTA_SESSION_KEY = "contaStateful";
HttpServletRequest request = null;
protected void processRequest(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
this.request = request;
try {
Conta conta = (Conta) request.getSession().getAttribute(CONTA_SESSION_KEY);
if(conta == null) {
conta = createEJB();
}
float deposito = 0;
if (request.getParameter("deposito") != null && !"".equals(request.getParameter("deposito")))
deposito = Float.valueOf(request.getParameter("deposito"));
float saque = 0;
if (request.getParameter("saque") != null && !"".equals(request.getParameter("saque")))
saque = Float.valueOf(request.getParameter("saque"));
if (deposito > 0)
conta.depositar(deposito);
if (saque > 0)
conta.sacar(saque);
System.out.println("Conta vale:" + conta.getBalanco());
request.setAttribute("balanco", conta.getBalanco());
RequestDispatcher d = request.getRequestDispatcher("/index.jsp");
d.forward(request,response);
} catch (IOException | ServletException e) {
e.printStackTrace();
} catch (NoSuchEJBException n) {
try {
createEJB();
} catch (NamingException e) {
e.printStackTrace();
}
RequestDispatcher d = request.getRequestDispatcher("/index.jsp");
d.forward(request,response);
} catch (NamingException e) {
e.printStackTrace();
}
}
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
processRequest(request, response);
}
@Override
protected void doPost(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
processRequest(request, response);
}
private Conta createEJB() throws NamingException {
Properties p = new Properties();
p.put("java.naming.factory.initial",
"org.openejb.client.LocalInitialContextFactory");
p.put("openejb.home", "C:\\Users\\higor\\Desktop\\proj_seguranca\\apache-tomee-webprofile-1.6.0.2");
p.put(Context.PROVIDER_URL, "t3://localhost:8080");
InitialContext ctx = new InitialContext(p);
Conta conta = (Conta) ctx.lookup("java:global/TesteEJBLite/Conta");
request.getSession().setAttribute(CONTA_SESSION_KEY, conta);
return conta;
}
}
No código acima na linha "Conta conta = (Conta) request.getSession().getAttribute(CONTA_SESSION_KEY);" guardamos na sessão o EJB Stateful utilizado. Se a conta é nula cria-se um novo EJB.
Na linha “if (request.getParameter("deposito") != null && !"".equals(request.getParameter("deposito")))" e nas linhas abaixo verificamos o que foi recebido do formulário disponibilizado para o usuário e fazemos um depósito ou um saque na conta. Na linha "request.setAttribute("balanco", conta.getBalanco());" gravamos na seção o balanço atual da nossa conta. Por fim, na linha "RequestDispatcher d = request.getRequestDispatcher("/index.jsp");" redirecionamos para a página index novamente com o balanço atualizado. Um ponto importante a ser observado é na captura da exceção "catch (NoSuchEJBException n)". Normalmente as aplicações tratam esse tipo de exceção, pois é possível que a sessão com o cliente continue ativa, porém a instância do EJB tenha sido perdida, pois o servidor foi reinicializado.
Nos projetos empresariais normalmente utiliza-se um EJB Stateful em casos específicos, como em sites de compras onde devemos manter o estado dos objetos. Porém, também podemos utilizar sessões (HttpSession) para isso, ao invés de utilizar EJB. De forma geral, os EJBs Stateful são mais eficientes para cache do que HttpSession. HttpSession são mais utilizados para lógica de apresentação, como Wizzards, navegação, validação, etc. Outra prática muito utilizada no mercado é que se usarmos um HTTPSession nas nossa páginas devemos usar Stateless Session Bean para o EJB, porém se armazenamos EJB em HtppSession devemos usar um Stateful Session Bean. Essa prática pode se tornar complicada se utilizarmos clusters, mas pode ser gerenciado. Essas são algumas das discussões que ocorrem quando utilizamos essas práticas nos projetos, pessoalmente procuro utilizar HttpSession para armazenar dados em sessão e Stateless Session Bean para EJBs. Se você leitor utiliza outras práticas não deixe de me enviar um e-mail para que possamos discutir melhores práticas e criarmos outros artigos amplicando e sugerindo outras práticas de desenvolvimento utilizando EJBs.
Segue na Listagem 14a página principal da nossa aplicação de exemplo.
Listagem 14. Página principal da aplicação de Conta.
<%@taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%>
<%@page contentType="text/html" pageEncoding="UTF-8"%>
<html>
<body>
<form name="f" action="/TesteEJBLite/Controller" method="post">
<label>Escolha Depósito ou Saque</label>
<br/><br/>
<label>Quantidade para Depositar:</label>
<br/>
<input type="text" id="deposito" name="deposito"/>
<br/>
<br/>
<label>Quantidade para Sacar:</label>
<br/>
<input type="text" id="saque" name="saque"/>
<br/>
<br/>
<input id="enviar" name="enviar" type="submit" value="Enviar"/>
</form>
<br/><br/><br/>
Balanço Atual: ${balanco}
</body>
</html>
Por fim, nosso arquivo web.xml está configurado conforme o código da Listagem 15.
Listagem 15. Arquivo web.xml da nossa aplicação.
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://java.sun.com/xml/ns/javaee" xmlns:web="http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd" id="WebApp_ID" version="2.5">
<display-name>Exemplo FrontController</display-name>
<welcome-file-list>
<welcome-file>/index.jsp</welcome-file>
</welcome-file-list>
<servlet>
<servlet-name>frontcontroller</servlet-name>
<servlet-class>servlets.Controller</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>frontcontroller</servlet-name>
<url-pattern>/Controller</url-pattern>
</servlet-mapping>
</web-app>
A estrutura do nosso projeto ficou conforme ilustrado na Figura 8.
Figura 8. Estrutura geral do projeto.
Como tarefa de casa experimente fazer diversos saques e depósitos na conta, e implemente validações com beans validation ou transações através de depósitos e saques. A melhor forma de aprender a utilizar as novas ferramentas é praticando e com EJBs não é diferente. A partir de agora com a dica de aplicação e configurações acima fica muito mais simples de utilizarmos EJBs, principalmente após o fim da obrigação de utilizarmos arquivos descritores que exigiam muitas configurações.
Referências
G. Arun, What's new in EJB 3.2. Disponível em https://blogs.oracle.com/arungupta/entry/what_s_new_in_ejb.
JSR-000345 Enterprise JavaBeansTM 3.2, Disponível em https://jcp.org/aboutJava/communityprocess/final/jsr345/index.html.