jakartaee / messaging

Jakarta Messaging
https://eclipse.org/ee4j/messaging
Other
39 stars 32 forks source link

Declarative Annotation Based JMS Listeners #134

Closed glassfishrobot closed 1 year ago

glassfishrobot commented 11 years ago

This issue is closed now, with a follow-up issue here: https://github.com/jakartaee/messaging/issues/243.

MDB is currently the only declarative way of listening for JMS messages. While the MDB model is adequate, it has a few shortcomings (primarily related to it's true age). The first is that since it is rather generic to JCA, it makes the syntax less than ideal for JMS (ironically, the vast majority of MDB use is for JMS). The MDB model is highly coupled to the EJB component model, which makes generic use in managed beans needlessly difficult. Lastly and perhaps most importantly, reimaging the MDB model using CDI/annotations in a manner specific to JMS introduces the possibility of significant usability improvements/API modernization.

It is perhaps best to demonstrate these concepts using some simple code examples:

@Pooled // Documented here: https://java.net/jira/browse/EJB_SPEC-113
public class MyListenerComponent {

  @JmsListener(destinationLookup="...", clientId="...", subsciptionName="...", messageSelector="...", ...)
  @JMSConnectionFactory("...")
  @Transactional
  public void listen (Message message) {
    ...
  }
}
@ApplicationScoped
@MaxConcurrency(...) // Documented here: https://java.net/jira/browse/EJB_SPEC-113
public class MyListenerComponent {

  @JmsListener(destinationLookup="...")
  public void listen (String message) { // Message payload automatically type-checked and unwrapped
    ...
  }

}
@RequestScoped // A message received is a "request"
public class MyListenerComponent {

  @JmsListener(destinationLookup="...")
  // Message payload automatically type-checked and unwrapped, 
  // message contents automatically injected, type-checked, unwrapped.
  public void listen (byte[] message, 
      @JmsMessageProperty boolean myFlag, 
      @JmsMessageProperty("myFlag2") boolean myFlag2,
      @JmsCorrelationId String myCorrelationId,
      ... 
      ) {
    ...
  }

}
@Pooled
public class MyListenerComponent {

  @JmsListener(destinationLookup="...", batchSize="...", retry="...", retryDelay="...", ...) 
  // Batch delivery, retry, retry delay, DLQ, etc new features.
  public void listen (Message... messages) {
    ...
  }

}

As suggested in the examples above, decoupling from the EJB component model also allows for a great deal of flexibility in putting together the component life-cycle, programming model, features and re-use.

Although I am entering this in JMS as I feel it is the correct place to get this done, I assume that this body of work will require a great deal of collaboration with the EJB, CDI and platform expert groups.

Do let me know if anything needs to be explained further - I am happy to help.

Please note that these are purely my personal views and certainly not of Oracle's as a company.

glassfishrobot commented 6 years ago
glassfishrobot commented 11 years ago

@glassfishrobot Commented Reported by reza_rahman

glassfishrobot commented 11 years ago

@glassfishrobot Commented genomeprjct said: It's a great use case when you know your queues up front. We need to have more research in to use cases where destinations are created dynamically, based on tenant changes.

glassfishrobot commented 11 years ago

@glassfishrobot Commented clebertsuconic said: @John what use cases do you mean? I think this would be a great way to replace MDBs.

It would be great if we could also support receiving multiple messages (arrays) (that's probably what you meant by (Message...messages)

I'm very favorable on this!!! +1000 from me

glassfishrobot commented 11 years ago

@glassfishrobot Commented reza_rahman said: I imagine one could come up with an API adaptation that could parameterize some annotation metadata and supply them/trigger the listener POJO programmatically. In my experience though, in most enterprise cases, it's fine to assume meta data is known at deployment time.

glassfishrobot commented 11 years ago

@glassfishrobot Commented reza_rahman said: Yes, the Message...messages/batchSize="..." is intended for batch/bulk message processing.

glassfishrobot commented 11 years ago

@glassfishrobot Commented genomeprjct said: So, is the issue a case of needing to compare enterprise use cases to cloud based use cases?

I think the ability to support both a programmatic API for binding at runtime plus a series of annotations supporting deployment time options both make sense. It should be possible to do either using a simple DSL around MDB bindings. Enterprise use cases all know about how to do this at deployment time, however the cloud requires runtime manipulation to support multitenancy. Let's suppose that I need a queue per customer, there is no Java EE supported approach that allows me to do this.

1. Setup a message listener. Oh wait, Java EE won't allow us to bind message listeners. 2. Bind an MDB. Works assuming that you can redeploy your whole cloud to bring up a new tenant, including configuring deployment descriptors in between. Not a viable option for cloud providers. 3. Periodically poll the queue. This will work, but doesn't allow for immediate consumption. Easy to setup a scheduled job that does it, but becomes difficult when you have many tenants.

I've also considered that multitenancy is something that could be scripted, or prototyped. Some kind of "NewTenantInit" listener could be called that could setup everything needed to bring that tenant online. A message listener like the one you're describing could be expanded to have a @MultiTenantPrototype annotation that means nothing other than "don't deploy me unless explicitly requested" and all of your annotations could have a tenantId variable in there:

@JmsListener(destinationLookup="jsm/someQueue{tenantId}")

Of course, this would still require JMS providers to create an API that allowed for dynamic destination creation (which would be invoked, in this use case during the creation of a tenant). Maybe something along the lines of:

@Inject
private JMSServerContext serverContext;

...

Queue fooBar = serverContext.createNew(Queue.class).jndiF("jms/fooBar%s",tenantId).name("FooBar Queue for Tenant "+tenantId).storage("/path/to/tentantData").build();
serverContext.bindMDB(MyListenerComponent.class);  //assuming that MyListenerComponent is correct annotated for multitenant

For me right now, this is my biggest use case (and biggest struggle w/ JMS as a user)

glassfishrobot commented 11 years ago

@glassfishrobot Commented reza_rahman said: I've said this before elsewhere, but I guess those thoughts are worth repeating here.

In Java EE, I think we need to be very careful in standardizing anything under the cloud moniker by making sure:

1. We are not too far ahead of common use cases that are fairly obvious/pervasive. I think that's a good general principal to follow for any standard and certainly Java EE. 2. We do not unduly delay or make more complex the pervasive use cases that we already understand well to try to support the cloud (heretofore) edge cases.

That being said, it's clear to me that by and large the EGs (especially the platform EG) is exercising the correct amount of caution on this particular feature set (for example, I think delaying cloud features like the tenant ID in Java EE 7 was the right call).

Please note that these are purely my personal views and certainly not of Oracle's as a company.

glassfishrobot commented 11 years ago

@glassfishrobot Commented @nigeldeakin said: Good suggestions. I've added formatting to make the examples easier to read. Here are my thoughts on this proposal (For want of a better name I'll refer to these as "JMS listener beans" in what follows).

I think it's worth making clear that this would need to be a feature of the Java EE web and EJB containers only. Applications running a Java SE environment (or the Java EE application client container) would need to continue to use the JMS API (JMSConsumer#setMessageListener).

There are a number of significant ideas introduced here.

1. The JMS listener bean is a CDI managed bean, so its lifecycle would depend on its scope.

A scope of tt>@ApplicationScoped</tt would give a lifecycle equivalent to a MDB in that the bean(s) would be instantiated when the application was started (or deployed), and destroyed when the application was terminated (or undeployed). We'd need to ensure it was created automatically when the application was started rather than created lazily the first time it was invoked from some other bean. This might be a change to the way CDI beans work currently.

A scope of tt>@Dependent</tt would allow a shorter lifecycle which depends on the object into which it was injected. So you could inject a dependent-scoped JMS listener into a WebSocket endpoint. When a websocket client was connected, the websocket endpoint object is instantiated which would cause the JMS listener to be instantiated. When the websocket client disconnected, the websocket endpoint object is destroyed, which would cause the JMS listener to be destroyed.

Other scopes would be available as well (though we'd need to consider them carefully).

I think the key thing here is that it would allow the developer to define the lifecycle of their listener object, which is something that they cannot do with MDBs.

2. The JMS listener bean is defined to be "pooled".

This means that when the bean is instantiated you're actually instantiating a pool of instances, but just one. This pool of instances would share the load of processing listener callbacks. This is a key concept if this feature is to replace MDBs.

But if the JMS listener bean is actually a pool of beans, and it is injected into some other bean using

@Inject MyListenerBean myBean;

then what does the myBean variable actually refer to? It doesn't make sense to think of this as an object which the application can directly access.

3. Annotations on the JMS listener bean class and methods

I think the general principle of using JMS-specific method annotations to define the callback method, the queue or topic, and other consumer parameters is relatively straightforward, and is something that we could equally define for JMS MDBs as well. However we would need to clearly define the semantics of configuring two callback methods with different queues or topics, or two callback methods with the same queue or topic but with different message selectors.

Relationship to JCA

One final issue we would need to consider is the relationship to the JCA specification. The application server needs to be able to implement these JMS listener beans using a JCA resource adapter. What JCA specification changes would be needed to support JMS listener beans? With a bit of luck the existing endpointActivation/endpointDeactivation API will be sufficient to handle the creation/destruction of JMS listener bean pools. The Activation spec would become redundant with the resource adapter having to scan the annotations on the endpoint class to find out what it needs to do.

glassfishrobot commented 11 years ago

@glassfishrobot Commented reza_rahman said: Thanks indeed for correcting the formatting - my bad .

I do agree figuring out the scoping is probably the most non-trivial part of this effort. You'd obviously want to work this out with the CDI/Java EE EGs, but I think things make the most sense if you can think of the underlying JMS message delivery infrastructure as a distinct CDI bean "client" that may share the bean with other clients, whatever the scope/life-cycle (including pooled beans). Semantically, this is fairly close to the current CDI event observer model IMO.

Do let me know, I can help with some of this as we discussed most of this as part of an alternative MDB model for Resin 4 (in part inspired by Spring JMS listeners: http://static.springsource.org/spring/docs/3.2.x/spring-framework-reference/html/jms.html#jms-receiving and CDI observers: http://docs.jboss.org/weld/reference/latest/en-US/html/events.html#d0e3794).

Please note that these are purely my personal views and certainly not of Oracle's as a company.

glassfishrobot commented 11 years ago

@glassfishrobot Commented genomeprjct said: So, my assumption with all of this is that this new "MDB" still leverages the brand new spec allowances for MDBs. It allows for better alignment w/ CDI, and allows for more annotations (not just activation config). Is there any preference from anyone on the list that MDBs will not support us? This should fix any EJB spec, object scoping or JCA alignment that may need to be clarified.

@Reza you lost me a little. All I'm suggesting is that we need a dynamic way of binding MDBs to destinations that have been created. While it's a common use case for cloud, it has some usage outside of cloud technology. This would be the functional equivalent of creating an MDB against a temporary queue/topic, except the queue/topic would live longer than the temporary object (almost permanent).

glassfishrobot commented 11 years ago

@glassfishrobot Commented reza_rahman said: No worries and understood - I think your proposal could be a good one, especially if there are uses outside the cloud. My concern is a rather general one - that we use due caution to standardize any cloud features primarily because of the relative volatility/immaturity of the cloud. Now, all that might change in a few years and perhaps in enough time for EE 8, so it's definitely worth keeping an eye on/seriously considering. In particular, the tenant ID feature is one that I thought was premature/incomplete (and perhaps it still is). To be more specific, I am talking about the tenant ID discussion that took place in the Java EE 7 EG.

Please note that these are purely my personal views and certainly not of Oracle's as a company.

glassfishrobot commented 11 years ago

@glassfishrobot Commented @nigeldeakin said: @John D. Ament: The changes in JCA 1.7 (which allow the resource adapter to query the methods and annotations on the endpoint class) would allow us to define a MDB which has JMS-specific method annotations to define the callback method, the queue or topic, and other consumer parameters. We already have #116 (which you logged) to cover that, and I mentioned this as point (3) in my comment above.

However Reza's issue here is introducing some additional features, which I listed as points (1) and (2) above. These would depend on changes in the EJB spec, which Reza has logged as EJB_SPEC-113. I think that CDI changes may also be needed.

We might actually be able to develop this in two stages.

@John D. Ament: I think allowing "JMS listener beans" to have a defined scope would help with your multi-tenancy requirements. For example the use of dependent scope would allow them to have the same lifecycle as some tenant-specific application object. You also make some suggestions which would allow applications to dynamically generate Queue objects on a per-tenant basis. I think it would be worth you logging as a separate issue, since it isn't specific to async listeners but would be needed for sending messages and sync consumers as well. That said, explicit JMS support for multi-tenancy needs to take its lead from the Java EE platform spec. That's where multi-tenancy requirements should be raised initially (and obviously it's early days for Java EE 8 currently).

glassfishrobot commented 10 years ago

@glassfishrobot Commented miojo said: I'd prefer to simply go with #100 for next version of JMS, until we are certain about Cloud use cases.

glassfishrobot commented 10 years ago

@glassfishrobot Commented clebertsuconic said: I don't see a relation between #100 and this...

Cloud use cases AFAIK are more about multi-tenant support. I don't see that as a stopper on this. Or am I missing something?

glassfishrobot commented 10 years ago

@glassfishrobot Commented reza_rahman said: I do agree that it is not necessary to delay this for the clouds to clear . In fact, I think the fundamental programming model is flexible enough such that it could be easily adapted later on to make into account dynamic destination creation, multi-tenancy, etc.

With regards to https://java.net/jira/browse/JMS_SPEC-100, I think the overlap is a good indication that the current MDB model really does need to be revisited. The reason I did not build upon it is because I felt it did not really take full advantage of the opportunity to revisit the entire programming model, still has a very over-generalized JCA/EJB/MDB centric syntax and did not have sufficient technical detail (with all due respect/credit to Bruno - technical detail is not an easy thing from a non container implementer perspective).

glassfishrobot commented 10 years ago

@glassfishrobot Commented clebertsuconic said: We should seriously look into that. People are already taking this approach (look at Camel for instance).. If we keep banging the MDB route, it will become less and less relevant to our users.

We need something more programmers friendly like this IMHO.

glassfishrobot commented 11 years ago

@glassfishrobot Commented Issue-Links: is related to JMS_SPEC-100 JMS_SPEC-116

glassfishrobot commented 7 years ago

@glassfishrobot Commented This issue was imported from java.net JIRA JMS_SPEC-134

hantsy commented 1 year ago

This is a long-awaited feature, it should be included in the new version, to replace JMS from EJB @MessageDriven.

As @reza_rahman suggested, add a Spring similar @JmsLisenter(desnitation="queue name or topic name", replyTo="optional queue to reply return value to client, returning void also can send back a ack result in header ", connectionFactory="optionally to select connection factory name if there are multiple cf defined") annotation on method to receive message. (give up JNDI lookup please)

The method parameter can be a Message object( and TextMessage, BinaryMessage, etc.), or an message body annotated with @Payload body and headers(a Map<String, Object>) with @Headers(defined a collection of common header property, corelatoinid, ack_knowlegage, etc.).

The retries, capability, backPressureStrategy, errorQueue, messageConverter, etc can be defined on the JMS ConnctionFactory definition annotations or Spring similar JmsLisenterContainerFactoryBean programmatic APIs .

OndroMih commented 1 year ago

This is a very old issue and it's now superceeded by https://github.com/jakartaee/messaging/issues/243. I'm closing this issue.