What is JMSJCA

J2EE 1.4 specifies J2EE Connector Architecture 1.5 as the basic mechanism to integrate JMS providers with J2EE application servers. JMSJCA is an implementation of the Java Connector Architecture 1.5 that is used to integrate JMS providers within J2EE application servers. Not only does JMSJCA supports several different JMS providers, it also supports a number of advanced features not found in other connectors.

The generic JMSJCA adapter is packaged and specialized for the following connectors:

Release information

5.1.0

5.1.1

5.1.2

5.1.3

5.1.3U1

5.1.3.SUNIT

6

Features

JMSJCA adapters provide the following advanced features:

How to use a JMSJCA adapter

There are two ways to use a JMSJCA adapter with an EAR:
  1. the adapter can be packaged in the EAR as a "local RAR"
  2. the adapter can be deployed in the application server as a "global RAR"

In either case, to use of outbound connections, a mapping must be made in the deployment descriptor of the EJB or servlet that ties the JNDI names of the connection factories to names in the ENC (Environment Naming Context). The application code can then lookup the JMS connection factory and create outbound connections.

Example:

TopicConnection topicConn = null;
try {
    TopicConnectionFactory factory = (TopicConnectionFactory) context.lookup("java:comp/env/jms/TCF");
    topicConn = factory.createTopicConnection();
    TopicSession topicSession = topicConn.createTopicSession(false, Session.AUTO_ACKNOWLEDGE);
    topic = (Topic) context.lookup("jms/topic1");
    TopicPublisher publisher = topicSession.createPublisher(topic);
    TextMessage txtMsg = topicSession.createTextMessage("Hello world");
    publisher.publish(txtMsg);
} finally {
    if (topicConn != null) {
        topicConn.close();
    }
}

Note that because a JMS connection is a transactional resource:

Configuration

The connector is configured through ra.xml. The configuration is divided into these sections:
  1. for each MDB there is an activation spec; it is defined in ra.xml, but the values for the activation spec parameters are specified in the ejb-jar.xml of the EJB.
  2. for each connection factory there is a section in the ra.xml in which parameters can be specified
  3. the connector can have default values that will be used if values are not set in the connection factory settings or activation spec. These values are specified at the top of ra.xml.

Generic parameters

The following parameters can be specified at the connector level, i.e. at the top of ra.xml.
Parameter nameMeaning
ConnectionURLdefault value for connectionURL
UserNamedefault username
Passworddefault password
MBeanObjectNameMBeanObjectName: name of the MBean that the adapter should create. The MBean will provide access to statistical info and a relay MBean for management of destinations in the JMS server.
MBeanServerDomainMBean server domain: JMX name for the MBeanServer to be used to register the RA MBean in. Not used if no MBean name is specified. When left blank or unspecified, the default MBeanServer will be used.
OptionsSee below

Connection factory configuration

Parameter nameMeaning
ConnectionURLconnection URL. Takes precedence over the value specified in the generic section of ra.xml. This can be of the form of lookup://name. See below.
UserNameusername. Takes precedence over the value specified in the generic section of ra.xml.
Passwordpassword. Takes precedence over the value specified in the generic section of ra.xml.
ClientIdthe client id, used in Connection.setClientID()
ProducerPoolingboolean that indicates if producers are pooled by the connector. For some JMS providers JMS producers (topic-publishers and queue-senders) are expensive to create because it may involve creating a socket connection. When ProducerPooling is turned on, the socket resources of a producer will not be closed when the application closes the producer. Instead, the producer will be returned to a pool that is tied to the session. The next time the application uses a producer on the session, it will be reused from the pool.
IdleTimeoutthis parameter is used for connection validation. If a connection is not used successfully for a period longer than the IdleTimeout period, the connection is marked as invalid. "Successfully" is defined as a msg was sent or received without an exception from the underlying JMS implementation.
Optionsswitches and options, see below

Configuration of the activation spec

