Nigel,
At the onset of all this, I assumed this would be simpler than it ended up.
I also assumed the answer should be CDI from the get-go. My answers below
even surprised me.
*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*
B - At this point I believe that the injection behavior we're looking for
is closer to how JPA handles injection (within EJB), though we should aim
to allow it even in a non-EJB container if CDI is available. I would
support this even if a jms.xml descriptor were added (for some reason).
*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)*
I think the use case of needing multiple injection points is quite small.
Injecting into multple stateless session beans, or even stateful session
beans, should result in different instances. CDI scopes though behave
different because the objects are related. CDI injection should use the
same instance for same scoped objects that are all active together.
*Question 3: Even if the session and connection are shared, should the
producer properties listed above still have dependent scope? (A=yes, B=no)*
The properties should be set against the instance, not a proxy. Not sure
what this has to do with dependent scope.
*Question 4: Should the scope of the injected JMSContext be (A) request or
(B) extended to cover any transaction as well?*
Within an EE application it should match up to a JTA transaction.
*Question 5: Do you prefer (A) multiple small annotations, similar to
qualifiers (*_at_JMSConnectionFactory*, *_at_JMSPasswordCredential*, *
@JMSSessionMode* and *_at_SessionMode*) or (B) a single annotation with many
attributes (*_at_JMSContextConfiguration*)*
Is there a reason both cannot be supported simultaneously?
John
On Thu, Apr 5, 2012 at 11:40 AM, Nigel Deakin <nigel.deakin_at_oracle.com>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.xmldescriptor.
>
> 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, @JMSSessionModeand
> @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, @JMSSessionModeand
> @SessionMode) or (B) a single annotation with many attributes (
> @JMSContextConfiguration)*
>
> Nigel
>
>