egbokul
egbokul

Reputation: 3974

JMS message re-delivery delay

I have a JMS client that can ssh to remote systems upon receiving a message (and do various things there - not relevant to the question). It is possible that hundreds of such messages will arrive in a short period of time which need to be processed ASAP.

However, it is also possible that certain remote systems are not available when the message is received, so they should be postponed until later (eg. 1 hour or so). The best solution would be to put the message back to the queue with some "delay" value set, which will tell the JMS broker not to try to deliver the message again within an hour.

What is not OK: sleep in the receiving thread and wake up an hour later. Since the message consumer pool is limited (eg. 8 connections available) having 8 non-reachable systems would block the whole processing unnecessarily, which is unacceptable.

I didn't find a setting for either the message or the queue itself for such a "delay" value, does it exist?

A workaround solution is to use a second queue for storing messages to unreachable systems, and process these separately. But it is not a very elegant solution, and requires additional programming. Perhaps there is a better way.

Upvotes: 10

Views: 11071

Answers (6)

A. Neznakhin
A. Neznakhin

Reputation: 1

Good day!

For Oracle WebLogic Server exactly, there are two parameters:

  • Redelivery Delay Override - on the level of Queue, and
  • Default Redelivery Delay - on the level of Connection factory.

Upvotes: 0

99Sono
99Sono

Reputation: 3677

On glassfish, the following references can of use.

https://docs.oracle.com/cd/E19798-01/821-1794/aeooq/index.html

On the above reference you get a list of activation properties that are supported by glassfish. E.g. endpointExceptionRedeliveryAttempts -
Number of times to redeliver a message when MDB throws an exception during message delivery

Then you have the following reference that describes valid xml elements to use on the sun-ejb-jar.xml, which is supported by glassfish. https://docs.oracle.com/cd/E19798-01/821-1750/beaqm/index.html

Finally, you could configure an Mdb as depicted in the following snippet.

<ejb>
    <ejb-name>MyMdbWith0MsRedeliveryDelayAndMultipleRedeliveriesMdb</ejb-name>
    <bean-pool>
        <steady-pool-size>1</steady-pool-size>
        <resize-quantity>1</resize-quantity>
        <max-pool-size>1</max-pool-size>
    </bean-pool>
    <mdb-resource-adapter>
        <activation-config>
            <activation-config-property>
                <activation-config-property-name>endpointExceptionRedeliveryAttempts</activation-config-property-name>
                <activation-config-property-value>1000</activation-config-property-value>
            </activation-config-property>
            <activation-config-property>
                <activation-config-property-name>endpointExceptionRedeliveryAttempts</activation-config-property-name>
                <activation-config-property-value>0</activation-config-property-value>
            </activation-config-property>
        </activation-config>              
    </mdb-resource-adapter>
</ejb>

This should ensure that for this particular mdb, glassfish will immediate deliver a message and if it fails it will retry it immediately.

In your WAR project, create a sub-ejb-jar.xml and place it under WEB-INF/sun-ejb-jar.xml.

If you are using maven, this would be your src/main/webapp/WEB-INF/sub-ejb-jar.xml path on the war project component.

Upvotes: 0

T.Rob
T.Rob

Reputation: 31832

This is not possible through the JMS API prior to JMS 2.0. As a general rule, message transports are optimized to deliver messages as fast as possible and lack schedulers to hold a message for redelivery at some arbitrary interval. Assuming there is a transport provider that does have such functionality, anything you write would then be bound to that transport provider because, although the code might be JMS-compliant, the app itself would rely on this vendor-specific behavior.

See @Shashi's answer for an answer that applies to versions of MQ that support JMS 2.0.

Upvotes: 4

Shashi
Shashi

Reputation: 15263

JMS 2.0 Specification defines a "Delivery delay" where client can specify a delivery delay value in milliseconds for each message it sends. This value defines a message delivery time which is the sum of the message’s delivery delay and the GMT it is sent (for transacted sends, this is the time the client sends the message, not the time the transaction is committed).

A message's delivery time is the earliest time when a JMS provider may make the message visible on the target destination and available for delivery to consumers. The provider must not deliver messages before the delivery time has been reached.

This feature is quite handy for the scenario described above.

Upvotes: 10

user2701935
user2701935

Reputation: 1

I would look at the problem as: jms message consumer pool is available ...while some backend resources (i.e. ssh) are not available to allow completion of the message.

If you agree with that then the problem becomes why is the consumer-pool available since it cannot complete consumption? Had you had the pool not available then the messages would pile up in the queue....until available...and that could have been ok to continue when available. Then if that is the case you just need a monitoring component to start/stop the pool depending if resources are available. you dont necessary need to wait 1hr but as long as the backend are not available. at the end jms is everything but not a monitoring tool.

Upvotes: 0

Amer A.
Amer A.

Reputation: 1045

In this situations, Container Managed Transactions are used. The transaction is started when the onMessage method is called by the container and is commited if the onMessage method finishes successfuly (it will fail when a RuntimeException is thrown or setRollBackOnly is called from the MessageDrivenBean context). You can also configure the redelivery interval and maximum number of redelivery's.

If you are using OpenMQ with Glassfish server, you can configure this inside the ejb-jar.xml descriptor. Inside the ejb-jar.xml descriptor set the properties endpointExceptionRedeliveryInterval (in milliseconds) and endpointExceptionRedeliveryAttempts (number of times the message is redelivered before it is sent to the dead message queue). Here is an example:

<?xml version="1.0" encoding="UTF-8"?>
<ejb-jar  xmlns="http://java.sun.com/xml/ns/javaee"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://java.sun.com/xml/ns/javaee/ejb-jar_3_1.xsd"
    version="3.1">    
    <enterprise-beans>
        <message-driven>
            <ejb-name>EjbName</ejb-name>
            <ejb-class>com.example.MyMessageDrivenBean</ejb-class>
            <messaging-type>javax.jms.MessageListener</messaging-type>
            <transaction-type>Container</transaction-type>
            <activation-config>
                <activation-config-property>
                    <activation-config-property-name>destination</activation-config-property-name>
                    <activation-config-property-value>someQueue</activation-config-property-value>
                </activation-config-property>
                <activation-config-property>
                   <activation-config-property-name>destinationType</activation-config-property-name>
                   <activation-config-property-value>javax.jms.Queue</activation-config-property-value>
                </activation-config-property>

                <activation-config-property>
                    <activation-config-property-name>endpointExceptionRedeliveryInterval</activation-config-property-name>
                    <activation-config-property-value>5000</activation-config-property-value>
                </activation-config-property>
                <activation-config-property>
                    <activation-config-property-name>endpointExceptionRedeliveryAttempts</activation-config-property-name>
                    <activation-config-property-value>4</activation-config-property-value>
                </activation-config-property>
            </activation-config>
        </message-driven>
    </enterprise-beans>
</ejb-jar>

And inside the message driven bean throw a RuntimeException to mark it as failed and the message will be returned to the queue.

Here are also the configuration properties for WebLogic Server: http://docs.oracle.com/cd/E12839_01/apirefs.1111/e13952/pagehelp/JMSjmstemplatesjmstemplateconfigredeliverytitle.html

Upvotes: 4

Related Questions