Skip Headers
Oracle® Application Server Containers for J2EE Services Guide
10g Release 2 (10.1.2) for Windows or UNIX
B14012-02
  Go To Documentation Library
Home
Go To Product List
Solution Area
Go To Table Of Contents
Contents
Go To Index
Index

Previous
Previous
Next
Next
 

3 Java Message Service (JMS)

This chapter discusses the following topics:

Download the JMS example used in this chapter from the OC4J sample code page on the OTN Web site at

http://www.oracle.com/technology/tech/java/oc4j/demos/index.html

Overview

Java clients and Java middle-tier services must be capable of using enterprise messaging systems. Java Message Service (JMS) offers a common way for Java programs to access these systems. JMS is the standard messaging API for passing data between application components and allowing business integration in heterogeneous and legacy environments.

JMS provides two programming models:

JMS queues and topics are bound in the JNDI environment and made available to J2EE applications.

You can choose between several JMS providers, depending on their integration and quality-of-service (QOS) requirements, as follows:

Oracle Application Server JMS

OracleAS JMS is a Java Message Service that provides the following features:

This section covers the following topics:

Configuring OracleAS JMS Ports

You can use the Oracle Enterprise Manager 10g to configure the port range for OracleAS JMS. The default range is between 3201 and 3300.

From the OC4J Home page, select the Administration page, then the Instance Properties column, then Server Properties. Scroll to the Multiple VM Configuration section.

Configuring OracleAS JMS Destination Objects

OracleAS JMS Destination objects are configured in the jms.xml file. OracleAS JMS Destination objects can be either queues or topics. OracleAS JMS is already installed with OC4J, so the only configuration necessary is for the queues, topics, and their connection factories that your applications use.

  • Oracle Enterprise Manager 10g configuration—To edit the jms.xml file directly through Oracle Enterprise Manager 10g, select Advanced Properties under the Instance Properties column on the Administration page. In this section, choose jms.xml to modify the straight XML file.

  • Standalone OC4J configuration—You can configure the default jms.xml file in J2EE_HOME/config/jms.xml. If you want, you can change the name and location of this file. To modify the name and location of the JMS configuration file, specify the new name and location in the OC4J server configuration file—J2EE_HOME/config/server.xml. The server.xml file designates the name and location of the JMS configuration file through the <jms-config> element.


    Note:

    Configuration changes made to OracleAS JMS (by modifying jms.xml) take effect only on OC4J restart, or shutdown and start.

Figure 3-1 shows the order in which the elements in the jms.xml file are structured. "OracleAS JMS Configuration File Elements for jms.xml" provides a complete description of all elements and their attributes in the jms.xml file.

Figure 3-1 Configuration Elements Hierarchy

Hierarchy of elements within the jms.xml file.
Description of the illustration cfgelmhi.gif

The jms.xml file defines the topics and queues used. For each Destination object (queue or topic)—you must specify its name (also known as its location) and connection factory in the jms.xml file. The following jms.xml file example configuration defines a queue used by the Oc4jjmsDemo demo.

The queue is defined as follows:

  • The name (location) of the queue is jms/demoQueue.

  • Its queue connection factory is defined as jms/QueueConnectionFactory.

The topic is defined as follows:

  • The name (location) of the topic is jms/demoTopic.

  • Its topic connection factory is defined as jms/TopicConnectionFactory.

<?xml version="1.0" ?>
<!DOCTYPE jms-server PUBLIC "OracleAS JMS server" "http://xmlns.oracle.com/ias/dtds
/jms-server.dtd">

<jms-server port="9127">
   <queue location="jms/demoQueue"> </queue>
   <queue-connection-factory location="jms/QueueConnectionFactory">
   </queue-connection-factory>

   <topic location="jms/demoTopic"> </topic>
   <topic-connection-factory location="jms/TopicConnectionFactory">
   </topic-connection-factory>

    <!-- path to the log-file where JMS-events/errors are stored -->
    <log>
        <file path="../log/jms.log" />
    </log>
</jms-server>

Note:

Because all of these values are defaults, you do not have to configure them. However, the example shows the configuration for the queue, the topic, and their connection factories so that you understand how to configure your own Destination objects and connection factories.

See "OracleAS JMS Configuration File Elements for jms.xml" for descriptions of the elements in the jms.xml file.

Default Destination Objects

OracleAS JMS creates two default Destination objects, as follows:

  • The default queue is defined as jms/demoQueue.

  • The default topic is defined as jms/demoTopic.

You can use these Destination objects in your code without adding them to the jms.xml configuration file.

The default connection factories that are automatically associated with these objects are as follows:

  • jms/QueueConnectionFactory

  • jms/TopicConnectionFactory

Default Connection Factories

OracleAS JMS creates six default connection factories for the XA/non-XA and various JMS domains. You can use these connection factories in your code without adding them to the jms.xml configuration file, rather than defining new connection factories. The only reason to define a new connection factory in the jms.xml file is if you need to specify nondefault values for one or more of the optional attributes of connection-factory elements.

The default connection factories are as follows:

  • jms/ConnectionFactory

  • jms/QueueConnectionFactory

  • jms/TopicConnectionFactory

  • jms/XAConnectionFactory

  • jms/XAQueueConnectionFactory

  • jms/XATopicConnectionFactory

Thus, if you used only the default connection factories, then you could define only the topic and queues necessary in the jms.xml file. The following example defines the jms/demoQueue and the jms/demoTopic. Both of these objects use their respective default connection factories.

<?xml version="1.0" ?>
<!DOCTYPE jms-server PUBLIC "OracleAS JMS server" "http://xmlns.oracle.com/ias/dtds
/jms-server.dtd">

<jms-server port="9127">
   <queue location="jms/demoQueue"> </queue>
   <topic location="jms/demoTopic"> </topic>
    <!-- path to the log-file where JMS-events/errors are stored -->
    <log>
        <file path="../log/jms.log" />
    </log>
</jms-server>

OracleAS JMS internally creates the default connection factory objects and binds them to the default names within the OC4J server where the JMS connection is created.

However, you can also redefine the default connection factories to have specific attributes by configuring them in the jms.xml file.

Steps for Sending a Message

A JMS client sends a JMS message by doing the following:

  1. Retrieve both the configured JMS Destination object (queue or topic) and its connection factory using a JNDI lookup.

  2. Create a connection from the connection factory.

  3. If you are receiving messages, then start the connection.

  4. Create a session using the connection.

  5. Provide the retrieved JMS Destination, create a sender for a queue, or a publisher for a topic.

  6. Create the message.

  7. Send out the message using either the queue sender or the topic publisher.

  8. Close the queue session.

  9. Close the connection for either JMS Destination types.

Example 3-1 demonstrates these steps for sending a JMS message. For the complete example, download the JMS example used in this chapter from the OC4J sample code page on the OTN Web site at

http://www.oracle.com/technology/tech/java/oc4j/demos/index.html

Note:

For simplicity, most of the error code is removed in Example 3-1. To see the error processing, see the sample code available on the OTN Web site.

Example 3-1 OracleAS JMS Client that Sends Messages to a Queue

The JNDI lookup for OracleAS JMS requires that the OracleAS JMS Destination and connection factory be defined within the jms.xml file, prepended with the java:comp/env/ prefix.


Note:

Alternatively, you could use logical names in the JNDI lookup. See "Map Logical Names in Resource References to JNDI Names" for directions. The only difference between an OracleAS JMS client and an OJMS client is the name provided in the JNDI lookup. To make your client independent of either JMS provider, use logical names in the implementation, and change only the OC4J-specific deployment descriptor.

The dosend method, shown in the following example, sets up a queue to send messages. After creating the queue sender, this example sends out several messages.

public static void dosend(int nmsgs)
{
   // 1a. Retrieve the queue connection factory. 
    QueueConnectionFactory qcf = (QueueConnectionFactory) 
            ctx.lookup("java:comp/env/jms/QueueConnectionFactory");
   // 1b. Retrieve the queue. 
    Queue q = (Queue) ctx.lookup("java:comp/env/jms/demoQueue");
         
   // 2. Create the JMS connection. 
    QueueConnection qc  = qcf.createQueueConnection();
   // 3. Start the queue connection. 
    qc.start();
   // 4. Create the JMS session over the JMS connection. 
    QueueSession    qs  = qc.createQueueSession(false,
                                        Session.AUTO_ACKNOWLEDGE);
    //5. Create a sender on the JMS session to send messages. 
    QueueSender     snd = qs.createSender(q);

    // Send out messages...  
    for (int i = 0; i < nmsgs; ++i)
    {
     //6. Create the message using the createMessage method of the 
     // JMS session. 
     Message msg = qs.createMessage();
     //7. Send the message out over the sender (snd) using the 
     // send method. 
     snd.send(msg);
     System.out.println("msg:" + " id=" + msg.getJMSMessageID());
    }

   //8 & 9 Close the sender, the JMS session and the JMS connection. 
    snd.close();
    qs.close();
    qc.close();

}

Steps for Receiving a Message

A JMS client receives a JMS message by doing the following:

  1. Retrieve both the configured JMS Destination object (queue or topic) and its connection factory using a JNDI lookup.

  2. Create a connection from the connection factory.

  3. If you are receiving messages, then start the connection.

  4. Create a session using the connection.

  5. Providing the retrieved JMS Destination, create a receiver for a queue or a topic subscriber.

  6. Receive the message using the queue receiver or the topic subscriber.

  7. Close the queue session.

  8. Close the connection for either JMS Destination types.

Example 3-2 demonstrates these steps for receiving a JMS message. For the complete example, download the JMS example used in this chapter from the OC4J sample code page on the OTN Web site at

http://www.oracle.com/technology/tech/java/oc4j/demos/index.html

Note:

For simplicity, most of the error code is removed in Example 3-2. To see the error processing, see the sample code available on the OTN Web site.

Example 3-2 OracleAS JMS Client That Receives Messages Off a Queue

The dorcv method, shown in the following example, sets up a queue to receive messages off of it. After creating the queue receiver, it loops to receive all messages off the queue and compares it to the number of expected messages.

public static void dorcv(int nmsgs)
{
   Context ctx = new InitialContext();
   
   // 1a. Retrieve the queue connection factory. 
    QueueConnectionFactory qcf = (QueueConnectionFactory) 
            ctx.lookup("java:comp/env/jms/QueueConnectionFactory");
   // 1b. Retrieve the queue. 
    Queue q = (Queue) ctx.lookup("java:comp/env/jms/demoQueue");
         
   // 2. Create the JMS connection. 
    QueueConnection qc  = qcf.createQueueConnection();
   // 3. Start the queue connection. 
    qc.start();
   // 4. Create the JMS session over the JMS connection. 
    QueueSession    qs  = qc.createQueueSession(false,
                                        Session.AUTO_ACKNOWLEDGE);
   // 5. Create a receiver, as we are receiving off of the queue. 
    QueueReceiver   rcv = qs.createReceiver(q);

   // 6. Receive the messages. 
    int count = 0;
    while (true)
    {
      Message msg = rcv.receiveNoWait();
      System.out.println("msg:" + " id=" + msg.getJMSMessageID());
      ++count;
    }

    if (nmsgs != count)
    {
        System.out.println("expected: " + nmsgs + " found: " + count);
    }

   // 7 & 8 Close the receiver, the JMS session and the JMS connection. 
    rcv.close();
    qs.close();
    qc.close();
}

OracleAS JMS Utilities

OC4J JMS comes with an OC4J-specific command-line utility, com.evermind.server.jms.JMSUtils, for debugging and information access.

The path J2EE_HOME/oc4j.jar must be in the CLASSPATH variable. Then execute JMSUtils, as follows:

java com.evermind.server.jms.JMSUtils [gen_options] [command] 
                     [command_options]

