BEA Logo BEA WebLogic Server Release 5.0

  Corporate Info  |  News  |  Solutions  |  Products  |  Partners  |  Services  |  Events  |  Download  |  How To Buy

Using WebLogic JMS

I. Introduction
WebLogic JMS overview
JMS messaging models
Connections
Sessions
Destinations -- Queues and Topics
MessageProducers and MessageConsumers
Messages
Configuring WebLogic JMS
Create a JMS database
Define a JMS connection pool
Define JMS ConnectionFactories
Define JMS Queues and Topics
Create database tables for transacted, durable subscribers

II. The JMS API

III. Implementing with WebLogic JMS
Import statements
Concurrent use of JMS objects
Using transactions with JMS
Setting up a JMS client
Sending messages
Receiving messages
Using message header fields
Using message property fields
Using message selectors
Developing a server session pool application
Setting up ACLs for JMS in the WebLogic Realm
JMS in a clustered environment

IV. Change history

Other related documents
Installing WebLogic (Getting Started)
Writing a WebLogic client application (Documentation)
Writing a server-side application (Documentation)
Using WebLogic Server Clusters
Using WebLogic Events
Developers Guides (Documentation)
API Reference Manual (Documentation)
Code examples

I. Introduction

WebLogic JMS overview

Java Message Service (JMS) allows Java programs to exchange messages with other Java programs sharing a messaging system. A messaging system accepts messages from "producer" clients and delivers them to "consumer" clients.

Messaging systems, sometimes called Message-Oriented Middleware (MOM) products, enable Java clients to use their services by supplying a Java layer called a JMS Provider, which implements JMS for the specific product.

WebLogic JMS implements the JavaSoft JMS specification version 1.0.1. WebLogic JMS includes a full-featured messaging system, which can be configured by setting properties in the weblogic.properties file, from the WebLogic Console, or programmatically, using the JMS interfaces. Configuration properties are described in the Configuring WebLogic JMS section of this document.

You can use WebLogic JMS with the other WebLogic Server APIs and facilities, such as Enterprise Java Beans (EJBeans), JDBC connection pools, Servlets, and RMI. JMS operations can participate in transactions with other Java APIs that use the Java Transaction API.

WebLogic JMS has some similarities to WebLogic Events, a proprietary publish/subscribe (pub/sub) messaging system implemented prior to the release of the JMS specification. Below is a summary of the main areas in which these systems differ. Refer to the appropriate Developer Guide for more information on these features.

Feature WebLogic JMS WebLogic Events
messaging models point-to-point and pub/sub pub/sub only
guaranteed message delivery order supported not supported
message persistence supported not supported
message selectors SQL-like selector Evaluate class
relative system overhead heavyweight lightweight
topic hierarchy not supported supported

JMS messaging models

JMS supports two messaging models: point-to-point (PTP) and publish/subscribe. The terms "message producer" and "message consumer" describe clients that send and receive messages in either of the models, although each model has its own specific terms for producers and consumers.

Point-to-point messaging
The point-to-point messaging model is based on message Queues. A QueueSender (producer) sends a message to a specified Queue. A QueueReceiver (consumer) receives messages from the Queue. A Queue can have multiple QueueSenders and QueueReceivers, but an individual message can only be delivered to one QueueReceiver. If multiple QueueReceivers are listening for messages on a Queue, WebLogic JMS determines which will receive the next message. If no QueueReceivers are listening on the Queue, messages remain in the Queue until a QueueReceiver attaches to the Queue.

Publish/subscribe messaging
The publish/subscribe messaging model is organized around Topics. TopicPublishers (producers) send messages to a Topic. TopicSubscribers (consumers) retrieve messages from a Topic. Unlike the point-to-point model, many TopicSubscribers can receive the same message.

The PTP and pub/sub messaging models are very similar. They are implemented with classes that extend common root classes. For example, the PTP javax.jms.Queue class and the pub/sub javax.jms.Topic class each extend a common javax.jms.Destination class.

Messages can be NON-PERSISTENT or PERSISTENT. NON-PERSISTENT messages are not stored in a database, and may be lost during a failure. JMS specifies that a NON-PERSISTENT message will be delivered at most once. A PERSISTENT message is not considered sent until it has been stored in the database. JMS guarantees that PERSISTENT messages are delivered at least once. WebLogic JMS writes PERSISTENT messages to database via a JDBC connection pool assigned to JMS in the weblogic.properties file.

JMS objects

You use the javax.jms client APIs in your JMS client applications. You retrieve the initial JMS object, a ConnectionFactory, using WebLogic JNDI. The Topics and Queues your application uses are also retrieved through JNDI. An administrator defines ConnectionFactories, Topics, and Queues in the weblogic.properties file, and the WebLogic Server adds them to the JNDI space during startup. WebLogic JMS provides default connection factories, so it is not necessary to define connection factories for most applications. See Configuring WebLogic JMS for information about defining JMS objects in the weblogic.properties file.

The steps for setting up a JMS client are presented in Implementing with WebLogic JMS. The next sections describe the behaviors and features of the main JMS objects.

Connections

A Connection represents an open connection to the messaging system. It creates the associated server-side and client-side objects that manage the state of the JMS connection. It also creates Sessions, objects that manage the active exchange of messages between the client and the messaging system.

Connections are created by a ConnectionFactory obtained with a JNDI lookup. The ConnectionFactory and Connection classes for the two JMS messaging models extend ConnectionFactory and Connection:

Point-to-point:
javax.jms.QueueConnectionFactory
javax.jms.QueueConnection

Publish/subscribe:
javax.jms.TopicConnectionFactory
javax.jms.TopicConnection

In the WebLogic Server, JMS traffic is multiplexed with the other WebLogic services on the client connection to the server. No additional TCP/IP connections are created for JMS. Servlets and other server-side objects may also obtain JMS Connections.

A Connection may contain a client identifier that is used for clients with durable subscriptions. Durable subscriptions are a feature unique to the pub/sub messaging model, so client IDs are only of consequence with TopicConnections; QueueConnections also contain Client IDs, but JMS does not use them. The client ID can be supplied in two ways:

  • The preferred method, according to the JMS specification, is to configure the ConnectionFactory with the client ID. For WebLogic JMS, this means adding a separate TopicConnectionFactory definition to the weblogic.properties file for each client ID. Clients then look up their own TopicConnectionFactory in JNDI and use them to create Connections containing their own client IDs.

  • Alternatively, clients can set their client ID in the Connection after it is created by calling the setClientID() method of the Connection. If you use this alternative, you can use the default TopicConnectionFactory and avoid editing the weblogic.properties file. However, clients with durable subscriptions must take care to call setClientID() after creating their TopicConnection.

