3 Sending and Receiving Messages using the User Messaging Service Java API

This chapter describes how to use the User Messaging Service (UMS) client API to develop applications. This API serves as a programmatic entry point for Fusion Middleware application developers to incorporate messaging features within their enterprise applications.

For more information about the classes and interfaces, see User Messaging Service Java API Reference.

This chapter includes the following sections:

The API provides a plain old java (POJO/POJI) programming model and this eliminates the needs for application developers to package and implement various Java EE modules (such as an EJB module) in an application to access UMS features. This reduces application development time because developers can create applications to run in a Java EE container without performing any additional packaging of modules, or obtaining specialized tools to perform such packaging tasks.

Consumers of the UMS Java API are not required to use any Java EE mechanism such as environment entries or other Java EE deployment descriptor artifacts. Besides the overhead involved in maintaining Java EE descriptors, many client applications already have a configuration framework that does not rely on Java EE descriptors.

Note:

To learn more about the code samples for Oracle User Messaging Service, or to run the samples yourself, refer to the samples at:

http://www.oracle.com/technetwork/indexes/samplecode/sample-ums-1454424.html

3.1 Introduction to the UMS Java API

The UMS Java API is exposed as a POJO/POJI API. Consumers of the API can get an instance of a MessagingClient object using a factory method. The consumers do not need to deploy any EJB or other Java EE modules in their applications, but must ensure that the UMS libraries are available in an application' s runtime class path. The deployment is as a shared library, "oracle.sdp.client".

The UMS Java API consists of packages grouped as follows:

  • Common and Client Packages

    • oracle.sdp.messaging

    • oracle.sdp.messaging.filter: A MessagingFilter is used by an application to exercise greater control over what messages are delivered to it.

The samples with source code are available on Oracle Technology Network (OTN).

3.2 Creating a UMS Client Instance and Specifying Runtime Parameters

This section describes the requirements for creating a UMS Client. You can create a MessagingClient instance by using the code in the MessagingClientFactory class. Specifically, use the MessagingClientFactory.createMessagingClient() method to create the instance.

Client applications can specify a set of parameters at runtime when instantiating a client object. For example, you configure a MessagingClient instance by specifying parameters as a map of key-value pairs in a java.util.Map<String, Object>. Among other things, the configuration parameters serve to identify the client application, point to the UMS server, and establish security credentials. Client applications are responsible for storing and loading the configuration parameters using any available mechanism.

Table 3-1 lists some configuration parameters that may be set for the Java API. In typical use cases, most of the parameters do not need to be provided and the API implementation uses sensible default values.

Table 3-1 Configuration Parameters Specified at Runtime

Parameter Notes

APPLICATION_NAME

Optional. By default, the client is identified by its deployment name. This identifier can be overridden by specifying a value for key ApplicationInfo.APPLICATION_NAME.

APPLICATION_INSTANCE_NAME

Optional. Only required for certain clustered use cases or to take advantage of session-based routing.

SDPM_SECURITY_PRINCIPAL

Optional. By default, the client's resources are available to any application with the same application name and any security principal. This behavior can be overridden by specifying a value for key ApplicationInfo.SDPM_SECURITY_PRINCIPAL. If a security principal is specified, then all subsequent requests involving the application's resources (messages, access points, and so on.) must be made using the same security principal.

MESSAGE_LISTENER_THREADS
STATUS_LISTENER_THREADS

Optional. When listeners are used to receive messages or statuses asynchronously, the number of listener worker threads can be controlled by specifying values for the MessagingConstants.MESSAGE_LISTENER_THREADS and MessagingConstants.STATUS_LISTENER_THREADS keys.

RECEIVE_ACKNOWLEDGEMENT_MODE
LISTENER_ACKNOWLEDGEMENT_MODE

Optional. When receiving messages, you can control the reliability mode by specifying values for the MessagingConstants.RECEIVE_ACKNOWLEDGEMENT_MODE (synchronous receiving) and MessagingConstants.
LISTENER_ACKNOWLEDGEMENT_MODE (asynchronous receiving) keys.


To release resources used by the MessagingClient instance when it is no longer needed, call MessagingClientFactory.remove(client). If you do not call this method, some resources such as worker threads and JMS listeners may remain active.

Example 3-1 shows code for creating a MessagingClient instance using the programmatic approach:

Example 3-1 Programmatic Approach to Creating a MessagingClient Instance

