Informações

Tipo: Tutorial
Data de Publicação: 01/01/2004
Revisado em: 01/01/2004

Vote!

  • Currently 5,0/5 Stars.
  • 1
  • 2
  • 3
  • 4
  • 5

Tags Relacionadas

j2ee ejb

Comentários ( 2 )

Imprimir

Implementação de Stateful Session Beans

por:

Ulisses Telemaco (ulisses@j2eebrasil.com.br)
Gleydson Lima (gleydson@jeebrasil.com.br)

Iniciaremos esse tutorial fazendo uma breve descrição sobre o que o leitor vai encontrar nesse texto. Alguns irão achar curioso o fato de grande parte desse tutorial ser semelhante ao anterior sobre Stateless Session Bean. Na verdade isso se deve ao fato do desenvolvimento desses componentes (Stateful e Stateless) serem bem semelhantes, a diferença é mínima. Então por que um tutorial sobre Stateful Session Bean? Aproveitaremos esse tutorial para explicarmos alguns conceitos ligados aos componentes Stateful (ciclo de vida, contrução, pool de objetos, ...). Além disso, rever os passos de desenvolvimento de um componente Session Bean irá ajudar o leitor a fixar tais conceitos.

1. Introdução

Stateful Session Beans são componentes com manutenção de estado entre diversas chamadas remotas. O uso desses componentes é útil em diversas situações em que se deseja manter uma conversação e manutenção de estado do objeto. Um exemplo clássico é o componente de carrinho de compras. O gerenciamento de contexto se faz necessário para que o componente mantenha um registro dos produtos adicionados ao carrinho durante uma sessão onde o usuário está efetuando compras.

O desenvolvimento de componentes Stateful segue os mesmos padrões dos componentes Stateless. No entanto, o seu ciclo de vida é um pouco diferente. Dois novos eventos surgem no gerenciamento de componentes Stateful: ativação e passivação.

2. Ciclo de vida dos Stateful Session Bean

A Figura 1 ilustra o ciclo de vida de um Stateful Session Bean.

Figura 1 - Ciclo de Vida do Stateful Session Bean

Para entender melhor os conceitos relacionados ao ciclo de vida de um EJB é importante ter em mente como o container gerencia tais componentes. O container deve manter em memória uma referência de cada objeto EJB relacionado aos vários clientes conectados ao servidor. Imagine agora a situação onde existe uma grande quantidade de objetos EJB gerenciados pelo Container. A medida que novos usuários se conectam ao Servidor, a performance total do sistema é comprometida. Para otimizar o gerenciamento desses objetos, J2EE introduz o conceito de passivação e ativação de componentes EJB.

No evento de passivação, a instância do componente é retirada da memória e persistida em algum meio de armazenamento secundário. Antes de efetuar esse mecanismo, o Container notifica a instância do EJB através do método ejbPassivate(). Isso permite que o implementador execute algum procedimento necessário antes de passivar o objeto, como liberar uma conexão como banco de dados ou outro recurso. O processo de ativação é o inverso, o Container recarrega a instância novamente para a memória. Através do método ejbActivate() o componente poderá executar procedimentos necessários para voltar a usar o EJB, como obtenção de conexões ou outros recursos liberados durante a passivação.

As etapas de criação e remoção, já estudadas no texto sobre Stateless Session Bean, tem os métodos ejbCreate e ejbRemove relacionados a elas.

3. Etapas de desenvolvimento de um Stateful Session Bean

O desenvolvimento de componentes Stateful segue, a princípio, as mesmas etapas de desenvolvimento visto na seção anterior: codificação, build e deploy, como mostrado na Figura 2. Na verdade, veremos que a única diferença na codificação dos componentes Stateful e Stateless são os arquivos de configuração. É nos descriptores que determinamos se um Session Bean será Stateful ou Stateless.

Figura 2

4. Codificação do Componente

Como já antecipamos, o desenvolvimento de um Stateful Session Bean é muito parecido ao detalhado na seção anterior. No entanto, consideramos valido rever os elementos necessários para codificar um Session Bean: interfaces Home e Remote e Classe que implementa o Bean.

  • A interface Remote que publica para os clientes quais serão os métodos capazes de serem chamados remotamente. Os clientes devem apenas conhecer essa interface Remota para se comunicar com o componente remotamente. Como veremos a seguir, nenhuma chamada é feita ao Bean de forma direta.
  • A interface Home publica o método de criação do EJB. Os métodos de criação de um EJB tem a assinatura "create()". Uma interface Home pode sobrecarregar vários métodos de criação e assim fornecer diversas formas de se criar um objeto (assim como fazemos com sobrecarga de construtores).
  • A classe Bean não deve implementar a interface Remote. Ao contrário, deve implementar a interface SessionBean e fornecer métodos callback (ejbCreate, ejbRemote, ...) para auxiliar no controle do ciclo de vida do componente.