Note that the JMS client ID is not equivalent to the WebLogic Server username used to authenticate a user in the WebLogic security realm. You can, of course, use the WebLogic username for the JMS client ID if it is appropriate for your JMS application.

A newly created Connection is stopped -- no messages are received until the Connection is started. Typically, the other JMS objects are set up to handle messages before the Connection is started. Messages may be produced on a stopped Connection, but cannot be delivered to a stopped connection.

Sessions

A JMS Session is a context for producing and consuming messages. A Session creates MessageProducers and MessageConsumers, the objects that transmit and receive messages. A Session can also create a temporary Queue or Topic, which exists only for the duration of the session. Sessions are created by the JMS Connection. QueueConnections create QueueSessions and TopicConnections create TopicSessions.

Sessions can be transacted. A transacted Session has one active transaction at a time. Messages sent or received during a transaction are treated as an atomic unit. If the client rolls back the transaction, the messages it received during the transaction are not acknowledged and messages it sent are discarded. When a client commits a transaction, all of the messages received during the transaction are acknowledged by the messaging system and messages it sent are accepted for delivery. JMS can participate in distributed transactions with other Java services that use the Java Transaction Service (JTS), such as EJB. The javadocs for JTS are on the JavaSoft website.

To guarantee that messages are delivered, Sessions retain received messages until the receiving client acknowledges them. When a Session is transacted, the acknowledgement is automatic -- a side effect of committing the transaction.

In non-transacted Sessions, the client creating the Session selects one of three acknowledgement modes. The most efficient in terms of resource usage is DUPS_OK_ACKNOWLEDGE, which allows the session to "lazily" acknowledge receipt on behalf of the client. This mode can result in duplicate messages if there is a failure and you should avoid it if your application is not able to handle duplicates. If you create a Session with AUTO_ACKNOWLEDGE, the Session acknowledges receipt of a message when the client method that receives the message has returned from processing it. A Session created with CLIENT_ACKNOWLEDGE relies on the client to call an acknowledge method on a received message. Once this method is called, the Session acknowledges all messages received since the last acknowledgement. This allows the client to receive and process a batch of messages and then acknowledge them all with one call.

Destinations -- Queues and Topics

JMS Queues and Topics extend javax.jms.Destination. Queues manage messages for the point-to-point messaging model and Topics manage messages for the publish/subscribe model.

Client programs retrieve Queues and Topics with JNDI. Like ConnectionFactories, Queues and Topics can be configured in the weblogic.properties file, in the WebLogic Console, or programmatically by using JMS interfaces. As part of their configuration in the weblogic.properties file and in the WebLogic Console, Queues and Topics are automatically bound to JNDI names. If configured programmatically, the message destinations must be bound explicitly using the appropriate JNDI interface.

Clients can also create temporary Queues and Topics that exist only for the duration of the JMS connection in which they are created.

On the client side, Queue and Topic objects are handles to the object on the server. Their only methods simply return their names. To access them for messaging, you create message producers and consumers that attach to them.

MessageProducers and MessageConsumers

The Session object creates message senders and receivers attached to Queues and Topics. The message sender/receiver objects are subclassed from the MessageProducer and MessageConsumer classes. A MessageProducer sends messages to a Queue or Topic. A MessageConsumer receives messages from a Queue or Topic. The point-to-point and pub/sub messaging models have different names for these objects, as this table shows:

Base class Point-to-point Publish/subscribe
MessageProducer QueueSender TopicPublisher
MessageConsumer QueueReceiver TopicSubscriber

The QueueReceiver and TopicSubscriber objects can be created with a message selector that specifies which messages are to be delivered to the client. The selector is a String patterned after the WHERE clause of an SQL SELECT statement. The selector filters messages based on values of properties that are set in the message. Selectors are discussed in Using message selectors.

The client can request that messages be delivered synchronously or asynchronously. If synchronous, the client calls a receive() method on the QueueReceiver or TopicSubscriber and blocks until a message arrives. If asynchronous, the client provides an implementation of the JMS MessageListener interface. When a message arrives, the onMessage() method of that implementation is called to receive the message.

With the point-to-point model, clients have the option of browsing queues by creating a QueueBrowser object in their QueueSession. This object produces an enumeration of the messages in the queue at the time the QueueBrowser is created -- a snapshot. The client can view the messages in the queue, but the messages are not considered "read" or removed from the queue.

There can be multiple Sessions with QueueReceivers for the same Queue. However, a message can only be delivered to one QueueReceiver. JMS does not specify which QueueReceiver will receive a message when there are multiple candidates.

With the publish/subscribe model, messages are delivered to multiple TopicSubscribers. TopicSubscribers can be non-durable or durable. With non-durable TopicSubscribers, messages are only delivered to clients with an active Session. Messages sent to a Topic while a client is not listening are never seen by that client. With a durable subscription, WebLogic JMS stores messages in a database until the message has been delivered to all durable subscribers or has expired. The subscriptions, too, are stored in the database. The client can modify a durable subscription by creating a new one with the same client ID. To delete a durable subscription, the TopicSession has an unsubscribe() method.

Another particular feature of the publish/subscribe model is that a client can have a JMS connection in which it both publishes to and subscribes to the same Topic. Since Topic messages are delivered to all subscribers, the client can receive messages it has published itself. A JMS client can set a NoLocal attribute on the TopicSubscriber to prevent WebLogic JMS from delivering messages sent from its own connection.

Messages

A JMS message (javax.jms.Message) contains a set of standard header fields, application-specific properties, and a message body.

Message header fields

JMS Messages contain a standard set of headers that are always transmitted with the message. They are available to message consumers that receive messages and some fields can be set by the message producers that send messages.

JMSDestination
The JMSDestination field contains the destination (Queue or Topic name) where the message is to be delivered. The value of the field is set when the message is sent via the client's message producer. Any previous value is ignored.

