jsr343-experts@jms-spec.java.net

[jsr343-experts] Re: JMS Support for DI

From: John D. Ament <john.d.ament_at_gmail.com>
Date: Thu, 18 Aug 2011 21:48:59 -0400

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>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.
- if a CDI extension is found, then it may manually add components.

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.

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.



>
> 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.


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



>
> 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.



>
> 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);
   }

}


>
> 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.
>

 I thought about this. I would recommend using a qualifier to control this
at the injection level. For example: @Inject @JmsProvider(“WebLogic”)
MessageFactory messageFactory. 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?



>
> 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.



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


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

Could you explain this a bit further?


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