Spring Boot, Apache Camel e ActiveMQ: una route “from JMS to File” per spostare un file partendo da un messaggio con il suo path – Parte 3

Al termine della seconda parte dell’articolo siamo arrivati ad avere una route Camel configurata e attiva, composta da uno step di ricezione del messaggio e da uno step di log a console dello stesso. In questa terza parte continueremo lo sviluppo e introdurremo lo step (Processor) che ci permette di trasformare il body del messaggio da una stringa con il fullpath del file al contenuto del file stesso.

Per fare questo dobbiamo introdurre alcuni concetti e oggetti di Apache Camel:

  • Il meccanismo di Bean Binding: consiste nel definire uno step custom di una route Camel costituito da una classe che espone un metodo handler che processa opportunamente il messaggio in ingresso proveniente dallo step precedente della route
  • L’oggetto Exchange: è l’oggetto che contiene i messaggi e le proprietà di input e di output di ogni step di processamento di una route.

Ogni step di una route ha infatti un messaggio in ingresso e uno in uscita con il risultato della trasformazione da esso effettuata. Tali messaggi possono essere recuperati tramite i due metodi getIn() e getOut() che ritornano appunto entrambi un Message (Documentazione)
CamelJMStoFile 16_0 - Camel Processor Exchange In Out Messages
In realtà il messaggio Out esiste solo se viene creato esplicitamente, altrimenti viene utilizzato lo stesso messaggio IN come output da un processor e come input dello step successivo. E’ in generale consigliato utilizzare il messaggio IN in quanto il messaggio di OUT viene creato come nuovo messaggio e non ha quindi tutte le proprietà base del messaggio IN originale.

Ma procediamo per gradi.

Iniziamo con creare nel nostro progetto un nuovo package che chiamiamo “binders”. Al suo interno creiamo una nuova classe che chiamiamo “PathToFileBinder”, in quanto essa si occuperà di effettuare la trasformazione del messaggio da path a file. Annotiamo la classe con @Component e definiamo al suo interno un metodo “fromPathToFile” che accetta come parametro un oggetto Exchange e ha come tipo di ritorno “void”. Questo metodo lo arricchiamo con l’annotazione @Handler fornita da Camel.

Il codice sarà quindi:

package net.davismol.cameljmsexample.binders;

import org.apache.camel.Exchange;
import org.apache.camel.Handler;
import org.springframework.stereotype.Component;

@Component
public class PathToFileBinder {

	@Handler
	public void fromPathToFile(Exchange exchange)
	{

		// TO BE IMPLEMENTED
	}
}

CamelJMStoFile 17 - SpringBoot Camel Route Bean Binding class

Per inserire in una route Camel uno step di processamento custom, tramite il meccanismo di Bean Binding occorre utilizzate il metodo .bean(Class beanType) delle fluent API, passando quindi come parametro la classe che contiene il metodo che si occuperà di effettuare la trasformazione del messaggio.

Nel nostro caso quindi, dovremo aggiungere alla nostra route lo step seguente:

.bean(PathToFileBinder.class)

La route definita nel metodo configure della nostra classe JMSToFolderRoute diventa quindi:

package net.davismol.cameljmsexample.routes;

import org.apache.camel.builder.RouteBuilder;
import org.springframework.stereotype.Component;

import net.davismol.cameljmsexample.binders.PathToFileBinder;

@Component
public class JMSToFolderRoute extends RouteBuilder {

	@Override
	public void configure() throws Exception {

		from("jms:queue:camel-example")
		.log("TEXT: " + body())
		.bean(PathToFileBinder.class);
	}
}

CamelJMStoFile 18 - SpringBoot Camel Route Bean Class step
Dopo aver aggiunto lo step bean alla nostra route, torniamo alla classe PathToFileBinder ed inseriamo una riga di log che stampa in output un messaggio che ci permetta di verificare che il metodo handler venga correttamente invocato. Per farlo, aggiungiamo semplicemente un riferimento ad un oggetto Logger di slf4j, senza dover aggiungere nessuna dipendenza aggiuntiva al nostro progetto, in quanto Spring Boot ci fornisce out-of-the-box già tutto il necessario in termini di sistema di logging (Logback) e configurazione di default.

Modifichiamo quindi semplicemente la classe PathToFileBinder come segue:

import org.apache.camel.Exchange;
import org.apache.camel.Handler;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;

@Component
public class PathToFileBinder {
	
	public static final Logger Logger = LoggerFactory.getLogger(PathToFileBinder.class);
	
	@Handler
	public void fromPathToFile(Exchange exchange)
	{
		Logger.info("PathToFileBinder bean, @Handler annotated method...");
	}
}

CamelJMStoFile 19 - SpringBoot Camel Bean binding handler method
A questo punto proviamo ad eseguire l’applicazione e ad inviare un nuovo messaggio sulla coda “camel-example” dalla console di amministrazione di ActiveMQ.
Come possiamo vedere dall’immagine seguente, il messaggio viene ricevuto, loggato dalla route tramite l’apposito step e poi sottoposto correttamente al processamento del bean tramite l’esecuzione del metodo fromPathToFile annotato con @Handler.

