Annotation Type Listener


@Retention(RUNTIME) @Target(TYPE) public @interface Listener
Class-level annotation used to annotate an object as being a valid cache listener. Used with the Listenable.addListener(Object) and related APIs.

Note that even if a class is annotated with this annotation, it still needs method-level annotation (such as CacheStarted) to actually receive notifications.

Objects annotated with this annotation - listeners - can be attached to a running Cache so users can be notified of Cache events.

There can be multiple methods that are annotated to receive the same event, and a method may receive multiple events by using a super type.

Delivery Semantics

An event is delivered immediately after the respective operation, sometimes before as well, but must complete before the underlying cache call returns. For this reason it is important to keep listener processing logic short-lived. If a long running task needs to be performed, it's recommended to invoke this in a non blocking way or to use an async listener.

Transactional Semantics

Since the event is delivered during the actual cache call, the transactional outcome is not yet known. For this reason, events are always delivered, even if the changes they represent are discarded by their containing transaction. For applications that must only process events that represent changes in a completed transaction, TransactionalEvent.getGlobalTransaction() can be used, along with TransactionCompletedEvent.isTransactionSuccessful() to record events and later process them once the transaction has been successfully committed. Example 4 demonstrates this.

Listener Modes

A listener can be configured to run in two different modes: sync or async.

The first, non-blocking, is a mode where the listener is notified in the invoking thread. Operations in this mode should be used when either the listener operation is expected to complete extremely fast or when the operation can be performed in a non-blocking manner by returning a CompletionStage to delay the operation until the stage is complete. This mode is the default mode, overrided by the sync() property. A method is non blocking if it declares that it returns a CompletionStage or one of its subtypes. Note that the stage may return a value, but it will be ignored. The user must be very careful that no blocking or long running operation is done while in a sync listener as it can cause thread starvation. You should instead use your own thread pool to execute the blocking or long running operation and return a CompletionStage signifying when it is complete.

The second, async, is pretty much identical to sync except that the original operation can continue and complete while the listener is notified in a different thread. Listeners that throw exceptions are always logged and are not propagated to the user. This mode is enabled when the listener has specified sync as false and the return value is always ignored.

Locking semantics

The sync mode will guarantee that listeners are notified for mutations on the same key sequentially, since the lock for the key will be held when notifying the listener. Async however can have events notified in any order so they should not be used when this ordering is required. If however the notification thread pool size is limited to one, this will provide ordering for async events, but the throughput of async events may be reduced.

Because the key lock is held for the entire execution of sync listeners (until the completion stage is done), sync listeners should be as short as possible. Acquiring additional locks is not recommended, as it could lead to deadlocks.

Threading Semantics

A listener implementation must be capable of handling concurrent invocations. Local sync notifications reuse the calling thread; remote sync notifications reuse the network thread. If a listener is async, it will be invoked in the notification thread pool.

Notification Pool

Async events are made in a separate notification thread, which will not cause any blocking on the caller or network thread. The separate thread for async listeners is taken from a pool, which can be configured using GlobalConfiguration.listenerThreadPool(). The default values can be found in the KnownComponentNames class.

Clustered Listeners

Listeners by default are classified as a local listener. That is that they only receive events that are generated on the node to which they were registered. They also receive pre and post notification events. A clustered listener, configured with clustered=true, receives a subset of events but from any node that generated the given event, not just the one they were registered on. The events that a clustered listener can receive are: CacheEntryCreatedEvent, CacheEntryModifiedEvent, CacheEntryRemovedEvent and CacheEntryExpiredEvent. For performance reasons, a clustered listener only receives post events.

Summary of Notification Annotations

\ <td valign@="top">TransactionRegisteredEvent <td valign=@"top">TransactionCompletedEvent <td valign=@"top">CacheEntryInvalidatedEvent

Annotation Event Description
CacheStarted CacheStartedEvent A cache was started
CacheStopped CacheStoppedEvent A cache was stopped
CacheEntryModified CacheEntryModifiedEvent A cache entry was modified
CacheEntryCreated CacheEntryCreatedEvent A cache entry was created
CacheEntryRemoved CacheEntryRemovedEvent A cache entry was removed
CacheEntryExpired CacheEntryExpiredEvent A cache entry was expired
CacheEntryVisited CacheEntryVisitedEvent A cache entry was visited
CacheEntryLoaded CacheEntryLoadedEvent A cache entry was loaded
CacheEntriesEvicted CacheEntriesEvictedEvent A cache entries were evicted
CacheEntryActivated CacheEntryActivatedEvent A cache entry was activated
CacheEntryPassivated CacheEntryPassivatedEvent One or more cache entries were passivated
ViewChanged ViewChangedEvent A view change event was detected
TransactionRegistered The cache has started to participate in a transaction
TransactionCompleted The cache has completed its participation in a transaction
CacheEntryInvalidated A cache entry was invalidated by a remote cache. Only if cache mode is INVALIDATION_SYNC or INVALIDATION_ASYNC.

