jsr343-experts@jms-spec.java.net

[jsr343-experts] Re: JMS Support for DI

From: Nigel Deakin <nigel.deakin_at_oracle.com>
Date: Fri, 19 Aug 2011 12:40:54 +0100

John,

I'm responding to the specific issues where I have a ready response. (Others may require more thought).

On 19/08/2011 02:48, John D. Ament wrote:
> Nigel,
>
> Thanks for the comments. My responses are in line.
>
> - John
>
> On Thu, Aug 18, 2011 at 10:31 AM, Nigel Deakin <nigel.deakin_at_oracle.com <mailto:nigel.deakin_at_oracle.com>> wrote:
>
> John,
>
> I have an assortment of questions and comments here, some minor, others more major.
>
> But before I go through those I'd like to say that I think this document is exactly the kind of thing I had in
> mind when suggesting a user-friendly annotation-based layer on top of the JMS API.
>
> I think that your separating out "event messaging" into a separate proposal has helped a lot. The API you're
> proposing is still very recognisably JMS and doesn't divert us into discussing new programming models such as events.
>
> However I still think there a lot of questions that need to be asked. I'm sure you'll have answers ready for many
> of them!
>
> 1. AtInject
> -----------
>
> Let's start with a minor point. You call this "JMS Support for AtInject" . We probably want to put that the other
> way round!
>
> Also, is "AtInject" an official name for JSR 330? The Java EE 6 spec calls it "Dependency Injection for Java
> specification (JSR-330)"
>
> I'm a little bit vague about where DI (JSR 330) ends and CDI (JSR 299) starts, so it might be helpful if you gave
> us an overview of the differences as you see them.
>
> Your first assumption is "1. CDI 1.1 will support a bootstrap API for SE environments.". Does this mean that
> you're assuming a JSR 299 environment, not a purely JSR 330 one? Would there be any benefit in requiring "just" DI
> support rather than CDI support?
>
> With "just" DI, do we still have automatic creation of injected resources when they go into scope, and automatic
> closing of these resources when they go out of scope? What scopes are available, and what do they mean?
>
> (I appreciate some of these are standard questions. Perhaps as we develop the specification we should also work on
> a tutorial which would allow users to make use of these new features without needing to open the DI or CDI specs
> or manuals.
>
>
> JSR-330 is typically considered “@Inject” or just “AtInject,” where as the original “WebBeans” spec is simply
> JSR-299 or CDI. Obviously CDI works better long term since it's JSR-346 in EE7. Referring to it as under AtInject
> simply states that the JSR-330 annotations are enough to define the DI behavior, making the API compatible with
> any JSR-330 implementation, and supporting frameworks like Spring, Guice natively. As far as the terminology, we
> are providing JMS capabilities to a CDI/AtInject module, so I'm not sure if I'm missing your point ( I figure I
> am, since I don't see what's wrong with the name, reading it the other way it makes me think that CDI components
> can be injected into the JMS components, but that will likely not be the case here )
>
> Based on what I put in, I believe the only requirement is AtInject, but obviously it would be up to the
> implementor to provide managing of the objects. Object management would only apply to CDI implementations. Nice
> call out about scoping. My current assumption is that everything is dependent scoped, where the life cycle of the
> injected component is tied to the injection point. The choices are Dependent, RequesetScoped (bound to HTTP
> request), SessionScoped (bound to HTTP session), ApplicationScoped (bound to the lifecycle of the application), or
> Singleton (one per application; but “application” needs to be clarified in the CDI spec for these two).
>
>
>
> 2. Support in Java EE
> ---------------------
>
> The Java EE 6 specification, section EE.5.20 "Support for Dependency Injection (JSR-330)" defines the extent of
> support for JSR 330 required in a Java EE container. Are there any limitations there which would affect your proposal?
>
> In Java EE, would the application need to confirm to any conventions (like having a beans.xml) to allow it to use
> this API?
>
>
> Injection is supported into bean archives only, as defined by the CDI spec
> - if beans.xml is found then the archive is scanned.

So we would expect, say, a servler or session bean to supply a beans.xml file.
> - if a CDI extension is found, then it may manually add components.

Is this invisible to the application?

>
> I need to look a bit further to see if library scanning only applies to beans.xml or if anything with a CDI extension
> is also scanned. I believe this is one of the open questions currently, since it seems to be managed differently for
> each app server.
>
>
> 3 Implicit conversion of payload to message types:
> --------------------------------------------------
>
> You suggested that support the automatgic conversion of certain common classes and types to JMS message types:
>
> byte[], Byte[] => BytesMessage
> InputStream => StreamMessage
> CharSequence, char[], Character[] => TextMessage
> Map, and any implementation of => MapMessage
> Otherwise, an ObjectMessage is used.
>
> I think we should definitely discuss this, but since most of this document isn't actually dependent on implicit
> conversion we can afford to consider it as being slightly separate.
>
> "Implicit conversion" would require either some new factory methods to create messages, or new methods on the
> existing interfaces.
>
> I think allowing applications to avoid having to create an ObjectMessage to send an Object (Serializable,
> actually), or a TextMessage to send a String, is a nice idea, but I'm less sure about BytesMessage, MapMessage and
> StreamMessage.
>
> Note that an ObjectMessage sends a Serializable, not an Object.
>
> A TextMessage has a String as its payload, not a CharSequence (which is an interface implemented by String). Were
> you thinking of calling CharSequence.toString() and using that as the payload of a TextMessage, or were you
> thinking of a new JMS message type? If the latter then I think it is misleading to accept a CharSequence only to
> convert it to a String, and it would be clearer to expect any payload to be provided as a String.
>
> A BytesMessage has numberous methods to allow Ints, Floats etc to be written directly to the message (and vice
> versa). This allows vendors to optimise the implementation, especially when using very large messages and message
> compression. If we expected clients to assemble a separate byte[] and then copy it to a BytesMessage we would be
> forcing a potentially large object to be copied, and prevent vendor optimisations (like only decompressing a
> message when the client actually tried to read it).
>
> The same point applies to StreamMessage and MapMessage.
>
>
> My thinking was that there would be a new method (one only) to MessageProducer that took an Object (java.util.Map is
> not Serializable, which is why I say Object not Serializable). This object would be compared to CharSequence,
> Serializable, etc. For CharSequence, yes, I was thinking about using the toString representation to convert, since it
> is required in the CharSequence interface.
>

Aren't we abandoning type safety by having a single method that took an Object?

I think it's important for the sender to be able to be certain about what message type will be used, since the receiver
will typically be written on the assumption that messages for a particular purpose will be of a particular type.

> I agree about the notes about the other message types. My point is that for very simple cases it should be possible to
> send basic types with implicit conversion to the mapped message types. I agree that this isn't dependent on DI, but
> was something I wanted to raise. If it makes more sense to discuss separately, we can move that discussion.
>
>

Perhaps we could get 90% of the cases by providing createMessage(Serializable s), which returns an ObjectMessage, and
createMessage(String s) which returned a TextMessage

My remark about this being "slightly separate" is that changes to the underlying API need to be be considered more
widely, since they would benefit/affect users who aren't using injection. I'm not trying to stop us discussing it here
for now.

>
> 4. Injecting a connection
> -------------------------
>
> You suggest
>
> @Inject @JmsConnection(factory="foo")
> Connection connection;
>
> The attribute "factory" here is inconsistent with the use of the attribute "value" for @JmsDestination, both of
> which are intended to be the JNDI name of an administered object.
>
> However in Java EE applications can already use
>
> // JNDI name of resource
> @Resource (name="foo")
> Connection connection;
>
> // Name of resource
> @Resource (lookup="foo")
> Connection connection;
>
> // A product specific name that this resource should be mapped to.
> @Resource (mappedName="foo")
> Connection connection;
>
> The differences between these three ways to define a resource are defined in the Java EE spec.
>
> We would need to make sure that @Inject @JmsConnection could make use of all these different ways to access
> resources. I think having a single "factory" attribute may be insufficient to reflect the richness of the ways
> that resources can be obtained in Java EE.
>
>
> So part of the issue is that @Resource is not a qualifier, if it were the issue is moot mostly. I use factory to
> indicate the ConnectionFactory, and the attribute can be renamed to clarify. I can't find anything indicating that you
> can inject a javax.jms.Connection using @Resource.
>

My point here is that we don't want to reduce the features currently available to developers who use @Resource. I'll try
to fnd out more about how we might do that.

>
> 5. Injecting a session (1)
> -------------------------
>
> @Inject @JmsConnection(factory="foo", acknowledgeMode=ClientAcknowledge)
> Session session;
>
> How would you create a (local) transacted session? (in the Java SE case)
>
> In http://java.net/jira/browse/JMS_SPEC-45 I propose a new method which combines the transacted and
> acknowledgeMode parameters in a single sessionMode parameter:
>
> Session createSession(int sessionMode) throws JMSException
>
> So for consistency, the annotation should be
>
> @Inject @JmsConnection(factory="foo", sessionMode=ClientAcknowledge)
> Session session;
>
> @Inject @JmsConnection(factory="foo", sessionMode=Transacted)
> Session session;
>
>
> I do want to get alignment between my proposed annotation and your proposed change to support transacted and
> non-transacted clearer. Could your spec change perhaps use an enum to describe the sessionMode as well?
>

Good idea.
>
>
> 6. Injecting a session (2)
> --------------------------
>
> You don't say anything about the relationship between connection and session objects.
>
> Would each session have its own connection, to satisfy Java EE requirements? Would there be a switch somewhere
> which told the implementation that Java EE restrictions were in force?
>
> In Java SE, where multiple sessions could use the same connection, when you injected a new session, how would the
> runtime decide whether to re-use an existing connection (if there were one with the same connection factory, user
> and password) rather than create a new one?
>
>
> Assuming dependent scope, each session would result in a new connection, regardless of SE or EE.
>
>
>
> 7. Injecting a destination
> --------------------------
>
> You propose
>
> @JmsDestination(value="foo")
>
> I note that this is a qualifier for use when injecting MessageConsumer and MessageProducer objects. You wouldn't
> inject a Destination directly. I like that.
>
> The attribute "value" here is inconsistent with the use of the attribute "factory" for @JmsConnection, both of
> which are intended to be the JNDI name of an administered object.
>
> However in a Java EE environment the same issues regarding names applies for this as for connection factories. We
> would need to make sure that users can supply the equivalent of name, mappedName or lookup.
>
>
> I think value should be overloaded, to try resolution by mappedName, name or lookup depending on what is found first.
> However, underneath the destination would typically be resolved via InitialContext.lookup rather than injecting a
> resource, so I'm not sure we can support this fully.
>
>

This is the same point as I raised for @JmsConnection.

I'm not sure about your idea of merging name,lookup and mappedName in a single attirbute. If this is a desirable feature
it is something that should be defined at Java EE level rather than us offering something different for JMS only.

>
> 8. Using a MessageConsumer/MessageProducer
> ------------------------------------------
>
> How would local transactions work? In particular, how would a application create, say, a consumer and a producer
> on the same session, receive a message, send a message, and then commit the session?
>
>
> This really depends on a decision around scope. If we choose to keep it as dependent, then this couldn't happen,
> unless the object injected is a session, and the consumer/producer are created in a post construct method, e.g.:
>
> @RequestScoped
> @Named("messageSender")
> public class MessageSender {
>
> @Inject @JmsConnect(factory="/ConnectionFactory", transacted = true)
> Session session;
>
> private MessageConsumer msgConsumer;
> private MessageProducer msgProducer;
>
> @Resource(mappedName="jms/FooDestination") Destination d;
>
> @PostConstruct
> public void init() {
> msgProducer = session.createProducer(d);
> msgConsumer = session.createConsumer(d);
> }
>
> }

OK. This is also what users would need to do when using local transactions (even with a single producer/consumer), since
the commit method is on the session. Perhaps we should add a new commit method on the producer/consumer as a
convenience? (Just like we have an acknowledge method on Message as a convenience, even though it operates on the whole
session).

>
> 9. Injecting a Message
> ----------------------
>

This idea of a MessageFactory is a good idea, one I had been contemplating for the main API and which we should consider
in this wider context.

>
> You suggest a new MessageFactory class which can be used to instantiate provider-specific message impementations:
>
> //Instantiate the default MessageFactory implementation.
> MessageFactory messageFactory = new MessageFactory();
> TextMessage textMessage = messageFactory.createMessage(TextMessage.class);
>
> You suggest that the "JMS Implementation that is in use" would define the actual MessageFactory implementation
> class in the resource directory META-INF/services.
>
> I support the idea of separating from the Session object the factory methods to create messages, but I'm not sure
> whether the ServiceLoader mechanism is going to work, for a number of reasons:
>
> a. The ServiceLoader mechanism doesn't seem to allow multiple MessageFactory implementations to be used within the
> same application. This would cause problems from applications that needed to use two different JMS providers at
> the same time.
>
>
> I thought about this. I would recommend using a qualifier to control this at the injection level. For example: @Inject
> @JmsProvider(“WebLogic”) MessageFactory messageFactory.
>

I don't think we would want that as it would defeat the JMS goal of keeping vendor-specific configuration out of the code.
>
> But even then I'm not sure it'll be required. Would we perhaps expect the opposite, that MessageFactory would take the
> underlying implementation's interfaces as arguments intead?
>
>
There isn't currently any concept of "underlying implementation" - especially in Java SE. As you mentioned yourself, an
application might be using two JMS providers.

>
> b. The idea of using a classpath resource to define what JMS provider is being used is inconsistent with the
> existing convention of using a JNDI resource.
>
>
> I probably misstated something. Classpath would be used to state what Messages were available for use, based on how
> ServiceLoaders work.
>
>
The ServiceLoader mechanism is being used to define a provider-specific MessageFactory. This means that users would be
using the classpath to define what provider they were using to create messages. Whereas currently users define what
provider they are using to create connections by using JNDI. I was observing that this was inconsistent.

>
> c. In a Java EE application it would seem to prevent two separate JMS providers being made available in the same
> global classpath, for use by different applications.
>
>
> I'm not sure why this is implied. Could you explain further?

If both applications (running in the same application server) had the same classpath then they would be forced to use
the same MessageFactory.

>
> I've been trying to think of an alternative, none of which seem very attractive: the best I can think up is
>
> x. Simply clarify in the spec that messages created using the existing API are not associated with the Session
> used to create them,
>

This is the really just the current situation. The application would have to find a Session (any Session from the
required provider) and use it to create Messages.

I was wondering whether we could invent a different MessageFactory class which held a reference to a Session, and used
that Session to create objects. Applications could then inject MessageFactorys in the same way as they can inject
MessageProducers. But with dependent scope, I think this would mean that we'd create a new connection and a new session
just so we could create messages, which is clearly unacceptable.

>
> y. Move the factory methods to ConnectionFactory
>

I mean a new createMessage() method on javax.jms.ConnectionFactory. Since the ConnectionFactory is provider-specific it
would know how to instantiate messages.

Again we could invent a different MessageFactory class which held a reference to a ConnectionFactory, and inject that.
This would mean a JNDI lookup whenever we wanted to create inject a MessageFactory, but that isn't so bad.

>
> z. Require MessageFactory objects to be looked up from JNDI
>
>
> Could you explain this a bit further?

Given that your propoed MessageFactory is a provider-specific object, we should ask administrators to instantiate one,
using provider-specific tools, and bind it to JNDI for the application to lookup. This would be entirely analogous with
the way in which connection factories and destinations are created, bound, and looked up now.

Nigel

>
> 10. Miscellaneous
> -----------------
>
> A. In the example on page 7:
>
> @Inject
> @JmsConnection(factory=”/JmsXA”,transacted=true)
> @JmsDestination(“jms/SomeStaticDestination”)
> MessageProducer staticProducer;
>
> Can you clarify why you think of these as "static"?
>
>
> I'll restate the example as:
>
> @Inject
> @JmsConnection(factory=”/JmsXA”,transacted=true)
> @JmsDestination(“jms/SomeFooDestination”)
> MessageProducer fooProducer;
>
>
> B. In the example on pages 7-8:
>
> @ApplicationScoped
> public class HandleMessages {
>
> @Inject
> @JmsConnection(factory=”/JmsXA”,transacted=true)
> @JmsDestination(“jms/SomeStaticDestination”)
> MessageConsumer genericConsumer;
>
> //handles a generic application driven event to look for messages and process them.
> //MyApplicationEvent is an event raised by the application and defined by the application itself.
> public void lookForMessages(@Observes MyApplicationEvent evt) {
> TextMessage received = null;
> while((received = genericConsumer.receive(3000l,TextMessage.class)) != null) {
> String text = received.getText();
> doWork(text);
> }
> }
>
> Is there any particular reason why you used events here? I think it might be better to choose an example which
> doesn't use CDI events since it might give the midleading impression that we're defining an event API here.
>
>
> I thought about that after I sent it out, and will probably change the example to use an EJB timer. I was trying to
> setup something really simple to use as an example.
>
>
>
>
>
>
> Nigel
>
>
>
>
>