Controlling Message Delivery

WebLogic provides a number of features that can be used to control the different aspects of message delivery and handling. These features are critical in building a robust and scalable messaging system.

The timed message delivery feature lets you control exactly when messages are delivered. Using this mechanism, you can either configure a delay before the message is actually delivered, or set up a custom schedule for a destination that determines the permitted days and times during which the messages may be delivered. In this way, you can ensure that messages are delivered only when their consumers are ready to process them. The time-to-live attribute lets you adjust the maximum age of the outgoing messages i.e., the maximum time period for which the messages will be retained by the system. After this time period, the messages will have expired and will no longer be tracked by the JMS server. The previous section showed how WebLogic can periodically reap expired messages from the JMS server.

WebLogic also provides enhanced support for redelivery by letting you limit the number of attempts at redelivery and configure a pause between redelivery attempts. WebLogic JMS makes two additional acknowledge modes available. The NO_ACKNOWLEDGE mode instructs the JMS server to not expect acknowledgment for the message. The MULTICAST_NO_ACKNOWLEDGE mode is similar, except that it enables the JMS client to use a more scalable multicast message distribution when delivering messages to a topic destination.

8.3.1 Delivery Modes

The delivery mode indicates whether the messages are stored in a persistent store. If a persistent delivery mode is used, the message will be stored so that the system can guarantee once-and-only-once message delivery. A nonpersistent delivery mode cannot offer this level of guarantee, as the JMS server may fail and lose a message. In this case, the best you can hope for is at-most-once message delivery. There are several ways to configure the delivery mode for outgoing messages:

Remember, if you require support for persistent messages, you also must configure a persistent store for the JMS server. If no store has been configured for the JMS server, the persistent mode is downgraded to nonpersistent delivery. Similarly, if a topic doesn't have any durable subscribers, again the persistent mode is automatically lowered to nonpersistent delivery.

8.3.2 Timed Delivery

There are two ways in which to achieve finer control over when a message is delivered:

Delayed message delivery

You can delay the delivery of a message by specifying a relative delivery time. The message will be made visible at the destination only after the delay has expired.

Scheduled message delivery

You can configure a destination to schedule message delivery during particular time periods, using a flexible cron-like format.

8.3.2.1 Delayed message delivery

You can specify a delay between when a message is produced and when it is made visible on a destination. The delay value, specified in milliseconds, determines a relative delivery time, offset from the current time on the JMS server. The delay time period can be configured for a connection factory, or it can be set on an individual message producer using the proprietary API provided by WebLogic JMS. To set the delivery delay for a connection factory, select the factory from the Administration Console and change the value of the Default Time to Deliver attribute on the Configuration/General tab. This delay characteristic for the connection factory will be shared by all producers created using the connection factory. For instance, if a producer that has been created by a connection factory that has a time to deliver of 10000 sends a message to a queue, any queue receiver will not receive that message until at least 10 seconds after the message is sent.

Setting the delivery delay on the connection factory is useful because your code will remain largely portable. Alternatively, you could set the delay on an individual message producer using the setTimeToDeliver(long delay) method exposed by the weblogic.jms.extensions.WLMessageProducer interface. The following example illustrates this usage:

QueueSender qsender = session.createSender(queue); WLMessageProducer mp = (WLMessageProducer) qsender; mp.setTimeToDeliver(20000);

When you set the delivery delay on a message producer, this delay overrides the default time to deliver that has been configured for the connection factory.

There is another twist to the tale. A JMS destination can override the Time to Deliver attribute set on the connection factory or the message producer. If you specify a positive numeric value for the Time to Deliver Override setting in the Configuration/Overrides tab for a chosen destination, this value overrides the delivery delay you may have set for the connection factory or any message producer. If the setting has a value of -1 instead, no override occurs, and messages intended for that destination essentially will take on the delay characteristics of the connection factory or producer.

8.3.2.2 Scheduled delivery

