In questo e nei successivi due articoli, mostreremo come implementare alcuni web service, come utilizzare meccanismi di comunicazione sincroni e asincroni per connettere fra di loro le varie componenti e come implementare pattern di integrazione utilizzando FUSE (open.iona.com), soluzione Open Source basata su alcuni dei più diffusi progetti Apache e supportata da IONA Technologies (www.iona.com).
Integrazione e FUSE
La progettazione e l’ingegnerizzazione di soluzioni di integrazione in ambienti eterogenei sono ancora fra i processi più complessi nell’ambito IT. Gli elementi da valutare nella scelta della soluzione migliore sono molteplici e spesso la rapida evoluzione dei requisiti di progetto rende necessaria la ridefinizione dell’architettura. Inoltre, molte delle soluzioni tecnologiche proposte dai vendor determinano scelte architetturali obbligate. La disponibilità di un set di soluzioni componibili in maniera diversa in base alla scelta della migliore architettura da implementare, come quello presente nel bundle Open Source FUSE (open.iona.com), semplifica la realizzazione di scenari di integrazione utilizzando tecnologia basata su open standard e facilita l’adozione di pattern di integrazione basati su meccanismi di comunicazione asincroni.
Il processo che modelleremo è quello di un utente che richiede la quotazione per un prestito a diverse banche attraverso un agente di prestito che è in grado di interagire con un agenzia di credito per la raccolta delle informazioni necessarie alle banche per la quotazione del rateo di interesse.
L’architettura che definisce il processo di business è illustrata in figura 1 e nel corso di questi tre articoli implementeremo i diversi elementi che la compongono.
Il flusso dei messaggi che definiscono il processo è il seguente:
- L’agente di Prestito (AdP) riceve la richiesta di quotazione del rateo da parte dell’Utente (U);
- AdP riceve dall’Agenzia di Credito (AdC) il punteggio associato a U;
- AdP invia una richiesta ad ogni Banca (B);
- AdP raccoglie le risposte da tutte le B;
- AdP stabilisce qual è la migliore quotazione;
- AdP fornisce il risultato a U.
Nel corso di questo primo articolo mostreremo come implementare i servizi che rappresentano le banche e l’agenzia di credito esponendo interfacce sincrone in SOAP/HTTP e asincrone utilizzando code JMS.
Per facilitare la possibilità di sperimentare sulla propria macchina gli esempi proposti, è stato reso disponibile il file articolo_1.zip all’URL www.iona.com/FUSE_EIP/articolo_1.zip, contenente i sorgenti Java e il file POM (Project Object Model) da utilizzare con Maven. Maven è un progetto Open Source (maven.apache.org) che facilita la compilazione e la gestione di progetti Java che è disponibile all’URL http://maven.apache.org/download.html.
Dopo aver eseguito l’installazione di Maven, è utile preparare uno script per inizializzare le variabili d’ambiente. Per gli utenti Windows le seguenti istruzioni del file maven_env.bat contenuto nel file articolo_1.zip dovranno essere modificate in base alle directory di installazione del JDK e di Maven:
set JAVA_HOME= set M2_HOME=
Per gli utenti Linux le seguenti istruzioni del file maven_env.sh contenuto nel file articolo_1.zip dovranno essere modificate in base alle directory di installazione del JDK e di Maven:
export JAVA_HOME= export MAVEN_HOME= export M2_HOME=
Per modificare i permessi di esecuzione del file dovrà essere eseguita la seguente istruzione:
chmod u+x maven_env.sh
e per inizializzare le variabili d’ambiente dovrà essere eseguita la seguente istruzione:
. ./maven_env.sh
Nel corso dell’articolo si farà riferimento al solo script relativo al sistema operativo Windows
Implementazione dei service provider delle banche in SOAP/HTTP
Per prima cosa definiamo l’interfaccia che i service provider delle banche esporranno. La prima operazione è getBankName e serve ad identificare quale banca è stata invocata; la seconda operazione è getQuote e serve a richiedere il rateo proposto dalla banca invocata in base ai seguenti parametri: Social Security Number dell’utente (String ssn), quantità del prestito richiesto (double loanAmount), durata del prestito (int loanDuration) e due valori generati dall’agenzia di credito e relativi all’utente che richiede il prestito (int creditHistory e int creditScore). Pertanto, l’interfaccia sarà:
import javax.jws.WebService; @WebService public interface BankWS { String getBankName(); BankQuote getQuote(String ssn, double loanAmount, int loanDuration, int creditHistory, int creditScore); }
L’operazione getBankName restituisce il nome della banca utilizzato durante la generazione dell’istanza della classe Bank, mentre getQuote restituisce un’istanza della classe BankQuote contenente il nome della banca, il Social Security Number dell’utente e il rateo richiesto.
public class BankQuote { private String bankName; private String ssn; private Double rate; public BankQuote(String name, String s, Double r) { bankName = name; ssn = s; rate = r; } }
La generazione del rateo richiesto avviene con la formula espressa di seguito, sulla base del valore di primeRate utilizzato per la costruzione della specifica istanza della classe Bank:
Double rate = primeRate + (double)(loanDuration / 12) / 10 + (double)(Math.random() * 10) / 10;
Per l’esecuzione del codice e l’esposizione dei servizi è stato utilizzato Spring (www.springframework.org), framework Open Source per creare e gestire applicazioni basate sulla Java Platform.
Il main dei service provider delle banche chiama il costruttore della classe MainWS mostrata di seguito:
public MainWS() { AbstractApplicationContext ctx = new ClassPathXmlApplicationContext( new String[] { "bank-server-ws-spring.xml" }); }
Il file bank-server-ws-spring.xml contiene i costruttori dei tre service provider delle banche, ad esempio:
che crea un’istanza della classe Bank utilizzando i valori “HTTP Bank 1” e “3.5”.
Per questo primo articolo è stato implementato un semplice Client WS che, per ognuno dei tre service provider delle banche, esegue il seguente codice:
BankWS bankClient = client.getProxy(ADDRESS); long startTime = System.currentTimeMillis(); String bankName = bankClient.getBankName(); BankQuote bankQuote = bankClient.getQuote("Sequential SSN", 1000.54, 10, 1, 2); long endTime = System.currentTimeMillis(); System.out.println("It takes " + (endTime - startTime) + " milliseconds to call the Bank:" + bankName); System.out.println("The result:" + bankQuote);
Per lanciare i service provider delle banche è sufficiente posizionarsi sulla directory articolo1, aprire la shell con il file batch maven_env.bat ed eseguire il comando:
mvn exec:java -Dexec.mainClass=com.iona.fuse.mokabyte.articolo1.bank.MainWS
contenuto nel file README.txt.
È comunque sempre consigliabile, prima di lanciare i servizi, ricompilare tutto con il comando:
mvn compile
Dopo aver lanciato i service provider delle banche, è possibile visualizzare i WSDL esposti lanciando dal proprio browser le seguenti URL:
http://localhost:9001/bank1?wsdl http://localhost:9002/bank2?wsdl http://localhost:9003/bank3?wsdl
Per lanciare il client dei service provider delle banche è sufficiente posizionarsi sulla directory articolo1, aprire la shell con il file batch maven_env.bat ed eseguire il comando:
mvn exec:java -Dexec.mainClass=com.iona.fuse.mokabyte.articolo1.bank.test.BankClientWS
Il risultato dell’invocazione sulla finestra del client conterrà i ratei forniti dai service provider delle banche e sarà del tipo:
It takes 1687 milliseconds to call the Bank:HTTP Bank 1 The result:[ ssn:Sequential SSN bank:HTTP Bank 1 rate:3.7657910149263123 ] It takes 1219 milliseconds to call the Bank:HTTP Bank 2 The result:[ ssn:Sequential SSN bank:HTTP Bank 2 rate:4.166622943294823 ] It takes 1047 milliseconds to call the Bank:HTTP Bank 3 The result:[ ssn:Sequential SSN bank:HTTP Bank 3 rate:4.247074899761304 ]
Implementazione del service provider dell’agenzia di credito in SOAP/HTTP
Passiamo alla definizione dell’interfaccia che esporrà l’agenzia di credito. Le operazioni esposte dal service provider dell’agenzia di credito sono getCreditScore e getCreditHistoryLength, che in base al Social Security Number dell’utente (String ssn) definiscono i valori creditScore e creditHistory da utilizzare nell’invocazione dell’operazione getQuote esposta dai service provider delle banche. Pertanto, l’interfaccia sarà:
import javax.jws.WebService; @WebService public interface CreditAgencyWS { int getCreditScore(String ssn); int getCreditHistoryLength(String ssn); }
Per la generazione dei valori restituiti da getCreditScore e getCreditHistoryLength, si utilizzeranno le seguenti funzioni matematiche:
public int getCreditScore(String ssn) { int creditHistoryLength = (int)(Math.random() * 19 + 1); return creditHistoryLength; }
public int getCreditHistoryLength(String ssn) { int creditScore = (int)(Math.random() * 600 + 300); return creditScore; }
Anche in questo caso utilizzeremo Spring per l’esecuzione del codice e l’esposizione dei servizi.
Il main del service provider dell’agenzia di credito chiama il costruttore della classe MainWS mostrata di seguito:
public MainWS() { AbstractApplicationContext ctx = new ClassPathXmlApplicationContext( new String[] { "credit-agency-server-ws-spring.xml" });
Il file credit-agency-server-ws-spring.xml contiene i costruttori del service provider dell’agenzia di credito:
che crea un’istanza della classe CreditAgency.
Anche in questo caso è stato implementato un semplice client WS che esegue il seguente codice:
CreditAgencyClientWS client = new CreditAgencyClientWS(); CreditAgencyWS proxy = client.getProxy(); System.out.println(" === CreditAgencyWS === "); System.out.println("get credit history length" + proxy.getCreditHistoryLength("ssn")); System.out.println("get credit score" + proxy.getCreditScore("ssn")); System.out.println(" === Done === ");
Per lanciare il service provider dell’agenzia di credito è sufficiente posizionarsi sulla directory articolo1, aprire la shell con il file batch maven_env.bat ed eseguire il comando:
mvn exec:java -Dexec.mainClass=com.iona.fuse.mokabyte.articolo1.credit.MainWS
contenuto nel file README.txt.
Dopo aver lanciato il service provider dell’agenzia di credito, è possibile visualizzare il WSDL esposto lanciando dal proprio browser il seguente URL:
http://localhost:9006/creditAgency?wsdl
Per lanciare il client del service provider dell’agenzia di credito è sufficiente posizionarsi sulla directory articolo1, aprire la shell con il file batch maven_env.bat ed eseguire il comando:
mvn exec:java -Dexec.mainClass =com.iona.fuse.mokabyte.articolo1.credit.test.CreditAgencyClientWS
Il risultato dell’invocazione sulla finestra del client conterrà i valori di getCreditScore e getCreditHistoryLength forniti dal service provider dell’agenzia di credito e sarà del tipo:
get credit history length: 492 get credit score: 4
Meccanismo di comunicazione asincrono mediante code JMS
Il meccanismo di integrazione tra sistemi basato su messaggi abilita il paradigma “loosely coupled” tra le applicazioni mediante la comunicazione asincrona e affida al sistema di messaging la responsabilità del trasferimento dei dati da un’applicazione ad un’altra, lasciando focalizzare queste ultime su quali dati devono scambiarsi piuttosto che sulla loro modalità di condivisione.
Come broker JMS è stato utilizzato FUSE Message Broker, progetto Open Source supportato da IONA Technologies e basato su Apache ActiveMQ. Il file articolo_1.zip all’URL www.iona.com/FUSE_EIP/articolo_1.zip, contiene la configurazione necessaria per avviare il broker JMS, ma nel caso si ritenga interessante installare sulla propria macchina la versione completa del broker, comprendente anche la consolle di monitoraggio illustrata nel corso dell’articolo, è possibile scaricare il FUSE Message Broker all’URL http://open.iona.com/downloads, installarlo e avviarlo con il comando activemq.bat presente nella directory bin.
Per lanciare il broker JMS è sufficiente posizionarsi sulla directory articolo1, aprire la shell con il file batch maven_env.bat ed eseguire il comando:
mvn exec:java -Dexec.mainClass=com.iona.fuse.mokabyte.articolo1.EmbeddedBroker
contenuto nel file README.txt.
Dopo aver lanciato il broker JMS, la consolle all’URL
http://localhost:8161/admin
mostra la schermata illustrata in figura 2, dove è presente la coda test.cxf.jmstransport.queue.
Figura 2 – Consolle JMS
In questo primo articolo mostreremo come esporre i service provider su code JMS e l’intrinseca reliability offerta da questo meccanismo di comunicazione.
Esposizione dei service provider delle banche su code JMS
Il main dei service provider delle banche chiama il costruttore della classe MainJMS mostrata di seguito:
public MainJMS() { AbstractApplicationContext ctx = new ClassPathXmlApplicationContext( new String[] { "bank-server-jms-spring.xml" }); }
Il file bank-server-jms-spring.xml contiene il costruttore del service provider di una banca:
che crea un’istanza della classe Bank utilizzando i valori “HTTP Bank 1” e “3.5”.
Il client JMS esegue il seguente codice:
BankWS bankJMS = (BankWS) factory.create(); System.out.println("The BankName is:" + bankJMS.getBankName());
Per lanciare il service provider della banca dopo aver avviato il broker JMS è sufficiente posizionarsi sulla directory articolo1, aprire la shell con il file batch maven_env.bat ed eseguire il comando:
mvn exec:java -Dexec.mainClass=com.iona.fuse.mokabyte.articolo1.bank.MainJMS
contenuto nel file README.txt.
A questo punto la consolle JMS indicherà la presenza di un consumer per la coda test.cxf.jmstransport.queue, come illustrato in figura 3.
Figura 3 – Consolle JMS: è presente un consumer per la coda.
Per lanciare il client del service provider della banca è sufficiente posizionarsi sulla directory articolo1, aprire la shell con il file batch maven_env.bat ed eseguire il comando:
mvn exec:java -Dexec.mainClass=com.iona.fuse.mokabyte.articolo1.bank.test.BankClientJMS
La consolle JMS indicherà che è stato inviato e ricevuto un messaggio, come mostrato in figura 4.
Figura 4 – Consolle JMS: è stato inviato e ricevuto un messaggio.
Il risultato dell’invocazione sulla finestra del client conterrà il nome della banca invocata:
The BankName is:JMS Bank 1
Vediamo i vantaggi di un meccanismo di comunicazione reliable nel caso in cui il service provider della banca non sia in esecuzione quando il client invia la richiesta. Per fare questo arrestiamo il processo del service provider con il comando Ctrl+C e lanciamo il client.
La consolle JMS indicherà la presenza di un messaggio in attesa e l’avvenuto invio di due messaggi. Come illustrato in figura 5, il numero di messaggi ricevuti è ancora pari ad “1”, visto che in questo momento nessun consumer è attivo, e il numero di consumer è pari a “0”.
Figura 5 – Consolle JMS: nessun consumer è attivo.
Se ora avviamo nuovamente il service provider, questo automaticamente preleverà dalla coda JMS il messaggio precedentemente inviato, e il risultato sulla finestra del service provider sarà:
getBankName() has been invoked!!!!
La consolle JMS dopo l’avvio del service provider è mostrata in figura 6.
Figura 6 – Consolle JMS: il service provider è attivo.
All’interno del file articolo_1.zip all’URL www.iona.com/FUSE_EIP/articolo_1.zip è presente anche l’esempio di esposizione del service provider dell’agenzia di credito su coda JMS.
Conclusioni
In questo primo articolo abbiamo presentato il business case relativo all’integrazione di diversi sistemi e abbiamo implementato i service provider dei fornitori dei servizi. L’esposizione dei servizi è stata fatta sia in SOAP/HTTP che su code JMS, evidenziando i vantaggi derivanti dalla reliability offerta da quest’ultima soluzione. Nel prossimo articolo mostreremo come utilizzare i pattern di integrazione per implementare un workflow tra i vari servizi.
Riferimenti
[1] FUSE Services Framework
http://open.iona.com/products/enterprise-cxf
[2] FUSE Message Broker
http://open.iona.com/products/enterprise-activemq
[3] FUSE Mediation Router
http://open.iona.com/products/enterprise-camel
[4] FUSE ESB
http://open.iona.com/products/enterprise-servicemix-4-0-preview
[5] Gregor Hohpe – Bobby Woolf, “Enterprise Integration Patterns”, The Addison-Wesley Professional, 2003
[6] Maven Project
http://maven.apache.org/
[7] Spring Framework
http://www.springframework.org