Map<String, Object> params = new HashMap<String, Object>();
// params.put(key, value);  // if optional parameters need to be specified.
MessagingClient messagingClient = MessagingClientFactory.createMessagingClient(params); 

A MessagingClient cannot be reconfigured after it is instantiated. Instead, you must create a new instance of the MessagingClient class using the desired configuration.

The API reference for class MessagingClientFactory can be accessed from the Javadoc.

3.3 Sending a Message

The client application can create a message object using the MessagingFactory class of oracle.sdp.messaging. MessagingFactory. You can use other methods in this class to create Addresses, AccessPoints, MessageFilters, and MessageQueries. See User Messaging Service Java API Reference for more information about these methods.

The client application can then send the message. The API returns a String identifier that the client application can later use to retrieve message delivery status. The status returned is the latest known status based on UMS internal processing and delivery notifications received from external gateways.

The types of messages that can be created include plaintext messages, multipart messages that can consist of text/plain and text/html parts, and messages that include the creation of delivery channel (DeliveryType) specific payloads in a single message for recipients with different delivery types.

The section includes the following topics:

3.3.1 Creating a Message

This section describes the various types of messages that can be created.

3.3.1.1 Creating a Plaintext Message

Example 3-2 shows how to create a plaintext message using the UMS Java API.

Example 3-2 Creating a Plaintext Message Using the UMS Java API

Message message = MessagingFactory.createTextMessage("This is a Plain Text message.");

or

Message message = MessagingFactory.createMessage();
message.setContent("This is a Plain Text message.", "text/plain");

3.3.1.2 Creating a Multipart/Alternative Message (with Text/Plain and Text/HTML Parts)

Example 3-3 shows how to create a multipart or alternative message using the UMS Java API.

Example 3-3 Creating a Multipart or Alternative Message Using the UMS Java API

Message message = MessagingFactory.createMessage();
MimeMultipart mp = new MimeMultipart("alternative");
MimeBodyPart mp_partPlain = new MimeBodyPart();
mp_partPlain.setContent("This is a Plain Text part.", "text/plain");
mp.addBodyPart(mp_partPlain);
MimeBodyPart mp_partRich = new MimeBodyPart();
mp_partRich
        .setContent(
                "<html><head></head><body><b><i>This is an HTML part.</i></b></body></html>",
                "text/html");
mp.addBodyPart(mp_partRich);
message.setContent(mp, "multipart/alternative");

3.3.1.3 Creating Delivery Channel-Specific Payloads in a Single Message for Recipients with Different Delivery Types

When sending a message to a destination address, there could be multiple channels involved. Oracle UMS application developers are required to specify the correct multipart format for each channel.

Example 3-4 shows how to create delivery channel (DeliveryType) specific payloads in a single message for recipients with different delivery types.

Each top-level part of a multiple payload multipart/alternative message should contain one or more values of this header. The value of this header should be the name of a valid delivery type. Refer to the available values for DeliveryType in the enum DeliveryType.

Example 3-4 Creating Delivery Channel-specific Payloads in a Single Message for Recipients with Different Delivery Types

Message message = MessagingFactory.createMessage();
 
// create a top-level multipart/alternative MimeMultipart object.
MimeMultipart mp = new MimeMultipart("alternative");
 
// create first part for SMS payload content.
MimeBodyPart part1 = new MimeBodyPart();
part1.setContent("Text content for SMS.", "text/plain");
 
part1.setHeader(Message.HEADER_NS_PAYLOAD_PART_DELIVERY_TYPE, "SMS");
 
// add first part
mp.addBodyPart(part1);
 
// create second part for EMAIL and IM payload content.
MimeBodyPart part2 = new MimeBodyPart();
MimeMultipart part2_mp = new MimeMultipart("alternative");
MimeBodyPart part2_mp_partPlain = new MimeBodyPart();
part2_mp_partPlain.setContent("Text content for EMAIL/IM.", "text/plain");
part2_mp.addBodyPart(part2_mp_partPlain);
MimeBodyPart part2_mp_partRich = new MimeBodyPart();
part2_mp_partRich.setContent("<html><head></head><body><b><i>" + "HTML content for EMAIL/IM." +
"</i></b></body></html>", "text/html");
part2_mp.addBodyPart(part2_mp_partRich);
part2.setContent(part2_mp, "multipart/alternative");
 
