I'd like to review where I think we are on the issue of injecting JMSContext objects into a JavaEE application.
(JMSContext is the new name for MessagingContext, and to avoid confusion I'll use the new name throughout in this email)
I ask a number of questions which are repeated in a single list at the end. If you have a strong view on any of them
please do express an opinion. However these are not easy questions, there may be options other than the alternatives
suggested, and we may need to discuss this further before coming to a final conclusion. I plan to call a conference
call in the near future to discuss this.
*Recap of Early Draft proposal*
The current proposals are as described in section 11.3 "Injection of JMSContext" objects of the Early Draft. Here's a
concise version of what it says,
/This section relates to application classes which run in the Java EE web, EJB or application client containers and
which support injection. Section EE.5 of the Java EE specification lists the application classes that support injection.
Applications may declare a field of type javax.jms.JMSContext and annotate it with the javax.inject.Inject annotation:
The container will inject a JMSContext. It will have request scope and will be automatically closed when the request
ends. However, unlike a normal CDI request-scoped object, a separate JMSContext instance will be injected for every
injection point.
@Inject
private JMSContext context;
The following annotations may be added to specify the injected JMSContext in more detail:
@JMSConnectionFactory("jms/connectionFactory") - used to specify the lookup name of the connection factory
@JMSPasswordCredential(userName="admin",password="mypassword") - used to specify the user/password passed to
createConnection
@JMSSessionMode(JMSContext.AUTO_ACKNOWLEDGE) - used to specify the session mode of the JMSContext (subject to existing
JavaEE restrictions)
@JMSAutoStart(false) - used to prevent the connection being started automatically
I have a prototype implementation of the above proposal (created with the help of Pete Muir) which demonstrates that it
can be implemented reasonably easily./
*Issues*
I believe that, despite my successfully prototyping the proposal above, the following issues are unresolved:
*1. Is this CDI injection or not? *
The existing proposal is that the way in which JMSContext objects are injected should be as consistent with standard CDI
dependency injection as possible and be implemented by normal CDI mechanisms. This means:
* Injection is defined using @Inject
* Containers need to "support injection points annotated with the javax.inject.Inject annotation only to the extent
dictated by CDI" (Java EE 6 Section EE 5.21).
* It will be optional to support the injection of JMSContext objects in the application client (since CDI support in
the application client is optional)
* Injection of JMSContext objects will only be possible in application modules for which CDI is enabled by including a
META-INF/beans.xml descriptor.
However it is not essential for this to be CDI injection (even if it is implemented using CDI internally). There are
numerous examples in Java EE of non-CDI injection, including @Resource and @PersistenceContext. By removing the
connection with CDI, and simply defining the required behaviour and leaving the implementation up to the container, some
additional options become available:
* Injection could be defined using any annotation we choose rather than @Inject (perhaps @MessagingContext, in the
manner of @PersistenceContext)
* JMS would be free to mandate support for the injection of JMSContext objects in the application client, just as the
Java EE spec mandates support for resource injection using @Resource.
* JMS could mandate support for the injection of JMSContext objects without the need to include a META-INF/beans.xml
descriptor.
Personally I can see benefits in making this non-CDI injection. This will avoid the need for applications to define a
META-INF/beans.xml descriptor. It will make it possible to mandate injection in the Application Client. It will be
consistent with @PersistenceContext. It will avoid any confusion caused by injection "looking like "normal CDI injection
but having a special scope and using annotations rather than qualifiers.
Note that defining this as "non-CDI" injection does not prevent CDI being used by the container to actually implement it.
/Question 1: should the injection of JMSContext be tied to CDI or should we simply define the injection behaviour we
require? A=Yes, B=No/
*2. Do we want to create a separate instance for each injection point?*
This relates to what I describe as the "sharing" aspects of the injected object.
The current proposal states "unlike a normal CDI request-scoped object, a separate JMSContext instance will be injected
for every injection point." An injected JMSContext is never shared between different injection points, even within the
same request. In this sense it is behaving as if it had dependent scope, though since we are closing the injected
JMSContext at the end of the request it is also behaving as if it had request scope.
What this means in practice is that two beans, called within the same request, which injected a JMSContext (using
identical annotations), would use /different /JMSContext instances. The main functional significance of this is that
since the two JMSContext objects would use different underling sessions, any messages sent by the two beans would not
necessarily be delivered in the order in which they were sent.
Allowing messages sent within a request to be delivered in order seems a useful feature, which could be easily provided
by defining that injected JMSContext objects be shared according to the defined scope, just like with normal CDI
injection. So two beans, called within the same request, which injected a JMSContext (using identical annotations),
would use the same JMSContext instance. This change would also reduce the number of JMSContext instances that need to
be exist to service the request and thereby increase scalability.
I think that switching to the normal CDI object sharing model would therefore be a good idea.
However Rüdiger has suggested (to me) that this might be confusing, since calling setPriority, setAutoStart,
setTimeToLive, setDeliveryMode, setDeliveryDelay, setDisableMessageTimestamp or setDisableMessageID on a JMSContext in
one bean would then have an effect on a JMSContext in another bean. He has proposed that we share the underlying
connection and session (in order to preserve message order) but not these properties. One way to describe this might be
to say that the underlying connection and session are request-scoped, but the underlying producer (which holds the
properties) is dependent scope. Whilst this might not be too difficult to implement it would be rather complicated to
describe and might itself be confusing for users. So my inclination is to discount this objection and for the whole
JMSContext to have the same, request, scope. However I'd welcome further views on this.
/Question 2: should there be (A) a separate instance for each injection point , or (B) normal CDI sharing (according to
scope) used? /
/Question 3: even if the session and connection are shared, should the producer properties listed above still have
dependent scope? (A=yes, B=no)/
*3. Should the scope be "transaction" rather than "request"?*
One of the most important features offered by the injection of JMSContext objects is removing the need for applications
to need to close the JMSContext (and thereby the connection) after use. Connections are a limited resource and
application components should not hold on to a connection when it is not needed, such as when the application is not
being used. Since connections are pooled by the container there is no need for applications to hold on to connections
simply to avoid the expense of creating them. For this reasons, application developers are currently recommended to
create a connection (which will fetch one from the pool), use it to send a message, and then close the connection
(return it to the pool) as soon as possible.
For this reason, the current proposal states that an injected JMSContext object "will have request scope and will be
automatically closed when the request ends". Request scope was chosen because it appeared to be the most appropriate of
the CDI built-scopes (which are "request", "session", "application", "conversation", plus the psuedo-scope "dependent" ).
* "Application" or "dependent" scope would keep connections in use even when the application was not being used
* "Conversation" scope can only be used by applications which explicitly demarcate the start and end of the conversation
* "Session" scope was only briefly considered but appears to be longer than is necessary, and I was unsure whether it
has any meaning for MDBs and EJBs.
* "Request" scope seemed the most suitable, especially since the CDI spec defines it as including a MDB invocation
and an EJB invocation.
However there have been suggestions that the injected objects should have "transaction" scope rather than "request".
However CDI does not define a transaction scope.
JPA does define something vaguely similar to transaction scope: it defines that when an EntityManager is injected using
the @PersistenceContext annotation, its "persistence context" is "propagated with the current JTA transaction".
The absence of transaction scope in CDI leaves it to JMS to define what scope we require. As I see it, the main benefit
of using transaction scope is that it would allow JMSContext objects to be shared, and therefore the order of sent
messages to be preserved, in the case where the transaction spans multiple requests. This is a possibility when an
external client is using a stateful session bean, where one business method starts a user transaction and another
business method commits it. It is unclear just how important it would be to allow message order to be preserved in such
a case.
However the idea of "transaction" scope raises the question of what happens if a JMSContext is used when there is no JTA
transaction? Although EJB 3.1 is not particularly explicit about how JMS should work in such a case, it is most
definitely not an error and needs to work. One answer might be to declare if a JMSContext is used when there is no
transaction, the scope should be "request".
There is also the question of what happens if the transaction is user-demarcated (bean-managed) and a JMSContext is used
before the call to userTransaction.begin() and perhaps after it, as in the following rather contrived example:
@Inject
@JMSConnectionFactory("jms/connectionFactory")
private JMSContext context;
@Resource(mappedName = "jms/inboundQueue")
private Queue inboundQueue;
@Inject UserTransaction ut;
public void sendMessages() {
context.send(inboundQueue, "Message 1");
ut.begin();
context.send(inboundQueue, "Message 2");
ut.commit();
}
The important question here is "where are the scope boundaries?". Are the two places where a message is sent within the
same or different scopes? In other words, does the context variable refer to the same JMSContext in both places in the
same method? My view is that it would be very confusing to start a new scope in the middle of a method, and that instead
the scope should defines as "a single transaction or a request, whichever is longer".
The above code would then be identical to the following code which doesn't use injection:
@Resource(lookup=""jms/connectionFactory")
ConnectionFactory cf;
@Resource(mappedName = "jms/inboundQueue")
private Queue inboundQueue;
@Inject UserTransaction ut;
public void sendMessages() {
try (JMSContext context = connectionFactory.createContext(JMSContext.AUTO_ACKNOWLEDGE)) {
context.send(inboundQueue, "Message 1");
ut.begin();
context.send(inboundQueue, "Message 2");
ut.commit();
}
}
Note that the important thing here is translating the application code into a sequence of JMS API calls in a clear and
unambiguous manner. It is less important what the actual JMS behaviour is (this is currently undefined but may be
clarified in the future).
I therefore have suggested an alternative definition of the scope of the injected JMSContext , as follows:
"The container will inject a container-managed JMSContext whose lifetime will be managed by the container and which is
scoped to a single transaction or a request, whichever is longer. An injected JMSContext with a given set of annotations
will be propagated by the container to other injection points with the same annotations in the same transaction or
request, whichever is longer.
/Question 4: Should the scope of the injected JMSContext be (A) request or (B) extended to cover any transaction as well?/
*4. Lots of small annotations or one big annotation?*
The final issue that needs to be resolved is what are the most suitable annotations to configure the injected
JMSContext. The current proposal is to provide several annotations, each of which configures a specific aspect of the
injected object:
@JMSConnectionFactory("jms/connectionFactory") - used to specify the lookup name of the connection factory
@JMSPasswordCredential(userName="admin",password="mypassword") - used to specify the user/password passed to
createConnection
@JMSSessionMode(JMSContext.AUTO_ACKNOWLEDGE) - used to specify the session mode of the JMSContext (subject to existing
JavaEE restrictions)
@JMSAutoStart(false) - used to prevent the connection being started automatically
This was proposed because it is similar to the CDI concept of qualifiers, and was expressly recommended by Pete Muir as
being consistent with CDI style. However an alternative might be to define a single annotation with multiple parameters,
something like:
@Inject
@JMSContextConfiguration(lookup="jms/connectionFactory",
userName="admin",password="mypassword",
sessionMode=JMSContext.AUTO_ACKNOWLEDGE,
autoStart=false)
JMSContext context;
/
Question 5: Do you prefer (A) multiple small annotations, similar to qualifiers (@JMSConnectionFactory,
@JMSPasswordCredential, @JMSSessionMode and @SessionMode) or (B) a single annotation with many attributes
(@JMSContextConfiguration)/
*Those questions again*
Here are those questions again in a single list. If you have a strong view on any of them please do express an opinion.
However these are not easy questions and we may need to discuss this further before coming to a final conclusion.
/Question 1: Should the injection of JMSContext be tied to CDI or should we simply define the injection behaviour we
require? A=Yes, B=No
//Question 2: Should there be a separate instance for each injection point (answer A), or should normal CDI sharing
(according to scope) be used? (answer B)/
/Question 3: Even if the session and connection are shared, should the producer properties listed above still have
dependent scope? (A=yes, B=no)
//Question 4: Should the scope of the injected JMSContext be (A) request or (B) extended to cover any transaction as well?/
/
Question 5: Do you prefer (A) multiple small annotations, similar to qualifiers (@JMSConnectionFactory,
@JMSPasswordCredential, @JMSSessionMode and @SessionMode) or (B) a single annotation with many attributes
(@JMSContextConfiguration)/
Nigel