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 SistemaFinanceiroApacheCamelexistente 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:

File

Log

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étodosfrom(...) e to(...)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");
        } 
    };
}

Projeto no GitHub

https://github.com/gabrieljony/camel-springboot-example

Comentários