The OracleAS JMS server must be running, and only the administrator can use JMSUtils. You define a user within the administrator role in the security User Manager. For information on defining users within security roles, see the Oracle Application Server Containers for J2EE Security Guide.

The generic options for JMSUtils facilitate connecting to the OracleAS JMS server. Table 3-1 describes these options.

Table 3-1 JMSUtils Options

Option Description
-host <hostname> The (remote) host where the OracleAS JMS server is installed. This is not required if the client exists on the same node as the OracleAS JMS server.
-port <port> The (remote) port through which the OracleAS JMS server is accessed. The default JMS port number is 9127.
-username <username> The username to access the OracleAS JMS server for creating the JMS connection. This user is defined in the User Manager security configuration within the administrative roles.
-password <password> The password to access the OracleAS JMS server for creating the JMS connection. This password is defined in the User Manager security configuration within the administrative roles.
-clientID <ID> Use this identifier for all JMS connections. This is required only for identifying durable subscriptions on topics.

The commands describe the action to be taken and are discussed in Table 3-2. Some of these commands have their own options (command_options) to further describe the action desired.

To display the syntax usage, issue the JMSUtils command with no argument. To display extensive information about the available command set, the argument options, and the behavior of each command, issue the following:

java com.evermind.server.jms.JMSUtils help

Table 3-2 OC4J JMS Utilities

Utility Command Description
help Print detailed help for all utilities commands.
check [<other-selector>] Check validity of a JMS message selector, identified by the -selector command option. Optionally, check if two specified selectors are treated as equivalent (useful for reactivating durable subscriptions), where the second selector is identified in the optional <other-selector>.
knobs Display all available system properties (shown in Table 3-6) and their current settings on the OC4J JMS server.
stats Display all available DMS statistics on the OC4J JMS server (this includes non-JMS statistics as well). (For information on DMS, see the Oracle Application Server Performance Guide.)
destinations Print a list of all permanent Destination objects known to OC4J JMS.
durables Print a list of all durable subscriptions known to OC4J JMS.
subscribe <topic> Create a new durable subscription on the <topic>. Specify a name, message selector, whether it is local, and a durable subscription client identifier. This replaces existing, inactive durable subscriptions. The name is identified using the -name command option. The message selector is identified using the -selector command option. Whether the durable subscription is local or not is identified using the -noLocal command option. The client identifier is defined with the -clientID generic option.
unsubscribe Drop an existing, inactive durable subscription. The durable subscription is identified by a name (-name command option) and the client identifier (-clientID generic option).
browse <destination> Browse messages on a given destination (queue or topic durable subscription, defined in jms.xml).
drain <destination> Dequeue messages on a given destination (queue or topic durable subscription).
copy <from-destination> <to-destination> Copy messages from one destination (queue or topic durable subscription) to a different destination. If the source and sink destinations are the same, then the command is not executed, generating an error instead.
move <from-destination> <to-destination> Move messages from one destination (queue or topic durable subscription) to a different destination. If the source and sink destinations are the same, then the command is not executed, generating an error instead.

Table 3-3 describes the command options.

Table 3-3 JMSUtils Command Options

Command Option Description
-selector <selector> Create queue receivers and durable subscribers with the specified JMS message selector.
-noLocal [true|false] If set to true, the subscriber does not see the messages that are published in the same connection. Use when creating a durable subscriber. The default value is false.
-name <name> Defines a name for a durable subscription, operating on a topic. This option is mandatory for commands that read topics, and is ignored for reading queues.
-silent Do not print messages while processing. Keeps a count of the total number of messages processed, which is printed to standard error.
-count <count> Do not process more than the indicated number of messages during the current operation. If the count is negative or zero, then all selected messages are processed.

An example of using JMSUtils to browse the exception queue is as follows:

java com.evermind.server.jms.JMSUtils -username admin -password welcome 
        browse jms/Oc4jJmsExceptionQueue

OracleAS JMS File-Based Persistence

OC4J JMS supports file-based persistence for JMS Destination objects (queues and topics). The following sections provide more detail on file-based persistence:

Overview

If persistence is enabled, then OC4J automatically performs the following:

  • If a persistence file does not exist, OC4J automatically creates the file and initializes it with the appropriate data.

  • If the persistence file exists and is empty, then OC4J initializes it with the appropriate data.


Caution:

A persistence file must not be moved, removed, or modified when the OC4J server is active. Doing so can result in data corruption and message loss. If OC4J is not active, then removing a persistence file is equivalent to deleting all messages and durable subscriptions in the destination associated with that persistence file. When OC4J starts up again, the JMS server reinitializes the file as usual.

Even if persistence is enabled, only certain messages are persisted to a file. For a message to be persisted, all of the following must be true:

  • The Destination object is defined to be persistent within the persistence-file attribute in the jms.xml file.

  • The message has a persistent delivery mode, which is the default. Messages sent to persistent destinations that are defined with a non-persistent delivery mode (defined as DeliveryMode.NON_PERSISTENT) are not persistent.

The complete semantics of which messages are persisted are documented within the JMS specification.

Enabling Persistence

To enable file-based persistence for Destination objects, specify the persistent-file attribute in the jms.xml file. For JMS objects within standalone OC4J, this is all you have to do to enable persistence. The following XML configuration demonstrates how the persistence-file attribute defines the name of the file as pers. See "OracleAS JMS Configuration File Elements for jms.xml" for information on the path and naming conventions for the persistence-file attribute.

<queue name="foo" location="jms/persist" persistence-file="pers">
</queue>

The path for the persistence-file attribute is either an absolute path of the file or a path relative to the persistence directory defined in application.xml.

The JMS server will not create any directories for persistence files. So when a persistence file is defined in jms.xml it must either be in an existing absolute directory, for example:

persistence-file="/this/dir/exists/PersistenceFile"

or simply be a filename for example:

persistence-file="PersistenceFile"

In the latter case, by default the persistence file will be created in $J2EE_HOME/persistence (for a standalone instance) or $J2EE_HOME/persistence/<island_name> (for an iAS environment).

Oracle Application Server may have multiple OC4J instances writing to the same file directory, even with the same persistence filename. Setting this attribute enables file-based persistence, but also creates the possibility that your persistence files can be overwritten by another OC4J instance.

Recovery

The file-based persistence of OC4J JMS provides recoverable and persistent storage of messages. Each OC4J JMS Destination, which can be either a queue or topic, can be associated with a relative or absolute path name that points to a file that stores the messages sent to the Destination object. The file can reside anywhere in the file system (and not necessarily inside a J2EE_HOME directory). Multiple persistence files can be placed in the same directory; persistence files can be placed on a remote network file system or can be part of a local file system.

The following sections discuss the various aspects of persistence recovery for OracleAS JMS:

Scope of Recoverability

OC4J JMS cannot recover from all possible types of failures. If any of the following failures occur, then OC4J JMS does not guarantee the recoverability of the persistence file.

  • Media corruption—the disk system holding the persistence file fails abnormally or gets corrupted.

  • External corruption—the persistence file is deleted, edited, modified, or otherwise corrupted (by software). Only the OC4J JMS server should write into a persistence file.

  • Silent failure or corruption—the I/O methods in the JDK fail silently or corrupt data that are being read or written silently.

  • A java.io.FileDescriptor.sync() failure—the sync() call does not properly and completely flush all file buffers associated with the given descriptor to the underlying file system.

Persistence File Management

When the OC4J JMS server is running, you must not copy, delete, or rename persistence files currently in use. It is an unrecoverable error to perform any of these actions on any of these files when they are being used by OC4J JMS.

However, when no OC4J JMS server is using a persistence file, you can perform the following administrative and maintenance operations on these files:

  • deleting: Deleting a persistence file removes all messages and, in the case of topics, all durable subscriptions. On startup, OC4J JMS initializes a new (and empty) persistence file.

  • copying: An existing persistence file can be copied for archival or backup purposes. If an existing persistence file gets corrupted, an earlier version can be used (as long as the association between the OC4J JMS Destination name and the file is maintained), pointed to by any suitable path name, to go back to the previous contents of the JMS Destination.

Persistence files cannot be concatenated, split up, rearranged, or merged. Attempting any of these operations causes unrecoverable corruption of the data in these files.

In addition to persistence files specified by a user and lock files, OC4J JMS uses a special file, jms.state, for internal configuration and transaction state management. OC4J JMS cleans up this file and its contents during normal operations. You must never delete, move, copy, or otherwise modify this file, even for archival purposes. Attempting to manipulate the jms.state file can lead to message and transaction loss.


Note:

The location of the jms.state file is different whether you are operating OC4J in standalone or Oracle Application Server mode, as follows:
  • Standalone: J2EE_HOME/persistence directory

  • Oracle Application Server: J2EE_HOME/persistence/<island_name> directory

The location of the persistence directory is defined in the application.xml file.


Reporting Errors to the JMS Client

The sequence of operations when a JMS client enqueues or dequeues a message, or commits or rolls back a transaction, is as follows:

  • Client makes a function call

    • pre-persistence operations

      • persistence occurs

    • post-persistence operations

  • Client function call returns

If a failure occurs during the pre-persistence or persistence phase, then the client receives a JMSException or some other type of error, but no changes are made to the persistence file.

If a failure occurs in the post-persistence phase, the client may receive a JMSException or some other type of error; however, the persistence file is still updated, and OC4J JMS recovers as if the operation succeeded.

OracleAS JMS Recovery Steps

Lock files prevent multiple OC4J processes from writing into the same persistence file. If multiple OC4J JVMs are configured to point to the same file in the same persistence-file location, then they could overwrite each others data and cause corruption or loss of persisted JMS messages. To protect against these kinds of sharing violations, OracleAS JMS associates each persistence file with a lock file. Thus, each persistence file—for example, /path/to/persistenceFile— is associated with a lock file named /path/to/persistenceFile.lock. See "Enabling Persistence" for more information on persistence files.

OC4J must have appropriate permissions to create and delete the lock file. If OC4J is terminated normally, then the lock files are cleaned up automatically. However, if OC4J is terminated abnormally, the lock files continue to exist in the file system. Because OC4J cannot distinguish leftover lock files from sharing violations, the user must manually remove all lock files before restarting OC4J after abnormal termination. OracleAS JMS will not attempt to create the relevant persistent JMS destinations if it detects an existing lock file for it.

OC4J JMS never attempts to delete lock files automatically. Lock files must be manually deleted for OC4J JMS to use a given persistence file. The remainder of the discussion in this subsection assumes that all lock files in question have been removed.


Note:

This manual intervention is required only on abnormal shutdown. See "Abnormal Termination".

OC4J JMS performs recovery operations on all persistence files as configured in OC4J JMS at the time of abnormal termination. In other words, if OC4J terminates abnormally, the user modifies the JMS server configuration and restarts OC4J, the JMS server still attempts to recover all the persistence files in the original configuration, and, after recovery is successful, moves to using the new configuration specified.

If recovery of the old configuration fails, then the OC4J JMS server does not start. The server must be shut down or restarted repeatedly to give recovery another chance, until recovery is successful.

OC4J JMS caches its current persistence configuration in the jms.state file, which is also used to maintain transaction state. If you wish to bypass all recovery of the current configuration, you can remove the jms.state file, remove all lock files, possibly change the OC4J JMS server configuration, and start the server in a clean-slate mode. (We do not recommend doing this.) If the OC4J JMS server cannot find a jms.state file, then it creates a new one.

If, for some reason, the jms.state file itself is corrupted, then the only recourse is to delete it, with the attendant loss of all pending transactions—that is, transactions that have been committed, but the commits not yet performed by all individual Destination objects participating in the transactions.

If messaging activity was in progress during abnormal termination, then OC4J JMS tries to recover its persistence files. Any data corruption (of the types mentioned earlier) is handled by clearing out the corrupted data; this may lead to a loss of messages and transactions.

