Integração de sistemas com Spring Integration – parte 1

Introdução

Integração, sempre vista como sinônimo de problemas e dores de cabeça na construção de sistemas. Nos dias de hoje, dificilmente uma aplicação estará sozinha, sem trocar informações com outros sistemas, sempre acaba existindo alguma integração. Com isso, nós desenvolvedores, temos que nos preocupar com os vários tipos de integrações possíveis, envolvendo diversas tecnologias, com as  particularidades de cada sistema, compatibilidade dos dados, etc.

Este é o primeiro de um sequência de dois post. Neste, serão apresentados o EAI – Enterprise Application Integration e o framework Spring Integrador, utilizado para realizar integrações entre sistemas. No próximo post serão abordados os detalhes de implementação com o framework.

EAI – Enterprise Application Integration

EAI trata-se de um conjunto de padrões para serem aplicados no desenvolvimento de integrações de sistemas.

De maneira geral as integrações em aplicações corporativas, excluindo as particularidades do negócio, seguem um certo padrão. Por exemplo:

  1. consome os dados fornecidos por um Web-Service;
  2. realiza um tratamento (transformação) nos dados;
  3. grava esses dados em um banco de dados.

Observando isso, Gregor Hohpe juntamente com Bobby Woolf, sob a orientação de Martin Fowler e Kyle Brown e motivados pela falta de uma “linguagem” apropriada para tratar esse assunto, definiram 65 padrões de integração. Os mesmos podem ser consultados em: http://www.enterpriseintegrationpatterns.com/toc.html

Os padrões estão organizados da seguinte forma:

  • Integration Styles descreve as diferentes maneiras de integrar aplicações;
  • Channel Patterns descreve as características de um sistema baseado em mensagens;
  • Message Construction Patterns descreve o formato e o conteúdo das mensagens;
  • Routing Patterns descreve os padrões para o envio das mensagens entre produtor/receptor;
  • Transformation Patterns descreve os padrões para transformações das mensagens;
  • Endpoint Patterns descreve o comportamento dos clientes dos sistemas de mensagens;
  • System Management Patterns descreve os padrões para o gerenciamento do sistema de mensagens.

Spring Integration Framework

É um framework da família Spring, utilizado para integrações e que implementa um sistema de mensagens baseado nos padrões de integração (EAI).

Seus principais objetivos são:

  1. Fornecer uma forma simples para a implementação de soluções de integração;
  2. Facilitar o comportamento assíncrono para o tratamento das mensagens;
  3. Aproveitar as facilidades fornecidas no Spring framework.

Principais componentes

Message
São os objetos que carregam informações. A mensagem é composta de 2 partes:

  • Header (informações adicionais, como por exemplo, uma flag utilizada num filtro);
  • Payload (dados ou informações que serão processadas).

message-sml

figura1 – representação do objeto mensagem

Message Channel
Representa o container onde as mensagens são colocadas para interligar um consumidor/produtor.

channel-sml

figura2 – representação de um channel

Endpoint
Como o princípio do Spring é a inversão de controle, o framework disponibiliza diversos endpoints prontos para serem utilizados como produtor/consumidor. Para utilizar, basta configurá-los conforme a necessidade. Alguns endpoints disponibilizados:

  • File
  • HTTP
  • JDBC
  • FTP
  • Mail
  • JMS
  • JPA
  • WebServices (SOAP / REST)

Message Endpoints
São componentes que desacoplam a lógica de negócios das funcionalidades de integração. São baseados no padrões (EAI) e possuem a responsabilidade de ligar os componentes do aplicativo com os canais de mensagens.

Alguns exemplos de message endpoints:

Splitter
Responsável por receber uma mensagem e fragmentá-la em diversas outras mensagens.
splitter-sml

figura3 – representação gráfica de um splitter

Aggregator
É o oposto do splitter.
aggregator-sml

figura4 – representação gráfica de um aggregator

Router
Baseado em uma condição, determina em qual canal de saída a mensagem deverá ser postada.
router-sml

figura5 – representação gráfica de um router

Filter
Baseado em um condição, determina quais mensagem serão encaminhadas para um canal de saída.
filter-sml

figura6 – representação gráfica de um filter

Transformer
Responsável pela conversão de conteúdo ou da estrutura de uma mensagem, retornando a mensagem modificada. Abaixo o exemplo do endpoint Enricher, responsável por enriquecer dos dados da mensagens com informações recuperadas de um banco de dados.
enricher-sml

figura7 – representação grárfica de um enricher

Exemplo

Para exemplificar os conceitos apresentados, faremos uma integração com um serviço na WEB que informa a localização geográfica de um endereço IP. Basicamente, dada uma lista de IPs, será consultado um Web Service que devolve a localização geográfica que é exibida no console.

