8.2. Listener Notifications

Each cache event triggers a notification that is dispatched to listeners. A listener is a simple POJO annotated with @Listener. A Listenable is an interface that denotes that the implementation can have listeners attached to it. Each listener is registered using methods defined in the Listenable.
A listener can be attached to both the cache and Cache Manager to allow them to receive cache-level or cache manager-level notifications.

8.2.1. About Cache-level Notifications

In Red Hat JBoss Data Grid, cache-level events occur on a per-cache basis. Examples of cache-level events include the addition, removal and modification of entries, which trigger notifications to listeners registered on the relevant cache.

8.2.2. Cache Manager-level Notifications

Examples of events that occur in Red Hat JBoss Data Grid at the cache manager-level are:
  • The starting and stopping of caches
  • Nodes joining or leaving a cluster;
Cache manager-level events are located globally and used cluster-wide, but are restricted to events within caches created by a single cache manager.
The first two types of events, CacheStarted and CacheStopped are highly similar, and the following example demonstrates printing out the name of the cache that has started or stopped:
@CacheStarted
public void cacheStarted(CacheStartedEvent event){
    // Print the name of the Cache that started
    log.info("Cache Started: " + event.getCacheName());
}
    
@CacheStopped
public void cacheStopped(CacheStoppedEvent event){
    // Print the name of the Cache that stopped
    log.info("Cache Stopped: " + event.getCacheName());
}
When receiving a ViewChangedEvent or MergeEvent note that the list of old and new members is from the node that generated the event. For instance, consider the following scenario:
  • A JDG Cluster currently consists of nodes A, B, and C.
  • Node D joins the cluster.
  • Nodes A, B, and C will receive a ViewChangedEvent with [A,B,C] as the list of old members, and [A,B,C,D] as the list of new members.
  • Node D will receive a ViewChangedEvent with [D] as the list of old members, and [A,B,C,D] as the list of new members.
Therefore, a set intersection may be used to determine if a node has recently joined or left a cluster. By using getOldMembers() in conjunction with getNewMembers(), we may determine the set of nodes that have joined or left the cluster, as seen below:
@ViewChanged
public void viewChanged(ViewChangedEvent event){
    HashSet<Address> oldMembers = new HashSet(event.getOldMembers());
    HashSet<Address> newMembers = new HashSet(event.getNewMembers());
    HashSet<Address> oldCopy = (HashSet<Address>)oldMembers.clone();
        
    // Remove all new nodes from the old view.
    // The resulting set indicates nodes that have left the cluster.
    oldCopy.removeAll(newMembers);
    if(oldCopy.size() > 0){
        for (Address oldAdd : oldCopy){
            log.info("Node left:" + oldAdd.toString());
        }
    }
        
    // Remove all old nodes from the new view.
    // The resulting set indicates nodes that have joined the cluster.
    newMembers.removeAll(oldMembers);
    if(newMembers.size() > 0){
        for(Address newAdd : newMembers){
            log.info("Node joined: " + newAdd.toString());
        }
    }
}
Similar logic may be used during a MergeEvent to determine the new set of members in the cluster.

8.2.3. About Synchronous and Asynchronous Notifications

By default, notifications in Red Hat JBoss Data Grid are dispatched in the same thread that generates the event. Therefore the listener must be written in a way that does not block or prevent the thread's progression.
Alternatively, the listener can be annotated as asynchronous, which dispatches notifications in a separate thread and prevents blocking the operations of the original thread.
Annotate listeners using the following:
@Listener (sync = false)
public class MyAsyncListener { .... }
Use the <asyncListenerExecutor/> element in the configuration file to tune the thread pool that is used to dispatch asynchronous notifications.

Important

When using a synchronous, non-clustered listener that handles the CacheEntryExpiredEvent ensure that this listener does not block execution, as the expiration reaper is also synchronous in a non-clustered environment.