Simplificando atividades com Flat Files utilizando Flatworm

O objetivo da biblioteca Flatworm é permitir de forma simples a conversão de flat files para JavaBeans ou vice versa. Neste artigo, iremos abordar como esta biblioteca pode acelerar atividades que envolvam a leitura ou escrita de flat files.

Marcelo Nascimento, Application Architect

Marcelo NascimentoMarcelo Nascimento

Marcelo Nascimento é pós-graduado em análise de sistemas pela Universidade Presbiteriana Mackenzie. Atualmente atua como arquiteto de aplicações.



14/Jun/2013

Introdução

Quando desejamos implementar um sistema, temos a disposição uma vasta variedade de opções tecnológicas e linguagens de programação. Esta variedade nos dá flexibilidade de fazer escolhas de acordo com nossas necessidades. Mas, esta mesma flexibilidade que nos permite fazer escolhas de qual tecnologia utilizar, algumas vezes pode nos colocar diante de um desafio de realizar troca de informações entre sistemas em tecnologias diferentes.

Quando estamos diante de situações como esta, ou seja, necessitamos trocar informações entre sistemas em tecnologias diferentes, temos como opção de solução os flat files.


O que é flat file

De forma ampla, podemos definir que flat file é um arquivo geralmente no formato de texto plano (plain text) composto por linhas e colunas.

Para indicar o início e fim das colunas, pode-se utilizar caracteres delimitadores ou determinar uma largura fixa.

Na listagem 1 é demonstrado um exemplo de arquivo onde o caractere ponto e vírgula é o delimitador de colunas.

Listagem 1. Arquivo com caractere delimitador de colunas.

10;COMPUTADOR;200
11;DVD;500
12;APARELHO DE SOM;200
13;GELADEIRA;150
14;VIDEO GAME;30

Neste momento, não se preocupe em entender o que significam estas informações. O importante é notar que este arquivo é composto por 5 linhas e 3 colunas. Para determinar o término de uma coluna e início de outra é utilizado o caractere ponto e vírgula (;).

Vamos analisar outro exemplo na listagem 2 do mesmo arquivo, porém a delimitação das colunas é feita por largura fixa.

Listagem 2. Arquivo com colunas de largura fixa.

Clique aqui para ver lista de códigos

10COMPUTADOR                         0020011DVD                                0050012APARELHO DE SOM                    0020013GELADEIRA                          0015014VIDEO GAME                         00030

No arquivo da listagem 2, a primeira coluna tem uma largura de 2 caracteres (ou posições), enquanto a terceira coluna tem uma largura de 5 caracteres. Mesmo quando à largura determinada for superior ao necessário, é importante sempre preencher as posições remanescentes para não ocasionar um problema de posicionamento das colunas e consequentemente um problema na leitura do arquivo. Podemos notar isso em nossa segunda coluna (a partir da terceira posição), onde claramente 35 posições foram mais do que suficiente para preencher com dados (nomes de eletroeletrônicos), mas mesmo assim, completamos com espaços em branco a diferença que não foi utilizada antes de iniciar a terceira coluna.

Se compararmos a listagem 1 e 2, podemos notar que os dados são exatamente os mesmos, ou seja, o arquivo ainda continua com 5 linhas e 3 colunas, porém na listagem 2 cada coluna possui um tamanho fixo e na listagem 1 o fim da coluna é determinado por um caractere.

Muitas linguagens de programação possuem mecanismos de desenvolvimento para possibilitar a geração de flat files. Desta forma, é possível trocar de informações entre aplicações mesmo que desenvolvidas em tecnologias diferentes.


O que é Flatworm

Apesar das linguagens de programação possuírem mecanismos para criação de flat files, muitos desenvolvedores já tiveram a amarga experiência de necessitar ler e separar dados de um flat file para transformá-los em JavaBeans. Esta é uma tarefa que pode exigir que o desenvolvedor crie uma série conversores de tipos de dados, leitores de registros, dentre outros mecanismos, que se não forem abstratos o suficiente, deverão ser ajustados para novos projetos que tenham este mesmo requisito.

Foi para facilitar este trabalho árduo que James Turner criou uma biblioteca chamada de Flatworm, sob a licença Apache License, versão 2.0 (licença definida para a versão 2.0.1 utilizada neste artigo).

