Il termine Enterprise Application Integration (EAI) si riferisca da un software o ad una architettura software in grado di integrare applicazioni enterprise eterogenee. Sebbene si possa pensare che le applicazioni EAI possano eseguire le operazioni più disparate, in realtà esistono in tale ambito problemi ricorrenti a cui sono applicabili soluzioni comuni. Questo implica che esistono dei design pattern applicabili nell’EAI che sono stati ben raccolti e documentati nel libro Enterprise Integration Patterns (EIP) di Gregor Hohpe and Bobby Woolf.
Spring Integration (SI) è una architettura di messaggistica, realizzata al di sopra di Spring Framework, che fornisce una implementazione java dei pattern EIP, mettendo a disposizione una serie di componenti che possono essere combinati per realizzare un’applicazione EAI. La configurazione dell’applicazione avviene attraverso un XML che ne definisce le componenti e la loro interazione.
Componenti Principali
I concetti principali di Spring Integration sono:
- i producer (sender) ed i consumer (receiver) detti endpoint che si scambiano messaggi;
- le pipe dette anche channel che rappresentano i canali di comunicazione tra producer e consumer;
- i messaggi che trasportano l’informazione scambiata.
Queste componenti possono essere combinate per realizzare interazioni complesse in cui il messaggio originale subisce diverse operazioni prima di giungere al destinatario finale..
Il messaggio si compone di un header e di un payload. L’header contiene informazioni di sistema attribuite da SI (es. messageId, timestamp, etc.), mentre il payload contiene in dato scambiato dalle applicazioni che può assumere diversi formati (es. XML, JSON, etc.).
Canali
Il canale è la componente SI attraverso la quale i messaggi transitano da un end-point al successivo. Possono essere classificati in due categorie principali: Pollable Channel e Subscribable Channel, che sono dichiarati rispettivamente nelle interfacce java PollableChannel
e SubscribableChannel
.
I Pollable Channel bufferizzano i messaggi in una coda ed il receiver attivamente richiede il messaggio successivo presente in coda. La coda ha una capacità massima dichiarata al momento della costruzione del canale o in caso contrario corrisponde alla costante Integer.MAX_VALUE
. Generalmente questa tipologia di canali sono utilizzati per la comunicazione punto-punto in cui c’è un solo producer ed un solo consumer.
ISubscribable Channel consentono di avere più subscriber/consumer registrati come consumatori dei messaggi. Il messaggio è ricevuto da tutti i consumer registrati. Non hanno un buffer per la conservazione di messaggi ma mantengono la lista aggiornata dei subscriber. Un esempio di applicazione che utilizza tale tipologia di canali è la gestione degli eventi.
Ovviamente esistono diverse implementazioni di tali tipologie di canali che sono descritte nella documentazione ufficiali al link Messaging Channels. In particolare menzioniamo il DirectChannel
ed il NullChannel
. Il primo implementa una semplice connessione point-to-point, mentre il secondo 8da usare solo per test) è un canale che non delivera mai il messaggio sebbene restituisca sempre TRUE
al sender e NULL
al receiver.
Un DirectChannel
si definisce semplicemente con l’XML:
1 2 3 4 5 6 7 8 |
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:int="http://www.springframework.org/schema/integration" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/integration http://www.springframework.org/schema/integration/spring-integration.xsd> <int:channel id="messageChannel"/> </beans> |
Altre implementazioni di canale fornite da SI sono: PublishSubscribeChannel, QueueChannel, PriorityChannel, RendezvousChannel, ExecutorChannel e ScopedChannel.
Message Endpoint
I message endpoint sono le componenti che isolano il codice applicativo dall’infrastruttura di comunicazione fornita da Spring Integration. Esistono divers tipologie di message endpoint:
- Adapter: per la connessione dei canali a sistemi esterni;
- Filter: per filtrare i messaggi nel canale;
- Transformer: per la conversione dei messaggi;
- Enricher: per aggiungere contenuto informativo ai messaggi;
- Service activator: per l’invocazione di servizi;
- Gateway: per agganciare sistemi di messaggistica esterni;
- Router: per la selezione del canale che il messaggio deve percorrere;
- Splitter: per suddividere il messaggio in più messaggi per essere inviati su canali diversi.
- Aggregator: aggrega più messaggi in un unico messaggio.
Di seguito vediamo alcuni esempi di message endpoint, rimandando l’analisi degli altri ad un successivo post.
Channel Adapter
E’ utilizzato per connettere il canale a sistemi esterni (bridge) di fatto disaccoppiando il messaggio dal trasporto e protocollo utilizzato. Possono essere di tipo inbound, ovvero “portano” il messaggio dentro il canale, o outbound, ovvero lo portano fuori. Spring Integration fornisce diversi adapter configurabili: Stream, File, JMS, JDBC & JPA, FTP e SFTP, Feed (RSS, Atom, etc), Mail, MongoDB, UDP, Twitter, etc. Ovviamente è possibile implementarne di custom.
Il primo e più semplice channel adapter è l’inbound/outbound channel adapter che esegue un qualsiasi metodo di un bean e lo invia in un canale dopo averlo convertito in un messaggio. Il seguente esempio utilizza due classi Producer
e Consumer
che vengono utilizzate da due channel adapter connessi mer mezzo di un direct channel:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:int="http://www.springframework.org/schema/integration" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:int-stream="http://www.springframework.org/schema/integration/stream" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/integration http://www.springframework.org/schema/integration/spring-integration.xsd"> <int:inbound-channel-adapter ref="producer" method="produce" channel="messageChannel" id="inboundAdapter" > <int:poller fixed-rate="1000"/> </int:inbound-channel-adapter> <int:outbound-channel-adapter ref="consumer" method="consume" channel="messageChannel" id="outboundAdapter"/> <int:channel id="messageChannel"/> <bean id="producer" class="it.javaboss.Producer"/> <bean id="consumer" class="it.javaboss.Consumer"/> </beans> |
<int:poller fixed-rate="1000"
/>
indica che il canali di input deve essere interrogato ogni secondo.
Spring Integration supporta anche una descrizione visuale del sistema prodotto, conforme alle icone introdotte nel libro Enterprise Integration Patterns.
Un altro semplice adapter è lo standard-in/out/error che consente di leggere o scrivere il messaggio nello standard in/out/error. Per questi adapter esiste uno specifico namespace che che introduce tre nuovi tag: stdin-channel-adapter, stdout-channel-adapter e stderr-channel-adapter con utilizzo intuitivo. Il seguente XML definisce un sistema in cui l’input da tastiera viene letto e scritto su consolle:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:int="http://www.springframework.org/schema/integration" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:int-stream="http://www.springframework.org/schema/integration/stream" xsi:schemaLocation=" http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/integration http://www.springframework.org/schema/integration/spring-integration.xsd http://www.springframework.org/schema/integration/stream http://www.springframework.org/schema/integration/stream/spring-integration-stream.xsd"> <!-- message producer / a Spring Integration wrapped Java Standard input stream --> <int-stream:stdin-channel-adapter id="producer" channel="messageChannel"> <int:poller fixed-rate="200"/> </int-stream:stdin-channel-adapter> <!-- message consumers / a Spring Integration wrapped Java Standard output streams --> <int-stream:stdout-channel-adapter id="consumer" channel="messageChannel" append-newline="true" /> <!-- a direct channel without the queue, a pollable channel with the queue --> <int:channel id="messageChannel"/> </beans> |
Un’altra interessante coppia di adapter è quella che consente di leggere e scrivere messaggi su filesystem. Anche in questo caso SI introduce un ulteriore namespace che “ridefinisce” i tag inbound-channel-adapter e outbound-channel-adapter. Il seguente XML configura un sistema in cui i file nella cartella inbound
sono letti e copiati nella cartella outbound
.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 |
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:int="http://www.springframework.org/schema/integration" xmlns:int-mail="http://www.springframework.org/schema/integration/mail" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:int-stream="http://www.springframework.org/schema/integration/stream" xmlns:int-file="http://www.springframework.org/schema/integration/file" xsi:schemaLocation=" http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/integration http://www.springframework.org/schema/integration/spring-integration.xsd http://www.springframework.org/schema/integration/stream http://www.springframework.org/schema/integration/stream/spring-integration-stream.xsd http://www.springframework.org/schema/integration/file http://www.springframework.org/schema/integration/file/spring-integration-file.xsd"> <int-file:inbound-channel-adapter id="producer-file-adapter" channel="messageChannel" directory="file:inbound" prevent-duplicates="true"> <int:poller fixed-rate="5000" /> </int-file:inbound-channel-adapter> <int-file:outbound-channel-adapter channel="messageChannel" id="consumer-file-adapter" directory="file:outbound" /> <int:poller id="defaultPoller" default="true" max-messages-per-poll="5" fixed-rate="200" /> <!-- a direct channel --> <int:channel id="messageChannel" /> </beans> |
Esecuzione
Prima di procedere con gli altri componenti di Spring Integration, vediamo la semplice classe che attiva il sistema, che banalmente “tira su” il contesto di Spring.
1 2 3 4 5 6 7 8 |
public class Startup { public static void main(String[] args) { ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext( "/META-INF/spring/si-components-adapter.xml"); //"/META-INF/spring/si-components-stdinout-adapter.xml"); //"/META-INF/spring/si-components-file-adapter.xml"); } } |
Codice Sorgente
Il codice sorgente completo degli esempi visti è scaricabile qui javaboss-si-parte1.