Introduzione a Smooks

1. Panoramica

In questo tutorial, introdurremo il framework Smooks.

Descriveremo di cosa si tratta, elencheremo le sue caratteristiche chiave e alla fine impareremo come utilizzare alcune delle sue funzionalità più avanzate.

Prima di tutto, spieghiamo brevemente cosa intende ottenere il framework.

2. Smooks

Smooks è un framework per applicazioni di elaborazione dati, che si occupa di dati strutturati come XML o CSV.

Fornisce sia API che un modello di configurazione che ci consentono di definire trasformazioni tra formati predefiniti (ad esempio da XML a CSV, da XML a JSON e altro).

Possiamo anche utilizzare una serie di strumenti per impostare la nostra mappatura, inclusi gli script FreeMarker o Groovy.

Oltre alle trasformazioni, Smooks offre anche altre funzionalità come la convalida dei messaggi o la suddivisione dei dati.

2.1. Caratteristiche principali

Diamo un'occhiata ai principali casi d'uso di Smooks:

  • Conversione dei messaggi: trasformazione dei dati da vari formati sorgente a vari formati di output
  • Arricchimento del messaggio: compilazione del messaggio con dati aggiuntivi, che provengono da un'origine dati esterna come il database
  • Divisione dei dati: elaborazione di file di grandi dimensioni (GB) e suddivisione in file più piccoli
  • Associazione Java: creazione e popolamento di oggetti Java dai messaggi
  • Convalida dei messaggi: esecuzione di convalide come regex o persino creazione di regole di convalida personalizzate

3. Configurazione iniziale

Cominciamo con la dipendenza Maven che dobbiamo aggiungere al nostro pom.xml :

 org.milyn milyn-smooks-all 1.7.0 

L'ultima versione può essere trovata su Maven Central.

4. Associazione Java

Iniziamo ora concentrandoci sull'associazione dei messaggi alle classi Java. Faremo una semplice conversione da XML a Java qui.

4.1. Concetti basilari

Inizieremo con un semplice esempio. Considera il seguente XML:

 771 IN_PROGRESS 

Per portare a termine questo compito con Smooks, dobbiamo fare due cose: preparare i POJO e la configurazione di Smooks.

Vediamo come appare il nostro modello:

public class Order { private Date creationDate; private Long number; private Status status; // ... } 
public enum Status { NEW, IN_PROGRESS, FINISHED }

Ora passiamo alle mappature di Smooks.

Fondamentalmente, le mappature sono un file XML che contiene la logica di trasformazione. In questo articolo, useremo tre diversi tipi di regole:

  • bean - definisce la mappatura di una sezione strutturata concreta alla classe Java
  • valore - definisce la mappatura per la particolare proprietà del bean. Può contenere una logica più avanzata come i decoder, che vengono utilizzati per mappare i valori su alcuni tipi di dati (come la data o il formato decimale)
  • w iring: ci consente di collegare un bean ad altri bean (ad esempio, il bean del fornitore verrà collegato al bean Order )

Diamo un'occhiata alle mappature che useremo nel nostro caso qui:

      yyyy-MM-dd   

Ora, con la configurazione pronta, proviamo a verificare se il nostro POJO è costruito correttamente.

Innanzitutto, dobbiamo costruire un oggetto Smooks e passare l'XML di input come flusso:

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

Infine, asserisci se la configurazione è stata eseguita correttamente:

@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. Associazione avanzata - Riferimento ad altri bean ed elenchi

Estendiamo il nostro esempio precedente con i tag del fornitore e degli articoli dell'ordine :

 771 IN_PROGRESS  Company X 1234567    1 PX1234 9.99   1 RX990 120.32   

E ora aggiorniamo il nostro modello:

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; // ... }

È inoltre necessario estendere la mappatura della configurazione con le definizioni del fornitore e del bean articolo .

Si noti che abbiamo anche definito separati elementi di fagioli, che conterrà tutti gli articoli elementi di ArrayList .

Infine, useremo l' attributo di cablaggio di Smooks , per raggruppare tutto insieme.

Dai un'occhiata a come appariranno le mappature in questo caso:

      yyyy-MM-dd                 

Infine, aggiungeremo alcune affermazioni al nostro test precedente:

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. Validazione dei messaggi

Smooks viene fornito con un meccanismo di convalida basato su regole. Diamo un'occhiata a come vengono utilizzati.

La definizione delle regole è memorizzata nel file di configurazione, annidato nel tag ruleBase , che può contenere molti elementi ruleBase .

Ogni elemento ruleBase deve avere le seguenti proprietà:

  • name – unique name, used just for reference
  • src – path to the rule source file
  • provider – fully qualified class name, which implements RuleProvider interface

Smooks comes with two providers out of the box: RegexProvider and MVELProvider.

The first one is used to validate individual fields in regex-like style.

The second one is used to perform more complicated validation in the global scope of the document. Let's see them in action.

5.1. RegexProvider

Let's use RegexProvider to validate two things: the format of the customer name, and phone number. RegexProvider as a source requires a Java properties file, which should contain regex validation in key-value fashion.

In order to meet our requirements, we'll use the following setup:

supplierName=[A-Za-z0-9]* supplierPhone=^[0-9\\-\\+]{9,15}$

5.2. MVELProvider

We'll use MVELProvider to validate if the total price for each order-item is less then 200. As a source, we'll prepare a CSV file with two columns: rule name and MVEL expression.

In order to check if the price is correct, we need the following entry:

"max_total","orderItem.quantity * orderItem.price < 200.00"

5.3. Validation Configuration

Once we've prepared the source files for ruleBases, we'll move on to implementing concrete validations.

A validation is another tag in Smooks configuration, which contains the following attributes:

  • executeOn – path to the validated element
  • name – reference to the ruleBase
  • onFail – specifies what action will be taken when validation fails

Let's apply validation rules to our Smooks configuration file and check how it looks like (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):

Now, with the configuration ready, let's try to test if validation will fail on supplier's phone number.

Again, we have to construct Smooks object and pass input XML as a stream:

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

And finally assert, if validation error occurred:

@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. Message Conversion

The next thing we want to do is convert the message from one format to another.

In Smooks, this technique is also called templating and it supports:

  • FreeMarker (preferred option)
  • XSL
  • String template

In our example, we'll use the FreeMarker engine to convert XML message to something very similar to EDIFACT, and even prepare a template for the email message based on XML order.

Let's see how to prepare a template for EDIFACT:

UNA:+.? ' UNH+${order.number}+${order.status}+${order.creationDate?date}' CTA+${supplier.name}+${supplier.phoneNumber}'  LIN+${item.quantity}+${item.code}+${item.price}' 

And for the email message:

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:  ${item.quantity} X ${item.code} (total price ${item.price * item.quantity}) 

The Smooks configuration is very basic this time (just remember to import the previous configuration in order to import Java binding settings):

    /path/to/template.ftl  

Questa volta dobbiamo solo passare un StringResult al motore di Smooks:

Smooks smooks = new Smooks(config); StringResult stringResult = new StringResult(); smooks.filterSource(new StreamSource(OrderConverter.class .getResourceAsStream(path)), stringResult); return stringResult.toString();

E possiamo, ovviamente, testarlo:

@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. Conclusione

In questo tutorial, ci siamo concentrati su come convertire i messaggi in diversi formati o trasformarli in oggetti Java usando Smooks. Abbiamo anche visto come eseguire le convalide in base alle regole regex o di logica aziendale.

Come sempre, tutto il codice utilizzato qui può essere trovato su GitHub.