part2.addHeader(Message.HEADER_NS_PAYLOAD_PART_DELIVERY_TYPE, "EMAIL");
part2.addHeader(Message.HEADER_NS_PAYLOAD_PART_DELIVERY_TYPE, "IM");
 
// add second part
mp.addBodyPart(part2);
 
// set the content of the message
message.setContent(mp, "multipart/alternative");
 
// set the MultiplePayload flag to true
message.setMultiplePayload(true);

The API reference for class MessagingFactory , interface Message and enum DeliveryType can be accessed from User Messaging Service Java API Reference.

3.3.2 Addressing a Message

This section describes type of addresses and how to create address objects.

3.3.2.1 Types of Addresses

There are two types of addresses, device addresses and user addresses. A device address can be of various types, such as email addresses, instant messaging addresses, and telephone numbers. User addresses are user IDs in a user repository.

3.3.2.2 Creating Address Objects

You can address senders and recipients of messages by using the class MessagingFactory to create Address objects defined by the Address interface.

3.3.2.2.1 Creating a Single Address Object

Example 3-5 shows code for creating a single Address object:

Example 3-5 Creating a Single Address Object

Address recipient = MessagingFactory.createAddress("Email:john@example.com");
3.3.2.2.2 Creating Multiple Address Objects in a Batch

Example 3-6 shows code for creating multiple Address objects in a batch:

Example 3-6 Creating Multiple Address Objects in a Batch

String[] recipientsStr = {"Email:john@example.com", "SMS:123456"};
Address[] recipients = MessagingFactory.createAddress(recipientsStr);
3.3.2.2.3 Adding Sender or Recipient Addresses to a Message

Example 3-7 shows code for adding sender or recipient addresses to a message:

Example 3-7 Adding Sender or Recipient Addresses to a Message

Address sender = MessagingFactory.createAddress("Email:john@example.com");
Address recipient = MessagingFactory.createAddress("Email:jane@example.com");
message.addSender(sender);
message.addRecipient(recipient);

3.3.2.3 Creating a Recipient with a Failover Address

Example 3-8 shows code for creating a recipient with a failover address:

Example 3-8 Creating a Single Address Object with Failover

String recipientWithFailoverStr = "Email:john@example.com, SMS:123456";
Address recipient = MessagingFactory.createAddress(recipientWithFailoverStr);

3.3.2.4 API Reference for Class MessagingFactory

The API reference for class MessagingFactory can be accessed from User Messaging Service Java API Reference.

3.3.2.5 API Reference for Interface Address

The API reference for interface Address can be accessed from User Messaging Service Java API Reference.

3.3.3 Sending Group Messages

You can send messages to a group of users by sending it to a group URI, or sending a message to LDAP groups (or enterprise roles) and application roles.

3.3.3.1 Sending Messages to a Group

You can send messages to an LDAP group or to enterprise roles. The message is resolved to the users by fetching the email address of each user. If the email address of a particular user specified in the group does not exist, then that user is skipped.

To send a message to a group, use the MessagingFactory property to create a recipient address of type GROUP and send the message as shown in Example 3-9.

Example 3-9 Creating and addressing a message to a group

Address groupAddr = MessagingFactory.createAddress("GROUP:MyGroup");
Message message = MessagingFactory.createTextMessage("Sending message to a group");
message.addRecipient(groupAddr);
message.setSubject("Testing groups");
String id = messagingClient.send(message);

The group address groupAddr is eventually replaced by user addresses and the result will be as shown in Example 3-10.

Example 3-10 Group Address replaced by user addresses

Address groupMember1 = MessagingFactory.createAddress("USER:MyGroupMember1");
Address groupMember2 = MessagingFactory.createAddress("USER:MyGroupMember2");
Address groupMember3 = MessagingFactory.createAddress("USER:MyGroupMember3");
Message message = MessagingFactory.createTextMessage("Sending message to a group");
message.addRecipient(groupMember1);
message.addRecipient(groupMember2);
message.addRecipient(groupMember3);
message.setSubject("Testing groups");
String id = messagingClient.send(message);

3.3.3.2 Sending Messages to a Group Through a Specific Channel

You can specify the outgoing channel before sending a group message. To specify the outgoing channel for a group message, you must set the DeliveryType property of the group address (groupAddr) as shown in Example 3-11.

Example 3-11 Creating and addressing a message to a group through a channel

