Iniziare con JMS

C'é una certa carenza di esempi pratici che spieghino come iniziare ad usare JMS però, con un po' di pazienza, si trova qualcosa di interessante, come questo post del blog di Masoud Kalali su java.net, che risale al maggio 2006 ma é tuttora utile come riferimento.

Lo uso perciò per scrivere una semplice applicazione per JMS, usando NetBeans 6.7 come ambiente di sviluppo e GlassFish v2 come Application Server, il cui scopo é generare messaggi per mezzo di una pagina JSP e riceverli con un MDB (Message-Driver Enterprise Java Bean).

GlassFish

Ho aperto NetBeans, per prima cosa mi accerto che GlassFish sia attivo: vado sulla pagina dei services, e, sel server GlassFish non sta già correndo, ci faccio sopra un click di destra e do il comando "Run".

Poi guardo la console dell'amministratore di Glassfish: "View Admin Console". Il che equivale, se si sono tenuti i settaggi standard, ad aprire con il browser la pagina

http://localhost:4848/

Nella sezione Resources, JMS Resources seleziono il nodo Connection Factories. Ne creo una nuova, New JMS Connection Factory:
  • JNDI Name: jms/tConnectionFactory
  • Resource Type: javax.jms.ConnectionFactory
  • Description: Test JMS Connection
Per il resto lascio i valori di default indicati.

Passo ora alle Destination Resources e ne creo una nuova, New JMS Destination Resource:
  • JNDI Name: jms/tQueue
  • Physical Destination Name: tConnectionFactory
  • Resource Type: javax.jms.Queue
  • Description: Test JMS Destination
Nota che la destination é il nome (senza il prefisso jms) della connection factory che abbiamo appena creato. In pratica abbiamo legato la risorsa destinazione alla factory delle connessioni.

Torniamo a NetBeans, facciamo un refresh del server GlassFish e vediamo tra le JMS Resources la nostra connection factory e destination resource.

Enterprise Application

Creo una applicazione enterprise, a nome JmsOne, accetto i default e lascio che NetBeans generi la struttura del progetto.

La mia applicazione JmsOne contiene due moduli Java EE, un war per l'applicazione web e un jar per gli EJB. Questi due moduli mi vengono messi a disposizione anche come applicazioni a se stanti: JmsOne-ejb e JmsOne-war.

Message-Driven Bean

Costruiamoci per prima cosa il bean che riceve il messaggio da JMS. Faccio perciò un click di destra su JmsOne-ejb e dico di voler creare un nuovo Message-Driven Bean.

Scelgo un nome per il bean (OneMDBean) e per il package (beans), ma la cosa importante é la "Server Destination", ovvero la connessione a JMS, scelgo jms/tQueue, il nome JNDI della destinazione JMS che abbiamo creato poco fa.

Guardiamo il codice generato da NetBeans per OneMDBean, che vediamo essere una classe Java che implementa l'interfaccia MessageListener ed é annotata @MessageDriven specificando il riferimento a jms/tQueue.

Il grosso del lavoro é già fatto: quando JMS riceve un messaggio sulla coda, lo passa a questo bean invocando il metodo onMessage(), ci resta dunque da definire cosa deve fare il bean di questo messaggio.

In questa implementazione il bean fa solo un po' di logging sullo standard output, così che io possa vedere, nella finestra dell'output di GlassFish v2, se tutto ha funzionato come ci aspettiamo:

public void onMessage(Message message) {
System.out.println(this.getClass().getSimpleName() + ".onMessage()");

try {
if(message instanceof TextMessage) {
TextMessage msg = (TextMessage)message;
System.out.println("Message received: " + msg.getText());
}
else {
System.out.println("Unexpected message type: " +
message.getClass().getName());
}
}
catch (Throwable te) {
te.printStackTrace();
}
}

Nota che ci aspettiamo che il messaggio sia un TextMessage, se non lo é non sappiamo come gestirlo, e segnaliamo la situazione anomala.

Web Application

Costruiamo una piccola applicazione web per generare i messaggi che mandiamo su JMS.

L'interfaccia utente é una semplice pagina html, che richiama una servlet:

<pre>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>JMS Message Producer</title>
</head>
<body>
<h1>Enter a message:</h1>
<form action="SendMessage">
<input type="text" name="message" value="A message" size="30" />
<input type="submit" value="Send The message" name="send" />
</form>
</body>
</html>
</pre>


Creiamo quindi la servlet che viene richiamata dalla pagina html. Faccio un click di destra sulla applicazione web JmsOne-war, "New", "Servlet..." e specifico come "Class Name" SendMessage (é l'action della form della nostra pagina html), come "Package" servlet (o quello che mi par meglio), passo alla pagina successiva, accetto i valori di default proposti da NetBeans per il nome della servlet e il pattern URL, e confermo.

Modifichiamo ora il metodo processRequest(). Perché mandi il messaggio sulla coda JMS e mostri un report all'utente.

protected void processRequest(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
System.out.println(this.getClass().getSimpleName() + ".processRequest()");

response.setContentType("text/html;charset=UTF-8");
PrintWriter out = response.getWriter();
try {
String message = request.getParameter("message");
String queue = sendMessage(message);
System.out.println("Message sent: " + message);

// servlet report
out.println("<html>");
out.println("<head>");
out.println("<title>Message Sending Report</title>");
out.println("</head>");
out.println("<body>");
out.print("The message "<i>" + message + "</i>"" ));
out.print(" has been sent to the JMS queue <b>");
out.println(queue + "</b>.");
out.println("</body>");
out.println("</html>");
}
catch(Exception ex){
ex.printStackTrace();
}
finally {
out.close();
}
}

Il lavoro specifico per JMS l'abbiamo raccolto nel metodo sendMessage(), che prende in input il messaggio e ritorna il nome della coda a cui viene mandato, che vediamo qui di seguito:

private final String FACTORY = "jms/tConnectionFactory";
private final String QUEUE = "jms/tQueue";

private String sendMessage(String msg) throws Exception {
Context ctx = new InitialContext();
ConnectionFactory factory = (ConnectionFactory)ctx.lookup(FACTORY);
Connection connection = factory.createConnection();
Session session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE);

Queue queue = (Queue)ctx.lookup(QUEUE);
MessageProducer messageProducer = session.createProducer(queue);

TextMessage message = session.createTextMessage(msg);
messageProducer.send(message);

return queue.getQueueName();
}

Dal contesto iniziale accedo alla connection factory, che mi permette di creare una connessione, che uso per creare una sessione. Accedo anche alla coda via contesto iniziale, e posso quindi crearmi l'oggetto MessageProducer che uso per mandare il messaggio.

Ritorno il nome della coda per fare un po' di logging.

E questo é tutto.

Testing

Eseguiamo l'applicazione, il che equivale a lanciare la nostra applicazione web, ovvero ad accedere http://localhost:8080/JmsOne-war/ (se abbiamo tenuto i default suggeriti per GlassFish e per JmsOne-war), ci viene mostrata dunque index.html. Cliccando sul bottone mandiamo il messaggio a JMS che, a sua volta, chiama OneMDBBean.onMessage(), come possiamo vedere dal log di GlassFish.

Nessun commento:

Posta un commento