A biblioteca Flatworm permite que o desenvolvedor descreva todas as informações de conversão através de um arquivo XML (eXtensible Markup Language) de definição. Este XML é utilizado pelo Flatworm para ler as linhas do flat file e transformá-las em um ou mais JavaBeans. Se necessário, pode ser feito o processo inverso, ou seja, ler objetos JavaBeans e transformar seus dados em flat file.

A figura 1 ilustra de maneira macro o funcionamento do Flatworm.

Figura 1. Funcionamento da biblioteca Flatworm.

Na figura 1, podemos notar que a biblioteca Flatworm busca as informações de como interpretar o flat file a partir de um arquivo XML de definição. Então, a API analisa (parsing) o flat file contra as configurações carregadas, para no final gerar objetos JavaBeans devidamente carregados com as informações provenientes do flat file. É importante notar também que o caminho inverso também é válido, ou seja, primeiro podemos fazer a leitura dos dados providos em JavaBeans para ao final gerar entradas no flat file.

Com este mecanismo, a biblioteca Flatworm tem um alto nível de abstração, pois a maneira de como a API irá tratar as informações, separá-las e gravá-las estão em um arquivo XML de definição, e pode ser personalizado para cada necessidade.


Pré-requisitos

O pacote (ou JAR) da biblioteca Flatworm não é autossuficiente, ou seja, é necessário ter a disposição algumas dependências auxiliares para seu correto funcionamento, portanto algumas bibliotecas do projeto Apache Commons são necessárias. Abaixo, podemos visualizar a lista das dependências necessárias que devem estar à disposição quando a biblioteca Flatworm estiver carregada:

  • Commons Beanutils;
  • Commons Collections;
  • Commons Logging;
  • Commons Lang.

Instalação

Para a instalação, primeiramente será necessário obter a biblioteca Flatworm no portal do projeto ou através da página direta de download.

A instalação da biblioteca dependerá de como o aplicativo será empacotado e como ele funcionará. Dentre as opções de instalação, podemos:

  • Incluir a biblioteca no diretório WEB-INF/lib da aplicação;
  • Disponibilizar a biblioteca em uma biblioteca compartilhada (shared library);
  • Configurar a biblioteca no caminho de classe (classpath) da aplicação.

Importante: Não podemos nos esquecer das dependências que citamos na seção "Pré-requisitos", se não incluí-las, a biblioteca Flatworm não funcionará corretamente.


Utilizando o Flatworm

Agora que conhecemos a biblioteca Flatworm, vamos criar um exemplo de aplicação JavaTM (WEB) para demonstrar seu funcionamento. Na tabela 1 são apresentadas todas as bibliotecas e suas versões que utilizaremos na escrita do exemplo.

Tabela 1. Versões das bibliotecas utilizadas.

BibliotecaVersão
Flatworm 2.0.1
Commons Beanutils 1.8.3
Commons Collections 3.2.1
Commons Lang 2.6
Commons Logging 1.1.1

Durante a criação do exemplo, não será foco a aplicação de boas práticas de programação, pois o intuito é apresentar o funcionamento da API.

O ambiente de desenvolvimento e execução definido é o Eclipse® Java EE IDE for Web Developers, com Apache Tomcat 7.0 e Java 1.6.

Exemplo – Troca de arquivos através de flat files

Uma situação comum de utilização de flat file é a troca de informações entre empresas ou parceiros comerciais. A figura 2 apresenta como esta interação pode ocorrer.

Figura 2. Cenário de troca de informações entre parceiros.

Como pode ser notado na figura 2, a empresa XPTO gera um flat file que será consumido pela empresa XYZ. Após a empresa XYZ consumir, ela gera um flat file que neste caso será consumido pela empresa XPTO. Em outras palavras, podemos dizer que as empresas XPTO e XYZ trocam informações entre si através de flat files.

Basicamente, ambas as empresas necessitam ler e gerar flat files. Nos exemplos a seguir, vamos entender como podemos realizar estas atividades utilizando a biblioteca Flatworm.

O flat file enviado pela empresa XYZ deve seguir um formato para que a empresa XPTO possa ler adequadamente e consumi-lo. A tabela 2 apresenta o formato (layout) definido.

Tabela 2. Formato do flat file.

