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

Na parte 1 desse post apresentamos o padrão EAI e explicamos como o Spring Integration implementa esses padrões. Vimos também uma aplicação exemplo que faz a integração com um WebService que nos fornece a localização geográfica de um IP.
Nesse post, iremos nos aprofundar um pouco mais no assunto, adicionaremos algumas novas funcionalidades ao nosso projeto exemplo. Basicamente faremos o seguinte:

  1. Substituiremos a lista de IPs hardcode pela leitura de arquivo texto do File System. A aplicação irá monitorar um diretório configurável, quando for copiado um arquivo “.TXT”, contendo os IPs para pesquisa, será disparada a integração;
  2. Acessaremos, com os IPs do arquivo, o mesmo WebService do exemplo da parte 1 desse post;
  3. Iremos ler o envelope de resposta e classificar o IP, se ele encontra-se no Brasil ou Exterior;
  4. Através dessa classificação iremos gerar dois arquivos em um diretório destino: a) um arquivo contendo todos os IPs que estão no Brasil e b) um segundo arquivo contendo os IPs que estão fora do país.
  5. Colocaremos uma regra para descartar os endereços que estão armazenados na Argentina.

Então, mãos à obra…

1. O primeiro passo é configurar o endpoint responsável por monitorar e ler arquivos do fileSystem. Para isso, iremos modificar o arquivo que define o contexto do Spring e adicionar o seguinte:

<file:inbound-channel-adapter channel="filesIn"
	auto-create-directory="true"
	auto-startup="true"
	prevent-duplicates="true"
	filename-pattern="*.txt"
	directory="file:${app.inputdir}">
	<int:poller id="poller" fixed-delay="5" time-unit="SECONDS" />
</file:inbound-channel-adapter>

Acima estamos configurando o diretório que será monitorado de 5 em 5 segundos. Caso o diretório não exista, ele será criado automaticamente.
Só serão lidos arquivos “.TXT” e enquanto a aplicação estiver rodando, não será permitido o reprocessamento de arquivos que tenham o mesmo nome de arquivos já processados anteriormente.

Os arquivos lidos pelo endpoint serão enviados ao channel “filesIn”

<!-- Channel responsável por receber os arquivos carregado pelo endpoint que monitora o diretório do fileSystem -->
<int:channel id="filesIn" />

Em seguida o consumidor desse channel invoca um serviço handler que retorna um tipo File

<!-- Realiza o processamento do arquivo -->
<int:chain input-channel="filesIn" output-channel="filesOut">
	<int:service-activator ref="handler" />
</int:chain>

Conforme definido acima, o File, retornado pelo handler abaixo, é enviado para o channel “filesOut”.

Trecho do código do handler:

/**
 * Handler para File
 * @param input
 * @return
 */
public File handleFile(File input) {
	LOGGER.info("Copiando arquivo: " + input.getAbsolutePath());
	return input;
}

2. O acesso ao webservice é feito da mesma maneira que no exemplo do post 1, a única diferença é que agora a URL do endpoint está definido no properties da aplicação (app.properties):

<!-- Ao receber uma mensagem no channel "filesOut", inicia o processo de integração 
	 invocando 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 IP por thread -->
<int:chain id="obtemIps-chain" input-channel="filesOut" output-channel="construtorRequestChannel">
	<int:service-activator id="obtemIps" ref="servicoIntegracao" method="obtemIps" />
	<int:splitter id="splitterIps" />
</int:chain>

<!-- Canal que recebe os IPs para realizar a pesquisa ao webservice -->
<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="responseChannel">
	<!-- Coloca SOAP-Action no header da mensagem -->
	<ws:header-enricher id="soap-action">
		<!-- SOAP-Action -->
		<ws:soap-action value="${app.ws.endpoint.action}"/>
	</ws:header-enricher>
	<!-- URI do Web Service -->
	<ws:outbound-gateway id="soap-uri" uri="${app.ws.endpoint}"/>
</int:chain>

3. Na próxima etapa pegaremos o envelope retornado pelo endpoint de geo localização e faremos a classificação do IP, que será utilizada para realizar o roteamento das mensagens no Spring Integration.

<!-- Identifica se o IP está no brasil ou no exterior -->
<int:chain input-channel="responseChannel" output-channel="routerChannel">
	<int:service-activator ref="servicoIntegracao" method="classificaResponse" />
</int:chain>
/**
 * Gera o contexto e separa o que é IP hospedado no Brasil e o que está fora
 * @param response
 * @return Contexto
 */
public Contexto classificaResponse(Message<Object> response) {
	LOGGER.debug("Response [" + response.getPayload() + "]");
	if ("BRA".equals(Util.getCountry(response))) {
		return new Contexto(response, Util.getCountry(response), "BRA", Util.getIP(response));
	} else {
		return new Contexto(response, Util.getCountry(response), "EXTERIOR", Util.getIP(response));
	}
}