Address groupAddr = MessagingFactory.createAddress("GROUP:MyGroup");
groupAddr.setDeliveryType(DeliveryType.EMAIL);
Message message = MessagingFactory.createTextMessage("Sending message to a group");
message.addRecipient(groupAddr);
message.setSubject("Testing groups through email");
String id = messagingClient.send(message);

3.3.3.3 Sending Messages to an Application Role

An application role is a collection of users, groups, and other application roles; it can be hierarchical. Application roles are defined by application policies and not necessarily known to a JavaEE container. For more information about application role, see Oracle Fusion Middleware Application Security Guide.

Note:

An application role may map to other application roles, such as the following roles:

  • Authenticated role: Any user who successfully authenticates. This may result in a large number of recipients.

  • Anonymous role: There will no recipient for this role.

To send a message to an Application role, use must create a recipient address of type APPROLE by using the MessagingFactory property. The message should, then, be sent. An application role belongs to an application ID (also known as application name or application stripe). Therefore, both these parameters must be specified in the recipient address as shown in Example 3-12.

Example 3-12 Creating and addressing a message to an application role

Address appRoleAddr =
MessagingFactory.createAppRoleAddress("myAppRole", "theAppId");
Message message = MessagingFactory.createTextMessage("Message to an application role");
message.addRecipient(appRoleAddr);
message.setSubject("Testing application roles");
String id = messagingClient.send(message);

The application role APPROLE is eventually replaced by user addresses. However, if the application id is that of the calling application, then you need not specify the application id when creating the recipient address. UMS will automatically fetch the application id that is specified in the application.name parameter in the JpsFilter(web.xml) or JpsInterceptor(ejb-jar.xml). For more information, see Oracle Fusion Middleware Application Security Guide.

3.3.3.4 Sending Messages to an Application Role Through a Specific Channel

The user can specify a channel for the outgoing message in the same way as specifying a channel for sending a message to a group. You must set the delivery type on the application role address.

The following is an example of sending a message to an application role specifying email as the delivery channel:

Example 3-13 Creating and addressing a message to an application through a channel

Address appRoleAddr =
MessagingFactory.createAppRoleAddress("myAppRole", "theAppId");
appRoleAddr.setDeliveryType(DeliveryType.EMAIL);
Message message = MessagingFactory.createTextMessage("Message to an application role");
message.addRecipient(appRoleAddr);
message.setSubject("Testing application roles");
String id = messagingClient.send(message);

3.3.4 User Preference Based Messaging