As especificações do serviço e WSDL podem ser obtidos em: http://www.webservicex.net/geoipservice.asmx

soap

figura8 – especificação do request e response do serviço utilizado no exemplo

As configurações do Spring Integration: channels, componentes e endpoints devem ser realizadas em um XML que descreve o workflow da integração.

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xmlns:int="http://www.springframework.org/schema/integration"
	xmlns:stream="http://www.springframework.org/schema/integration/stream"
	xmlns:ws="http://www.springframework.org/schema/integration/ws"
	xmlns:context="http://www.springframework.org/schema/context"
	xsi:schemaLocation="http://www.springframework.org/schema/beans	http://www.springframework.org/schema/beans/spring-beans-4.0.xsd http://www.springframework.org/schema/integration http://www.springframework.org/schema/integration/spring-integration-4.0.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.0.xsd http://www.springframework.org/schema/integration/stream http://www.springframework.org/schema/integration/stream/spring-integration-stream-4.0.xsd http://www.springframework.org/schema/integration/ws http://www.springframework.org/schema/integration/ws/spring-integration-ws-4.0.xsd">

	<!-- Configurações do Spring Framework para localizar os Beans -->
	<context:component-scan base-package="com.matera.integration"/>
	<context:annotation-config />

	<!-- Dispatcher para execução das mensagens multithread (configurado para executar 3 threads)-->
	<bean id="executorMultitherad" class="org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor">
		<property name="corePoolSize" value="3" />
		<property name="daemon" value="false" />
	</bean>

	<!-- Ao receber uma mensagem no channel "startChannel", inicia o processo de integração e
		 invocano componente o "servicoIntegracao" o método "obtemIP", que recupera os IPS
		 que devem ser consultados no Web Service.
		 Faz um splitter na lista de ips, executando 1 mensagem por thread -->
	<int:chain id="obtemIps-chain" input-channel="startChannel" output-channel="construtorRequestChannel">
		<int:service-activator id="obtemIps" ref="servicoIntegracao" method="obtemIps" />
		<int:splitter id="splitterIps" />
	</int:chain>

	<!-- Canal que recebe os IPs -->
	<int:channel id="construtorRequestChannel">
		<int:dispatcher task-executor="executorMultitherad" />
	</int:channel>

	<!-- Ao receber uma mensagem com o IP no channel "construtorRequestChannel", invoca o método "constroiXmlRequest",
		 do serviço "servicoIntegracao", método responsável por construir o XML do request do Web Service. -->
	<int:chain id="constroiXmlRequest-chain" input-channel="construtorRequestChannel" output-channel="wsRequestChannel">
		<int:service-activator id="constroiXmlRequest" ref="servicoIntegracao" method="constroiXmlRequest" />
	</int:chain>

	<!-- Channel contendo os XMLs de request do Web Service -->
	<int:channel id="wsRequestChannel" />

	<!-- Define os channels de entrada e saída, a URI do Serviço e o SOAP-Action para realizar a integração
		 Quando uma mensagem for inserida no channel de entrada, o Spring Integration executa a Action
		 e coloca o retorno do serviço no channel de saída	-->
	<int:chain id="ws-chain" input-channel="wsRequestChannel" output-channel="outputChannel">
		<!-- Coloca SOAP-Action no header da mensagem -->
		<ws:header-enricher id="soap-action">
			<!-- SOAP-Action -->
			<ws:soap-action value="http://www.webservicex.net/GetGeoIP"/>
		</ws:header-enricher>
		<!-- URI do Web Service -->
		<ws:outbound-gateway id="soap-uri" uri="http://www.webservicex.net/geoipservice.asmx"/>
	</int:chain>

	<!-- Exibe no console as mensagens com o XML de resposta dos servicos -->
	<stream:stdout-channel-adapter id="outputChannel" append-newline="true"/>
</beans>

listagem 1 – configurações do workflow de integração

diagrama_integracao

figura9 – representação gráfica do workflow de integração

Abaixo segue exemplo da classe responsável por carregar o contexto e iniciar a execução da integração.

public class SpringIntegratorWebServiceTest {

	public static void main(String[] args) {
		// Carrega o contexto do Spring Integration
		ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("/META-INF/spring/integration/googleService.xml");
		// Cria objeto para manipular channels
		DestinationResolver<MessageChannel> channelResolver = new BeanFactoryChannelResolver(context);
		// Cria uma mensagem
		Message<String> message = MessageBuilder.withPayload("GO").build();
		// Cria o canal inChannel
		MessageChannel channel = channelResolver.resolveDestination("startChannel");
		// Coloca mensagem no channel para iniciar a integração
		channel.send(message);
	}

}

listagem 2 – Método main

 Abaixo o componente utilizado na integração.