NomeTipoInícioFim
Código Caractere 1 2
Nome do Produto Caractere 3 38
Quantidade Inteiro 39 43

Podemos notar na tabela 2 que o flat file gerado a partir deste formato (layout) será de largura fixa (posicional) e composto por três colunas. Agora que sabemos como o flat file será gerado, podemos criar nosso JavaBean que posteriormente será criado e carregado com informações pelo Flatworm. A listagem 3 mostra o JavaBean criado para armazenar estas informações.

Listagem 3. Javabean utilizado para carregar os dados do flat file.

import java.io.Serializable;

public class ProdutoBean implements Serializable {

	private static final long serialVersionUID = 1L;

	private String codigo;
	private String nome;
	private Integer quantidade;

	//Implementação omitida dos métodos getters e setters.
}

Percebam na listagem 3 que criamos uma classe chamada ProdutoBean e nela temos os mesmos atributos definidos no layout (vide tabela 2), com os mesmos tipos de dados.

Neste momento, nós já sabemos como o flat file será gerado, já criamos o JavaBean que representará cada linha que estará contida no flat file, mas ainda não criamos o arquivo XML de definição que irá dizer ao flatworm como separar adequadamente as informações. Na listagem 4, este arquivo é apresentado.

Listagem 4. Código fonte do arquivo XML de definição (flatworm-definition.xml).

  <?xml version="1.0" encoding="ISO-8859-1"?>
  
  <file-format encoding="iso-8859-1">
        <converter name="string" 
  class="com.blackbear.flatworm.converters.CoreConverters" method="convertChar" 
  return-type="java.lang.String" />
        <converter name="integer" 
  class="com.blackbear.flatworm.converters.CoreConverters" method="convertInteger" 
  return-type="java.lang.Integer" />
    <record name="produto">
      <record-ident>
        <length-ident minlength="43" maxlength="43" />
      </record-ident>
      <record-definition>
        <bean name="produtoBean" class="br.com.ibm.dw.flatworm.vo.ProdutoBean" />
        <line>
          <record-element length="2" beanref="produtoBean.codigo" type="string">
            <conversion-option name="justify" value="left" />
          </record-element>
          <record-element length="35" beanref="produtoBean.nome" type="string">
            <conversion-option name="justify" value="left" />
          </record-element>
          <record-element length="6" beanref="produtoBean.quantidade" type="integer">
            <conversion-option name="justify" value="right" />
            <conversion-option name="pad-character" value="0" />
            <conversion-option name="default-value" value="0" />
          </record-element>
        </line>
      </record-definition>
    </record>
  </file-format>

A listagem 4 apresenta as definições que configuramos para que a biblioteca Flatworm utilize durante a execução. Este arquivo será chamado de flatworm-definition.xml. Vamos entender as configurações apresentadas.

O elemento (tag) file-format é a raiz, todas as configurações deverão estar dentro deste elemento.

Em seguida, definimos os conversores que serão utilizados através do elemento converter. Os conversores tem um papel importante, pois eles transformam os dados contidos no flat file em um tipo Java. Este elemento possui quatro atributos:

  • name: Este atributo é uma identificação que definimos para referenciar o conversor;
  • class: Este atributo define a classe que contém o conversor;
  • method: Este atributo define o método da classe definido para realizar a conversão;
  • return-type: Este atributo define qual será o tipo de retorno após a conversão.

A biblioteca fornece por padrão um conjunto de conversores através da classe com.blackbear.flatworm.converters.CoreConverters. A tabela 3 lista os conversores pré-definidos na biblioteca Flatworm até o momento da escrita do artigo.

Tabela 3. Lista de conversores pré-definidos na biblioteca Flatworm.

ConversorDescrição
convertChar Não faz conversão, apenas retorna os dados em um objeto do tipo java.lang.String.
convertDecimal Converte os dados em um objeto do tipo java.lang.Double.
convertDate Converte os dados em um objeto do tipo java.util.Date.
convertInteger Converte os dados em um objeto do tipo java.lang.Integer.
convertLong Converte os dados em um objeto do tipo java.lang.Long.
covertBigDecimal Converte os dados em um objeto do tipo java.math.BigDecimal.

Se necessário, podemos definir nossos próprios conversores personalizados. Para o nosso exemplo, apenas dois da própria biblioteca foram necessários (convertChar e convertInteger).

