Visualizzazione post con etichetta jms. Mostra tutti i post
Visualizzazione post con etichetta jms. Mostra tutti i post

JMS con Glassfish

Note dalla lettura di Beginning Java EE 6 Platform with GlassFish 3 di Antonio Goncalves. Capitolo 13: Sending Messages.

Con l'acronimo JMS, che sta per Java Message Service, intendiamo parlare delle specifiche Java per l'implementazione di un MOM (Message-oriented middleware).

MOM é un provider (detto anche broker) che permette uno scambio asincrono di messaggi tra sistemi eterogenei.

OpenMQ é l'implementazione di riferimento per JMS.

Chi manda un messaggio é detto producer, lo manda a una destinazione (destination) e chi riceve il messaggio é un consumer.

Ci sono due modelli architetturali per JMS, che implicano una differente destinazione.
  • Modello point-to-point (P2P): Il producer mette il messaggio in una coda (queue) che il consumer legge. Quando il consumer conferma la lettura (to acknowledge) il provider rimuove il messaggio dalla coda.
  • Modello publish-subscribe (pub-sub): Il producer pubblica il messaggio su di un topic, e tutti i sottoscrittori lo ricevono.
Le interfacce Java usate variano a seconda del modello utilizzato. Nell'elenco qui sotto in prima posizione é l'interfaccia generica, seguita da quella propria del modello point-to-point e quindi dal pub-sub.
  • Destination, Queue, Topic
  • ConnectionFactory, QueueConnectionFactory, TopicConnectionFactory
  • Connection, QueueConnection, TopicConnection
  • Session, QueueSession, TopicSession
  • MessageConsumer, QueueReceiver, TopicSubscriber
  • MessageProducer, QueueSender, TopicPublisher

Proprietà del messaggio

Oltre ai campi standard definiti nell'header di un messaggio, é possibili definire dei campi custom, usando le funzionalità messe a disposizione da JMS per la gestione delle proprietà.

Il nome di una proprietà deve rispettare le regole definite per la sintassi di un selettore.
Una proprietà può assumere valori di tipo boolean, byte, short, int, long, float, double e String.

I valori di una proprietà per un messaggio sono fissati prima della sua spedizione. Per il cliente sono disponibili in sola lettura, il suo tentativo di modificarli risulta in una eccezione di tipo MessageNotWriteableException.

Per iterare la lettura delle proprietà di un messaggio si usi il metodo getPropertyNames() per ottenere un enumeratore dei nomi delle proprietà, e quindi si legga il valore associato per ognuno di essi.

E' possibile eliminare tutti i valori delle proprietà di un messaggio usando il metodo clearProperties().

Cercare di leggere il valore di una proprietà non definita per uno specifico messaggio fa ottenere null come risultato.

I campi dell'intestazione del messaggio

Gran parte dei campi dell'intestazione sono inizializzati dal metodo che lo manda:
  • JMSDestination
  • JMSDeliveryMode
  • JMSExpiration
  • JMSPriority
  • JMSMessageID
  • JMSTimestamp
Altri tre sono settati dal cliente:
  • JMSCorrelationID
  • JMSReplyTo
  • JMSType
E infine il provider setta
  • JMSRedelivered.
L'amministratore del sistema può configurare JMS per dare un valore specifico per JMSDeliveryMode, JMSExpiration e JMSPriority.
  • JMSDestination: destinazione a cui é stato mandato il messaggio.
  • JMSDeliveryMode: modo di spedizione secondo il quale é stato mandato il messaggio.
  • JMSMessageID: identifica univocamente ogni messaggio spedito da un provider
  • JMSTimestamp: é il momento in cui il messaggio é stato passato a un provider per essere spedito.
  • MSCorrelationID: può essere usato dal client per creare una relazione tra messaggi.
  • JMSReplyTo: la destinazione dove si dovrebbe mandare la risposta a un determinato messaggio.
  • JMSRedelivered: settato se il messaggio é stato mandato più volte, e quindi forse già recepito dal client.
  • JMSType: tipo del messaggio.
  • JMSExpiration: Settato a zero nel caso il messaggio sia sempre valido.
  • JMSPriority: priorità dal messaggio definita nell'intervallo [0..9], con 0 ad indicare il valore più basso.