If the headers of a persistence file are corrupted, OC4J JMS may not be able to recover the file, because such a corrupted file is often indistinguishable from user configuration errors. The oc4j.jms.forceRecovery administration property (described in Table 3-6) instructs the OC4J JMS server to proceed with recovery, clearing out all invalid data at the cost of losing messages or masking user configuration errors.

Abnormal Termination

If OC4J terminates normally, then the lock files are cleaned up automatically. However, if OC4J terminates abnormally (for example, a kill -9 command), the lock files remain in the file system. Because OC4J cannot distinguish leftover lock files from sharing violations, you must manually remove all lock files before restarting OC4J after abnormal termination. OC4J JMS does not even attempt to create the relevant persistent JMS Destination objects if it detects already existing lock files for them.

The default location of the lock files is in the persistence directory—J2EE_HOME/persistence. (The persistence directory is defined in the application.xml file.) Other locations can be set within the persistence-file attribute of the Destination object.

Predefined OracleAS JMS Exception Queue

As an extension to the JMS specification, OC4J JMS comes with a predefined exception queue for handling undeliverable messages. This is a single, persistent, global exception queue to store undeliverable messages in all of its Destination objects. The exception queue has a fixed name (jms/Oc4jJmsExceptionQueue), a fixed JNDI location (jms/Oc4jJmsExceptionQueue), and a fixed persistence file (Oc4jJmsExceptionQueue).


Note:

The location of the Oc4jJmsExceptionQueue persistence file varies according to whether you are operating OC4J in standalone or Oracle Application Server mode, as follows:
  • Standalone: J2EE_HOME/persistence directory

  • Oracle Application Server: J2EE_HOME/persistence/<island_name> directory

The location of the persistence directory is defined in the application.xml file.


The exception queue is always available to OC4J JMS and its clients, and should not be explicitly defined in the jms.xml configuration file; attempting to do so is an error. The name, JNDI location, and persistence path name of the exception queue must be considered reserved words in their respective name spaces; any attempt to define other entities with these names is an error.

Messages can become undeliverable because of message expiration and listener errors. The following subsection explains what happens to undeliverable messages in the first case.

Message Expiration

By default, if a message sent to a persistent Destination expires, then OC4J JMS moves the message to the exception queue. The JMSXState of the expiring message is set to the value 3 (for EXPIRED), but the message headers, properties, and body are not otherwise modified. The message is wrapped in an ObjectMessage (with appropriate property name and value copies performed as described elsewhere in this chapter), and the wrapping message is sent to the exception queue.

To affect the behavior of what goes into the exception queue, use the oc4j.jms.saveAllExpired property (described in Table 3-6).

The wrapping ObjectMessage has the same DeliveryMode as the original message.

By default, messages expiring on nonpersistent or temporary Destination objects are not moved to the exception queue. The messages sent to these Destination objects are not worth persisting and neither should their expired versions be.

You can specify that all expired messages be sent to the OC4J JMS exception queue, regardless of whether they are sent to persistent, nonpersistent, or temporary Destination objects, by setting the oc4j.jms.saveAllExpired administration property (described in Table 3-6) to true when starting the OC4J server. In this case, all expired messages are moved to the exception queue.

Message Paging

The OracleAS JMS server supports paging in and out message bodies under the following circumstances:

  • The message has a persistent delivery mode.

  • The message is sent to a persistent Destination object (see "OracleAS JMS File-Based Persistence").

  • The OC4J server JVM has insufficient memory.

Only message bodies are paged. Message headers and properties are never considered for paging. You set the paging threshold through the OracleAS JMS system property, oc4j.jms.pagingThreshold, which is a double value (narrowed into the range [0,1]) that represents the memory usage fraction above which the OracleAS JMS server considers message bodies for paging. This value is an estimate of the fraction of memory in use by the Java virtual machine (JVM). This value can range from 0.0 (the program uses no memory at all) to 1.0 (the program is using all available memory).

The value ranges from somewhere above 0.0 to somewhere below 1.0: it is almost impossible to write a Java program that uses no JVM memory, and programs almost always die by running out of memory before the JVM heap gets full.

For example, if the paging threshold is 0.5, and the memory usage fraction of the JVM rises to 0.6, OracleAS JMS tries to page out as many message bodies as possible until the memory usage fraction reduces below the threshold, or no more message bodies can be paged out.

When a message that has been paged out is requested by a JMS client, the OracleAS JMS server automatically pages in the message body (regardless of the memory usage in the JVM) and delivers the correct message header/body to the client. After the message has been delivered to the client, it may once again be considered for paging out, depending on the memory usage in the server JVM.

If the memory usage fraction drops below the paging threshold, then the OracleAS JMS server stops paging out message bodies. The bodies of messages already paged out are not automatically paged back in. The paging in of message bodies happens only on demand (that is, when a message is dequeued or browsed by a client).

By default, the paging threshold of the OracleAS JMS server is set to 1.0. In other words, by default, the OracleAS JMS server never pages message bodies.

Depending on the JMS applications, and the sizes of the messages they send/receive, and the results of experiments and memory usage monitoring on real-life usage scenarios, the user should choose a suitable value for the paging threshold.

No value of the paging threshold is ever incorrect. JMS semantics are always preserved regardless of whether paging in enabled or disabled. Control of the paging threshold does allow the OracleAS JMS server to handle more messages in memory than it might have been able to without paging.

OracleAS JMS Configuration File Elements for jms.xml

This section describes the XML elements for OC4J JMS configuration in jms.xml. The following is the element order structure within the XML file:

<jms-server>
   <queue>
      <description></description>
   </queue>
   <topic>
     <description></description>
   </topic>
   <connection-factory></connection-factory>
   <queue-connection-factory></queue-connection-factory>
   <topic-connection-factory></topic-connection-factory>
   <xa-connection-factory></xa-connection-factory>
   <xa-queue-connection-factory></xa-queue-connection-factory>
   <xa-topic-connection-factory></xa-topic-connection-factory>
   <log>
     <file></file>
   </log>
</jms-server>

Table 3-4 defines the JMS configuration elements.

Table 3-4 JMS Configuration Elements

Element Description Attributes
jms-server The root element of the OC4J JMS server configuration. host—The host name defined in a String (DNS or dot-notation host name) to which this OC4J JMS server should bind. By default, the JMS server binds to 0.0.0.0 (also known as [ALL] in the configuration file). This attribute is optional.

port—The port defined as an int (valid TCP/IP port number) to which this OC4J JMS server should bind. The default setting is 9127. This setting applies only to the standalone configuration of OC4J. In the Oracle Application Server, the port setting in the configuration file is overridden by command-line arguments that are used (by, for example, OPMN, DCM, and others) when starting the OC4J server. This attribute is optional.

queue This element configures OracleAS JMS queues. The queues are available when OC4J JMS starts up, and are available for use until the server is restarted or reconfigured. You can configure zero or more queues in any order. Any newly configured queue is not available until OC4J is restarted. name—This required attribute is the provider-specific name (String) for the OC4J JMS queue. The name can be any valid nonempty string (with white space and other special characters included, although this is not recommended). The name specified here can be used in Session.createQueue() to convert the provider-specific name to a JMS queue. It is invalid for both a queue and a topic to specify the same name. However, multiple queues can specify the same name and different locations. There is no default name.

location—This required attribute states the JNDI location (String) to which the queue is bound. The value should follow the JNDI rules for valid names. Within the OC4J JMS container, the location is bound and accessible as is. In application clients, the name is part of the java:comp/env/ JNDI name space, and should be appropriately declared in the relevant deployment descriptors. The java:comp/env/ names can also be used within the container, assuming that the relevant deployment descriptors have been appropriately specified. The location should be unique across all Destination objects and connection factory elements in jms.xml. There is no default.

persistence-file—An optional path and filename (String). The path for the persistence-file attribute is either an absolute path of the file or a path relative to the persistence directory defined in application.xml; the default path is J2EE_HOME/persistence/<island> for Oracle Application Server environments and J2EE_HOME/persistence for standalone environments.

See "Recovery" for more details on the meaning of the persistence-file attribute. If multiple queue elements with the same name and different locations are declared in jms.xml, then all of them should specify the same value for persistence-file, or should not specify the value at all. If at least one of these multiple declarations specifies a persistence-file, that value is used for this queue.

topic This element configures OracleAS JMS topic. The topics are available when OC4J JMS starts up, and are available for use until the server is restarted or reconfigured. You can configure zero or more topics in any order. Any newly configured topic is not available until OC4J is restarted. name—This required attributes is the provider-specific name (String) for the OC4J JMS topic. The name can be any valid nonempty string (with white space and other special characters included, although this is not recommended). The name specified here can be used in Session.createTopic() to convert the provider-specific name to a JMS topic. It is invalid for both a queue and a topic to specify the same name. However, multiple topics can specify the same name and different locations. There is no default name.

location—This required attribute states the JNDI location (String) to which the topic is bound. The value should follow the JNDI rules for valid names. Within the OC4J JMS container, the location is bound and accessible as is. In application clients, the name is part of the java:comp/env/ JNDI name space, and should be appropriately declared in the relevant deployment descriptors.

The java:comp/env/ names can also be used within the container, assuming that the relevant deployment descriptors have been appropriately specified. The location should be unique across all topics and connection factory elements in jms.xml. There is no default.

persistence-file—An optional path and filename (String). The path for the persistence-file attribute is either an absolute path of the file or a path relative to the persistence directory defined in application.xml; the default path is J2EE_HOME/persistence/<island> for Oracle Application Server environments and J2EE_HOME/persistence for standalone environments.

See "Recovery" for more details on the meaning of the persistence-file attribute. If multiple queue or topic elements with the same name and different locations are declared in jms.xml, then all of them should specify the same value for persistence-file, or should not specify the value at all. If at least one of these multiple declarations specifies a persistence-file, that value is used for this topic.

description A user-defined string to remind the user for what the queue or topic is used.
connection-factory JMS domain connection factory configuration. Table 3-5 describes the attributes for this element.
queue-connection-factory JMS domain connection factory configuration. Table 3-5 describes the attributes for this element.
topic-connection-factory JMS domain connection factory configuration.Table 3-5 describes all of the attributes for this element. Table 3-5 describes the attributes for this element.
xa-connection-factory XA variants of connection factory configuration. Table 3-5 describes the attributes for this element.
xa-queue-connection-factory XA variants of connection factory configuration. Table 3-5 describes the attributes for this element.
xa-topic-connection-factory XA variants of connection factory configuration. Table 3-5 describes the attributes for this element.
log Enables logging of the JMS activity in either file or ODL format. See the section "Enabling OC4J Logging" in the Oracle Application Server Containers for J2EE User's Guidefor information on logging.

Table 3-5 describes the attributes for any connection factory definition.

Table 3-5 Connection Factory Configuration Attributes

Attribute Type Mandatory? Default Description
location String Yes (n/a) The JNDI location to which the connection factory is bound. The value should follow the JNDI rules for valid names. Within the OC4J JMS container, the location is bound and accessible as is. In application clients, the name is part of the java:comp/env/ JNDI name space, and should be appropriately declared in the relevant deployment descriptors. The java:comp/env/ names can also be used within the container, assuming that the relevant deployment descriptors have been appropriately specified. The location should be unique across all Destination and connection factory elements in jms.xml.
host String (DNS or dot notation host name) No [ALL] The fixed OC4J JMS host to which this connection factory will connect. By default, a connection factory uses the same host as configured for the jms-server element. Nondefault values can be used to force all JMS operations to be directed to a specific OC4J JVM, bypassing any locally available OC4J JMS servers and other Oracle Application Server or clustered configurations.
port int (valid TCP/IP port number) No 9127 The fixed OC4J JMS port to which this connection factory connects. By default, a connection factory uses the same port as configured for the jms-server element (or the value of the port that was specified for Oracle Application Server or clustered configurations on the command line). Nondefault values can be used to force all JMS operations to be directed to a specific OC4J JVM, bypassing any locally available OC4J JMS servers and other Oracle Application Server or clustered configurations.
username String No The empty string The user name for the authentication of JMS default connections created from this connection factory. The user name itself must be properly created and configured with other OC4J facilities.
password String No The empty string The password for the authentication of JMS default connections created from this connection factory. The password itself must be properly created and configured with other OC4J facilities.
clientID String No The empty string The administratively configured, fixed JMS clientID for connections created from this connection factory. If no clientID is specified, then the default is an empty string, which can also be programmatically overridden by client programs, according to the JMS specification. The clientID is used only for durable subscriptions on topics; its value does not matter for queue and nondurable topic operations.