Vamos continuar com a análise do XML.

O elemento record define as configurações para leitura/geração de um registro. Em nosso exemplo, definimos um record com o nome de "produto" através do atributo name.

Para a identificação de cada linha no flat file, utilizamos as tags record-ident e length-ident. Com base no layout (vide tabela 2), sabemos que cada linha tem um tamanho de 43 posições e este foi o valor definido nos atributos minlength e maxlength.

Agora, devemos fazer o mapeamento entre a linha do flat file e o JavaBean. Isto será feito através do elemento record-definition. Dentro do elemento record-definition, utilizamos o elemento bean para identificar qual será o JavaBean que receberá as informações do flat file. Neste elemento necessitamos definir uma identificação (nome único de livre escolha) e a classe que será instanciada. Para estas definições, utilizamos os atributos: name com o valor "produtoBean" e class com o valor br.com.ibm.dw.flatworm.vo.ProdutoBean (vide listagem 3).

Através do atributo line, relacionamos cada coluna da linha com todos os atributos do JavaBean. O relacionamento deve ser um a um, por isso, utilizamos o elemento record-element. Neste elemento definimos os atributos:

  • length: Para definir quantas posições serão lidas do registro do flat file;
  • beanref: Para definir para qual atributo do JavaBean estes dados serão enviados;
  • type: Para definir qual o conversor será utilizado antes de preencher o dado no atributo.

No elemento record-element, podemos definir configurações adicionais antes de definir o dado no atributo do JavaBean. Fazemos isso através do elemento conversion-option. Este elemento é composto pelos atributos name e value. O atributo name informa qual configuração queremos definir e o atributo value é o valor que queremos definir para a configuração. Em nosso exemplo, utilizamos as configurações:

  • default-value: Para definir o caractere a ser utilizado caso o campo esteja vazio;
  • justify: Para definir regra de justificação (left, right ou both) no campo. Esta configuração geralmente é usada em conjunto com pad-character;
  • pad-character: Para definir um caractere para completar os espaços remanescentes em uma cadeia de caracteres. Esta configuração geralmente é usada em conjunto com justify.

Outras configurações tais como: decimal-places e decimal-implied, também são possíveis opções de configuração de conversão. Acesse a página do projeto para maiores informações de configurações.

Agora que nosso arquivo de definição está pronto, a biblioteca Flatworm saberá como deve ler as informações do flat file para a geração dos JavaBeans.

Vamos construir um Servlet que fará a leitura de um flat file e imprimir as informações em uma página WEB. Este Servlet será chamado de ConsomeFlatFileServlet e analisaremos seu código fonte na listagem 5.

Listagem 5. Código fonte do Servlet ConsomeFlatFileServlet.