Neste tutorial iremos codificar um componente Contador que acumula o estado do número de chamadas. Convencionalmente, as classes que compõem o componente seguem a seguinte nomenclatura: Contador, ContadorHome e ContadorBean. Essas classes representam, respectivamente, a interface remote, a interface home e a classe bean.

Na Listagem 1 segue a implementação da interface Home do componente contador:

package br.com.j2eebrasil.artigos.ejb;

/**
 * A interface Home controla o ciclo de vida do componente. É através dessa 
 * interface que criamos e destruímos o componente. No processo de lookup, 
 * sempre se procura pela interface Home para a partir dela criarmos um objeto 
 * remoto.
 *
 * @author  Gleydson Lima
 */

import javax.ejb.EJBHome;
import java.rmi.RemoteException;
import javax.ejb.CreateException;

public interface ContadorHome extends EJBHome {
    
    public Contador create() throws RemoteException, CreateException;

    public Contador create(int inicial) throws RemoteException, CreateException;
    
}

Listagem 1 - Implementação da Interface Home do Componente Contador

A interface Home controla o ciclo de vida do componente. Note que a implementação da interface do componente ContadorHome herda a interface EJBHome. Quando o método create da interface Home é acionado pelo cliente o Container chama o método ejbCreate() correspondente da implementação do componente ContadorBean.

A interface Home do Contador possui dois métodos create. Um create padrão, sem passagens de argumentos, e um outro que recebe um valor numérico do tipo inteiro. Se o método create() for chamado, o container irá chamar o ejbCreate() da implementação do componente. Caso contrário, se o método create(int) for chamado, o Container irá chamar o ejbCreate(int) da implementação do componente.

Na Listagem 2 temos a interface Remota do componente.

package br.com.j2eebrasil.artigos.ejb;

/**
 *  A interface é o contrato de comunicação com o componente remoto. 
 *  Somente os métodos declarados na interface remota podem ser chamados pelos clientes
 *  através de uma chamada remota. 
 *
 *  Essa interface deverá ser do conhecimento de todos os clientes que desejem 
 *  acessar o componente EJB Contador.
 *
 @author  Gleydson Lima
 */

import javax.ejb.EJBObject;
import java.rmi.RemoteException;

public interface Contador extends EJBObject {
    
    /* Método incrementa o contador e retorna o numero atual */
    public int getCount() throws RemoteException;
    
}

Listagem 2 - Interface Remota do Componente

A interface Remota rege quais métodos serão passíveis de chamada remota. A implementação do componente (ContadorBean) poderá ter quantos métodos desejar, mas, passíveis de chamada remota, somente aqueles que estiverem na interface remote (Contador).

O único método publicado por esse componente é o getCount(). A implementação do Bean é mostrada na Listagem 3.

package br.com.j2eebrasil.artigos.ejb;

/**
 * Implementação do Bean - Essa classe contém a implementação da lógica
 * de negócio.
 *
 * @author  Gleydson Lima
 */

import javax.ejb.SessionBean;
import javax.ejb.SessionContext;
	
public class ContadorBean implements SessionBean {
    
    int contador;
    SessionContext sc;
    
    /** O método ejbCreate() será chamado quando for solicitado
     * o método create na interface Home. 
     **/
    
    public void ejbCreate() {
        contador = 0;
    }

    
    /** O método ejbCreate(int) será chamado quando for solicitado
     * o método create(int) na interface Home.
     **/
    
    public void ejbCreate(int i) {
        contador = i;
    }
    


    /* Métodos da interface SessionBean */
    
    public void ejbActivate()  {
        System.out.println("Ativando componente");
    }
    
    public void ejbPassivate()  {
        System.out.println("Passivando componente");
    }
    
    public void ejbRemove()  {
        System.out.println("Bean sendo removido");
    }
    
    public void setSessionContext(javax.ejb.SessionContext sessionContext) {
        sc = sessionContext;
    }
    
    /* Implementação do método da interface Remota */
    public int getCount() {
    
        return ++contador;
        
    }
    
}

Listagem 3 - Implementação do Bean

O código da listagem acima mostra a implementação do componente, mais especificamente a implementação do método getCount.

Para finalizarmos a etapada de codificação, precisamos ter os deployment descriptors do nosso componente. Os deployment descriptors são documentos que descrevem e configuram um componente. É através deles que podemos configurar o comportamento transacional dos componentes, persistência, e diversos outros recursos providos automaticamente pelo Container.

