Stateful Session Beans Carrinho de compras corporativo Ricardo Cavalcanti [email protected] Jobson Ronan [email protected] Por que? Quando um cliente chama um metodo de um bean ele esta iniciando um dialogo Assim, o estado do dialogo precisa ser mantido para a próxima chamada Logo, o Container não pode fazer o mesmo tipo de “pooling” que faz com os beans Stateless Solução: passivação e ativação Passivação e ativação Semelhante ao processo de paginação efetuado pelo sistema operacional Dados do bean são armazanados durante a passivação e recuperados na ativação Permite manter poucas instancias no ar atendendo a vários clientes O Container decide quais beans serão passivados. Estratégia mais comum: LRU – Last Recently Used Se o Container precisar de mais recursos o bean a mais tempo usado será passivado O bean será serializado, assim, farão parte do dialogo apenas objetos e variáveis não transientes! Stateful Session Beans Precisam implementar a mesma interface que Stateless Session Beans Porém, algumas operações agora farão sentido • void ejbPassivate() – Chamado antes do container fazer o swap do Bean. • void ejbActivate() – Chamado depois do container recuperar o bean do disco. Stateful Session Beans Se há mais clientes realizando operações contínuas que beans no pool, container pode criar mais instancias Se há mais clientes que beans no pool mas uma boa parte permanece inativa por certos períodos (cenário realista) o container gerencia os recursos utilizando ativação e passivação O Administrador do sistema pode configurar o servidor para obter melhor desempenho (deployment descriptor) Ciclo de Vida Cliente chama create(...) na interface home Cliente chama remove ou a sessão expirou Bean não existe 1: Class.newInstance() 2: ejbSetSessionContext() ejbRemove() Time out 3: ejbCreate(...) Limite de beans do container atingido ejbPassivate() metodo() Bean inativo Bean ativo ejbActivate() Qualquer cliente chama um método de negócio em qualquer EJBObject Qualquer cliente chamou um método de negócio em um bean inativo. Container precisa ativar o bean Exemplo package org.citi.pec.ejbs; //..imports /** * @ejb.bean * * * * * */ public class name="Calculator" display-name="Name for Calculator" description="Description for Calculator" jndi-name="ejb/Calculator" type="Stateful" view-type="remote" CalculatorBean implements SessionBean { private BigDecimal memory = BigDecimal.ZERO; public void setSessionContext(SessionContext ctx) throws EJBException, RemoteException { } public void ejbRemove() throws EJBException, RemoteException { System.out.println("CalculatorBean.ejbRemove()"); } public void ejbActivate() throws EJBException, RemoteException { System.out.println("CalculatorBean.ejbActivate()"); } public void ejbPassivate() throws EJBException, RemoteException { System.out.println("CalculatorBean.ejbPassivate()"); } Exemplo //continuação /** * @ejb.create-method */ public void ejbCreate() throws CreateException { System.out.println("CalculatorBean.ejbCreate()"); } /** * @ejb.interface-method view-type = "remote" */ public double add(double value) { memory = memory.add(new BigDecimal(value)); return memory.doubleValue(); } /** * @ejb.interface-method view-type = "remote" */ public double mult(double value) { memory = memory.multiply(new BigDecimal(value)); return memory.doubleValue(); } } Exemplo Para demonstrar o efeito ejbActivate() e ejbPassivate() vamos reduzir o tamando da cache do bean artificialmente para a capacidade máxima de 2 beans Jboss.xml <session> <ejb-name>Calculator</ejb-name> <jndi-name>ejb/Calculator</jndi-name> <configuration-name>myEjbsConf</configuration-name> </session> </enterprise-beans> <container-configurations> <container-configuration extends="Standard Stateful SessionBean"> <container-name>myEjbsConf</container-name> <container-cache-conf> <cache-policy> org.jboss.ejb.plugins.LRUStatefulContextCachePolicy </cache-policy> <cache-policy-conf> <max-capacity>2</max-capacity> </cache-policy-conf> </container-cache-conf> </container-configuration> </container-configurations> Exemplo Em execução... ejbCreate() e create() Para cada create() na interface Home deve haver um ejbCreate() no bean Com número e tipo dos parâmetros idênticos Cada create() na interface Home retorna o tipo da interface do componente Cada ejbCreate() no bean retorna void Ambos lançam as mesmas exceções( exceto RemoteException que só é lançada na interface Home remota) ...XDoclet faz isso para nós! Exercício (1) Altere o tipo do bean Calculadora de Stateful para Stateless, rode a aplicação novamente e discuta os resultados Exercício (2) (a) Implemente um Session Bean com a seguinte interface Carrinho +adicionarProduto(String) +listarProdutos():String • O Bean mantém uma String com Produtos por linha • listarProdutos() retorna a String • adicionarProduto concatena a String Faça o deploy Clientes Para acessar um Ejb é necessário encontralo (lookup) no servidor Faz-se o lookup da interface home ao servidor JNDI Context ctx; //..obenten-se o contexto do servidor de nomes Object o = ctx.lookup("ejb/Calculator"); Caso a interface procurada seja a local, o retorno do lookup já é esta CalculatorLocalHome calculator = (CalculatorLocalHome) ctx.lookup("ejb/Calculator"); Clientes Caso a interface procurada seja a remota, o retorno do lookup é um objeto RMI-IIOP. Assim, é necessário efetuar um “cast” especial Object o = ctx.lookup("ejb/Calculator"); CalculatorHome calculatorHome = (CalculatorHome) PortableRemoteObject.narrow(o, CalculatorHome.class); De posse da interface home desejada, basta chamar o create() para obter acesso a interface do bean Clientes Obtendo (configurando) o contexto JNDI Arquivo “jndi.properties” no classpath Context ctx = new InitialContext(); Configuração programática Hashtable props = new Hashtable(); props.put(Context.INITIAL_CONTEXT_FACTORY, "org.jnp.interfaces.NamingContextFactory"); props.put(Context.URL_PKG_PREFIXES, "org.jboss.naming:org.jnp.interfaces"); props.put(Context.PROVIDER_URL, "jnp://localhost:1099"); Context ctx = new InitialContext(props); Exercício (2) Crie um cliente para o Carrinho Crie dois carrinhos Preencha ambos com 3 ou 4 produtos diferentes Liste o conteúdo de ambos Exercício (3) Carrinho de compras usando Produto Implemente um Stateful Session Bean que mantém na memória uma coleção de Produtos O Bean deve ter a seguinte interface e utilizar o objeto Produto • • • • public public public public Produto[] listarConteudoCarrinho(); void adicionarProduto(Produto produto); void removerProduto(String cod); Produto detalharProduto(String cod); Faça o deploy Crie um cliente semelhante ao do Carrinho Após rodar o cliente, altere o Bean para Stateles e rode novamente e comente os resultados Referências [1] Ed Roman, Mastering EJB 3, 2002, Capítulo 4 [2] Dale Green. Session Beans. J2EE Tutorial, Sun [3] Linda G. deMichiel et al. Enterprise JavaBeans™ 2.1 Specification. Sun Microsystems, 2003 [4] XDoclet, Doc for 1.2.2-RC1. http://xdoclet.sourceforge.net/