Parameter namerequired?Meaning
ConnectionURLnot requiredconnectionURL. Takes precedence over the value specified in the generic section of ra.xml.
UserNamenot requiredusername. Takes precedence over the value specified in the generic section of ra.xml.
Passwordnot requiredpassword. Takes precedence over the value specified in the generic section of ra.xml.
ClientIdnot requiredthe client id passed to setClientID)(). Note that many JMS providers require a client id to be set in order to use durable topics. If this value is omitted or left blank, a client ID will be automatically generated based on the durable subscription name.
destinationmandatoryname of the queue or topic that messages should be read from. To indicate that this is the name of an object that should be looked up in JNDI, prefix the name with lookup://. If this prefix is not found, the queue or topic will be created using the approach most suitable for the JMS server, typically by calling createQueue(name) or createTopic(name) See release notes of individual adapters for more information.
destinationTypemandatoryshould be either javax.jms.Queue or javax.jms.Topic
subscriptionDurabilitydependsapplies only to topics. Should be set to either Durable or NonDurable
subscriptionNamedependsin case the subscriptionDurability is set to Durable, this parameter must be specified and must indicate the name of the durable subscriber.
ConcurrencyModenot requiredeither serial, cc, or sync. See below.
endpointPoolMaxSizenot requiredIn the case of concurrent processing, this value (integer) specifies the number of MDBs that can be used to process messages concurrently. This should match the value specied in the application server specific deployment descriptor that specifies the number of MDBs the bean pool.
MBeanNamenot requiredMBeanObjectName: name of the MBean that the adapter should create. The MBean will provide access to statistical info and a relay MBean for management of destinations in the JMS server.
selectornot requiredspecifies a JMS message selector (optional)
Optionsnot requiredswitches and options, see below
RedeliveryHandlingnot requiredsee below.
BatchSizenot requiredsee below. (Set to a number greater than 1 to turn on batching)
HoldUntilAcknot requiredsee below. (Set to 1 to turn on this special mode)
ContextNamenot requiredBefore the inbound connector calls the onMessage() method on an MDB, it will first log a message to the Logger with name com.stc.EnterContext. This entry in the activation spec defines the contents of the message. After the onMessage() method has returned, the same message (i.e. the value of ContextName), will be logged to the com.stc.ExitContext logger. The parameter ContextName may be left blank or may be omitted; in that case the value of ContextName is not logged to com.stc.EnterContext and com.stc.ExitContext. The Java CAPS application server interprets the com.stc.EnterContext and com.stc.ExitContext specially, and will actually not log the message (i.e. the value of ContextName), but will prepend the value of ContextName to all log entries that are logged by the application in the MBD.

ConnectionURL

The connector typically uses a URL as the connectivity information to connect to the JMS server, even if that JMS server normally doesn't use a URL format. The format is of the form
     protocol://server:port?key1=value1&key2=value2
The query string is optional and contains both properties for the JMS server as well as for the connector. See also the section on Options below.

The ConnectionURL can be specified at the connector level (i.e. top of ra.xml), but also for each activation spec. This allows multiple MDBs to use the same connector, but connect to different JMS servers. The ConnectionURL can also be specified for each connection factory. This is useful for global connectors: to connect to multiple JMS servers, one can specify a different ConnectionURL for each connection factory. For embedded connectors this is slightly different: the connection factory is configured in the ra.xml, and the number of connection factories in ra.xml is limited to exactly one per connectionfactory-interface class. This class must be one of the three JMS factory classes. Hence, the number of connection factories is limited to three per connector. Hence, if no global connectors are used, a separate embedded connector has to be used for each distinct JMS server used in an EAR. An alternative construct to using one different embedded connector for each distinct JMS server is by using a special facility in JMSJCA in which the username is overloaded: see below "Overloaded username".

In the activation spec, the ConnectionURL can take the form of lookup://jndiname where jndiname should represent a JMSJCA connection pool bound in JNDI. The advantage of this construct is that all connectivity parameters no longer need to be present in the activation spec, but can be delegated to a globally shared connection pool. The connection pool is typically configured in the application server, i.e. outside of the application, i.e. outside of the EAR file.

During activation, JMSJCA checks the ConnectionURL; if it starts with lookup:// it will try to use the remainder of that string to lookup an object in the default JNDI provider. The returned object must a connection factory that was created with JMSJCA; the activation and the connection pool also must use the same classloader. The configuration of both the connection pool (MCF) and its resource adapter (RA) will be inspected. Next, the following properties will be changed in the activation spec:

Authentication

At each place where the connection URL can be specified, it is also necessary to specify the credentials to connect to that JMS server: hence the username and password can be specified at the connector level, the connection factory level, and at the activation spec level. Further, for outbound connections, the application code can specify a username and password when calling ConnectionFactory.createConnection(username, password) or one of the equivalents for queue and topic. See also the section "Overloaded username".

The precedence order for outbound connections is as follows: 1) createConnection(username, password), 2) at the connection factory level, and 3) at the connector level.

The precedence order for MDBs is as follows: 1) at the activation spec level, and 2) at the connector level.

Overloaded username

Normally the connectionURL is tied to a connection factory, so for each JMS server there should be a separate connection factory. The application code then has to choose the correct connection factory that is tied to the desired JMS server.

A special feature of JMSJCA is that the ConnectionURL can also be specified in the application code when creating a connection through ConnectionFactory.createConnection(username, password) or one of the equivalents for queue and topic. If the username can be recognized as a ConnectionURL, e.g. if the username starts with stcms:// or stcmss:// for STCMS, the username is interpreted as a ConnectionURL rather than a username. Hence, createConnection(), createQueueConnection(), and createTopicConnection() can be used as follows:

createConnection("u", "p");
createConnection("protocol://host:port?option=value", null);
createConnection("protocol://host:port?option=value&username=u&password=p", null);
createConnection("protocol://host:port?option=value&username=u", "p");
As shown, the username can still be specified: the query parameters username and password will be read from the ConnectionURL. The password can also be specified in the password parameter to createConnection(username, password).

Example:
The following first two statements are equivalent and will all create a connection to the STCMS server on BLUE at port 18008 using the username "X" and the password "Y". In the third statement, the username is obtained through the normal presedence rules, i.e. from the connection factory level specification or the connector.