The same Time to Deliver Override setting for a JMS destination can also be used to define a delivery schedule that overrides any delivery delays you may have configured for the connection factory or producers. Instead of a numeric value, you can specify a string value for the setting that represents the message delivery schedule.

The format of this delivery schedule is based on a cron-like syntax. A schedule is defined by specifying space-separated values for the millisecond, second, minute, hour, day of month, month, and day of the week fields. Figure 8-1 illustrates this format along with the minimum and maximum ranges for these fields.

Figure 8-1. Fields and field values for a schedule

The order of the fields is important, and no field may be omitted. The values for each field can be specified in several different ways:

Together, the fields specify a scheduled time during which messages can be delivered to the destination. So, for example, the following schedule indicates that messages may be delivered every day between noon and 10 p.m.:

* * * 12-22 * * *

Table 8-1 lists sample schedules and their meanings.

Table 8-1. Example schedules with their interpretation

Example

Description

* * 0 8 * * *

Every day at 8:00 a.m., until 8:01 a.m.

* * * 13 * * *

Every day at 13:00 p.m. until just before 14:00 p.m.

0 0 0 8 * * 2,4,6

Every Monday, Wednesday, and Friday at 8:00 a.m.

0 0 0,30 * * * *

The exact next nearest half hour

* * 0,30 * * * *

Anytime in the first minute of the half hours

* * 0-30 * * * 2-6

Anytime during the first half hour of any hour from Monday to Friday

* * * * 1-7 * 2

The first Monday of the month

* * * * 31 * *

The last day of the month

* * * * 13 * 6

The next time the 13th falls on a Friday

* * * * 31 12 *

