users@jms-spec.java.net

[jms-spec users] Re: [jsr343-experts] Re: Re: Re: (JMS_SPEC-70) Define annotations for injecting MessagingContext objects

From: Pete Muir <pmuir_at_bleepbleep.org.uk>
Date: Tue, 10 Apr 2012 18:32:46 +0100

On 10 Apr 2012, at 18:25, Nigel Deakin wrote:

> Pete,
>
> On 10/04/2012 17:55, Pete Muir wrote:
>> I understand why you feel that CDI injection is not as suitable here as a domain-specific approach. However I think it is important that this group is aware of the direction the whole platform is taking regarding injection (I'm not really sure what it is), and follows that approach. Consistency is more important here.
> Clearly, to my first question "is this CDI injection or not?" you would answer "yes".

No, I would answer "do whatever is consistent with the approach the platform is taking, going forward". I believe that this is CDI injection, but a clear statement on this would be good.

> Does this inevitably mean using @Inject to inject JMSContext objects and requiring applications to define a META-INF/beans.xml descriptor?

I'm not that bothered *how* it is implemented, what I care about it is what the user sees. So if it's CDI injection, then yes.

>
> Does the suggested need for platform-wide consistency provide an answer to my other questions on scoping?

IMO how the JMSContext is obtained is clearly part of the programming model of Java EE, but the behaviour of the JMSContext is domain-specific concern. Therefore I think that correctness wins out here, with the caveat that any deviation from the behaviour users expect may be confusing. Of course, this goes both ways, and it may be the case that the "normal" behaviour of CDI is very confusing in a JMS context.

> Perhaps it simply means that the scope JMS requires (if we adopt something like my definition of "transaction or request, whichever is longer" rather than simple request scope) should be adopted by the platform rather than be unique to JMS?

I think this is a good idea, if we can generalise it. It seems likely as the persistence context has similar requirements, as I understand them.

>
> Nigel
>
>
>
>>
>> On 5 Apr 2012, at 16:40, Nigel Deakin wrote:
>>
>>> 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
>>>
>