Camel e ActiveMQ na prática usando Annotations
No posto anterior falei sobre a simplicidade e poder do Apache Camel. Neste post vou mostrar na prática como usar o camel para manipular diretórios, arquivos, filas JMS e código Java. Neste exemplo vou mostrar uma aplicação muito simples sobre cartão de crédito. Então vamos a aplicação :D
Imagine que você tem um restaurante e que a conta pode ser paga com cartão de credito, este cartão pode ser um VISA, Master, Hypercard ou Banricompras. Você recebe as informações de dos cartões de um sistema legado por exemplo feito em Clipper, ORACLE Forms, VB 6, Delphi ou qualquer outra tecnologia legada.
Você recebe este arquivos via arquivos texto em formato XML, você deve pegar cada XML e analisar o conteudo e de acordo com a bandeira do cartão vai mandar para uma fila especifica do ActiveMQ, desta forma você pode ter consumidores especificos para cada fila e fazer um processamento diferenciado para cada cartão de credito.
Esta aplicação na prática seria bem mais complexa do que estou proponto, meu objetivo aqui é mostrar como usar o apache camel em conjunto com o ActiveMQ usando annotações e o suporte do Spring, o cenário explicado a cima esta resumido a figura a baixo.
A Figura a cima mostra o fluxo de dados des da entrada dos arquivos XML em um diretório até o producer que é um bean do spring anotado com as anotações do camel colocar os dados nas filas corretas. Ao mesmo tempo os comsumer apropriados pegam os dados das filas e aplicam o processamento de negocios adequado.
Bom vamos ao código então. Primeiro vamos ver o pojo de dominio que representa o cartão de credito e depois vamos os serviços de negocio que estão no contexto do spring. Confira o código java a baixo.
CartaoCredito.java
Este é um simples POJO que representa o cartão de credito, na prática você poderia colocar mais atributos e colocar relacionamento com outros POJOS de dominio como por exemplo Conta, Cliente, Endereço, etc.
Agora vamos ver os serviços de negocio do Spring, estes serviços fazem o processamento a partr das informações no cartão de credito, eu criei uma interface chamada CartaoService e todos os serviços de cartão de credito implementam esta interface, então vamos ver a interface e as suas implementações.
Esta interface só tem um método o *efetuarPagamento* que recebe como parametro o cartão de credito, vamos ver agora as implementações que fiz em cima desta interface de negócio.
VisaCartaoCredito.java
Esta implementação efetua o processmaneto de negocio em cima dos cartões da bandeira Visa. Eu simplesmente estou mostrando uma mensagem na tela, mas você poderia fazer o que quizer, acessar o banco de dados, um webservice, aplicar validações, etc. As outras implementações são semelhantes apénas trocando a mensagem. Criei implementação para Master e Outros cartões então só confira o código por cima.
MasterCartaoCredito.java
OutrosCartaoService.java
Agora vamos ver o código do Camel para fazer a leitura dos dados que estão em XML no diretório, primeiro confira o formato do arquivo XML com este arquivo de exemplo a baixo;
Este XML foi gerado usando o Xstream. Eu criei um alias para o CartaoCredito e chamei de cc. Isto é configurado via código e você vai ver mais a frente. Agora vamos ver o código do producer, a explicação segue a baixo do código.
A annotation do Camel @Consume é resposável por consumir dados de um componente, neste caso o componente file que esta pegando arquivos do diretório C:/temp/arquivos. Quando entra um arquivo neste diretório o metodo quandoTemArquivosMandaParaFilaJMS é invocado recebendo como parametro uma string contendo o conteudo do arquivo.
Além disso existe uma anotação chamada @XPath ela serve para aplicar uma expressão XPath no XML,esta expressão pega apénas o campo bandeira do XML, isso vai ser usado para saber que tipo de cartão veio na mensagem. Dependendo do cartão a mensagem é roteada para uma fila especifica usando JMS.
Este roteamente se da através da classe ProducerTemplate do Camel em conjunto com a anotação @Produce que indica usar uma fila no ActiveMQ. Depois sera necessário configurar a localização e a forma de acesso ao ActiveMQ no spring, eu mostro isso na sequencia :)
Perceba que a configuração do activemq esta sendo feita através do bean 'activemq' que é do tipo ActiveMQComponent, dentro dele existe a propriedade 'brokerURL' que aponta para onde esta o activemq, estou acessando via tcp e na porta padrão que vem na instalação padrão.
*Sei que não falei muito sobre ActiveMQ neste post, mas vou abordar o mesmo em detalhes em posts futuros, até o momento você só precisa baixar a ultima versão e subir ele com a configuração padrão.
Voltando ao XML do Spring, podemos ver a declaração explicita do produucer e consumer do camel e tem um context-scan no spring que serve para ler os serviços de negocio do spring que estão sendo registrado com as anotações @Service e @Qualifier.
A tag 'camelContext' não tem nada, serve para indicar ao spring que suba o contexto do camel e jah inicie as rotas e o trabalho de mediação e roteamente imediatamente.
Agora vamos ver como que o Camel facilita a minha vida para ler das filas do ActiveMQ e acessar os serviços de negocio no Spring, para isso confira a classe chamada de PojoConsumer.
Nesta classe eu usei as anotações@Autowired e @Qualifier do Spring para injetar os serviços. Tudo isso é possivel por que o Camel nada mais é do que um conjunto de beans do spring no seu contexto, graças a essa integração fantastica é muito fácil colocar código de negocio aqui.
Perceba que existem 3 metodos diferentes usando a anotação @Consume. Cada um deles esta lendo uma fila diferente do ActiveMQ, depois disso cada um delega o processamento de negocios para o seriço apropriado.
Para rodar a aplicação basta subir o contecto do spring. Se você esta em uma aplicão desktop pode fazer aldo semelhante ao meu código a baixo;
Se você estiver usando Spring em uma aplicação Web basta configurar o listener no web.xml e apontar o arquivo de configurações do spring mostrado a cima neste post.
Ainda seria possivel melhorar muito esta *Arquitetura* você poderia separar os serviços do Spring do roteamente do Camel, assim você teria duas aplicações Web por exemplo, uma com o camel e outra com os beans de negocio do spring. Esta separação é possivel graças a mesageria e o ActiveMQ que é o nosso message broker/provider neste caso.
Se você quizer pode pegar os fontes completo deste projeto no meu repositório do Subversion na web. Espero que vocês tenham gostado deste post, na sequencia vou escrever mais coisas do genero, até lá um abraço e até a próxima Camel Riders!
Imagine que você tem um restaurante e que a conta pode ser paga com cartão de credito, este cartão pode ser um VISA, Master, Hypercard ou Banricompras. Você recebe as informações de dos cartões de um sistema legado por exemplo feito em Clipper, ORACLE Forms, VB 6, Delphi ou qualquer outra tecnologia legada.
Você recebe este arquivos via arquivos texto em formato XML, você deve pegar cada XML e analisar o conteudo e de acordo com a bandeira do cartão vai mandar para uma fila especifica do ActiveMQ, desta forma você pode ter consumidores especificos para cada fila e fazer um processamento diferenciado para cada cartão de credito.
Esta aplicação na prática seria bem mais complexa do que estou proponto, meu objetivo aqui é mostrar como usar o apache camel em conjunto com o ActiveMQ usando annotações e o suporte do Spring, o cenário explicado a cima esta resumido a figura a baixo.
A Figura a cima mostra o fluxo de dados des da entrada dos arquivos XML em um diretório até o producer que é um bean do spring anotado com as anotações do camel colocar os dados nas filas corretas. Ao mesmo tempo os comsumer apropriados pegam os dados das filas e aplicam o processamento de negocios adequado.
Bom vamos ao código então. Primeiro vamos ver o pojo de dominio que representa o cartão de credito e depois vamos os serviços de negocio que estão no contexto do spring. Confira o código java a baixo.
CartaoCredito.java
<br /> package com.blogspot.diegopacheco.camel.pojo;<br /> <br /> import java.io.Serializable;<br /> <br /> /**<br /> * Pojo que representa um cartao de credito. A ideia eh que ele seja serializado<br /> * e desserializado pelo xstrean. Sendo que a mesagem em XML trafega pelos<br /> * arquivos e filas JMS.<br /> * <br /> * @author Diego Pacheco<br /> * @version 1.0<br /> * @since 16/02/2010<br /> * <br /> */<br /> public class CartaoCredito implements Serializable {<br /> <br /> private static final long serialVersionUID = 1L;<br /> <br /> private Double numero;<br /> private String bandeira;<br /> private Integer seguranca;<br /> private String nomeProprietario;<br /> <br /> public CartaoCredito() {<br /> }<br /> <br /> public CartaoCredito(Double numero, String bandeira, Integer seguranca,<br /> String nomeProprietario) {<br /> super();<br /> this.numero = numero;<br /> this.bandeira = bandeira;<br /> this.seguranca = seguranca;<br /> this.nomeProprietario = nomeProprietario;<br /> }<br /> <br /> public Double getNumero() {<br /> return numero;<br /> }<br /> <br /> public void setNumero(Double numero) {<br /> this.numero = numero;<br /> }<br /> <br /> public String getBandeira() {<br /> return bandeira;<br /> }<br /> <br /> public void setBandeira(String bandeira) {<br /> this.bandeira = bandeira;<br /> }<br /> <br /> public Integer getSeguranca() {<br /> return seguranca;<br /> }<br /> <br /> public void setSeguranca(Integer seguranca) {<br /> this.seguranca = seguranca;<br /> }<br /> <br /> public String getNomeProprietario() {<br /> return nomeProprietario;<br /> }<br /> <br /> public void setNomeProprietario(String nomeProprietario) {<br /> this.nomeProprietario = nomeProprietario;<br /> }<br /> <br /> @Override<br /> public String toString() {<br /> return "Cartao:" + bandeira + " de numero:" + numero + " - "<br /> + nomeProprietario;<br /> }<br /> <br /> }<br />
Este é um simples POJO que representa o cartão de credito, na prática você poderia colocar mais atributos e colocar relacionamento com outros POJOS de dominio como por exemplo Conta, Cliente, Endereço, etc.
Agora vamos ver os serviços de negocio do Spring, estes serviços fazem o processamento a partr das informações no cartão de credito, eu criei uma interface chamada CartaoService e todos os serviços de cartão de credito implementam esta interface, então vamos ver a interface e as suas implementações.
<br /> package com.blogspot.diegopacheco.camel.service;<br /> <br /> import com.blogspot.diegopacheco.camel.pojo.CartaoCredito;<br /> <br /> /**<br /> * Service de Cartao de Credito<br /> * <br /> * @author Diego Pacheco<br /> * @version 1.0<br /> * @since 16/02/2010<br /> * <br /> */<br /> public interface CartaoService {<br /> public void efetuarPagamento(CartaoCredito cc);<br /> }<br />
Esta interface só tem um método o *efetuarPagamento* que recebe como parametro o cartão de credito, vamos ver agora as implementações que fiz em cima desta interface de negócio.
VisaCartaoCredito.java
<br /> package com.blogspot.diegopacheco.camel.service;<br /> <br /> import org.springframework.beans.factory.annotation.Qualifier;<br /> import org.springframework.stereotype.Service;<br /> <br /> import com.blogspot.diegopacheco.camel.pojo.CartaoCredito;<br /> <br /> /**<br /> * Service de Cartao de Credito: Visa<br /> * <br /> * @author Diego Pacheco<br /> * @version 1.0<br /> * @since 16/02/2010<br /> * <br /> */<br /> @Service<br /> @Qualifier("visa")<br /> public class VisaCartaoCredito implements CartaoService {<br /> <br /> @Override<br /> public void efetuarPagamento(CartaoCredito cc) {<br /> System.out.println("Efetuado o pagamento com visa! Usando cartao: " + cc.getNumero());<br /> }<br /> }<br />
Esta implementação efetua o processmaneto de negocio em cima dos cartões da bandeira Visa. Eu simplesmente estou mostrando uma mensagem na tela, mas você poderia fazer o que quizer, acessar o banco de dados, um webservice, aplicar validações, etc. As outras implementações são semelhantes apénas trocando a mensagem. Criei implementação para Master e Outros cartões então só confira o código por cima.
MasterCartaoCredito.java
<br /> package com.blogspot.diegopacheco.camel.service;<br /> <br /> import org.springframework.beans.factory.annotation.Qualifier;<br /> import org.springframework.stereotype.Service;<br /> <br /> import com.blogspot.diegopacheco.camel.pojo.CartaoCredito;<br /> <br /> /**<br /> * Service de Cartao de Credito: Master Card<br /> * <br /> * @author Diego Pacheco<br /> * @version 1.0<br /> * @since 16/02/2010<br /> * <br /> */<br /> @Service<br /> @Qualifier("master")<br /> public class MasterCartaoCredito implements CartaoService {<br /> <br /> @Override<br /> public void efetuarPagamento(CartaoCredito cc) {<br /> System.out.println("Efetuado o pagamento com master! Usando cartao: " + cc.getNumero());<br /> }<br /> <br /> }<br />
OutrosCartaoService.java
<br /> package com.blogspot.diegopacheco.camel.service;<br /> <br /> import org.springframework.beans.factory.annotation.Qualifier;<br /> import org.springframework.stereotype.Service;<br /> <br /> import com.blogspot.diegopacheco.camel.pojo.CartaoCredito;<br /> <br /> /**<br /> * Service de Cartao de Credito: Hypercard ou Banricompras<br /> * <br /> * @author Diego Pacheco<br /> * @version 1.0<br /> * @since 16/02/2010<br /> * <br /> */<br /> @Service<br /> @Qualifier("outros")<br /> public class OutrosCartaoService implements CartaoService {<br /> <br /> @Override<br /> public void efetuarPagamento(CartaoCredito cc) {<br /> System.out.println("Efetuado o pagamento com outros[" + cc.getBandeira() +"]! Usando cartao: " + cc.getNumero());<br /> }<br /> <br /> } <br />
Agora vamos ver o código do Camel para fazer a leitura dos dados que estão em XML no diretório, primeiro confira o formato do arquivo XML com este arquivo de exemplo a baixo;
<br /> <cc><br /> <numero>1.0</numero><br /> <bandeira>Master</bandeira><br /> <seguranca>1</seguranca><br /> <nomeProprietario>FulanoX0</nomeProprietario><br /> </cc><br />
Este XML foi gerado usando o Xstream. Eu criei um alias para o CartaoCredito e chamei de cc. Isto é configurado via código e você vai ver mais a frente. Agora vamos ver o código do producer, a explicação segue a baixo do código.
<br /> package com.blogspot.diegopacheco.camel.pojo.producer;<br /> <br /> import org.apache.camel.Consume;<br /> import org.apache.camel.Produce;<br /> import org.apache.camel.ProducerTemplate;<br /> import org.apache.camel.language.XPath;<br /> <br /> /**<br /> * Pojo que atraves das annotacoes Procuce e Consume le os arquivos xml de um<br /> * diretorio e manda os mesmos para um Fila JMS.<br /> * <br /> * @author Diego Pacheco<br /> * @version 1.0<br /> * @since 16/02/2010<br /> * <br /> */<br /> public class PojoProducer {<br /> <br /> @Produce(uri = "activemq:queue:fila.cartao.visa")<br /> private ProducerTemplate producerVISA;<br /> <br /> @Produce(uri = "activemq:queue:fila.cartao.master")<br /> private ProducerTemplate producerMASTER;<br /> <br /> @Produce(uri = "activemq:queue:fila.cartao.outros")<br /> private ProducerTemplate producerOutros;<br /> <br /> @Consume(uri = "file://C:/temp/arquivos")<br /> public void quandoTemArquivosMandaParaFilaJMS(String msg,@XPath("/cc/bandeira/text()") String bandeira) {<br /> if ("Master".toUpperCase().equals(bandeira.toUpperCase())) {<br /> producerMASTER.sendBody(msg);<br /> } else if ("Visa".toUpperCase().equals(bandeira.toUpperCase())) {<br /> producerVISA.sendBody(msg);<br /> } else {<br /> producerOutros.sendBody(msg);<br /> }<br /> }<br /> <br /> }<br />
A annotation do Camel @Consume é resposável por consumir dados de um componente, neste caso o componente file que esta pegando arquivos do diretório C:/temp/arquivos. Quando entra um arquivo neste diretório o metodo quandoTemArquivosMandaParaFilaJMS é invocado recebendo como parametro uma string contendo o conteudo do arquivo.
Além disso existe uma anotação chamada @XPath ela serve para aplicar uma expressão XPath no XML,esta expressão pega apénas o campo bandeira do XML, isso vai ser usado para saber que tipo de cartão veio na mensagem. Dependendo do cartão a mensagem é roteada para uma fila especifica usando JMS.
Este roteamente se da através da classe ProducerTemplate do Camel em conjunto com a anotação @Produce que indica usar uma fila no ActiveMQ. Depois sera necessário configurar a localização e a forma de acesso ao ActiveMQ no spring, eu mostro isso na sequencia :)
<br /> <?xml version="1.0" encoding="UTF-8"?><br /> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:amq="http://activemq.apache.org/schema/core" xmlns:context="http://www.springframework.org/schema/context" xsi:schemaLocation=" http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-2.5.xsd http://camel.apache.org/schema/spring http://camel.apache.org/schema/spring/camel-spring.xsd http://activemq.apache.org/schema/core http://activemq.apache.org/schema/core/activemq-core.xsd" ><br /> <br /> <camelContext xmlns="http://camel.apache.org/schema/spring" /><br /> <br /> <bean id="activemq" class="org.apache.activemq.camel.component.ActiveMQComponent"><br /> <property name="brokerURL" value="tcp://127.0.0.1:61616"/> <br /> </bean><br /> <br /> <bean class="com.blogspot.diegopacheco.camel.pojo.consumer.PojoConsumer" /><br /> <bean class="com.blogspot.diegopacheco.camel.pojo.producer.PojoProducer" /><br /> <br /> <context:component-scan base-package="com.blogspot.diegopacheco.camel.service" /><br /> <br /> </beans><br />
Perceba que a configuração do activemq esta sendo feita através do bean 'activemq' que é do tipo ActiveMQComponent, dentro dele existe a propriedade 'brokerURL' que aponta para onde esta o activemq, estou acessando via tcp e na porta padrão que vem na instalação padrão.
*Sei que não falei muito sobre ActiveMQ neste post, mas vou abordar o mesmo em detalhes em posts futuros, até o momento você só precisa baixar a ultima versão e subir ele com a configuração padrão.
Voltando ao XML do Spring, podemos ver a declaração explicita do produucer e consumer do camel e tem um context-scan no spring que serve para ler os serviços de negocio do spring que estão sendo registrado com as anotações @Service e @Qualifier.
A tag 'camelContext' não tem nada, serve para indicar ao spring que suba o contexto do camel e jah inicie as rotas e o trabalho de mediação e roteamente imediatamente.
Agora vamos ver como que o Camel facilita a minha vida para ler das filas do ActiveMQ e acessar os serviços de negocio no Spring, para isso confira a classe chamada de PojoConsumer.
<br /> package com.blogspot.diegopacheco.camel.pojo.consumer;<br /> <br /> import org.apache.camel.Consume;<br /> import org.springframework.beans.factory.annotation.Autowired;<br /> import org.springframework.beans.factory.annotation.Qualifier;<br /> <br /> import com.blogspot.diegopacheco.camel.pojo.CartaoCredito;<br /> import com.blogspot.diegopacheco.camel.service.CartaoService;<br /> import com.thoughtworks.xstream.XStream;<br /> <br /> /**<br /> * Pojo que atraves das annotacoes Procuce e Consume le os dados<br /> * em xml das filas do activemq e transforma a mensagem em pojo.<br /> * <br /> * @author Diego Pacheco<br /> * @version 1.0<br /> * @since 16/02/2010<br /> * <br /> */<br /> public class PojoConsumer {<br /> <br /> private XStream xstream;<br /> <br /> @Autowired<br /> @Qualifier("visa")<br /> private CartaoService visa;<br /> <br /> @Autowired<br /> @Qualifier("master")<br /> private CartaoService master;<br /> <br /> @Autowired()<br /> @Qualifier("outros")<br /> private CartaoService outros;<br /> <br /> <br /> public PojoConsumer() {<br /> xstream = new XStream();<br /> xstream.alias("cc", CartaoCredito.class);<br /> }<br /> <br /> private CartaoCredito xmlToPojo(String msg){<br /> return (CartaoCredito)xstream.fromXML(msg);<br /> } <br /> <br /> @Consume(uri = "activemq:queue:fila.cartao.visa")<br /> public void consumeVISA(String msg){<br /> CartaoCredito cc = xmlToPojo(msg);<br /> visa.efetuarPagamento(cc);<br /> }<br /> <br /> @Consume(uri = "activemq:queue:fila.cartao.master")<br /> public void consumeMASTER(String msg){<br /> CartaoCredito cc = xmlToPojo(msg);<br /> master.efetuarPagamento(cc);<br /> }<br /> <br /> @Consume(uri = "activemq:queue:fila.cartao.outros")<br /> public void consumeOutros(String msg){<br /> CartaoCredito cc = xmlToPojo(msg);<br /> outros.efetuarPagamento(cc);<br /> }<br /> <br /> }<br />
Nesta classe eu usei as anotações@Autowired e @Qualifier do Spring para injetar os serviços. Tudo isso é possivel por que o Camel nada mais é do que um conjunto de beans do spring no seu contexto, graças a essa integração fantastica é muito fácil colocar código de negocio aqui.
Perceba que existem 3 metodos diferentes usando a anotação @Consume. Cada um deles esta lendo uma fila diferente do ActiveMQ, depois disso cada um delega o processamento de negocios para o seriço apropriado.
Para rodar a aplicação basta subir o contecto do spring. Se você esta em uma aplicão desktop pode fazer aldo semelhante ao meu código a baixo;
<br /> package com.blogspot.diegopacheco.camel.main;<br /> <br /> import org.springframework.context.support.ClassPathXmlApplicationContext;<br /> <br /> public class CamelBootstraping {<br /> public static void main(String[] args) { <br /> try{ <br /> new ClassPathXmlApplicationContext(new String[] {"classpath:spring-camel-routing-beans.xml"}); <br /> }catch(Exception e){<br /> e.printStackTrace();<br /> } <br /> }<br /> }<br />
Se você estiver usando Spring em uma aplicação Web basta configurar o listener no web.xml e apontar o arquivo de configurações do spring mostrado a cima neste post.
Ainda seria possivel melhorar muito esta *Arquitetura* você poderia separar os serviços do Spring do roteamente do Camel, assim você teria duas aplicações Web por exemplo, uma com o camel e outra com os beans de negocio do spring. Esta separação é possivel graças a mesageria e o ActiveMQ que é o nosso message broker/provider neste caso.
Se você quizer pode pegar os fontes completo deste projeto no meu repositório do Subversion na web. Espero que vocês tenham gostado deste post, na sequencia vou escrever mais coisas do genero, até lá um abraço e até a próxima Camel Riders!