O método acima recebe a mensagem gerada pelo Spring Integration, contendo o envelope SOAP de resposta do WebService (o XML do envelope se encontra no payload da mensagem do Spring Integration). Em seguida criamos uma estrutura auxiliar, denominada Contexto, e implementamos a lógica para classificar o IP como: “BRA” e “EXTERIOR”. Esses valores são obtidos da TAG “CountryCode”, presente no XML do envelope de response.

4. e 5. Uma vez classificado o IP, iremos fazer o roteamento para realizar a gravação dos IPs em arquivos separados. O roteamento é feito através do componente:

<!-- Faz o roteamento conforme a classificação do país e retira da lista IPs que estão na Argentina -->
<int:chain input-channel="routerChannel">
	<int:filter expression="payload.country!='ARG'"/>
	<int:router expression="payload.localizacao">
		<int:mapping value="BRA" channel="braChannel"/>
		<int:mapping value="EXTERIOR" channel="exteriorChannel"/>
	</int:router>
</int:chain>

O que precisamos para realizar o roteamento e o filtro especificado, está no payload da mensagem. Como no nosso caso o payload é do tipo Contexto, quando necessário obter um valor do mesmo, basta acessar os seus atributos: Ex: payload.<nome_do_atributo/>.

Acima definimos o seguinte:

Filter para desconsiderar os IPs que estão na Argentina:

<int:filter expression="payload.country!='ARG'"/>

Regras de roteamento: Se a localização for “BRA”, as mensagens devem ser direcionadas para o channel “braChannel”, e se a localização for “EXTERIOR”, as mensagens devem ser direcionadas para o channel “exteriorChannel”.

<int:router expression="payload.localizacao">
	<int:mapping value="BRA" channel="braChannel"/>
	<int:mapping value="EXTERIOR" channel="exteriorChannel"/>
</int:router>

O trecho abaixo recebe a mensagem de roteamento e gera os arquivos para serem enviados ao endpoint responsável pela gravação no File System

<!-- Gera o arquivo com os IPs que estão no Brasil -->
<int:channel id="braChannel" />
<int:chain input-channel="braChannel" output-channel="fileBra">
	<int:service-activator ref="servicoIntegracao" method="geraArquivoBRA" />
</int:chain>

<!-- Gera o arquivo com os IPs que estão no Exterior -->
<int:channel id="exteriorChannel" />
<int:chain input-channel="exteriorChannel" output-channel="fileExterior">
	<int:service-activator ref="servicoIntegracao" method="geraArquivoExterior" />
</int:chain>

Finalmente, os endpoints do Spring Integration que gravam os arquivos em disco

<!-- Endpoint responsavel por fazer a gravacao do arquivo contendo os IPs do Brasil -->
<file:outbound-channel-adapter id="fileBra"
	auto-create-directory="true"
	directory="file:${app.outputdirbra}"
	delete-source-files="true"
	mode="APPEND" />

<!-- Endpoint responsavel por fazer a gravacao dos arquivos contendo os IPs do Exterior -->
<file:outbound-channel-adapter id="fileExterior"
	auto-create-directory="true"
	directory="file:${app.outputdirexterior}"
	delete-source-files="true"
	mode="APPEND" />

Acima configuramos: O caminho destino, que é definido no properties da aplicação; que os arquivos de origem devem ser apagados após realização da cópia para o destino; se o diretório configurado não existir ele será criado automaticamente; e que os arquivos devem der “appendados” com os novos IPs resultantes de novos processamentos.

Conclusão
Vimos que o Spring Integration nos auxilia muito no atendimento aos requisitos de uma integração e também a lidar com a infraestrutura necessária para a integração, sem que tenhamos que gastar esforço em construir nada além da lógica e fluxo de negócio. Outro fator importante é que não é necessário possuir um servidor de aplicação como o JBoss, por exemplo, ou um container Servlet, como o Apache Tomcat.

Pré Requisitos para a execução dos códigos de exemplo

  • Java SDK 1.8;
  • Maven 3.x;
  • Eclipse IDE.

Execução do projeto exemplo

  • Fazer o download e descompactar o .ZIP  que contém os fontes do projeto exemplo (ver referências);
  • Importar os fontes para a IDE Eclipse utilizando o plugin do Maven;
  • Configurar os caminhos desejados no arquivo app.properties;
  • Criar o diretório, que será monitorado, na máquina onde a aplicação irá executar;
  • Copiar o arquivo “..\Spring-Integrator-parte 2\springintegrator-exemplo\src\main\resources\META-INF\ips.txt” para o diretório monitorado;
  • Executar, no Eclipse, o método main da classe “org.springframework.context.support.ClassPathXmlApplicationContext.java”.


Referências

Integração com Spring Integration – parte-1
Códigos fontes exemplo Spring-Integration – parte 2

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: 25 de setembro 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