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: Ruediger zu Dohna <ruediger.dohna_at_1und1.de>
Date: Wed, 03 Aug 2011 15:03:58 +0200

Reza,

On 08/02/2011 10:39 PM, Reza Rahman wrote:
> It's somewhat besides the point right now, but there were a number of
> questions I had about the project that were not really clear to me. Some
> examples include annotation support, mapping properties, specifying
> properties vs. message body, "serialization" schemes, message selectors
> and so on. Really I wanted to get a better feel of the project overall
> without delving through too much code.

The current state of the project is that the core concept works out very
well: Hiding the packaging/unpacking of a message behind a business
interface. If you want to configure some things, you'll have to do that
with an XML file.

Of course, I'd like to change that to annotations, but it's not really
trivial to do and I currently can't spend a lot of time on the project.
So we'll have to rely on some imagination right now.

> More to the point, I think you are agreeing that if you distill your
> current API a bit further, it really basically looks like EJB
> @Asynchronous, @Remote or @WebService? To be specific, something along
> these lines:
>
> @Stateless
> public class CustomerService {
> ...
> @JmsRemote(destination="jms/CustomerQueue")
> public void createCustomer(@JmsProperty long customerId, @JmsBody
> Customer customer) {
> ...
> }
> ...
> }
>
> @Inject @JmsRemote(priority=HIGH, deliveryMode=PERSISTENT)
> private CustomerService customerService;

I think that JMS makes most sense for remote calls. So I prefer to share
only the interface between the sender and the receiver. Adding other
minor changes, this code could look like this:

@JMS
public interface CustomerService {
    public void createCustomer(Customer customer);
}

@Stateless
public class CustomerServiceReceiver {
    @Override
    public void createCustomer(Customer customer) {
       ...
    }
}

@WebServlet("/create")
public class CustomerForm extends HttpServlet {
    @Inject @Priority(HIGH) @DeliveryMode(PERSISTENT)
    CustomerService customerService;
    ...
}

In your example, you put a dynamic parameter, the customer id, into a
property, probably to do some routing. You do that by adding it as an
annotated parameter to the method. I'd prefer to annotate the id in the
Customer class with @JmsProperty, but both alternatives may be useful.

> Obviously, this would be all asynchronous calls since it's JMS. It could
> also be possible to support a model whereby a return type is mapped back
> to the client using reply-to/correlation ID.

I've been thinking about how to transparently correlate the reply, but
if it were just a return parameter, the client would wait for the reply,
wouldn't it? This would be simulating a synchronous call, i.e. the
programming model would remain the same, which is nice, but it would
stop the client thread until the server is available, did it's work, and
returned the reply. In most scenarios that we use JMS for, this is not
an option. A lot of out service calls take days or even weeks to
finish... mostly because of extensive manual processes involved, like
connecting a DSL line. OTOH if you have quick replies, why not use EJB
calls? They too are actually asynchronous below the TCP layer ;)

If we'd hide the correlation, I'd require the clients to be stateful and
to provide a callback interface... but that's still a very raw thought.

> As such, this is a good model for certain JMS use cases. Who knows, it
> might even be a sufficient model?
>
> The trouble is that it can't be the only high level model for JMS right
> now. The fundamental issue is that because of the very high level of
> abstraction it would not be able to support the very flexible features
> that JMS provides today. This means that it might run the risk of being
> unusable to a large number of existing JMS users.
>
> Just consider some of the following cases. You'll see that they really
> just do not fit very well to a Java RPC like model. In the least case
> the API becomes quite clumsy. In the worst case, it is not possible to
> implement the feature at all.

I'll try to address all of them.

> * How do I send messages to clients not written in Java (such as
> AS400/mainframe clients on WebSphere MQ/MQSeries)

I subsumed such use cases as legacy systems ;) The current MessageApi
project already allows that: You can configure an arbitrary converter to
map the call to any message format you like... and back. This can even
be generic, e.g. we have one converter that maps your call to a map
message by converting the property camel case names to uppercase with
underscores; or you can can configure the names by hand.

Converting to/from xml is just the default.

> * How can I change the payload format at runtime?

Same thing, I guess.

> * How do I change the destination, priority, delivery mode,
> time-to-live, etc at runtime?

How about adding properly annotated parameters to your method calls?
void createCustomer(@TimeToLive long timeToLive, Customer customer);

> * How do I browse queues?

I think this is a different topic from sending or receiving messages,
requiring a dedicated API.

> * How do I handle reply-to/correlation ID schemes that might change at
> runtime?

I'm not 100% sure if hiding the correlation mechanisms is feasible. If
it's not hidden, then it works the same as with JMS 1.1, isn't it?

> * How do I send/receive a group of messages in the same session?

Maybe message-batches need a different API... I'm undecided, yet. But
it's not part of JMS 1.1 either, is it?

> * How can I change selectors at runtime?

Can you with an MDB? If you can or could, it could be added here as well.

