Introdução ao Smooks

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}'

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})

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.