Introducing JMS and MOM - Wrox Press Java Programming 24-Hour Trainer 2nd (2015)

Wrox Press Java Programming 24-Hour Trainer 2nd (2015)

Lesson 30. Introducing JMS and MOM

People send messages to each other via e-mail, instant messages, Twitter, Facebook, and so on. People can also communicate using more traditional methods by sending regular mail. You just need to drop a letter in a mailbox, and the rest is taken care of by postal service providers and logistics companies such as USPS, FedEx, UPS, and DHL.

Applications can send messages to each other using specialized servers known as message-oriented middleware (MOM), which plays a role similar to that of the delivery services. A program “drops the message” into a message queue (think mailbox) using the Java Messaging Service (JMS) application programming interface (API), and the message is delivered to another application that reads messages off of this queue. In short, JMS is an API for working with MOM servers.

Although JMS is a part of the Java EE specification, you can use it with Java SE applications without needing to have any Java application server—just make a .jar file containing JMS classes available to your program. This lesson shows you how to write both standalone Java clients and those that live inside a Java EE server. These clients send and receive applications with the JMS API via a MOM provider. In Lesson 31 you learn about the value that message-driven beans bring to the table.

Messaging Concepts and Terminology

You have already learned several methods of data exchange in distributed Java applications: direct socket communication, RMI, and HTTP-based interactions. But all of them were based on remote procedure calls (RPC) or the request/response model. MOM enables you to build distributed systems that communicate asynchronously.

JMS itself isn’t the transport for messages. JMS is to MOM what JDBC is to a relational DBMS. Java applications can use the same JMS classes with any MOM provider. Here’s a list of some popular MOM software:

· WebSphere MQ (IBM)

· EMS (Tibco Software)

· SonicMQ (Progress Software)

· ActiveMQ (open source, Apache)

· Open MQ (open source, Oracle)

If you place an order to buy some stocks by invoking the method placeOrder() on a remote machine, that’s an example of a synchronous or blocking call (also known as remote procedure call). The calling program can’t continue until the code in the placeOrder() method is finished or has thrown an error.

With asynchronous communications it’s different. You can place an order but don’t have to wait for its execution. Similarly, when you drop a letter in a mailbox you don’t have to wait for a mail truck to arrive to the recipient. The same applies to e-mail—press the Send button and continue working on other things without waiting until your message has been delivered. Recipients of your e-mails also don’t have to be online when you send a message; they can read it later.

The process of placing a trade order comes down to putting a Java object that describes your order into a certain message queue of your MOM provider. After placing an order, the program may continue its execution without waiting until the processing of the order is finished. Multiple users can place orders into the same queue. Another program (not necessarily in Java) should be de-queueing and processing messages. Figure 30-1 shows how a trading application can place orders (and receive executions) with another application running on a stock exchange.

image

Figure 30-1: Brokerage company communicates with a stock exchange via MOM

Even from this very high-level representation of a trading application you can see that messaging allows you to build loosely coupled distributed systems. Say, the stock exchange server is down, the brokerage company servers can still take customers' orders and send them to MOM. As soon as the stock exchange servers become operational, they start retrieving orders from the MOM queues. If the brokerage company would make synchronous RPC-type calls to the stock exchange, the entire trading application would stop functioning it the stock exchange servers were down.

The trading orders are placed in one queue, and when they are executed at the stock exchange the confirmations go into another queue and, if the application at the brokerage firm is active at that time, it will de-queue the messages immediately upon their arrival. If your application is not running, but you’ve opted for guaranteed delivery, the messages will be preserved in the queue by the MOM provider.

To increase the throughput of your messaging-based application, add multiple parallel consumers reading messages off of the same queue. You can create a consumer Java program that starts multiple threads, each of them de-queuing messages from the same queue. But a better way is to configure multiple consumers using message-driven beans (MDB), which l explain in Chapter 31.

With guaranteed message delivery, MOM—just like the post office—keeps the message in a queue until the receiver gets it. In this mode messages are persistent—the MOM provider stores them in its internal storage, which can be a DBMS or a filesystem. In a non-guaranteed mode, MOM delivers a message only to the receiving applications that are up and running at the moment that the message arrives.