JMSDeliveryMode
The JMSDeliveryMode field contains either NON_PERSISTENT or PERSISTENT. NON_PERSISTENT messages are not stored in the JMS database, so they can be lost following a failure. A PERSISTENT message is stored in the JMS database when the message is sent. The send operation is not considered successful until the message has been written to the database. A NON_PERSISTENT message is guaranteed to be delivered at most once. A PERSISTENT message is guaranteed to be delivered once, and only once.

JMSTimestamp
The JMSTimestamp field contains the time the message was transferred from the client's MessageProducer to WebLogic JMS. The value stored in the field is a Java millis time value. The timestamp is written in the message when WebLogic JMS accepts the message for delivery, which may be later than when the client sends the message.

JMSCorrelationID
The JMSCorrelationID field can be used to link messages. The field can hold a WebLogic JMS message ID, an application-specific string, or a byte array. A common use for this field is to set up requests and responses. When a client sends a message, it remembers its message ID. When a receiving client receives the message, it copies the message ID into the JMSCorrelationID field of a response message that it sends to the first client. All JMS message IDs start with an ID: prefix. If the JMSCorrelationID is used for some other application-specific string, it must not begin with the ID: prefix.

The byte[] JMSCorrelationID is available for external JMS providers and is not supported by WebLogic JMS. Calling setJMSCorrelationIDAsBytes() throws a java.lang.UnsupportedOperationException.

JMSReplyTo
A client stores a Queue or Topic name in the JMSReplyTo field before sending a message. It is up to the receiving client to decide whether to reply to the message. The JMSReplyTo field can also be NULL, which may have a semantic meaning to the receiving client, such as a notification event.

JMSRedelivered
The JMSRedelivered field is only of interest to a receiving client. If set, then JMS may have delivered the Message earlier. It can mean that the client has already received the message, but did not acknowledge it, or that the session's recover() method was called to restart the session beginning after the last acknowledged message. See the section on Sessions for more information.

JMSType
The JMSType field contains the message type identifier set by the sending client. The JMS specification allows some flexibility with this field in order to accommodate diverse JMS providers. Some messaging systems allow applications to formally define message types within the messaging system. For those systems, the JMSType field could contain a message type ID that provides access to the stored type definitions. WebLogic JMS does not restrict the use of this field.

JMSExpiration
The client can set a time-to-live value on a message when it is sent. WebLogic JMS calculates the JMSExpiration value as the sum of the client's time-to-live and the current GMT. If the client specifies time-to-live as 0, JMSExpiration is set to 0, which means the message never expires.

WebLogic JMS periodically scans the JMS database and deletes any persistent messages that have expired, including messages waiting to be delivered to durable subscribers.

JMSPriority
JMS defines ten priority levels, 0 to 9. The lowest priority is 0. The sending client sets the priority level in the JMSPriority field before sending the message. Levels 0-4 are to be considered gradations of normal priority and level 5-9 are to be considered gradations of expedited priority. JMS does not require that JMS providers honor message priority, but only to "do their best." Currently, WebLogic JMS does not honor message priority. However, a simple priority mechanism can be implemented using message property fields.

Message property fields

The property fields section of a Message contains additional header fields added by the sending client. The properties are standard Java name/value pairs. The values can be boolean, byte, short, int, long, float, double, and String data types.

Although message property fields may be used for application-specific purposes, JMS provides them primarily for use in message selectors. Message selectors allow clients to choose the messages they want to receive by providing a simple query string. The sending client can set message property fields to describe or classify a message in a standardized way so that interested receivers can choose it with their message selector. Since message selectors cannot reference the contents (body) of a message, some fields from the message contents may be duplicated as message property fields.

Message body

The Message body contains the message content. JMS defines six message types:

javax.jms.Message
Contains only message headers and properties, but no message body. All other message types extend Message.

javax.jms.BytesMessage
Contains a stream of bytes, which must be understood by the sender and receiver. The access methods for this message type are stream-oriented readers and writers based on the java.io.DataInputStream and java.io.DataOutputStream.

javax.jms.StreamMessage
Similar to a BytesMessage, except that only Java primitive types are written to or read from the stream.

javax.jms.ObjectMessage
Holds a single serializable Java object.

javax.jms.MapMessage
Holds a set of name/value pairs where names are Strings and values are Java primitive types. The pairs can be read sequentially or randomly by specifying a name.

javax.jms.TextMessage
Holds a single String. In addition to applications where it is natural to represent messages as text, the TextMessage can carry more complex data represented with XML.

Top

Configuring WebLogic JMS

The WebLogic Server has built-in defaults for JMS, so you can use some JMS features without any special configuration. To use persistent messages or durable subscriptions, or to set up custom JMS applications the WebLogic administrator can perform any of these configuration tasks:

  1. Create a database for JMS.

  2. Define a JDBC connection pool for the JMS database.

  3. Define connection factories.

  4. Define JMS Topics and Queues.

  5. Create database tables for transacted, durable subscribers.

Once these steps are completed, JMS clients can begin sending and receiving messages through the JMS API.

You can use the WebLogic Console to perform most of this work. The Console lists JMS objects that have been created, allows you to create factories, Topics, and Queues. You can also initialize a database for JMS, which creates the JMS system tables.

The weblogic.properties file included in the WebLogic distribution has a section for WebLogic JMS properties, including properties you need to execute the JMS examples. Before you start the WebLogic Server, search the properties file for "WEBLOGIC JMS" and uncomment the properties for the examples you want to try. Also, you need a JDBC connection pool for some examples. Search for "DEMO CONNECTION POOL" to find the properties that define a connection pool on a Cloudscape database that is initialized for JMS.

Top

Create a JMS database

JMS stores persistent messages in a database, accessed through a JDBC connection pool, which you define in the weblogic.properties file. If you do not use persistent messages, you do not need to assign a connection pool for JMS.

The JMS connection pool can use any database that you can access through a JDBC driver. WebLogic provides JDBC drivers for Oracle, Microsoft SQL Server, and Informix Dynamic Server. If the database can store Java objects directly (Cloudscape, for example) then JMS stores messages in the database as Java objects. Otherwise, Message objects are serialized and stored in a blob (or database vendor equivalent type) column.

Important: If you combine JMS and EJBs in an application, and require operations on both within the same transaction, both must use the same database connection pool. See Using transactions with JMS for more information about developing JMS applications that use transactions.

