Friday, August 29, 2008

Integrando Aplicações com Apache Camel

Foi-se o tempo em que a informatização de processos das organizações era feita em ilhas departamentais, quando sistemas isolados cuidavam dos diferentes aspectos do negócio. Hoje as empresas esperam que o software automatize todos os processos de negócio de forma integrada e ortogonal, esperam que a mesma informação não seja duplicada em bancos de dados diferentes, esperam que as regras de negócio não sejam duplicadas em diferentes aplicações. Infelizmente elas continuam esperando, pois ainda não é uma tarefa simples atender a todos estes requisitos.
Um passo importante nesta direção foi o advento dos Web Services, que permitem que aplicações diferentes, escritas usando os mais diversos ambientes de desenvolvimento, possam interoperar através da publicação de regras de negócio na forma de interfaces baseadas em padrões bem definidos como WSDL e SOAP. A utilização de web services permite que a salada de sistemas que normalmente povoam os data centers das empresas conversem de forma menos traumática que os famigerados métodos tão conhecidos como transferências de dados via arquivos texto ou escritas em bancos de dados alheios. Porém, implementar fluxos de negócio complexos usando web services é bem mais difícil que parece à primeira vista. As principais dificuldades relacionadas ao uso de web services são:
  • As diversas implementações de WSDL e SOAP não seguem as especificações à risca, o que muitas vezes faz com que certos web services não sejam acessíveis a todas as aplicações.
  • Em alguns casos, um web service deve executar de forma assíncrona do ponto de vista do cliente que o invocou, mas mesmo assim deve existir uma garantia de que o mesmo será executado.
  • Se uma aplicação que publica um conjunto de web services sai do ar, isso afetará todas as aplicações que dependem destes web services. Seria interessante que web services pudessem ser invocados mesmo quando as aplicações que os publicam estivessem indisponíveis.
  • Web services são semanticamente próximos a chamadas de métodos, mas as trocas de informações entre sistemas, muitas vezes, são mais parecidas com eventos ou trocas de mensagens, que podem ser implementadas através de um gerenciador de filas baseado em JMS, por exemplo.
  • Os dados que trafegam entre aplicações diferentes podem necessitar de algum tipo de tradução. Em alguns casos precisam ser mesclados a partir de fontes diferentes, ou uma mensagem pode ser dividida em várias mensagens, que podem ser enviadas para destinatários.
A atual abordagem para solucionar parte destes problemas é a utilização de um Enterprise Service Bus, ou ESB. Um ESB é um componente de software capaz de atuar como um ponto central para a publicação de serviços baseados em diversos protocolos, além de auxiliar no roteamento e tradução de mensagens. Em arquiteturas orientadas a serviços, conhecidas como SOA, o ESB é o barramento onde os serviços corporativos são pendurados, permitindo a troca de mensagens entre as aplicações, sejam elas internas ou externas à organização. A implantação de um ESB é um dos requisitos para a implantação de uma arquitetura SOA.
Existem várias implementações de ESB, sendo a maioria de alto custo e fornecidas por grandes empresas como IBM e SAP. Porém algumas implementações open source vêm sendo desenvolvidas e ganhando maturidade à medida que sua adoção aumenta. Entre estas implementações, destacam-se: Apache Servicemix, Mule ESB, JBoss ESB e Apache Camel. Neste post vou me concentrar no Camel em função da sua simplicidade quando comparado aos demais.
Uma visão geral do Camel
Os próprios desenvolvedores admitem que o Camel não é um ESB completo, como mostra a resposta do FAQ à pergunta "Is Camel an ESB?":
Typically vendors claim practically everything is an ESB these days, so the answer is probably yes
However our view is that an ESB is more of a container of integration components, so we view Apache ServiceMix to be a true ESB based around JBI to provide a standards based integration platform of components.
We view Camel as being a rule based routing & mediation engine which can be used inside a full blown ESB, a message broker or a web services smart client. Though if you want to, you could consider that Camel is a small, lightweight embeddable ESB since it can provide many of the common ESB services like smart routing, transformation, mediation, monitoring, orchestration etc.
Independente de ser um ESB verdadeiro ou não, o importante é que o Camel é extremamente útil na implementação dos patterns típicos de integração de aplicações. O melhor é que isso pode ser feito com grande simplicidade, que é o maior diferencial em relação às demais soluções open source.
O Camel é construído tendo como base o Spring Framework, que fornece um ambiente de execução leve e de fácil configuração. Todos os componentes que formam o Camel são criados e gerenciados via Spring, o que traz todos os benefícios da inversão de controle, como baixo acoplamento, extensibilidade e facilidade de automação de testes. O Camel é composto pelos seguintes elementos:
CamelContext
Representa uma instância do Camel e é uma extensão do ApplicationContext do Spring. Os demais elementos da arquitetura são pendurados embaixo deste objeto.
Component
São plugins que enriquecem o Camel em termos de funcionalidades. Components são factories de endpoints (ver próximo tópico) que são os blocos elementares das rotas que podem ser criadas pelo desenvolvedor. O Camel fornece um rico conjunto de componentes, que permitem integração com diversor protocolos e ambientes diferentes, tais como JMS, JDBC, HTTP, File Systems, etc.
Route
O Camel pode ser definido como um roteador de mensagens. Routes são usados para definir a estrutura das rotas pelas quais irão fluir as mensagens.
Endpoint
Quando trafegam pelas rotas, as mensagens passam por pontos intermediários chamados de Endpoints. Os Endpoints são criados por componentes e são responsáveis pelo processamento das mensagens. O conceito de Endpoint implementado pelo Camel pode ser visto aqui. Os endpoints são declarados através de URL's. Uma URL serve como um identificador único de um endpoint dentro de uma rota, além disso ela informa ao CamelContext qual componente será responsável pela criação do endpoint.
Exchange
As mensagens são empacotadas em Exchanges. Estes objetos são usados pelos endpoints para a transferência das mensagens ao longo da rota.
Resolvendo problemas comuns de Integração
O Camel pode ser usado para criar proxies de web services. A vantagem desta abordagem é a centralização os endereços dos web services da organização em um único ponto. Além disso, é possível usar os recursos de roteamento para aplicar filtros ou enviar as mensagens para mais de um destinatário, entre outras milhares de coisas que podem ser feitas dentro do Camel. O caso mais simples é mostrado na figura a seguir:

