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