Il background del modello del messaggio

Nella messaggistica enterprise i messaggi sono entità che consistono di una intestazione (header) e un corpo (body).

L'header contiene i campi che sono usati per il routing e l'identificazione del messaggio; il body contiene i dati che sono spediti.

Ogni sistema tende a implementare questo modello in un modo diverso, dove le maggiori differenze sono nel contenuto e nella semantica dei campi dell'intestazione.

Obiettivi del modello del messaggio JMS
  • Fornire una API unica che sia adatta alla creazione di messaggi che sia in linea con il formato usato da applicazioni esistenti non JMS
  • Supportare lo sviluppo di applicazioni eterogenee che operino su differenti macchine, sistemi operativi, linguaggi di programmazione.
  • Supportare l'uso di oggetti Java
  • Supportare XML
Messaggi JMS

I messaggi JMS sono composti da queste parti:
  • Header: i campi dell'header contengono valori usati dai clienti e dal provider per identificare e fare il routing dei messaggi.
  • Proprietà: oltre ai campi standard dell'header é possibile aggiungere campi aggiuntivi all'header.
    • Proprietà specifiche dell'applicazione
    • Proprietà standard
    • Proprietà specifiche del provider
  • Body: sono definiti da JMS svariati tipi di body che coprono la maggior parte dei tipi di messaggi correntemente in uso.

Request/Reply

Il campo JMSReplyTo specificato nell'header del messaggio permette di specificare al Destination dove debba essere mandata una risposta.

Il campo JMSCorrelationID della risposta può essere usato per dare un riferimento a quale fosse la richiesta originale. Parleremo meglio più avanti dei campi nell'header dei messaggi.

Inoltre JMS fornisce anche funzionalità per create code e topic temporanei che possono essere usati come destinazione univoca per le risposte.

JMS definisce alcune classi di base per la gestione di Request/Reply, implementazioni più specializzate sono lasciate alla cura del provider JMS e dei client.

Multithreading

Per ridurre la complessità del sistema JMS richiede che sia supportato l'accesso concorrente solo per quegli oggetti che viene naturale pensare vengano condivisi da client che operano in multithreading.

Gli oggetti JMS che supportano uso concorrente sono: ConnectionFactory, Connection e Destination.

Gli oggetti JMS progettati per essere acceduti sequenzialmente da diversi thread sono: Session, MessageProducer, MessageConsumer.

Per gli oggetti di tipo Session sono definite da JMS alcune specifiche regole per limitarne l'uso in condizioni di concorrenza. Ne parleremo in un post successivo, quando ne sapremo abbastanza di JMS per poterne parlare.

Ci sono due motivi per limitare l'accesso in concorrenza a oggetti Session.
  1. JMS supporta le transazioni via Session, ed é molto difficile implementare transazioni in condizioni di multithreading.
  2. Session supporta il consumo asincrono di messaggi. Implementare la gestione del consumo asincrono di messaggi in ambiente multithreading sarebbe estremamente complesso sia per il server JMS che per il client.
Imporre che la Session non sia multithreading ne semplifica l'uso per il client tipico. Un client più evoluto può ottenere il livello di concorrenza desiderato usando più sessioni.

Sviluppare applicazioni JMS

In generale una applicazione JMS consiste di uno o più client JMS che scambiano messaggi. L'applicazione può includere anche client non-JMS che facciano uso della API nativa del provider JMS invece che usare JMS.

Una applicazione JMS può essere progettata e rilasciata come un'unità ma spesso i client JMS sono aggiunti incrementalmente a una applicazione esistente.

La definizione del messaggio usato da una applicazione può venire sia dalla parte JMS dell'applicazione sia dalla non-JMS.