When sending a message to a user recipient (to leverage the user's messaging preferences), you can pass current values for various business terms in the message as metadata. The UMS server matches the supplied facts in the message against conditions for business terms specified in the user's messaging filters.

Note:

All facts must be added as metadata in the Message.NAMESPACE_NOTIFICATION_PREFERENCES namespace. Metadata in other namespaces are ignored (for resolving User Communication Preferences).

Example 3-14 shows how to specify a user recipient and supply facts for business terms for the user preferences in a message. For a complete list of supported business terms, refer to Chapter 6, "User Communication Preferences."

Example 3-14 User Preference Based Messaging

Message message = MessagingFactory.createMessage();
// create and add a user recipient
Address userRecipient1 = MessagingFactory.createAddress("USER:sampleuser1");
message.addRecipient(userRecipient1);
// specify business term facts
message.setMetaData(Message.NAMESPACE_NOTIFICATION_PREFERENCES, "Customer
Name", "ACME");
//  where "Customer Name" is the Business Term name, and "ACME" is the
Business Term value (i.e, fact).

3.4 Retrieving Message Status

After sending a message, you can use Oracle UMS to retrieve the message status either synchronously or asynchronously.

3.4.1 Synchronous Retrieval of Message Status

To perform a synchronous retrieval of current status, use the following flow from the MessagingClient API:

String messageId = messagingClient.send(message);
Status[] statuses = messagingClient.getStatus(messageId);

or,

Status[] statuses = messagingClient.getStatus(messageId, address[]) --- where
 address[] is an array of one or more of the recipients set in the message.

3.4.2 Asynchronous Receiving of Message Status

When asynchronously receiving status, the client application specifies a Listener object and an optional correlator object. When incoming status arrives, the listener' s onStatus callback is invoked. The originally-specified correlator object is also passed to the callback method.

3.4.2.1 Creating a Listener Programmatically

Listeners are purely programmatic. You create a listener by implementing the oracle.sdp.messaging.Listener interface. You can implement it as any concrete class - one of your existing classes, a new class, or an anonymous or inner class.

The following code example shows how to implement a status listener:

import oracle.sdp.messaging.Listener;
 
    public class StatusListener implements Listener {

      @Override
      public void onMessage(Message message, Serializable correlator) {
      }  
      @Override
      public void onStatus(Status status, Serializable correlator) {
          System.out.println("Received Status: " + status + " with optional correlator: " +
 
correlator);
      }
    }

You pass a reference to the Listener object to the setStatusListener or send methods, as described in "Default Status Listener" and "Per Message Status Listener". When a status arrives for your message, the UMS infrastructure invokes the Listener's onStatus method as appropriate.

3.4.2.2 Default Status Listener

The client application typically sets a default status listener (Example 3-15). When the client application sends a message, delivery status callbacks for the message invoke the default listener's onStatus method.

Example 3-15 Default Status Listener

messagingClient.setStatusListener(new MyStatusListener());
messagingClient.send(message);

3.4.2.3 Per Message Status Listener

In this approach, the client application sends a message and specifies a Listener object and an optional correlator object (Example 3-16). When delivery status callbacks are available for that message, the specified listener's onStatus method is invoked. The originally-specified correlator object is also passed to the callback method.

Example 3-16 Per Message Status Listener

messagingClient.send(message, new MyStatusListener(), null);

3.5 Receiving a Message

This section describes how an application receives messages. To receive a message you must first register an access point.

An application that wants to receive incoming messages must register one or more access points that represent the recipient addresses of the messages. The server matches the recipient address of an incoming message against the set of registered access points, and routes the incoming message to the application that registered the matching access point. From the application perspective there are two modes for receiving a message, synchronous and asynchronous.

3.5.1 Registering an Access Point

The client application can create and register an access point, specifying that it wants to receive incoming messages sent to a particular address. Since the client application has not specified any message listeners, any received messages are held by UMS. The client application can then invoke the receive method to fetch the pending messages. When receiving messages without specifying an access point, the application receives messages for any of the access points that it has registered. Otherwise, if an access point is specified, the application receives messages sent to that access point.

AccessPoint represents one or more device addresses to receive incoming messages.

You can use MessagingFactory.createAccessPoint to create an access point and MessagingClient.registerAccessPoint to register it for receiving messages.

To register an email access point:

Address apAddress = MessagingFactory.createAddress("EMAIL:user1@example.com");
AccessPoint ap = MessagingFactory.createAccessPoint(apAddress);
MessagingClient.registerAccessPoint(ap);

To register an SMS access point for the number 9000:

AccessPoint accessPointSingleAddress =
 MessagingFactory.createAccessPoint(AccessPoint.AccessPointType.SINGLE_ADDRESS,
 DeliveryType.SMS, "9000");
messagingClient.registerAccessPoint(accessPointSingleAddress);

To register SMS access points in the number range 9000 to 9999:

AccessPoint accessPointRangeAddress =
 MessagingFactory.createAccessPoint(AccessPoint.AccessPointType.NUMBER_RANGE,
 DeliveryType.SMS,"9000,9999");
messagingClient.registerAccessPoint(accessPointRangeAddress);

3.5.2 Synchronous Receiving

A receive is a nonblocking operation. If there are no pending messages for the application or access point, the call returns a null immediately. Receive is not guaranteed to return all available messages, but may return only a subset of available messages for efficiency reasons.

You can use the method MessagingClient.receive to synchronously receive messages that UMS makes available to the application. This is a convenient polling method for light-weight clients that do not want the configuration overhead associated with receiving messages asynchronously. This method returns an array of messages that are immediately available in the application inbound queue.

Note:

A single invocation does not guarantee retrieval of all available messages. You must poll to ensure receiving all available messages.

3.5.3 Asynchronous Receiving

When asynchronously receiving messages, the client application registers an access point and specifies a Listener object and an optional correlator object. When incoming messages arrive at the specified access point address, the listener' s onMessage callback is invoked. The originally-specified correlator object is also passed to the callback method.

3.5.3.1 Creating a Listener Programmatically

Listeners are purely programmatic. You create a listener by implementing the oracle.sdp.messaging.Listener interface. You can implement it as any concrete class - one of your existing classes, a new class, or an anonymous or inner class.

The following code example shows how to implement a message listener:

import oracle.sdp.messaging.Listener;
 
    public class MyListener implements Listener {
 
      @Override
      public void onMessage(Message message, Serializable correlator) {
          System.out.println("Received Message: " + message + " with optional correlator: " +
correlator);
      }  
      @Override
      public void onStatus(Status status, Serializable correlator) {
          System.out.println("Received Status: " + status + " with optional correlator: " +
correlator);
      }
     
    }

You pass a reference to the Listener object to the setMessageListener or registerAccessPoint methods, as described in "Default Message Listener" and "Per Access Point Message Listener". When a message arrives for your application, the UMS infrastructure invokes the Listener's onMessage method.

3.5.3.2 Default Message Listener

The client application typically sets a default message listener (Example 3-17). This listener is invoked for any delivery statuses for messages sent by this client application that do not have an associated listener. When Oracle UMS receives messages addressed to any access points registered by this client application, it invokes the onMessage callback for the client application's default listener.

To remove a default listener, call this method with a null argument.

Example 3-17 Default Message Listener

messagingClient.setMessageListener(new MyListener());

See the sample application usermessagingsample-echo for detailed instructions on asynchronous receiving.

3.5.3.3 Per Access Point Message Listener

The client application can also register an access point and specify a Listener object and an optional correlator object (Example 3-18). When incoming messages arrive at the specified access point address, the specified listener' s onMessage method is invoked. The originally-specified correlator object is also passed to the callback method.

Example 3-18 Per Access Point Message Listener

messagingClient.registerAccessPoint(accessPoint, new MyListener(), null);

3.5.4 Message Filtering

A MessageFilter is used by an application to exercise greater control over what messages are delivered to it. A MessageFilter contains a matching criterion and an action. An application can register a series of message filters; they are applied in order against an incoming (received) message; if the criterion matches the message, the action is taken. For example, an application can use MessageFilters to implement necessary blacklists, by rejecting all messages from a given sender address.

You can use MessagingFactory.createMessageFilter to create a message filter, and MessagingClient.registerMessageFilter to register it. The filter is added to the end of the current filter chain for the application. When a message is received, it is passed through the filter chain in order; if the message matches a filter's criterion, the filter's action is taken immediately. If no filters match the message, the default action is to accept the message and deliver it to the application.For example, to reject a message with the subject "spam":

MessageFilter subjectFilter = MessagingFactory.createMessageFilter("spam",
 MessageFilter.FieldType.SUBJECT, null, MessageFilter.Action.REJECT);
messagingClient.registerMessageFilter(subjectFilter);

To reject messages from email address spammer@foo.com:

MessageFilter senderFilter =
 MessagingFactory.createBlacklistFilter("spammer@foo.com");
messagingClient.registerMessageFilter(senderFilter);

3.6 Configuring for a Cluster Environment

The API supports an environment where client applications and the UMS server are deployed in a cluster environment. For a clustered deployment to function as expected, client applications must be configured correctly. The following rules apply:

  • Two client applications are considered to be instances of the same application if they use the same ApplicationName configuration parameter. Typically this parameter is synthesized by the API implementation and does not need to be populated by the application developer.

  • Instances of the same application share most of their configuration, and artifacts such as Access Points and Message Filters that are registered by one instance are shared by all instances.

  • The ApplicationInstanceName configuration parameter enables you to distinguish instances from one another. Typically this parameter is synthesized by the API implementation and does not need to be populated by the application developer. Refer to the Javadoc for cases in which this value must be populated.

  • Application sessions are instance-specific. You can set the session flag on a message to ensure that any reply is received by the instance that sent the message.

  • Listener correlators are instance-specific. If two different instances of an application register listeners and supply different correlators, then when instance A's listener is invoked, correlator A is supplied; when instance B's listener is invoked, correlator B is supplied.

3.7 Using UMS Client API for XA Transactions

UMS provides support for XA enabled transactions for outbound and inbound messages. The industry standard, X/Open XA protocol, is widely supported in other Oracle products such as Business Process Management (BPM).

Note:

You do not need to install the XA support feature, as this feature is included in the UMS server and in the UMS client. Also note that the XA support is available only for the POJO API, not for the Web Services API.

3.7.1 About XA Transactions

Java Messaging Service (JMS) defines a common set of enterprise messaging concepts and facilities. It is used in User Messaging Service (UMS) for messaging, queuing, sorting, and routing. Java Transaction API (JTA) specifies local Java interfaces between a transaction manager and the parties involved in a distributed transaction system - the application, the resource manager, and the application server. The JTA package consists of the following three components:

  • A high-level application interface that allows a transactional application to demarcate transaction boundaries.

  • A Java mapping of the industry standard X/Open XA protocol that allows a transactional resource manager to participate in a global transaction controlled by an external transaction manager.

  • A high-level transaction manager interface that allows an application server to control transaction boundary demarcation for an application being managed by the application server.

JTA is used by a Java Messaging Service (JMS) provider to support XA transactions (also known as distributed transactions). The JMS provider that supports XA Resource interface is able to participate as a resource manager in a distributed transaction processing system that uses a two-phase commit transaction protocol.

3.7.2 Sending and Receiving XA Enabled Messages

The XA support enables UMS to send messages from within a transaction boundary only when the transaction is committed. If the transaction is rolled back, then the sending of the message fails. A commit leads to a successful transaction; whereas rollback leaves the message unaltered. UMS provides XA transaction support for both, outbound and inbound messages.

Outbound messaging using XA

The messages sent from a UMS client application to recipients via UMS server are called outbound messages. When an XA transaction is enabled on a UMS client, an outbound message is sent to the UMS server, only if the transaction is committed. Upon successful transaction, the message is safely stored and prepared for delivery to the recipients. If the client transaction fails to commit and a rollback occurs, then the message is not sent to the UMS server for delivery.

The following code snippet demonstrates how to send an outbound message using XA:

transaction.begin();
String messageID = mClient.send(message);
transaction.commit();

Inbound messaging using XA

The following code snippet demonstrates how to receive an inbound message using XA:

The messages received by a UMS driver from the UMS server and later routed to a UMS client are called inbound messages. When an XA transaction is enabled on a UMS client, an inbound message is retrieved from the UMS server and deleted from UMS server store, only if the transaction is committed. If a transaction rollback occurs, then the message is left unaltered in the UMS server for later redelivery.

transaction.begin();
messages = mClient.receive();
 
        for (Message receivedMessage : messages) {
                        // process individual messages here.
}
transaction.commit();

Once the external XA transaction is committed by the client, the messages received by the UMS driver will be permanently removed from the UMS server. If the transaction fails to commit and a rollback occurs, then the messages will be received after the transaction times out. This message will be redelivered with the getJMSRedelivered method returning true. For more information about this method, see User Messaging Service Java API Reference.

The transaction timed out can be changed by calling setTransactionTimeout. To receive messages that failed to commit due to a server crash, the server and the client must be restarted, or the specific server migration procedure must be executed. For more information, see chapter Configuring Advanced JMS System Resources in Oracle Fusion Middleware Configuring and Managing JMS for Oracle WebLogic Server.

Using a listener for XA transactions

You can also use a listener in a transaction while receiving messages. This is done by specifying the constant MessagingConstants.LISTENER_TRANSACTED_MODE. Set the value of this constant to TRUE or FALSE when creating a MessagingClient instance, as shown in the example below.

Note:

If you use a listener, transactions will be committed when the messaging constant LISTENER_TRANSACTED_MODE is set to TRUE and when no exceptions are raised. When LISTENER_TRANSACTED_MODE is set to FALSE, transactions will be committed irrespective of the exceptions.

If you want to roll back a transaction, set the exception accordingly. For more information about ListenerException, see User Messaging Service Java API Reference.

Example 3-19 Using a listener to receive XA enabled messages

Map<String, Object> params = new HashMap<String, Object>();
params.put(MessagingConstants.LISTENER_TRANSACTED_MODE, Boolean.TRUE);
MessagingClient mClient = MessagingClientFactory.createMessagingClient(params);

mClient.registerAccessPoint(MessagingFactory.createAccessPoint(receiverAddr),
new MyListener(), null);

private class MyListener implements Listener {
 
    @Override
    public void onMessage(Message message,
    Serializable correlator) throws ListenerException {
 
     }}

For more information about the messaging constant, see User Messaging Service Java API Reference.

Using EJB calls for XA transactions

You can send XA enabled messages using EJB calls. To roll back the transaction, specify the setRollbackOnly() method. For more information about this method, see: http://docs.oracle.com/javaee/7/api/javax/ejb/EJBContext.html#setRollbackOnly()

You can also control the scope of a transaction by specifying the transaction attributes (such as NotSupported, RequiresNew, and Never) as described in the Java EE tutorial at:

http://docs.oracle.com/javaee/6/tutorial/doc/bncij.html

Example 3-20 Sending XA enabled messaging using an EJB call

Map<String, Object> params = new HashMap<String, Object>();
MessagingClient mClient =
MessagingClientFactory.createMessagingClient(params);
MimeMultipart mp = new MimeMultipart("alternative");
MimeBodyPart part1 = new MimeBodyPart();
Message message = MessagingFactory.createMessage();
...
...

mClient.sendMessage();

if(failure)
setRollbackOnly()

3.8 Using UMS Java API to Specify Message Resends

UMS allows you to schedule a message resend when the message send attempt fails. You can specify the maximum number of message resends by calling the setMaxResend method as shown in the following example:

MessageInfo msgInfo = message.getMessageInfo();
msgInfo.setMaxResend(1);
String mid = messagingClient.send(message);

The status of the failover addresses can be received by calling getTotalFailovers() and getFailoverOrder(). When failover order equals total failovers, the API user knows that the failover chain is exhausted. However, the resend functionality works as a loop over the failover chain. You can call getMaxResend() and getCurrentResend() to know when the resend and failover chain is completely exhausted.

For more information about setMaxResend, getTotalFailovers , getFailoverOrder, and other methods, see User Messaging Service Java API Reference.

3.9 Configuring Security

Client applications may need to specify one or more additional configuration parameters (described in Table 3-1) to establish a secure listener.

3.10 Threading Model

Client applications that use the UMS Java API are usually multithreaded. Typical scenarios include a pool of EJB instances, each of which uses a MessagingClient instance; and a servlet instance that is serviced by multiple threads in a web container. The UMS Java API supports the following thread model:

  • Each call to MessagingClientFactory.createMessagingClient returns a new MessagingClient instance.

  • When two MessagingClient instances are created by passing parameter maps that are equal to MessagingClientFactory.createMessagingClient, they are instances of the same client. Instances created by passing different parameter maps are instances of separate clients.

  • An instance of MessagingClient is not thread safe when it has been obtained using MessagingClientFactory.createMessagingClient. Client applications must ensure that a given instance is used by only one thread at a time. They may do so by ensuring that an instance is only visible to one thread at a time, or by synchronizing access to the MessagingClient instance.

  • Two instances of the same client (created with identical parameter maps) do share some resources – notably they share Message and Status Listeners, and use a common pool of Worker threads to execute asynchronous messaging operations. For example, if instance A calls setMessageListener(), and then instance B calls setMessageListener(), then B's listener is the active default message listener.

The following are typical use cases:

  • To use the UMS Java API from an EJB (either a Message Driven Bean or a Session Bean) application, the recommended approach is to create a MessagingClient instance in the bean' s ejbCreate (or equivalent @PostConstruct) method, and store the MessagingClient in an instance variable in the bean class. The EJB container ensures that only one thread at a time uses a given EJB instance, which ensures that only one thread at a time accesses the bean' s MessagingClient instance.

  • To use the UMS Java API from a Servlet, there are several possible approaches. In general, web containers create a single instance of the servlet class, which may be accessed by multiple threads concurrently. If a single MessagingClient instance is created and stored in a servlet instance variable, then access to the instance must be synchronized.

    Another approach is to create a pool of MessagingClient instances that are shared among servlet threads.

    Finally, you can associate individual MessagingClient instances with individual HTTP Sessions. This approach allows increased concurrency compared to having a single MessagingClient for all servlet requests. However, it is possible for multiple threads to access an HTTP Session at the same time due to concurrent client requests, so synchronization is still required in this case.

3.10.1 Listener Threading

For asynchronous receiving described in Section 3.4.2, "Asynchronous Receiving of Message Status" and Section 3.5.3, "Asynchronous Receiving" UMS by default uses one thread for incoming messages and one thread for incoming status notifications (assuming at least one message or status listener is registered, respectively). Client applications can increase the concurrency of asynchronous processing by configuring additional worker threads. This is done by specifying integer values for the MessagingConstants.MESSAGE_LISTENER_THREADS and MessagingConstants.STATUS_LISTENER_THREADS keys, settings these values to the desired number of worker threads in the configuration parameters used when creating a MessagingClient instance.