Integração de sistema sempre foi e sempre será uma necessidade para
qualquer departamento de TI. Esta integração é feita, na maioria das vezes,
de forma manual e pontual. Porém, este tipo de abordagem traz diversos
problemas, como a complexidade de construir a integração sem
utilizar um framework que lhe dô benefícios como padrões, design certo a
seguir, reutilização de código, reuso.
É normal se você estiver pensando em um ESB. Porém, um ESB requer muito conhecimento, pensamento em design e você vai
ter que lidar com uma certa complexidade. Neste artigo vamos falar do Apache Camel, que é um framework de
integração de sistemas como se fosse um *ESB-Leve* fortemente baseado em
Spring.
O Camel é um solução open source da Apache muito
simples e extremamente flexível, parte da sua flexibilidade é dada
através da sua excelente integração com o Spring Framework. Ele não apenas facilita a sua vida quando você trabalha com JMS ou WebServices,
por exemplo, mas também quando você tem que trabalhar com arquivos
texto (os bons e velhos “flat-file”). O Camel possibilita que você realize um roteamento baseado em regras
declarativas através de beans do Spring ou através de uma DSL
fluente por exemplo em Java ou Groovy.
Se você está pensando em adotar
SOA em sua organização, uma excelente abordagem é começar com o
Camel, porque ele é muito mais simples que um ESB, mas não perde em
facilidades e simplicidade. Confira a baixo um exemplo de rotamento:
<camelContext id="camel" xmlns="http://camel.apache.org/schema/spring">
<route>
<from uri="file://C:/dados"/>
<to uri="jms:fila.exemplo"/>
</route>
</camelContext>
No exemplo acima estou roteamando tudo que entra no diretório
C:/dados para uma fila jms. Esta fila deve ser configurada, para que o
camel saiba onde ela está. Neste ponto podemos ver outra grande
vantagem do Camel, porque ele tem uma integração fantástica com a stack
da apache, especificamente falando do Apache CXF para Webservices e o ActiveMQ como message broker.
Ainda seria possível colocar condições neste roteamento usando a DSL do
camel ou até mesmo XPath. Abaixo vou mostrar como configurar para que o
camel acesse a fila no ActiveMQ.
<bean id="jms" class="org.apache.camel.component.jms.JmsComponent">
<property name="connectionFactory">
<bean class="org.apache.activemq.ActiveMQConnectionFactory">
<property name="brokerURL" value="tcp://127.0.0.1:61616"/>
</bean>
</property>
</bean>
Esta configuração assume que você tem o ActiveMQ no ar e rodando
na porta default, ele será acessando via tcp. O Camel faz isso
usando seu componente tcp que delega para o Apache Mina. O Camel também
implementa a maioria dos padrões de integração de sistema os famosos Enterprise
Integration Patterns. A lista completa de todos os compoenentes que
o Camel suporte pode ser encontrada aqui.
Na prática
Agora que já vimos a simplicidade e poder do Apache
Camel, vamos entender como usá-lo 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.
Imagine que você tem um restaurante e que a conta pode ser paga com
cartão de crédito, que pode ser Visa, Master, Hipercard ou
Banricompras. Você recebe as informações 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 conteúdo e, de acordo com a bandeira do
cartão, vai mandar para uma fila especifica do ActiveMQ; desta forma você
pode ter consumidores específicos 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 propondo,
meu objetivo aqui é mostrar como usar o apache camel em conjunto com o
ActiveMQ usando anotações e suporte do Spring, o cenário explicado está resumido na figura abaixo:
A Figura acima mostra o fluxo de dados, desde a entrada dos arquivos XML
em um diretório até o producer, que é um bean do spring anotado com as
anotações do camel para colocar os dados nas filas corretas. Ao mesmo tempo,
os comsumer apropriados pegam os dados das filas e aplicam o
processamento de negócios adequado.
Bom, vamos ao código, então. Primeiro vamos ver o pojo de domínio que
representa o cartão de crédito, depois aos serviços de negócio que
estão no contexto do spring. Confira o código java:
CartaoCredito.java
package com.blogspot.diegopacheco.camel.pojo;
import java.io.Serializable;
/**
* Pojo que representa um cartao de credito. A ideia eh que ele seja serializado
* e desserializado pelo xstrean. Sendo que a mesagem em XML trafega pelos
* arquivos e filas JMS.
*
* @author Diego Pacheco
* @version 1.0
* @since 16/02/2010
*
*/
public class CartaoCredito implements Serializable {
private static final long serialVersionUID = 1L;
private Double numero;
private String bandeira;
private Integer seguranca;
private String nomeProprietario;
public CartaoCredito() {
}
public CartaoCredito(Double numero, String bandeira, Integer seguranca,
String nomeProprietario) {
super();
this.numero = numero;
this.bandeira = bandeira;
this.seguranca = seguranca;
this.nomeProprietario = nomeProprietario;
}
public Double getNumero() {
return numero;
}
public void setNumero(Double numero) {
this.numero = numero;
}
public String getBandeira() {
return bandeira;
}
public void setBandeira(String bandeira) {
this.bandeira = bandeira;
}
public Integer getSeguranca() {
return seguranca;
}
public void setSeguranca(Integer seguranca) {
this.seguranca = seguranca;
}
public String getNomeProprietario() {
return nomeProprietario;
}
public void setNomeProprietario(String nomeProprietario) {
this.nomeProprietario = nomeProprietario;
}
@Override
public String toString() {
return "Cartao:" + bandeira + " de numero:" + numero + " - "
+ nomeProprietario;
}
}
Este é um simples POJO que representa o cartão de crédito. Na prática,
você poderia colocar mais atributos e colocar relacionamento com outros
POJOS de domínio, como Conta, Cliente, Endereço etc.
Agora, vamos ver os serviços de negócio do Spring, que fazem o
processamento a partir das informações no cartão de crédito. Eu criei
uma interface chamada CartaoService e todos os serviços de cartão de
credito implementam esta interface. Vejam a interface e as
suas implementações:
package com.blogspot.diegopacheco.camel.service;
import com.blogspot.diegopacheco.camel.pojo.CartaoCredito;
/**
* Service de Cartao de Credito
*
* @author Diego Pacheco
* @version 1.0
* @since 16/02/2010
*
*/
public interface CartaoService {
public void efetuarPagamento(CartaoCredito cc);
}
Esta interface só tem um método, o “efetuarPagamento”, que recebe como
parâmetro o cartão de crédito. Vamos ver as implementações que fiz
em cima desta interface de negócio.
package com.blogspot.diegopacheco.camel.service;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.stereotype.Service;
import com.blogspot.diegopacheco.camel.pojo.CartaoCredito;
/**
* Service de Cartao de Credito: Visa
*
* @author Diego Pacheco
* @version 1.0
* @since 16/02/2010
*
*/
@Service
@Qualifier("visa")
public class VisaCartaoCredito implements CartaoService {
@Override
public void efetuarPagamento(CartaoCredito cc) {
System.out.println("Efetuado o pagamento com visa! Usando cartao: " + cc.getNumero());
}
}
Esta implementação efetua o processamento de negócio em cima dos cartões
da bandeira Visa. Eu simplesmente estou mostrando uma mensagem na tela,
mas você poderia fazer o que quiser, acessar o banco de dados, um
webservice, aplicar validações etc. As outras implementações são
semelhantes, apenas trocando a mensagem. Criei implementação para Master e
Outros Cartões, então só confira o código por cima:
package com.blogspot.diegopacheco.camel.service;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.stereotype.Service;
import com.blogspot.diegopacheco.camel.pojo.CartaoCredito;
/**
* Service de Cartao de Credito: Master Card
*
* @author Diego Pacheco
* @version 1.0
* @since 16/02/2010
*
*/
@Service
@Qualifier("master")
public class MasterCartaoCredito implements CartaoService {
@Override
public void efetuarPagamento(CartaoCredito cc) {
System.out.println("Efetuado o pagamento com master! Usando cartao: " + cc.getNumero());
}
}
package com.blogspot.diegopacheco.camel.service;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.stereotype.Service;
import com.blogspot.diegopacheco.camel.pojo.CartaoCredito;
/**
* Service de Cartao de Credito: Hypercard ou Banricompras
*
* @author Diego Pacheco
* @version 1.0
* @since 16/02/2010
*
*/
@Service
@Qualifier("outros")
public class OutrosCartaoService implements CartaoService {
@Override
public void efetuarPagamento(CartaoCredito cc) {
System.out.println("Efetuado o pagamento com outros[" + cc.getBandeira() +"]! Usando cartao: " + cc.getNumero());
}
}
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 exempl:
<cc>
<numero>1.0</numero>
<bandeira>Master</bandeira>
<seguranca>1</seguranca>
<nomeProprietario>FulanoX0</nomeProprietario>
</cc>
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 à frente. Agora vamos ver o
código do producer, a explicação segue abaixo do código:
package com.blogspot.diegopacheco.camel.pojo.producer;
import org.apache.camel.Consume;
import org.apache.camel.Produce;
import org.apache.camel.ProducerTemplate;
import org.apache.camel.language.XPath;
/**
* Pojo que atraves das annotacoes Procuce e Consume le os arquivos xml de um
* diretorio e manda os mesmos para um Fila JMS.
*
* @author Diego Pacheco
* @version 1.0
* @since 16/02/2010
*
*/
public class PojoProducer {
@Produce(uri = "activemq:queue:fila.cartao.visa")
private ProducerTemplate producerVISA;
@Produce(uri = "activemq:queue:fila.cartao.master")
private ProducerTemplate producerMASTER;
@Produce(uri = "activemq:queue:fila.cartao.outros")
private ProducerTemplate producerOutros;
@Consume(uri = "file://C:/temp/arquivos")
public void quandoTemArquivosMandaParaFilaJMS(String msg,@XPath("/cc/bandeira/text()") String bandeira) {
if ("Master".toUpperCase().equals(bandeira.toUpperCase())) {
producerMASTER.sendBody(msg);
} else if ("Visa".toUpperCase().equals(bandeira.toUpperCase())) {
producerVISA.sendBody(msg);
} else {
producerOutros.sendBody(msg);
}
}
}
A annotation do Camel @Consume é resposável por consumir dados de um
componente, neste caso o componente file, que está pegando arquivos do
diretório C:/temp/arquivos. Quando entra um arquivo neste diretório o
método quandoTemArquivosMandaParaFilaJMS é invocado, recebendo como
parâmetro uma string com o conteúdo 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 apenas 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 roteamento se dá 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. Veremos em um outro artigo!
<?xml version="1.0" encoding="UTF-8"?>
<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" >
<camelContext xmlns="http://camel.apache.org/schema/spring" />
<bean id="activemq" class="org.apache.activemq.camel.component.ActiveMQComponent">
<property name="brokerURL" value="tcp://127.0.0.1:61616"/>
</bean>
<bean class="com.blogspot.diegopacheco.camel.pojo.consumer.PojoConsumer" />
<bean class="com.blogspot.diegopacheco.camel.pojo.producer.PojoProducer" />
<context:component-scan base-package="com.blogspot.diegopacheco.camel.service" />
</beans>
Perceba que a configuração do activemq está sendo feita através do bean
‘activemq’, que é do tipo ActiveMQComponent. Dentro dele existe a
propriedade ‘brokerURL’, que aponta para onde está 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 artigo, mas vou abordar o mesmo em detalhes no futuro. Por enquanto você só precisa baixar a última versão e subi-lo com a
configuração padrão.
Voltando ao XML do Spring, podemos ver a declaração explícita do
produucer e consumer do camel e tem um context-scan no spring, que serve
para ler os serviços de negócio 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 já inicie as rotas e o trabalho de mediação e
roteamento imediatamente.
Agora, vamos ver como que o Camel facilita a minha vida para ler das
filas do ActiveMQ e acessar os serviços de negócio no Spring. Confira a classe chamada de PojoConsumer.
package com.blogspot.diegopacheco.camel.pojo.consumer;
import org.apache.camel.Consume;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import com.blogspot.diegopacheco.camel.pojo.CartaoCredito;
import com.blogspot.diegopacheco.camel.service.CartaoService;
import com.thoughtworks.xstream.XStream;
/**
* Pojo que atraves das annotacoes Procuce e Consume le os dados
* em xml das filas do activemq e transforma a mensagem em pojo.
*
* @author Diego Pacheco
* @version 1.0
* @since 16/02/2010
*
*/
public class PojoConsumer {
private XStream xstream;
@Autowired
@Qualifier("visa")
private CartaoService visa;
@Autowired
@Qualifier("master")
private CartaoService master;
@Autowired()
@Qualifier("outros")
private CartaoService outros;
public PojoConsumer() {
xstream = new XStream();
xstream.alias("cc", CartaoCredito.class);
}
private CartaoCredito xmlToPojo(String msg){
return (CartaoCredito)xstream.fromXML(msg);
}
@Consume(uri = "activemq:queue:fila.cartao.visa")
public void consumeVISA(String msg){
CartaoCredito cc = xmlToPojo(msg);
visa.efetuarPagamento(cc);
}
@Consume(uri = "activemq:queue:fila.cartao.master")
public void consumeMASTER(String msg){
CartaoCredito cc = xmlToPojo(msg);
master.efetuarPagamento(cc);
}
@Consume(uri = "activemq:queue:fila.cartao.outros")
public void consumeOutros(String msg){
CartaoCredito cc = xmlToPojo(msg);
outros.efetuarPagamento(cc);
}
}
Nesta classe eu usei as anotações@Autowired e @Qualifier do Spring para
injetar os serviços. Tudo isso é possível porque o Camel nada mais é do
que um conjunto de beans do spring no seu contexto. Graças a essa
integração fantástica é muito fácil colocar código de negócio aqui.
Perceba que existem três métodos diferentes usando a anotação @Consume.
Cada um deles está lendo uma fila diferente do ActiveMQ, depois disso
cada um delega o processamento de negócios para o serviço apropriado.
Para rodar a aplicação, basta subir o contecto do spring. Se você está em
uma aplicão desktop, pode fazer algo semelhante ao meu código:
package com.blogspot.diegopacheco.camel.main;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class CamelBootstraping {
public static void main(String[] args) {
try{
new ClassPathXmlApplicationContext(new String[] {"classpath:spring-camel-routing-beans.xml"});
}catch(Exception e){
e.printStackTrace();
}
}
}
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 acima neste artigo.
Ainda seria possível melhorar muito esta “Arquitetura”; você poderia
separar os serviços do Spring do roteamente do Camel, assim teria
duas aplicações Web, uma com o camel e outra com os beans de
negócio do spring. Esta separação é possível graças ao mensageiro e o
ActiveMQ que é o nosso message broker/provider neste caso.
Se você quiser pode pegar os fontes completo deste projeto no meu
repositório do Subversion na web.
Até a próxima!
*
Gostou do artigo? Deixe seu comentário e aproveite para aprofundar seus conhecimentos no iMasters Shop!