Integrazione di BeanIO con Spring Batch

Un interessante utilizzo del framework BeanIO, che abbiamo introdotto nella serie di articoli ad esso dedicati (Primi Passi con BeanIO parte 1, parte 2 e parte 3), è la sua integrazione con Spring Batch. Come descritto in Primi Passi con Spring Batch una tipica applicazione Spring Batch si compone di un job che combina step eseguiti sequenzialmente. Ogni step consiste di tre attività: data reading, processing e data writing. tali attività sono codificate attraverso specifiche interfacce, rispettivamente denominate ItemReaderItemProcessor e ItemWriter.

Interfacce BeanIO

BeanIO implementa le interfacce ItemReader e ItemWriter, rispettivamente per scrivere e leggere flat file codificati secondo quanto specificato nel file di mapping, attraverso le classi BeanIOFlatFileItemReader<T>BeanIOFlatFileItemWriter<T>. Tali classi sono utilizzabili nella definizione dei job di Spring Batch semplicemente definendo e configurando opportunamente il bean relativo. In particolare le proprietà minime necessarie a configurare le due tipologie di classi sono:

  • il file di mapping utilizzato per configurare SpringIO e che definisce il modo in cui i bean sono scritti o letti su file (proprietà streamMapping);
  • il nome dello stream definito nel file di mapping da utilizzare per la serializzazione o deserializzazione (proprietà streamName);
  • il nome (e path) del file di input da deserializzare o di output in cui serializzare gli oggetti (proprietà resource).

Il Progetto di Esempio

Per comprendere il funzionamento di tale integrazione consideriamo un semplice esempio in cui un job è utilizzato per leggere i dati, su clienti ed ordini, da un file di input in formato CSV e di produrre un report come output su un secondo file CSV.

Dependency

Iniziamo, quindi, creando un progetto maven ed inserendo le dipendenze necessarie nel file pom.xml:

 Si noti che è importante utilizzare la versione 2.1.x di Spring Batch affiché l’integrazione possa funzionare correttamente con l’ultima versione di BeanIO, ovvero la 2.1.0.M1. 

File di Input

Come detto il file di input  customer-orders.csv  contiene gli ordini dei clienti e si presenta nel seguente modo:

Ogni linea inizia con un identificatore del record (Customer, Order, eof) ed i diversi record si presentano secondo un ordine prestabilito in cui ogni cliente è seguito dagli ordini da lui effettuati. Si noti che per mantenere l’esempio semplice si è stabilito di utilizzare un record di fine file denominato eof (End Of File).

Utilizziamo parte del codice già visto negli articoli precedenti dedicati al framework e definiamo le classi Customer e Order di cui abbiamo bisogno per leggere il file:

Per la configurazione di BenaIO definiamo il file di mapping  order-mapping.xml  in cui è definito lo stream denominato  customerOrderFile , ed in cui la corretta sequenzializzazione dei record è garantita utilizzando un tag <group>. Il file si presenta quindi nel seguente modo:

Si noti l’uso di un bean EndOfFile senza proprietà, ma col solo recordType utile al suo riconoscimento nel file.

Unendo tutte le definizioni fatte fino ad ora possiamo infine configurare il bean che implementa la classe BeanIOFlatFileItemReader, e che sarà utilizzato nel job di Spring Batch:

Processamento

Il report che vogliamo produrre nell’esempio è un semplice riepilogo del numero di ordini effettuati da ciascun cliente e del valore monetario complessivo di tali ordini.  A tale scopo definiamo la classe Report così definita:

Per il processamento dei record è necessario implementare l’interfaccia ItemProcessor di Spring Batch. Realizziamo quindi la classe OrderProcessor codificata nel seguente modo:

Ogni record letto da Spring è processato dal metodo process il cui comportamento è differente a seconda del tipo di record. Innanzitutto si noti che il bean è un singleton e si utilizza tale caratteristica per mantenere lo stato dell’ultimo report nella variabile report. Soluzione non elegantissima ma funzionale allo scopo della demo.

A seguito della lettura di un record di tipo customer viene creato un nuovo oggetto Report ed il precedente oggetto, se presente, è restituito in output. Si noti che restituire un oggetto NULL non ha alcun effetto sulla scrittura del file di output. Se il record è di tipo order viene semplicemente aggiornato il report corrente incrementando il numero di ordini ed aggiornando il totale. Infine se il record è di tipo eof (caso else senza condizioni) si restituisce l’ultimo report in quanto il file è terminato.

File di Output

Per la scrittura del file di output utilizziamo la classe Report ed il file  report-mapping.xml  per configurare il mapping su BeanIO, in cui definiamo lo stream reportFile :

Configuriamo infine il bean che implementa la classe BeanIOFlatFileItemWriter, e che sarà utilizzato nel job di Spring Batch, nel seguente modo:

L’aspetto del file  report.csv  prodotto sarà quindi del tipo:

Spring Batch

Per configurare Spring Batch definiamo due xml, uno di contesto (context.xml) ed uno per il job (beanio-job.xml). Per un ulteriore dettaglio sul primo si rimanda all’articolo Primi Passi con Spring Batch. Il secondo, invece, oltre ad ospitare i bean beanioReaderbeanioWriter e processordefiniti nei paragrafi precedenti, definisce anche il job di Spring:

Per completezza riportiamo anche la classe Main di avvio del batch:

Codie Sorgente

Il codice sorgente del progetto è disponibile qui spring-batch-bean-io.