Introdução ao Smooks
1. Visão geral
Neste tutorial, apresentaremos oSmooks framework.
Descreveremos o que é, listaremos seus principais recursos e, eventualmente, aprenderemos como usar algumas de suas funcionalidades mais avançadas.
Em primeiro lugar, vamos explicar brevemente o que a estrutura se destina a alcançar.
2. Smooks
Smooks é uma estrutura para aplicativos de processamento de dados - lidando com dados estruturados como XML ou CSV.
Ele fornece APIs e um modelo de configuração que nos permite definir transformações entre formatos predefinidos (por exemplo, XML para CSV, XML para JSON e mais).
Também podemos usar várias ferramentas para configurar nosso mapeamento - incluindo scripts FreeMarker ou Groovy.
Além de transformações, o Smooks também oferece outros recursos, como validações de mensagens ou divisão de dados.
2.1. Características principais
Vamos dar uma olhada nos principais casos de uso de Smooks:
-
Conversão de mensagens - transformação de dados de vários formatos de origem em vários formatos de saída
-
Enriquecimento de mensagens - preenchendo a mensagem com dados adicionais, provenientes de fontes de dados externas, como banco de dados
-
Divisão de dados - processando arquivos grandes (GBs) e dividindo-os em arquivos menores
-
Ligação Java - construindo e preenchendo objetos Java a partir de mensagens
-
Validação de mensagem - executando validações como regex, ou mesmo criando suas próprias regras de validação
3. Configuração inicial
Vamos começar com a dependência Maven que precisamos adicionar ao nossopom.xml:
org.milyn
milyn-smooks-all
1.7.0
A versão mais recente pode ser encontrada emMaven Central.
4. Ligação Java
Agora vamos começar focando em vincular mensagens a classes Java. Faremos uma simples conversão de XML em Java aqui.
4.1. Conceitos Básicos
Começaremos com um exemplo simples. Considere o seguinte XML:
771
IN_PROGRESS
Para realizar esta tarefa com o Smooks, precisamos fazer duas coisas: preparar a configuração dos POJOs e do Smooks.
Vamos ver como é o nosso modelo:
public class Order {
private Date creationDate;
private Long number;
private Status status;
// ...
}
public enum Status {
NEW, IN_PROGRESS, FINISHED
}
Agora, vamos passar para os mapeamentos de Smooks.
Basicamente, os mapeamentos são um arquivo XML que contém lógica de transformação. Neste artigo, usaremos três tipos diferentes de regras:
-
bean – define o mapeamento de uma seção estruturada concreta para a classe Java
-
value - define o mapeamento para a propriedade particular do bean. Pode conter lógica mais avançada, como decodificadores, que são usados para mapear valores para alguns tipos de dados (como data ou formato decimal)
-
wiring – nos permite conectar um bean a outros beans (por exemploSupplier bean será conectado aOrder bean)
Vamos dar uma olhada nos mapeamentos que usaremos em nosso caso aqui:
yyyy-MM-dd
Agora, com a configuração pronta, vamos tentar testar se nosso POJO foi construído corretamente.
Primeiro, precisamos construir um objeto Smooks e passar o XML de entrada como um fluxo:
public Order converOrderXMLToOrderObject(String path)
throws IOException, SAXException {
Smooks smooks = new Smooks(
this.class.getResourceAsStream("/smooks-mapping.xml"));
try {
JavaResult javaResult = new JavaResult();
smooks.filterSource(new StreamSource(this.class
.getResourceAsStream(path)), javaResult);
return (Order) javaResult.getBean("order");
} finally {
smooks.close();
}
}
E, finalmente, afirme se a configuração é feita corretamente:
@Test
public void givenOrderXML_whenConvert_thenPOJOsConstructedCorrectly() throws Exception {
XMLToJavaConverter xmlToJavaOrderConverter = new XMLToJavaConverter();
Order order = xmlToJavaOrderConverter
.converOrderXMLToOrderObject("/order.xml");
assertThat(order.getNumber(), is(771L));
assertThat(order.getStatus(), is(Status.IN_PROGRESS));
assertThat(
order.getCreationDate(),
is(new SimpleDateFormat("yyyy-MM-dd").parse("2018-01-14"));
}
4.2. Encadernação Avançada - Referenciando Outros Beans e Listas
Vamos estender nosso exemplo anterior com as tagssuppliereorder-items:
771
IN_PROGRESS
Company X
1234567
-
1
PX1234
9.99
-
1
RX990
120.32
E agora vamos atualizar nosso modelo:
public class Order {
// ..
private Supplier supplier;
private List- items;
// ...
}
public class Item {
private String code;
private Double price;
private Integer quantity;
// ...
}
public class Supplier {
private String name;
private String phoneNumber;
// ...
}
Também temos que estender o mapeamento de configuração com as definições de beansuppliereitem.
Observe que também definimos o beanitems separado, que conterá todos os elementositem emArrayList.
Finalmente, usaremos o atributo Smookswiring, para agrupar tudo junto.
Veja como serão os mapeamentos neste caso:
yyyy-MM-dd
Por fim, adicionaremos algumas afirmações ao nosso teste anterior:
assertThat(
order.getSupplier(),
is(new Supplier("Company X", "1234567")));
assertThat(order.getItems(), containsInAnyOrder(
new Item("PX1234", 9.99,1),
new Item("RX990", 120.32,1)));
5. Validação de Mensagens
Smooks vem com mecanismo de validação baseado em regras. Vamos dar uma olhada em como eles são usados.
A definição das regras é armazenada no arquivo de configuração, aninhado na tagruleBases, que pode conter muitos elementosruleBase.
Cada elementoruleBase deve ter as seguintes propriedades:
-
name – nome exclusivo, usado apenas para referência
-
src – caminho para o arquivo de origem da regra
-
provider - nome de classe totalmente qualificado, que implementa a interfaceRuleProvider
Smooks vem com dois provedores prontos para uso:RegexProvidereMVELProvider.
O primeiro é usado para validar campos individuais no estilo de regex.
O segundo é usado para executar uma validação mais complicada no escopo global do documento. Vamos vê-los em ação.
5.1. RegexProvider
Vamos usarRegexProvider para validar duas coisas: o formato do nome do cliente e o número de telefone. RegexProvider como fonte requer um arquivo de propriedades Java, queshould contain regex validation in key-value fashion.
Para atender aos nossos requisitos, usaremos a seguinte configuração:
supplierName=[A-Za-z0-9]*
supplierPhone=^[0-9\\-\\+]{9,15}$
5.2. MVELProvider
UsaremosMVELProvider para validar se o preço total de cadaorder-item é menor que 200. Como fonte, prepararemos um arquivo CSV com duas colunas: nome da regra e expressão MVEL.
Para verificar se o preço está correto, precisamos da seguinte entrada:
"max_total","orderItem.quantity * orderItem.price < 200.00"
5.3. Configuração de Validação
Depois de preparar os arquivos de origem pararuleBases, passaremos para a implementação de validações concretas.
Uma validação é outra marca na configuração do Smooks, que contém os seguintes atributos:
-
executeOn - caminho para o elemento validado
-
name - referência aoruleBase
-
onFail - especifica qual ação será tomada quando a validação falhar
Vamos aplicar as regras de validação ao nosso arquivo de configuração Smooks e verificar sua aparência (note that if we want to use the MVELProvider, we’re forced to use Java binding, so that’s why we’ve imported previous Smooks configuration):
Agora, com a configuração pronta, vamos tentar testar se a validação falhará no número de telefone do fornecedor.
Novamente, temos que construir o objetoSmooks e passar o XML de entrada como um fluxo:
public ValidationResult validate(String path)
throws IOException, SAXException {
Smooks smooks = new Smooks(OrderValidator.class
.getResourceAsStream("/smooks/smooks-validation.xml"));
try {
StringResult xmlResult = new StringResult();
JavaResult javaResult = new JavaResult();
ValidationResult validationResult = new ValidationResult();
smooks.filterSource(new StreamSource(OrderValidator.class
.getResourceAsStream(path)), xmlResult, javaResult, validationResult);
return validationResult;
} finally {
smooks.close();
}
}
E, finalmente, afirme, se ocorreu um erro de validação:
@Test
public void givenIncorrectOrderXML_whenValidate_thenExpectValidationErrors() throws Exception {
OrderValidator orderValidator = new OrderValidator();
ValidationResult validationResult = orderValidator
.validate("/smooks/order.xml");
assertThat(validationResult.getErrors(), hasSize(1));
assertThat(
validationResult.getErrors().get(0).getFailRuleResult().getRuleName(),
is("supplierPhone"));
}
6. Conversão de Mensagem
A próxima coisa que queremos fazer é converter a mensagem de um formato para outro.
In Smooks, this technique is also called templatinge suporta:
-
FreeMarker (opção preferida)
-
XSL
-
modeloString
Em nosso exemplo, usaremos o mecanismo FreeMarker para converter a mensagem XML em algo muito semelhante ao EDIFACT e até mesmo preparar um modelo para a mensagem de e-mail com base no pedido XML.
Vamos ver como preparar um modelo para EDIFACT:
UNA:+.? '
UNH+${order.number}+${order.status}+${order.creationDate?date}'
CTA+${supplier.name}+${supplier.phoneNumber}'
<#list items as item>
LIN+${item.quantity}+${item.code}+${item.price}'
#list>
E para a mensagem de email:
Hi,
Order number #${order.number} created on ${order.creationDate?date} is currently in ${order.status} status.
Consider contacting the supplier "${supplier.name}" with phone number: "${supplier.phoneNumber}".
Order items:
<#list items as item>
${item.quantity} X ${item.code} (total price ${item.price * item.quantity})
#list>
A configuração do Smooks é muito básica desta vez (lembre-se de importar a configuração anterior para importar as configurações de ligação do Java):
/path/to/template.ftl
Desta vez, precisamos apenas passar umStringResult para o motor Smooks:
Smooks smooks = new Smooks(config);
StringResult stringResult = new StringResult();
smooks.filterSource(new StreamSource(OrderConverter.class
.getResourceAsStream(path)), stringResult);
return stringResult.toString();
E podemos, é claro, testá-lo:
@Test
public void givenOrderXML_whenApplyEDITemplate_thenConvertedToEDIFACT()
throws Exception {
OrderConverter orderConverter = new OrderConverter();
String edifact = orderConverter.convertOrderXMLtoEDIFACT(
"/smooks/order.xml");
assertThat(edifact,is(EDIFACT_MESSAGE));
}
7. Conclusão
Neste tutorial, focamos em como converter mensagens em diferentes formatos ou transformá-las em objetos Java usando Smooks. Também vimos como realizar validações com base em regras regex ou de lógica de negócios.
Como sempre, todo o código usado aqui pode ser encontradoover on GitHub.