330 likes | 447 Views
Introdução a JMS e Message-Driven Bean. Comunicação Assíncrona. Ricardo Cavalcanti roc3@cin.ufpe.br. Jobson Ronan jrjs@cin.ufpe.br. Por que usar um MDB?. Tipicamente devido a algumas limitações de RMI-IIOP
E N D
Introdução a JMS e Message-Driven Bean Comunicação Assíncrona Ricardo Cavalcanti roc3@cin.ufpe.br Jobson Ronan jrjs@cin.ufpe.br
Por que usar um MDB? • Tipicamente devido a algumas limitações de RMI-IIOP • Performance. Um cliente típico RMI-IIOP precisa esperar enquanto o servidor está processando. Apenas quando o servidor completa o trabalho e o cliente recebe o resultado, este pode continuar seu processamento • Garantia. Quando um cliente RMI-IIOP invoca o servidor, este tem que restar rodando. Se o servidor ou a rede cair o cliente não pode efetuar a operação desejada
Por que usar um MDB? • Suporte para vários emissores e receptores. RMI-IIOP se limita a comunicação de um único cliente a um único servidor • Integração com outros sistemas MOM(Middleware Orientado a Mensagem).
Messaging • Mensagens são uma alternativa a invocação de métodos remotos. • A idéia é inserir uma camada entre o cliente e o servidor Invocação de método remoto Aplicação Aplicação Messaging Message Middleware Aplicação Aplicação
Messaging • Vantagens • Processos não bloqueáveis • Garantia de entrega • Suporte a múltiplos emissores e receptores
JMS – Java Message Service • JMS é um padrão para Messaging • Tem como objetivo eliminar muitas das desvantagem que MOMs encontraram com o passar dos anos • O Desenvolvedor aprende a usar a API de JMS e reusa seu código com diferentes implementações plugáveis de MOM (idéia similar APIs do J2EE, como JNDI e JDBC)
Domínios de Mensagens • Publish/subscribe(pub/sub) • Análogo a assistir televisão. Pode haver muitos produtores de mensagens e muitos consumidores. Consumidor 1 Produtor 1 Canal Produtor 2 Consumidor 2
Domínios de Mensagens • Point-to-point(PTP) • Múltiplos produtores podem enviar mensagens para a fila mas cada mensagem é entregue a apenas um consumidor Produtor 1 Fila Consumidor 1 Produtor 2
Usando a API JMS • Passos • 1. Localizar o provedor JMS, instancia de ConnectionFactory • 2. Criar um conexão JMS • 3. Criar uma Sessão JMS • 4. Localizar o destino • 5. Criar um JMS Provider ou um JMS Consumer • 6. Enviar ou Receber suas mensagens
JMS Connection Factory 2. Criar conexão Fila1 3. Criar sessão JSM Connection Fila2 Cliente 5. Criar producer ou consumer JMS Session Tópico1 1. Obter o Driver JMS (ConnectionFactory) 6. Enviar ou receber mensagens JSM Prosucer ou Consumer Servidor JMS 4. Obter o destino JMS Driver JMS do cliente 6. Enviar ou receber mensagens JNDI Serviço de nomes
Exemplo //..imports public class Client { public static void main (String[] args) throws Exception { Context ctxt = new InitialContext(); TopicConnectionFactory factory = (TopicConnectionFactory) ctxt.lookup("jms/TopicConnectionFactory"); TopicConnection connection = factory.createTopicConnection(); TopicSession session = connection.createTopicSession (false, Session.AUTO_ACKNOWLEDGE); Topic topic = (Topic) ctxt.lookup("jms/Topic"); TopicPublisher publisher = session.createPublisher(topic); TextMessage msg = session.createTextMessage(); msg.setText("This is a test message."); publisher.publish(msg); } }
Integração JMS-EJB • Motivação • Possuir componentes Ejbs com características como clientes “não-bloqueaveis” e comunicação n-ária
Integração JMS-EJB • Como implementar a integração? • Implementar objetos Java que recebem mensagens e realizam chamadas a componentes EJBs? • Reusar um tipo existente de componente EJB para receber Mensagens JMS?
Messsage-Driven Beans • O que são? • Introduzido na especificação EJB 2.0 • São componentes EJBs especiais capazes de receber mensagens enviadas a filas e canais JMS • Invocados pelo Container dada a chegada de um mensagem ao destino que um MDB escuta • Não se envia uma mensagem direto a um MDB(envia-se ao canal que o bean escuta) • Proporcionando acoplamento fraco entre cliente e MDB (conhecimento do canal de comunicação)
Messsage-Driven Beans • Para efetuar a comunicação é necessário o uso de uma API específica, como JMS Pool de MDBs Cliente Destino JMS Instancias de Message-Driven Beans
Messsage-Driven Beans • Características • Não possuem interface home, local home, interface remota, nem interface local • Possuem apenas um método que recebe qualquer tipo de mensagem • Não têm retorno, e também não lançam exceções ao cliente • São Stateless • Podem ser ouvintes de uma fila, ou assinantes durável ou não-durável de um canal(Topic)
Implementando MDBs • MDBs devem implementar duas interfaces • javax.jms.MessageListener • javax.ejb.MessageDrivenBean • Métodos de MessageDrivenBean e da especificação • ejbRemove(): método da interface. Chamado pelo container quando o message-driven bean está sendo removido do pool • ejbCreate(): método definido na especificação (não está na interface). É chamado quando um novo bean é criado pelo container • setMessageDrivenContext(MessageDrivenContext ctx): método da interface. Chamado antes do ejbCreate() quando o bean está sendo adicionado ao pool
Implementando MDBs • Método de MessageListener • onMessage(Message m): chamado cada vez que uma mensagem é enviada para o canal do bean (se o bean estiver ativado).
Implementando MDBs package org.citi.pec.ejbs; //..imports /** * @ejb.bean name="ExecuterSQLMessage" * display-name="Name for Executer" * description="Description for Executer" * destination-type="javax.jms.Queue" * acknowledge-mode="Auto-acknowledge" * @jboss.destination-jndi-name name = "queue/MyQueueMDB" */ public class ExecuterBean implements MessageDrivenBean, MessageListener { public void onMessage(Message message) { try { if (message instanceof TextMessage) { TextMessage text = (TextMessage) message; System.out.println(text.getText()); } } catch (JMSException e) { throw new EJBException(e); } } public void setMessageDrivenContext(MessageDrivenContext ctx) throws EJBException {} public void ejbCreate() throws EJBException {} public void ejbRemove() throws EJBException {} }
Deployment Descriptors <?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE ejb-jar PUBLIC "-//Sun Microsystems, Inc.//DTD Enterprise JavaBeans 2.0//EN“ "http://java.sun.com/dtd/ejb-jar_2_0.dtd"> <ejb-jar > <description><![CDATA[No Description.]]></description> <display-name>Generated by XDoclet</display-name> <enterprise-beans> <message-driven > <description><![CDATA[Description for Executer]]></description> <display-name>Name for Executer</display-name> <ejb-name>ExecuterSQLMessage</ejb-name> <ejb-class>org.citi.pec.ejbs.ExecuterBean</ejb-class> <transaction-type>Container</transaction-type> <acknowledge-mode>Auto-acknowledge</acknowledge-mode> <message-driven-destination> <destination-type>javax.jms.Queue</destination-type> </message-driven-destination> </message-driven> </ejb-jar> ...e se fosse Topic ???
Deployment Descriptors • Outras Tags • <message-selector> Usado para incluir um filtro SQL para cabeçalhos de mensagens. Apenas mensagens que combinarem com o filtro serão recebidas pelo bean. Exemplo: <message-selector><![CDATA[ Formato LIKE '%Imagem%‘ AND JMSExpiration > 0 AND Valor IS NOT NULL]]> </message-selector>
Deployment Descriptors <?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE jboss PUBLIC "-//JBoss//DTD JBOSS 3.2//EN“ "http://www.jboss.org/j2ee/dtd/jboss_3_2.dtd"> <jboss> <enterprise-beans> <message-driven> <ejb-name>ExecuterSQLMessage</ejb-name> <destination-jndi-name>queue/MyQueueMDB</destination-jndi-name> </message-driven> </enterprise-beans> <resource-managers> </resource-managers> </jboss>
JBoss • ...empacotar e fazer o deploy... • Warning no deploy: a fila referênciada (queue/MyQueueMDB)não existe, o JBoss criará uma nova fila temporária para o MDB
JBoss • Como a maioria das coisas no JBoss, JMS Topics e Queues são implementadas como MBeans • Para criar uma fila permanente no JBoss, basta adicionar uma declaração de um MBean no arquivo jbossmq-destinations-service.xml (diretório jms dentro do diretório deploy) <!-- .... --> <mbean code="org.jboss.mq.server.jmx.Queue“ name="jboss.mq.destination:service=Queue,name=MyQueueMDB"> <depends optional-attribute-name="DestinationManager"> jboss.mq:service=DestinationManager </depends> </mbean> <!-- .... -->
Implementando cliente //..imports public class MDBClient { public static void main(String[] args) throws Exception { Properties namingProperties = new Properties(); namingProperties.put(Context.INITIAL_CONTEXT_FACTORY, "org.jnp.interfaces.NamingContextFactory"); namingProperties.put("java.naming.provider.url", "jnp://localhost:1099"); namingProperties.put("java.naming.factory.url.pkgs", "org.jboss.naming.client"); Context ctx = new InitialContext(namingProperties); QueueConnectionFactory cf = (QueueConnectionFactory) ctx.lookup("XAConnectionFactory"); QueueConnection conn = cf.createQueueConnection(); QueueSession session = conn.createQueueSession(false, Session.AUTO_ACKNOWLEDGE); Queue queue = (Queue) ctx.lookup("queue/MyQueueMDB"); QueueSender sender = session.createSender(queue); TextMessage message = session.createTextMessage(); message.setText("Hello World of Messaging!"); sender.send(message); } } • Como um cliente JMS comum
Exercício • Construa um MDB simples que recebe uma mensagem de texto e imprime seu conteúdo se a mensagem possuir no cabeçalho a informação “fonte : cliente” • Construa um cliente que envia uma Mensagem a fila na qual o MDB está escutando
...mas quem pode e deve ser um cliente de um bean: • Servlets locais • Beans locais • Classes java remotas • ...
Exercício (++) • “... How the pieces fit together?” • Construa um Entity Bean CompraBean com os segiuntes atributos: • Código • Cliente • produtos (pode ser uma string contendo todos os produtos) • valor total • status da compra (um booleano que indica se a compra foi validada ou não)
Exercício (++) • Construa um MDB ValidadorBean que recebe uma mensagem contendo os dados de uma compra e simula uma verificação de perfil de compra. Em seguida ele altera o estado do CompraBean Correspondente para validado • você pode usar um "Thread.sleep()" para simular um processo demorado
Exercício (++) • Crie um ValueObject para o Bean Compra (CompraVO) • Construa um SessionBean stateless que contém o método efetuarCompra(CompraVO compra) que • Cria um CompraBean equicvalente ao CompraVO passado por parametro • E em seguida envia uma mensagem ao ValidadorBean, solicitando a validação da compra
Exercício (++) • Crie um cliente simples que efetua uma compra através o session Bean que você implementou • ...E...