Two Modes of Message Delivery

A program can send or publish a message. When it sends a message to a particular queue and another program receives the message from this queue it’s called point-to-point (P2P) messaging. In this mode a message is removed from a queue as soon as it’s successfully received. Figure 30-2shows that each message goes to only one consumer.

image

Figure 30-2: P2P messaging

If a program publishes a message to be consumed by multiple recipients, that’s publish/subscribe (pub/sub) mode. A message is published to a particular topic and many subscribers can subscribe to receive it. Figure 30-3 illustrates pub-sub, where on message can be consumed by multiple subscribers.

image

Figure 30-3: Pub/sub messaging

A topic represents some important news for applications and/or users; for example, PriceDropAlert, BreakingNews, and so on. In pub/sub mode, a message is usually removed from a queue as soon as all subscribers receive it (read about durable subscribers in the section How to Subscribe for a Topic).

Another good example of a pub/sub application is a chat room. A message published by one person is received by the other people present in the chat room. Developing a chat room with JMS and MOM is a pretty trivial task.

Introducing OpenMQ MOM

To learn JMS, you need to install a messaging server and configure some message queues there. You can use an open source MOM provider called Open MQ. As a bonus, it comes with GlassFish. You already have it installed in the mq directory in your GlassFish install. If you want to use Open MQ with any other application server you could download Open MQ as a separate product from https://mq.java.net/downloads/index.html.

First, I’m not going to use Java EE server. The goal is to test standalone Java clients communicating with Open MQ directly, without the middlemen. You need to start the Open MQ server and create a named queue there.

Edit the configuration file glassfish4/glassfish/mq/etc/imqenv.conf to specify the location of Java 8 on your computer. For example, this is how this configuration line looks on my Mac computer:

IMQ_DEFAULT_JAVAHOME=/Library/Java/JavaVirtualMachines/jdk1.8.0_25.j

dk/Contents/Home

Open a command (or Terminal) window to the glassfish4/mq/bin directory, and start the Open MQ broker. In Windows you need to run the program imqbrokerd.exe. If you use Mac OS, enter the following command:

./imqbrokerd

You see a message informing you that the broker is ready on port 7676. Now open another command window, change to the glassfish4/mq/bin directory, and start the admin graphical user interface (GUI) tool imqadmin to create the required messaging destinations:

./imqadmin

The administration console opens. In Open MQ there are applications called brokers that manage all message exchanges between clients. So you configure a message broker that manages messages related to stock trading. Right-click the Brokers node and add a new broker namedStockBroker, enter the password admin, and click OK. Then right-click StockBroker and select Connect to Broker from the menu. Figure 30-4 shows a snapshot of my screen after these steps.

Now create a physical destination—a queue for placing trading orders. Right-click the Destinations under the StockBroker node and select Add Broker Destination. In the pop-up window, enter the name of the destination: TradingOrdersQueue, as in Figure 30-5.

image

Figure 30-4: Open MQ console with newly created StockBroker

image

Figure 30-5: Configuring a destination in Open MQ

If you want to create a topic rather than a queue you just need to select the Topic radio button. As you can see, there are some other parameters you can set on a destination.

It’s out of the scope of this book to include a detailed overview of Open MQ MOM; try reading the Open MQ Administration Guide for more information. As long as you have a MOM server running and the messaging destination is configured, you can start writing programs that send and receive messages to/from this destination.

JMS API Overview

JMS API that includes Java classes that enable you to send and receive messages. In this lesson you discuss JMS 2.0 introduced in Java EE 7. If you have to work with application servers that support only older Java EE specifications look for tutorials describing JMS 1.1 API. All of the supporting classes and interfaces are defined in the package javax.jms, and you can read about them at http://docs.oracle.com/javaee/7/api/javax/jms/package-summary.htm.

In the old JMS specification you’d need to use multiple classes and interfaces such as Queue, QueueConnection, QueueConnectionFactory, QueueSession, Connection, QueueSender, QueueReceiver , Topic, TopicPublisher, and more. With JMS 2.0 this list is shorter. You can send and receive messages using just the few classes and interfaces listed here:

· JMSContext combines the JMS Connection and Session objects. To communication with MOM you need to connect to it and all your messaging exchanges are done within a session.

· ConnectionFactory is an object that creates Connection object(s) encapsulated in the JMSContext .

· Destination is a queue or a topic. Both Queue and Topic interfaces are inherited from Destination.

· JMSProducer is an interface that has methods to send messages to a destination.

· JMSConsumer is an interface that has methods to retrieve messages from a destination.

· Message is a root interface for all messages. It consists of a header and a body.

Types of Messages

Every message contains a header and optional body and has facilities for providing additional properties. The header contains the message identification (unique message ID, destination, type, and so on). The optional properties can be set by a program to tag a message with application-specific data; for example, UrgentOrder.

The optional body contains a message that has to be delivered. Following are the types of JMS messages that you can place in a message body. All these interfaces are inherited from javax.jms.Message.

· TextMessage is an object that can contain any Java String.

· ObjectMessage can hold any Serializable Java object.

· BytesMessage holds an array of bytes.

· StreamMessage has a stream of Java primitives.

· MapMessage contains any key/value pairs; for example, id=123.

Typically the message producer creates an object of one of the preceding types, initializes it with the application data, and then sends it to a MOM queue or topic. The only exception is String messages, which can be sent as-is without wrapping them inside the TextMessage. The consumer application extracts the message content by invoking a method Message.getBody(), which returns the message body of a specified type; for example:

msg.getBody(String.class); // msg is a reference to received message

If you’d need to send an object of, say Order type (must be Serializable), the message producer needs to wrap it up into the ObjectMessage—for example:

Order order = new Order();

ObjectMessage objMsg = context.createObjectMessage(order);

then the invocation of getBody() on the message consumer would look like this:

Order receivedOrder = msg.getBody(Order.class);

How to Send a Message Directly to MOM

This section and the following section show you how a Java SE client can communicate with MOM directly without using any Java EE server as a middleman. This mode isn’t often used, but it’ll help you to understand the benefits that Java EE and JNDI bring to the table when it comes to messaging. After you see these simple examples, you can rewrite them for a Java EE server using JNDI.

To send and receive messages, queues or topics should be preconfigured in MOM and their names must be known before a program can start sending messages. I used the world should, because even if you wouldn’t preconfigure the queue, some MOM providers (Open MQ included) may create a temporary queue in memory, which will be destroyed on server restart.

In the real world, a MOM server administrator manages queues and other messaging artifacts, but for the training purposes you already did it in the previous section with the queue TradingOrdersQueue .

To send messages directly to a MOM provider, a program has to perform the following steps:

1. In a Java class create a ConnectionFactory object using the implementation classes provided by the MOM vendor.

2. Create a JMSContext object.

3. Using the JMSContext create a Destination (for example, invoke createQueue() ).

4. Create a JMSProducer object.

5. Create one of the Message objects and put some data in it.

6. Call the send() method on the JMSProducer providing Destination and Message as arguments.

Listing 30-1 shows the class DirectMessageSender that implements all of the preceding steps except creating a Message instance - for strings it’s not required. This code sends a message to a queue TradingOrdersQueue.

Listing 30-1: Sending a message directly to MOM