> * How do I use temporary queues, especially with reply-to/correlation ID?

How about yet another annotation?
@Inject @ReplyTo(CustomerReplyService.class) CustomerService
customerService;

This is as well only a very raw thought, as I'm not settled about the
correlation mechanisms.

> * What if I need to do some tasks between sending a JMS message and
> waiting for the results of that message (a return type in our case)?

See above: I prefer not to hide the asynchrony.

> * And so on...

I'm glad that you brought up all of these use cases. I yet have mainly
considered the actual use cases we already have, which is not sufficient
for a standard. But maybe we can achieve that!

> Now, all this being said, I do think that we could consider this once we
> get a more flexible high level API in place or in a future version of
> JMS if this model actually does get some more traction hopefully via
> your project and it is really obvious that JMS is more flexible than it
> really needs to be.

I think that JMS 1.1 is as over-engineered as EJB was before 3.0. I
never wanted to use e.g. stream messages, and I'm not sure if they are
really used in significant cases. To me they only make sense, if the
delivery starts before the send is finished, i.e. large data streams
like movies. But I guess that if you do that, you'd need even more
control than JMS offers... namely on the wire protocol.

Maybe we should evaluate if we can strip it here or there, but the main
thing in my eyes is to make the common use case as easy as possible,
most of the not-so-common use cases easier that they are now, and look
at esoteric use cases: Maybe they are worth being supported by a special
API, maybe not.

> Hope this makes sense.

Very much so! Thanks for you help!

Rüdiger

> On 8/2/2011 7:35 AM, Ruediger zu Dohna wrote:
>> Reza,
>>
>> On 08/01/2011 08:33 PM, Reza Rahman wrote:
>>> Just to be clear, are you proposing this as an even higher level
>>> abstraction or a replacement to what I had outlined earlier?
>>
>> Could be both... I'm not fixated on either... but see below.
>>
>>> Do you have
>>> detailed documentation on this solution other than the article you sent
>>> earlier?
>>
>> There is some documentation on the MessageApi website... but not up to
>> date: http://java.net/projects/messageapi/pages/Home
>>
>> What information are you looking for?
>>
>>> As I mentioned earlier, the biggest practical issue I see with this is
>>> that it reduces flexibility a bit too much...
>>
>> I can see your point, but it's a bit too vague to put a finger on. Can
>> you give some examples? Maybe we can address it!
>>
>>> ... If we go
>>> this route, we might want to see if that model suffices instead of
>>> placing mappings on something that looks like a business logic artifact
>>> but is not really a business logic artifact at all (which is basically
>>> what John was saying I think). Other than some arguably semantic
>>> benefit, this approach requires creating a fairly mechanical interface
>>> that really has no other purpose than serving as a redundant artifact
>>> for the framework...
>>
>> I always compare these messaging interfaces to the interfaces of EJB 3
>> remote calls: EJB interfaces describe the business-semantic of a
>> synchronous remote call, but in order to make it actually work, there
>> has to be done quite some magic in the background, namely to hide the
>> remote exceptions that the underlying RMI calls require... or even the
>> complete WS-* stack! So both types of interfaces are on a very high
>> level of abstraction and without the helpful magic from the container,
>> they simply wouldn't work. Instead the container derives the technical
>> stuff it needs from those interfaces.
>>
>> I can see no redundancy in this approach... OTOH there is a very
>> fragile redundant know-how in a JMS client and server in how to
>> package or unpack messages.
>>
>>> Hope this is sensible. It's a good idea that might indeed eventually be
>>> broadly accepted (or not), I just don't think it's right for a standard
>>> API at this point. Maybe others see this differently?...
>>
>> I like the saying that you should do one thing that scares you every
>> day. But I also believe that people who are not afraid of anything
>> will eventually kill themselves. Respecting that some things simply
>> take time to get used to, I'd suggest that we continue double tracked:
>> We continue to refine the required simplifications for the existing
>> API, as you and others have suggested. But we add one track about how
>> to shift to a better level of abstraction. Everybody should give both
>> ways a close enough look (maybe even bringing it into production) to
>> get a feel for it.
>>
>> The result may be that the latter replaces the former, will sit on top
>> of it, be delayed to JMS 3.0, or even be moved out of the JMS spec. Do
>> you think that's a feasible road to follow?
>>
>>
>> Rüdiger
>>
>>> On 8/1/2011 5:01 AM, 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]
>>>>
>>>> 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
>>>>
>>>>
>>>>
>>>> -----
>>>> No virus found in this message.
>>>> Checked by AVG - www.avg.com
>>>> Version: 10.0.1390 / Virus Database: 1518/3801 - Release Date: 07/31/11
>>>>
>>>>
>>>
>>>
>>>
>>
>>
>> -----
>> No virus found in this message.
>> Checked by AVG - www.avg.com
>> Version: 10.0.1390 / Virus Database: 1518/3804 - Release Date: 08/01/11
>>
>>
>
>
>
>