Note:

In Table 3-5, the property password supports password indirection. For more information, refer to theOracle Application Server Containers for J2EE Security Guide.

Examples

This section contains code samples that show connection factory configuration fragments.

The following code sample configures a connection factory of jms/Cf, a queue connection factory of jms/Qcf, and an XA topic connection factory of jmx/xaTcf.

<connection-factory location="jms/Cf">
</connection-factory>

<queue-connection-factory location="jms/Qcf">
</queue-connection-factory>

<xa-topic-connection-factory location="jms/xaTcf"
    username="foo" password="bar" clientID="baz">
</xa-topic-connection-factory>


If you want to add a topic connection factory, you must use a unique name. For example, you could not use the same name as the connection factory (above) of jms/Cf. Thus, the following would be invalid.

<!-- Invalid: cannot reuse "location"  -->
<topic-connection-factory location="jms/Cf">
</topic-connection-factory>


The following code sample shows queue and topic configuration fragments. This segment creates a queue foo and a topic bar.

<queue name="foo" location="jms/foo">
</queue>

<topic name="bar" location="jms/bar">
</topic>

Certain locations are reserved and cannot be redefined within the jms.xml configuration file. The following code sample shows that you cannot use the jms/Oc4jJmsExceptionQueue when defining a queue location, because it is a reserved location.

<!-- Invalid: cannot use a reserved "location" -->
<queue name="fubar" location="jms/Oc4jJmsExceptionQueue">
</queue>

When defining a persistence file for queues and topics, you can define the location and the filename. In addition, you can specify multiple persistence files, as long as the persistence filename is the same. Thus, the persistence file is written out to two locations for the same queue.

<queue name="foo" location="jms/persist" persistence-file="pers">
</queue>

<!-- OK: multiple persistence file specification ok if consistent -->
<queue name="foo" location="jms/file" persistence-file="pers">
</queue>

<!-- Invalid: multiple persistence file specifications should be consistent -->
<queue name="foo" location="jms/file1" persistence-file="notpers">
</queue>

Alternatively, you cannot have two objects writing out to the same persistence file. Each queue or topic must have its own persistence filename, even if the locations are different.

<topic name="demoTopic" location="jms/dada" persistence-file="/tmp/abcd">
</topic>

<!-- Invalid: cannot reuse persistence-file for multiple destinations -->
<topic name="demoTopic1" location="jms/dada1" persistence-file="/tmp/abcd">
</topic>

OracleAS JMS System Properties

OC4J JMS allows runtime configuration of the OC4J JMS server and JMS clients through JVM system properties. None of these properties affect basic JMS functionality. They pertain to OC4J JMS-specific features, extensions, and performance optimizations.

Table 3-6 contains a brief summary of these administration properties.

Table 3-6 OC4J JMS Administration Properties

JVM System Property Property Type Default Value Server/Client Use
oc4j.jms.serverPoll long 15000 JMS client Interval (in milliseconds) that JMS connections ping the OC4J server and report communication exceptions to exception listeners.
oc4j.jms.messagePoll long 1000 JMS client Maximum interval (in milliseconds) that JMS asynchronous consumers wait before checking the OC4J JMS server for new messages.
oc4j.jms.listenerAttempts int 5 JMS client Number of listener delivery attempts before the message is declared undeliverable.
oc4j.jms.maxOpenFiles int 64 OC4J server Maximum number of open file descriptors (for persistence files) in the OC4J JMS server; relevant if the server is configured with more persistent Destination objects than the maximum number of open file descriptors allowed by the operating system.
oc4j.jms.saveAllExpired boolean false OC4J server Save all expired messages on all Destination objects (persistent, nonpersistent, and temporary) to the OC4J JMS exception queue.
oc4j.jms.socketBufsize int 64 * 1024 JMS client When using TCP/IP sockets for client-server communication, use the specified buffer size for the socket input/output streams. A minimum buffer size of 8 KB is enforced. The larger the size of messages being transferred between the client and server, the larger the buffer size should be to provide reasonable performance.
oc4j.jms.debug boolean false JMS client If true, enable tracing of NORMAL events in JMS clients and the OC4J JMS server. All log events (NORMAL, WARNING, ERROR, and CRITICAL) are sent to both stderr and, when possible, either J2EE_HOME/log/server.log or J2EE_HOME/log/jms.log. Setting this property to true typically generates large amounts of tracing information.
oc4j.jms.noDms boolean false JMS client If true, disable instrumentation.
oc4j.jms.forceRecovery boolean false OC4J server If true, forcibly recover corrupted persistence files. By default, the OC4J JMS server does not perform recovery of a persistence file if its header is corrupted (because this condition is, in general, indistinguishable from configuration errors). Forcible recovery allows the OC4J JMS server almost always to start up correctly and make persistence files and Destination objects available for use.
oc4j.jms.pagingThreshold double 1.0 OC4J server Represents the memory usage fraction above which the OracleAS JMS server begins to consider message bodies for paging. This value is an estimate of the fraction of memory in use by the JVM. This value can range from 0.0 (the program uses no memory at all) to 1.0 (the program is using all available memory).

See "Message Paging" for more information.

oc4j.jms.usePersistenceLockFiles boolean true OC4J server Controls whether lock files should be used to protect OracleAS JMS persistence files from being overwritten by more than one OC4J process. By default, lock files are used to protect against accidental overwrite by more than one OC4J process. But this requires users to manually remove lock files when OC4J terminates abnormally. Setting this system property to false does not create lock files for persistent destinations. Set this property to false only if you can guarantee that only one active process accesses each persistence file. Set when starting the OC4J server. It remains in effect for all JMS clients until shutdown.

Resource Providers

OC4J provides a ResourceProvider interface to transparently plug in JMS providers.

The ResourceProvider interface of OC4J allows EJBs, servlets, and OC4J clients to access many different JMS providers. The resources are available under java:comp/resource/. Oracle JMS is accessed using the ResourceProvider interface. See "Oracle JMS" for more information on Oracle JMS.

Configuring a Custom Resource Provider

You can configure a custom resource provider in one of these ways:

  • If this is the resource provider for all applications (global), then configure the global application.xml file.

  • If this is the resource provider for a single application (local), then configure the orion-application.xml file of the application.

To add a custom resource provider, add the following code to the chosen XML file (as listed above):

<resource-provider class="providerClassName" name="JNDIname">
     <description>description </description>
     <property name="name" value="value" />
</resource-provider>


For the <resource-provider> attributes, configure the following:

  • class—The name of the resource provider class.

  • name—A name by which to identify the resource provider. This name is used in finding the resource provider in the application JNDI as:

    java:comp/resource/JNDIname/ 
    
    
    

The subelements of the <resource-provider> are configured as follows:

  • description subelement—A description of the specific resource provider.

  • property subelement—The name and value attributes are used to identify parameters provided to the resource provider. The name attribute identifies the name of the parameter, and its value is provided in the value attribute.

When retrieving the resource provider, use the following syntax in the JNDI lookup:

java:comp/resource/JNDIname/resourceName 

where JNDIname is the name of the resource provider (as given in the name attribute of the <resource-provider> element) and resourceName is the resource name, which is defined in the application implementation. See "Using OJMS as a Resource Provider" for an example of Oracle JMS defined as a resource provider.

Oracle JMS

Oracle JMS (OJMS) is the JMS interface to the Oracle Database Streams Advanced Queuing (AQ) feature in the Oracle database. OJMS implements the JMS 1.0.2b specification and is compatible with the J2EE 1.3 specification. OJMS access in OC4J occurs through the resource provider interface. For more information about AQ and OJMS, see the Oracle9i Application Developer's Guide—Advanced Queuing for Release 2 (9.2).

The following sections describe Oracle JMS:

Using OJMS as a Resource Provider

To access OJMS queues, do the following:

  1. Install and configure OJMS on the database. See "Install and Configure the JMS Provider".

  2. On the database, create an RDBMS user—which the JMS application will connect to the back-end database—and assign privileges. The user must have the necessary privileges to perform OJMS operations. OJMS allows any database user to access queues in any schema, provided that the user has the appropriate access privileges. See "Create User and Assign Privileges".

  3. Create the JMS Destination objects in OJMS. See "Create JMS Destination Objects".

  4. In the OC4J XML configuration, define an OJMS resource provider in the <resource-provider> element with information about the back-end database. Create data sources or LDAP directory entries, if needed. See "Define the OJMS Resource Provider".

  5. Access the resource in your implementation through a JNDI lookup. See "Access the OJMS Resources".

Install and Configure the JMS Provider

You or your DBA must install OJMS according to theOracle9i Application Developer's Guide—Advanced Queuing for Release 2 (9.2) and generic database manuals. After you have installed and configured this JMS provider, you must apply additional configuration. This includes the following:

  1. You or your DBA must create an RDBMS user through which the JMS client connects to the database. Grant this user appropriate access privileges to perform OJMS operations. See "Create User and Assign Privileges".

  2. You or your DBA must create the tables and queues to support the JMS Destination objects. See "Create JMS Destination Objects".


    Note:

    The following sections use SQL for creating queues, topics, their tables, and assigning privileges within the JMS demo on the OC4J sample code page on the OTN Web site at
    http://www.oracle.com/technology/tech/java/oc4j/demos/index.html
    

Create User and Assign Privileges

Create an RDBMS user through which the JMS client connects to the database. Grant access privileges to this user to perform OJMS operations. The privileges that you need depend on what functionality you are requesting. Refer to the Oracle9i Application Developer's Guide—Advanced Queuing for Release 2 (9.2) for more information on privileges necessary for each type of function.

The following example creates jmsuser, which must be created within its own schema, with privileges required for OJMS operations. You must be a SYS DBA to execute these statements.

DROP USER jmsuser CASCADE ;

GRANT connect,resource,AQ_ADMINISTRATOR_ROLE TO jmsuser 
   IDENTIFIED BY jmsuser ;
GRANT execute ON sys.dbms_aqadm  TO  jmsuser;
GRANT execute ON sys.dbms_aq     TO  jmsuser;
GRANT execute ON sys.dbms_aqin   TO  jmsuser;
GRANT execute ON sys.dbms_aqjms  TO  jmsuser;

connect jmsuser/jmsuser;

You may need to grant other privileges, such as two-phase commit or system administration privileges, based on what the user needs. See Chapter 7, "Java Transaction API", for information on two-phase commit privileges.

Create JMS Destination Objects

Each JMS provider requires its own method for creating the JMS Destination object. Refer to the Oracle9i Application Developer's Guide—Advanced Queuing for Release 2 (9.2) for more information on the DBMS_AQADM packages and OJMS messages types. For our example, OJMS requires the following methods:


Note:

