jsr343-experts@jms-spec.java.net

[jsr343-experts] Re: JMS Support for DI

From: Nigel Deakin <nigel.deakin_at_oracle.com>
Date: Thu, 18 Aug 2011 15:31:28 +0100

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.

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?

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.

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.

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;

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?

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.

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?

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

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.

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.

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'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,

y. Move the factory methods to ConnectionFactory

z. Require MessageFactory objects to be looked up from JNDI

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"?

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.





Nigel