Apache Camel: Um guia completo - Partes 1 e 2
Conheça esse poderoso framework que simplifica a integração entre sistemas.
Apache Camel: Um guia completo - Partes 3 e 4
A TI se encaixa como uma grande aliada para suportar os negócios das empresas e assegurar que todo o ecossistema globalizado e interligado funcione de forma satisfatória. Portanto, há um grande esforço por parte da TI para construir, integrar e manter os diversos sistemas que compõem todo o fluxo de negócio da empresa.
Diante dessas condições, o artigo apresentará em detalhes os problemas que geralmente são enfrentados nesse contexto de integração de sistemas, além de desafios que constantemente colocam sistemas à prova, e, as soluções/estratégias que ao longo do tempo surgiram para simplificar todo esse cenário.
Além disso, será apresentado o framework Apache Camel, que foi desenvolvido com base nessas soluções e cuja principal função é facilitar a construção de sistemas voltados especialmente para integrações.
Para iniciar o estudo do tema proposto, a próxima seção apresentará em detalhes os problemas e desafios que são largamente enfrentados pelos desenvolvedores quando eles lidam com requisitos de integração.
Problemas e desafios para a integração
Uma integração efetiva é um pré-requisito fundamental para o sucesso dos negócios, mas os desafios relacionados à mesma, como tempo, custo e problemas técnicos, podem acabar com grandes investimentos e levar ao fracasso muitos projetos. Esses problemas incluem:
- Sistemas legados baseados no desenvolvimento “tapa buraco”;
- Sistemas com arquitetura de uma única camada;
- Sistemas legados que não foram projetados para atender novos requisitos de qualidade e que são diretamente afetados pela necessidade de interoperabilidade, desempenho e segurança;
- Sistemas que foram projetados para trabalhar com os dados restritos ao uso interno;
- Decisões tomadas sem as devidas análises de arquitetura de software; e
- Falta de definições adequadas referentes ao escopo e esforço necessários para a integração.
Além dos problemas mencionados, existem alguns desafios que devem ser considerados durante a integração entre sistemas. São eles:
- Redes são instáveis e as soluções de integração implicam no transporte de dados por meio de redes que muitas vezes não são resilientes. Portanto, os sistemas integrados devem estar preparados para lidar com problemas relacionados à rede;
- Como as redes são lentas, o envio de dados por meio destas é mais lento que chamadas locais. Sendo assim, pensar em uma solução distribuída da mesma forma que se pensa em uma solução isolada pode acarretar em sérios problemas relacionados a desempenho;
- Visto que os sistemas são implementados com as mais diversas tecnologias, uma solução de integração necessita transmitir e receber dados de sistemas que utilizam diferentes linguagens de programação, plataformas, frameworks e formatos de dados. Portanto, a solução a ser construída deve ser capaz de atender a esse cenário;
- Como mudanças são inevitáveis, sistemas são alterados a todo momento. Sendo assim, uma solução de integração deve garantir seu correto funcionamento e, consequentemente, o fluxo de negócio, independentemente das alterações realizadas nos sistemas que se conecta.
Felizmente os problemas e desafios apresentados até o momento têm sido superados pelos desenvolvedores ao longo do tempo, através de estilos de integração que foram adotados amplamente pelo mercado. Para nos aprofundarmos nesses estilos, a seção a seguir trará uma análise com mais detalhes.
Estilos de integração
Diante de todas as dificuldades e necessidades identificadas anteriormente, as integrações precisam ocorrer não apenas entre soluções homogêneas, mas também entre soluções estruturadas em plataformas distintas, separadas geograficamente dentro e fora do escopo da organização e que adotam as mais variadas tecnologias. Baseado nisso, as diferentes abordagens para que sistemas se comuniquem podem ser resumidas em quatro grandes estilos, a saber:
- Transferência de arquivo (Electronic Data Interchange ou EDI): um sistema escreve um arquivo de texto ou binário para que um outro sistema leia. Esse estilo exige conformidades no nome do arquivo, localização e formato (layout). Normalmente está associado ao uso do protocolo FTP (File Transfer Protocol). Na Figura 1 é exibido um diagrama exemplificando a integração por EDI;
Figura 1. Integração entre sistemas através da transferência de arquivo.
- Banco de dados compartilhado: múltiplos sistemas utilizam um mesmo banco de dados físico para consultar e manipular dados. Com isso, não há duplicidade de informação. A Figura 2 ilustra esse estilo;
Figura 2. Integração entre sistemas através do dado compartilhado via banco de dados.
- Invocação Remota de Procedimento (Remote Procedure Call ou RPC): um sistema expõe suas funcionalidades para que sejam acessadas remotamente por outros sistemas. Nesse estilo a comunicação ocorre em tempo real, sincronamente e necessita que os sistemas integrados possuam compatibilidade em relação à tecnologia remota para que a comunicação funcione. A Figura 3 demonstra um cenário de integração com RPC;
Figura 3. Integração entre sistemas através de RPC.
- Mensageria: é a tecnologia que permite a entrega rápida e confiável de mensagens através da comunicação assíncrona. Nessa proposta, a mensagem é simplesmente um tipo de estrutura de dados como um texto (string), um arranjo de bytes, um registro ou um objeto, servindo como um meio de integrar sistemas. Tudo isso funciona através de um sistema que publica uma mensagem em um canal para que outro, que também possui acesso a esse canal, possa ler e interpretar essa mensagem. A Figura 4 apresenta um diagrama exibindo um exemplo de comunicação através desse estilo;
Figura 4. Integração entre sistemas através de mensagens.
Apesar dos quatro estilos apresentados resolverem os mesmos problemas de integração, existem vantagens e desvantagens a serem consideradas na escolha de cada um. Para auxiliar essa escolha, foram definidos alguns critérios que devem ser analisados cuidadosamente. A Tabela 1 apresenta um comparativo entre os estilos de integração considerando esses critérios.
Critérios |
Transferência de Arquivo |
Banco de Dados Compartilhado |
Invocação Remota de Procedimento |
Mensageria |
Acoplamento entre os sistemas |
++ |
--- |
- |
+++ |
Nível de intrusão |
++ |
--- |
+ |
+++ |
Escolha da tecnologia |
++ |
++ |
++ |
- |
Formato dos dados |
+ |
++ |
++ |
+++ |
Tempo para atualização dos dados |
-- |
+++ |
++ |
++ |
Exposição das funcionalidades |
--- |
--- |
+++ |
++ |
Processamento assíncrono |
+++ |
++ |
++ |
+++ |
Confiabilidade |
-- |
+++ |
+ |
+++ |
Tabela 1. Comparativo de pontos positivos e negativos de cada estilo baseado em critérios comuns.
Observa-se que o estilo de mensageria é o que possui mais pontos positivos dentre os critérios comparados, ou seja, a mensageria é o estilo de integração que possui mais vantagens e por isso é considerada a melhor opção. Porém, os sistemas podem se integrar por meio de vários estilos, de forma que cada ponto da integração tire o melhor proveito de cada um.
Baseado na experiência dos desenvolvedores com o uso dos estilos apresentados, foram reconhecidos e definidos padrões empresarias de integração que indicam soluções elegantes para problemas comumente identificados nesse contexto. A seguir, por constituírem a base do framework Apache Camel, esses padrões serão analisados.
Enterprise Integration Patterns
Os Enterprise Integration Patterns ou EIPs foram criados para resolver uma variedade de problemas recorrentes do contexto de integração entre sistemas. Eles definem uma série de recomendações e boas práticas, independentes de plataforma e tecnologia, que foram documentadas no livro “Enterprise Integration Patterns: Designing, Building, and Deploying Messaging Solutions”, dos autores Gregor Hohpe e Bobby Woolf (veja a seção Links).
O livro apresenta 65 padrões agrupados e classificados em seis categorias relacionadas a diferentes cenários da integração. Além disso, são indicadas as vantagens e desvantagens de cada padrão, um vocabulário e uma notação de modelagem a serem seguidos.
Mesmo divididos e categorizados, os padrões de integração podem ser combinados para atender cenários específicos do mundo real empresarial. Ademais, não são “receitas de bolo” que devem ser copiadas e colocadas para a resolução de um problema. Eles devem ser encarados como pequenos conselhos que descrevem soluções para problemas recorrentes e que por meio de tecnologia modelam padrões de negócio de forma elegante.
Visando garantir sistemas mais eficientes, flexíveis, adaptáveis e com baixo custo de manutenção, os EIPs são fortemente baseados no conceito de baixo acoplamento, onde o princípio fundamental é reduzir a dependência que os sistemas integrados têm um do outro quando trocam informações para então aumentar a testabilidade e rastreabilidade do código. Para isso, os autores indicam o uso do estilo de integração baseado em mensageria.
Baseado em todo esse contexto de integração, juntamente com as boas práticas, padrões e mensageria, os autores do livro de EIPs propuseram diagramas formados por três elementos base com o objetivo de definir uma notação em linguagem visual que facilitasse o entendimento e representasse de forma mais clara os padrões. Essa notação acabou sendo difundida pela comunidade e consolidada como um padrão para representar e modelar visualmente a integração de sistemas. Portanto, entender os elementos base que constituem essa notação tornou-se bastante importante atualmente e sendo assim, na seção seguinte os mesmos serão apresentados.
Notação base para modelagem dos padrões
Conforme observa-se na Figura 5, os três elementos base das notações são: mensagem, conexão e componente. O elemento Mensagem representa os dados trafegando através das conexões e é constituído por uma estrutura em árvore onde o círculo sinaliza o nó raiz e os pequenos quadrados aninhados sinalizam os dados propriamente ditos. Já a Conexão representa o transporte dessas mensagens e o Componente é o item responsável por receber, processar e enviar essas mensagens.
Figura 5. Elementos básicos da notação dos padrões de integração.
A partir das notações exibidas é possível realizar toda a modelagem necessária para descrever um padrão. Porém, em grande parte dos cenários de integração os mesmos são utilizados em conjunto para atender uma determinada solução. Nesses casos, pode-se adotar a notação ou imagem que representa o próprio padrão conectada aos outros através de um símbolo de conexão. Para facilitar o entendimento, a Figura 6 apresenta um exemplo de padrões utilizados em conjunto.
Figura 6. Padrões em conjunto para resolver um cenário de integração.
Apresentados os conceitos envolvidos em um padrão e as notações básicas para representação visual, já é possível entender de forma mais clara a distribuição dos mesmos e seus propósitos específicos. Portanto, a seção a seguir abordará com mais detalhes essa classificação.
Além dos elementos básicos apresentados para a modelagem visual de um padrão de integração, existe uma imagem específica e única que o representa, isto é, uma figura composta por elementos base que procuram indicar de forma direta o objetivo daquele padrão. Para a relação completa dos padrões com suas imagens de representação visual, verifique a seção Links.
Classificações dos padrões
A integração entre sistemas abrange muitos problemas de domínio e negócio e por isso um bom recurso é dividir os padrões em categorias que refletem seu escopo e suas abstrações. Baseado nessa ideia, os autores do livro classificaram os padrões em seis categorias. Para apresentar a divisão proposta, a Figura 7 exibe um diagrama completo com os padrões agrupados em suas respectivas categorias.
Figura 7. Categorias e seus respectivos padrões de integração.
Conforme observa-se nessa imagem, os padrões estão classificados em:
- Canais de Mensagens (Messaging Channels): oferecem um meio para que aplicações possam trocar dados através de um canal conhecido, diminuindo bastante o acoplamento entre os envolvidos. A aplicação que envia o dado pode não saber exatamente qual sistema irá recebê-lo, mas há garantia de que este será enviado à aplicação correta em virtude da utilização de um canal em comum;
- Construção de Mensagem (Message Construction ou Messaging Patterns): oferecem soluções que descrevem as várias formas, funções e atividades que envolvem a criação e transformação da mensagem a ser trafegada entre os sistemas;
- Roteamento de Mensagens (Messaging Routing): oferecem mecanismos para direcionar mensagens de um remetente para um destinatário através de canais de comunicação como, por exemplo, filas e tópicos. Essas mensagens roteadas podem ser divididas, redirecionadas separadamente e reagrupadas posteriormente. Porém, vale ressaltar que esses padrões não alteram o conteúdo de uma mensagem. Eles apenas movem a mesma de um canal para outro;
- Transformação de Mensagem (Message Transformation): visam mudar o conteúdo de uma mensagem para diferentes necessidades de envio e recebimento. Em alguns cenários, dados precisam ser retirados ou adicionados para que as mensagens sejam reconstruídas;
- Terminais de Mensagens (Messaging Endpoints): esses padrões oferecem uma interface entre a aplicação e a API do servidor de mensageria (vide BOX 1), servindo como um “ponto de ligação”. Com isso, a aplicação pouco conhece sobre os formatos de mensagens e canais, pois o padrão encapsula as particularidades do servidor de mensageria;
- Gerenciamento do Sistema (System Management): proveem ferramentas para manter e gerenciar um complexo sistema baseado em mensageria funcionando, haja vista que existem soluções que podem processar bilhões de mensagens por dia. Os padrões dessa categoria lidam com condições de erro, gargalos de desempenho e mudanças nos sistemas.
BOX 1. Servidor de mensageria
O transporte de mensagens em canais de comunicação é tipicamente provido por um sistema de software separado, chamado de servidor de mensageria ou middleware orientado a mensagem (MOM – Message Oriented Middleware). O motivo pelo qual há a necessidade de se utilizar um sistema de mensageria é garantir que a mensagem seja entregue de um sistema a outro, pois as redes que os conectam são incertas e podem falhar a qualquer momento.
Exibidas as categorias e seus respectivos objetivos, o próximo passo é se aprofundar e apresentar em detalhes os principais padrões de integração, incluindo o(s) problema(s) que o mesmo busca atender e a solução proposta.
Todas as figuras que serão apresentadas durante o estudo sobre os principais padrões foram retiradas do próprio site dos EIPs, disponível na seção Links.
Canais de mensagens (Messaging Channels)
Os principais padrões dessa categoria são:
- Message Channel: busca solucionar o problema de como uma aplicação pode comunicar-se com outra usando mensageria. A solução consiste em utilizar um canal de mensagens comum, onde uma aplicação grava a informação no canal e a outra lê. A Figura 8 ilustra o conceito desse padrão;
Figura 8. Message Channel.
- Point-to-Point Channel: busca solucionar o problema de como o remetente pode ter certeza que apenas um destinatário irá receber a mensagem ou executar um comando. A solução consiste em enviar a mensagem através de um canal ponto-a-ponto, onde há a garantia de que apenas um destinatário receberá a mensagem. A Figura 9 ilustra o conceito do padrão;
Figura 9. Point-to-Point Channel.
- Publish-Subscribe Channel: busca solucionar o problema de como um remente pode transmitir uma mensagem a todos os destinatários interessados. A solução consiste em enviar a mensagem para um canal (tópico) que entrega uma cópia da mesma para cada destinatário. A Figura 10 ilustra o conceito do padrão;
Figura 10. Publish-Subscribe Channel.
- Channel Adapter: busca solucionar o problema de como conectar uma aplicação ao servidor de mensageria para que ela possa enviar e receber mensagens. A solução consiste em acessar a API da aplicação ou seus dados, publicar mensagens em um canal baseado nesses dados, receber mensagens e chamar funcionalidades dentro da aplicação. A Figura 11 ilustra o conceito do padrão.
Figura 11. Channel Adapter.
Construção de mensagens (Message Construction ou Messaging Patterns)
Os padrões com maior destaque na categoria de construção de mensagens são:
- Message: busca solucionar o problema de como duas aplicações conectadas por um canal de mensagens podem trocar informação. A solução consiste em empacotar a informação dentro de uma mensagem, ou seja, um registro de dados que o sistema de mensageria pode transmitir através de um canal de mensagens. A Figura 12 exemplifica o funcionamento desse padrão;
Figura 12. Message.
- Request-Reply: busca solucionar o problema de como uma aplicação pode obter uma resposta do servidor ao enviar uma mensagem. A solução consiste em enviar um par de mensagens request-response em canais diferentes, isto é, a mensagem de envio é publicada em um canal e a resposta do servidor é publicada em outro, para que os sistemas envolvidos na integração possam identificar onde deve enviar/receber uma mensagem de request-response. A Figura 13 exemplifica o funcionamento desse padrão;
Figura 13. Request-Reply.
- Command Message: busca solucionar o problema de como uma aplicação pode invocar uma função de outra aplicação. A solução consiste em usar uma mensagem de comando para chamar, de forma confiável, uma função em outra aplicação. A Figura 14 exemplifica o funcionamento desse padrão;
Figura 14. Command Message.
- Message Expiration: busca solucionar o problema de como o remente da mensagem pode indicar quando a mesma deve ser descartada ou ignorada. A solução consiste em definir um limite de tempo para que a mensagem seja considerada válida. A Figura 15 exemplifica o funcionamento desse padrão.
Figura 15. Message Expiration.
Roteamento de mensagens (Messaging Routing)
Atualmente, os padrões mais utilizados para atender cenários onde o roteamento de mensagens é necessário são:
- Message Router: busca solucionar o problema de como desacoplar passos individuais de processamento de forma que mensagens possam ser passadas para filtros diferentes dependendo de uma série de condições. A solução consiste em utilizar um roteador que consome a mensagem de um canal e publica em outro dependendo de uma série de condições. Para conceituar a solução proposta, a Figura 16 ilustra o padrão;
Figura 16. Message Router.
- Content-Based Router: busca solucionar o problema de como uma aplicação deve lidar com o roteamento de uma mensagem quando existem vários destinatários disponíveis para a receberem. Dessa forma, a solução consiste em rotear cada mensagem para o receptor de acordo com o conteúdo da mensagem. Para conceituar a solução proposta, a Figura 17 ilustra o padrão;
Figura 17. Content-Based Router.
- Recipient List: busca solucionar o problema de como rotear uma mensagem para uma lista de destinatários especificados dinamicamente. A solução consiste em definir um canal de comunicação para cada destinatário, inspecionar a mensagem entrante com base em uma lista de destinatários desejados e então repassar a mensagem para todos os canais associados com os destinatários da lista. Para conceituar a solução proposta, a Figura 18 ilustra o padrão;
Figura 18. Recipient List.
- Sliptter: busca solucionar o problema de como uma mensagem pode ser processada quando possui muitos elementos que devem ser processados de maneira diferente. A solução consiste em dividir a mensagem em uma série de outras mensagens individuais, cada qual contendo dados relacionados. Para conceituar a solução proposta, a Figura 19 ilustra o padrão;
Figura 19. Sliptter.
- Aggregator: busca solucionar o problema de como agrupar mensagens individuais que estão relacionadas e devem ser processadas como uma só. A solução consiste em coletar, armazenar e agrupar todas as mensagens individuais relacionadas e então publicar uma única mensagem. Para conceituar a solução proposta, a Figura 20 ilustra o padrão;
Figura 20. Aggregator.
- Message Filter: busca solucionar o problema de como uma aplicação pode evitar o recebimento de mensagens indesejadas. A solução consiste em utilizar um filtro para eliminar mensagens indesejadas de um canal com base em um conjunto de critérios. Para conceituar a solução proposta, a Figura 21 ilustra o padrão.
Figura 21. Message Filter.
Transformação de mensagens (Message Transformation)
Na categoria de transformação de mensagens os padrões de maior relevância são:
- Message Translator: busca solucionar o problema de como realizar a comunicação usando mensageria para aplicações que usam formatos de dados diferentes. A solução consiste em utilizar um filtro para traduzir de um formato de dado específico para o outro. A Figura 22 retrata esse padrão;
Figura 22. Message Translator.
- Content Enricher: busca solucionar o problema de como uma aplicação vai trafegar a mensagem se a mesma não possui todos os dados necessários para o destinatário seguinte. A solução consiste em buscar num repositório externo os dados faltantes da mensagem. A Figura 23 retrata esse padrão;
Figura 23. Content Enricher.
- Content Filter: busca solucionar o problema de como diminuir o tamanho de uma mensagem, uma vez que nem todos os dados da mensagem são importantes. A solução consiste em utilizar um filtro para remover os dados que não são necessários. A Figura 24 retrata esse padrão;
Figura 24. Content Filter.
- Claim Check: busca solucionar o problema de como reduzir o tamanho de uma mensagem sem danificar o seu conteúdo. A solução consiste em guardar os dados da mensagem num repositório persistente e passar um comprovante da requisição para as aplicações seguintes para que quando necessário as mesmas possam utilizar esse comprovante para recuperar os dados armazenados. A Figura 25 retrata esse padrão.
Figura 25. Claim Check.
Terminais de mensagens (Messaging Endpoints)
Os padrões de terminais de mensagens (endpoints) com maior relevância são:
- Message Endpoint: busca solucionar o problema de como uma aplicação pode conectar-se num canal de mensageria para enviar e receber mensagens. A solução consiste em utilizar um cliente ou API do sistema de mensageria para enviar e receber mensagens. A Figura 26 ilustra o padrão;
Figura 26. Message Endpoint.
- Messaging Gateway: busca solucionar o problema de como isolar o acesso ao sistema de mensageria do restante da aplicação. A solução consiste em utilizar uma classe que encapsula chamadas específicas ao sistema de mensageria e expõe uma interface com métodos específicos ao domínio da aplicação. A Figura 27 ilustra o padrão;
Figura 27. Messaging Gateway.
· Service Activator: busca solucionar o problema de como projetar um serviço que possa ser chamado de forma síncrona (sem o uso de mensageria) ou de forma assíncrona (com o uso de mensageria). A solução consiste em projetar um ativador de serviços que conecta as mensagens do canal ao serviço da aplicação. A Figura 28 ilustra o padrão;
Figura 28. Service Activator.
Gerenciamento do sistema (System Management)
Por fim, os padrões para gerenciamento do sistema em maior evidência atualmente são:
- Message History: busca solucionar o problema de como analisar e depurar de forma efetiva todo o fluxo da mensagem. A solução consiste em anexar junto a mensagem a lista de todas as aplicações em que a mesma passou desde sua origem. A Figura 29 apresenta o padrão;
Figura 29. Message History.
- Control Bus: busca solucionar o problema de como administrar e monitorar de forma efetiva todo o sistema de mensageria, muitas vezes distribuído em plataformas e localizações geográficas distintas. A solução consiste em aproveitar o mesmo mecanismo de mensageria das aplicações para publicar durante o fluxo mensagens de monitoramento em um canal específico. A Figura 30 apresenta o padrão;
Figura 30. Control Bus.
- Detour: busca solucionar o problema de como executar funções de validação, teste ou depuração, quando necessário, nas mensagens. A solução consiste em utilizar um roteador com um controle de condição para desviar a mensagem para etapas ou componentes intermediários antes de publicá-la de fato no canal de destino. A Figura 31 apresenta o padrão.
Figura 31. Detour.
Apresentados os detalhes e funcionamento dos principais padrões de integração, é interessante analisarmos agora as tecnologias que estão em maior destaque no mercado e que se baseiam fortemente nos conceitos mencionados até aqui. É sobre isso que trataremos na seção a seguir.
Tecnologias para integração entre sistemas
Atualmente já estão consolidados no mercado vários frameworks e ESBs (vide BOX 2) para auxiliar os desenvolvedores na construção de sistemas voltados para integração, diminuindo a complexidade e garantindo que boas práticas identificadas e amadurecidas ao longo de anos sejam respeitadas. Alguns exemplos são:
- Microsoft BizTalk Server: conhecido também como BizTalk, trata-se de um ESB que através do uso de adaptadores permite que sistemas diferentes possam se comunicar e automatizar processos de negócio;
- IBM WebSphere Message Broker: o WMB é um ESB da IBM pertencente à família de produtos WebSphere. Este possibilita que as informações de negócio sejam trafegadas entre os diversos sistemas dos mais variados hardwares e plataformas de software. O produto opera sob uma Arquitetura Orientada a Serviços (vide BOX 3);
- Mule ESB: é um ESB e também framework para integração entre sistemas capaz de lidar com sistemas desenvolvidos com tecnologias totalmente diferentes e também que disponibilizam formas de comunicação distintas para troca de informação. A plataforma é baseada em Java, mas permite interações entre outras plataformas, como .NET, usando serviços web e sockets;
- Apache Camel: é um framework para integração entre sistemas cuja principal função é servir como um motor de mediação e roteamento baseado em regras. Escrito em Java e de código fonte aberto sob a licença da Apache 2, implementa a maioria dos EIPs;
- Niklas Integration Platform: Niklas é uma plataforma de integração corporativa comercial open source. Suporta a transformação e transporte entre os vários tipos de dados e pode processar arquivos de formato livre, assim como EDIs padronizados e XML;
- Spring Integration: é um framework open source para integração entre sistemas que possui uma estrutura leve que se baseia no núcleo do famoso framework Spring.
No cenário atual o Apache Camel se destaca por ser uma solução gratuita, extremamente flexível, baseada nos EIPs e ser uma excelente opção para substituir um ESB, pois é muito mais simples e não perde em comparação aos recursos. Outro diferencial é que ele é de fácil integração com o Spring, acrescentando assim todo o potencial do já consagrado framework. Diante dessas vantagens, a seguir será apresentado em detalhes o Apache Camel, para que o desenvolver possa conhecê-lo melhor e dessa forma acrescentá-lo em seu repertório de soluções.
BOX 2. ESB
ESB ou Enterprise Service Bus é uma plataforma de integração baseada em normas que combina as funcionalidades de mensageria, web services, transformação de dados e roteamento inteligente para conectar e coordenar com segurança a interação de um número significativo de sistemas diversos por meio do conceito de corporação estendida, isto é, sistemas que ligam as unidades locais da corporação, os parceiros de negócios, fornecedores e clientes. Para oferecer as funcionalidades mencionadas anteriormente, um ESB tem como premissa o baixo acoplamento na comunicação, além da utilização de redes distribuídas que possam ser facilmente escaláveis.
BOX 3. Arquitetura Orientada a Serviços
Arquitetura Orientada a Serviços ou simplesmente SOA é uma abordagem para definir arquiteturas de integração baseadas no conceito de serviço, sendo serviço uma função de um sistema computacional que é disponibilizada para outro sistema. Para atingir esse objetivo, SOA baseia-se na orientação a objetos, componentização e padrões de integração, visando trazer para a solução os benefícios do baixo acoplamento e encapsulamento.
Apache Camel
Concebido inicialmente como um subprojeto do Apache ActiveMQ (vide BOX 4), o Apache Camel foi criado por um grupo de engenheiros de software e lançado no dia 2 de julho de 2007. Completando quase dez anos, esta solução ganhou mercado, foi base para a criação de muitos sistemas de integração, evoluiu e hoje é um framework maduro e consolidado. Por estas razões, possui atualmente uma comunidade extremamente forte, competente e ativa.
Camel é o acrônimo para Concise Application Message Exchange Language e recebeu esse nome principalmente pelo fato do nome Camel ser pequeno e fácil de ser lembrado. Porém, há também outros motivos que levaram a essa escolha, incluindo o fato de um dos criadores gostar de cigarros da marca americana Camel. A lista completa com todos os motivos pode ser encontrada na seção Links.
Com poucas linhas de código ou configuração é possível processar arquivos, invocar web services, publicar mensagens em um canal de mensageria, entre diversos outros recursos que precisariam de muito código, bibliotecas externas e controle interno. Portanto, nota-se que o Apache Camel provê simplicidade nas soluções de sistemas complexos, tornando a integração bastante produtiva, principalmente por trazer de forma embutida e de fácil aplicação os padrões EIPs. A ideia principal é que, com ele, os desenvolvedores foquem na implementação das regras e fluxos de negócio.
Ademais, o Apache Camel é uma excelente opção para oferecer roteamento, transformação de dados e regras de mediação na integração entre sistemas, sendo um dos mais completos frameworks de código fonte aberto para essa finalidade. Portanto, conhecer as principais características e as vantagens em utilizar o Camel é extremamente importante.
BOX 4. Apache ActiveMQO Apache ActiveMQ é um dos mais populares servidores de mensageria (MOM) de código fonte aberto. Escrito em Java, implementa a especificação JMS 1.1 (Java Message Service) e pode ser utilizado em modo standalone, embarcado na própria aplicação ou em um servidor Java EE. Além disso, é rápido, facilmente integrado aos EIPs, possui suporte a várias linguagens e protocolos de comunicação.
Principais características do Apache Camel
As principais características do Camel são:
- Engine de roteamento e mediação: representa a sua característica principal, ou seja, é o núcleo do framework. Esse mecanismo encaminha mensagens seletivamente, com base na configuração de rotas. As rotas indicam de qual endpoint a mensagem deve ser consumida e para qual deve ser enviada, além da possibilidade de serem criadas com uma combinação de implementações dos EIPs e uma linguagem específica de domínio (DSL);
- Padrões empresariais de integração (EIPs): implementa grande parte dos padrões de integração do livro de Gregor Hohpe e Bobby Woolf. Para verificar a lista completa dos padrões implementados pelo Apache Camel, verifique a seção Links;
- Linguagem específica de domínio: do inglês DSL (Domain Specific Language), trata-se de uma linguagem criada para resolver os problemas de um domínio específico, ao contrário de linguagens de propósito geral, que são criadas para lidar com vários contextos. O Apache Camel possui uma DSL específica para trabalhar com a integração entre sistemas, sendo a principal e mais robusta baseada em Java, mas não se resume a isso. Possui também suporte a linguagens como Scala, Groovy e XML;
- Biblioteca de componentes: possui uma ampla biblioteca, contendo atualmente cerca de 220 componentes para as mais variadas tecnologias, recursos e formatos de dados. Também abrange, de forma adicional, cerca de 23 linguagens de expressão e predicados, tais como JavaScript, Python, Ruby, BeanShell e Spring Expression Language, para que os mesmos auxiliem a implementação dos EIPs nas rotas em combinação com a DSL. Ademais, é possível customizar e desenvolver componentes;
- Roteamento flexível: pode rotear qualquer mensagem, pois não está restrito ao conteúdo desta. Essa facilidade permite que não seja necessário transformar o conteúdo da mensagem em um formato canônico para facilitar o roteamento;
- Arquitetura modular e plugável: o Apache Camel possui uma arquitetura modular e plugável, permitindo que qualquer um de seus componentes seja carregado independentemente de ser distribuído junto com as bibliotecas do framework, ser um componente de terceiros ou mesmo uma customização própria;
- Modelo baseado em POJO: Plain Old Java Objects são os elementos principais para a extensão do framework e podem ser usados em qualquer lugar e a qualquer momento no sistema. Isso possibilita estender funcionalidades internas do Camel com código personalizado;
- Configuração simplificada: segue o paradigma da convenção sobre configuração, que consiste em, sempre que possível, minimizar os requisitos necessários para configuração. Para isso, o Apache Camel utiliza uma configuração fácil e intuitiva;
- Framework leve (Lightweight core): o núcleo do Apache Camel é leve. A biblioteca total possui poucos megabytes e poucas dependências externas. Com isso, é possível incorporá-lo em qualquer aplicação standalone, aplicação Java Web ou Java EE, Google App Engine, entre outras;
- Conversores automáticos de tipos: possui um mecanismo interno de conversão de tipos onde não há necessidade de configurar regras de conversão;
- Kit para testes: disponibiliza um kit de testes que torna mais fácil testar as aplicações Camel. Inclusive é usado extensivamente para testar o próprio código;
- Comunidade ativa: o projeto possui uma comunidade atuante e competente, que contribui oferecendo suporte, sanando dúvidas e implementando novas funcionalidades. Para acessar a página da comunidade, veja o endereço indicado na seção Links.
Vale ressaltar que o Apache Camel não é um ESB, embora contenha algumas funcionalidades deste. O Apache Camel roda embutido em uma aplicação, enquanto um ESB é um container/servidor separado e um produto complexo com mais funcionalidades, tais como: monitoramento e gerenciamento de processos, balanceamento de carga, alta disponibilidade, registro de serviços, segurança e disponibilização de ferramentas (tooling), entre outras. Por sua vez, o Apache Camel foca apenas no roteamento e mediação de mensagens.
Arquitetura do Apache Camel
A Figura 32 apresenta a arquitetura do Apache Camel em alto nível. Observe que a Engine de Roteamento usa as rotas definidas em DSL para saber para onde as mensagens devem ser enviadas. Na sequência, os Processadores são utilizados para transformar e manipular as mensagens durante o roteamento e também para implementar os EIPs; e os Componentes, para fazer a comunicação com outros sistemas. Por fim, para que o framework funcione corretamente, é necessário juntar os elementos citados em um contexto padrão do Apache Camel, para que cada um possa desempenhar sua função.
Figura 32. Arquitetura do Apache Camel.
Como verificado, essa figura apresenta muitos conceitos importantes sobre o funcionamento e estrutura do Apache Camel, tais como: contexto, processadores, engine de roteamento de mensagens, endpoints e DSL. Para entender a fundo esses conceitos, os tópicos a seguir analisarão cada um, além de trazer uma explicação sobre o modelo de mensagens disponível no framework.
Modelo de mensagens
O Apache Camel oferece dois tipos de modelo de mensagem para que sejam utilizados conforme o cenário ou contexto da integração. A escolha do tipo deve ser adequada para representar o objetivo da troca de informações (comunicação) entre os sistemas envolvidos. O primeiro modelo, denominado Message, é designado para lidar com dados e fluxos simples, de modo que a mensagem é recebida pelo componente, seus dados são lidos e utilizados e na sequência é simplesmente “descartada”, enquanto o segundo, conhecido como Exchange, tem como principal função lidar com “conversas”, ou seja, informações que são trocadas entre componentes e que carregam conteúdo relevante capturado ao longo do fluxo. Na sequência, os dois modelos serão detalhados:
- Message (org.apache.camel.Message): é a entidade fundamental, que contém os dados que serão trafegados pelas rotas. Possui um body, headers, anexos (opcional), um identificador único e uma flag para sinalizar algum erro (fault flag). A Figura 33 apresenta a estrutura desse modelo de mensagem;
Figura 33. Estrutura de modelagem de uma Message.
- Exchange (org.apache.camel.Exchange): é uma abstração para troca de mensagens entre rotas que incorpora o padrão MEPs (Message Exchange Patterns) para diferenciar comunicações assíncronas (InOnly) e síncronas (InOut). Esse tipo de modelagem encapsula uma Message para representar a mensagem de entrada (In message) e outra para a de saída (Out message), uma exceção que venha ocorrer durante o fluxo, propriedades que são armazenadas durante todo o tempo de troca de mensagens e um identificador único chamado de Exchange ID. A Figura 34 apresenta a estrutura desse modelo de mensagem.
Figura 34. Estrutura de modelagem de uma Exchange.
Dados os dois modelos de mensagens, o funcionamento interno do Apache Camel para trabalhar com ambos se inicia quando um consumidor de requisições da aplicação é invocado, isto é, um consumidor recebe mensagens. Nesse momento, automaticamente um Exchange é criado com uma Message incorporada em sua estrutura. Essa Message contém em seu body os dados da mensagem como, por exemplo, um JSON.
Na sequência, o Exchange inicia seu ciclo de vida e será utilizado e manipulado por toda a cadeia de processadores da rota, considerando a seguinte regra: a mensagem de saída (Out message) produzida pelo processador anterior é definida como a mensagem de entrada (In message) para o próximo. Se a mensagem de saída não for definida, então a mensagem de entrada continuará sendo repassada. Ao final da cadeia de processadores, a última mensagem de saída ou entrada (dependendo do cenário) será enviada de volta para o chamador original da requisição.
CamelContext
Conforme pode-se observar na Figura 32, o CamelContext é um container ou sistema de execução que mantém todos os outros recursos e mecanismos necessários juntos e conectados, provendo serviços úteis para o funcionamento do framework. A Figura 35 mostra esses serviços e a Tabela 2 descreve os detalhes dos mesmos.
Figura 35. Serviços que o CamelContext provê.
Serviço |
Descrição |
Componentes |
Contém os componentes utilizados. Permite que os componentes sejam carregados na medida em que forem necessários através de detecção automática no classpath. |
Endpoints |
Contém os endpoints que serão criados. |
Rotas |
Contém as rotas que serão adicionadas. |
Conversores |
Contém os conversores que viabilizam que tipos de dados sejam convertidos de forma manual ou automática. |
Formato dos dados |
Contém os formatos de dados carregados. |
Registro |
Contém um registro que, por padrão, possibilita localizar os beans através de JNDI (vide BOX 5). |
Linguagens |
Contém as linguagens de expressões e predicados carregados. É permitido que diferentes linguagens sejam utilizadas para criar expressões. |
Tabela 2. Serviços e suas descrições.
BOX 5. JNDI
JNDI ou Java Naming and Directory Interface possibilita a associação de um nome (ou uma representação alternativa mais simples) a recursos computacionais como: endereços de memória, de rede, de serviços, objetos e código em geral. Suas funções básicas são associar (mapear) um nome a um recurso e localizar um recurso a partir de seu nome.
Engine de roteamento
A engine ou mecanismo de roteamento é o que realmente move as mensagens e faz todo o trabalho pesado garantindo que as mensagens sejam encaminhadas corretamente. No entanto, ela não é exposta aos programadores.
A engine trabalha com o conceito de rotas – abstração base para o Camel – que são definidas através do uso de DSLs e consistem basicamente na declaração de um ou mais endpoints cuja lógica entre eles pode ser ou não a representação de um dos EIPs. Logo, a utilização de rotas pode oferecer algumas vantagens para aplicações que utilizam mensagens. Dentre essas vantagens, vale ressaltar:
- Desvinculam clientes de servidores;
- Desvinculam os produtores de mensagens dos consumidores;
- Podem decidir de forma dinâmica qual servidor um cliente deve invocar;
- Permitem que sistemas clientes e servidores sejam desenvolvidos de forma independente;
- Permitem que clientes de servidores sejam substituídos por mocks para realização de testes;
- Permitem promover as melhores práticas de design para conectar diferentes sistemas;
- Melhoram as funcionalidades e recursos de alguns sistemas.
Além das vantagens citadas, as rotas também possuem um identificador único usado para log, debug, monitoramento, inicialização e finalização do funcionamento da própria rota.
Linguagem específica de domínio
A linguagem específica de domínio ou DSL é uma linguagem de programação específica para solucionar os problemas de um domínio em particular. Por exemplo, o Apache Camel possui uma DSL baseada na linguagem Java com notações e definições próprias voltadas para atender o cenário de integração entre sistemas, facilitando dessa forma o entendimento e o foco na solução do problema.
Como um dos seus diferenciais, o Apache Camel oferece múltiplas DSLs para diversas linguagens de programação, como Java e Scala, além de permitir que as regras de roteamento de mensagens sejam especificadas em um arquivo XML com o auxílio do framework Spring. Esse variado suporte, como recém-mencionado, é uma das grandes vantagens que essa solução oferece em relação aos seus concorrentes.
Para exemplificar o uso de DSLs, na Tabela 3 são exibidos exemplos baseados em diferentes linguagens com funcionalidades equivalentes.
Camel DSL com Java |
Camel DSL com XML e auxílio do Spring |
Camel DSL com Scala |
from(“file:/tmp”).to(“jms:aQueue”); |
<route> <from uri=“file:/tmp”/> <from uri=“jms:aQueue”/> </route> |
from “file:/tmp” -> “jms:aQueue” |
Tabela 3. Exemplos de DSLs em diferentes linguagens.
Processador
Conhecido também como Processor (do inglês), é definido através da implementação da interface Processor (org.apache.camel.Processor), que disponibiliza o método process(Exchange) a ser sobrescrito e a lógica de processamento seja implementada, incluindo as devidas manipulações no conteúdo da mensagem. Assim, os processadores podem ou não implementar EIPs para atender um cenário específico de integração.
Componentes
São as implementações de mais baixo nível no Apache Camel, pois são os responsáveis pela definição do protocolo ou tecnologia a ser utilizada para que um sistema se conecte a um endpoint. Para realizar tal tarefa o componente necessita de uma referência do CamelContext (org.apache.camel.CamelContext) para que seja possível criar o endpoint e estabelecer de fato a comunicação. A Figura 36 exibe um diagrama que demonstra o relacionamento e funcionamento de um componente.
Figura 36. Funcionamento básico de um componente.
Diante dos conceitos apresentados e da possibilidade de criação/customização, a lista de componentes é ampla e abrange mecanismos de conectividade, acesso a banco de dados, automação de tarefas, log, notificação, entre outros, além de cada vez mais crescer e se consolidar. Para se ter uma ideia, na versão 2.5.0 do Camel haviam cerca de 80 componentes. Já na versão mais atual, a 2.16.1 (até a escrita desse artigo), existem aproximadamente 220.
Considerando toda essa lista de componentes, a Tabela 4 apresenta, de forma sucinta, os mais utilizados pelas aplicações atualmente. Para a relação e descrição completa, além de exemplos dos componentes, veja o endereço indicado na seção Links.
Recurso |
Componente |
Leitura e escrita de arquivos (I/O) |
File |
Mensagens assíncronas |
JMS |
Web services |
CXF |
Redes |
MINA FTP |
Banco de Dados |
JDBC JPA |
Mensagens em memória |
Direct SEDA VM |
Automação de tarefas |
Timer Quartz |
Log |
Log |
Tabela 4. Principais componentes do Apache Camel.
Endpoint
No Apache Camel, um endpoint é uma abstração que modela o fim de um canal de comunicação e é através dele que as mensagens são enviadas e recebidas pelos sistemas. Para que isso ocorra, um endpoint deve ser criado utilizando uma URI (Uniform Resource Identifier).
Para demonstrar como um endpoint é declarado e criado programaticamente e também qual é sua composição básica, a Figura 37 apresenta um código de exemplo a partir do qual arquivos são lidos de um determinado diretório – no caso, /data/inbox – em intervalos de cinco segundos.
Nessa figura, observa-se que o endpoint (que é formado pela linha inteira) foi dividido em três partes para facilitar a explicação. A primeira parte, representada por Scheme, refere-se ao tipo de componente que o Apache Camel deve utilizar; neste caso, trata-se do File, para manipulação de arquivos. Na segunda parte temos o Context path, que se trata do diretório que o componente anterior deve considerar como base para a leitura de arquivos. Por fim, na terceira parte, denominada de Options, é especificado um parâmetro para que a leitura ocorra em intervalos de cinco segundos.
Figura 37. Exemplo e detalhes da composição de um endpoint.
Produtor
Conhecido também como Producer (do inglês), é a abstração responsável por, internamente no framework, criar e enviar mensagens para um endpoint. Para conhecer como um produtor funciona e como está relacionado aos conceitos do Apache Camel, observe a Figura 38.
Figura 38. Ilustração de como um produtor funciona no Apache Camel.
Veja que é possível observar que o producer cria e popula um exchange com dados compatíveis que foram recuperados do endpoint para o qual a mensagem será enviada posteriormente. Essa funcionalidade é um recurso importante do Apache Camel porque abstrai toda a complexidade necessária de interação/implementação do desenvolvedor com os mecanismos de transporte de mensagem. Logo, basta configurar uma rota para enviar uma mensagem a um endpoint que o produtor fará o trabalho pesado.
Consumidor
Conhecido também como Consumer (do inglês), é a abstração responsável por receber as mensagens enviadas pelo produtor e para auxiliar nesse processo, existem dois tipos de consumidores, a saber:
- Event-Driven Consumer (consumidor dirigido a evento): é um consumidor assíncrono que fica aguardando que um cliente envie uma mensagem em um canal de comunicação e quando a mesma chega, imediatamente a consome e envia para processamento, ficando, logo em seguida, disponível novamente para consumir outra mensagem. Para ilustrar seu funcionamento, a Figura 39 é exibida;
Figura 39. Ilustração de um consumidor do tipo dirigido a evento.
- Pooling Consumer (consumidor centralizador): é um consumidor síncrono e em contraste com o tipo dirigido a evento, fica aguardando ativamente que um cliente envie uma mensagem em um canal de comunicação e quando uma mensagem chega, consome a mesma e não aceita nenhuma outra até que o processamento atual seja concluído. Para ilustrar seu funcionamento, a Figura 40 é exibida.
Figura 40. Ilustração de um consumidor do tipo centralizador.
Segurança
A segurança é um dos requisitos mais importantes dentro de qualquer sistema e quando o cenário é integração, temas como confidencialidade, autenticidade, disponibilidade e integridade são de extrema importância e devem ser devidamente atendidos. Nessas condições, o Apache Camel oferece muitas formas e níveis de segurança que podem ser utilizados no roteamento de mensagens, inclusive, empregadas em conjunto. As principais formas e níveis de segurança são aplicáveis em:
- Roteamento das mensagens: oferece serviços de autenticação e autorização que podem ser adicionados em rotas e segmentos de rotas. Para isso, é utilizado um padrão de estratégia para aplicar interceptadores nos processadores de mensagens. Os principais componentes dessa categoria são Shiro Security e Spring Security;
- Conteúdo das mensagens: oferece serviços de criptografia e descriptografia para assegurar o conteúdo das mensagens ou até mesmo partes desse conteúdo. Os principais componentes dessa categoria são XMLSecurity DataFormat, XML Security component, Crypto DataFormat e Crypto component;
- Endpoint: alguns componentes utilizados para comunicação e troca de mensagens oferecem recursos para proteger seus endpoints através de interceptadores, autenticação e autorização. Os principais componentes dessa categoria são Jetty, CXF, Spring Web Services, Netty, MINA, CometD e JMS;
- Arquivos de configuração: oferece um componente de propriedades chamado Properties para externalizar os valores e configurações para arquivos. Os valores dessas propriedades podem conter nomes de usuário e senhas, por exemplo, e por isso são criptografados e descriptografados automaticamente.
Erros e exceções
Em sistemas simples, cujo o controle do fluxo está totalmente dentro de seu contexto e ambiente, é fácil capturar erros e tratá-los, mas quando o cenário é de integração, um grande desafio é lidar com erros e problemas inesperados.
Consequentemente, vários são os riscos associados, como: a rede pode ficar indisponível, o sistema remoto pode responder em um tempo inadequado ou simplesmente pode ocorrer um evento de falha que a princípio apresenta razões obscuras. Além disso, problemas que não estão diretamente relacionados ao contexto de integração também podem ocorrer, como: disco cheio no servidor, falta de memória e lentidão devido ao alto consumo do processador.
Efetivamente, sistemas devem estar preparados para lidar com erros, problemas e situações inesperadas. Nessas condições, quando ocorrer um erro será possível dividi-lo em recuperável e irrecuperável. Um erro irrecuperável acontece quando, independentemente da quantidade de tentativas que o sistema faça para realizar uma tarefa ou função específica, ele sempre vai ocorrer. Um exemplo desse tipo de erro é verificado quando o sistema tenta acessar uma tabela que não existe no banco de dados. Já um erro recuperável é temporário, ou seja, ele pode não causar um problema na próxima tentativa. Um bom exemplo são as instabilidades de rede, que podem levar à perda de conexão em certos momentos, mas depois voltam a funcionar. Para contextualizar os dois tipos abordados, a Figura 41 apresenta um cenário.
Figura 41. Classificação dos erros e conceito de retentativa com insucesso e sucesso.
Com base nisso, o Apache Camel orienta que um erro recuperável é derivado de Throwable (java.lang.Throwable) ou Exception (java.lang.Exception) e deve ser definido através do uso de um método exclusivo chamado setException(Throwable cause) em um Exchange. Já um erro irrecuperável é representado por uma Message (que está contida em um Exchange) com uma mensagem de texto no body indicando o motivo ou descrição do erro e também pela definição de uma flag para indicar que houve falha, sendo o valor dessa flag informado através do método setFault(true).
Para auxiliar no processo de tratamento de erros, o Apache Camel possui alguns manipuladores ou error handlers para as exceções do tipo recuperável. Assim, através da exceção definida em Exchange, conseguem recuperar dados e tratar o erro. Já para as exceções do tipo irrecuperável não há como utilizar os manipuladores, pois são apenas mensagens com uma flag habilitada. Para conhecer os manipuladores mais básicos de erros recuperáveis, observe a Tabela 5.
Manipulador de Erro |
Descrição |
DefaultErrorHandler |
Esse é o manipulador de erro padrão. Está previamente habilitado, para o caso de outro manipulador não ser configurado. |
DeadLetterChannel |
Manipulador que implementa o padrão Dead Letter Channel (EIP). |
K!TransactionErrorHandler |
Essa opção estende o manipulador padrão e está associado a erros em transações. |
NoErrorHandler |
Manipulador que desabilita todos os manipuladores de erro. |
LoggingErrorHandler |
Manipulador que apenas faz o log da exceção ocorrida. |
Tabela 5. Manipuladores de erro oferecidos pelo Apache Camel.
Além de todos esses recursos para o tratamento de erros, o Apache Camel suporta escopos que podem ser utilizados para definir diferentes níveis de manipuladores de erro. As opções de escopos são: de contexto (context scope), cujo acesso é global, ou seja, toda a aplicação pode herdar/acessar; e também o de rota (route scope), que está restrito a uma rota específica.
Para complementar toda essa estrutura, o Apache Camel permite ainda aplicar regras e políticas de exceções através da captura de erros com a declaração do método onException() na própria rota, cuja finalidade é interceptar exceções específicas a serem tratadas.
Testes
Em projetos cujo foco é a integração entre sistemas, os testes são fundamentais para aumentar ainda mais as chances de sucesso, justamente devido a todo o processo de integração, que na maioria das vezes é complexo.
Visando atender essa situação, o Apache Camel oferece um kit para tornar a implementação mais rápida utilizando como base o JUnit. A Figura 42 exibe, em alto nível, a estrutura desse kit.
Figura 42. Kit de testes disponibilizado em dois arquivos JAR.
Observa-se a existência de três elementos nessa imagem: JUnit extensions, que representa as classes do framework JUnit que facilitam os testes; Mock component, que como o próprio nome sinaliza, serve para simular componentes; e ProducerTemplate (producer), que é o mecanismo que permite enviar mensagens durante os testes.
Porém, não basta ter todo esse ferramental em mãos. É necessário saber a melhor maneira de escrever testes unitários no Apache Camel e esta maneira é enviando uma mensagem através de um canal e verificando se a mesma foi corretamente roteada pela aplicação. A Figura 43 demonstra esse conceito de teste, onde uma mensagem com o conteúdo XXX é enviada para uma aplicação (Application) que, por sua vez, a converte para outro formato, no caso YYY, e a retorna.
Figura 43. Exemplo de um cenário de teste.
Diante de toda essa estrutura, vale ressaltar que assim como em código de produção, os códigos escritos para testes unitários também devem seguir boas práticas. No contexto do Apache Camel, algumas delas são:
- Escrever testes unitários com o kit de testes desde o começo do projeto;
- Utilizar o Mock component, pois é completo, simples e oferece tudo que é necessário para a escrita dos testes, sendo a sua maior contribuição voltada para a simulação e substituição de componentes (endpoints);
- Não esquecer ou desconsiderar os testes baseados em cenários alternativos, pois a integração entre sistemas é difícil e muitos problemas podem ocorrer. Portanto, verificar se a aplicação lida corretamente com falhas, por exemplo, é uma boa prática;
- Escrever testes de integração para validar se o comportamento da aplicação será conforme o esperado quando realmente estiver integrada a sistemas reais.
Apresentadas as características, os conceitos, vantagens, recursos e arquitetura do Apache Camel, é importante ter ciência dos desafios que o mesmo deverá enfrentar nos próximos anos para que seu sucesso continue aumentando e também para que o desenvolver fique ligado no que está por vir nas próximas versões do framework.
Desafios para o futuro
O Apache Camel está em constante evolução, seja oferecendo novas funcionalidades, componentes, recursos, suporte a formatos de dados, seja na correção de problemas e bugs. Isso tudo é reflexo de uma comunidade atuante e dedicada, que em quase 10 anos conseguiu elevar o patamar do projeto e consolidá-lo no mercado.
Apesar de todo esse avanço, o Apache Camel possui muitos desafios pela frente. Um dos principais é continuar garantindo que a agilidade e performance sejam um diferencial para qualquer sistema que preza pelo sucesso dos negócios. Portanto, o Apache Camel deve apresentar no futuro novos recursos e um suporte refinado para os seguintes tópicos:
- Escalabilidade horizontal;
- Edição de regras por analistas de negócio;
- Inteligência estatística;
- Big Data.
Esta primeira parte do artigo, através da apresentação em detalhes dos conceitos, fundamentos, estrutura e funcionamento do framework, demonstrou que o Apache Camel pode ser utilizado como solução para praticamente todos os cenários em que há integração entre sistemas, mas é indicado principalmente para aqueles em que tecnologias diferentes, plataformas e formatos de dados distintos são obstáculos e cuja complexidade não é pequena e nem grande.
Para cenários em que há poucas integrações e com tecnologias e plataformas iguais, o uso de bibliotecas específicas e destinadas a atender determinado cenário de integração é mais vantajoso, pois elimina-se o tempo de aprendizado e adaptação para utilização do Camel, tornando o desenvolvimento mais rápido e fácil, além de simplificar a arquitetura do sistema, evitando a inclusão de um framework nos quais os objetivos e funcionalidades vão muito além do que uma simples integração.
Já para projetos grandes, com muitos fluxos, integrações e sistemas com tecnologias heterogêneas, o uso do Apache Camel pode ser substituído por um ESB, pois o mesmo oferece de forma nativa muitas funcionalidades adicionais importantes para um cenário mais complexo que, com o uso do Camel, teriam que ser adaptadas e até mesmo implementadas, gerando assim um esforço muito grande e um alto custo para manutenção.
Diante de tudo o que foi apresentado, pode-se afirmar que o futuro do Apache Camel é bastante promissor, pois dentro das empresas o número de sistemas que necessitam de integração está aumentando e as tecnologias, cada vez mais diversificadas. Nesse cenário, o Apache Camel se encaixará muito bem, pois sua finalidade principal é justamente servir como um integrador entre aplicações e serviços para atender necessidades de negócios.
LinksSite dos Padrões
Empresarias de Integração
http://www.enterpriseintegrationpatterns.com
Site do Apache
Camel
http://camel.apache.org
Página com a
relação completa dos Padrões Empresarias de Integração
http://www.enterpriseintegrationpatterns.com/patterns/messaging/toc.html
Página com os
motivos da escolha do nome Camel
http://camel.apache.org/why-the-name-camel.html
Página com os Padrões
Empresarias de Integração suportados pelo Apache Camel
http://camel.apache.org/enterprise-integration-patterns.html
Página da
comunidade do Apache Camel
http://camel.apache.org/community.html
Página com a lista
completa de componentes do Apache Camel
http://camel.apache.org/components.html
Referências
Livro sobre
Padrões Empresariais de Integração
HOHPE, Gregor; WOOLF,
Bobby. Enterprise Integration Patterns: Designing, Building, and Deploying
Messaging Solutions. Addison-Wesley Professional, 2003.
Livro Camel in
Action
IBSEN Claus; ANSTEY Jonathan.
Camel in Action. Manning Publications, 2011.
Livro Apache Camel
Developer’s Cookbook
CRANTON Scott; KORAB Jakub.
Apache Camel Developer's Cookbook. Packt Publishing, 2013.
Livro Mastering
Apache Camel
ONOFRE Jean-Baptiste. Mastering Apache Camel. Packt
Publishing, 2015.
Parte II
Apache Camel: Um guia completo – Parte 2
Na parte 1 desta série de artigos foram levantados os principais problemas e desafios que são enfrentados nas integrações de sistemas. Esses problemas estão diretamente relacionados com sistemas legados, arquiteturas mal definidas e decisões de projeto erradas. Além do mais, alguns desafios, como instabilidade e lentidão de redes, sistemas heterogêneos e mudanças de requisitos, também estão incluídos nesse contexto e devem ser considerados.
Porém, esses problemas e desafios foram superados pelos desenvolvedores ao longo do tempo, através de estilos de integração conhecidos como: transferência de arquivo (Electronic Data Interchange ou EDI), banco de dados compartilhado, invocação remota de procedimento (Remote Procedure Call ou RPC) e mensageria, sendo a mensageira o que possui mais vantagens e benefícios.
Baseando-se na experiência dos desenvolvedores adquirida com a adoção dos estilos, foram reconhecidos e definidos padrões empresarias de integração com o uso de mensageria para indicar soluções elegantes para problemas comumente identificados nesse contexto. Esses padrões foram apresentados em detalhes no artigo anterior.
Exibidos os detalhes e funcionamento dos padrões, foram indicadas as principais tecnologias para integração que utilizam esses padrões como base, e o destaque ficou para o Apache Camel. Essa tecnologia busca simplificar a integração entre sistemas permitindo que com poucas linhas de código e com pouca configuração seja possível processar arquivos, invocar web services, publicar mensagens em um canal de mensageria, entre diversos outros recursos que precisariam de muito código, bibliotecas externas e controle interno.
Apresentados todos esses conceitos, padrões e principalmente o Apache Camel como um framework em destaque atualmente para integração de sistemas, é importante a elaboração de um exemplo prático para consolidação do conhecimento adquirido. Sendo assim, nesta série de artigos será abordada a implementação de uma solução completa para pagamento bancário.
Solução para pagamento bancário
Visando consolidar o que foi explicado na primeira parte desta série, será desenvolvida uma solução para integrar um aplicativo mobile aos sistemas necessários para que uma transação de pagamento bancário com cartão de crédito disparada por esse aplicativo seja realizada com sucesso. Para construir essa solução, os requisitos funcionais, descritos na Tabela 1, e os requisitos não funcionais, descritos na Tabela 2, foram criados com a finalidade de enriquecer o exemplo a ser implementado.
Código - requisito funcional |
Descrição |
RF-01-APPMOB |
A solução de pagamento bancário deve expor um web service para receber do aplicativo mobile requisições com os seguintes dados: id da transação, CPF e nome do titular do cartão, número e código de segurança do cartão e valor da compra. |
RF-02-SISBAN |
A solução de pagamento bancário, após receber os dados da requisição de pagamento do aplicativo mobile, deve enviar o número do cartão e o CPF do titular para o sistema bancário para validação. |
RF-03-SISBAN |
A solução de pagamento deve aceitar do sistema bancário a resposta contendo os mesmos atributos de envio, além de uma flag indicando se os dados são válidos. |
RF-04-SISSEG |
A solução de pagamento, após receber a resposta do sistema bancário, deve avaliar a flag indicativa dos dados válidos. Se positiva, enviar para o sistema de segurança o número e o código de segurança do cartão para validação. |
RF-05-SISSEG |
A solução de pagamento deve aceitar do sistema de segurança a resposta contendo os mesmos atributos de envio, além de uma flag indicando se os dados são válidos. |
RF-06-SISAUT |
A solução de pagamento, após receber a resposta do sistema de segurança, deve avaliar a flag indicativa dos dados válidos. Se positiva, enviar para o sistema de autorização o número do cartão e o valor da compra para aprovação do pagamento. |
RF-07-SISAUT |
A solução de pagamento deve aceitar a resposta do sistema de autorização com os atributos: número do cartão, valor da compra, status e código de autorização do pagamento, mensagem complementar do status e data de autorização. |
RF-08-SISMON |
Após a conclusão de uma transação de pagamento, ou seja, após a resposta do sistema de autorização, a solução de pagamento bancário deve enviar uma mensagem com os dados da transação, baseada no layout do requisito RF-11-SISMON, para o sistema de monitoração. |
RF-09-APPMOB |
A solução de pagamento deve enviar a resposta da autorização para o aplicativo mobile com os atributos: id da transação, status e código de autorização do pagamento e data de autorização. |
RF-10-SISMON |
A solução de pagamento deve enviar uma mensagem para o sistema de monitoração baseada no layout do requisito RF-12-SISMON caso ocorra qualquer erro inesperado durante um fluxo de pagamento bancário. |
RF-11-SISMON |
O layout da mensagem de conclusão da transação de pagamento deve seguir o padrão: id da transação + data inicial da transação + data final da transação + CPF do titular do cartão + número do cartão + status do pagamento. Os campos referentes a datas devem ser enviados no formato yyyyMMddHHmmss. |
RF-12-SISMON |
O layout da mensagem de erro inesperado da transação de pagamento deve seguir o padrão: data inicial da transação + mensagem de erro. O campo referente à data deve ser enviado no formato yyyyMMddHHmmss. |
Tabela 1. Requisitos funcionais.
Código - requisito não funcional |
Descrição |
RNF-01-APPMOB |
A solução de pagamento bancário deve expor web services RESTful. |
RNF-02-SISBAN |
A comunicação com o sistema bancário deve ser realizada via mensageria com o formato JSON. |
RNF-03-SISSEG |
A comunicação com o sistema de segurança deve ser realizada via mensageria com o formato JSON. |
RNF-04-SISAUT |
A comunicação com o sistema de autorização deve ser realizada via mensageria com o formato JSON. |
RNF-05-SISMON |
A comunicação com o sistema de monitoração deve ser realizada via mensageria com o formato texto baseado em layout pré-definido. |
RNF-06-APPMOB |
A solução de pagamento bancário deve responder ao aplicativo mobile em até 20 segundos. |
Tabela 2. Requisitos não funcionais.
Baseado nos requisitos apresentados, é possível definir o fluxo de negócio que a solução de pagamento bancário deverá atender, incluindo os sistemas externos com necessidade de integração. E foi exatamente isso que foi feito na Figura 1. Ela exibe o diagrama de sequência com todas as etapas necessárias para o funcionamento do nosso exemplo.
Figura 1. Diagrama de sequência para o fluxo de pagamento bancário
Com o auxílio dessa imagem e das Tabelas 1 e 2, observa-se que o fluxo iniciará com o aplicativo mobile enviando uma requisição para a solução de pagamento bancário com o id da transação, CPF e nome do titular do cartão, número e código de segurança do cartão e valor da compra.
Com a posse desses dados, a solução de pagamento bancário enviará o número do cartão e o CPF do proprietário para o sistema bancário para verificar se ambos são válidos. Se os dados estiverem corretos, o número do cartão e o código de segurança serão enviados ao sistema de segurança para verificação.
Na sequência, se a resposta do sistema de segurança for positiva, a solução de pagamento bancário enviará para o sistema de autorização o número do cartão e o valor da compra para que, de fato, o pagamento seja efetuado. Por fim, a solução de pagamento bancário enviará ao sistema de monitoração os dados da transação e também a resposta final ao aplicativo mobile.
Com todas as informações mencionadas, ou seja, requisitos e fluxo de negócio conhecidos, já é possível definir a arquitetura da solução, de forma que a mesma possa atender e corresponder ao cenário esperado.
Arquitetura da solução
Além de procurar atender a todos os requisitos indicados, a escolha da arquitetura precisa considerar as necessidades de baixo acoplamento, processamento assíncrono, performance, flexibilidade e tolerância a falhas. Para isso, foi definida uma arquitetura baseada em componentes com comunicação interna utilizando mensageria com o formato JSON. Dessa forma, além de cobrir os requisitos e necessidades de negócio, haverá grande poder de escalabilidade e também baixo custo de manutenção. Por fim, para compor o restante da solução, os frameworks e ferramentas listados a seguir foram escolhidos (para realizar o download de cada um, verifique a seção Links):
- Apache Camel (versão 2.16.1): framework para integração entre os componentes e sistemas;
- Apache ActiveMQ (versão 5.6.0): servidor de mensageria para suportar a solução;
- Apache Maven (versão 3.3): ferramenta para gerenciamento de dependências e construção automática de build;
- Spring Framework (versão 4.2.4.RELEASE): framework para injeção de dependências;
- WildFly (versão 9.0.2-final): servidor de aplicação Java EE para execução da solução.
Definida a arquitetura, a partir de agora iniciaremos a implementação da solução de forma gradual, de acordo com os requisitos e fluxo de negócio.
Iniciando a construção da solução
Conforme relatado na seção anterior, onde foi definida uma arquitetura baseada em componentes, será preciso criar uma estrutura de projeto para atender às necessidades arquiteturais mencionadas. Como foi escolhido o Maven para auxiliar no gerenciamento de dependências e build, os componentes serão organizados em projetos na estrutura multimódulos.
Para ajudar no entendimento de como é essa organização multimódulos, a Figura 2 exibe a estrutura a ser utilizada no caso da solução de pagamento bancário, mostrando que o diretório SolucaoPagamentoBancario é onde estão localizados o projeto agregador e os diretórios dos módulos MobileIntegracao e MobileIntegracaoComum, que são exemplos de componentes da própria solução. Note que esses módulos devem ficar dentro do diretório do projeto agregador e também conter seu próprio pom.xml.
Figura 2. Exemplo da estrutura inicial de multimódulos do Maven para a solução a ser construída
Explicada a estrutura da solução, podemos iniciar a criação do projeto agregador e de seu respectivo pom.xml. Para isso, crie um diretório com o nome SolucaoPagamentoBancario e, dentro do mesmo, o arquivo pom.xml conforme as Listagens 1, 2 e 3.
Listagem 1. Arquivo pom.xml do projeto agregador — definições relativas às características da solução, versões de dependências e módulos.
01 <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
02 xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
03
04 <modelVersion>4.0.0</modelVersion>
05 <name>SolucaoPagamentoBancario</name>
06 <groupId>br.com.devmedia</groupId>
07 <artifactId>solucao-pagamento-bancario</artifactId>
08 <version>1.0</version>
09 <packaging>pom</packaging>
10 <description>Solução de Pagamento Bancário utilizando Apache Camel</description>
11
12 <properties>
13 <camel.version>2.16.1</camel.version>
14 <spring.version>4.2.4.RELEASE</spring.version>
15 <activemq.version>5.6.0</activemq.version>
16 <maven.compiler.plugin.version>3.3</maven.compiler.plugin.version>
17 <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
18 <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
19 </properties>
20
21 <modules>
22 <module>MobileIntegracao</module>
23 <module>MobileIntegracaoComum</module>
24 </modules>
Listagem 2. Arquivo pom.xml do projeto agregador — declaração das dependências de bibliotecas externas.
01 <dependencyManagement>
02 <dependencies>
03 <dependency>
04 <groupId>org.apache.camel</groupId>
05 <artifactId>camel-core</artifactId>
06 <version>${camel.version}</version>
07 </dependency>
08 <dependency>
09 <groupId>org.apache.camel</groupId>
10 <artifactId>camel-spring</artifactId>
11 <version>${camel.version}</version>
12 </dependency>
13 <dependency>
14 <groupId>org.apache.camel</groupId>
15 <artifactId>camel-jackson</artifactId>
16 <version>${camel.version}</version>
17 </dependency>
18 <dependency>
19 <groupId>org.apache.camel</groupId>
20 <artifactId>camel-jms</artifactId>
21 <version>${camel.version}</version>
22 </dependency>
23 <dependency>
24 <groupId>org.apache.activemq</groupId>
25 <artifactId>activemq-camel</artifactId>
26 <version>5.13.0</version>
27 </dependency>
28 <dependency>
29 <groupId>org.apache.activemq</groupId>
30 <artifactId>activemq-core</artifactId>
31 <version>${activemq.version}</version>
32 </dependency>
33 <dependency>
34 <groupId>org.apache.activemq</groupId>
35 <artifactId>activemq-pool</artifactId>
36 <version>5.13.0</version>
37 </dependency>
38 </dependencies>
39 </dependencyManagement>
Listagem 3. Arquivo pom.xml do projeto agregador — informações para build e geração de pacotes.
01 <build>
02 <plugins>
03 <plugin>
04 <groupId>org.apache.maven.plugins</groupId>
05 <artifactId>maven-compiler-plugin</artifactId>
06 <version>${maven.compiler.plugin.version}</version>
07 <configuration>
08 <source>1.6</source>
09 <target>1.6</target>
10 </configuration>
11 </plugin>
12 </plugins>
13 <finalName>${project.artifactId}</finalName>
14 </build>
15 </project>
Na Listagem 1, observa-se nas linhas de 4 a 10 que são definidas as características básicas para um projeto gerenciado pelo Maven, tais como: name, groupId, artifactId, packaging, version, modelVersion e description. Como diferencial, vale ressaltar que a tag packaging possui o valor pom para indicar que esse projeto agregador servirá como base para os demais módulos, isto é, as características básicas e dependências declaradas serão herdadas diretamente.
Já nas linhas 13 a 15 são definidas propriedades cujos valores são as versões das bibliotecas externas que serão utilizadas e que estão declaradas dentro da parte de gerenciamento de dependências. Essa prática é recomendada porque, caso haja necessidade de alteração nas versões utilizadas, não é preciso replicar essa mudança em todos os lugares onde as mesmas estão indicadas. Além do mais, evita que erros ocorram ao declarar dependências com versões erradas ou trocadas.
Ainda nas definições de propriedades da Listagem 1, verifica-se na linha 16 uma propriedade a ser utilizada para indicar qual versão do plugin maven-compiler-plugin o Maven deve utilizar para auxiliar na compilação de toda a solução. Continuando, nas linhas 17 e 18 são definidas propriedades específicas para o Maven com o objetivo de indicar o tipo correto de encode a ser utilizado na manipulação de arquivos para evitar erros e alertas com acentuação de palavras — no caso da solução de pagamento bancário será utilizado o UTF-8. Por fim, a Listagem 1 possui nas linhas 21 a 24 a tag modules, que é usada para definir os módulos que serão agregados à solução de pagamento bancário; nesse caso, estão incluídos inicialmente o MobileIntegracao e o MobileIntegracaoComum a fim de atender à primeira parte do fluxo de negócio. Os detalhes da implementação desses componentes serão descritos na próxima seção.
Prosseguindo com a criação do arquivo POM do projeto agregador, será necessário incluir o código da Listagem 2 para declarar as dependências de bibliotecas externas que serão utilizadas pela solução. Basicamente, existem as dependências do Camel e do ActiveMQ, com a definição da versão através das propriedades criadas na Listagem 1.
Já na Listagem 3 são declaradas as informações de build e geração de pacotes com a utilização do plugin maven-compiler-plugin. A função desse plugin é bem simples e consiste basicamente em auxiliar na compilação dos projetos, como se pode notar nas linhas 8 e 9, onde é especificada a escolha pela versão 1.6 do Java.
Por fim, a linha 13 revela que o artefato gerado (WAR ou JAR) deve ter o mesmo nome que foi declarado na tag artifactId do pom.xml, para simplificar o nome do artefato. Sem essa configuração os mesmos seriam gerados com o número da versão declarado na tag version.
Implementando os requisitos RF-01-APPMOB e RNF-01-APPMOB
Com a estrutura básica da solução pronta, será apresentada a implementação dos primeiros requisitos, identificados pelos códigos RF-01-APPMOB e RNF-01-APPMOB. Logo, será criado o módulo MobileIntegracaoComum, cuja finalidade é representar os dados que serão enviados pelo aplicativo mobile na requisição de pagamento através de web services, além de servir como um intermediário na comunicação com os demais componentes internos da solução, isto é, ele será o responsável por trafegar os dados entre os componentes. Sua implementação é muito simples e consiste basicamente de um POJO com os atributos que serão enviados.
Criando o módulo MobileIntegracaoComum
Desse modo, crie um novo módulo/projeto do tipo Java Standalone (com o nome MobileIntegracaoComum) dentro do diretório do projeto agregador para que a estrutura fique conforme a Figura 2. Após isso, crie dentro desse novo projeto um arquivo pom.xml com o código da Listagem 4.
Listagem 4. Arquivo pom.xml do módulo MobileIntegracaoComum.
01 <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
02 xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
03
04 <modelVersion>4.0.0</modelVersion>
05
06 <parent>
07 <groupId>br.com.devmedia</groupId>
08 <artifactId>solucao-pagamento-bancario</artifactId>
09 <version>1.0</version>
10 </parent>
11
12 <name>MobileIntegracaoComum</name>
13 <artifactId>mobile-integracao-comum</artifactId>
14 <packaging>jar</packaging>
15
16 </project>
Observa-se nessa listagem que é utilizada a tag parent entre linhas 6 e 10 para determinar que esse módulo possui ligação com o projeto agregador e que, portanto, as informações referentes a groupId e version serão herdadas e obtidas automaticamente. Além disso, a tag packaging é definida como JAR, pois, assim, um JAR dessa biblioteca será gerado para que seja incorporado e utilizado pelos outros componentes.
Seguindo com a implementação da biblioteca, devemos criar um pacote com o nome br.com.devmedia.mobile.integracao.comum para conter a classe PagamentoRequisicao, que representará o POJO. O código dessa classe é apresentado na Listagem 5.
Listagem 5. Código da classe PagamentoRequisicao.
01 package br.com.devmedia.mobile.integracao.comum;
02
03 import java.io.Serializable;
04 import java.math.BigDecimal;
05
06 public class PagamentoRequisicao implements Serializable {
07
08 private static final long serialVersionUID = 2672206963968858712L;
09
10 private String idTransacao;
11 private String cpfTitularCartao;
12 private String nomeTitularCartao;
13 private String numeroCartao;
14 private String codigoSegurancaCartao;
15 private BigDecimal valorCompra;
16
17 public PagamentoRequisicao() {
18 }
19
20 // Getters e setters dos atributos omitidos.
21
22 @Override
23 public String toString() {
24 StringBuilder builder = new StringBuilder();
25 builder.append("PagamentoRequisicao [");
26 if (idTransacao != null) {
27 builder.append("idTransacao=");
28 builder.append(idTransacao);
29 builder.append(", ");
30 }
31 if (cpfTitularCartao != null) {
32 builder.append("cpfTitularCartao=");
33 builder.append(cpfTitularCartao);
34 builder.append(", ");
35 }
36 if (nomeTitularCartao != null) {
37 builder.append("nomeTitularCartao=");
38 builder.append(nomeTitularCartao);
39 builder.append(", ");
40 }
41 if (numeroCartao != null) {
42 builder.append("numeroCartao=");
43 builder.append(numeroCartao);
44 builder.append(", ");
45 }
46 if (codigoSegurancaCartao != null) {
47 builder.append("codigoSegurancaCartao=");
48 builder.append(codigoSegurancaCartao);
49 builder.append(", ");
50 }
51 if (valorCompra != null) {
52 builder.append("valorCompra=");
53 builder.append(valorCompra);
54 builder.append(", ");
55 }
56 builder.append("]");
57 return builder.toString();
58 }
59 }
Analisando esse código, verifica-se nas linhas 10 a 15 a declaração dos atributos idTransacao, cpfTitularCartao, nomeTitularCartao, numeroCartao, codigoSegurancaCartao e valorCompra, que representarão os dados enviados pela requisição de pagamento. Já nas linhas 22 a 58 é implementado o método toString(), para que os dados do objeto sejam registrados de forma estruturada em logs. Por fim, vale ressaltar que os métodos getters e setters dos atributos foram omitidos por questão de espaço, mas devem ser implementados.
Criando o módulo MobileIntegracao
Finalizada a implementação da primeira versão do componente MobileIntegracaoComum, é a vez do MobileIntegracao, responsável por realizar a integração com o aplicativo mobile e manter todas as regras e necessidades voltadas a essa integração.
Para isso, crie um novo módulo/projeto do tipo Java Web dentro do diretório do projeto agregador com o nome MobileIntegracao, assim como realizado anteriormente para o MobileIntegracaoComum. Caso seja necessário algum auxílio com a estrutura dos projetos, verifique novamente a Figura 2. Feito isso, no novo projeto, crie um arquivo pom.xml com o código das Listagens 6, 7 e 8.
Listagem 6. pom.xml do módulo MobileIntegracao — definições do projeto agregador, nomes, propriedades e repositórios.
01 <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
02 xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
03
04 <modelVersion>4.0.0</modelVersion>
05
06 <parent>
07 <groupId>br.com.devmedia</groupId>
08 <artifactId>solucao-pagamento-bancario</artifactId>
09 <version>1.0</version>
10 </parent>
11
12 <name>MobileIntegracao</name>
13 <artifactId>mobile-integracao</artifactId>
14 <packaging>war</packaging>
15
16 <properties>
17 <restlet.version>2.3.4</restlet.version>
18 <jackson.version>2.7.0</jackson.version>
19 </properties>
20
21 <repositories>
22 <repository>
23 <id>maven-restlet</id>
24 <name>Public online Restlet repository</name>
25 <url>http://maven.restlet.org</url>
26 </repository>
27 </repositories>
Listagem 7. pom.xml do módulo MobileIntegracao — declaração de dependências relacionadas ao Camel e outras bibliotecas.
01 <dependencies>
02 <dependency>
03 <groupId>${project.parent.groupId}</groupId>
04 <artifactId>mobile-integracao-comum</artifactId>
05 <version>${project.parent.version}</version>
06 </dependency>
07 <dependency>
08 <groupId>org.apache.camel</groupId>
09 <artifactId>camel-core</artifactId>
10 </dependency>
11 <dependency>
12 <groupId>org.apache.camel</groupId>
13 <artifactId>camel-spring</artifactId>
14 </dependency>
15 <dependency>
16 <groupId>org.apache.camel</groupId>
17 <artifactId>camel-jms</artifactId>
18 </dependency>
19 <dependency>
20 <groupId>org.apache.camel</groupId>
21 <artifactId>camel-restlet</artifactId>
22 <version>${camel.version}</version>
23 </dependency>
24 <dependency>
25 <groupId>org.apache.camel</groupId>
26 <artifactId>camel-jackson</artifactId>
27 </dependency>
Listagem 8. pom.xml do módulo MobileIntegracao — declaração de dependências relacionadas ao Spring e outras bibliotecas.
01 <dependency>
02 <groupId>org.springframework</groupId>
03 <artifactId>spring-expression</artifactId>
04 <version>${spring.version}</version>
05 </dependency>
06 <dependency>
07 <groupId>org.springframework</groupId>
08 <artifactId>spring-beans</artifactId>
09 <version>${spring.version}</version>
10 </dependency>
11 <dependency>
12 <groupId>org.springframework</groupId>
13 <artifactId>spring-context</artifactId>
14 <version>${spring.version}</version>
15 </dependency>
16 <dependency>
17 <groupId>org.springframework</groupId>
18 <artifactId>spring-web</artifactId>
19 <version>${spring.version}</version>
20 </dependency>
21 <dependency>
22 <groupId>org.restlet.jee</groupId>
23 <artifactId>org.restlet.ext.spring</artifactId>
24 <version>${restlet.version}</version>
25 <exclusions>
26 <exclusion>
27 <artifactId>spring-asm</artifactId>
28 <groupId>org.springframework</groupId>
29 </exclusion>
30 </exclusions>
31 </dependency>
32 <dependency>
33 <groupId>org.restlet.jee</groupId>
34 <artifactId>org.restlet.ext.servlet</artifactId>
35 <version>${restlet.version}</version>
36 </dependency>
37 <dependency>
38 <groupId>org.apache.activemq</groupId>
39 <artifactId>activemq-camel</artifactId>
40 </dependency>
41 <dependency>
42 <groupId>org.apache.activemq</groupId>
43 <artifactId>activemq-core</artifactId>
44 </dependency>
45 <dependency>
46 <groupId>org.apache.activemq</groupId>
47 <artifactId>activemq-pool</artifactId>
48 </dependency>
49 </dependencies>
50 </project>
Na Listagem 6, observa-se a declaração, nas linhas 6 a 10, das informações do projeto agregador, nas linhas 12 a 14, os dados específicos do módulo, de 16 a 19, as propriedades para indicar a versão das bibliotecas externas que serão utilizadas e, por fim, de 21 a 27, a inclusão da tag repositories para indicar o repositório externo onde a biblioteca Restlet será encontrada. A mesma será utilizada para implementação do web service RESTful.
Continuando com a criação do pom.xml, a Listagem 7 apresenta parte das bibliotecas externas que serão necessárias, e o restante está detalhado na Listagem 8. Na Listagem 7 são declaradas as dependências para inclusão do MobileIntegracaoComum e também para utilização do Camel. Um detalhe nessa listagem é a inclusão do camel-restlet, que se trata de um componente do próprio Camel para integração e funcionamento com a biblioteca Restlet, conforme se observa nas linhas 19 a 23. Por fim, na Listagem 8, encontram-se as dependências para bibliotecas do Spring, Restlet e ActiveMQ.
Uma observação válida sobre essas listagens é que para as bibliotecas que foram declaradas no pom.xml do projeto agregador não é necessário incluir a versão, pois a mesma será recuperada automaticamente pelo Maven.
Na sequência é essencial alterar o arquivo web.xml, localizado no diretório WEB-INF, para que a aplicação carregue (ao iniciar) as configurações do Spring, Camel, ActiveMQ e Restlet (web service). Portanto, o arquivo deve ficar conforme a Listagem 9.
Listagem 9. web.xml do módulo MobileIntegracao.
01 <?xml version="1.0" encoding="UTF-8"?>
02 <web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
03 xmlns="http://java.sun.com/xml/ns/javaee" xmlns:web="http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"
04 xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"
05 id="WebApp_ID" version="2.5" metadata-complete="true">
06
07 <display-name>Mobile Integração</display-name>
08
09 <listener>
10 <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
11 </listener>
12
13 <listener>
14 <listener-class>org.springframework.web.context.request.RequestContextListener</listener-class>
15 </listener>
16
17 <context-param>
18 <param-name>contextConfigLocation</param-name>
19 <param-value>classpath:application-context.xml</param-value>
20 </context-param>
21
22 <servlet>
23 <servlet-name>RestletServlet</servlet-name>
24 <servlet-class>org.restlet.ext.spring.SpringServerServlet</servlet-class>
25 <init-param>
26 <param-name>org.restlet.component</param-name>
27 <param-value>RestletComponent</param-value>
28 </init-param>
29 </servlet>
30 <servlet-mapping>
31 <servlet-name>RestletServlet</servlet-name>
32 <url-pattern>/webservice/*</url-pattern>
33 </servlet-mapping>
34
35 </web-app>
Analisando esse arquivo, verifica-se na linha 7 a declaração da tag display-name para informar o nome da aplicação web, nesse caso, “Mobile Integração”. Já as linhas 9 a 11 indicam a utilização do listener ContextLoaderListener do Spring para criar um contexto geral para o componente e incluí-lo no ServletContext. Com isso, será possível que o Spring gerencie os beans que serão utilizados no componente.
As linhas 17 a 20 indicam que o arquivo application-context.xml deve ser carregado e interpretado ao iniciar a aplicação para que as configurações presentes no mesmo sejam incluídas no contexto geral da aplicação. É nesse arquivo que serão declarados os beans a serem utilizados.
Concluindo com o arquivo web.xml, nas linhas 22 a 33 é definido o servlet RestletServlet da biblioteca Restlet para compor o web service. Seu funcionamento ocorrerá baseado em um servlet Java EE, onde, dada uma requisição para uma determinada URL, o mesmo será invocado. Em especial, observa-se nas linhas 30 a 33 que a URL mapeada para o servlet é /webservice/*, isto é, qualquer requisição que iniciar com o path /webservice será processada por ele.
Na sequência, devemos incluir, ainda no diretório WEB-INF, o arquivo jboss-web.xml conforme a Listagem 10.
Listagem 10. jboss.xml do módulo MobileIntegracao.
01 <jboss-web xmlns="http://www.jboss.com/xml/ns/javaee"
02 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
03 xsi:schemaLocation="
04 http://www.jboss.com/xml/ns/javaee
05 http://www.jboss.org/j2ee/schema/jboss-web_5_1.xsd">
06
07 <context-root>/</context-root>
08
09 </jboss-web>
A criação desse arquivo com a configuração indicada é apenas para declarar ao servidor de aplicação — no caso, o WildFly — que a aplicação web será acessada pelo contexto do próprio endereço do servidor de aplicação. Por exemplo, ao invés da aplicação ser acessada através do endereço http://localhost:8080/mobile-integracao será apenas http://localhost:8080/. Essa configuração foi incluída para simplificar o acesso ao web service, para não expor detalhes da solução e diminuir a dependência que o sistema externo teria ao vincular o nome da aplicação com o web service.
Ainda ajustando as configurações de MobileIntegracao, é preciso criar o arquivo application-context.xml no diretório src/main/resources de acordo com a Listagem 11.
Listagem 11. application-context.xml do módulo MobileIntegracao.
01 <beans xmlns="http://www.springframework.org/schema/beans"
02 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:camel="http://camel.apache.org/schema/spring"
03 xmlns:context="http://www.springframework.org/schema/context"
04 xsi:schemaLocation="
05 http://www.springframework.org/schema/context
06 http://www.springframework.org/schema/context/spring-context-3.0.xsd
07 http://www.springframework.org/schema/beans
08 http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
09 http://camel.apache.org/schema/spring
10 http://camel.apache.org/schema/spring/camel-spring.xsd">
11
12 <camel:camelContext id="mobileIntegracao">
13 <camel:routeBuilder ref="mobileIntegracaoRouteBuilder" />
14 </camel:camelContext>
15
16 <bean id="RestletComponent" class="org.restlet.Component" />
17
18 <bean id="RestletComponentService" class="org.apache.camel.component.restlet.RestletComponent">
19 <constructor-arg ref="RestletComponent" />
20 </bean>
21
22 <bean id="jmsConnectionFactory" class="org.apache.activemq.ActiveMQConnectionFactory">
23 <property name="brokerURL"
24 value="failover:(tcp://localhost:61616,tcp://127.0.0.1:61616)?randomize=false" />
25 </bean>
26
27 <bean id="pooledConnectionFactory" class="org.apache.activemq.pool.PooledConnectionFactory"
28 init-method="start" destroy-method="stop">
29 <property name="maxConnections" value="8" />
30 <property name="connectionFactory" ref="jmsConnectionFactory" />
31
32 </bean>
33
34 <bean id="jmsConfig" class="org.apache.camel.component.jms.JmsConfiguration">
35 <property name="connectionFactory" ref="pooledConnectionFactory" />
36 <property name="concurrentConsumers" value="10" />
37 </bean>
38
39 <bean id="activemq" class="org.apache.activemq.camel.component.ActiveMQComponent">
40 <property name="configuration" ref="jmsConfig" />
41 </bean>
42
43 <context:component-scan base-package="br.com.devmedia.mobile.integracao" />
44
45 </beans>
As configurações implementadas nesse arquivo são muito importantes, pois são através das declarações dos beans (gerenciados pelo Spring) que a conexão com o ActiveMQ é efetuada, o carregamento inicial das rotas do Camel é feito, além da utilização da biblioteca Restlet. Analisando mais a fundo, as linhas 12 a 14 informam para o Camel qual a classe que contém as rotas a serem iniciadas quando a aplicação carregar. Nesse caso, trata-se da MobileIntegracaoRouteBuilder, a ser implementada neste artigo, referenciada pela tag camel:routeBuilder na linha 13.
Já a linha 16 declara um bean chamado RestletComponent, que é uma implementação do próprio Restlet a ser utilizado para o web service. Na sequência, as linhas 18 a 20 indicam a declaração de outro bean, denominado RestletComponentService, relacionado ao componente camel-restlet do Camel, que, por sua vez, necessita do bean anterior para ser construído.
Prosseguindo com a análise da listagem, as linhas 22 a 25 criam um bean chamado jmsConnectionFactory para fazer a conexão com o ActiveMQ. Vale ressaltar que na linha 24 é indicada a URL para conexão com o servidor de mensageira, incluindo a opção failover para disponibilizar redundância nas conexões, ou seja, se a conexão ou comunicação falhar com o servidor da primeira URL, automaticamente é estabelecida uma conexão com o segundo servidor através da outra URL. O parâmetro randomize indica nesse caso que a conexão com os servidores deve acontecer conforme a ordem da configuração e não de forma aleatória.
Na sequência do arquivo, é declarado nas linhas 27 a 32 mais um bean, com o nome de pooledConnectionFactory, para ser um pool de conexões com o ActiveMQ baseado nas configurações do bean anterior. Esse bean permitirá no máximo oito conexões em uso ao mesmo tempo, conforme a propriedade maxConnections da linha 29.
Já nas linhas 34 a 37, outro bean é criado, com o nome jmsConfig, para customizar e indicar um número de consumidores que estarão funcionando em paralelo para cada conexão do ActiveMQ definida anteriormente. Essa alteração visa uma melhor performance para o processamento das mensagens, pois o padrão é 1 consumidor apenas. Essa configuração é realizada na linha 36 através da propriedade concurrentConsumers.
Finalizando a criação do arquivo, as linhas 39 a 41 definem um bean chamado activemq, cuja implementação é o componente do Camel camel-activemq. Esse bean inclui em suas configurações o bean mencionado anteriormente, jmsConfig, conforme observa-se na linha 40, e com esse último bean é que será possível enviar mensagens para as filas nas rotas do Camel. Por último, na linha 43 é definida uma configuração do Spring para que o mesmo faça uma busca e injeção de dependência de forma automática a partir do pacote br.com.devmedia.mobile.integracao. Com isso, as classes anotadas com @Component que estejam nesse pacote serão automaticamente gerenciadas pelo Spring. Essa configuração é valiosa, pois evita que seja necessário indicar classe por classe no arquivo XML.
Concluída a implementação do application-context.xml, é hora de criarmos a classe que será a detentora das rotas do Camel, ou seja, a classe que vai manipular o fluxo. Essa é a principal classe do componente e as rotas definidas nela são inicializadas no momento em que a aplicação é carregada. Para isso, crie o pacote br.com.devmedia.mobile.integracao.rota e, depois, a classe MobileIntegracaoRouteBuilder, conforme a Listagem 12.
Listagem 12. Código da classe MobileIntegracaoRouteBuilder, do módulo MobileIntegracao.
01 package br.com.devmedia.mobile.integracao.rota;
02
03 import org.apache.camel.LoggingLevel;
04 import org.apache.camel.builder.RouteBuilder;
05 import org.apache.camel.model.rest.RestBindingMode;
06 import org.springframework.stereotype.Component;
07
08 import br.com.devmedia.mobile.integracao.comum.PagamentoRequisicao;
09
10 @Component
11 public class MobileIntegracaoRouteBuilder extends RouteBuilder {
12
13 @Override
14 public void configure() throws Exception {
15
16 rest("/pagamento").id("rotaEntradaWS")
17 .description("Serviço para efetuar pagamento bancário")
18 .consumes("application/json")
19 .produces("application/json")
20 .post()
21 .bindingMode(RestBindingMode.json)
22 .type(PagamentoRequisicao.class)
23 .to("seda:postPagamento");
24
25 from("seda:postPagamento")
26 .log(LoggingLevel.INFO, "[MobileIntegracao] Nova requisição de pagamento bancário")
27 .end();
28 }
29 }
Analisando esse código, observa-se na linha 10 a declaração da anotação @Component para indicar que essa classe será gerenciada pelo Spring. Logo após, a linha 11 cria uma classe chamada MobileIntegracaoRouteBuilder. Atente para a parte final do nome, o trecho RouteBuilder, uma boa prática para que classes específicas, detentoras de rotas, sejam padronizadas e localizadas facilmente. Ainda na linha 11 é possível verificar que MobileIntegracaoRouteBuilder estende a classe abstrata do Camel RouteBuilder, o que a faz tornar-se uma classe específica para definição e construção de rotas. Com isso, é necessário sobrescrever o método void configure() para, então, definir em seu interior as rotas.
Já as linhas 16 a 23 declaram uma rota com o auxílio da DSL do Restlet para compor o web service RESTful responsável por receber as requisições de pagamento. Nesse código, a linha 16 permite a criação de um web service do tipo REST cujo path é /pagamento. Assim, a URL final para exposição do web service será formada da seguinte forma: http://localhost:8080/webservice/pagamento, onde localhost:8080 é o endereço padrão do servidor de aplicação, /webservice origina-se do servlet denominado RestletServlet mapeado no arquivo web.xml da aplicação e /pagamento é a declaração do path na classe em questão. Por fim, a inclusão do id “rotaEntradaWS” ao final da linha é uma boa prática para identificar e nomear rotas no contexto geral do Camel.
Continuando com a análise da Listagem 12, a linha 17 é apenas uma descrição da finalidade do web service. As linhas 18 e 19, por sua vez, indicam que o mesmo consumirá requisições com o conteúdo em JSON e produzirá respostas também com conteúdo em JSON. Já a linha 20 declara que o método desse web service será do tipo POST. Na sequência, as linhas 21 e 22 são incluídas para que, ao receber a requisição, o Camel faça a conversão automática do body da requisição para um objeto (POJO) PagamentoRequisicao da biblioteca MobileIntegracaoComum. Nessa conversão, o objeto já é incluído pelo próprio Camel na mensagem (Exchange) que está sendo trafegada na rota.
Na sequência, a linha 23 faz o envio da mensagem que está sendo manipulada ao longo da rota do web service para uma outra rota, chamada de postPagamento, que nesse caso é interna e específica do componente. Essa nova rota utiliza um componente do próprio Camel conhecido como SEDA, cuja finalidade é prover, através de uma fila, um comportamento assíncrono para uma arquitetura SEDA (vide BOX 1), de modo que as mensagens sejam trocadas em uma abordagem BlockingQueue (vide BOX 2) e os consumidores sejam chamados em uma thread separada do produtor.
Por último, as linhas 25 a 27 declaram uma outra rota para consumir as mensagens oriundas da primeira, que é a do web service. Para essa nova rota, chegará uma mensagem do tipo Exchange com o POJO devidamente incluído. Porém, na implementação desse primeiro requisito, apenas é logado, em modo INFO, que uma requisição de pagamento bancário foi enviada.
Com todas essas implementações, o primeiro fluxo de negócio foi atendido, conforme se observa na Figura 3, e a solução de pagamento bancário se encontra com uma arquitetura semelhante à Figura 4.
Figura 3. Primeiro fluxo de negócio atendido.
Figura 4. Diagrama atual da arquitetura da solução de pagamento bancário
BOX 1. SEDASEDA é a sigla para Staged Event-Driven Architecture e tem como proposta decompor uma complexa aplicação dirigida a eventos em componentes menores ligados por filas. Com essa arquitetura é possível evitar a alta sobrecarga de processamento que é associada à utilização de threads para tratar concorrência, além de servir como um modelo para desacoplar eventos e threads da camada lógica da aplicação. BOX 2. BlockingQueue
BlockingQueue ou Fila Bloqueante é uma estrutura de dados que utiliza uma fila onde os elementos são removidos de forma ordenada, seguindo um modelo conhecido como FIFO (First in, first out), em que o primeiro elemento que chega é o primeiro a sair. Além do mais, nesse modelo, qualquer tentativa em adicionar elementos em um fila cheia ou retirar de uma vazia será recusada, e sua principal vantagem é garantir que uma thread obtenha uma única mensagem por vez. No contexto do Java existem várias implementações de BlockingQueues, pois se trata apenas de uma interface. As mais conhecidas são: ArrayBlockingQueue, DelayQueue, LinkedBlockingQueue, PriorityBlockingQueue e SynchronousQueue.
Nesta segunda parte do artigo, iniciamos a construção e codificação de uma solução baseada em um cenário real para integrar um aplicativo mobile a sistemas externos que são necessários para que uma transação de pagamento bancário com cartão de crédito seja realizada com sucesso. Nessa primeira etapa prática, foi possível demonstrar, através da implementação do primeiro fluxo de negócio, todas as facilidades, vantagens e os recursos básicos do Apache Camel.
Portanto, pode-se destacar a facilidade de entendimento e compreensão do código através do uso de DSL para a declaração de rotas e processadores. Além disso, a utilização de componentes pré-existentes, a fim de realizar a conexão e comunicação com outros sistemas e componentes, também é destaque. Por fim, a inclusão do framework Spring para gerenciar os recursos da aplicação e realizar as injeções de dependências também demonstrou a simplicidade e agilidade no desenvolvimento que o mesmo pode acrescentar a soluções que adotam o Apache Camel.
Links Úteis e Referência
- http://camel.apache.org/download.html
- http://activemq.apache.org/activemq-560-release.html
- https://maven.apache.org/download.cgi
- http://projects.spring.io/spring-framework
- http://wildfly.org/downloads
Saiba mais sobre Apache Camel ;)
Artigos relacionados
-
Artigo
-
Artigo
-
Artigo
-
Artigo
-
Artigo