The SQL for creating the tables for the OJMS example is included in the JMS example available on the OC4J sample code page on the OTN Web site at
http://www.oracle.com/technology/tech/java/oc4j/demos/index.html

  1. Create the tables that handle the JMS Destination (queue or topic).

    In OJMS, both topics and queues use a queue table. The JMS example creates a single table, demoTestQTab, for a queue.

    To create the queue table, execute the following SQL:

    DBMS_AQADM.CREATE_QUEUE_TABLE(
            Queue_table            => 'demoTestQTab',
            Queue_payload_type     => 'SYS.AQ$_JMS_MESSAGE',
            sort_list => 'PRIORITY,ENQ_TIME',
            multiple_consumers  => false,
            compatible             => '8.1.5');
    
    

    The multiple_consumers parameter specifies whether there are multiple consumers. Thus, is always false for a queue and true for a topic.

  2. Create the JMS Destination. If you are creating a topic, you must add each subscriber for the topic. The JMS example requires a single queue—demoQueue.

    The following command creates a queue called demoQueue within the queue table demoTestQTab. After creation, the queue is started.

    DBMS_AQADM.CREATE_QUEUE(
          Queue_name          => 'demoQueue',
          Queue_table         => 'demoTestQTab');
    
    DBMS_AQADM.START_QUEUE(
          queue_name         => 'demoQueue');
    
    

    If you want to add a topic, then the following example shows how you can create a topic called demoTopic within the topic table demoTestTTab. After creation, two durable subscribers are added to the topic. Finally, the topic is started, and a user is granted a privilege to it.


    Note:

    Oracle AQ uses the DBMS_AQADM.CREATE_QUEUE method to create both queues and topics.

    DBMS_AQADM.CREATE_QUEUE_TABLE(
            Queue_table            => 'demoTestTTab',
            Queue_payload_type     => 'SYS.AQ$_JMS_MESSAGE',
            multiple_consumers  => true,
            compatible             => '8.1.5');
    DBMS_AQADM.CREATE_QUEUE( 'demoTopic', 'demoTestTTab');
    DBMS_AQADM.ADD_SUBSCRIBER('demoTopic',                          sys.aq$_agent('MDSUB', null, null));
    DBMS_AQADM.ADD_SUBSCRIBER('demoTopic',                          sys.aq$_agent('MDSUB2', null, null));
    DBMS_AQADM.START_QUEUE('demoTopic');
    

    Note:

    The names defined here must be the same names used to define the queue or topic in the deployment descriptors of the application.

Define the OJMS Resource Provider

You can define the OJMS resource provider either through the Oracle Enterprise Manager 10g or by hand-editing the XML files, as described in the following sections:

Configure the OJMS Provider Through the Oracle Enterprise Manager 10g

Use Application Server Control in the JMS section to configure the OJMS provider. To add an OJMS provider, select JMS Providers under the Application Defaults column on the Administration page. This brings you to the following page:

Click the Add new JMS Provider button to configure each JMS provider.

You can configure either OJMS or a third-party JMS provider. OracleAS JMS is always provided and preconfigured, except for the topics and queues, with the OC4J installation.


Note:

This discussion also includes the directions for configuring third-party JMS providers, because both OJMS and third-party providers are configured in the same manner.

After you choose the type of JMS provider, you must specify the following:

  • OJMS: Specify the data source name and JNDI location for the database in which OJMS is installed and configured.

  • Third-party JMS provider: Specify the name, JNDI initial context factory class, and JNDI URL for the third-party provider. To add JNDI properties for this JMS provider, such as java.naming.factory.initial and java.naming.provider.url, click Add a property. A row is added in which you can specify the name for each JNDI property and its value.

Doing this configures only the providers; it does not configure the Destination objects (topic, queue, and subscription).

To configure a JMS provider only for a specific application, select the application from the Applications page, scroll down to the Resources column, and select JMS Providers. The screens that appear are the same as for the default JMS provider.

Configure the OJMS Provider in the OC4J XML Files

Configure the OJMS provider within the <resource-provider> element.

  • If this is to be the JMS provider for all applications (global), configure the global application.xml file.

  • If this is to be the JMS provider for a single application (local), configure the orion-application.xml file of the application.

The following code sample shows how to configure the JMS provider using XML syntax for OJMS.

<resource-provider class="oracle.jms.OjmsContext" name="ojmsdemo">
   <description> OJMS/AQ </description>
   <property name="datasource" value="jdbc/emulatedDS"></property>
</resource-provider>

where the attributes of the <resource-provider> element contain the following:

  • class attribute—The OJMS provider is implemented by the oracle.jms.OjmsContext class, which is configured in the class attribute.

  • name attribute—The name of the OJMS resource provider is ojmsdemo.

In addition, the name/value attributes of the <property> element identify the data source used by OJMS. The topic or queue connects to this data source to access the tables and queues that facilitate the messaging. In this example, a data source is identified as jdbc/emulatedDS.

How you configure the attributes of the <property> element in the resource provider configuration depends on where your application is running. With OJMS and accessing AQ in the database, the resource provider must be configured using either a data sources property or a URL property, as discussed in the following sections:

Configuring the Resource Provider with a Data Sources Property

Use a data source when the application runs within OC4J. To use a data source, first you must configure it within the data-sources.xml file in which the OJMS provider is installed. The JMS topics and queues use database tables and queues to facilitate messaging. The type of data source you use depends on the functionality you want.


Note:

For no transactions or single-phase transactions, you can use either an emulated or nonemulated data source. For two-phase commit transaction support, you can use only a nonemulated data source. See the JTA chapter for more information.

Example 3-3 Emulated Data Source with Thin JDBC Driver

The following example contains an emulated data source that uses the Thin JDBC driver. To support a two-phase commit transaction, use a nonemulated data source. For differences between emulated and nonemulated data sources, see "Defining Data Sources".

The example is displayed in the format of an XML definition; see the Oracle Application Server Containers for J2EE User's Guide for directions on adding a new data source to the configuration through Oracle Enterprise Manager 10g.

<data-source
  class="com.evermind.sql.DriverManagerDataSource"
  name="OracleDS"
  location="jdbc/emulatedOracleCoreDS"
  xa-location="jdbc/xa/emulatedOracleXADS"
  ejb-location="jdbc/emulatedDS"
  connection-driver="oracle.jdbc.driver.OracleDriver"
  username="jmsuser"
  password="jmsuser"
  url="jdbc:oracle:thin:@//localhost:5521/oracle.regress.rdbms.dev.us.oracle.com"
/>

Customize this data source to match your environment. For example, substitute the host name, port, and SID of your database for myhost:1521:orcl.


Note:

Instead of providing the password in the clear, you can use password indirection. For details, see the Oracle Application Server Containers for J2EE Services Guide.

Next, configure the resource provider using the data source name. The following is an example of how to configure the resource provider for OJMS using a data source of jdbc/emulatedDS.

<resource-provider class="oracle.jms.OjmsContext" name="ojmsdemo">
  <description> OJMS/AQ </description>
  <property name="datasource" value="jdbc/emulatedDS"></property>
</resource-provider>

For details on configuring data sources, see "Defining Data Sources".

Configuring the Resource Provider with a URL Property

In this release, the data source is not serializable. Thus, application clients must use a URL definition to access OJMS resources. When the application is a standalone client (that is, when it runs outside of OC4J), configure the <resource-provider> element with a URL property that has the URL of the database where OJMS is installed and, if necessary, provides the username and password for that database. The following demonstrates a URL configuration:

<resource-provider class="oracle.jms.OjmsContext" name="ojmsdemo"> 
  <description> OJMS/AQ </description> 
  <property name="url" value="jdbc:oracle:thin:@//localhost:5521/oracle.regress.rdbms.dev.us.oracle.com">
  </property> 
  <property name="username" value="user"></property> 
  <property name="password" value="passwd"></property>

Access the OJMS Resources

The steps for accessing OJMS resources are the same as for OracleAS JMS resources, as listed in "Steps for Sending a Message". The only difference is the name of the resource provided in the JNDI lookup.

  • The OJMS syntax for the connection factory is "java:comp/resource" + JMS provider name + "TopicConnectionFactories" or "QueueConnectionFactories" + a user defined name. The user-defined name can be anything and does not match any other configuration. The xxxConnectionFactories details what type of factory is being defined. For this example, the JMS provider name is defined in the <resource-provider> element as ojmsdemo.

    • For a queue connection factory: Because the JMS provider name is ojmsdemo and you decide to use a name of myQCF, the connection factory name is: java:comp/resource/ojmsdemo/ QueueConnectionFactories/myQCF

    • For a topic connection factory: Because the JMS provider name is ojmsdemo and you decide to use a name of myTCF, the connection factory name is: java:comp/resource/ojmsdemo/ TopicConnectionFactories/myTCF

    The user defined names, as shown above by myQCF and myTCF, are not used for anything else in your logic. So, any name can be chosen.

  • The OJMS syntax for any Destination is "java:comp/resource" + JMS provider name + "Topics" or "Queues" + Destination name. The Topic or Queue details what type of Destination is being defined. The Destination name is the actual queue or topic name defined in the database.

    For this example, the JMS provider name is defined in the <resource-provider> element as ojmsdemo. In the database, the queue name is demoQueue.

    • For a queue: If the JMS provider name is ojmsdemo and the queue name is demoQueue, then the JNDI name for the topic is: java:comp/resource/ojmsdemo/Queues/demoQueue

    • For a topic: If the JMS provider name is ojmsdemo and the topic name is demoTopic, then the JNDI name for the topic is: java:comp/resource/ojmsdemo/Topics/demoTopic

Example 3-4 demonstrates the steps for sending a JMS message; Example 3-5 demonstrates the steps for receiving a JMS message. For the complete example, download the JMS example used in this chapter from the OC4J sample code page on the OTN Web site at

http://www.oracle.com/technology/tech/java/oc4j/demos/index.html

Note:

For simplicity, most of the error handling is removed in Example 3-4 and Example 3-5. To see the error processing, see the sample code available on the OTN Web site.

Example 3-4 OJMS Client That Sends Messages to an OJMS Queue

The dosend method, shown in the following example, sets up a queue to send messages. After creating the queue sender, this example sends out several messages. The steps necessary for setting up the queue and sending out the message are summarized in "Steps for Sending a Message".

public static void dosend(int nmsgs)
{
   // 1a. Retrieve the queue connection factory
    QueueConnectionFactory qcf = (QueueConnectionFactory) 
    ctx.lookup(       "java:comp/resource/ojmsdemo/QueueConnectionFactories/myQCF");
   // 1b. Retrieve the queue
    Queue q = (Queue) 
       ctx.lookup("java:comp/resource/ojmsdemo/Queues/demoQueue");
         
   // 2. Create the JMS connection
    QueueConnection qc  = qcf.createQueueConnection();
   // 3. Start the queue connection.
    qc.start();
   // 4. Create the JMS session over the JMS connection
    QueueSession    qs  = qc.createQueueSession(false,
                                        Session.AUTO_ACKNOWLEDGE);
    // Create a sender on the JMS session to send messages.
    QueueSender     snd = qs.createSender(q);

    // Send out messages...  
    for (int i = 0; i < nmsgs; ++i)
    {
     //Create the message using the createMessage method 
    // of the JMS session
     Message msg = qs.createMessage();
     // Send the message out over the sender (snd) using the send method
     snd.send(msg);
     System.out.println("msg:" + " id=" + msg.getJMSMessageID());
    }

   // Close the sender, the JMS session and the JMS connection.
    snd.close();
    qs.close();
    qc.close();
}

Example 3-5 OJMS Client That Receives Messages Off a Queue

The dorcv method, shown in the following example, sets up a queue to receive messages off it. After creating the queue receiver, it loops to receive all messages off the queue and compares it to the number of expected messages. The steps necessary for setting up the queue and receiving messages are summarized in "Steps for Sending a Message".