The JMS examples are set up to work with the Cloudscape Java database. An evaluation version of Cloudscape is included in your WebLogic distribution and a "demo" database containing the JMS system tables is provided.

The JMS database contains five system tables, used internally by JMS. You can create the tables using the WebLogic Console or using the utils.Schema utility. The utils.Schema utility is a Java program that takes command line arguments to specify the JDBC driver, database connection information and the name of a file containing the semicolon-terminated SQL Data Definition Language (DDL) commands that create the database tables. By convention, the DDL file has a .ddl extension. We have included DDL files for Cloudscape, MSSQL Server, IBM DB2, and Oracle databases.

To execute utils.Schema, your CLASSPATH must contain the weblogic/classes directory. Here is the syntax for the utils.Schema command:

  java utils.Schema url JDBC_driver [options] DDL_file

url
The database connection URL. This is a colon-separated URL as defined by the JDBC specification.

JDBC_driver
The full package name of the JDBC Driver class.

options
If the database requires a username and password, you supply them with options, like this:
  -u username -p password

You can also include the -verbose option, which causes utils.Schema to echo the SQL commands as they are executed.

DDL_file
The full pathname of a text file containing the SQL commands to execute. Lines beginning with pound signs (#) are comments. An SQL command can span several lines and is terminated with a semicolon (;). The weblogic/classes/jms/ddl directory contains JMS DDL files for Cloudscape, MSSQL Server, IBM DB2, and Oracle databases. You can copy and edit one of these files if you want to use a different database.

Here is a utils.Schema command to create the JMS tables in an Oracle server named DEMO, with the username "scott" and password "tiger":

  java utils.Schema jdbc:weblogic:oracle:DEMO \
    weblogic.jdbc.oci.Driver -u scott -p tiger -verbose \
    /weblogic/classes/jms/ddl/jms_oracle.ddl

With the Cloudscape database, no username or password are required. However, the Cloudscape JDBC driver uses the cloudscape.system.home system property to find the directory containing its database files. You supply the value for this property with the -D Java command option. Also, you must have the Cloudscape classes in your CLASSPATH. If you are using the evaluation version of Cloudscape, add weblogic/eval/cloudscape/lib/embeddedJBMS.zip to your CLASSPATH before you execute utils.Schema. Here is the utils.Schema command for Cloudscape:

  java -Dcloudscape.system.home=/weblogic/eval/cloudscape/data \
    utils.Schema jdbc:cloudscape:demoPool;create=true \
    COM.cloudscape.core.JDBCDriver -verbose \
    /weblogic/classes/jms/ddl/jms_cloudscape.ddl

The Cloudscape JDBC URL specifies the demo database, which is included in the WebLogic distribution. The JMS tables have already been created in this database. Running the utils.Schema command will drop the tables and recreate them. If you are using JMS and EJB together in transactions, you should create the JMS tables in the same database you use for EJB persistence. You define a single JDBC connection pool and then assign both EJB and JMS to that connection pool, as described in the next section.

The property, weblogic.jms.tableNamePrefix can be used to specify a prefix that is added to the table name when WebLogic JMS accesses the JMS tables. This is usefull when the DBMS requires a fully qualified name when accessing tables. For example, the following weblogic.properties entry:

  weblogic.jms.tableNamePrefix=master.dbo.
would make WebLogic JMS reference master.dbo.JMSMessages rather than JMSMessages when accessing the JMSMessages table. Note that the trailing dot is necessary and the prefix should be of a form that will result in a valid table name when prepended to the JMS table name.

The weblogic.jms.tableNamePrefix can also be used to differentiate the JMS tables of two different WebLogic Servers. Therefore allowing the tables to be housed within a single DBMS. For example, server A's weblogic.properties file could contain the entry:

  weblogic.jms.tableNamePrefix=A
and server B's weblogic.properties file could contain the entry:
  weblogic.jms.tableNamePrefix=B
In this scenerio, server A's JMS would reference AJMSTableName (for example, AJMSMessage) and server B's JMS would reference BJMSTableName (for example, BJMSMessage). Obviously, this scenerio would require the DDL files used to create the JMS tables to be modified to create table names that are consistent with the established prefixes.

Top

Define a JMS connection pool

Define a JDBC pool in the weblogic.properties file to provide access to the database. If you do not use persistent messages with JMS, you do not need to set up a connection pool for JMS. If you have already set up a pool for EJB persistence, you do not need to add another, but you must set the weblogic.jms.connectionPool shown below to the name of your EJB connection pool.

Here is an example JDBC connection pool definition using the Cloudscape JDBC driver:

  weblogic.jdbc.connectionPool.demoPool=\
        url=jdbc:cloudscape:demo;create=true;autoCommit=false,\
        driver=COM.cloudscape.core.JDBCDriver,\
        initialCapacity=1,\
        maxCapacity=5,\
        capacityIncrement=2

  weblogic.allow.reserve.weblogic.jdbc.connectionPool.demoPool=everyone

  weblogic.jms.connectionPool=demoPool

The name of the connection pool is "demoPool." The url parameter gives the JDBC driver's connection URL. "demo" is the name of the Cloudscape database you created for JMS. The driver parameter specifies the JDBC driver class, which must be in the CLASSPATH of the WebLogic Server. This connection pool is created when the WebLogic Server starts, with one active database connection. As more connections are required, connections are added to the pool 2 at a time until the maximum of 5 connections is reached.

The weblogic.allow property permits all users to get connections from the connection pool. You can restrict this access control list (ACL) if you choose. The WebLogic "system" user and any user who sends JMS messages must be included in the list.

Top

Define JMS ConnectionFactories, Queues, and Topics

ConnectionFactories allow JMS clients to create JMS connections. They are configurable so that they create connections with predefined attributes. The JMS specification classifies ConnectionFactories, Queues, and Topics as "administered" objects. They are configured by the messaging system administrator and added to the JNDI namespace to allow access to JMS clients.

To define a WebLogic JMS ConnectionFactory, add a weblogic.jms.connectionFactoryName property:

  weblogic.jms.connectionFactoryName.factoryName=jndiName

The factoryName part of the property is a virtual name for this ConnectionFactory. The jndiName part is the string clients use to look up the ConnectionFactory in JNDI. The jndiName parameter begins with the string "jms.connection".

WebLogic JMS provides two default factories, javax.jms.QueueConnectionFactory and javax.jms.TopicConnectionFactory. Since you can use the default connection factories, you usually do not have to configure connection factories.

A ConnectionFactory can, however, be customized with three arguments: ClientID, DeliveryMode, and TransactionTimeout.

ClientID
The ClientID argument sets the client ID for a durable subscription. Since only one JMS connection can use a durable subscription at a time, you would define a separate ConnectionFactory for each durable client. Alternatively, you can use the default ConnectionFactory, which does not specify a ClientID. Then clients can create durable subscriptions by specifying unique client IDs to the createDurableSubscription() method.

DeliveryMode
The delivery mode can be persistent or non_persistent. When you set the delivery mode to persistent, the messages sent from connections created by the ConnectionFactory are saved in the database by default. You can set the delivery mode on individual Messages, overriding the delivery mode specified for the Connection. a different delivery mode for messages. The delivery mode for Connections created by the the default connection factories is non_persistent.

TransactionTimeout
JMS transacted Sessions use a chained transaction mode -- a new transaction is begun whenever a transaction is committed or rolled back. TransactionTimeout specifies how many seconds WebLogic Server waits for a transaction to complete before it rolls back the transaction.

If you want to supply any of the ConnectionFactory arguments, add the following property:

weblogic.jms.connectionFactoryArgs.factoryName=\
  ClientID=clientID,\
  DeliveryMode=mode,\
  TransactionTimeout=seconds

The factoryName has the same value as in the associated weblogic.jms.connectionFactoryName property.

Top

Define JMS Queues and Topics

The JMS Queues and Topics that clients access may be defined in the weblogic.properties file with weblogic.jms.queue and weblogic.jms.topic properties. (You can also define them with JMS APIs or in the WebLogic Console.) Here is the syntax for these properties:

  weblogic.jms.queue.queueName=jndiName
  weblogic.jms.topic.topicName=jndiName

The queueName or topicName part is the name of the Queue or Topic property. The jndiName is the string that clients use to look up the Queue or Topic in the JNDI namespace.

The Queue and Topic used by several of the JMS examples are added to the JNDI namespace by default. The jndiName of the example Queue is javax.jms.exampleQueue. The jndiName of the example Topic is javax.jms.exampleTopic.

Top

Create database tables for transacted, durable subscribers

This configuration task is relevant, only if your system uses persistent message delivery and may contain multiple, active, transacted, durable subscribers.

Internally, WebLogic JMS uses JMSMessageQueue tables to store the state of durable subscribers. By default, WebLogic JMS uses one table for all active durable subscribers. If the DBMS used as the backing store for JMS does not support row-level locking, this can lead to deadlocks in the system. MSSQL Server and Cloudscape do not support row-level locking. To avoid deadlocks, you can configure WebLogic JMS to use a separate JMSMessageQueue table for the transacted operations of each active durable subscriber by performing the following steps:

  1. Create additional JMSMessageQueue tables in the database.

  2. Define the maximum number of active, transacted, durable subscribers within the weblogic.properties file.

Create additional JMSMessageQueue tables

The DDL files shipped in the weblogic/classes/jms/ddl directory can be modified to create additional JMSMessageQueue tables. Note that the number of tables created will serve as an upper limit to the number of active, transacted, durable subscribers allowed in your system. You should choose this number accordingly.

The following is the DDL syntax used to create the tables in a MSSQL Server database. If you are using another database, refer to the appropriate DDL file for the correct syntax.

CREATE TABLE JMSMessageQueueTx<num> 
     (consumerId int, messageId NUMERIC(12), state int);
where <num> is 0 to N-1 and N is the number of tables desired.

For example, if you want to create three tables, you must add the following lines to your DDL file:

CREATE TABLE JMSMessageQueueTx0 
     (consumerId int, messageId NUMERIC(12), state int);
CREATE TABLE JMSMessageQueueTx1 
     (consumerId int, messageId NUMERIC(12), state int);
CREATE TABLE JMSMessageQueueTx2 
     (consumerId int, messageId NUMERIC(12), state int);

Define the maximum number of active, transacted, durable subscribers

Within the weblogic.properties file, you must define the maximum number of active, transacted, durable subscribers with the weblogic.jms.maxTransactedDurableSubscribers property. This value should be equal to the number of JMSMessageQueue tables created. For example, if you have created three tables, the following line must be added to the weblogic.properties file:
  weblogic.jms.maxTransactedDurableSubscribers=3
The default value for this property is zero, which indicates that all transactions will be done against a single JMSMessageQueue table. Again, this default behavior is fine for DBMSs that support row-level locking.

Top

II. JMS API

WebLogic JMS implements the JavaSoft JMS 1.0.1 specification. The JMS API documentation is available on the JavaSoft website.

Top

III. Implementing with WebLogic JMS

Import statements
Concurrent use of JMS objects
Using transactions with JMS
Setting up a JMS client
Sending messages
Receiving messages
Using message header fields
Using message property fields
Using message selectors
Developing a server session pool application

Import statements

WebLogic JMS uses the JavaSoft JMS API, javax.jms. You also need JNDI with JMS, and if you use transactions you need the JTS classes. Here are the basic imports for JMS applications:

  import java.util.*;
  import javax.naming.*;
  import javax.jms.*;
  import javax.jts.*;

WebLogic JMS provides one public API for use with server session pools, an optional application server facility described in the JMS specification. If you implement a server session pool application, add this package to your import list:

  import weblogic.jms.ServerSessionPoolFactory.*

Concurrent use of JMS objects

JMS Connections, ConnectionFactories, Topics, and Queues support concurrent use. Other JMS objects -- including QueueSenders and QueueReceivers, TopicPublishers and TopicSubscribers, and Sessions -- can be accessed by only one thread at a time.

Using transactions with JMS

To support transactions, JMS uses Java Transaction Service (JTS), which is implemented in the WebLogic Server. There are two ways to use transactions with JMS. If you are using only JMS in your transactions, you can create a transacted JMS session. If you are mixing other operations, such as EJB, with JMS operations, you should use a JTS UserTransaction in a non-transacted JMS session.

Transacted JMS sessions
Transactions in JMS transacted sessions are chained -- whenever you commit or roll back a transaction, another transaction automatically begins. The JMS Connection has a TransactionTimeout value that specifies the number of seconds a transaction can run before it is timed out. When a transaction times out, it is rolled back. The default TransactionTimeout is 3600 seconds (one hour). If you have long-running transactions you might need to increase this value to allow your transactions to complete.

JTS UserTransactions
If you are using JMS and EJBs, or have already started a user transaction, WebLogic JMS respects the existing UserTransaction. If you create a JMS transacted session when you already have a UserTransaction, an exception is thrown when you call any transaction method on the JMS Session object.

To combine JMS and EJB operations in a transaction, you can start a transaction from an EJB or by getting a javax.jts.UserTransaction with a JNDI lookup. Here is some code to show how to set up a client for mixed EJB and JMS operations in a transaction:

  public final static String JNDI_FACTORY=
               "weblogic.jndi.WLInitialContextFactory";
  public final static String JTS_USER_XACT=
               "javax.jts.UserTransaction";
  Context ctx = new InitialContext();
  UserTransaction xact = ctx.lookup(JTS_USER_XACT);
  // start a transaction
  xact.begin();
  //
  // perform some JMS and EJB operations
  //
  // commit the transaction
  xact.commit()

Setting up a JMS client

JMS clients create several objects to begin using WebLogic JMS. Here are the general steps for setting up a JMS client:

  1. Look up a JMS ConnectionFactory in JNDI.
  2. Look up Queues and Topics in JNDI.
  3. Use the ConnectionFactory to create a Connection.
  4. Use the Connection to create a Session.
  5. Use the Session and the Queues or Topics to create message producers and consumers.
  6. Start the connection.

The init() method in the examples.jms.queue.QueueSend example shows how to set up a QueueSession for a JMS client. The init() method is reproduced here with added comments.

  public final static String JNDI_FACTORY=
               "weblogic.jndi.WLInitialContextFactory";
  public final static String JMS_FACTORY=
               "javax.jms.TopicConnectionFactory";
  public final static String QUEUE="javax.jms.exampleQueue";

  private QueueConnectionFactory qconFactory;
  private QueueConnection qcon;
  private QueueSession qsession;
  private QueueSender qsender;
  private Queue queue;
  private TextMessage msg;

  /**
   * Create all the necessary objects for sending
   * messages to a JMS queue.
   */
  public void init(Context ctx, String queueName)
       throws NamingException, JMSException
  {
    // The ctx object is a JNDI initial context passed 
    // in by the main() method. 

    // Look up the ConnectionFactory
    qconFactory = (QueueConnectionFactory)  
                    ctx.lookup(JMS_FACTORY);

    // Get a QueueConnection from the 
    // QueueConnectionFactory
    qcon = qconFactory.createQueueConnection();

    // Get a QueueSession that is not transacted
    // and acknowledges automatically 
    qsession = qcon.createQueueSession(false, 
                    Session.AUTO_ACKNOWLEDGE);

    // Look up the Queue
    queue = (Queue) ctx.lookup(queueName);

    // Create a message producer (QueueSender)
    // for the Queue we looked up. 
    qsender = qsession.createSender(queue);

    // Create a message object 
    msg = qsession.createTextMessage();

    // Start the connection
    qcon.start();
  }

You use the same process for setting up a Topic session, substituting the point-to-point object names with the publish/subscribe names: TopicConnectionFactory, TopicConnection, Topic, and TopicSession.

Top

Sending messages

To send a message, you first create the message, fill it in with the information you want to send, and then transmit it to its destination through the message producer. JMS provides six different message types, which are described in the section Message types earlier in this document.

You create a message by calling a method on the Session object. Here is the code to create a TextMessage, set the text of the message, and send the message off to a Queue:

  msg = qsession.createTextMessage();
  msg.setText("Welcome to WebLogic JMS");
  qsender.send(msg);

The body of a TextMessage holds a single String. The send() message in the example puts the String into the message and calls the QueueSender's send() method:

  /**
   * Send a message to a JMS queue.
   */
  public void send(String message)
       throws JMSException
  {
    msg.setText(message);
    qsender.send(msg);
  }

Top

Receiving messages

In the QueueSend example, messages originate from the client's QueueSender. The examples.jms.queue.QueueReceive example has a QueueReceiver object to receive the messages the QueueSend example produces. The code for setting up the receiver is very similar to QueueSend. If your application receives messages, you choose whether to receive them synchronously or asynchronously. The QueueReceive example receives messages asynchronously by implementing the javax.jms.MessageListener interface, which includes an onMessage() method. Here is the code from the init() method of the examples.jms.QueueReceive example that establishes the message listener:

  qreceiver = qsession.createReceiver(queue);
  qreceiver.setMessageListener(this);

The first line creates the QueueReceiver object on the Queue. The second line sets the object that implements the MessageListener interface, in this case examples.jms.QueueReceive itself. When a message is delivered to the QueueSession, it is passed to the examples.jms.QueueReceive.onMessage() method.

The onMessage() method processes messages received through the QueueReceiver. In the QueueReceive example, it checks that the message is a TextMessage and if so, prints the text of the message. If it receives a different message type, it uses the message's toString() method to display the message contents. In a message receiver, it is good practice to check that the the received message is of the type the message handler method expects. Here is the implementation of the onMethod() interface from the QueueReceive example:

  public void onMessage(Message msg)
  {
    try {
      String msgText;
      if (msg instanceof TextMessage) {
        msgText = ((TextMessage)msg).getText();
      } else { // If it is not a TextMessage...
        msgText = msg.toString();
      }

      System.out.println("Message Received: "+ msgText );

      if (msgText.equalsIgnoreCase("quit")) {
        synchronized(this) {
          quit = true;
          this.notifyAll(); // Notify main thread to quit
        }
      }
    } catch (JMSException jmse) {
      jmse.printStackTrace();
    }
  }

To receive messages synchronously, the example would call qreceiver.receive() for each message instead of setting a listener with qreceiver.setMessageListener(). The receive() method blocks and waits for a message.

Top

Using message header fields

You can set some standard Header fields before you send a message or on the send() method. When the send() method returns, JMS has set some other Header fields that you can inspect in the sending or receiving code. For example, the following code placed after the send() method displays the message ID WebLogic JMS assigned to the message:

  System.out.println("Sent message " +
               msg.getJMSMessageID() + " to " + 
               msg.getJMSDestination());

Here are the Message class methods you use to set Header fields:

setJMSCorrelationID()
You can link messages by setting the JMSCorrelationID field. This could be used in a request/response scheme. When a client receives a request message, it prepares a response message, setting the JMSCorrelationID field to the MessageID of the original message before sending it to a destination where the sender of the original message is waiting for the response. The JMSCorrelationID field can actually carry any String you choose, so a series of messages could be linked with some application-determined value.

setJMSReplyTo()
A client sets the JMSReplyTo header field to the Queue or Topic where the receiver should send a reply message. This feature can be used with the JMSCorrelationID header field to coordinate request/response messages. Simply setting the JMSReplyTo field does not guarantee a response. It is up to the receiving client to decide whether to respond.

setJMSType()
The JMSType header field holds a message type identifier set by the sending client. A JMS application typically would define a set of message types to help the receiving client process the message correctly. The WebshareServlet example sets and tests the JMSType header field to ensure that it doesn't fail while trying to process foreign messages.

The delivery mode (DeliveryMode.PERSISTENT or DeliveryMode.NON_PERSISTENT), priority (0-9), and time to live (in milliseconds) can be set on the QueueSender or TopicPublisher send() method. For example, the following code sends a persistent message with a priority of 4 and a time to live of one hour:

  qsender.send(msg, DeliveryMode.PERSISTENT, 4, 3600000);

The SenderServlet example shows how to set header fields in messages you send. The QueueBrowse example shows how to access header fields of received messages.

Top

Using Message property fields

A message can have a set of application-specific properties. The sending client can set properties in the message; the receiving client can read them, but cannot change them without first clearing them.

The main reason to put data into a Property field instead of into the message body is that Property fields are used to specify selectors -- message filters. Data in the message body cannot be accessed with Selectors.

For example, a property field can be used to indicate when a message is of expedited priority. A message consumer can then be created with a selector that accesses this property field and selects only messages of expedited priority. Selectors are described in the next section.

Property values can be of types boolean, byte, short, int, long, float, double, and String. The "JMSX" property name prefix is reserved for JMS.

The Message class has get and set methods for each of the supported data types, as shown in this table:

Data type Set method Get method
boolean setBooleanProperty() getBooleanProperty()
byte setByteProperty() getByteProperty()
double setDoubleProperty() getDoubleProperty()
float setFloatProperty() getFloatProperty()
int setIntProperty() getIntProperty()
long setLongProperty() getLongProperty()
short setShortProperty() getShortProperty()
String setStringProperty() getStringProperty()

To set a property field, call the set method with the property name and value. Here is an example from the WebshareServlet example that sets two String properties and an int property:

    // Properties used for selectors
    msg.setStringProperty("User", user);
    msg.setStringProperty("Category", category);
    msg.setIntProperty("Rating", rating);

The WebshareServlet example uses these properties in message selectors, which are described next.

Top

Using message selectors

With selectors, WebLogic JMS can filter messages for a client. A message selector is a String with a syntax similar to the where clause of an SQL select statement. You specify a message selector with QueueSession.createReceiver() or TopicSession.createSubscriber().

Here are a few examples of selectors. See the javax.jms.Message javadoc or the JMS specification for full details of selector syntax.

A selector is a Boolean expression. It can reference the property fields in a message, and the standard message header fields JMSDeliveryMode, JMSPriority, JMSMessageID, JMSTimestamp, JMSCorrelationID, and JMSType.

Here are some examples of selector expressions:

  salary > 64000 and dept in ('eng','qa')

  (product like 'WebLogic%' or product like '%T3') 
        and version > 3.0

  hireyear between 1990 and 1992 
        or fireyear is not null

  fireyear - hireyear > 4

The following example shows how you would install a selector that filters out messages with priority less than 6:

  String selector = "JMSPriority >= 6";
  qsession.createReceiver(queue, selector);

To update a selector, you create a durable subscriber with the same client ID. The Webshare example allows clients to update the selector on their subscription. When a client loads the Servlet, they log in, entering an ID which becomes the client ID of their durable subscription. Initially, the selector for their subscription is TRUE -- all messages are delivered to their subscriber. The client can use a browser form to construct a new selector. WebshareServlet updates the durable subscriber by creating a new durable subscriber with the new selector. Here is the code that modifies the durable subscription:

  // Create a DurableSubscriber with the new selector
  tconFactory = (TopicConnectionFactory) ctx.lookup(JMS_FACTORY);
  topic = (Topic) ctx.lookup(TOPIC);
  connection = tconFactory.createTopicConnection();

  // Override configured ClientID with the login ID
  connection.setClientID(userName); 
  session = connection.createTopicSession(false, 
    Session.AUTO_ACKNOWLEDGE);

  // Create the subscription
  subscriber = 
    session.createDurableSubscriber(topic, userName, 
                                    selector, false);
  session.close();
  connection.close();

To remove the selector from a durable subscription, just replace it with "TRUE" so that every message is passed through the filter.

Top

Developing JMS session pool applications

WebLogic JMS implements an optional JMS facility for defining a server-managed pool of message consumers -- a ServerSessionPool. The ServerSessionPool receives messages from a Topic and passes them to a server-side MessageListener implementation that you provide to process the messages. Your class provides an onMessage() method that processes a message.

The ServerSessionPool manages a pool of JMS Sessions, each executing your single-threaded onMessage() method, so that messages are processed in parallel.

To create the ServerSessionPool, WebLogic provides a ServerSessionPoolFactory class. This factory, which is bound in the JNDI tree, creates a ServerSessionPool with your values for the following attributes:

  • The Topic whose messages the pool will process
  • The maximum number of sessions the pool will contain
  • Whether the Sessions are transacted
  • The acknowledge mode of the sessions: (AUTO_ACKNOWLEDGE, DUPS_OK_ACKNOWLEDGE, or CLIENT_ACKNOWLEDGE)
  • The name of your MsgListener class

When messages arrive on the Topic, the ServerSessionPool passes them to an instance of your MessageListener class.

Before creating a ServerSessionPool, WebLogic Server tests the weblogic.allow.create.jms.ServerSessionPool Acl to ensure the user has "create" permission. This permission is granted to "everyone" in the default weblogic.properties file. You can edit the property to restrict the permission to a set of users and groups or delete the property to disable the ServerSessionPool feature.

The sessionpool JMS example demonstrates how to set up a ServerSessionPool. The ListenerPoolSend class creates the ServerSessionPool, specifies the MsgListener class as the MessageListener implementation, and sends messages to the Topic. The MsgListener.onMessage() method simply prints the messages on the WebLogic Server output.

The sessionpool example has two Java classes. The SessionPoolSend class sets up the ServerSessionPool and sends messages to it. The MsgListener class implements the onMessage() interface that prints the messages to the WebLogic Server's output.

First, we look at the SessionPoolSend.java program. This class is designed to be executed from a separate JVM after the WebLogic Server is running. Its main() method gets a JNDI initial context, calls the init() method to create the ServerSessionPool, and then calls the readAndSend() method to send messages to the Topic:

  public static void main(String[] args)
       throws Exception 
  {
    if (args.length != 1) {
      System.out.println(
        "Usage: java examples.jms.sessionpool.SessionPoolSend" +
	      " WebLogicURL");
      return;
    }

    // Get an initial JNDI context from the target server
    InitialContext ic = getInitialContext(args[0]);
    SessionPoolSend sps = new SessionPoolSend();

    // Set up the ServerSessionPool
    sps.init(ic, TOPIC);

    System.out.println("JMS Session Pool Established");

    readAndSend(sps);
    sps.close();
  }

The init() method does all the work of setting up the JMS environment. It actually does two jobs:

  1. It establishes the JMS environment used by the readAndSend() method (which just sends messages to the Topic). This is most of the code in the init() method.
  2. It gets the creates the ServerSessionPoolFactory()

Here is the init() method:

  public void init(Context ctx, String topicName)
       throws NamingException, JMSException
  {

    // Set up the JMS objects readAndSend() needs 
    tconFactory = (TopicConnectionFactory) ctx.lookup(JMS_FACTORY);
    tcon = tconFactory.createTopicConnection();
    tsession = tcon.createTopicSession(false, 
      Session.AUTO_ACKNOWLEDGE);
    try {
      topic = (Topic) ctx.lookup(topicName);
    } catch (NamingException ne) {
      topic = tsession.createTopic(topicName);
      ctx.bind(topicName, topic);
    }

    // Create a publisher and message for readAndSend()
    tpublisher = tsession.createPublisher(topic);
    msg = tsession.createTextMessage();

    // Start the (publisher) connection
    tcon.start();

    // Create the ServerSessionPool.
    // First look up the SessionPoolFactory
    sessionPoolFactory = (ServerSessionPoolFactory) 
      ctx.lookup(SESSION_POOL_FACTORY);

    // Create a ServerSessionPool 
    sessionPool = 
      sessionPoolFactory.getServerSessionPool(
        tcon,   // Connection on the Topic   
        5,      // Maximum of 5 sessions
        false,  // Don't used transacted sessions
        Session.AUTO_ACKNOWLEDGE, // Auto-acknowledge 
                                  // messages received 
        // Name of the MessageListener to instantiate
        "examples.jms.sessionpool.MsgListener"
       );
    // Create the ConnectionConsumer
    consumer = tcon.createConnectionConsumer(topic, 
      "TRUE", sessionPool, 10);
  }

Top

Setting up ACLs for JMS in the WebLogic Realm

weblogic.jms.topic.topicName
weblogic.jms.queue.queueName

WebLogic controls access to internal resources like JMS through ACLs set up in the WebLogic Realm. Entries for ACLs in the WebLogic Realm are listed as properties in the weblogic.properties file. The access control list name for a queue is weblogic.jms.queue.queueName, where queueName is the name of a queue. The access control list name for a topic is weblogic.jms.topic.topicName, where topicName is the name of a topic.

You can set the Permissions "receive" and "send" for a JMS queue or topic by entering a property in the properties file. If you do not set an ACL for a JMS queue or topic, all permissions for that queue or topic are by default granted to everyone.

In the examples below, we illustrate how you can set ACLs for various JMS topics and queues. Peter, Brown, and Eric can receive messages from the "payroll" topic, but only Peter has permission to send messages to that topic. Bob has permission to send messages to the "reminder" queue. Since no ACL is set for the receive permission of the "reminder" queue, everyone can receive messages from that queue.

Example:

weblogic.allow.receive.weblogic.jms.topic.payroll=peter,brown,eric
weblogic.allow.send.weblogic.jms.topic.payroll=peter
weblogic.allow.send.weblogic.jms.queue.reminder=bob

Top

JMS in a clustered environment

JMS is not a clustered service; it does not support load balancing or fail over. However, it is possible to use JMS in a clustered environment, connecting to any node in the cluster and being routed to a particular instance of JMS.

In a cluster, all of the servers are running JMS independently. Conceptually, it is simpler for all clients to use the same instance of JMS on a particular member of the cluster. To accomplish this, it is necessary to have one of the servers in the cluster configured (via the per-server weblogic.properties file) to be the JMS provider. This means that the properties file should define the connection pool that JMS will use as well as the connection factory that all the clients will use to get to JMS.

For example, the weblogic.properties file for one of the servers in the cluster may contain:

weblogic.jdbc.connectionPool.csPool=\
         url=jdbc:cloudscape:demo,\
         driver=COM.cloudscape.core.JDBCDriver,\
         initialCapacity=0,\
         maxCapacity=5,\
         capacityIncrement=1,\
         weblogic.t3.waitSecondsForConnection=20,\
         waitSecondsForConnection=20,\
         props=user=none;password=none;server=none

weblogic.allow.reserve.weblogic.jdbc.connectionPool.csPool=everyone

java.system.property.cloudscape.system.home=C:/weblogic/eval/cloudscape/data

weblogic.jms.connectionPool=csPool
weblogic.jms.connectionFactoryName.trader=jms.connection.clusterFactory
weblogic.jms.connectionFactoryArgs.trader=ClientID=traderReceive
weblogic.jms.topic.exampleTopic=javax.jms.exampleTopic

Clients that connect to the cluster and look up jms.connection.traderFactory in JNDI will be automatically routed to this server for their JMS connection.

Top

Change history

Release 4.5 -- 08/31/1999

First release.

 

Copyright © 2000 BEA Systems, Inc. All rights reserved.
Required browser: Netscape 4.0 or higher, or Microsoft Internet Explorer 4.0 or higher.
Last updated 3/27/2000