Flexible JMS MDBs: method annotations (version 4)
This page contains some proposals for JMS 2.1 that were being considered by the expert group before work was halted and JSR 368 withdrawn. It is retained here as a historical record and in case it proves useful to a future JMS expert group. See also the main JMS 2.1 page
This page contains version 4 of proposals to simplify the configuration of JMS MDBs in JMS 2.1 and Java EE 8.
These proposals supersede version 1, version 2 and version 3.
These proposals were superseded by version 5](JMSListener5).
Contents
- Original proposal (option A)
- Proposal in Early Draft (option B)
- New option F
- Another proposal (option G)
Original proposal (option A)
Queue (option A)
@MessageDriven
public class MyMessageBean {
@JMSListener(lookup="java:global/java:global/requestQueue",type=JMSListener.Type.QUEUE)
@JMSConnectionFactory("java:global/connectionFactory")
@MessageSelector("JMSType = 'car' AND colour = 'pink'")
@Acknowledge(Acknowledge.Mode.DUPS_OK_ACKNOWLEDGE)
@ListenerProperty(name="foo1", value="bar1")
@ListenerProperty(name="foo2", value="bar2")
public void myMessageCallback(Message message) {
...
}
}
Non-Durable subscription on topic (option A)
@MessageDriven
public class MyMessageBean {
@JMSNonDurableTopicListener(
@JMSListener(lookup="java:global/java:global/someTopic",type=JMSListener.Type.TOPIC)
@JMSConnectionFactory("java:global/connectionFactory")
@MessageSelector("JMSType = 'car' AND colour = 'pink'")
@Acknowledge(Acknowledge.Mode.DUPS_OK_ACKNOWLEDGE)
@ListenerProperty(name="foo1", value="bar1")
@ListenerProperty(name="foo2", value="bar2")
)
public void myMessageCallback(Message message) {
...
}
}
Durable subscription on topic (option A)
@MessageDriven
public class MyMessageBean {
@JMSDurableTopicListener(
@JMSListener(lookup="java:global/java:global/someTopic",type=JMSListener.Type.TOPIC)
@JMSConnectionFactory("java:global/connectionFactory")
@MessageSelector("JMSType = 'car' AND colour = 'pink'")
@Acknowledge(Acknowledge.Mode.DUPS_OK_ACKNOWLEDGE)
@SubscriptionName("mySubName")
@SubscriptionDurability(SubscriptionDurability.Mode.DURABLE)
@ClientId("myClientId")
public void myMessageCallback(Message message) {
...
}
}
Option A: discussion
Proposal in Early Draft (option B)
Queue (option B)
@MessageDriven
public class MyMessageBean {
@JMSQueueListener(
destinationLookup="java:global/requestQueue",
connectionFactoryLookup="java:global/connectionFactory",
messageSelector="JMSType = 'car' AND colour = 'pink'",
acknowledge=JMSQueueListener.Mode.DUPS_OK_ACKNOWLEDGE
)
public void myMessageCallback(Message message) {
...
}
}
Non-Durable subscription on topic (option B)
@MessageDriven
public class MyMessageBean {
@JMSNonDurableTopicListener(
destinationLookup="java:global/someTopic",
connectionFactoryLookup="java:global/connectionFactory",
messageSelector="JMSType = 'car' AND colour = 'pink'",
acknowledge=JMSNonDurableTopicListener.Mode.DUPS_OK_ACKNOWLEDGE
)
public void myMessageCallback(Message message) {
...
}
}
Durable subscription on topic (option B)
@MessageDriven
public class MyMessageBean {
@JMSDurableTopicListener(
destinationLookup="java:global/someTopic",
connectionFactoryLookup="java:global/connectionFactory",
messageSelector="JMSType = 'car' AND colour = 'pink'",
acknowledge=JMSDurableTopicListener.Mode.DUPS_OK_ACKNOWLEDGE,
subscriptionName="mySubName",
clientId="myClientId"
)
public void myMessageCallback(Message message) {
...
}
}
Proposals in early draft: some observations
Good:
-
Very obvious whether a queue or topic, or whether topic subscription is durable or non-durable
-
Once the user has chosen which attribute to use, the IDE can prompt them of the various elements available
-
No need for the user to explicitly set destinationType or subscriptionDurability
Bad:
-
Many elements (attributes) to set
-
The word “listener” (as in JMSQueueListener) is inappropriate on a method as users think of the class that is the listener, not the callback method.
-
There are issues with the
acknowledge
element:- It is set to a enumerated type specific to the annotation. This is verbose and a likely cause of confusion.
- It is ignored unless bean-managed transactions have been explicitly configured using the class annotation <
@TransactionManagement(value=TransactionManagementType.BEAN)
. Ideally the two settings should be combined, but since the@TransactionManagement
annotation is used for all types of EJB we can’t extend it just for JMS (and it would be confusing if tried to replace it with a different annotation just for flexible JMS MDBs). SO we’re probably stuck with this. - There is never any need to set the
acknowledge
element to AUTO_ACKNOWLEDGE since this is the default. So the only time you’d ever need to set this element is to configure DUPS_OK_ACKNOWLEDGE. If we removed the ability to set auto-ack it may avoid users mistaking thinking this is all they need to do to turn off bean-managed transactions.
New option F
This option is based on the discussion at the JMS face-to-face meeting on 29th October, and marks a return to the original approach which used a larger number of simpler annotations rather than using a smaller number of larger annotations.
Queue (option F)
@MessageDriven
public class MyMessageBean {
@OnMessage(java:global/requestQueue")
@Queue
@JMSConnectionFactory("java:global/connectionFactory")
@MessageSelector("JMSType = 'car' AND colour = 'pink'")
@DupsOKAcknowledge
@ListenerProperty(name="foo1", value="bar1")
@ListenerProperty(name="foo2", value="bar2")
)
public void myMessageCallback(Message message) {
...
}
}
Non-Durable subscription on topic (option F)
@MessageDriven
public class MyMessageBean {
@OnMessage("java:global/someTopic")
@Topic
@JMSConnectionFactory("java:global/connectionFactory")
@MessageSelector("JMSType = 'car' AND colour = 'pink'")
@DupsOKAcknowledge
@ListenerProperty(name="foo1", value="bar1")
@ListenerProperty(name="foo2", value="bar2")
)
public void myMessageCallback(Message message) {
...
}
}
Durable subscription on topic (option F)
@MessageDriven
public class MyMessageBean {
@OnMessage("java:global/someTopic")
@Topic
@JMSConnectionFactory("java:global/connectionFactory")
@MessageSelector("JMSType = 'car' AND colour = 'pink'")
@DupsOKAcknowledge
@DurableSubscription(name="mySubName", clientId="myClientId")
@ListenerProperty(name="foo1", value="bar1")
@ListenerProperty(name="foo2", value="bar2")
public void myMessageCallback(Message message) {
...
}
}
New option F: discussion
@OnMessage
is used to define the callback method and may only be specified at method level.- The
value
element defines the JNDI name of the destination (i.e. thedestinationLookup
property) and must be provided. - This doesn’t use the word “listener” which makes it more appropriate for a callback method.
- The name is consistent with the existing annotation
@javax.websocket.OnMessage
- The
@Queue
,@Topic
,@JMSConnectionFactory
,MessageSelector
,@DupsOKAcknowledge
@DurableSubscription
andListenerProperty
may be specified at either class or method level. Annotations at method level override annotations are class level.- If any of
@Queue
,@Topic
,@JMSConnectionFactory
,MessageSelector
,@DupsOKAcknowledge
@DurableSubscription
andListenerProperty
are specified at class level and no callback method is defined then deployment will fail. -
If any of
@Queue
,@Topic
,@JMSConnectionFactory
,MessageSelector
,@DupsOKAcknowledge
@DurableSubscription
andListenerProperty
are specified at method level but the method is not defined to be a callback method then deployment will fail. -
JMSConnectionFactory
is an existing annotation (used when injectingJMSContext
objects) which is being extended for use here. It is optional whether this annotation is used, but if it is used then thevalue
element defines the JNDI name of the connection factory (i.e. theconnectionfactoryLookup
property) and must be provided. If this annotation is not used then the application server or resource adapter may either provide a suitable default connection factory or cause deployment to fail. (Note that we can’t simply say that by default the Java EE platform default JMS connection factory is used since this may not be appropriate for the resource adapter being used). @Queue
and@Topic
define the value of thedestinationType
property.@Queue
specifies that the destination is a queue.@Topic
specifies that the destination is a topic.- If both are specified on the same method, or both are specified at class level, then deployment will fail.
- What happens if neither
@Queue
nor@Topic
are specified? Currently the EJB spec does not define what happens ifdestinationType
is not specified- (Proposed) It is optional whether
@Queue
or@Topic
are specified. There is no default value. The container will discover the destination type using a new methodgetDestinationType
on theDestination
interface. If@Queue
or@Topic
is specified then deployment will fail if the specified type does not match the actual type of the destination. - (Alternative 1) It is undefined whether
@Queue
or@Topic
is required, and whether there is any default value. This is essentially howdestinationType
works at present. - (Alternative 2) It is optional whether
@Queue
or@Topic
are specified. If neither are specified then@Queue
is assumed. The container will discover the destination type (see above) and deployment will fail if the specified or default type does not match the actual type of the destination. - (Alternative 3) There are no
@Queue
or@Topic
annotations. The container will discover the destination type (see above).
- (Proposed) It is optional whether
-
@MessageSelector
. It is optional whether this annotation is used, but if it is used then the value element defines the message selector to be used (i.e. themessageSelector
property) and must be provided. -
@DurableSubscription
. It is optional whether this annotation is used. If it is used then thesubscriptionDurability
property is set toDurable
. It has two elements, both of which are optional. IfclientId
is specified then this sets theclientId
property. Ifname
is specified then this sets thesubscriptionName
property. - Annotations for setting acknowledgement mode:
- The
@DupsOKAcknowledge
annotation is equivalent to setting acknowledgeMode to “Dups-ok-acknowledge”. It is ignored unless the class is also annotated with@TransactionManagement(TransactionManagementType.BEAN)
- Do we also need a
@AutoAcknowledge
annotation? This would never be needed (since this is the default when container-managed transactions are specified). One advantage of not having a@AutoAcknowledge
annotation is that a user might mistakenly think that specifying@AutoAcknowledge
causes the MDB to use auto-acknowledgement, when in fact the user needs to specify the class annotation@TransactionManagement(TransactionManagementType.BEAN)
. - Alternatively we could define both
@DupsOKAcknowledge
and@AutoAcknowledge
and require that deployment fails if the application has not also set@TransactionManagement(TransactionManagementType.BEAN)
. This would prevent the user thinking they had configured auto-ack but still using a transaction.
- The
@ListenerProperty
This annotation may be used to specify a single arbitrary activation property. The elementsname
andvalue
must be specified. This is a repeatable annotation and may be specified multiple times.
Another proposal (option G)
Key features of this proposal
-
The user is required to specify the messaging semantic being used: queue, durable topic subscription, non-durable topic subscription using
@JMSQueueListener
,@JMSDurableTopicListener
,@NonDurableTopicListener
. -
Other properties are set using individual annotations
-
The user has the option of specifying the delivery QoS (transactional, auto-ack, dups-ok-ack) directly on the callback method.
Queue (option G)
@MessageDriven
public class MyMessageBean {
@JMSQueueListener(java:global/java:global/requestQueue")
@JMSConnectionFactory("java:global/connectionFactory")
@MessageSelector("JMSType = 'car' AND colour = 'pink'")
@DupsOKAcknowledge
@ListenerProperty(name="foo1", value="bar1")
@ListenerProperty(name="foo2", value="bar2")
public void myMessageCallback(Message message) {
...
}
}
Setting @DupsOKAcknowledge
overrides an explicit or by-default setting of @TransactionManagement(TransactionManagementType.CONTAINER)
Non-Durable subscription on topic (option G)
@MessageDriven
public class MyMessageBean {
@JMSNonDurableTopicListener("java:global/java:global/someTopic")
@JMSConnectionFactory("java:global/connectionFactory")
@MessageSelector("JMSType = 'car' AND colour = 'pink'")
@AutoAcknowledge
@ListenerProperty(name="foo1", value="bar1")
@ListenerProperty(name="foo2", value="bar2")
)
public void myMessageCallback(Message message) {
...
}
}
Setting @AutoAcknowledge
overrides an explicit or by-default setting of @TransactionManagement(TransactionManagementType.CONTAINER)
Durable subscription on topic (option G)
@MessageDriven
public class MyMessageBean {
@JMSDurableTopicListener(java:global/java:global/someTopic")
@JMSConnectionFactory("java:global/connectionFactory")
@MessageSelector("JMSType = 'car' AND colour = 'pink'")
@javax.jms.Transactional
@SubscriptionName("mySubName")
@ClientId("myClientId")
public void myMessageCallback(Message message) {
...
}
}
Even though the @JMSDurableTopicListener
has been specified, the subscription name and clientId are set using two separate annotations. This is because in practice these settings (which only became standard in EJB 3.2) are often not required. It also follows the approach of having multiple annotations rather than having a single annotation with multiple attributes.
Option G: discussion
- The user is required to specify the messaging semantic being used: queue, durable topic subscription, non-durable topic subscription using
@JMSQueueListener
,@JMSDurableTopicListener
,@NonDurableTopicListener
.- The idea here is that there are three messaging semantics, and that we should require the user to specify which they are using. It would be inconsistent to require the user to specify whether a topic subscription is durable or non-durable but not whether the destination is a queue or topic.
- This also reduces the number of annotations needed by combining the annotation which specifies the callback method with the annotation which specifies the messaging semantic.
- The user has the option of specifying the delivery QoS (transactional, auto-ack, dups-ok-ack) directly on the callback method.
- At the very least, this could be taken as an assertion, so that if the user has configured an incompatible
@TransactionManagement
annotation on the class. - We could extend this to be an alternative to setting
@TransactionManagement
annotation on the class. In that case we would need to consider what would happen if the two has incompatible settings. Would this be an error, or would one override the other? - Instead of separate ` @AutoAcknowledge
,
@DupsOKAcknowledge,
@javax.jms.Transactionalannotations we could have a single annotation with three element values:
@QOS(QOS.AutoAcknowledge),
@QOS(QOS.DupsOKAcknowledge),
@QOS(QOS.Transactional)`.
- At the very least, this could be taken as an assertion, so that if the user has configured an incompatible