Example 1 - Method receiving a single event, sync

    @Listener
    public class SingleEventListener
    {
       @CacheStarted
       public CompletionStage<Void> doSomething(Event event)
       {
          System.out.println("Cache started.  Details = " + event);
          return null;
       }
    }
 

Example 2 - Method receiving multiple events - sync

    @Listener
    public class MultipleEventListener
    {
       @CacheStarted
       @CacheStopped
       public void doSomething(Event event)
       {
          if (event.getType() == Event.Type.CACHE_STARTED)
             System.out.println("Cache started.  Details = " + event);
          else if (event.getType() == Event.Type.CACHE_STOPPED)
             System.out.println("Cache stopped.  Details = " + event);
       }
    }
 

Example 3 - Multiple methods receiving the same event - async

    @Listener(sync=false)
    public class SingleEventListener
    {
       @CacheStarted
       public void handleStart(Event event)
       {
          System.out.println("Cache started");
       }
 

@CacheStarted @CacheStopped @CacheBlocked @CacheUnblocked @ViewChanged public void logEvent(Event event) { logSystem.logEvent(event.getType()); } }

Example 4 - Processing only events with a committed transaction - sync/non-blocking

    @Listener
    public class EventHandler
    {
       private ConcurrentMap<GlobalTransaction, Queue<Event>> map = new ConcurrentHashMap<GlobalTransaction, Queue<Event>>();

       @TransactionRegistered
       public CompletionStage<Void> startTransaction(TransactionRegisteredEvent event)
       {
          map.put(event.getGlobalTransaction(), new ConcurrentLinkedQueue<Event>());
          return null;
       }

       @CacheEntryCreated
       @CacheEntryModified
       @CacheEntryRemoved
       public CompletionStage<Void> addEvent(TransactionalEvent event)
       {
          map.get(event.getGlobalTransaction()).add(event);'
          return null;
       }

       @TransactionCompleted
       public CompletionStage<Void> endTransaction(TransactionCompletedEvent event)
       {
          Queue<Event> events = map.get(event.getGlobalTransaction());
          map.remove(event.getGlobalTransaction());

          System.out.println("Ended transaction " + event.getGlobalTransaction().getId());

          if(event.isTransactionSuccessful())
          {
             // Lets say we want to remotely log the events for the transaction - if this has an async or non blocking
             // API you can use that and not block the thread and wait until it returns to complete the Stage.
             CompletionStage<Void> stage = performRemoteEventUpdateNonBlocking(events);
             return stage;
          } else {
             return null;
          }
       }
    }
 
Since:
4.0
Author:
Manik Surtani, Jason T. Greene, William Burns
See Also:
  • Nested Class Summary

    Nested Classes
    Modifier and Type
    Class
    Description
    static enum 
    Enumeration that defines when a listener event can be observed.
  • Optional Element Summary

    Optional Elements
    Modifier and Type
    Optional Element
    Description
    boolean
    Defines whether the annotated listener is clustered or not.
    boolean
    If set to true then the entire existing state within the cluster is evaluated.
    Returns the type of observation level this listener defines.
    boolean
    Specifies whether the event should be fired on the primary data owner of the affected key, or all nodes that see the update.
    boolean
    Specifies whether callbacks on any class annotated with this annotation happens synchronously or asynchronously.
  • Element Details

    • sync

      boolean sync
      Specifies whether callbacks on any class annotated with this annotation happens synchronously or asynchronously. Please see the appropriate section on the Listener class for more details. Defaults to true.
      Returns:
      true if the expectation is that the operation waits until the callbacks complete before continuing; false if the operation can continue immediately.
      Since:
      4.0
      Default:
      true
    • primaryOnly

      boolean primaryOnly
      Specifies whether the event should be fired on the primary data owner of the affected key, or all nodes that see the update.

      Note that is value is ignored when clustered() is true.

      Returns:
      true if the expectation is that only the primary data owner will fire the event, false if all nodes that see the update fire the event.
      Since:
      5.3
      Default:
      false
    • clustered

      boolean clustered
      Defines whether the annotated listener is clustered or not. Important: Clustered listener can only be notified for CacheEntryRemoved, CacheEntryCreated, CacheEntryRemoved and CacheEntryExpired events.
      Returns:
      true if the expectation is that this listener is to be a cluster listener, as in it will receive all notifications for data modifications
      Since:
      7.0
      Default:
      false
    • includeCurrentState

      boolean includeCurrentState
      If set to true then the entire existing state within the cluster is evaluated. For existing matches of the value, an @CacheEntryCreated event is triggered against the listener during registration. This is only supported if the listener is also clustered().

      If using a distributed clustered cache it is possible to retrieve new events before the initial transfer is completed. This is handled since only new events are queued until the segment it belongs to is completed for iteration. This also will help reduce memory strain since a distributed clustered listener will need to eventually retrieve all values from the cache.

      Returns:
      true if the expectation is that when the listener is installed that all of the current data is sent as new events to the listener before receiving new events
      Since:
      7.0
      Default:
      false
    • observation

      Returns the type of observation level this listener defines.
      Returns:
      the observation level of this listener
      Since:
      7.2
      See Also:
      Default:
      BOTH