public static void dorcv(int nmsgs)
{
   Context ctx = new InitialContext();
   
   // 1a. Retrieve the queue connection factory
    QueueConnectionFactory qcf = (QueueConnectionFactory) ctx.lookup(
          "java:comp/resource/ojmsdemo/QueueConnectionFactories/myQCF");
   // 1b. Retrieve the queue
    Queue q = (Queue) 
      ctx.lookup("java:comp/resource/ojmsdemo/Queues/demoQueue");
         
   // 2. Create the JMS connection
    QueueConnection qc  = qcf.createQueueConnection();
   // 3. Start the queue connection.
    qc.start();
   // 4. Create the JMS session over the JMS connection
    QueueSession    qs  = qc.createQueueSession(false,
                                        Session.AUTO_ACKNOWLEDGE);
   // Create a receiver, as we are receiving off of the queue.
    QueueReceiver   rcv = qs.createReceiver(q);

   // Receive the messages
    int count = 0;
    while (true)
    {
      Message msg = rcv.receiveNoWait();
      System.out.println("msg:" + " id=" + msg.getJMSMessageID());
      ++count;
    }

    if (nmsgs != count)
    {
        System.out.println("expected: " + nmsgs + " found: " + count);
    }

   // Close the receiver, the JMS session and the JMS connection.
    rcv.close();
    qs.close();
    qc.close();
}

Using OJMS with Oracle Application Server and the Oracle Database

This section addresses common issues encountered by users of OJMS (AQ/JMS) with Oracle Application Server.

Error When Copying aqapi.jar

A common error seen when using OJMS with the Oracle Application Server is:

PLS-00306 "wrong number or types of arguments"

If you receive this message, then the aqapi.jar file being used in Oracle Application Server is not compatible with the version of the Oracle database being used for AQ. A common mistake is to copy the aqapi.jar file from the Oracle database installation into the Oracle Application Server installation, or from the Oracle Application Server installation into the Oracle database installation, under the false assumption that they are interchangeable. The confusion is because the Oracle Application Server and the Oracle database both ship the OJMS client JAR file. Do not copy this file. Use the matrix in Table 3-7 to find the correct version of the database and Oracle Application Server, then use the aqapi.jar file that comes with the Oracle Application Server.

In an Oracle Application Server installation, the OJMS client JAR file can be found at ORACLE_HOME/rdbms/jlib/aqapi.jar and should be included in the CLASSPATH.

OJMS Certification Matrix

Table 3-7 summarizes which version of the Oracle database to use with the Oracle Application Server when the OJMS client is running in OC4J. An X indicates that the Oracle database version and the Oracle Application Server version that intersect at that cell are certified to work together. If the intersection has no X, then the corresponding version of the Oracle database and Oracle Application Server should not be used together.


Note:

This is not a certification matrix for Oracle Application Server and the Oracle database in general. It is only for OJMS when used in the Oracle Application Server.

Table 3-7 OJMS Certification Matrix

OracleAS / Oracle database v9.0.1 v9.0.1.3 v9.0.1.4 v9.2.0.1 v9.2.0.2+ v10.1.0+
9.0.2 X X
X

9.0.3

X

X
9.04

X
X
9.0.4.1




X
10.1.2

X
X X

Map Logical Names in Resource References to JNDI Names

The client sends and receives messages through a JMS Destination object. The client can retrieve the JMS Destination object and connection factory either through using its explicit name or by a logical name. The examples in "Oracle Application Server JMS" and "Oracle JMS" use explicit names within the JNDI lookup calls. This section describes how you can use logical names in your client application, thereby limiting the JNDI names for the JMS provider within the OC4J-specific deployment descriptors. With this indirection, you can make your client implementation generic for any JMS provider.

If you want to use a logical name in your client application code, then define the logical name in one of the following XML files:

Map the logical name to the actual name of the topic or queue name in the OC4J deployment descriptors.

Create Logical Names

You can create logical names for the connection factory and Destination objects, as follows:

Example

The following example illustrates how to specify logical names for a queue.

<resource-ref>
  <res-ref-name>myQCF</res-ref-name>
  <res-type>javax.jms.QueueConnectionFactory</res-type>
  <res-auth>Container</res-auth>
  <res-sharing-scope>Shareable</res-sharing-scope>
</resource-ref>
<resource-env-ref>
  <resource-env-ref-name>myQueue</resource-env-ref-name>  
  <resource-env-ref-type>javax.jms.Queue</resource-env-ref-type>
</resource-env-ref>

Map Logical Names to Actual Names

After the logical names are created, you map the logical names to actual names in the OC4J deployment descriptors. The actual names, or JNDI names, are different in OracleAS JMS than those in OJMS. However, the mapping is defined in one of the following files:

The logical names in the client's deployment descriptor are mapped as follows:

See the following sections for how the mapping occurs for both OracleAS JMS and OJMS and how clients use this naming convention:

JNDI Naming for OracleAS JMS

The JNDI names for the OracleAS JMS Destination and connection factory are defined within the jms.xml file. As Example 3-1 shows, the JNDI names for the queue and the queue connection factory are as follows:

  • The JNDI name for the topic is "jms/demoQueue."

  • The JNDI name for the topic connection factory is "jms/QueueConnectionFactory."

Prepend both of these names with "java:comp/env/". The following example shows the mapping in the orion-ejb-jar.xml file:

<resource-ref-mapping 
    name="myQCF" 
   location="java:comp/env/jms/QueueConnectionFactory">
</resource-ref-mapping>

<resource-env-ref-mapping
    name="myQueue" 
    location="java:comp/env/jms/demoQueue">
</resource-env-ref-mapping>

JNDI Naming for OJMS

The JNDI naming for OJMS Destination and connection factory objects is the same name that was specified in the orion-ejb-jar.xml file as described in "Access the OJMS Resources".

The following example maps the logical names for the connection factory and queue to their actual JNDI names. Specifically, the queue defined logically as "myQueue" in the application-client.xml file is mapped to its JNDI name of "java:comp/resource/ojmsdemo/Queues/demoQueue."

<resource-ref-mapping 
  name="myQCF" 
  location="java:comp/resource/ojmsdemo/QueueConnectionFactories/myQF">
</resource-ref-mapping>

<resource-env-ref-mapping 
  name="myQueue" 
  location="java:comp/resource/ojmsdemo/Queues/demoQueue"> 
</resource-env-ref-mapping>

JNDI Naming Property Setup for Java Application Clients

In the Oracle Application Server, a Java application client accesses a JMS Destination object by providing the following code in the JNDI properties:

java.naming.factory.initial=
   com.evermind.server.ApplicationClientInitialContextFactory 
java.naming.provider.url=opmn:ormi://$HOST:$OPMN_REQUEST_PORT:$OC4J_INSTANCE/ 
java.naming.security.principal=admin 
java.naming.security.credentials=welcome 

You must:

  • Use the ApplicationClientInitialContextFactory as your initial context factory object.

  • Supply the OPMN host and port and the OC4J instance in the provider URL.

In an OC4J standalone environment, a Java application client accesses a JMS Destination object by providing the following code in the JNDI properties:

java.naming.factory.initial=
   com.evermind.server.ApplicationClientInitialContextFactory 
java.naming.provider.url=ormi://myhost/ 
java.naming.security.principal=admin 
java.naming.security.credentials=welcome 
  

You must:

  • Use the ApplicationClientInitialContextFactory as your initial context factory object.

  • Supply the standalone OC4J host and port in the provider URL.

Client Sends JMS Message Using Logical Names

After the resources have been defined and the JNDI properties configured, the client must perform the following steps to send a JMS message. These steps summarize the procedure that Example 3-6 shows in detail.

  1. Retrieve both the configured JMS Destination and its connection factory using a JNDI lookup.

  2. Create a connection from the connection factory. If you are receiving messages for a queue, start the connection.

  3. Create a session over the connection.

  4. Providing the retrieved JMS Destination, create a sender for a queue, or a publisher for a topic.

  5. Create the message.

  6. Send the message using either the queue sender or the topic publisher.

  7. Close the queue session.

  8. Close the connection for either JMS Destination types.

Example 3-6 JSP Client Sends a Message to a Topic

The method of sending a message to a topic is similar to that of sending a message to a queue. Instead of creating a queue, you create a topic. Instead of creating a sender, you create publishers.

The following JSP client code sends a message to a topic. The code uses logical names, which are mapped in the OC4J deployment descriptor. The numbered comments in the example correspond to the steps summarized a the beginning of this section.

<%@ page import="javax.jms.*, javax.naming.*, java.util.*" %>
<%

//1a. Look up the topic. 
jndiContext = new InitialContext();
topic = (Topic)jndiContext.lookup("demoTopic");

//1b. Look up the Connection factory. 
topicConnectionFactory = (TopicConnectionFactory)
   jndiContext.lookup("myTCF");

//2 & 3. Retrieve a connection and a session on top of the connection. 
topicConnection = topicConnectionFactory.createTopicConnection();
topicSession = topicConnection.createTopicSession(true,
                                     Session.AUTO_ACKNOWLEDGE);

//4. Create the publisher for any messages destined for the topic. 
topicPublisher = topicSession.createPublisher(topic);

//5 & 6. Create and send out the message. 
for (int  ii = 0; ii < numMsgs; ii++)
{
  message = topicSession.createBytesMessage();
  String  sndstr = "1::This is message " + (ii + 1) + " " + item;
  byte[]  msgdata = sndstr.getBytes();
  message.writeBytes(msgdata);
  
  topicPublisher.publish(message);
  System.out.println("--->Sent message: " + sndstr);
}

//7 & 8. Close publisher, session, and connection for topic. 
topicPublisher.close();
topicSession.close();
topicConnection.close();
%>

Third-Party JMS Providers

This section discusses the following third-party JMS providers and how they integrate with OC4J using the resource provider interface:

Here are the operations that the resource provider interface supports:

The context-scanning resource provider class is a generic resource provider class shipped with OCJ for use with third-party message providers.

Using WebSphere MQ as a Resource Provider

WebSphere MQ is an IBM messaging provider. This example demonstrates how to make WebSphere MQ the default resource provider for JMS connections. The WebSphere MQ resources are available in OC4J under java:comp/resource/MQSeries/.

Configuring WebSphere MQ

To configure WebSphere MQ, perform the following steps:

  1. Install and configure WebSphere MQ on your system, then verify the installation by running any examples or tools supplied by the vendor. (See the documentation supplied with your software for instructions.)

  2. Configure the resource provider. You can use either of two methods to configure the resource provider: Use Oracle Enterprise Manager 10g (as shown in "Define the OJMS Resource Provider"), or use the <resource-provider> element in orion-application.xml. Use either method to add WebSphere MQ as a custom resource provider. The following example demonstrates using the <resource-provider> element to configure WebSphere MQ. You can use the same information to configure using Oracle Enterprise Manager 10g.

    <resource-provider
        class="com.evermind.server.deployment.ContextScanningResourceProvider"
        name="MQSeries">
      <description> MQSeries resource provider </description>
      <property
          name="java.naming.factory.initial"
          value="com.sun.jndi.fscontext.RefFSContextFactory">
      </property>
      <property
          name="java.naming.provider.url"
          value="file:/var/mqm/JNDI-Directory">
      </property>
    </resource-provider>
    
    
  3. Add the following WebSphere MQ JMS client JAR files to J2EE_HOME/lib:

    com.ibm.mq.jar
    com.ibm.mqbind.jar
    com.ibm.mqjms.jar
    mqji.properties
    
    
  4. Add the file system JNDI JAR files fscontext.jar and providerutil.jar to J2EE_HOME/lib.

Using SonicMQ as a Resource Provider

SonicMQ is a messaging provider from Sonic Software Corporation. The resource provider interface furnishes support for plugging in third-party JMS implementations. This example describes how to make SonicMQ the default resource provider for JMS connections. The SonicMQ resources are available in OC4J under java:comp/resource/SonicMQ.