Every Hogmanay (New Year's Eve)

Sometimes a single delivery schedule isn't sufficient. For instance, using the syntax explained previously, you cannot schedule delivery at a particular time on weekdays and some other time on weekends. To accomplish this sort of compound schedule, you need to specify multiple schedules. Do this by separating each schedule with a semicolon. In this case, the next scheduled time is determined by the schedule that returns the "soonest" value. The following schedule allows for message delivery between 9:00 a.m. to 5:00 p.m. on weekdays, and noon to 4:00 p.m. on weekends:

* * * 9-17 * * 2-6; * * * 12-16 * * 1,7

8.3.2.3 Evaluating the next scheduled delivery time

WebLogic provides a programmatic interface to a schedule parser, which can be found in weblogic.jms.extensions.Schedule. This Schedule class lets you determine the next scheduled time for delivery that is permitted by a given schedule expression. The returned value can be either absolute, or relative to some specific date and time.

The nextScheduledTimeInMillisRelative(String schedule) method allows you to obtain the difference in milliseconds until the next scheduled time for delivery it returns a long value. You can supply an additional time parameter, in which case it calculates the difference from the supplied time to the next scheduled time for delivery. The following example illustrates its usage:

long t = Schedule.nextScheduledTimeInMillisRelative( "0 0 30 11 * * * ", System.currentTimeMillis( ));

If this code is executed at 11:20 a.m., it will return the number of milliseconds equal to 10 minutes (10 * 1000 * 60).

The nextScheduledTimeInMillis( ) method takes a schedule as its parameter and determines the closest absolute time that fulfills the delivery schedule, returning a long value representing the time. If we run the following example at any time after 12:10 p.m. and before 12:05 p.m. the next day, it will return an absolute time of 12:05 for the following day:

long t = Schedule.nextScheduledTimeInMillis( "0 0 5-10 12 * * * "); DateFormat df = DateFormat.getDateTimeInstance( ); System.err.println(df.format(c.getTime( ))); // Prints out 25-Oct-2002 12:05:00 when run on 24th at 12:12:22 // Prints out 24-Oct-2002 12:10:17 when run on 24th at 12:10:17

The final method, nextScheduledTime( ), takes a delivery schedule as a parameter and returns a Calendar object, representing the next scheduled time for delivery after the current time. It can take a Calendar object as an additional parameter, in which case the method returns the next scheduled time after the given time. The following piece of code prints out the date of the first Monday in the next month, provided you don't run this code in the first week of the month:

DateFormat df = DateFormat.getDateTimeInstance( ); Calendar c = Schedule.nextScheduledTime("* * * * 1-7 * 2"); System.err.println(df.format(c.getTime( )));

8.3.2.4 Determining delivery time

Once a message has arrived at a destination, you can use the weblogic.jms.extensions.WLMessage interface to determine when the message was delivered. The WLMessage interface, which extends the standard javax.jms.Message interface, provides a getJMSDeliveryTime( ) method to return the actual delivery time for a message. If the delivery of the message was delayed, this value will be different from the timestamp returned by the standard getJMSTimestamp( ) method. The following example illustrates this difference:

public void onMessage(Message msg) { try { DateFormat timeFormatter = DateFormat.getDateTimeInstance( ); //returns the time when the message was handed over to WebLogic JMS System.err.println("Msg Timestamp: " + timeFormatter.format(new Date(msg.getJMSTimestamp( )))); //returns the time when the message was actually delivered to the destination System.err.println("Msg Delivery Time: " + timeFormatter.format(new Date(((WLMessage)msg).getJMSDeliveryTime( )))); } catch (JMSException ehm) { System.err.println(ehm); } }

If you are not using delayed message delivery, simply use the standard approach.

8.3.3 Time-to-Live

The delivery delay mechanisms also must take into account the expiry time set on the messages. If a message has an expiry value that is less than or equal to a delay, the message will be delivered and subsequently silently expire. The JMS API provides a standard approach to set the time-to-live attribute on a message, using the send( ) method on a QueueSender or the publish( ) method on a TopicPublisher. WebLogic JMS also lets you configure a default time-to-live attribute on the connection factory, which is then inherited by all messages sent using that factory. To configure this, choose the connection factory from the left pane of the Administration Console, and then enter the suitable value for the Default Time to Live attribute in the Configuration/General tab. A value of 0 indicates that any messages sent using the factory will not expire.

A message producer also can override the default expiry value set for the connection factory. For this, you must programmatically set the time-to-live attribute using the MessageProducer interface:

QueueSender qsender = session.createSender(queue); qsender.setTimeToLive(20000);

If an expiry value is later specified in the send( ) or publish( ) methods, this value takes priority over any default expiry value set in the connection factory, or the time-to-live attribute set for the message producer itself. Finally, a JMS destination also can override the time-to-live attribute set on the connection factory or the message producer. To achieve this, simply specify a numeric value for the Time to Live Override setting in the Configuration/Overrides tab for a chosen destination.

8.3.4 Redelivery

Message redelivery occurs during a rollback or session recovery. WebLogic JMS defines a number of extensions to this standard mechanism giving you finer control over message delivery.

In WebLogic 8.1, the JMS server can guarantee that the redelivered messages are sent in the same order in which they were produced. There are a few sensible qualifications, though:

8.3.4.1 Configuring redelivery

WebLogic lets you define a delay, in milliseconds, which is the length of time the server will wait before it tries to redeliver a message. This delay is especially useful in situations where external conditions affecting the delivery of messages are resolved only after a particular amount of time has passed, thus saving pointless attempts to redeliver the message.

You can use the Administration Console to configure the redelivery delay for a connection factory. Simply choose a suitable value for the Default Redelivery Delay attribute in the Configuration/General tab for the chosen connection factory. All JMS sessions created from the connection factory inherit this default redelivery delay. If a message is consumed by multiple sessions within the context of a user transaction, the message will receive different redelivery delays, depending on the settings of each JMS session. Of course, messages that are left unacknowledged or uncommitted by a client, either intentionally or as a result of a failure, are not assigned a redelivery delay.

An individual JMS session can override the default redelivery delay determined by its connection factory. You can set the redelivery delay for a JMS session using the proprietary weblogic.jms.extensions.WLSession interface. The following code example shows how to use the WLQueueSession subinterface to set the redelivery delay on a queue session:

QueueSession session = qcon.createQueueSession(false, Session.AUTO_ACKNOWLEDGE); WLQueueSession qsession = (WLQueueSession) session; qsession.setRedeliveryDelay(10000);

Any change to the session's redelivery delay will affect all subsequent messages that are consumed and later rolled back or recovered by the session. If no session-specific redelivery delay has been set, the session inherits its delay from its connection factory. In that case, you can use the getRedeliveryDelay( ) method to determine the redelivery delay inherited by the session.

A JMS destination can override the redelivery delay, specified for any connection factory or any JMS sessions. To set the redelivery delay for a destination, you must navigate to its Configuration/Redelivery tab. Here you additionally can specify a limit on the number of attempts that are made to redeliver a JMS message. If the message still can't be delivered after so many attempts, the message is considered to be undeliverable. Depending on whether an error destination has been configured, the undelivered message is either dropped or sent to the error destination. This applies to both persistent and nonpersistent messages.

8.3.4.2 Error destinations

Error destinations, if configured, receive messages that are deemed undeliverable. This includes those messages that have expired, and those that cannot be redelivered by the JMS server. An error destination is no different from any other destination, except that it must exist on the same JMS server that hosts the destination. To route undeliverable messages to this error destination, you must navigate to the Configuration/Redelivery tab for a chosen destination and specify the name of an existing queue or topic for the Error Destination attribute.

If the error destination supports persistent messaging, the undelivered messages will remain in the JMS store. In this way, WebLogic can ensure that the persistent messages eventually are delivered to the error destination, even if the JMS server fails for some reason.[3]

[3] Prior to WebLogic 8.1, if an error destination reached its quota limit, undelivered messages simply were logged and deleted from the system.

8.3.5 Extended Acknowledge Modes

The JMS standard provides three acknowledge modes for nontransacted JMS sessions:

AUTO_ACKNOWLEDGE

This mode ensures that the consumer's runtime will acknowledge the message automatically, after it has finished processing the message.

CLIENT_ACKNOWLEDGE

This mode requires consumers to explicitly invoke the acknowledge( ) method on a message, to acknowledge all messages received since the previous acknowledgment. This mode enables a JMS client to acknowledge a batch of messages via a single method call.

DUPS_OK_ACKNOWLEDGE

The consumer's runtime will acknowledge the received message automatically once it has finished processing the message, although duplicate acknowledgments also may be permitted.

Together with persistent messaging, these acknowledge modes form the basis of reliable and guaranteed message delivery. Remember, the acknowledge modes are completely ignored for transacted JMS sessions, or when a nontransacted session is used within the context of a JTA transaction.

WebLogic JMS provides two additional acknowledge modes:

NO_ACKNOWLEDGE

Unlike the standard modes where an acknowledgment always is expected and sent, a JMS server that delivers messages to a session created in the NO_ACKNOWLEDGE mode does not expect an acknowledgment.

MULTICAST_NO_ACKNOWLEDGE

This mode supports multicasting for applications that can tolerate the quality of service offered by no acknowledgments. Messages sent to a session created in the MULTICAST_NO_ACKNOWLEDGE mode share the same characteristics as a JMS session created in the NO_ACKNOWLEDGE mode both provide improved levels of performance.

In these no-acknowledge modes, the JMS server acknowledges the message itself before sending it! Thus, messages sent to JMS sessions created in either of these modes are deleted from the JMS server immediately. For this reason, there are a few consequences of using the no-acknowledge modes:

These extended acknowledge modes can be found in the proprietary WLSession class. The following code shows how to set up an asynchronous queue receiver within a JMS session that has been created in the NO_ACKNOWLEDGE mode:

QueueSession qsession = qcon.createQueueSession(false, WLSession.NO_ACKNOWLEDGE); Queue queue = (Queue) ic.lookup(QUEUE_JNDI_NAME); QueueReceiver qrec = qsession.createSubscriber(queue); qrec.setMessageListener(this);

8.3.6 Multicasting

In general, a JMS topic will have subscribers running on different hosts within a domain. The more subscribers, the more network resources needed by the JMS server to deliver the messages to them. In such cases, you can set up multicasting on the JMS session. This enables the JMS server to deliver the messages to a select group of hosts that later can forward the messages to all subscribers running on that host. The benefits of multicasting are two-fold:

A potential risk of using multicasting is that the JMS messages are not guaranteed to be delivered to all subscribers within the host group. Messages may be lost (or duplicated) in a congested network, or if subscribers fall behind in their processing. If the subscribers cannot tolerate this quality of service, you should not enable multicasting on the topic.

Multicasting is supported for nondurable subscribers that specify the MULTICAST_NO_ACKNOWLEDGE acknowledge mode. As a single destination can support both types of subscribers, durable subscribers will continue to get reliable replicas of the message via TCP/IP.

 

8.3.6.1 Enabling multicasting

In order to make use of multicast sessions, you must first configure the JMS connection factory and topics to support multicasting. Use the Administration Console to specify the multicast attributes for the connection factory and JMS topic. First, choose the desired JMS topic from the left pane and then navigate to the Configuration/Multicast tab. Here you can adjust the following multicast settings for the destination.

Multicast Address and Multicast Port

Use these attributes to specify a valid multicast address and port for your network. The JMS server will broadcast the messages intended for this topic, on this address, and the subscribers running on the different hosts will pick up the messages via this same address and port.

Multicast TTL

Use this attribute to specify the time-to-live for each multicast packet. Its value determines the number of routers that a multicast message can traverse. If this attribute is set to 1, the message will not be permitted to traverse any routers, and so all multicast packets will be restricted to the current subnet.

After this, you must navigate to the Configuration/General tab for the chosen connection factory to further refine the runtime multicast behavior. Here you can adjust the following two multicast attributes:

Messages Maximum

For a nonmulticast session, Messages Maximum determines the maximum number of messages that may exist on an asynchronous session but have not yet been passed to a listener. While a nonmulticast session will ensure that the JMS server retains messages if the pipeline is full, a multicast session will simply discard messages if the pipeline is full, and throw a DataOverrunException exception. The manner in which the messages are discarded is based on the overrun policy.

Overrun policy

You can choose from two overrun policies: Keep Old or Keep New. The default Keep Old value gives the older messages a higher priority so that the JMS server discards the most recent messages. The Keep New value ensures that the JMS server retains the most recent messages, but discards the older messages.

The message age is determined by the order of receipt and not by the message timestamp.

The overrun policy and maximum messages threshold also can be set on an individual producer at runtime. To accomplish this, simply invoke the setMessagesMaximum( ) and setOverrunPolicy( ) methods on the WebLogic-specific WLTopicSession interface. In this case, the runtime values for the multicast session override the default values for the multicast attributes on the connection factory.

8.3.6.2 Using a multicast session

Once you've configured the multicast attributes on the JMS topic and the connection factory, you are ready to programmatically create multicast sessions. The following code sample shows how to create a JMS subscriber that handles multicast messages arriving at the topic:

TopicSession tsession = tcon.createTopicSession(false, WLSession.MULTICAST_NO_ACKNOWLEDGE); Topic topic = (Topic) ctx.lookup(MULTICAST_TOPIC_JNDI_NAME); TopicSubscriber tsub = tsession.createSubscriber(topic); tsub.setMessageListener(this);

Notice how we've specified a WebLogic-specific acknowledgment mode to indicate that the JMS session would like to receive multicast messages. Except for this, we have adopted the standard approach to registering a topic subscriber.

A topic subscriber can only receive multicast messages asynchronously. Any attempt to synchronously receive a message within a multicast session will result in a JMSException.

 

8.3.6.3 Sequence violations

WebLogic tracks received messages to correlate their order with the order in which the messages were produced. A multicast subscriber may receive a message out of sequence, which means that one or more messages may be skipped. If the JMS server determines that a message has been delivered out of sequence, WebLogic throws a SequenceGapException. This exception can be handled by an exception listener if you have registered one on the multicast session. If a skipped message is subsequently delivered, WebLogic will simply discard it.

Категории