users@jms-spec.java.net

[jms-spec users] Re: JMS_SPEC-116: Make Message Poisoning Handling a priority?

From: Nigel Deakin <nigel.deakin_at_oracle.com>
Date: Wed, 12 Aug 2015 14:56:53 +0100

On 12/08/2015 00:50, David Heffelfinger wrote:
> New guy here, apologies if I'm breaking any rules, if this is the case please let me know.
Welcome! Comments like this are just fine (and a meaningful subject line like you have used is helpful too, especially
when they reference the JIRA issue under discussion)

(As a general point, you may see me respond to comments by trying to redirect discussions to an existing issue, or
subdivide an issue into separate aspects. That's just me trying to organise discussions into manageable parts, or
reducing the dependency between one set of changes and another set of changes.)

>
> In any case, I read Nigel's "More flexible JMS MDBs (Version 2)" document at
> https://java.net/projects/jms-spec/pages/JMSListener2 and have some comments.
>
> I like a lot of the ideas that have been suggested.
That's good to hear!
>
> Most of the ideas suggested will result in a better, easier to use APIs, which is great, however, the following
> particular suggestions addresses what, in my experience, is one of the biggest pain points when using MDB's:
>
> "The JMS provider should detect repeated attempts to redeliver the same message to a MDB. Such messages may either be
> discarded or delivered to a provider-specific dead message queue. (Note that this not completely new to JMS: JMS 2.1
> section 8.7 refers to a JMS provider "giving up" after a message has been redelivered a certain number of times)."
>
> I am actually working on a project right now that uses JMS extensively, and "JMS Message Poisoning" has been a problem
> that has been not that easy to solve. Essentially if there is an exception when handling a message, the message is put
> back in the queue then immediately consumed again by the same MDB, again the exception is thrown, the message goes back
> in the queue, essentially in an infinite loop. It would be great if the spec provided a standard, easily configurable
> way to deal with this situation. If I may, I think it would be nice that this particular suggestion does not "fall
> through the cracks" (i.e. make it a high priority).
In terms of managing our work on JMS 2.1 there are two aspects to this.

*1. Poison messages generally*

The first aspect is defining how "poison messages" should be handled in JMS and Java EE generally. This is a
frequently-requested feature. I looked to see whether we had a JIRA issue logged for this, and came across JMS_SPEC-72
<https://java.net/jira/browse/JMS_SPEC-72> which seems to cover the general case.

This was one of the issues mentioned when I originally proposed JSR 368 (see https://www.jcp.org/en/jsr/detail?id=368,
which mentions redelivery behaviour), so it's definitely on the roadmap for JMS 2.1. I suspect this might be quite
difficult, since (as Vaquar mentioned) each application has different needs, and also because several JMS vendors and
Java EE app servers currently provide their own features to handle redelivery (redelivery delay, redelivery limits, dead
message queues etc) and it will be a challenge to define standard behaviour without undermining or breaking these
existing features.

*2. Poison messages with new-style JMS MDBs*

The other aspect is addressing the specific requirements of the current new JMS MDB proposals. (That's issue
JMS_SPEC-116 <https://java.net/jira/browse/JMS_SPEC-116>, with detailed proposals at JMSListener2
<https://java.net/projects/jms-spec/pages/JMSListener2>).

The reason that the current MDB proposals mention "poison message" handling is that they introduce two new use cases
which are not currently possible. (If it were not for these two cases there would be no need for the current proposals
to mention redelivery at all.)

2a. If a user-defined callback method throws a checked exception (the existing onMessage method is not allowed to throw
a checked exception, but I am proposing that a user-defined callback method is allowed to do so)

2b. If a message cannot be delivered because the user-defined callback method has parameters which cannot be set (e.g.
if the callback message is handleMessage(String text), but the message is a BytesMessage).

*Suggested approach*

We should have discussions on both, but I see the priority as being (2), defining how the new MDB cases 2a and 2b should
be handled. I would like to avoid the new MDB proposals becoming dependent on solving the general case (1).

Currently, the JMS spec is fairly vague on how "poison messages" are handled. Despite that, vendors have managed to
implement JMS MDBs (and Java SE MessageListeners), adding their own extra features on top.

My suggestion is that we continue this approach for the new-style MDBs: use a wording that allows vendors to handle
these new cases in a similar way to how they do now. This would then allow us to finish defining the MDB improvements
without getting too side-tracked into the issue of poison message handling.

*For exceptions thrown by user-defined MDB callback methods* *(case 2a)*

The current proposal is:

    "Callback methods will be allowed to declare and throw exceptions. Exceptions thrown by the callback method
    (including unchecked exceptions thrown by the onMessage method of a MessageListener) will be handled by the EJB
    container as defined in the EJB 3.2 specification section 9.3.4 "Exceptions thrown from Message-Driven Bean Message
    Listener methods". This defines whether or not any transaction in progress is committed or rolled back, depending on
    whether or not the exception is a "system exception" or an "application exception", whether or not the application
    exception is specified as causing rollback, and whether or not the application has called setRollbackOnly. It also
    defines whether or not the MDB instance is discarded. If the transaction is rolled back, or a transaction is not
    being used, then the message will be redelivered." (Paragraph 1)

    "The JMS provider should detect repeated attempts to redeliver the same message to a MDB. Such messages may either
    be discarded or delivered to a provider-specific dead message queue. " (Paragraph 2)

Paragraph 1 is mostly a repeat of what the EJB spec already says, so doesn't add anything new. Hidden in that is the
requirement that if the exception is an "application exception" (and checked exceptions are application exceptions by
default) then the transaction will be /committed /unless the exception is explicitly annotated as causing rollback. This
may be surprising: do we want to change this?

Paragraph 2 goes rather further than the existing spec, which doesn't mention automatically sending messages to dead
message queues. The closest thing I can find is this sentence in JMS 2.0 section 8.7 which refers to the auto-ack case
only:

    "The number of times a JMS provider will redeliver the same message before giving up is provider-dependent. The
    JMSRedelivered message header field will be set, and the JMSXDeliveryCount message property incremented, for a
    message redelivered under these circumstances."

I'm tempted to change Paragraph 2, to drop the reference to discarding the message, or to dead message queues, and
replace it with the sentence above. When/if we get around to defining poison message handling more completely we can
extend this. What do people think about this?

*If a message cannot be delivered because the user-defined callback method has parameters which cannot be set (Case 2b)*

This is a different case since it does not involve the MDB throwing an exception. Instead, this is an error condition
detected within the application server or resource adapter before the MDB has been called, and possibly before any
transaction has been started. So this is a new case that needs to be defined.

The current proposal is:

    "If any of the parameters of the callback method cannot be set because they have an incompatible type then callback
    method will not be invoked. Such messages may either be discarded or delivered to a provider-specific dead message
    queue. "

I wonder whether we should again drop the reference to discarding the message, or to dead message queues, and instead
refer to the JMS provider making repeated attempts to deliver the message before "giving up".

In this case, a second attempt to deliver the message to the same MDB would fail for exactly the same reason. It would
only succeed if the application was changed and restarted, or if (in the case of a queue) it was delivered to some
different MDB or consumer.

We should also define whether or not "repeated attempts to deliver the message" counts as redelivery (meaning
JMSRedelivered must be set). Since the application hasn't actually seen the message before, it isn't really redelivery.
My inclination is to state that JMSRedelivered does not have to be set (and JMSXDeliveryCount does not have to be
incremented), but to repeat that setting JMSRedelivered in this case (and incrementing JMSXDeliveryCount) is not
considered an error.

Any other views?

Nigel