ConnectionFactory.createConnection("stcms://blue:18008?username=X", "Y")
ConnectionFactory.createConnection("stcms://blue:18008?username=X&password=Y", null)
ConnectionFactory.createConnection("stcms://blue:18008, null)

Connections to JMS servers specified in the username parameter are pooled in the same pool as the "normal" connections in the connector pool.

Concurrency

The JMSJCA connector can deliver messages to multiple MDBs concurrently. The following concurrency modes are available (specify one of these values for the ConcurrencyMode-parameter) in the activation spec:
Parameter valueMeaning
serialUses one asynchronous listener; the JMS thread is used to invoke the onMessage() method.
ccProvides for concurrent processing by using connection consumer mode. Messages are dispatched to the WorkManager. Messages may be processed out of order.
syncProvides for multiple synchronous receivers that call receive(TIMEOUT) in a loop. This mode is mandatory for some implementations that do not properly implement the connection consumer mode (CC), or do not allow the XA start() method to be called from within the onMessage() method. A consequence is that for Topics, there will be no concurrent processing in this mode.

The default value for the ConcurrencyMode-parameter is serial.

Administrative objects

The ra.xml defines two administrative objects: one for a javax.jms.Queue, and the other one for a javax.jms.Topic. These are generic objects that can be bound in JNDI. When they are created and bound in JNDI, they may not represent physical destinations in the JMS server yet. Only when the application code uses an administrative object is the object actually turned into a JMS destination specific for the JMS server in use. This happens in a manner that is specific for the type of JMS server. For example, for the RAWL adapter, the name of an administrative Queue is interpreted as a name bound in the same JNDI provider as is specified for the adapter.

The destination in an activation spec can also be specified as an administrative object. To indicate that the destination parameter in an activation spec denotes an object that should be looked up, prefix the name with lookup://.

The destination obtained through Session.createQueueue() or Session.createTopic() also uses this approach: if the destination name supplied to these methods starts with lookup://, the destination is looked up in the container's JNDI instead of calling Session.createQueue() or Session.createTopic() on the underlying JMS provider. If the looked up destination is an administrative object, this destination is converted into a JMS provider specific destination using an approach specific for that provider.

Example 1

A JNDI adapter is configured to use a JNDI provider on server X. An administrative object is created and bound in the application server's JNDI using the name orders. The name attribute of the administrative object is specified as jndi://queues/q1234. Assume the application code looks like this:
    Destination q = (Destination) new InitialContext().lookup("orders");
    session.createProducer(q).send(session.createTextMessage("hello 1"));
    session.createProducer(q).send(session.createTextMessage("hello 2"));
In the first send() method in this code fragment, the destination is looked up in the JNDI provider at server X using the lookup-name queues/q1234. Because destinations are cached in the adapter, in the second send() method, the cached destination object is used.

In this example, the activation spec has the destination attribute has the value lookup://orders, the destination object is looked up in a similar fashion in the JNDI provider at server X using the lookup-name queues/q1234. If on the other hand the destination attribute has the value orders, the destination object is obtained using createQueue("orders") or createTopic("orders").

Example 2

An STCMS adapter is used. An administrative object is created and bound in the application server's JNDI using the name jms/destinations/orders. The name attribute of the administrative object is specified as q1234. Assume the application code looks like this:
    Destination q = session.createQueue("lookup://jms/destinations/orders");
This will result in a lookup in the container's jndi of the object with the jndi-name jms/destinations/orders. The resulting object is an administrative object containing the name q1234. The adapter will call session.createQueue("q1234") on the STCMS JMS client, and return the resulting object.

RA Options

The following RA options can be defined
Parameter namein/outMeaning
JMSJCA.NoXAin/outif set to true, this indicates that the resource adapter should not use XA. This can be used in case the JMS provider is not configured to use XA. This feature can also be used when the resource adapter is used outside of the application server. See the note on transaction control below.
JMSJCA.LocatorClassin/outSpecifies the Java class name of the class that will be used to access the transaction manager in the application server. The transaction manager is used in case of temporary destinations (in order to delete them when the connection closes) and when messages are moved to the dead letter queue. The default value will most likely suffice.
JMSJCA.redeliveryredirectinIf set to true, in the case of messages being sent to dead letter queue, messages will be redirected rather than copied.
JMSJCA.redeliveryhandlinginspecifies the behavior of the dead letter queue. See below.
JMSJCA.concurrencymodeinallows the concurrency mode to be overridden. Values are sync, CC, or serial. This is useful in particular cases (e.g. FIFO modes in STCMS) where the default concurrency mode does not suffice.
JMSJCA.ACCoutif set to true this indicates that the resouce adapter should behave as if it is running inside a client container. This means that the resource adapter will not be under the control of a transaction manager. The default is false. This property can also be set as a system property.
JMSJCA.IgnoreTxoutif set to true, the resource adapter will ignore the isTransacted parameter to the method createSession(isTransacted, ackmode) and equivalent functions and always change this parameter to isTransacted=true and the ackmode parameter is set to TRANSACTED. The default value is the opposite of JMSJCA.ACC. This property can also be set as a system property.
JMSJCA.BypassRAoutif set to true, this indicates that factories should not delegate to the resource adapter, but will instead delegate to the "native" JMS connection factory directly. The default is false. This property can also be set as a system property.
JMSJCA.Strictoutif set to true, the adapter will behave as close to the J2EE spec as possible. This property can also be set as a system property. See notes of specific adapters.
JMSJCA.sepin/outTo allow multiple properties to be specified in one single line (useful for some administrative consoles that limit the user input to one line), this options allows for a character to be specified that will be used to delimit multiple options. Example: "JMSJCA.sep=,JMSJCA.NoXA=true,JMSJCA.ACC=true" specifies two options: JMSJCA.NoXA = true and JMSJCA.ACC = true. Note that the separator can be escaped with a backslash in case it needs to be used as part of an option-value. Example: "JMSJCA.sep=*JMSJCA.NoXA=true*queuepattern=Q.\*" yields two values: JMSJCA.NoXA=true and queuepattern=Q.*
JMSJCA.nocfcacheoutDetermines if connection factories are cached. Default: true, but may depend on particular connector (e.g. WL). Can be set to false for those cases where connection factories constitute live connections. In case of connection failures, these connection factories should not be cached but recreated.
JMSJCA.messagewrappinginAlways create a message wrapper around a message when it gets delivered to the MDB which allows the application code to associate state with the message in case the message gets redelivered. See Message Wrapping. Possible values: 0=auto, 1=always. Default: 0.
JMSJCA.selectorinSpecifies a default selector for all activations. See Selectors. Default: ""

Options can be specified in:

  1. the Options field in the general section of the ra.xml; the options field is a serialized Java properties set. In short: this field consists of key-value pairs. One pair per line. A key is separated from the value using a = sign. Comments are indicated using ! or #.
  2. the Options field of the connection factory in the ra.xml, or in the activation spec of the ejb-jar.xml. Format: see 1. Takes precedence over 1.
  3. the connection URL in the form of query parameters. Takes precedence over 2.

JMX Management

Each JMSJCA Resource Adapter can specify the name of an MBean. This MBean can be used to access some generic management properties of the adapter.
MethodMeaning
getJMSServerMBean()returns the ObjectName of the MBean that provides management capabilities of the contents of destinations in the JMS server.
getJMSServerType()returns the type of the JMS server, e.g. STCMS.

Also, for each activation (i.e. MDB deployment), the JMSJCA connector can register an MBean. This MBean can be used to start/stop delivery of messages to the MDBs and can be used to extract performance data out of the connector. The name is specified in the MBeanName parameter of the activation spec.

AttributeMeaning
ActivationSpecA dump of the values in the activation spec
NActiveEndpointsNumber of active MDBs, i.e. number of threads that are currently in onMessage()
NConfiguredEndpointsNumber of MDBs specified in the activation spec
NHighestActiveEndpointsHighest number of active MDBs reached sofar
NMessagesTotal number of messages delivered, i.e. the number of times onMessage() was invoked
NTotalEndpointsCurrent number of MDBs in the pool
Statssnapshot of performance numbers

MethodMeaning
getJMSServerMBean()returns the ObjectName of the MBean that provides management capabilities of the contents of destinations in the JMS server.
getJMSServerType()returns the type of the JMS server, e.g. STCMS.
getStatusindicates if the connector is "Up" (i.e. connected to the JMS server, and potentially delivering messages to MDBs), "Down" (i.e. no connection exists to the JMS server), "Connecting" (i.e. the adapter is trying to establish a connection to the JMS server; the status of a connector immediately after activation is always "Connecting"), or "Disconnecting" (i.e. the connector is disconnecting from the JMS server and may be waiting for all threads to return from their onMessage() methods.
getPropertiesreturns a String specifying configuration parameters.

Transaction control

Outbound connections, default behavior (spec compliant)

By default the resource adapter assumes that the RA is used within an application server and that the RA is deployed with XATransaction support. Consequently the RA assumes that the container will call getXAResource() on the managed connection and will manage the transaction. Example:
Connection c = fact.createConnection();
Session s = c.createSession(false, Session.AUTO_ACKNOWLEDGE);

Outbound connections, special behavior (non spec compliant)

Using the option JMSJCA.NoXA=true, the default behavior can be changed. The RA is still assuming that it is used within within an application server and that the RA is deployed with XATransaction support, and that the container will call getXAResource() on the managed connection and may try to manage the transaction. However, the RA does not use an XAConnectionFactory, but tries to emulate XA as good as it can within the constraints imposed by the application code. The session may be transacted or non-transacted.

Transacted mode: This is the default mode: a call to Connection.createSession(isTransacted, ackmode is defaulted to c.createSession(true, Session.SESSION_TRANSACTED). The XA emulation will propagate only the second phase of the transaction commit protocol to the session, i.e. commit and rollback are propagated to the transacted session; the first phase is ignored. This implies that transactional integrity is not guaranteed: if Session.commit() or Session.rollback() fail, the transaction may be left in an inconsistent state. Note that the application code can call Session.commit() or Session.rollback() directly; in that case the transaction managed by the container is bypassed completely, and the transaction may be inconsistent.

Non transacted mode: To use this mode, set JMSJCA.IgnoreTx=false(default is true), and call Connection.createSession(false, Session.AUTO_ACKNOWLEDGE) (the values CLIENT_ACKNOWLEDGE or DUPS_OK_ACKNOWLEDGE may also be used). The XA emulation will not propagate anything of the container managed transaction to the session. This means that the session is used completely outside of the transaction managed by the container. There is no transactional integrity.

Inbound

Container Managed Transactions: by default the RA will use an XAConnectionFactory and enlist the XAResource in the container managed transaction.
Bean Managed Transactions: the RA will use a non-XA ConnectionFactory and use a transacted session. The received message will only be rolled back if the MDB throws an exception.
Special behavior: if the option JMSJCA.NoXA=true is specified, the RA will use Bean Managed Transaction semantics even in the case of a Container Managed Transaction deployment.

Redelivery handling (deadletter queue)

A poison message is a message that fails to be processed time and time again, thereby stopping other messages from being processed, and hogging the CPU so that other requests cannot be processed.

How it works

For each message that is received, the redelivery-flag is checked. If that flag is set, it will go through the redelivery handling process. This process uses a a cache of msgids of messages that have the JMSRedelivered flag set. This cache keeps a count for each of these messages of how often they were "seen", i.e. how often they were redelivered. Based on this count, a particular Action can be invoked. Actions are delaying, moving or deleting the message.

The msgid cache is not persistent, nor is it shared between multiple activations. This means that if a message was seen 10 times with the redelivered flag set, and the project is undeployed, the redelivery count will be set to zero when the project is deployed again. Also, if there are multiple application servers reading from the same queue, a message may be redelivered 10 times to one application server, and 10 times to the other application server, and both activations will see a count of 10 instead of 20.

The msgid cache is limited to 5000 entries; when this limit is reached, the oldest msgids are flushed from the cache. "Oldest" means least recently seen.

When to choose which action?

A message is typically redelivered because of an error in the processing of the message by the application code. This error may be permanent or transient. Delaying delivery of a redelivered message is useful to save CPU cycles instead of letting the message "spin" rapidly. If the error is transient, the message will eventually "go through". If the error is permanent, moving messages to a different destination may be a better approach. If the message is not valuable, deleting the message is another option.

Configuration

Specification of what actions to undertake when the message is repeatedly redelivered is done through a specially formatted string. The string has this format:
   format := entry[; entry]*
   entry := idx ":" action
   idx := number (denotes the n-th time a msg was seen)
   action := number (denotes delay in ms) | "delete" | "move"(args)
   move := "queue"|"topic" | "same" ":" destname
   destname :=  any string, may include "$" which will be replaced with the original
       destination name.
Example:
    5:1000; 10:5000; 50:move(queue:mydlq)
This causes no delay up to the 5th delivery; a 1000 ms delay is invoked when the message is seen the 5th, 6th, 7th, 8th, and 9th time. A 5 second delay is invoked when the msg is invoked the 10th, 11th, ..., 49th time. When the msg is seen the 50th time the msg is moved to a queue with the name "mydlq".

If the messages were received from "Queue1" and if the string was specified as

    5:1000; 10:5000; 50:move(queue:dlq$oops)
the messages would be moved to the destination "dlqQueue1oops": the special character "$" denotes the original destination name. Instead of "queue" one can also specify "topic" or "same". The latter denotes a queue if the message was received from a queue, or can denote a topic if the message was received from a topic.

Another example:

    5:1000; 10:5000
This causes no delay up to the 5th delivery; a 1000 ms delay is invoked when the message is seen the 5th, 6th, 7th, 8th, and 9th time. A 5 second delay is invoked for each time the message is seen thereafter.

Where redelivery handling is configured

The action string (e.g. 5:1000; 10:5000) can be specified in the RedeliveryHandling field of the activation spec. Alternatively, the string can be specified as an option in either the URL, the Options field in the activation spec or in the Options spec in the ra.xml using the property name JMSJCA.redeliveryhandling. Example:
     stcms://localhost:18007?JMSJCA.redeliveryhandling=5:1000; 10:5000

How messages are moved

Moving messages is done in the same transaction if the transaction is XA. Moving messages is done using auto-commit if the delivery is non-XA.

Moving messages is done by creating a new message of the same type unless the property JMSJCA.redeliveryRedirect is set to true in which case the messages are simply redirected. In the first case, the payload of the new message is set as follows:

Note that copying the payload of an ObjectMessage may cause classloader problems since the context classloader is not properly set. In this case the redelivery handler should be configured to redirect the message instead. The new message will have properties as follows:
PropertyMeaning
JMS_Sun_JMSJCA_RedeliveryCountnumber of times the message was seen with the redelivered flag set by JMSJCA. Will accurately reflect the total number of redelivery attempts only if there's one instance of the inbound adapter, and the inbound adapter was not redeployed.
JMS_Sun_JMSJCA_OriginalDestinationNamename of the destination as specified in the activation spec
JMS_Sun_JMSJCA_OriginalDestinationTypeeither "javax.jms.Queue" or "javax.jms.Topic"
JMS_Sun_JMSJCA_SubscriberNameas specified in the activation spec
JMS_Sun_JMSJCA_ContextNameas specified in the activation spec
JMS_Sun_JMSJCA_OriginalJMSMessageIDoriginal msgid as obtained through getJMSMessageID() of the original message
JMS_Sun_JMSJCA_OriginalJMSCorrelationIDoriginal JMS Correlation ID as obtained through getJMSCorrelationID() of the original message
JMS_Sun_JMSJCA_OriginalClientIDThe ClientID as specified in the activation spec
JMS_Sun_JMSJCA_ExceptionClassThe classname of the exception that was caught by the RA. This is typically a wrapper exception created by the container.
JMS_Sun_JMSJCA_ExceptionMessageThe exception message of the exception that was caught by the RA.
JMS_Sun_JMSJCA_ExceptionStackTraceThe exception stacktrace of the exception that was caught by the RA.
JMS_Sun_JMSJCA_UserRollbackData.*Values specified on the message while being processed in the MDB by the application code. See Message Wrapper.

Redirecting messages

If moving a message fails, the message is redirected instead. This means that the same unmodified message is sent to the target destination. This behavior can be choosen as default behavior instead of moving by setting the property JMSJCA.redeliveryredirect to true. This can be done in the URL or the Options field in the activation spec or the ra.xml.

How messages are delayed

Invoking a delay takes place by holding the processing thread occupied, that means that while the thread is sleeping, this thread will not be used to process any other messages. This means that the delaying strategy cannot be used to "side-track" messages at no expense. Note that long message delays have no effect on the speed of undeployment. Message delays cannot be longer than 5 seconds. No warning is logged unless the msg delay is divisible by 1000, in which case an INFO message is written to the log indicating that the thead is delaying message delivery.

Default behavior

There is a default behavior for message redelivery handling:
 
3:25; 5:50; 10:100; 20:1000; 50:5000

Extended Redelivery Handling

Applications can extend the built-in redelivery handling with application code in the MDB. Through functionality exposed by so-called message wrappers, the application can associate state with messages, or can apply specific actions on the message.

Arbitrary state can be associated with a message through the JMS_Sun_JMSJCA_UserRollbackData.* properties. For example, when an MDB calls message.setStringProperty("JMS_Sun_JMSJCA_UserRollbackData.failuremode", "permanent") and rolls back the transaction, the next time that the message gets delivered, a call to message.getStringProperty("JMS_Sun_JMSJCA_UserRollbackData.failuremode") will yield "permanent".

Note that this kind of state is not persisted and that its scope is limited to only the MDBs of the activation in which the state was set.

Through functionality exposed in message wrapper, the MDB can also query the number of times the message was seen before by the activation that the MDB is part of. This is done through the JMS_Sun_JMSJCA_RedeliveryCount object property which returnes a java.lang.Integer.

An MDB can find out what the redelivery handling is for the activation that it is part of: getStringProperty("JMS_Sun_JMSJCA_RedeliveryHandling") returns the redelivery string that applies to that message. The MDB can even change the redelivery handling for each particular message using setStringProperty("JMS_Sun_JMSJCA_RedeliveryHandling", h) where h is the redelivery handling string. This string should be of the same format as redelivery handling strings specified in the activation spec.

An MDB can even stop message delivery altogether by calling setStringProperty("JMS_Sun_JMSJCA_StopMessageDelivery", reason) where reason is a string that will be printed in the log as the reason why message delivery was stopped. Of course message delivery can only be resumed by an external action such as the call on the activation MBean, disabling/enabling or undeploying/redeploying the application.

Message wrapping

Messages that the connector delivers to the MDB may be wrapped in an object that provides additional functionality beyond the functionality provided by the JMS provider. Messages are wrapped in two cases:

The message wrapper implements one of the six javax.jms message types. The interface type will be the same as the wrapped message implements. Messages cannot be wrapped if a JMS provider delivers messages that implement more than one of the derived interfaces (TextMessage, BytesMessage, MapMessage, ObjectMessage, StreamMessage). If the JMS provider delivers a proprietary message that implements only javax.jms.Message, the wrapper will only expose the javax.jms.Message interface.

The message wrapper exposes new functionality through the methods that are declared in the javax.jms.Message type, especially the properties methods. A summary of all functionality is listed in the following table. Details on each item can be found in the corresponding sections.

ParameterTypemethodSee section onMeaning
--acknowledge()HUA modeTo indicate that processing of the message has finished
JMSJCA.setRollbackOnlyjava.lang.BooleansetObjectProperty()HUA modeTo rollback the transaction
JMSJCA.batchIndexjava.lang.IntegergetObjectProperty()BatchIndicates the number of messages that preceded this message
JMSJCA.batchSizejava.lang.IntegergetObjectProperty()BatchIndicates the size of the batch, the same value as in the activation spec
JMS_Sun_JMSJCA_UserRollbackData.*java.lang.StringsetStringProperty(), getStringProperty()RedeliveryGives access to arbitrary data that an application can associate with a message.
JMS_Sun_JMSJCA_RedeliveryCountjava.lang.IntegergetObjectProperty()RedeliveryThe number of times the message was seen before. A value of zero indicates that the message wasn't seen before.
JMS_Sun_JMSJCA_RedeliveryHandlingjava.lang.StringgetStringProperty() setStringProperty()RedeliveryThe current redelivery handling setting for this message; sets the redelivery handling for this message
JMS_Sun_JMSJCA_StopMessageDeliveryjava.lang.StringsetStringProperty()RedeliveryStops message delivery. The second parameter passed to setStringProperty will be printed in the log as the reason for stopping message delivery. Once message delivery is stopped, it can only be resumed by another invocation on the MBean.

Note that the extra properties on the message wrapper do not follow the JMS specification:

Selectors

Selectors are typically specified in the activation spec. It is also possible to specify a selector in the resource adapter section of the ra.xml, either in the Options section or as part of the URL. This mechanism can be used to specify a default URL for all activations that are using that resource adapter. Such a selector can refer to the selector specified in the activation using substitution parameters.

Substitution parameters are of the form ${Parameter} . Substitution can be disabled by escaping the $-sign with an extra $-sign. For instance, while s = '${subscribername}' may resolve to s = 'x47' if the subscriber name in the activation spec was x47. On the other hand, the string s = '$${subscribername}' will always resolve to s = '${subscribername}' .

Selectors can also refer to parameters in the activation spec: the ${subscribername} refers to the the subscriptionName parameter in the activation spec.

Here is a list of all sustitution parameters:

ParameterUsageDescription
${subscribername} Anywhere Will be replaced with the subscriptionName parameter in the activation spec
${selector} Override Will be replaced with the selector parameter in the activation spec
${andselector} Override Evaluates to and (${selector}) if the selector in the activation spec is non-empty string, or evaluates to an empty string "" if the selector spec is an empty string
${selectorand} Override Evaluates to (${selector}) and if the selector in the activation spec is a non-empty string, or evaluates to an empty string "" if the selector in the activation spec is an empty string.

Parameters that are not in this list will be resolved as System parameters using System.getProperty().

Examples

Batching

When the BatchSize parameter in the activation spec is set to a value greater than one, the resource adapter will deliver multiple messages in one transaction to the MDB.

Supported concurrency modes

This mode is supported in the sync and cc concurrency modes only.

EndOfBatch message

At the end of each batch, the connector delivers an EndOfBatch message to the MDB. Even if the transaction is marked for rollback, or if a message earlier caused an exception, the EndOfBatch message is delivered. Only if the MDB is shutdown by the application server, or if the application server unexpectedly exits, situations may arise where no EndOfBatch message is delivered.

The EndOfBatch message can be recognized using an Object message property with the name JMSJCA.EndOfBatch such that

    message.getObjectProperty("JMSJCA.EndOfBatch")
returns
    Boolean.TRUE

Number of messages in a batch

The connector tries to deliver the number of messages specified in BatchSize in one batch. The EndOfBatch message is added to this number. The actual number of messages is less than or equal to the specified number (plus one for the EndOfBatch message).

Transaction scope

The EndOfBatch message is part of the transaction scope. Rolling back the transaction or throwing an exception from the onMessage() method will cause the batch to be rolled back.

Note that since all messages in a batch are delivered in one transaction, all messages in the batch are rolled back. Therefore, in typical applications, it would be preferable to move faulty messages to an error-queue rather than to throw an exception or mark the transaction for rollback. Note that the redelivery handling feature (see above) works the same on all messages and cannot make a distinction which message in a batch may be faulty.

Threading

It is guaranteed that calls to onMessage() for all messages of the same batch are done in the same thread. However, it is not guaranteed that the calls to onMessage() for all messages of the same batch are done on the same MessageListener or MDB instance -- this depends on the application server.

Other properties on the message

The following properties are also accessible as read-only Object properties in batch mode:

Hold-for-acknowledge mode (non batch mode)

This mode allows for the processing of a message to be done in a thread that is different from the thread that calls onMessage(). This mode mimics the CLIENT_ACKNOWLEDGE mode in JMS with an extension for multi-threaded processing; this extension deviates from the J2EE 1.4 threading model. The advantage of this mode is that messages can be processed concurrently with fewer threads.

Here is an example that illustrates its intended use:

    // An imaginary way to post a request to a different thread
    private void postRequest(Message m, OnDoneHandler h) {
        // Do something
    }

    // An imaginary callback handler
    public interface OnDoneHandler {
        void onDone(boolean failed) throws Exception;
    };
    
    // The onMessage method
    public void onMessage(final Message m) {
        postRequest(m, new onDoneHander() {
            public void onDone(boolean failed) {
                m.acknowledge();
             }
        });
    }

In this example the RA-thread will call onMessage(), and after it has returned from this method it will not commit/rollback the transaction nor will it return the session to the pool. Instead, these two activities are done when the other thread calls acknowledge().

Supported modes

Acknowledging / rolling back a message

To acknowledge a message, call acknowledge() on the message. To rollback a message, call the setRollbackOnly() method of the Transaction object that controls the delivery of the message. If the MDB was deployed as a Bean managed transaction (BMT) with the transaction-attribute NotSupported, the message was not delivered in a transaction, and there will be no Transaction object. In that case, set the JMSJCA.setRollBackOnly boolean property in the message to true, and then call acknowledge(). E.g.:
message.setBooleanProperty("JMSJCA.setRollbackOnly", true);
message.acknowledge();

Threading and transaction details

Example

The following code snippet illustrates the intended use of the hold-for-ack mode:
    public void onMessage(final Message message) {
        try {
            postRequest(message, new OnDoneHandler() {
                public void onDone(boolean failed) throws Exception {
                    if (failed) { 
                        message.setBooleanProperty("JMSJCA.setRollbackOnly", true);
                    }
                    message.acknowledge();
                }
            });
        } catch (Exception e) {
            // Posting failed; rollback
            try {
                message.setBooleanProperty("JMSJCA.setRollbackOnly", true);
                message.acknowledge();
            } catch (JMSException e1) {
                throw new RuntimeException(e1);
            }
        }
    }    

How to configure

Specify the HoldUntilAck parameter in the activation spec; the value should be "1" to turn on the hold-until-ack mode.

Hold-for-acknowledge mode in batch mode

The hold-for-acknowledge mode can be combined with batch mode. This can lead to even more efficient use of JMS sessions and threads.

Acknowledging / rolling back a message

In addition to what is described above, the following constraints are there for batch mode:

Here is an example:

    public void onMessage(final Message message) {
        try {
            if (message.getObjectProperty("JMSJCA.EndOfBatch") != null) {
                // End of batch
                message.acknowledge();
            } else {
                // Message in middle of batch 
                try {
                    postRequest(message, new OnDoneHandler() {
                        public void onDone(boolean failed) throws Exception {
                            if (failed) { 
                                message.setBooleanProperty("JMSJCA.setRollbackOnly", true);
                            }
                            message.acknowledge();
                        }
                    });
                } catch (Exception e) {
                    // Posting failed; rollback
                    message.setBooleanProperty("JMSJCA.setRollbackOnly", true);
                    message.acknowledge();
                }
            }
        } catch (JMSException e) {
            throw new RuntimeException(e);
        }
    }    

Distributed durable subscribers

The JMS spec mandates that there can be only one client connected to a server that has the same ClientID and durable subscriber name. For instance, if there is a topic T1, and a durable subscriber D1, there can be only one application reading from T1-D1. Through the connection consumer mechanism, this application can process messages from T1-D1 concurrently. If the application is being clustered, only one cluster node can process messages, i.e. the active cluster node. The other cluster nodes will fail to connect to the durable subscriber. Through the retry mechanism in jmsjca, these other cluster nodes will keep on trying to connect, so that if the active cluster node fails, another cluster node will succeed in connecting and become the active cluster node. There can be only one active cluster node, hence the application does not scale. The mechanism of distributed durable subscribers aims to resolve this.

There are three approaches to this problem:

  1. The JMS server supports distributed durable subscribers natively.
  2. The durable subscriber is partitioned into N separate durable subscribers so that each message is sent to only one of these durable subscribers; each cluster node then uses one of these partitioned durable subscribers. A problem is the management of these partitioned subscribers, and the dynamic re-assignment of partitioned durable subscribers in the case of cluster node failures.
  3. The messages from the durable subscriber are sent to a queue, and this queue is used by different cluster nodes to read messages from. The active subscriber takes care of this. Each cluster node tries continuously to become the active subscriber, so in case of failure of the active subscriber, another cluster node will become the active subscriber automatically.

For those JMS servers for which the first approach does not work, jmsjca provides the third approach.

Configuration

This feature is activated by providing a special durable subscriber name:
jmsjca://distribution=1&subscribername=s[&queue=q][&mbeanname=m][&batchsize=b]

ParameterRequiredMeaningDefault
distributionyesturns on distributed durable subscribers. Valid values: 0=off, 1=distributed durable subscriber0
subscribernameyesthe name of the durable subscriber
queuenoname of the queue to send the messages toLOADBALQ_topicname_subscribername
mbeannamenoname of the MBean associated with the topic-to-queue delivery objectspecified MBean name + "-LOADBALQ"
batchsizenonumber of messages read in one batch from the topic and written to the queue10

Notes

Example

Specifying the following subscription name jmsjca://distributed=1&subscribername=mysub on topic T will cause a subscriber to be created with subscriptionName=mysub; this subscriber reads messsages in batches of 10 and sends them to queue dt-T1-mysub. The application will read messages from this queue.

Genericjmsra compatibility

Genericjmsra is a purely JNDI based connector. Like JMSJCA it provides administrative objects: queues and topics. These queues and topics may be passed to and can be used by JMSJCA.

Genericjmsra's queues and topics contain the name of a destination object bound in JNDI. JMSJCA will take this name, prefix it with jndi:// and create its own administrative object for it. This approach will work if the JNDI adapter is used, and if this adapter is pointing to the the same JNDI store as Genenericjmsra.