Existem dois tipos de deployment descriptors, os portáveis e os específicos. Os deployment descriptors portáveis configuram genericamente o componente com comportamentos providos por todos os Application Servers. A mudança de Container não afeta a mudança desses descriptors.

Os vários application servers diferentes possuem recursos e serviços variados. Não existe uma forma de prever quais são esses serviços, nem uma forma de configurá-los genericamente, uma vez que essa configuração varia de servidor para servidor. Dessa forma, existe também um deployment descriptors específico do application server.

O descriptor geral é armazenado em um arquivo no formato XML chamado ejb-jar.xml. É nesse descriptor que configuramos genericamente os componentes, como mostrado na Listagem 4.

<?xml version="1.0" encoding="Cp1252"?>
<!DOCTYPE ejb-jar PUBLIC '-//Sun Microsystems, Inc.//DTD Enterprise JavaBeans 2.0//EN' 
'http://java.sun.com/dtd/ejb-jar_2_0.dtd'>

<!-- Copyright 2002 Sun Microsystems, Inc. All rights reserved. -->

<ejb-jar>
  <display-name>ContadorJAR</display-name>
  <enterprise-beans>
    <session>
      <display-name>ContadorBean</display-name>
      <ejb-name>ContadorBean</ejb-name>
      <home>br.com.j2eebrasil.artigos.ejb.ContadorHome</home>
      <remote>br.com.j2eebrasil.artigos.ejb.Contador</remote>
      <ejb-class>br.com.j2eebrasil.artigos.ejb.ContadorBean</ejb-class>
      <session-type>Stateful</session-type>
      <transaction-type>Container</transaction-type>
      <security-identity>
        <description></description>
        <use-caller-identity></use-caller-identity>
      </security-identity>
    </session>
  </enterprise-beans>
  <assembly-descriptor>
    <container-transaction>
      <method>
        <ejb-name>ContadorBean</ejb-name>
        <method-intf>Remote</method-intf>
        <method-name>*</method-name>
      </method>
      <trans-attribute>Required</trans-attribute>
    </container-transaction>
  </assembly-descriptor>
</ejb-jar>

Listagem 4 - Configuração Genérica dos Componentes

Note que a única diferença do deployment descriptor desse componente Stateful para o componente mostrado no tutorial anterior sobre Stateless Session Bean é o valor do session-type.

O Application Server que iremos usar nesse exemplo é o JBoss. O deployment descriptor específico do JBoss é chamado jboss.xml. Mostrado na Listagem 5.

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE jboss PUBLIC "-//JBoss//DTD JBOSS 3.0//EN" "http://www.jboss.org/j2ee/dtd/jboss_3_0.dtd">

<jboss>

   <enterprise-beans>
      <session>
         <ejb-name>ContadorBean</ejb-name>
         <jndi-name>J2EEBrasilContador</jndi-name>
      </session>

   </enterprise-beans>

   <resource-managers>
   </resource-managers>

</jboss>

Listagem 5 - jboss.xml

A etapa de codificação acaba aqui. O próximo passo no desenvolvimento do nosso componente é o build.

5. Build

Vamos continuar utilizando o Ant para executar essa tarefa. O arquivo de configuração do ant (build.xml) é mostrado na Listagem 6.

<?xml version="1.0"?>

<!-- ======================================================================= -->
<!-- Template build file                                                                               -->
<!-- ======================================================================= -->

<project name="jspbrasiltutorial" default="main" basedir=".">
   
   <property name="Name" value="J2eebrasilContador"/>
   <property name="version" value="1.0"/>
   
   <property name="jboss.home" value="C://java//jboss" />
   <property name="jboss.configuration" value="default" />
   <property name="jboss.lib" value="${jboss.home}/server/${jboss.configuration}/lib" />
   
   <property name="src.dir" value="${basedir}/src"/>
   <property name="build.dir" value="${basedir}/build"/>
   <property name="deploy.dir" value="${jboss.home}/server/${jboss.configuration}/deploy" />
   
   <path id="classpath.lib">
        <pathelement location="${jboss.lib}/jboss-j2ee.jar" />
   </path>

   <!-- =================================================================== -->
   <!-- Compiles the source code                                            -->
   <!-- =================================================================== -->
   
   <target name="compile">
      <mkdir dir="${build.dir}"/>
      
      <javac
         destdir="${build.dir}"
         debug="on"
         deprecation="off"
         optimize="on"
         classpathref="classpath.lib"
      >
         <src path="${src.dir}"/>
      </javac>
   </target>
   
   <!-- =================================================================== -->
   <!-- Executa o build                                                     -->
   <!-- =================================================================== -->
   
   <target name="build" depends="compile">
      <mkdir dir="${build.dir}/META-INF" />
      <copy todir="${build.dir}/META-INF">
         <fileset dir="${src.dir}/META-INF" includes="**" />
      </copy>
      
   </target>
   
   <!-- =================================================================== -->
   <!-- Deploy in JBoss                                                     -->
   <!-- =================================================================== -->
   
   <target name="deploy" depends="build">
      <copy todir="${deploy.dir}">
         <fileset dir="${basedir}" includes="*.jar">
         </fileset>
      </copy>
   </target>
   
   <!-- =================================================================== -->
   <!-- Main Target                                                         -->
   <!-- =================================================================== -->
   
   <target name="main" depends="deploy" />
   
   <!-- =================================================================== -->
   <!-- Cleans up the current build                                         -->
   <!-- =================================================================== -->
   
   <target name="clean">
      <delete dir="${build.dir}"/>
      <delete file="${basedir}/${Name}.jar" />
   </target>

