org.jboss.cache.notifications.annotation
Annotation Type CacheListener


@Retention(value=RUNTIME)
@Target(value=TYPE)
public @interface CacheListener

Class-level annotation used to annotate an object as being a valid cache listener. Used with the Cache.addCacheListener(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, but 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 use another thread.

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.getTransaction() can be used, along with TransactionCompletedEvent.isSuccessful() to record events and later process them once the transaction has been successfully committed. Example 4 demonstrates this.

Threading Semantics

A listener implementation must be capable of handling concurrent invocations. Local notifications reuse the calling thread; remote notifications reuse the network thread.

Since notifications reuse the calling or network thread, it is important to realise that if your listener implementation blocks or performs a long-running task, the original caller which triggered the cache event may block until the listener callback completes. It is therefore a good idea to use the listener to be notified of an event but to perform any long running tasks in a separate thread so as not to block the original caller.

In addition, any locks acquired for the operation being performed will still be held for the callback. This needs to be kept in mind as locks may be held longer than necessary or intended to and may cause deadlocking in certain situations. See above paragraph on long-running tasks that should be run in a separate thread.

Note: Since 3.0, a new parameter, sync, has been introduced on this annotation. This defaults to true which provides the above semantics. Alternatively, if you set sync to false, then invocations are made in a separate thread, which will not cause any blocking on the caller or network thread. The separate thread is taken from a pool, which can be configured using Configuration.setListenerAsyncPoolSize(int).

Summary of Notification Annotations

Annotation Event Description
CacheStarted CacheStartedEvent A cache was started
CacheStopped CacheStoppedEvent A cache was stopped
NodeModified NodeModifiedEvent A node was modified
NodeMoved NodeMovedEvent A node was moved
NodeCreated NodeCreatedEvent A node was created
NodeRemoved NodeRemovedEvent A node was removed
NodeVisited NodeVisitedEvent A node was visited
NodeLoaded NodeLoadedEvent A node was loaded
NodeEvicted NodeEvictedEvent A node was evicted
NodeActivated NodeActivatedEvent A node was activated
NodePassivated NodePassivatedEvent A node was passivated
ViewChanged ViewChangedEvent A view change event was detected
CacheBlocked CacheBlockedEvent A cache block event was detected
CacheUnblocked CacheUnblockedEvent A cache unblock event was detected
TransactionRegistered TransactionRegisteredEvent The cache has started to participate in a transaction
TransactionCompleted TransactionCompletedEvent The cache has completed its participation in a transaction
BuddyGroupChanged BuddyGroupChangedEvent Buddy replication is enabled and one of the buddy groups that the instance is a member of has changed its membership.
NodeInvalidated NodeInvalidatedEvent A node was invalidated by a remote cache. Only if cache mode is INVALIDATION_SYNC or INVALIDATION_ASYNC.

Example 1 - Method receiving a single event

    @CacheListener
    public class SingleEventListener
    {
       @CacheStarted
       public void doSomething(Event event)
       {
          System.out.println("Cache started.  Details = " + event);
       }
    }
 

Example 2 - Method receiving multiple events

    @CacheListener
    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

    @CAcheListener
    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.

    @CacheListener
    public class TxGuaranteedListener
    {
       private class TxEventQueue
       {
          private ConcurrentMap<Transaction, Queue<Event>> map = new ConcurrentHashMap<Transaction, Queue<Event>>();
 

public void offer(Event event) { Queue<Event> queue = getQueue(event.getContext().getTransaction()); queue.offer(event); }

private Queue<Event> getQueue(Transaction transaction) { Queue<Event> queue = map.get(transaction); if (queue == null) { queue = new ConcurrentLinkedQueue<Event>(); map.putIfAbsent(transaction, queue); }

return queue; }

public Queue<Event> takeAll(Transaction transaction) { return map.remove(transaction); } }

private TxEventQueue events = new TxEventQueue();

@NodeModified @NodeMoved @NodeCreated @NodeRemoved public void handle(Event event) { events.offer(event); }

@TransactionCompleted public void handleTx(TransactionCompletedEvent event) { Queue<Event> completed = events.takeAll(event.getTransaction()); if (completed != null && event.isSuccessful()) System.out.println("Comitted events = " + completed); } }

Since:
2.0.0
Author:
Manik Surtani, Jason T. Greene
See Also:
CacheStarted, CacheStopped, NodeModified, NodeMoved, NodeCreated, NodeRemoved, NodeVisited, NodeLoaded, NodeEvicted, NodeActivated, NodePassivated, ViewChanged, CacheBlocked, CacheUnblocked, TransactionCompleted, TransactionRegistered, BuddyGroupChanged, NodeInvalidated, Cache.addCacheListener(Object), Cache.removeCacheListener(Object), Cache.getCacheListeners()

Optional Element Summary
 boolean sync
          Specifies whether callbacks on any class annotated with this annotation happens synchronously (in the caller's thread) or asynchronously (using a separate thread).
 

sync

public abstract boolean sync
Specifies whether callbacks on any class annotated with this annotation happens synchronously (in the caller's thread) or asynchronously (using a separate thread). Defaults to true.

Returns:
true if the expectation is that callbacks are called using the caller's thread; false if they are to be made in a separate thread.
Since:
3.0
Default:
true


Copyright © 2009 JBoss, a division of Red Hat. All Rights Reserved.