01. protected void doPost(HttpServletRequest request,
02.				HttpServletResponse response) throws ServletException, 
IOException {
03. 
04. 	//Objeto que faz a leitura da configuração.
05. 	ConfigurationReader configReader = new ConfigurationReader();
06. 
07. 	try {
08. 
09. 		//Obtemos o stream do arquivo de definição.
10. 		InputStream flatFileDefinition = 
this.getServletContext().getResourceAsStream("/WEB-INF/resources/flatworm-
definition.xml");
11. 
12. 		//Carregamos as configurações do arquivo de definição.
13. 		FileFormat ff = 
configReader.loadConfigurationFile(flatFileDefinition);
14. 
15. 		//Fazemos a leitura do arquivo flat file. 
16. 		InputStream in = new FileInputStream(new File("C:/file.txt"));
17. 		BufferedReader bufIn = new BufferedReader(new InputStreamReader(in));
18. 
19. 		//Imprimimos um título para a página.
20. 		response.setContentType("text/html");
21. 		PrintWriter printWriter = response.getWriter();
22. 		printWriter.write("<h1>Dados lidos do flat file</h1>");
23. 
24. 		MatchedRecord results;
25. 
26. 		//Percorremos todos os registros do flat file.
27. 		while((results = ff.getNextRecord(bufIn)) != null) {
28. 
29. 			if(results.getRecordName().equals("produto")) {
30. 
31. 				//Obtemos o JavaBean já carregado com as informações do 
flat file.
32. 				ProdutoBean produtoBean = (ProdutoBean) 
results.getBean("produtoBean");
33. 
34. 				//Imprimimos os dados do JavaBean.
35. 				printWriter.write("Código:" + produtoBean.getCodigo() + 
"<br />");
36. 				printWriter.write("Nome:" + produtoBean.getNome() + 
"<br />");
37. 				printWriter.write("Quantidade:" + 
produtoBean.getQuantidade() + "<br /><br />");
38.			}
39.		}
40.		
41.		flatFileDefinition.close();
42.		in.close();
43.		bufIn.close();
44.	}
45.	catch(Exception e) {
46.		e.printStackTrace();
47.	}
48. }

Acima, podemos observar na linha 05 a criação de um objeto da classe com.blackbear.flatworm.ConfigurationReader. Este objeto será utilizado na linha 13 para carregamos as informações do arquivo de definição através do método loadConfigurationFile. Este método recebe como parâmetro um objeto do tipo java.io.InputStream que declaramos na linha 10 e retorna um objeto do tipo com.blackbear.flatworm.FileFormat. Este objeto é utilizado no laço da linha 27 para extrair cada registro do flat file através do método getNextRecord . Este método recebe como parâmetro um objeto do tipo java.io.BufferedReader que foi criado nas linhas 16 e 17 e retorna um objeto do tipo com.blackbear.flatworm.MatchedRecord. Com esta referência, validamos na linha 29, através do método getRecordName se o registro do arquivo de definição que foi utilizado para geração do JavaBean se chama "produto" (vide listagem 4), assim garantimos que estamos lendo as configurações corretas. Em seguida na linha 32, obtermos o JavaBean com as informações do flat file através do método getBean. Nas linhas 35 a 37 apenas imprimimos os dados carregados no JavaBean.

O resultado da listagem 5 pode ser visto na figura 3.

Figura 3. Resultado após a execução do Servlet ConsomeFlatFileServlet.

Perceba que, neste nosso exemplo, os registros apresentados na figura 3 são os mesmos que apresentamos no flat file da listagem 2.

Mas e a situação inversa? Muitas vezes necessitamos gerar um flat file a partir de JavaBeans. E na interação que apresentada na Figura 2, uma das empresas poderia ter esta necessidade.

No código da listagem 5, geramos JavaBeans a partir de um flat file. Então agora, vamos gerar um flat file a partir de uma lista de JavaBeans.

A listagem 6 apresenta o código do Servlet (GeraFlatFileServlet) para a geração do flat file.

Listagem 6. Código fonte do Servlet GeraFlatFileServlet.

01. protected void doPost(HttpServletRequest request,
02. 				HttpServletResponse response) throws ServletException, 
IOException {
03. 
04. 	try {
05. 
06. 		//Obtemos o stream do arquivo de definição.
07. 		InputStream flatFileDefinition = 
this.getServletContext().getResourceAsStream("/WEB-INF/resources/flatworm-
definition.xml");
08. 
08. 		//Criamos o flat file.
10. 		FileCreator fileCreator = new FileCreator(flatFileDefinition,
"C:/file.txt");
11. 
12. 		//Definimos como será a quebra de linha.
13. 		fileCreator.setRecordSeperator("\r\n");
14. 
15. 		//Abrimos o flat file para escrita.
16. 		fileCreator.open();
17. 
18. 		for(ProdutoBean produtoBean: this.obterProdutos()) {
19. 
20. 			//Definimos o JavaBean para criação da linha.
21. 			fileCreator.setBean("produtoBean", produtoBean);
22. 
23. 			//Criamos a linha no flat file.
24. 			fileCreator.write("produto");
25. 		}
26. 
27. 		//Fechamos o flat file.
28. 		fileCreator.close();
29. 
30. 		//Imprimimos um título na página para indicar o sucesso.
31. 		response.setContentType("text/html");
32. 		PrintWriter printWriter = response.getWriter();
33. 		printWriter.write("<h1>Arquivo gerado com sucesso!</h1>");
34. 	}
35. 	catch(Exception e) {
36. 		e.printStackTrace();
37. 	}
38. }

Acima, na linha 07, criamos novamente uma referência do tipo java.io.InputStream para carregarmos as informações do arquivo de definição flatworm-definition.xml. Na linha 10, criamos uma instância da classe com.blackbear.flatworm.FileCreator. Este objeto é responsável por inserir as informações no flat file, por isso, seu construtor necessita do arquivo de definição e também um objeto do tipo java.lang.String contendo o caminho do arquivo que receberá os dados. Na linha 13, através do método setRecordSeperator definimos como será feita a quebra de linha. Na linha 16, abrimos o flat file para escrita através do método open. Na linha 18, temos um laço (for) que navegará por uma lista (java.util.ArrayList) de objetos do tipo ProdutoBean (vide listagem 3) gerada pelo método obterProdutos (vide listagem 7). No corpo deste laço, na linha 21, invocamos o método setBean passando por parâmetro o JavaBean que contém as informações a serem gravadas no flat file. Na linha 24, através do método write, gravamos as informações no flat file. Note que este método necessita de um parâmetro chamado nome do registro (record name). É através desta informação (definida no arquivo de definição) que a biblioteca Flatworm sabe como organizar as informações no flat file. Para finalizar, na linha 28, invocamos o método close para fechar o file file.

Listagem 7. Código fonte do método obterProdutos.

01. private List<ProdutoBean> obterProdutos() {
02. 
03. 	List<ProdutoBean> produtos = new ArrayList<ProdutoBean>();
04. 
05. 	produtos.add(new ProdutoBean("10", "COMPUTADOR", 200));
06. 	produtos.add(new ProdutoBean("11", "DVD", 500));
07. 	produtos.add(new ProdutoBean("12", "APARELHO DE SOM", 200));
08. 	produtos.add(new ProdutoBean("13", "GELADEIRA", 150));
09. 	produtos.add(new ProdutoBean("14", "VIDEO GAME", 30));
10. 
11. 	return produtos;
12. }

O resultado da listagem 6 pode ser visto na figura 4.

Figura 4. Resultado após a execução do Servlet GeraFlatFileServlet.

Abaixo, na figura 5, temos o flat file aberto e com os dados escritos.

Figura 5. Arquivo gerado após a execução do Servlet GeraFlatFileServlet.


Conclusão

Quando precisamos realizar a leitura de pequenos flat files, com poucas colunas, talvez seja simples desenvolver utilizando os artifícios da linguagem de programação. Mas quando estes arquivos se tornam complexos, com muitas colunas, muitos tipos de dados e diversas regras, a biblioteca Flatworm pode ajudar a reduzir o tempo de desenvolvimento e manutenção da aplicação.


Referências

Flat file

JavaBeans

Flatworm

Flatworm JavaDoc

Flatworm Download

Flatworm código fonte

Flatworm DTD e dependências

Apache Commons

Comentários

developerWorks: Conecte-se

Los campos obligatorios están marcados con un asterisco (*).


Precisa de um ID IBM?
Esqueceu seu ID IBM?


Esqueceu sua senha?
Alterar sua senha

Ao clicar em Enviar, você concorda com os termos e condições do developerWorks.

 


A primeira vez que você entrar no developerWorks, um perfil é criado para você. Informações no seu perfil (seu nome, país / região, e nome da empresa) é apresentado ao público e vai acompanhar qualquer conteúdo que você postar, a menos que você opte por esconder o nome da empresa. Você pode atualizar sua conta IBM a qualquer momento.

Todas as informações enviadas são seguras.

Elija su nombre para mostrar



Ao se conectar ao developerWorks pela primeira vez, é criado um perfil para você e é necessário selecionar um nome de exibição. O nome de exibição acompanhará o conteúdo que você postar no developerWorks.

Escolha um nome de exibição de 3 - 31 caracteres. Seu nome de exibição deve ser exclusivo na comunidade do developerWorks e não deve ser o seu endereço de email por motivo de privacidade.

Los campos obligatorios están marcados con un asterisco (*).

(Escolha um nome de exibição de 3 - 31 caracteres.)

Ao clicar em Enviar, você concorda com os termos e condições do developerWorks.

 


Todas as informações enviadas são seguras.


static.content.url=http://www.ibm.com/developerworks/js/artrating/
SITE_ID=80
Zone=Tecnologia Java
ArticleID=933822
ArticleTitle=Simplificando atividades com Flat Files utilizando Flatworm
publish-date=06142013