08 de julho de 2020 • 9 min de leitura
Apache Camel: O framework de integração entre sistemas
Nesse artigo irei demostrar como usar o Apache Camel.
A Primeira Rota
A complexidade da integração
Muitas das aplicações hoje em dia não funcionam de forma isolada, estão sempre dependendo de outra funcionalidade externa. Nesse cenário, podemos reaproveitar reaproveitar funcionalidades já existentes, como é o caso do SOA.
Segundo o Wikipédia SOA significa Service-Oriented Architecture ou Arquitetura Orientada a Serviços, é um estilo de arquitetura de software cujo princípio fundamental prega que as funcionalidades implementadas pelas aplicações devem ser disponibilizadas na forma de serviços.
Hoje em dia com o Cloud e SOA, a integração se torna algo comum para os desenvolvedor.
Contudo, a integração é uma tarefa muito difícil de se fazer, as aplicações tem que se comunicarem de maneira robusta e desacoplada, e nesse ponto chega a ser trabalhoso, pois na maioria das vezes não construímos as aplicações pensando na integração. Além do mais, tem que trabalhar com varios tipos de plataformas, linguagens, formatos e protocolos diferentes, manter versões diversas, lidar com problemas da rede e falhas em geral, entre outras preocupações. Logo, não é uma tarefa muito simples.
As Boas práticas: Padrões de Integração
Devido ao quadro de integrar aplicações, foram identificados alguns padrões de como resolver os problemas mais comuns na integração. Os Enterprise Integration Patterns definem uma série de boas práticas que foram documentadas no livro com o mesmo nome, que descreve as vantagens e desvantagens de cada padrão e define um vocabulário comum a ser seguido.
O que é um framework de integração?
O Apache Camel é um framework de integração, que implementa a maioria dos padrões de integração. O Camel ajuda a diminuir a complexidade e o impacto dessas integrações. Em vez de escrever código de integração na mão e tendo muito trabalho com isso, o Camel já disponibiliza uma serie de componentes para isso.
Com um framework de integração seguimos boas práticas que foram identificadas e amadurecidas ao longo do tempo. Apache Camel é o framework de integração mais famoso no mundo Java, mas não é a única opção. O Spring Integration é uma outra alternativa que é também popular.
O que é o Camel?
Apache Camel é um framework de integração que ajuda a diminuir a complexidade e o impacto dessas integrações. Como um framework de integração, seguimos boas práticas que foram identificadas e descritas nos padrões de integração.
Em vez de escrever manualmente o código de integração, o Camel já disponibiliza componentes para isso, que podem ser facilmente configurados. Essas configurações são feitas na rota (routing engine). Ou seja, o Camel não implementa os padrões como SOAP e WSDL ele apenas configuram o componente que trabalha com isso.
O desenvolvedor principal do Camel, Claus Ibsen, descreveu o Camel da seguinte maneira:
Apache Camel é um framework Java de código aberto que tenta deixar a integração mais simples e acessível para todos os desenvolvedores. Ele faz isso através de:
- Implementações concretas dos padrões de integração (EIP)
- Conectividade com uma grande variedade de protocolos e APIs
- Uso de uma Domain Specific Languages (DSLs) para amarrar os EIPs e protocolos
Segundo os autores do framework, o significado de Camelé:
-Concise
-Application
-Message
-Exchange
-Language.
Essa linguagem é a Camel DSL!
Roteamento entre endpoints com Apache Camel
Essencialmente, Camel é um roteador (routing engine), ou seja o Camel roteia os dados entre dois endpoints. Onde um endpoint é [1] um serviço web ou [2] um banco de dados, podendo ser um arquivo ou arquivo JMS. Em geral, é um ponto onde pegamos ou enviamos dados. A tarefa do desenvolvedor é configurar, por meio de um Builder, os endpoints e as regras de roteamento. O desenvolvedor decide de onde vem as mensagens (from()
), para onde enviar (to()
) e o que fazer com a mensagem no meio desse processo (mediation engine).
A rota de lançamento
Nesse exemplo que irei demostrar, implemento uma rota de lançamento. O objetivo é que um sistema financeiro virtual gere um lançamento quando um usuário solicitar, que precisará ser entregue para outros sistemas que recebam os dados em formatos diferentes. Nesta rota, vamos trabalhar com JMS, SOAP, HTTP e várias transformações!
Inicializando Apache Camel
Vamos começar a usar Apache Camel na prática. O projeto desenvolvido foi criado a partir Spring Initializr, usando o Maven. As dependências iniciais foram a Web e o Devtools:
O projeto já possui alguns arquivos XML para a implementação. Depois vou deixar os arquivos no Github.
Os testes serão executados dentro do método main
, onde configuraremos a rota. Para tal é preciso criar um CamelContext
. Abra a classe SistemaFinanceiroApacheCamel
existente que já inicialize o CamelContext
:
public class SistemaFinanceiroApacheCamel {
public static void main(String[] args) throws Exception {
CamelContext context = new DefaultCamelContext();
}
}
Uma vez criado o CamelContext
, podemos adicionar uma nova rota e configurá-la usando do RouteBuilder
:
CamelContext context = new DefaultCamelContext();
context.addRoutes(
RouteBuilder builder = new RouteBuilder() {
@Override
public void configure() throws Exception {
//implementação das rotas...
from("file:lancamentos").
to("file:saida");
}
}
)
Observe que a classe RouteBuilder
é abstrata e devemos estendê-la, normalmente através de uma classe anônima. Ao estender é preciso sobrescrever o método configure()
O RouteBuilder é uma classe base derivada para criar regras de roteamento usando o DSL. Instâncias do RouteBuilder são adicionadas ao CamelContext.
Usando a Camel DSL
Dentro do método configure()
temos acesso à Camel DSL, uma linguagem especifica (baseada em Java) para configuração da rota! Um exemplo mais simples, iremos ler alguns lançamentos em XML de uma pasta e enviar para outra pasta. Para isso usaremos os métodos da Camel DSL, from(...) to(...)
. Os métodos recebem uma string que definem o endpoint, ou seja, o lugar concreto na integração de onde pegamos os dados ou enviamos.
public void configure() throws Exception {
from("file:lancamentos").
to("file:saida");
}
Com essa rota, definimos que queremos ler os arquivos da pasta lancamentos
e enviar para a pasta saida
. Nesse exemplo vamos usar o componente do Camel file
. Ele é bastante simples e sempre começa com file
, seguido por :
e o nome da pasta que estamos lendo e que desejamentos enviar para a outra pasta.
Para finalizar faça o "start()" e esperar um pouco para o Camel fazer o trabalho (Thread.sleep(...)
).
Veja o código completo:
public class SistemaFinanceiroApacheCamel {
public static void main(String[] args) throws Exception {
CamelContext context = new DefaultCamelContext();
context.addRoutes(new RouteBuilder() {
@Override
public void configure() throws Exception {
from("file:lancamentos"). //aqui tem um ponto para encadear a chamada do próximo método
to("file:saida");
}
}
context.start(); //aqui camel realmente começa a trabalhar
Thread.sleep(); //esperando um pouco para dar um tempo para camel
}
});
Execute a aplicação e notamos que o Camel moveu os arquivos de uma pasta para outra. Da pasta lancamentos para a pasta saida.
A Camel Expression Language
Vamos mostrar que o Camel realmente está trabalhando e adicionar um passo intermediário entre from()
e to()
. Utilizar o componente log()
do Camel:
public void configure() throws Exception {
from("file:lancamentos").
log("O Camel sendo executado...").
to("file:saida");
}
O componente log()
, podemos aproveitar uma linguagem simples, muito parecido com a Expression Language do mundo JSP. Imprimir informações dos dados que o Camel está usando. Quando o Camel lê os dados de algum endpoint, ele cria internamente uma mensagem. Essa mensagem possui uma ID e um Body que são trafegados na rota definida. Podemos acessar a id
e body
pela Expression Language(EL) do Camel:
public void configure() throws Exception {
from("file:lancamentos").
log("${id} - ${body}").
to("file:saida");
}
O ID é automaticamente gerada e o BODY é o conteúdo de cada arquivo XML.
É importante mencionar que a id é gerada automaticamente pelo Camel para cada mensagem na rota. Para gerar a id, o Camel usa o nome da máquina na rede concatenado com um seed.
Pode ser preciso personalizar a geração dessa id. Isso pode ser feito a partir do CamelContext
, passando um gerador propriamente implementado:
context.setUuidGenerator(new MeuGeradorPersonalizado());
Personalizando o trabalho de um componente
Podemos configurar os componentes por meio de parâmetros. Os parâmetros são usados na Camel DSL com a mesma sintaxe de parâmetros de uma requisição HTTP, por exemplo:
public void configure() throws Exception {
from("file:lancamentos?delay=5s").
log("${id} - ${body}").
to("file:saida");
}
O parâmetro noop=true
significa que os arquivos não serão apagados da pasta lancamentos
, algo útil para nossos testes.
Encontrando as configurações
Como descobrir quais são os parâmetros de um determinado componente. A documentação do Camel vem com várias centenas de componentes usados para acessar bancos de dados, filas de mensagens e APIs. A referência Component fornece informações sobre a funcionalidade e configuração de cada componente.
Alguns dos exemplos que utiizamos até o momento:
O que apresentado até o momento?
- O que é um framework de integração
- Camel é essencialmente uma routing-engine(mecanismo de roteamento)
- Camel segue boas práticas por meio do Padrões de Integração
- Devemos criar um rota usando o
RouteBuilder
- A Camel DSL é utilizada para configurar a rota de alto nível
- Os métodos
from(...)
eto(...)
definem os enpoints - Por meio da Camel Expression Language podemos acessar a mensagem na rota
Aplicação Prática
Nesse exemplo iremos usar o Spring Boot para testar o Camel. Por que usei o Spring Boot? Algo que se tornou muito comum em aplicações de microserviços é a integração do Spring Boot com o Camel.
Configurando o projeto Camel no Spring Boot
O Camel já esta na versão 3.4.0, data de Junho, 2020.
Pode encontrar outras versões do Camel aqui.
<properties>
<camel-version>3.4.0</camel-version>
</properties>
A configuração do Spring Boot, precisa ser adicionado as dependências que agrupam todas as dependências necessárias para ferramenta/biblioteca no projeto. Portanto, a dependência no arquivo pom.xml
o módulo camel-spring-boot-stater
, para o projeto Maven do Spring Boot. Como segue o exemplo abaixo.
<dependency>
<groupId>org.apache.camel.springboot</groupId>
<artifactId>camel-spring-boot-starter</artifactId>
<version>${camel-version}</version>
</dependency>
Criando a Classe
Criado uma classe ProdutoService
que herda de RouteBuilder
, anote-a com o stereotype @Service
e implemente o método abstrato configure()
. Moveremos os arquivos da pasta lancamentos
para a pasta saida
@Service
public class ProdutoServices extends RouteBuilder {
@Override
public void configure() throws Exception {
from("file:lancamentos").
log("${id} - ${body}").
to("file:saida");
}
}
Repare que não precisamos executar os métodos start
e stop
do contexto, já que o Spring irá injetar uma instância de SpringCamelContext
- que é responsável por criar e destruir o contexto, além de automaticamente encontrar todas as instâncias de RouteBuilder
do contexto do Spring e injetar no CamelContext
. Ficando mais simples a implementação com Spring Boot.
Executando
Rode a classe CamelSpringBootExampleApplication
e observe, no painel de administração, que as mensagens já devem estar enfileiradas.
Desvantagem
Nessa abordagem é usado herança na classe ProdutoService
. Se por algum motivo precisarmos que nosso serviço herde de outra classe, não será possível.
Uma alternativa seria, na classe de configuração (a classe CamelSpringBootExampleApplication
) retornarmos um RouteBuilder
com a rota configurada, como exemplo para esse DESAFIO:
@Bean
public RoutesBuilder rota() {
return new RouteBuilder() {
@Override
public void configure() throws Exception {
from("file:lancamentos").
log("${id} - ${body}").
to("file:saida");
}
};
}