public class DirectMessageSender{

public static void main(String[] args){

// Vendor-specific factory implementation

ConnectionFactory factory=

new com.sun.messaging.ConnectionFactory();

try(JMSContext context=factory.createContext("admin","admin")){

factory.setProperty(ConnectionConfiguration.imqAddressList,

"mq://127.0.0.1:7676,mq://127.0.0.1:7676");

Destination ordersQueue =

context.createQueue("TradingOrdersQueue");

JMSProducer producer = context.createProducer();

// Send msg to buy 200 shares of IBM at market price

producer.send(ordersQueue,"IBM 200 Mkt");

System.out.println("Placed an order to purchase 200

shares of IBM to TradingOrdersQueue");

} catch (JMSException e){

System.out.println("Error: " + e.getMessage());

}

}

}

The class DirectMessageSender uses an Open MQ–specific implementation of JMS’s ConnectionFactory, which is located in the vendor’s package com.sun.messaging. If you decide to change MOM providers or the server address, you would need to modify, recompile, and redeploy this code. This is not great, but you’ll fix this issue in the Java EE version of this client.

How to Receive a Message Directly from MOM

The program that receives messages is called a message consumer (a listener). It can be a standalone Java program or a message-driven bean. You can receive messages either synchronously, using the receive() method, or asynchronously by implementing the MessageListener interface and programming a callback onMessage(). The receive() method is rarely used because it engages a polling mechanism that constantly asks for a message.

Using an asynchronous callback method onMessage() on a message consumer is a preferred way to receive messages. The callback method onMessage() is invoked immediately when a message is put in the queue. The consumer class has to perform the following steps to receive messages asynchronously:

1. Create a class that implements the MessageListener interface and instantiate it.

2. Create a ConnectionFactory object using the implementation classes provided by the MOM vendor.

3. Create a JMSContext object.

4. Using the JMSContext to create a Destination (for example, invoke createQueue() ).

5. Create a JMSConsumer object and invoke on it setMessageListener(), specifying the object that implements MessageListener.

6. Implement onMessage() to handle the message when it arrives.

The sample class MyReceiver in Listing 30-2 shows how to consume messages from the TradingOrdersQueue asynchronously. Its constructor creates JMS objects and registers itself as a message listener. The callback onMessage() has code for processing the received messages.

Listing 30-2: Receiving a message

public class DirectMessageReceiver implements MessageListener{

ConnectionFactory factory =

new com.sun.messaging.ConnectionFactory();

JMSConsumer consumer;

DirectMessageReceiver(){

try(JMSContext context=factory.createContext("admin","admin")){

factory.setProperty(ConnectionConfiguration.imqAddressList,

"mq://127.0.0.1:7676,mq://127.0.0.1:7676");

Destination ordersQueue = context.createQueue(

"TradingOrdersQueue");

consumer = context.createConsumer(ordersQueue);

consumer.setMessageListener(this);

System.out.println(

"Listening to the TradingOrdersQueue...");

// Keep the program running - wait for messages

Thread.sleep(100000);

} catch (InterruptedException e){

System.out.println("Error: " + e.getMessage());

} catch (JMSException e){

System.out.println("Error: " + e.getMessage());

}

}

public void onMessage(Message msg){

try{

System.out.println("Got the text message from " +

"the TradingOrdersQueue: " + msg.getBody(String.class));

System.out.println("\n === Here's what toString()

on the message prints \n" + msg);

} catch (JMSException e){

System.err.println("JMSException: " + e.toString());

}

}

public static void main(String[] args){

new DirectMessageReceiver(); // instantiate listener

}

}

The class DirectMessageReciever calls the method sleep(100000) to prevent the program from termination for 100 seconds. I did it for testing purposes so you can send messages to the queue and see that they are being received. If you place a message in a queue by running DirectMessageSender the DirectMessageReciever gets it, producing output on the console that might look like this:

Got the text message from the TradingOrdersQueue: IBM 200 Mkt

=== Here's what toString() on the message prints

Text: IBM 200 Mkt

Class: com.sun.messaging.jmq.jmsclient.TextMessageImpl

getJMSMessageID(): ID:7-192.168.1.113(d8:86:af:a3:e1:8d)-62948-

1412631694897

getJMSTimestamp(): 1412631694897

getJMSCorrelationID(): null

JMSReplyTo: null

JMSDestination: TradingOrdersQueue

getJMSDeliveryMode(): PERSISTENT

getJMSRedelivered(): false

getJMSType(): null

getJMSExpiration(): 0

getJMSDeliveryTime(): 0

getJMSPriority(): 4

Properties: {JMSXDeliveryCount=1}

The first line is the result of extracting the text of the message by calling getBody().

msg.getBody(String.class)

The argument String.class means “cast the message body to type String."

The rest of the console output is produced by the toString() method on the Message object. I used it in DirectMessageReceiverjust to show you that besides message body there are a number of properties that can be retrieved by the corresponding getter.

How to Publish a Message

Programs publish messages to topics, which should be created in advance by the MOM system administrator. In the case of Open MQ, you need to select the Topic radio button in the window shown in Figure 30-5. Multiple subscribers can get messages published to the same topic (this is also known as one-to-many mode).

Message publishing is very similar to message sending, but the program should create a Topic instead of a Queue; the rest is the same. Listing 30-3 shows how to change the DirectMessageSender to publish a text message with a price quote to a topic called PriceQuoteTopic:

Listing 30-3: Publishing a message to a topic

Destination priceQuoteTopic = context.createTopic(

"PriceQuoteTopic");

// Publish a price quote msg to subscribers of PriceQuoteTopic

producer.send(priceQuoteTopic,"IBM 187.22");

Multiple subscribers can receive the same message.

How to Subscribe for a Topic

Subscribers can be durable or non-durable. Durable subscribers are guaranteed to receive their messages; they do not have to be active at the time a message arrives. Non-durable subscribers receive only those messages that come when they are active. With non-durable subscriptions, MOM removes the message from its internal storage as soon as all active subscribers have acknowledged message delivery. With durable subscriptions MOM retains the message until it’s delivered to all subscribers.

Some applications don’t need durable subscriptions. For example, if a subscriber missed a stock price published a second ago it’s okay. But this is not the case if a brokerage company has to report a suspicious transaction to several financial fraud prevention organizations—make such subscriptions durable.

If you were to change the DirectMessageRetriever into a non-durable topic subscriber, the following slight change would do the trick:

Listing 30-4: Creating a non-durable subscriber

Destination priceQuoteTopic =

context.createTopic("PriceQuoteTopic");

consumer = context.createConsumer(priceQuoteTopic);

Durable subscribers are created by invoking createDurableConsumer(), and each durable subscriber must have a unique client ID. Each durable subscription is identified by a combination of the topic name, subscriber’s name, and the client ID. This is how you can create a durable subscriber named FraudPreventionUnit:

Listing 30-5: Creating a durable subscriber

Destination priceQuoteTopic =

context.createTopic("PriceQuoteTopic");

context.setClientID("client123");

consumer = context.createDurableConsumer((Topic)priceQuoteTopic,

"FraudPreventionUnit");

For scalability reasons, the same subscription can be shared by multiple standalone consumers working in parallel (for example, running on different JVMs). In this case, a shared consumer has to be created (it can be durable or non-durable). For example, you can create a shared durable subscriber, as shown in Listing 30-6:

Listing 30-6: Creating a shared durable subscriber

Destination priceQuoteTopic =

context.createTopic("PriceQuoteTopic");

context.setClientID("client123");

consumer = context.createSharedDurableConsumer(

(Topic)priceQuoteTopic,"FraudPreventionUnit");

Parallel Subscriptions with MDBs

In standalone Java applications you can’t create multiple threads to create several durable topic subscriptions; using a shared subscription is your only option. But if subscribers are created as message-driven beans in a Java EE server, more than one bean can consume messages from the same subscription.

At any time an application can unsubscribe from a topic by calling the method unsubscribe() on the JMSContext object, for example:

context.unsubscribe("FraudPreventionUnit");

Message Acknowledgments and Transactions Support

When the message is successfully delivered, the MOM physically removes it from the queue. But what does “successfully delivered” means? Is it when the message was passed to the method onMessage() ? But if the code in onMessage() fails due to some error, you want the message to remain in the queue!

JMS API has a concept of a messaging session in which you can specify either acknowledgments modes or request transaction support to give the applications control over message removals from queues or topics.

There are three acknowledgments modes:

· AUTO_ACKNOWLEDGE mode sends the acknowledgment back as soon as the method onMessage() is successfully finished. This is a default acknowledgment mode.

· CLIENT_ACKNOWLEDGE mode requires explicit acknowledgment by calling the method acknowledge() from the message receiver’s code.

· DUP_OK_ACKNOWLEDGE mode is used in case the server fails; the same message may be delivered more than once. In some use cases it’s acceptable—for example, receiving a price quote twice doesn’t hurt.

The message acknowledgment mode is defined when the JMSContext is created. So far, our code samples DirectMessageSender and DirectMessageReceiver have created the JMSContext object by specifying two arguments: user ID and password. But you could also use an overloadedcreateContext() method to specify a messaging session mode; for example:

JMSContext context = factory.createContext("admin","admin",

JMSContext.CLIENT_ACKNOWLEDGE));

As an alternative to using acknowledgments, you can request transaction support for message consumers. Imagine if a received message contains the data that must be saved in a database and forwarded to a Web Service as one unit of work, so unless both operations are successful the entire transaction must be rolled back. You may need transaction support on the JMS producer side, too. For example, if you need to send two messages as one logical unit of work—either both messages were successfully sent or rolled back the transaction. The following code snippet shows how to create JMSContext and a JMSProducer object that sends two messages in different queues as one transaction.

try(JMSContext context = factory.createContext("admin","admin",

JMSContext.TRANSACTED)){

JMSProducer producer = context.createProducer();

Destination queue1 = context.createQueue("Queue1");

Destination queue2 = context.createQueue("Queue2");

producer.send(queue1,"Msg1");

producer.send(queue2,"Msg2");

context.commit(); // commit the JMS transaction

} catch (JMSException e){

context.rollback(); // rollback the JMS transaction

System.out.println("Error: " + e.getMessage());

}

If both sends went through fine, the Session object ( encapsulated inside JMSContext) issues a commit. If the exception is thrown, no messages are placed in any of the queues.

Message Selectors

If you have to share a queue with some other applications or developers from your team, use message selectors (also known as filters) to avoid “stealing” somebody else’s messages. For example, in the message consumer application you can opt for receiving messages that have a propertysymbol with the value IBM:

String selector = "symbol=IBM";

Context.createConsumer(ordersQueue, selector);

In this case the queue listener dequeues only those messages that have a String property symbol with the value IBM. Accordingly, the message producers have to set this property on the message object:

TextMessage outMsg = context.createTextMessage();

outMsg.setText("IBM 200 Mkt");

outMsg.setStringProperty("symbol", "IBM");

Destination ordersQueue=context.createQueue("TradingOrdersQueue");

JMSProducer producer = context.createProducer();

producer.send(ordersQueue, outMsg);

Remember that message selectors slow down the process of retrieval. Messages stay in a queue until the listener with the matching selector picks them up. Selectors really help if your team has a limited number of queues and everyone needs to receive messages without interfering with the others. But if someone starts the queue listener without selectors, it just drains the queue.

Sending Messages from Java EE Containers

Now that you know how the messaging works, you can see how to send messages to MOM destinations from the Java objects that live inside a Java EE container. This time you bind MOM objects like ConnectionFactory, Queue, and Topic to the JNDI tree, and Java messaging clients get them from there. Figure 30-6 shows a high-level picture of JMS clients communicating with MOM with or without Java EE.

image

Figure 30-6: Bringing together JMS, Java EE , and MOM

An external client can talk to a MOM server directly or from inside the Java EE server represented by the oval. When the Java EE server starts, it binds MOM objects to the JNDI tree as JMS objects. So if a Java servlet or other object deployed in Java EE server needs to send messages to MOM, it gets the references to administered objects by using lookup() or resource injection.

I’ll give you an example of a Java Servlet that reuses most of the code shown in the DirectMessageSender class. Assuming that you are familiar with the JNDI concepts from Lesson 29, the code of the MessageSenderServlet should be easy to understand.

@WebServlet("/MessageSenderServlet")

public class MessageSenderServlet extends HttpServlet {

@Resource(lookup ="java:comp/DefaultJMSConnectionFactory")

ConnectionFactory factory;

@Resource(lookup = "OutgoingTradeOrders") // JNDI queue name

Destination ordersQueue;

protected void doGet(HttpServletRequest request,

HttpServletResponse response)

throws ServletException, IOException{

try(JMSContext context=factory.createContext("admin","admin")){

JMSProducer producer = context.createProducer();

// Send msg to buy 200 shares of IBM at market price

producer.send(ordersQueue,"IBM 200 Mkt");

System.out.println("Placed an order to purchase 200" +

"shares of IBM to OutgoingTradeOrders");

}

}

}

For the MessageSenderServlet to work, you need to configure JMS objects using the administration console of the Java EE server. In this example, I use the JNDI queue name OutgoingTradeOrders that will be mapped to the physical queue name TradingOrdersQueue. In the next section I show you how to do it in GlassFish.

Administering JMS Objects in GlassFish

Configuring JMS objects comes down to mapping JNDI names to physical MOM objects. Assuming that Open MQ and GlassFish servers are up and running, open GlassFish Administration Console by visiting http://localhost:4848. There is a JMS Resources node in the navigation tree on the left. Click the server node, and you see a tab for adding the JMS physical destination, as shown Figure 30-7.

image

Figure 30-7: JMS Physical Destinations in GlassFish

The only reason the destination TradingOrdersQueue is known is because Open MQ is integrated with GlassFish. To configure another MOM server you’d need to create a new JMS host by using the Configurations node in the navigation panel.

Now you need to create a GlassFish JMS entry mapped to the physical MOM queue. Add the new destination resource to JMS Resources (see Figure 30-8). I gave it a JNDI name OutgoingTradeOrders.

image

Figure 30-8: Mapping JNDI name to a physical queue

Creation and closing of JMS connections (it’s done internally by JMSContext) are slow operations; you should consider using JMS connection pools. Java EE servers enable you to automatically create such pools by configuring a connection factory. Figure 30-9 shows how to configure a connection factory to use pooled connections. I set it to create 20 JMS connections on the GlassFish server startup and, as the number of users increases, the pool size will grow to the maximum size of 100 connections.

image

Figure 30-9: Configuring JMS connection factory

Now you can create, deploy, and run the servlet MessageSenderServlet in GlassFish. Do it in Eclipse as explained in Lesson 26. Create an Eclipse Dynamic Web Project specifying GlassFish as a target run time. Then create a servlet MessageSenderServlet with the code shown earlier in this section. Finally, deploy this Eclipse project using the right-click menu Add and Remove on the GlassFish server and run it.

Lesson 31 shows you how to retrieve messages from a queue or topic using message-driven beans. The “Try It” section has instructions on how to use a standalone message consumer.

Try It

The goal is to use a standalone message consumer DirectMessageReceiver to retrieve messages sent by the Java servlet. Test the messaging scenario depicted in Figure 30-10.

image

Figure 30-10: Java EE message sender and a standalone receiver

Lesson Requirements

You should have Eclipse for Java EE Developers and GlassFish 4.1 (it comes with Open MQ) installed.

NOTE You can download the code and resources for this “Try It” section from the book’s web page at www.wrox.com/go/javaprog24hr2e. You can find them in the Lesson30.zip.

Hints

Open MQ started independently with imqbrokerd runs on a different port than embedded Open MQ started by GlassFish.

Step-by-Step

1. Stop both the Open MQ server started independently and GlassFish.

2. Open the file glassfish/domains/domain1/config/domain.xml and change the value of the system variable JMS_PROVIDER_PORT to be 7676. By default, GlassFish starts embedded Open MQ server on the port 27676, but our DirectMessageReceiver uses hardcoded 7676 as a port value.

3. Restart GlassFish. Now it starts embedded JMS provider on port 7676.

4. Make sure that the OutgoingTradeOrders is configured in the GlassFish, as shown on Figure 30-8.

5. Run the MessageSenderServlet as explained earlier. It’ll send the message to the queue that is known as OutgoingTradeOrders in the GlassFish JNDI tree.

6. Run DirectMessageReceiver. It prints on the console the message “Listening to the TestQueue” and then retrieves and prints the message from the physical queue named TradingOrdersQueue.

7. Modify the code of DirectMessageReceiver so it has no hardcoded values of the Open MQ server.

8. Self-study the use of the QueueBrowser class and write a program that prints the content of a queue without de-queuing messages.

TIP Please select the videos for Lesson 30 online at www.wrox.com/go/javaprog24hr2e. You will also be able to download the code and resources for this lesson from the website.