CamelJMStoFile 20 - SpringBoot Camel Bean step logs
Ora che abbiamo visto che lo step bean della route viene invocato correttamente, possiamo passare alla definizione della logica del metodo handler che, come detto, deve effettuare una trasformazione del body del messaggio da una stringa contenente il path del file ad un oggetto File che rappresenta il file vero e proprio.

Per prima cosa dobbiamo recuperare dall’oggetto Exchange il messaggio di input, il cui body al momento contiene la stringa testuale con il fullpath del file che vogliamo spostare.
Per farlo utilizziamo il metodo getIn();

Message in = exchange.getIn();
String fileFullpath = exchange.getIn().getBody().toString();

Ora che abbiamo il fullpath dobbiamo creare l’oggetto File vero e proprio e dobbiamo settarlo come body del messaggio, al posto della stringa con il path. Inoltre, dobbiamo estrapolare il nome del file dal suo fullpath, in modo da poterlo utilizzare nello step della route che effettua lo spostamento del file per mantenere lo stesso nome del file originale.

String fileFullpath = exchange.getIn().getBody().toString();
File f = new File(fileFullpath);
String fileName = f.getName();

Come abbiamo visto ad inizio articolo abbiamo due possibilità per settare l’oggetto File come body e cioè utilizzare:

  • Il Message di Input dell’oggetto Exchange
  • Il Message di Output dell’oggetto Exchange

Nel nostro caso, siccome utilizziamo soltanto il body del messaggio e un header personalizzato e non utilizziamo invece proprietà base del Message di input è indifferente utilizzare il messaggio In o Out per settare il risultato dell’elaborazione del nostro bean.
Optiamo comunque per l’utilizzo del messaggio In, di cui modifichiamo quindi il contenuto tramite il metodo setBody(), senza creare un Message di Out.

in.setBody(f);
in.setHeader("filename", fileName);

CamelJMStoFile 210 - SpringBoot Camel set body and header in Bean

Facciamo un’ultima modifica prima di fare un test su quanto abbiamo aggiunto all’implementazione del metodo handler del nostro bean. Torniamo nella definizione della nostra route “JMSToFolderRoute” e aggiungiamo un ulteriore step di log che ci stampa a video il valore dell’header “filename” ricevuto come output del processamento eseguito dal nostro bean.
Per referenziare un header all’interno dello step della route dobbiamo utilizzare la sintassi:

${in.header.filename}

dove con “in.header” indichiamo che vogliamo accedere ad un header del Message di input, mentre “filename” è il nome dell’header di cui vogliamo utilizzare il valore. Valore che avevamo settato nel nostro metodo handler.

La route diventa quindi:

@Component
public class JMSToFolderRoute extends RouteBuilder {

	@Override
	public void configure() throws Exception {

		from("jms:queue:camel-example")
		.log("TEXT: " + body())
		.bean(PathToFileBinder.class)
		.log("HEADER filename: ${in.header.filename}");
	}
}

A questo punto possiamo iniziare a preparare l’ambiente per testare l’applicazione e la nostra route Camel.
Creiamo la struttura delle directory dove depositiamo il nostro file di test. Creiamo ad esempio una cartella “input” e una “output” allo stesso path “C:\CAMEL-EXAMPLE”.

CamelJMStoFile 21 - Apache Camel and ActiveMQ file directories tree
All’interno della cartella “input” depositiamo un file di test, ad esempio un file PDF.

CamelJMStoFile 21b - Apache Camel and ActiveMQ input file path dir
Il path assoluto del file e, di conseguenza, la stringa di testo che invieremo come messaggio sulla coda ActiveMQ sarà quindi:

C:\CAMEL-EXAMPLE\input\File to move.pdf

Lanciamo nuovamente l’applicazione Spring Boot, torniamo poi nella console di amministrazione di ActiveMQ e inviamo il messaggio, sempre sulla solita coda “camel-example” usata finora.

CamelJMStoFile 22 - SpringBoot ActiveMQ send fullpath as message
Nei log illustrati nell’immagine seguente possiamo vedere che l’esecuzione va a buon fine, il metodo handler del nostro bean viene eseguito e lo step successivo logga correttamente il valore dell’header “filename”, settato correttamente con il nome del file estrapolato dalla stringa del suo full path.

CamelJMStoFile 23 - SpringBoot Camel ActiveMQ log header after bean binding

Bene, ora non ci resta che aggiungere lo step che prende il file contenuto nel Body del Message e lo salva su file system nel folder che gli indichiamo.

This entry was posted in $1$s. Bookmark the permalink.

One thought on “Spring Boot, Apache Camel e ActiveMQ: una route “from JMS to File” per spostare un file partendo da un messaggio con il suo path – Parte 3

  1. Pingback: Spring Boot, Apache Camel e ActiveMQ: una route “from JSM to File” per spostare un file partendo da un messaggio con il suo path – Parte 2 | Dede Blog

Leave a Reply

Your email address will not be published. Required fields are marked *