Distributed events for Web Beans

Posted by    |       CDI

The Web Beans event bus provides a very nice way for stateful components to synchronize their state with changes that take place in the application.

Suppose we have a Web Bean that updates the Product catalog. The following method is responsible for creating new Products.

public class ProductManager {

   @PersistenceContext EntityManager entityManager;
   @Fires @Created Event<Product> productCreated;

   public void create(Product prod) {
      entityManager.persist(prod);
      productCreated.fire(prod);
   }

   ...

}

When a Product is created, the Web Bean fires an event of type Product, with binding type @Created.

Suppose a second Web Bean caches the Product catalog in memory, to minimize database access:

@ApplicationScoped
public class Catalog {
   List<Product> products;
   
   ...
   
   @Produces List<Product> getProducts() {
      if (products==null) {
         products = getProductsFromDatabase();
      }
      return products;
   }
   
   void productCreated(@Observes @AfterTransactionCompletion @Created Product prod) {
      products.add(prod);
   }
   
}

When a new Product is successfully created in the database, the Catalog receives an event notification and adds the new Product to its cache.

Event processing is much more important in an environment with stateful objects like Catalog than it is in an environment with only stateless objects like stateful session beans. Therefore, I believe that event processing is a truly essential feature of Web Beans.

However, the eventing functionality defined by the Web Beans Public Draft is a purely local construct and does not help when the application is distributed across multiple cluster nodes or multiple physical tiers. We've identified this problem as one that must be solved for the revised Public Draft.

Designing a distributed event bus would potentially be a very difficult undertaking, with so many issues to think about: QoS, asynchronicity, transactionality, routing, etc. Fortunately, JMS already has the features we need, and is what is sometimes used today to solve the problems we're talking about. JMS features:

  • asynchronous, distributed semantics
  • a notion of both 1-of-N and N-of-N receivers (queues and topics)
  • a notion of logical channels (to support routing between tiers and applications)
  • support for a range of QoS semantics, including
  • transactionality

Furthermore, JMS is well-understood and well-supported in the EE environment.

So I've proposed that the specification simply define that events may be distributed via JMS.

The application developer would not be exposed to the JMS APIs. The actual interaction with JMS would be handled by the container. All the developer would need to do is specify that events with a specific type and binding types would be distributed by a certain queue or topic.

Of course, absolutely nothing would stop a vendor from also supporting an alternative distribution mechanism as a proprietary extensions (for example, in JBoss we could provide support for JGroups).

What I'm thinking is the following:

Asynchronous event observers

Event observers may specify that they receive events asynchronously:

void productCreated(@Observes @Asynchronously @Created Product product) { ... }

By default, an asynchronous observer is a purely local construct. However, unlike synchronous observers, an asynchronous observer is called in a different set of web beans contexts to the contexts in which the event was fired.

Mapping an event type to a JMS channel

A JMS topic or queue declaration may specify a set of events which are distributed via that queue/topic:

<Topic>
   <destination>java:comp/env/jms/CacheInvalidationEvents</destination>
   <connectionFactory>java:comp/env/jms/TopicConnectionFactory</connectionFactory>
   <events>
      <myapp:Product>
         <myapp:Created/>
      </myapp:Product>
   </events>
</Topic>

This declaration means that any event that is assignable to the specified type and binding types is a distributed event, distributed by the named JMS topic.

Since JMS is a transactional medium, we could support a further setting which writes the event transactionally to the queue/topic. However, this setting needs to be a global setting for the event type. Perhaps a @Transactional annotation for the event type?

Observers of distributed events

The container would validate that all observers for any event distributed via JMS are declared to be asynchronous observers, and throw an exception if there are any synchronous observers for the event.

We need to think carefully about the concept of an asynchronous transaction completion event observer. The following combinations are semantically correct, and need to be supported, even for the distributed case:

@Observes @Asynchronously @AfterTransactionCompletion
@Observes @Asynchronously @AfterTransactionSuccess
@Observes @Asynchronously @AfterTransactionFailue

However, this combination is not meaningful:

@Observes @Asynchronously @BeforeTransactionCompletion

Of course, this is all a work in progress, and I'm interested in hearing feedback on this proposal :-)


Back to top