</project>

Listagem 6 - build.xml

Vale lembrar que não é o foco desse tutorial detalhar o uso da ferramenta Ant. Você pode aprender mais sobre Ant no próprio site do projeto Jakarta ou através do tutorial Ant disponível aqui no J2EEBrasil.

Para que essa configuração funcione corretamente devemos seguir a seguinte hierarquia de diretórios da Figura 3.

Figura 3 - Hierarquia de Diretórios

Para executar o build do nosso componente, execute o comando "ant build" na linha de comando a partir da raiz do diretório "ContadorComponent". A Figura 4 exibe o resultado do build com a ferramenta Ant.

Figura 4 - Resultado do build com a Ferramenta Ant

O resultado dessa operação foi a compilação dos fontes Java e o empacotamento dos arquivos ".class" e dos descritores XML em um arquivo chamado J2EEBrasilContador.jar.

6. Deploy

O processo de deployment consiste simplificadamente em instalar o componente no Application Server. Esse processo é específico de cada servidores. Alguns deles possuem ferramentas próprias para efetuarem o deployment, outros possuem esquemas bem simples para efetuar a instalação de um novo componente.

Durante a instalação de um componente, o Container avalia os seus descritores para ativar os serviços configurados para aquele componente (transação, persistências, binding de objetos, etc). Possíveis erros nos descritores são identificados nessa etapa. Se o desenvolvedor, por exemplo, não informar as classes Home ou Remote de forma correta, o container irá cancelar o deployment do componente e exibir o erro para o desenvolvedor.

O processo de deployment no JBoss é bem robusto, basta copiar o arquivo JAR para o diretório deploy da configuração desejada. Normalmente, $JBOSS_HOME\server\default\deploy. Assim, para efetuarmos o depployment de nossa aplicação, a única coisa que temos que fazer é copiar o arquivo Jar gerado no processo de build para o diretório citado anteriormente. Se você preferir, pode utilizar a ferramenta Ant para executar essa operação através da chamada na linha de comando "ant deploy".

A Figura 5 abaixo ilustra a saída do JBoss após a instalação do componente. Observe que vários passos são executados durante esse processo. O componente está correto e foi instalado com sucesso.

Figura 5 - Saída do JBoss após Instalação do Componente

Poderíamos simular alguns erros nos descritores e verificar qual o efeito disso durante o processo de deploy. Contudo, fizemos essa simulação no tutorial anterior sobre Stateless.

7. Conclusão

Como já dissemos no início desse texto, as etapas de desenvolvimento de um Stateful e de um Stateless Session Bean são bastante semelhantes. O leitor provavelmente encontrou descrições bem parecidas nos dois tutoriais.

As diferenças podem ser resumidas da seguinte forma:

  • O ciclo de vida de um componente Stateful apresenta outras duas etapas: passivação e ativação. A passivação consiste em liberar memória através do armazenamento de instâncias EJB em um meio de armazenamento secundário. A ativação, no entanto, consiste em recarregar para a memória instâncias previamente passivadas.

  • A interface Home pode ter vários métodos create() sobrecarregados, oferecendo assim diversas formas de criação do componente EJB. Note que para os componentes Stateless, apenas o método create() sem argumentos é permitido.

  • Para indicar que um Session Bean será do tipo Stateful, configuramos no arquivo ejb-jar.xml a tag <session-type> com o valor "Stateful". Como mostrado abaixo:
  •         ...
            <session-type>Stateful</session-type> 
            ...
    

    Clique aqui e baixe os fontes deste tutorial.

    Comentários (2)

    muito bem explicado!
    postado por Cleuber em 08/10/2008 às 23:21
    Interessante, parabéns.
    postado por Acácio em 12/11/2008 às 23:21
    Comente!

    Observações

    Os campos em negrito são obrigatórios.

    Para evitar problemas, este espaço é moderado. Após o envio do comentário será necessário aguardar pela sua aprovação.