In questo articolo vedremo due esempi pratici del pattern Content-Based Router (CBR).
Il primo consiste nella realizzazione pratica di una classe CBR, il secondo riguarda la configurazione di un componente CBR già realizzato e disponibile da un prodotto d‘integrazione.
Nei precedenti articoli si sono introdotti i principali pattern d‘integrazione message-based (vedere [1] e [2]).
Esempio di implementazione di un Content-based Router
Riprendendo l‘esempio precedentemente introdotto (vedere [2]), concentriamoci sulla parte in cui, a seconda del tipo di prodotto che si vuole comprare, bisogna inoltrare il messaggio contenente l‘ordine all‘opportuna destinazione.
Supponiamo quindi di avere una semplice applicazione client che ha la possibilità di acquistare due possibili prodotti sportivi: una maglietta di Football Americano della squadra dei Pittsburgh Steelers o una maglietta di Basket della squadra dei Los Angeles Lakers.
A secondo dell‘ordine di acquisto, la richiesta deve essere inoltrata o al gestore vendite della National Footbal League – NFL (per la maglietta di Football), o al gestore vendite della National Baket Asscoiation – NBA (per la maglietta di Basket).
Piuttosto che fare conoscere le N possibili destinazioni al client (nell‘esempio proposto sono solo due per motivi didattici) e far sì che sia lui a capire quale deve essere l‘opportuno destinatario del messaggio, è opportuno (e vantaggioso!) prevedere un componente di router centralizzato che si occupi di “smistare” opportunamente i messaggi implementando le regole del pattern Content Base Router (vedere [3]).
I vantaggi del pattern CBR sono molti. La logica di routing è centralizzata nel Router e non negli N client. Se l‘algoritmo dovesse essere modificato si interverrebbe in un punto centrale così come se le destinazioni dovessero cambiare (modificate / tolte / aggiunte) l‘intervento risulterebbe circoscritto al solo Router.
Nel nostro scenario d‘esempio si procede quindi a creare un componente di router che disaccoppia il client dalle le due possibili destinazione (il gestore vendita NFL per la maglietta di footbal americano e il gestore vendite NBA per la maglietta di Basket).
Sviluppiamo quindi un semplice router per gestire lo scenario d‘esempio appena descritto.
Il client dovrà specificare nel contenuto del messaggio l‘identificativo del tipo di ordine. Per fare questo prevediamo una opportuna proprietà JMS di nome “KIND”.
Se il client deve acquistare una maglietta di Football Americano valorizzerà la proprietà “KIND” con il valore “NFL”:
TextMessage message = queueSession.createTextMessage();message.setStringProperty("KIND","NFL");message.setText(msg);queueSender.send(message);
mentre per l‘acquisto di una maglietta di basket si specificherà come valore della proprietà “KIND” il valore “NBA”:
TextMessage message = queueSession.createTextMessage();message.setStringProperty("KIND","NBA");message.setText(msg);queueSender.send(message);
Da notare come a secondo dell‘acquisto cambia il contenuto dell‘header JMS ma non la destinazione del QueueSender che è sempre la stessa: quella del Content Based Router(CBR).
this.queue = << lookup JNDI.this.queueConnectionFactory = << lookup JNDI.this.queueConnection = this.queueConnectionFactory.createQueueConnection();this.queueSession = this.queueConnection.createQueueSession(false,Session.AUTO_ACKNOWLEDGE);this.queueSender = queueSession.createSender(queue);
Costruiamo ora una semplice classe che implementa l‘algoritmo di Content Base Routing che, in base al contenuto della proprietà JMS, invia il messaggio all‘opportuna destinazione.
La classe
public class SampleCBR extends JFrame implements MessageListener{
si mette in ascolto sulla coda di input queue/CBR_Input dalla quale arriveranno le richieste d‘ordine
ctx = new InitialContext();Object obj = ctx.lookup("ConnectionFactory");ConnectionFactory factory = (ConnectionFactory)obj;queueConnectionFactory = (QueueConnectionFactory)factory;queueSession = queueConnection.createQueueSession(false,Session.AUTO_ACKNOWLEDGE);Queue queueInput = (Queue)ctx.lookup ("queue/CBR_Input");QueueReceiver queueReceiver = queueSession.createReceiver(queueInput);queueReceiver.setMessageListener(this);queueConnection.start();
ed effettua la lookup delle due code di ouput, la coda queue/CBR_Output_1 sulla quale è in ascolto il gestore degli ordini NFL
Queue queueOutput1 = (Queue)ctx.lookup("queue/CBR_Output_1");
queueSender1 = queueSession.createSender(queueOutput1);
e la coda queue/CBR_Output_2 sulla quale è in ascolto il gestore degli ordini NBA
Queue queueOutput2 = (Queue)ctx.lookup("queue/CBR_Output_2");queueSender2 = queueSession.createSender(queueOutput2);
Ad ogni ricezione del messaggio di un ordine d‘acquisto, verrà invocato dal sistema di Messaging il metodo onMessage(). All‘interno di tale metodo è implementata la seguente logica di routing del messaggio:
- se la property KIND del messaggio JMS vale "NFL", il messaggio viene indirizzato sulla coda di Output_1 (dove è in ascolto il gestore degli ordini dei prodotti NFL)
- se la property KIND del messaggio JMS vale "NBA", il messaggio viene indirizzato sulla coda di Output_2 (dove è in ascolto il gestore degli ordini dei prodotti NBA)
- altrimenti non viene effettuato nessun inoltro e viene stampato il messaggio di errore
public void onMessage(Message message) {TextMessage msg = null;String kind=null;try {if (message instanceof TextMessage) {msg = (TextMessage) message;kind = msg.getStringProperty("KIND");if("NFL".equals(kind)){queueSender1.send(msg);}else if("NBA".equals(kind)){queueSender2.send(msg);}else{System.err.println("# ERRORE: NO Routing! # ");}} else {System.err.println(CLASS_NAME + ".onMessage: Message of wrong type:!");}} catch (JMSException e) {System.out.println(CLASS_NAME + ".onMessage: " + e.getMessage());e.printStackTrace();} catch (Throwable t) {System.exit(-1);}}
Utilizzo di un ESB Content-Based Routing
Vediamo ora di vedere come lo stesso esempio visto in precedenza verrebbe "risolto" utilizzando un prodotto d‘integrazione.
I tool EAI mettono a disposizione dei componente CBR "ready-to-use" in cui è possibile personalizzare ad hoc le regole di configurazione del routing.
Ad esempio utilizzando il prodotto Sonic ESB (vedere [SONIC]) è possibile utilizzare i componenti CBR della suite provvedendo a configurarli per le proprie specifiche esigenze.
Per il nostro esempio, dal Tool di configurazione del prodotto bisogna specificare le condizioni di routing (Conditions) in base al valore della proprietà JMS "KIND" e la relativa destinazione (Address) nel caso in cui la condizione risulti soddisfatta.
Si configura quindi il CBR per inoltrare il messaggio ricevuto alla destinazione ESBSample.Q5 se la proprietà KIND JMS è presente e valorizzata con la stringa "NFL", mentre viene inoltrato alla destinazione ESBSample.Q6 se è valorizzata con la stringa "NBA".
Una volta configurato ed effettuato il deploy del componente (MokaCustomeRouterService) nel Container d‘interesse (SampleContainer) è possibile verificare il corretto funzionamento mediante il client di test JMS.
Inoltrando un messaggio avente proprietà KIND valorizzata con la stringa NFL, si deve verificare che il messaggio si inoltrato alla destinazione ESBSampleQ5 visto che il CBR trova soddisfatta la regola di routing "NFL".equals(XQ_getProperty("KIND")).
Se invece si specifica nel messaggio la proprietà KIND uguale a NBA si deve verificare che il messaggio si inoltrato alla destinazione ESBSampleQ6.
Nella figura sottostante si vede il caso in cui il messaggio si riferisce ad un acquisto di un prodotto NFL e quindi viene inoltrato alla destinazione ESBSampleQ5.
La figura seguente riassume l‘esempio fino ad ora spiegato.
Il CBR così ottenuto può essere utilizzato in modalità drag-and-drop dal tool BPM per il design di un processo di business.
Conclusioni
In questo articolo, che conclude la miniserie dedicata all‘introduzione dei pattern di integrazione, si sono presentati due esempi pratici di CBR: il primo riguardava lo sviluppo da zero, il secondo invece la configurazione di un componente disponibile da un prodotto d‘integrazione. Si è visto come in entrambi i casi la conoscenza del pattern è fondamentale per un corretto sviluppo e/o utilizzo del CBR.
1S.Rossini"Integration Patterns (I)" Mokabyte 100 Ottobre 20052S.Rossini"Integration Patterns (II)" Mokabyte 101 Novembre 20053Gregor Hohpe, Bobby Woolf"Enterprise Integration Patterns: Designing, Building, and Deploying Messaging Solutions" Addison-Wesley, 20044http://www.eaipatterns.com/index.html5Alur, Crupi, Malks"Core J2EE Patterns: Best Practices and Design Strategies"6Floyd Marinescu"EJB Design Patterns: Advanced Patterns, Processes and Idioms"7Ed Roman, Scott Ambler, Tyler Jewell"Mastering Enterprise JavaBeans"8Sun Java Center J2EE Patternshttp://developer.java.sun.com/developer/restricted/patterns/J2EEPatternsAtAGlance.html9Sun Blueprints Design Patterns Cataloghttp://java.sun.com/blueprints/patterns/j2ee_patterns/catalog.html10Gamma, Helm, Johnson, Vlissides