Sviluppo di un client JMS

Un tipico client JMS esegue la seguente procedura di setup JMS:
  • trova un oggetto ConnectionFactory via JNDI;
  • trova uno o più oggetti Destination via JNDI;
  • usa la ConnectionFactory per creare una JMS Connection con la consegna dei messaggi non abilitata;
  • usa la Connection per creare una o più JMS Session;
  • usa la Session e le Destination per creare i MessageProducer e i MessageConsumer necessari;
  • far partire la consegna dei messaggi sulla Connection.
A questo punto il client é pronto per produrre e consumare messaggi.

Due stili per i messaggi

Una applicazione JMS può usare due modalità diverse per scambiare messaggi: la point-to-point (PTP) o la publisher/subscriber (Pub/Sub).

Naturalmente é possibile usare entrambe le modalità nella stessa applicazione, basta far riferimento a destinazioni diverse.

Nell'ambito JMS, la destinazione PTP é chiamata Queue (coda) e quella Pub/Sub Topic.

In JMS é possibile usare sia interfacce generiche sia specializzate per gestire i differenti elementi del modello.

Interfacce comuniPTPPub/Sub
ConnectionFactoryQueueConnectionFactoryTopicConnectionFactory
ConnectionQueueConnectionTopicConnection
DestinationQueueTopic
SessionQueueSessionTopicSession
MessageProducerQueueSenderTopicPublisher
MessageConsumerQueueReceiver / QueueBrowserTopicSubscriber

Come consigliano le buone pratiche di progettazione Object-Oriented, bisognerebbe cercare di lavorare con le astrazioni più generiche, e quindi con le interfacce comuni, per permettere una maggiore indipendenza del codice scritto dagli oggetti sottostanti.

Ecco una breve descrizione delle interfacce:

  • ConnectionFactory: un oggetto amministrato usato dal client per creare una connessione;
  • Connection: una connessione a JMS;
  • Destination: un oggetto amministrato che incapsula l'identità della destinazione di un messaggio;
  • Session: un contesto single-threaded usato per mandare e ricevere messaggi;
  • MessageProducer: un oggetto creato da una sessione che viene usato per mandare messaggi a una destinazione;
  • MessageConsumer: un oggetto creato da una sessione che viene usato per ricevere messaggi da una destinazione.
Il flusso di esecuzione segue solitamente uno schema come questo:

La ConnectionFactory crea una Connection, che crea una Session, che crea un Message.

La Session crea anche un MessageProducer che manda un Message a una Destination, e crea pure un MessageConsumer che riceva un Message da una Destination.

Si dice che una applicazione JMS consuma un messaggio quando un cliente manda una ricevuta di accettazione per un messaggio. Si dice che produce un messaggio quando il cliente manda un messaggio ad una destinazione JMS.

Amministrazione

Ci sono due tipi di oggetti amministrati:
  • ConnectionFactory: usato da un client per creare una connessione ad un provider;
  • Destination: usato da un client per specificare la destinazione di una messaggio che sta mandando e l'origine di un messaggio che riceve.
Gli oggetti amministrati sono messi in un namespace JNDI da un amministratore. Un client JMS dichiara nella sua documentazione gli oggetti amministrati JMS che richiede e come i nomi JNDI di questi oggetti gli devono essere messi a disposizione.

Cos'é una applicazione JMS?

Il documento "Java Message Server Specification", un pdf di 140 pagine, é disponibile sul sito Sun.

Una applicazione JMS é composta da quattro parti:
  • Client JMS: i programmi Java che mandano e ricevono messaggi.
  • Messaggi: ogni applicazione definisce un insieme di messaggi che sono usati per comunicare informazioni tra i suoi client.
  • Provider JMS: un sistema di messaggistica che implementa JMS in aggiunta alle altre funzionalità amministrative e di controllo richieste da un prodotto per la gestione della messaggistica.
  • Oggetti amministrati: sono oggetti JMS preconfigurati creati dall'amministratore per essere utilizzati dai client.