Chapter 29 Using the Message Service
You can create a JMS application using either the point-to-point or the publish/subscribe messaging model. Both models support applications that are capable of:
You can download a copy of the JMS 1.0.2 API documentation from the Java Web site .
A JMS client application must instantiate a JMS InitialContext object and set these properties:
java.naming.factory.initial
to "com.sybase.jms.InitialContextFactory"
.
java.naming.provider.url
to
the location where the client can connect to EAServer.
When instantiating a JMS InitialContext from a base client, specify the user name with the SECURITY_PRINCIPAL property, and specify the password with the SECURITY_CREDENTIALS property. The default for both properties is an empty string.
This example runs the JMS client application JMSClientClass and sets the InitialContext factory, URL, user name, and password properties at runtime:
java -D java.naming.factory.initial=com.sybase.jms.InitialContextFactory -Djava.naming.provider.url=iiop://stack:9000 -DSECURITY_PRINCIPAL="jagadmin" -DSECURITY_CREDENTIALS="sybase" JMSClientClass
A connection factory allows you to create JMS connections and specify a set of configuration properties that define the connections. EAServer provides two types of connection factories, one to create queue connections and one to create topic connections. Queue connections allow you to send and receive messages using the PTP messaging model. Topic connections allow you to publish and receive messages using the Pub/Sub messaging model. EAServer provides two preconfigured connection factories that you can use javax.jms.QueueConnectionFactory and javax.jms.TopicConnectionFactory. To create connection factories, use Jaguar Manager--see Chapter 8, "Setting up the Message Service," in the EAServer System Administration Guide for details. Once you have created a connection factory, client applications must look up a ConnectionFactory object using JNDI, as in this example:
// Look up a queue connection factory QueueConnectionFactory queueCF = (QueueConnectionFactory) ctx.lookup("MyTestQueueCF"); // Look up a topic connection factory TopicConnectionFactory topicCF = (TopicConnectionFactory) ctx.lookup("MyTestTopicCF");
If the connection factory cannot be found, EAServer throws a javax.naming.NamingException.
Permanent destinations are message queues or topics whose configuration properties are stored in a database. You can create permanent destinations two ways. The recommended way is to use Jaguar Manager to create and configure message queues and topics. See Chapter 8, "Setting up the Message Service," in the EAServer System Administration Guide for information on how to do this. You can also create permanent destinations using the JMS APIs createQueue and createTopic. Sybase suggests that you use this option only when client applications need the ability to dynamically change the provider-specific destination name; applications using this technique are not portable.
When you create message queues and topics using Jaguar Manager, client applications can use their InitialContext object to look up the destinations; for example:
// Look up a message queue javax.jms.Queue queue = (javax.jms.Queue) ctx.lookup("MyQueue"); // Look up a topic javax.jms.Topic topic = (javax.jms.Topic) ctx.lookup("MyTopic");
To create permanent destinations using the JMS APIs, you must first create a session; see "Creating sessions" for details. Once you have access to a session object, use this syntax to create a destination:
javax.jms.Queue queue = queueSession.createQueue("MyQueue"); javax.jms.Topic topic = topicSession.createTopic("MyTopic");
You can also create temporary destination objects that are active only during the lifetime of a session. When the session is closed, temporary destination objects and their associated messages are discarded. These two lines illustrate how to create temporary message queue and topic destinations:
// Create a temporary queue javax.jms.Queue queue = queueSession.createTemporaryQueue(); // Create a temporary topic javax.jms.Topic topic = topicSession.createTemporaryTopic();
JMS provides two types of connections, one for message queues and one for topics. To create a connection from a JMS client application to EAServer, you must have access to a ConnectionFactory object. See "Looking up a ConnectionFactory object". Once you have created a connection, you must explicitly start it before EAServer can deliver messages on the connection. To avoid message delivery before a client has finished setting up, you may want to delay starting the connection. This code fragment creates a QueueConnection object and starts the connection:
QueueConnection queueConn = queueCF.createQueueConnection(); // other setup procedures queueConn.start();
This sample creates a connection object for a topic and starts the connection:
TopicConnection topicConn = topicCF.createTopicConnection(); // other setup procedures topicConn.start();
You can also stop delivery of messages using the QueueConnection.stop method, then use start to resume delivery. While a connection is stopped, receive calls do not return with a message, and messages are not delivered to message listeners. Any calls to receive or message listeners that are in progress when stop is called, complete before the stop method returns.
With a single connection to EAServer, the message service can send and receive multiple messages. Therefore, a JMS client usually only needs one connection.
A connection for a durable topic subscriber must have a client ID associated with it so that EAServer can uniquely identify a client if it disconnects and later reconnects. You can use Jaguar Manager to set the client ID when you create the topic connection factory; see Chapter 8, "Setting up the Message Service," in the EAServer System Administration Guide. If you do not set the client ID in the connection factory, you must set it immediately after creating the connection and before performing any other action on the connection. After this point, attempting to set the client ID throws an IllegalStateException. This code fragment illustrates how to set a topic connection's client ID:
topicConn.setClientID("Client ID String");
For more information about topic subscribers, see "Creating message consumers".
To enable EAServer to asynchronously notify clients of serious connection problems, create and register an ExceptionListener. The javax.jms.ExceptionListener must implement this method:
void onException(JMSException exception);To register a listener, call the Connection::setExceptionListener method, for example:
queueConn.setExceptionListener(MyExceptionListener);If an exception occurs and a listener has been registered, EAServer calls the onException method and passes the JMSException, which describes the problem.
Once a client has established a connection with EAServer, it needs to create one or more sessions. A session serves as a factory for creating message producers, message consumers, and temporary destinations. JMS provides two types of session objects, one for queue connections and one for topic connections. To create a QueueSession object, use a previously created QueueConnection object as follows:
QueueSession queueSession = queueConn.createQueueSession(true, Session.AUTO_ACKNOWLEDGE);
To create a TopicSession object, use a previously created TopicConnection object as follows:
TopicSession topicSession = topicConn.createTopicSession(true, Session.AUTO_ACKNOWLEDGE);
When you create a session, set the first parameter to true if you want a transacted session. When you publish or send a message in a transacted session, the transaction begins automatically. Once a transacted session starts, all messages published or sent in the session become part of the transaction until the transaction is committed or rolled back. When a transaction is committed, all published or sent messages are delivered. If a transaction is rolled back, any messages produced in the session are destroyed, and any consumed messages are recovered. When a transacted session is committed or rolled back, the current transaction ends and the next transaction begins. See Chapter 2, "Understanding Transactions and Component Lifecycles" for more information about transactions.
Set the first parameter to false when you do not want a transacted session. If a client has an active transaction context, it can still determine transactional behavior, even if it does not create a transacted session.
The second parameter indicates whether the message producer or the consumer will acknowledge messages. This parameter is valid only for nontransacted sessions. In transacted sessions, acknowledgment is determined by the outcome of the transaction.
Acknowledgment mode | Description |
---|---|
AUTO_ACKNOWLEDGE | The session automatically acknowledges messages. |
CLIENT_ACKNOWLEDGE | The client explicitly acknowledges a message, which automatically acknowledges all messages delivered in the session. |
DUPS_OK_ACKNOWLEDGE | EAServer implements this the same as AUTO_ACKNOWLEDGE. |
To stop message delivery within a session and redeliver all the unacknowledged messages, you can use the Session.recover method. When you call recover for a session, the message service:
The method can be called only by a non-transacted session; it throws an IllegalStateException if it is called by a transacted session.
Create message producers for sending and publishing messages. JMS defines two message producer objects, a QueueSender, used to send messages, and a TopicPublisher, used to publish messages. To create a QueueSender object, use a previously created QueueSession object and this syntax:
javax.jms.QueueSender sender = queueSession.createSender(queue);
To create a TopicPublisher object, use a previously created TopicSession object and this syntax:
javax.jms.TopicPublisher publisher = topicSession.createPublisher(topic)
Message consumers can be either QueueReceiver or TopicSubscriber objects. Create a QueueReceiver to retrieve messages that are sent using the PTP messaging model. Use previously created Queue and QueueSession objects, as follows:
javax.jms.QueueReceiver receiver = queueSession.createReceiver(queue);
A TopicSubscriber object receives published messages and can be either durable or nondurable. A nondurable subscriber can only receive published messages while it is connected to EAServer. If you want guaranteed message delivery, make the subscriber durable. For example, if you create a durable subscription on a topic, EAServer saves all published messages for the topic in a database. If a durable subscriber is temporarily disconnected from EAServer, its messages will be delivered when the subscriber is reconnected. The messages are deleted from the database only after they are delivered to the durable subscriber.
This example illustrates how to create both durable and nondurable topic subscribers. In both cases, you need to reference previously created Topic and TopicSession objects:
// Create a durable subscriber javax.jms.TopicSubscriber subscriber = topicSession.createDurableSubscriber(topic, "subscriptionName") // Create a non-durable subscriber javax.jms.TopicScuscriber subscriber = topicSession.createSubscriber(topic);
To remove a durable topic subscription, call the TopicSession.unsubscribe method, and pass in the subscription name; for example:
topicSession.unsubscribe("subscriptionName");
You can use selectors to specify which messages you want delivered to a message queue. Once you add a selector to a queue, the message service delivers only those messages whose message topic matches the selector. You can create message selectors using Jaguar Manager--see Chapter 8, "Setting up the Message Service," in the EAServer System Administration Guide. You can also create message selectors programmatically. This example illustrates how to create a message selector and use it when you are creating a new QueueReceiver:
// Create a selector to receive only Text messages // whose value property equals 100. String selector = "value = 100 and Type = TextMessage"; // Create a queue receiver using the selector. QueueReceiver receiver = queueSession.createReceiver(queue, selector);This code sample sends two messages to the message queue we just created. The properties of the first message match those of the message queue's selector. The properties of the second message do not.
// Create and send a message whose properties match // the message queue selector. TextMessage textMsg = queueSession.createTextMessage("Text Message"); textMsg.setIntProperty("Value", 100); textMsg.setStringProperty("Type", "TextMessage"); sender.send(textMsg); // Create and send a Bytes message, whose value // property equals 200. BytesMessage bytesMsg = queueSession.createBytesMessage(); bytesMsg.setIntProperty("Value", 200); bytesMsg.setStringProperty("Type", "BytesMessage"); sender.send(bytesMsg);When we retrieve messages from the message queue, the Text message will be returned but the Bytes message will not.
Message listeners allow you to receive messages asynchronously. Once you have implemented a listener, install it on a message consumer. When a message is delivered to the message consumer, the listener can send the message to other consumers or notify one or more components.
JMS message listeners implement the javax.jms.MessageLintener interface and can be either client-side listener objects or EJB 2.0 message-driven beans (MDB). The MessageListener interface contains only the onMessage method. This example illustrates the skeleton code for a message listener:
class QueueMessageListener implements MessageListener { public void onMessage(javax.jms.Message msg) { // process message, notify component } }
You can use Jaguar Manager to install a message listener on a message queue or topic--see "Installing and configuring an MDB". You can also install a message listener within your application. First create a message consumer, see "Creating message consumers", then install the listener, using this syntax:
receiver.setMessageListener(new QueueMessageListener());
An MDB is a type of Enterprise JavaBean (EJB) specifically designed as a JMS message consumer. You can install an MDB as a message listener on a message queue or topic.
When an MDB is installed as a listener on a message consumer and a JMS message arrives, EAServer instantiates the MDB to process the message. An MDB must implement the MessageDrivenBean interface, which consists of these methods:
Method name | Description |
---|---|
ejbCreate | Creates an instance of an MDB. |
setMessageDrivenContext | Associates an MDB instance with its context, which EAServer maintains. This provides the MDB instance access to the MessageDrivenContext interface. |
ejbRemove | Notifies the MDB instance that it is being removed and should release its resources. |
An MDB instance with container-managed transactions can call these MessageDrivenContext interface methods:
Method name | Description |
---|---|
setRollbackOnly | To specify that the current transaction must be rolled back. |
getRollbackOnly | To test whether the current transaction has been marked to roll back. |
getUserTransaction | Returns the javax.transaction.UserTransaction interface, with which the MDB can set and obtain transaction status. |
Unlike other EJBs, message-driven Beans do not have a home or remote interface, and clients cannot directly access an MDB. EAServer calls the onMessage method of the javax.jms.MessageListener interface to notify an MDB that a message has been delivered to the queue or topic on which it is installed. To prevent client access to the onMessage method, this component property is set automatically when you install and configure an MDB:
com.sybase.jaguar.component.roles = onMessage(security-roles=ServiceControl)
Installing and
configuring an MDB
MyPkg/MyBeanImpl
.
topic = 'StockPrice.SY'
Tab | Description link |
---|---|
Instances | "Component properties: Instances" |
Resources | "Component properties: Resources" |
EJB References | "EJB references" |
EJB Local References | "EJB local references" |
Environment | "Environment properties" |
Resource References | "Resource references" |
Resource Environment References | "Resource environment references" |
Role References | "Configuring role references and method permissions" |
Java Classes | Chapter 28, "Configuring Custom Java Class Lists" |
Additional Files | "Component properties: Additional Files" |
EAServer includes a sample JMS client and MDB listener. When you run the JMS client, it sends a message to the message queue myQueue; the MDB listens on this queue. When the MDB receives the message, it sends the message to the queue yourQueue, and the client receives the message from yourQueue. The message queues are created automatically when you run the JMS client. All the files you need to run the sample are contained in the /html/classes/MDBSample.jar file.
To run the JMS client, you first need to configure the message service--see Chapter 8, "Setting up the Message Service," in the EAServer System Administration Guide--then import the JAR file and install the MDB component. When you import the JAR file, the JMS client and the MDB implementation files are extracted to the /html/classes/Sample/MDBSample directory. The MDB's component properties have been preconfigured in the /Repository/Component/MDBSample/MDBBeanImpl.props file.
Importing the JAR file, installing the MDB, and
running the JMS client
%JAGUAR%\html\classes\Sample\MDBSample\run.batOn UNIX:
$JAGUAR/html/classes/Sample/MDBSample/run.sh
For information about Enterprise JavaBeans, see these chapters:
To create a message, you must first create an instance of either a queue or topic session. See "Creating sessions" for details. To create a text message, use this syntax:
// Create a text message using a QueueSession javax.jms.TextMessage queueTextMsg = queueSession.createTextMessage("Text message"); // Create a text message using a TopicSession javax.jms.TextMessage topicTextMsg = topicSession.createTextMessage("Text message");
EAServer supports six message types that a message producer can send or publish. Table 29-1 describes the message types and the javax.jms.Session interface APIs used to create instances of each.
Message type | Create message API | Comments |
---|---|---|
Plain | Session.createMessage | Creates a message without a message body. |
Text | Session.createTextMessage | Creates a message that can contain a string in the message body. |
Object | Session.createObjectMessage | Creates a message that can contain a serializable Java object in the message body. |
Stream | Session.createStreamMessage | Creates a message that can contain a stream of Java primitives in the message body. Fill and read the message sequentially. |
Map | Session.createMapMessage | Creates a message whose body can contain a set of name-value pairs where names are Strings and values are Java primitive types. |
Bytes | Session.createBytesMessage | Creates a message that can contain a stream of uninterpreted bytes in the message body. |
To improve interoperability with non-Java clients or components and to improve message receivers' ability to filter messages, Sybase recommends that you use either plain messages or text messages. Selectors allow you to filter messages based on the text in the message properties. You cannot filter messages based on the text in the message body. Therefore, message text in the message properties, instead of the message body, enables the message receivers to filter messages more efficiently.
For more information about the message types, see the JMS API documentation .
To send a message, you must specify the destination message queue. The message service notifies listeners that are registered for the queue and the message remains in the queue until it is received and acknowledged.
Figure 29-1: Send message flow
Figure 29-1illustrates the message flow that occurs when a client or component sends a message.
This example notifies a client of a completed order; it creates a new message, constructs the message text, and sends the message to the client's queue:
public void notifyOrder(QueueSession qSession, Queue queue, int orderNo, String product) { String time = new java.util.Date().toString(); String text = "Order " + orderNo + " for product " + product + " was completed at " + time; javax.jms.QueueSender sender = qSession.createSender(queue); javax.jms.TextMessage textMsg = qSession.createTextMessage(text); textMsg.setStringProperty("ProductDescription", product); textMsg.setIntProperty("OrderNumber", orderNo); sender.send(textMsg); }
When you publish a message, a copy is sent to all topic subscribers that have a message selector registered with the specified topic. Figure 29-2 illustrates the message flow when a client or component publishes a message.
Figure 29-2: Publish message flow
This example publishes a message that notifies clients of changes in a stock value; it creates the message text, creates a TopicPublisher and the message using the TopicSession object, and publishes the message:
public void notifyStockValue(TopicSession tSession, Topic topic, String stock, double value) { String time = new java.util.Date().toString(); String text = time + ": The stock " + stock + " has value " + value; // Create the publisher and message objects. javax.jms.TopicPublisher publisher = tSession.createPublisher(topic); javax.jms.TextMessage textMsg = tSession.createTextMessage(text); // Publish a non-persistent message with a priority of 9 and a // lifetime of 5000 milliseconds (5 seconds) publisher.publish(textMsg, DeliveryMode.NON_PERSISTENT, 9, 5000); }
To publish a persistent message using the default priority (4) and timeout (never expires) values, use this syntax:
publisher.publish(textMsg);
You can receive messages either synchronously or asynchronously. To receive messages synchronously (get all the messages at one time), call the receive method for the message consumer. The following code samples illustrate how to receive all the messages from a queue, using three different timeout options:
// Get all the messages from the queue. If none exists, // wait until a message arrives. javax.jms.TextMessage queueTextMsg = (javax.jms.TextMessage) receiver.receive(); // Get all the messages from the queue. If none exists, // wait 5000 milliseconds (5 seconds) or until a message // arrives, whichever comes first. javax.jms.TextMessage queueTextMsg = (javax.jms.TextMessage) receiver.receive(5000); // Get all the messages from the queue. If none exists, // return NULL. javax.jms.TextMessage queueTextMsg = (javax.jms.TextMessage) receiver.receiveNoWait();
To receive messages asynchronously, implement a message listener and install it on the message consumer, either a topic or a queue. See "Implementing and installing message listeners".
For information about creating message queues and topics, see "Creating message consumers".
You can look at messages in a queue without removing them using the QueueBrowser interface. You can browse through all the messages in a queue, or through a subset of the messages. To browse through a queue's messages, create an instance of a QueueBrowser object using a previously created QueueSession object. To create a browser for viewing all the messages in a queue, call createBrowser and pass the message queue:
javax.jms.QueueBrowser qbrowser = queueSession.createBrowser(queue);To create a browser for viewing a subset of the messages in a queue, call createBrowser and pass the message queue and a message selector string:
javax.jms.QueueBrowser qbrowser = queueSession.createBrowser(queue, selector);For information about message selectors, see "Filtering messages using selectors".
Once you have access to the QueueBrowser object, call getEnumeration, which returns an Enumeration that allows you to view the messages in the order that they would be received:
java.util.Enumeration enum = qbrowser.getEnumeration();
To help debug your JMS client application, you can enable
tracing by setting the com.sybase.jms.debug
property
to true in the InitialContext object. When you
enable tracing, diagnostic messages are printed in the console window.
By default, tracing is disabled. This code sample illustrates how
to set the tracing property:
Properties prop = new Properties(); prop.put("com.sybase.jms.debug", "true"); javax.naming.Context ctx = new javax.jms.InitialContext(prop);
The JMS server allocates resources for each of these objects: connections, sessions, message consumers, and message producers. When you no longer need one of these objects, you should close it to release its resources and help the application run more efficiently. To release each object's resources, EAServer provides these methods:
EAServer does not support these JMS interface methods:
JMS interface | Method |
---|---|
javax.jms.Session |
|
javax.jms.QueueConnection |
|
javax.jms.TopicConnection |
|
In addition, EAServer does not support these JMS interfaces:
EAServer supports the XA interfaces that are required
to support two-phase commit and the XA transaction coordinator
for JMS clients and components.
Copyright © 2002 Sybase, Inc. All rights reserved. |
![]() |