jsr343-experts@jms-spec.java.net

[jsr343-experts] Re: [jms-spec users] Re: (JMS_SPEC-33) Improving the JMS API with API simplifications, annotations and CDI

From: Nigel Deakin <nigel.deakin_at_oracle.com>
Date: Mon, 01 Aug 2011 15:00:29 +0100

Ruediger,

On 01/08/2011 10:01, Ruediger zu Dohna wrote:
> [I'm on the expert group by now, but not yet on the EG mailing list. I wanted to get this out now, so I send it on the
> users list... I hope everybody interested is on this list as well, esp. Reza and John]

I now given Ruediger permissions to join the EG list. That was my oversight.

Below...

>
> Hi,
>
> I agree that we should use CDI as much as possible to make JMS more usable. I also agree with Reza that it would be
> great, if this would be a CDI layer on top of the existing JMS APIs that would allow the user to get anything they need
> injected or even simply produce the message objects they want to send.
>
> *But I think we could do much more than that!*
>
> I'll need to do a bit of a round-trip to make my point clear... starting with the example from Nigel:
>
> @Resource(name="myQueue") Destination destination;
> @Resource(name="myConnectionFactory") ConnectionFactory connectionFactory;
>
> private void send(String messageText) throws JMSException {
> Connection connection = connectionFactory.createConnection();
> try {
> Session session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE);
> MessageProducer producer = session.createProducer(destination);
> Message message = session.createTextMessage(messageText);
> producer.send(message);
> } finally {
> if (connection!=null) connection.close();
> }
> }
>
> This code hides JMS from the business code that calls this method. But if we want to replace it completely by something
> the container provides, it has to be generic, which it is not for functional and non-functional reasons. I'll
> concentrate on the functional aspects first: The destination defines the semantic of the message and should be named
> accordingly. E.g. it could be a destination for twitter messages named TwitterDestination or a destination to delete
> customers by their id named DeleteCustomerDestination. So the method name should also not be "send" but e.g. "tweet" or
> "deleteCustomer".
>
> Adding more methods, you'd either have to add more destinations, or group functionality into one destination and put the
> desired operation into the message... either as a message property or into the message payload. I mostly prefer the
> latter, but you can change that without touching the clients of the existing method. Also the exact message format could
> change without the business code being affected... nice design.
>
> If the class gets too big and/or to make testing the business code easier, I'd refactor these JMS-methods and the
> resources they envy into a delegate... probably even extracting an interface.
>
> public interface CustomerService {
> public void createCustomer(String first, String last);
> public void deleteCustomer(String id);
> }
>
> *This* is what the business code programmer wants to see... nothing more. Now all the boiler plate code is in one place,
> and... magic fairy dust sparkle sound... we can get completely rid of it! Assuming that the exact message format, the
> destination name, and some other things are not really interesting as long as the sender and the receiver agree, this
> code can be implemented in a generic fashion! Convention-over-configuration.

I think you do raise an interesting and profound question about what JMS is for.

As I think I've remarked before, the approach you advocate seems to be to hide the actual message-sending and
message-receiving behind a layer which makes it appear to the application (business layer) as an async invocation of a
remote (or local) business method. In addition to "hiding" the message, would your approach also "hide" the destination?

I have no doubt that many applications do indeed view their JMS usage as being a way of achieving the async execution of
some local or remote method. However I suspect that there are many other applications where the message itself (or
rather its payload) maps on to real business entities such as orders, trades and the like, and the concept of a message
flowing through a system is a useful analog to an order flowing through an organisation, and there is no benefit in
hiding it behind a RPC API.

Having said that, what you describe is clearly a major use case for JMS, and we could consider the standardising of a
RPC-style abstraction on top of JMS if EG members thought it would be of benefit (it could even be a different spec).
Though I think our first priority should be to provide an improved API for applications which are perfectly happy to see
JMS as a way of passing messages (or, perhaps, message payloads) around. Obviously I'd like to hear other views on this.

Going back to the idea of JMS as a way of perfoming a remote method call, I see an overlap between this and the existing
EJB 3.1 feature of asynchronous method invocation, at least for the point-to-point case. How does this compare with what
you have in mind? After all, async requests to a session bean would need to be queued in the server. I suppose one
difference is that JMS offers a defined quality of service (e.g. queued requests may be persisted to ensure that they
are not be lost in the event of server failure) whereas async EJB does not.

Nigel

>
> If we need to explicitly configure these things, e.g. in order to communicate with some legacy system, or in order to
> serve some non-functional requirements, like a QoS, priority, etc., we could use use CDI qualifiers:
>
> @Inject
> @DestinationName("myQueue") // required by legacy system
> @Priority(HIGH) // non-functional requirement
> CustomerService sender;
>
> This would run in Java EE as well as in Java SE with only a CDI container. For clients without CDI, there would have to
> be a factory method... and the non-functional aspects would have to be configured with a builder.
>
> There are use cases that require more control on the message format... stream messages come to my mind. But I consider
> them as border cases that require additional API.
>
> I think it would be a great leap forward to hide *all* of this by default... and when you need to specify something, add
> an annotation and let the container figure out how to obey. If the reference implementation only delegates to the JMS
> 1.1 API, all JMS implementations would be supported right away, while a more direct binding to an implementation might
> provide additional benefits (as Reza pointed out).
>
> Nigel sees the asynchronous receiver side as a separate issue, as the current specs force us to use MDBs.
> The goal IMHO
> would be that the business code would simply implement e.g. the CustomerService interface and not care about the message
> format, etc. While it could be possible to generate the required MDB classes on the fly, it would be easier if we could
> use the MessageListener interface, as we can bind those generically to any destination. But how can we have a single
> transaction for the consumption of the message and the business code that is called? I'm optimistic that this can be
> resolved, but I agree this would require a deeper integration into the container... a completely separate layer on top
> of JMS, desirable as it is, may not suffice.
>
> Is this not a good thing to aim at? But maybe I completely miss something. Are there any real objections, other than we
> might have to completely rewrite the spec? I believe that hiding JMS behind a pure business interface is a logical
> result from viewing asynchronous messaging radically from the business programmer perspective, but I may have lost sight
> of some other, elemental features. Please tell me, if this is so! Or should I provide more detail?
>
>
> Regards
> Rüdiger
>