Note:

SonicMQ broker does not embed a JNDI service. Instead, it relies on an external directory server to register the administered objects. Administered objects, such as queues, are created by an administrator—either using SonicMQ Explorer or programmatically—using the Sonic Management API. Oracle registers the administered objects from SonicMQ Explorer using the file system JNDI.

Configuring SonicMQ

To configure SonicMQ, perform the following steps:

  1. Install and configure SonicMQ on your system, then verify the installation by running any examples or tools supplied by the vendor. (See the documentation supplied with your software for instructions.)

  2. Configure the resource provider. You can use either of two methods to configure the resource provider: Use Oracle Enterprise Manager 10g (as shown in "Define the OJMS Resource Provider"), or use the <resource-provider> element in orion-application.xml. Use either method to add SonicMQ as a custom resource provider. The following example demonstrates using the <resource-provider> element to configure SonicMQ. You can use the same information to configure using Oracle Enterprise Manager 10g.

    <resource-provider
      class="com.evermind.server.deloyment.ContextScanningResourceProvider"
      name="SonicJMS">
       <description>
          SonicJMS resource provider.
       </description>
       <property name="java.naming.factory.initial"
             value="com.sun.jndi.fscontext.RefFSContextFactory">
       <property name="java.naming.provider.url"
             value="file:/private/jndi-directory/">
    </resource-provider>
    
    
  3. Add the following SonicMQ JMS client JAR files to J2EE_HOME/lib:

    Sonic_client.jar
    Sonic_XA.jar
    

Using SwiftMQ as a Resource Provider

SwiftMQ is a messaging provider from IIT Software. This example describes how to make SwiftMQ the default resource provider for JMS connections. The SwiftMQ resources are available in OC4J under java:comp/resource/SwiftMQ.

Configuring SwiftMQ

To configure SwiftMQ, perform the following steps:

  1. Install and configure SwiftMQ on your system, then verify the installation by running any examples or tools supplied by the vendor. (See the documentation provided with your software for instructions.)

  2. Configure the resource provider. You can use either of two methods to configure the resource provider: Use Oracle Enterprise Manager 10g (as shown in "Define the OJMS Resource Provider"), or use the <resource-provider> element in orion-application.xml. Use either method to add SwiftMQ as a custom resource provider. The following example demonstrates using the <resource-provider> element to configure SwiftMQ. You can use the same information to configure using Oracle Enterprise Manager 10g.

    <resource-provider
      class="com.evermind.server.deloyment.ContextScanningResourceProvider"
      name="SwiftMQ">
       <description>
          SwiftMQ resource provider.
       </description>
       <property name="java.naming.factory.initial"
          value="com.swiftmq.jndi.InitialContextFactoryImpl">
       <property name="java.naming.provider.url"
          value="smqp://localhost:4001">
    </resource-provider>
    
    
  3. Add the following SwiftMQ JMS client JAR files to J2EE_HOME/lib:

    swiftmq.jar
    

Using Message-Driven Beans

See the Message-Driven Beans (MDB) chapter of the Oracle Application Server Containers for J2EE Enterprise JavaBeans Developer's Guide for details on deploying an MDB that accesses OracleAS JMS or OJMS.


Note:

The message-driven bean (MDB) transaction timeout, as defined in the transaction-timeout attribute in the orion-ejb-jar.xml file, is an optional parameter. This attribute controls the transaction timeout interval (in seconds) for any container-managed transactional MDB that uses Oracle JMS. The default is one day (86,400 seconds). The MDB transaction-timeout attribute applies only to CMT MDBs that use Oracle JMS as the JMS provider. This attribute setting has no effect on BMT MDBs or any MDBs that use OC4J JMS. (bug 3079322)
  • JMS behavior with Oracle Application Server — If the transaction has not completed in this time frame, then the transaction is rolled back and the message is redelivered to the Destination object. After Oracle JMS attempts to redeliver the message (the default is five attempts), the message is moved to the exception queue. For more information, refer to the Oracle9i Application Developer's Guide—Advanced Queuing for Release 2 (9.2).

  • JMS behavior with OC4J — The transaction-timeout setting does not work for CMT MDBs that use OC4J JMS. The timeout is always one day and cannot be modified. When the timeout occurs, OC4J JMS redelivers the message indefinitely, until the delivery is successful. You cannot set a retry limit.

In addition, the global transaction-timeout attribute defined in the server.xml file does not have any effect on MDBs.


High Availability and Clustering for JMS

A highly available JMS server provides a guarantee that JMS requests will be serviced with no interruptions because of software or hardware failures. One way to achieve high availability is through fail-over; if one instance of the server fails, then a combination of software, hardware, and infrastructure mechanisms causes another instance of the server to take over the servicing of requests.

Table 3-8 summarizes the support for high availability in OracleAS JMS and OJMS.

Table 3-8 High Availability Summary

Feature OJMS OracleAS JMS
High availability RAC + OPMN OPMN
Configuration RAC configuration, resource provider configuration Dedicated JMS server, jms.xml configuration, opmn.xml configuration
Message store On RAC database In dedicated JMS server/persistence files
Failover Same or different machine (depending on RAC) Same or different machine within Oracle Application Server Cold Failover Cluster (Midtier)

JMS clustering provides an environment wherein JMS applications deployed in this type of environment can load balance JMS requests across multiple OC4J instances or processes. Clustering of stateless applications is trivial: The application is deployed on multiple servers, and user requests are routed to one of them.

JMS is a stateful API: Both the JMS client and the JMS server contain state about each other, which includes informations about connections, sessions, and durable subscriptions. Users can configure their environment and use a few simple techniques when writing their applications to make them cluster-friendly.

The following sections discuss how both OJMS and OracleAS JMS use high availability and clustering:

OracleAS JMS High Availability Configuration

OracleAS JMS clustering normally implies that an application deployed in this type of environment is able to load balance messages across multiple instances of OC4J. There is also a degree of high availability in this environment because the container processes can be distributed across multiple nodes. If any of the processes or nodes goes down, then the processes on an alternate node continue to service messages.

This section describes two JMS clustering scenarios:

  • OracleAS JMS Server Distributed Destinations

    In this configuration, all factories and destinations are defined on all OC4J instances. Each OC4J instance has a separate copy of each of the destinations. Each copy of the destinations is unique and is not replicated or synchronized across OC4J instances. Destinations can be persistent or in-memory. A message enqueued to one OC4J instance can be dequeued only from that OC4J instance.

    This configuration is ideal for high-throughput applications where requests must be load balanced across OC4J instances. No configuration changes are required for this scenario.

  • Cold Failover Cluster

    This configuration is a two-node cluster. Only one node is active at any time. The second node is made active if the first node fails.

  • OracleAS Dedicated JMS Server

    In this configuration, a single JVM within a single OC4J instance is dedicated as the JMS server. All other OC4J instances that are hosting JMS clients forward their JMS requests to the dedicated JMS server.

    This configuration is the easiest to maintain and troubleshoot and should be suitable for the majority of OracleAS JMS applications, especially those where message ordering is a requirement.

Terminology

The terms introduced here are explained in the Oracle Application Server High Availability Guide and the Oracle Process Manager and Notification Server Administrator's Guide.

  • OHS—Oracle HTTP Server

  • OracleAS Cluster—A grouping of similarly configured Oracle Application Server instances

  • Oracle Application Server Instance—Represents an installation of Oracle Application Server (that is, an ORACLE_HOME)

  • OC4J Instance—Within an Oracle Application Server instance there can be multiple OC4J instances, and each OC4J instance has 1 to n identically-configured JVMs.

  • Factory—Denotes a JMS connection factory

  • Destination —Denotes a JMS destination

OracleAS JMS Server Distributed Destinations

In this configuration, OHS services HTTP requests and load balances them across the two Oracle Application Server instances in an Oracle Application Server cluster. This can scale to more than two Oracle Application Server instances. This type of deployment has several advantages:

  • High throughput is achieved because applications and the JMS server are both running inside the same JVM and no interprocess communication is necessary.

  • Load balancing promotes high throughput as well as high availability.

  • There is no single point of failure. As long as one OC4J process is available, then requests can be processed.

  • Oracle Application Server instances can be clustered without impacting JMS operations.

  • Destination objects can be persistent or in-memory.

Figure 3-2 OracleAS JMS Server Distributed Destinations

Load balancing requests across two instances in a cluster.
Description of the illustration O_1087.gif

Within each Oracle Application Server instance, two OC4J instances have been defined. Each of these OC4J instances is running a separate application. In other words, OC4J instance #1 (Home1) is running Application #1 while OC4J instance #2 (Home2) is running Application #2. Remember, each OC4J instance can be configured to run multiple JVMs, allowing the application to scale across these multiple JVMs.

Within an Oracle Application Server cluster, the configuration information for each Oracle Application Server instance is identical (except for the instance-specific information such as host name, port numbers, and so on). This means that Application #1 deployed to OC4J instance #1 in Oracle Application Server instance #1 is also deployed on OC4J instance #1 in Oracle Application Server instance #2. This type of architecture allows for load balancing of messages across multiple Oracle Application Server instances—as well as high availability of the JMS application, especially if Oracle Application Server instance #2 is deployed to another node to ensure against hardware failure.

The sender and receiver of each application must be deployed together on an OC4J instance. In other words, a message enqueued to the JMS Server in one OC4J process can be dequeued only from that OC4J process.

All factories and destinations are defined on all OC4J processes. Each OC4J process has a separate copy of each of the destinations. The copies of destinations are not replicated or synchronized. So, in the diagram, Application #1 is writing to a destination called myQueue1. This destination physically exists in two locations (Oracle Application Server instance #1 and #2) and is managed by the respective JMS servers in each OC4J instance.

Note that this type of JMS deployment is suited only for specific types of JMS applications. Assuming that message order is not a concern, messages are enqueued onto distributed queues of the same name. Given the semantics of JMS point-to-point messaging, messages must not be duplicated across multiple queues. In the preceding case, messages are sent to whatever queue the load balancing algorithm determines, and the MDBs dequeue them as they arrive.

Cold Failover Cluster

This configuration is a two-node cluster. Only one node is active at any time. The second node is made active if the first node fails. For Cold Failover documentation, see the Oracle Application Server 10g High Availability Guide.

Configure

Configure both nodes identically as described in the following example. Modify the jms.xml file for both OC4J instances. Set the host parameter in the jms-server to be:

<jms-server host=vmt.us.oracle.com port="9127">
….
….
</jms-server>
 

When using file-based message persistence for a queue, the file must be located on a shared disk that is accessible by both nodes. The shared disk must fail over with the virtual IP when failing over from one node to the other. Configure the persistence-file as follows:

<queue name="Demo Queue" location="jms/demoQueue" persistence-file="/path/to/shared_file_system/demoQueueFile">
        <description>A dummy queue</description>
</queue> 
 

Update, Stop, and Start

On each node, use the following commands to update configuration, stop, and start:

$ORACLE_HOME/dcm/bin/dcmctl updateConfig -ct  oc4j
$ORACLE_HOME/opmn/bin/opmnctl stopall
$ORACLE_HOME/opmn/bin/opmnctl startall
 

OracleAS Dedicated JMS Server

In this configuration, a single OC4J instance is configured as the dedicated JMS server within an Oracle Application Server clustered environment. This OC4J instance handles all messages, thus message ordering is always maintained. All JMS applications use this dedicated server to host their connection factories and destinations, and to service their enqueue and dequeue requests.

Only one OC4J JVM is acting as the dedicated JMS provider for all JMS applications within the cluster. This is achieved by limiting the JMS port range in the opmn.xml file to only one port for the dedicated OC4J instance.