@Service("servicoIntegracao")
public class ServicoIntegracao {

	/**
	 * Obtem uma lista de ips para serem utilizados na chamada do Web Service que retorna a
	 * localização geográfica do mesmo.
	 *
	 * @return lista de IPs
	 */
	public Collection<String> obtemIps() {
		Collection<String> ips = new ArrayList<String>();
		ips.add("200.147.67.142");
		ips.add("109.169.28.31");
		ips.add("43.253.231.176");
		ips.add("54.230.225.22");
		ips.add("174.35.93.189");
		return ips;
	}

	/**
	 * Constroi a requisição do serviço
	 * @param ip número IP
	 * @return xml request
	 */
	public String constroiXmlRequest(String ip) {
		// Cria XML de request, conforme definido no schema do serviço
		String requestXml =
			"<GetGeoIP xmlns=\"http://www.webservicex.net/\">" +
		         "<IPAddress>" + ip +"</IPAddress>" +
		    "</GetGeoIP>";
		return requestXml;
	}

}

listagem 3 – Componente Spring, utiliza pelo workflow de integração

Após executar a integração, o resultado abaixo será o apresentado no console:

<?xml version="1.0" encoding="UTF-8"?>
<GetGeoIPResponse xmlns = "http://www.webservicex.net/">
      <GetGeoIPResult>
            <ReturnCode>1</ReturnCode>
            <IP>200.147.67.142</IP>
            <ReturnCodeDetails>Success</ReturnCodeDetails>
            <CountryName>Brazil</CountryName>
            <CountryCode>BRA</CountryCode>
      </GetGeoIPResult>
</GetGeoIPResponse>
<?xml version="1.0" encoding="UTF-8"?>
<GetGeoIPResponse xmlns = "http://www.webservicex.net/">
      <GetGeoIPResult>
            <ReturnCode>1</ReturnCode>
            <IP>109.169.28.31</IP>
            <ReturnCodeDetails>Success</ReturnCodeDetails>
            <CountryName>United Kingdom</CountryName>
            <CountryCode>GBR</CountryCode>
      </GetGeoIPResult>
</GetGeoIPResponse>
<?xml version="1.0" encoding="UTF-8"?>
<GetGeoIPResponse xmlns = "http://www.webservicex.net/">
      <GetGeoIPResult>
            <ReturnCode>1</ReturnCode>
            <IP>43.253.231.176</IP>
            <ReturnCodeDetails>Success</ReturnCodeDetails>
            <CountryName>Japan</CountryName>
            <CountryCode>JPN</CountryCode>
      </GetGeoIPResult>
</GetGeoIPResponse>
<?xml version="1.0" encoding="UTF-8"?>
<GetGeoIPResponse xmlns = "http://www.webservicex.net/">
      <GetGeoIPResult>
            <ReturnCode>1</ReturnCode>
            <IP>174.35.93.189</IP>
            <ReturnCodeDetails>Success</ReturnCodeDetails>
            <CountryName>United States</CountryName>
            <CountryCode>USA</CountryCode>
      </GetGeoIPResult>
</GetGeoIPResponse>
<?xml version="1.0" encoding="UTF-8"?>
<GetGeoIPResponse xmlns = "http://www.webservicex.net/">
      <GetGeoIPResult>
            <ReturnCode>1</ReturnCode>
            <IP>54.230.225.22</IP>
            <ReturnCodeDetails>Success</ReturnCodeDetails>
            <CountryName>United States</CountryName>
            <CountryCode>USA</CountryCode>
      </GetGeoIPResult>
</GetGeoIPResponse>

listagem 4 – Resultado da execução

Conforme demonstrado, todo o fluxo de execução, paralelismo, acesso ao Web Service foi realizado pelo Spring Integration. Ao desenvolvedor coube a responsabilidade de implementar as regras de negócio necessárias para a integração e as configurações do workflow.

Baixe os fontes do projeto exemplo: springintegrator-exemplo.

No próximo post entrarei em mais detalhes do framework.

Até lá.

Referências
http://www.enterpriseintegrationpatterns.com
http://projects.spring.io/spring-integration

Por ANDRÉ AILTON DOS REIS

Arquiteto de Software, lidando com bits e bytes desde 1990. Músico, fotógrafo e aquarista nas horas vagas.

Postado em: 12 de março de 2015

Confira outros artigos do nosso blog

REST não é JSON

21 de agosto de 2017

Bruno Sofiato

[Webinar] Profile de aplicações Java com Oracle Mission Control e Flight Recorder

24 de julho de 2017

Danival Calegari

Criando Mocks de serviços REST com SoapUI

27 de junho de 2017

Monise Costa

JavaScript 6: diferença entre var, let e const

09 de maio de 2017

Otávio Felipe do Prado

Deixe seu comentário