jsr343-experts@jms-spec.java.net

[jsr343-experts] Re: [jms-spec users] Re: JMS Support for DI

From: Reza Rahman <reza_rahman_at_lycos.com>
Date: Tue, 30 Aug 2011 20:01:35 -0400

John/Nigel,

* JMS would end up providing an extension, perhaps we would require some
special notation somewhere (e.g. in beans.xml or something) to denote
when JMS should be activated against the bean archive.
- I don't think this necessarily needs to be a CDI portable extension
per se - that's more of an implementation detail. In fact, in Resin's
case, it would be part of it's JMS 2 implementation/core Java EE
container that is not pluggable at all. For other containers, it could
be implemented completely independent of CDI. That would really be the
value of sticking to the JSR 330 annotations.

* My point here is that we don't want to reduce the features currently
available to developers who use @Resource. I'll try to find out more
about how we might do that.
- We should confirm this with the platform EG, but I don't think not
supporting @Resource.name/_at_Resource.mappedName is a big issue. I think
we are trying to deprecate that in favor of global JNDI names anyway. If
this is indeed an issue, we could specify @Resource as being nested as a
parameter of @JmsConnection, etc.

Cheers,
Reza


On 8/30/2011 7:13 PM, John D. Ament wrote:
> Nigel,
>
> Comments are below. Wanted to think about this for a while before
> responding.
>
> On Fri, Aug 19, 2011 at 7:40 AM, Nigel Deakin <nigel.deakin_at_oracle.com
> <mailto:nigel.deakin_at_oracle.com>> wrote:
>
> 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?
>
>
> Both of these require a deeper understanding of CDI. In most cases,
> the application will just provide a beans.xml in the archive to denote
> that this is a bean archive. Bean archives are scanned for AtInject
> and CDI support. Providing an extension is more framework level. JMS
> would end up providing an extension, perhaps we would require some
> special notation somewhere (e.g. in beans.xml or something) to denote
> when JMS should be activated against the bean archive.
>
>
>>
>> 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.
>
>
>
> The problem I have though is that String implements Serializable.
> Maybe we just need separate method names, that take Serializable or
> String as arguments.
>
>
>> 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.
>
>
> I don't know that this would. Wouldn't ConnectionFactories still be
> available via @Resource ? My assumption is that unless we are
> explicitly say " JMS will not do X anymore" then JMS is still doing it.
>
>
>>
>> 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.
>
>
> At least from what I've seen, anytime you're dealing with an embedded
> JMS implementation (still haven't gotten OpenMQ yet, btw!) you have a
> customer way to look up destinations.
>
>
>
>>
>> 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).
>
>
> Agreed. Should we start a separate proposal for this?
>
>
>>
>> 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.
>
>
> Actually, I was hoping that it wouldn't be provider specific. I noted
> in my response to Reza that it should end up in the connection
> factory, for session nonspecific messaging..
>
> 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
>>
>>
>>
>>
>>
>
> ------------------------------------------------------------------------
>
> No virus found in this message.
> Checked by AVG - www.avg.com <http://www.avg.com>
> Version: 10.0.1392 / Virus Database: 1520/3867 - Release Date: 08/30/11
>