Although this diagram shows the active JMS server in the OC4J Home instance, Oracle recommends that the JMS provider be hosted in its own OC4J instance. For example, although Home is the default OC4J instance running after an Oracle Application Server install, you should create a second OC4J instance with the Oracle Enterprise Manager 10g. In the opmn.xml file example following, we have created an OC4J instance called JMSserver.

Figure 3-3 OracleAS Dedicated JMS Server

Handling requests across two instances where one is a dedicated JMS Server.
Description of the illustration O_1088.gif

After creating an OC4J instance called JMSserver, we must make the following two changes to the opmn.xml file for this Oracle Application Server instance:

  1. Make sure that only one JVM is started for this OC4J instance (JMSserver).

  2. Specify only one value for the JMS port range for this instance.

The single JVM in the OC4J instance ensures that other JVMs will not attempt to use the same set of persistent files.

The single port value ensures that OPMN always assigns this value to the dedicated JMS server. This port value is used to define the connection factory in the jms.xml file that other OC4J instances will use to connect to the dedicated JMS server.

For more information on OPMN and dynamic port assignments, see the Oracle Process Manager and Notification Server Administrator's Guide.

Modifying the OPMN Configuration


Note:

When editing any configuration file by hand (that is, not using Oracle Enterprise Manager 10g), run the following Distributed Configuration Management (DCM) command:

dcmctl updateConfig

See the Distributed Configuration Management Administrator's Guide for more information.


The following XML from the opmn.xml file shows what changes must be made and how to find where to make these changes.

  • Assuming an OC4J instance has been created through Oracle Enterprise Manager 10g called JMSserver, then the line denoted by (1) demonstrates where to locate the start of the JMSserver definition.

  • The line denoted by (2) is the JMS port range that OPMN uses when assigning JMS ports to OC4J JVMs. For the desired dedicated OC4J instance that acts as your JMS provider, narrow this range down to one value. In this example, the original range was from 3701-3800. In our connection factory definitions, we know the port to use by configuring this value as 3701-3701.

  • The line denoted by (3) defines the number of JVMs that will be in the JMSserver default island. By default, this value is set to 1. This value must always be 1.

<ias-component id="OC4J">
  (1) <process-type id="JMSserver" module-id="OC4J" status="enabled"> 
     <module-data>
       <category id="start-parameters">
         <data id="java-options" value="-server 
           -Djava.security.policy=$ORACLE_HOME/j2ee/home/config/java2.policy 
           -Djava.awt.headless=true
         "/>
       </category>
       <category id="stop-parameters">
         <data id="java-options" 
           value="-Djava.security.policy=
             $ORACLE_HOME/j2ee/home/config/java2.policy 
           -Djava.awt.headless=true"/>
       </category>
    </module-data>
    <start timeout="600" retry="2"/>
    <stop timeout="120"/>
    <restart timeout="720" retry="2"/>
    <port id="ajp" range="3000-3100"/>
    <port id="rmi" range="3201-3300"/>
    (2) <port id="jms" range="3701-3701"/> 
    (3) <process-set id="default_island" numprocs="1"/> 
  </process-type>
</ias-component>

Configuring OracleAS JMS

As already described in this scenario, one of the OC4J instances is dedicated as the JMS server. Other OC4J instances and standalone JMS clients running outside OC4J must be set up to forward JMS requests to the dedicated JMS server. All connection factories and destinations are defined in the JMS server instance's jms.xml file. This jms.xml file should then be copied to all the other OC4J instances that will be communicating with the JMS server.

The connection factories configured in the jms.xml file on the dedicated JMS server should specify, explicitly, the host name and the port number of the server. These values, in particular the port number, should also use the single port number defined by OPMN for the dedicated server as discussed above. The same connection factory configuration should also be used in all other OC4J instances so that they all point to the dedicated JMS server for their operations.

Thus, if the dedicated JMS server runs on host1, port 3701, then all connection factories defined within the jms.xml file for each OC4J instance in the cluster should point to host1, port 3701—where this port is the single port available in the opmn.xml file used in the dedicated OC4J instance (in our example, JMSserver) used for the dedicated JMS server.

The destinations configured in the jms.xml file on the dedicated JMS server should also be configured on all other OC4J instances; the physical store for these destinations, however, is on the dedicated JMS server.

Queue Connection Factory Definition Example

The following is an example for defining a queue connection factory in the jms.xml file of the dedicated OracleAS JMS server.

<!-- Queue connection factory -->
<queue-connection-factory name="jms/MyQueueConnectionFactory"
          host="host1" port="3701"
          location="jms/MyQueueConnectionFactory"/>

Administrative changes (that is, add a new Destination object) should be made to the dedicated JMS server's jms.xml file. These changes should then be made in the jms.xml files of all other OC4J instances running JMS applications. Changes can be made either by hand or by copying the dedicated JMS server's jms.xml file to the other OC4J instances.

Deploying Applications

The user decides where the JMS applications will actually be deployed. Although the dedicated JMS server services JMS requests, it can also execute deployed JMS applications. JMS applications can also be deployed to other OC4J instances (that is, Home).

Remember, the jms.xml file from the dedicated JMS server must be propagated to all OC4J instances where JMS applications are to be deployed. JMS applications can also be deployed to standalone JMS clients running in separate JVMs.

High Availability

OPMN provides the failover mechanism to keep the dedicated JMS server up and running. If for some reason the JMS server fails, OPMN detects this and restarts the JVM. If a hardware failure occurs, then the only way to recover messages is to have the persisted destinations hosted on a network file system. An OC4J instance can then be brought up and configured to point to these persisted files.

See the Oracle Process Manager and Notification Server Administrator's Guidefor more information on how OPMN manages Oracle Application Server processes.

OJMS High Availability Configuration

To enable high availability with OJMS, run the following:

  • Run an Oracle database that contains the AQ queues and topics in RAC-mode. This ensures that the database is highly available.

  • Run Oracle Application Server in OPMN-mode. This ensures that the application servers (and applications deployed on them) are highly available.

Each application instance in an Oracle Application Server cluster uses OC4J resource providers to point to the back-end Oracle database, which is operating in RAC-mode. JMS operations invoked on objects derived from these resource providers are directed to the real application clusters (RAC) database.

If a failure of the application occurs, then state information in the application is lost (that is, state of connections, sessions, and messages not yet committed). As the application server is restarted, the applications must re-create their JMS state appropriately and resume operations.

If network failover of a back-end database occurs, where the database is a non-RAC database, then state information in the server is lost (that is, state of transactions not yet committed). Additionally, the JMS objects (connection factories, Destination objects, connections, sessions, and so on) inside the application may also become invalid. The application code can see exceptions if it attempts to use these objects after the failure of the database occurs. The code throws a JMSException until it gets to the point where it can look up, through JNDI, all JMS administered objects, and proceed from there.

Failover Scenarios When Using a RAC Database With OJMS

An application that uses a RAC database must handle database failover scenarios. There are two types of failover scenarios, as described in Chapter 4, "Data Sources". The following sections demonstrate how to handle each failover scenario:

Using JMS with RAC Network Failover

A standalone OJMS client running against an RAC database must write code to obtain the connection again, by invoking the API com.evermind.sql.DbUtil.oracleFatalError(), to determine if the connection object is invalid. It must then reestablish the database connection if necessary. The oracleFatalError() method detects if the SQL error thrown by the database during network failover is a fatal error. This method takes in the SQL error and the database connection, and returns true if the error is a fatal error. If true, you may wish to aggressively roll back transactions and re-create the JMS state (such as connections, session, and messages that were lost).

The following example outlines the logic:

getMessage(QueueSesssion session)
{
    try
    {
        QueueReceiver rcvr;
         Message msgRec = null;
        QueueReceiver rcvr = session.createReceiver(rcvrQueue);
         msgRec = rcvr.receive();
    }
    catch(Exception e )
    {
        if (exc instanceof JMSException)
        {
           JMSException  jmsexc = (JMSException) exc;
           sql_ex = (SQLException)(jmsexc.getLinkedException());

           db_conn =
             (oracle.jms.AQjmsSession)session.getDBConnection();

           if ((DbUtil.oracleFatalError(sql_ex, db_conn))
           {
               // failover logic
            }
          }
     }
}

Using OJMS With Transparent Application Failover (TAF)


Note:

Chapter 4, "Data Sources" discusses transparent application failure (TAF).

In most cases where TAF is configured, the application does not notice that failover to another database instance has occurred. So, you need not do anything to recover from failure.

However, in some cases, OC4J throws an ORA error when a failure occurs. OJMS passes these errors to the user as a JMSException with a linked SQL exception. In this case, do one or more of the following:

  • As described in "Using JMS with RAC Network Failover", you can use the DbUtil.oracleFatalError method to determine if the error is a fatal error. If it is not a fatal error, then the client recovers by sleeping for a short time and then retrying the current operation.

  • You can recover from failback and transient errors caused by incomplete failover by trying to use the JMS connection after a short time. Waiting allows the database failover to recover from the failure and reinstate itself.

  • In the case of transaction exceptions (such as "Transaction must roll back" (ORA-25402) or "Transaction status unknown" (ORA-25405)) you must roll back the current operation and retry all operations past the last commit. The connection is not usable until the cause of the exception is dealt with. If this retry fails, then close and re create all connections and retry all noncommitted operations.

Server Side Sample Code for Failover for Both JMS Providers

The following shows JMS application code for a queue that is tolerant to server-side failover. This example is valid for both OJMS and OracleAS JMS.

while (notShutdown)
{
 Context ctx = new InitialContext();

 /* create the queue connection factory */
 QueueConnectionFactory qcf = (QueueConnectionFactory)
    ctx.lookup(QCF_NAME);
 /* create the queue */
 Queue q   = (Queue) ctx.lookup(Q_NAME);
 ctx.close();

 try
 {  
  /*Create a queue connection, session, sender and receiver */
  QueueConnection qc = qcf.createQueueConnection();
  QueueSession qs = qc.createQueueSession(true, 0);
  QueueSender snd = qs.createSender(q);
  QueueReceiver rcv = qs.createReceiver(q);
  
  /* start the queue */
  qc.start();
 
  /* receive requests on the queue receiver and send out
     replies on the queue sender.
  while (notDone)
  {
    Message request = rcv.receive();
    Message reply   = qs.createMessage();
    
   /* put code here to process request and construct reply */
   
   snd.send(reply);
   qs.commit();
  } 
  /* stop the queue */
  qc.stop();
 }
 catch (JMSException ex)
 {
  if (transientServerFailure)
 { // retry }
 else {
  notShutdown = false;
 }
}

Clustering Best Practices

  • Minimize JMS client-side state.

    • Perform work in transacted sessions.

    • Save/checkpoint intermediate program state in JMS queues/topics for full recoverability.

    • Do not depend on J2EE application state to be serializable or recoverable across JVM boundaries. Always use transient member variables for JMS objects, and write passivate/activate and serialize/deserialize functions that save and recover JMS state appropriately.

  • Do not use nondurable subscriptions on topics.

    • Nondurable topic subscriptions duplicate messages per active subscriber. Clustering and load-balancing creates multiple application instances. If the application creates a nondurable subscriber, it causes the duplication of each message published to the topic. This either inefficient or semantically invalid.

    • Use only durable subscriptions for topics. Use queues whenever possible.

  • Do not keep durable subscriptions alive for extended periods of time.

    • Only one instance of a durable subscription can be active at any given time. Clustering and load-balancing creates multiple application instances. If the application creates a durable subscription, only one instance of the application in the cluster succeeds. All other instances fail with a JMSException.

    • Create, use, and close a durable subscription in small time/code windows, minimizing the duration when the subscription is active.

    • Write application code that accommodates failure to create durable subscription due to clustering (when some other instance of the application running in a cluster is currently in the same block of code) and program appropriate back-off strategies. Do not always treat the failure to create a durable subscription as a fatal error.