As requisições são recebidas por um web service publicado pelo Camel. Estas mensagens são redirecionadas para a implementação real do web service. O código Java para implementar esta rota no Camel é o seguinte:
from("jetty:http://my.company/service").to("http://some.company/service");
Ok, ok. Concordo que isso não é tão incrível, mas é só o começo. Usando os recursos de roteamento e a rica coleção de componentes do Camel, é possível adicionar recursos bem interessantes a esta rota. Como por exemplo:
- Logar todas as mensagens em um arquivo texto:
from("jetty:http://my.company/service")
.to(
  "file:/logs/service.log",
  "http://some.company/service"
);

- Rotear a mensagem para diferentes endereços. Neste caso o Camel funcionaria como um balanceador de carga:
from("jetty:http://my.company/service")
  loadBalance().roundRobin().to(
    "http://some.company/service",
    "http://other.company/service"
);

- Rotear a mensagem para diferentes endereços de acordo com o conteúdo da mensagem. Como o formato de requisições SOAP é XML, é possível usar o filtro XPath do Camel usando o conteúdo da própria mensagem como entrada:
from("jetty:http://my.company/service").choice()
  .when().xpath("//cidade = 'Belo Horizonte' ")
    .to("http://sudeste/service")
  .when().xpath("//cidade = 'Salvador' ")
    .to("http://nordeste/service")
);

Outro cenário comum, mas difícil de ser implementado sem um software de mediação como o Camel, é a entrega garantida de mensagens. Imagine um sistema com alto requisito de disponibilidade que precisa enviar dados para um sistema externo que é parado diariamente , durante uma hora, para manutenção. Como nosso sistema 24x7 pode depender de outro sistema 23x7? O Camel fornece uma solução simples para este problema através do componente JMS. Claro que é possível usar JMS sem o Camel, mas se pensarmos em uma organização com um enorme parque de sistemas, nem todos eles são escritos em Java e conseguem usar JMS, ou só conseguem fazer isso usando conectores complexos. Usando a solução proposta a seguir, qualquer sistema que tenha integração com o sistema 23x7, só precisa invocar um web service publicado pelo Camel:
from("jetty:http://my.company/service").to("jms:filaEntrega");
from("jms:filaEntrega").to("http://some.company/service");

Caso o serviço saia do ar, o Camel intercepta o erro de conexão e re-submete a requisição automaticamente. Tanto a quantidade de tentativas quanto o intervalo entre elas pode ser configurado. Mesmo que o total de tentativas seja esgotado, é possível direcionar a mensagem para um dead letter channel, que é um endpoint especial que recebe mensagens não entregues, que podem ser tratadas mais tarde. A exemplo abaixo acrescenta tratamento de erro à rota anterior. Mensagens não entregues após 5 tentativas serão encaminhadas à fila "errosServico":
errorHandler(deadLetterChannel("jms:errosServico")
  .maximumRedeliveries(5).useExponentialBackOff());
from("jetty:http://my.company/service").to("jms:filaEntrega");
from("jms:filaEntrega").to("http://some.company/service");

Conclusão
Os exemplos que mostrei aqui são simples, mas acredito que mostram o enorme potencial do Apache Camel como mediador de mensagens.
Existem muitos casos de integração que podem ser tratados através do Camel. Uma lista bem completa baseada no best seller Enterprise Integration Patterns de Gregor Hohpe e Bobby Woolf pode ser encontrada aqui. Espero voltar a falar desse assunto em breve.