org.infinispan.notifications.Listener Maven / Gradle / Ivy
package org.infinispan.notifications;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* Class-level annotation used to annotate an object as being a valid cache listener. Used with the {@link
* org.infinispan.Cache#addListener(Object)} and related APIs. Note that even if a class is annotated with this
* annotation, it still needs method-level annotation (such as {@link org.infinispan.notifications.cachemanagerlistener.annotation.CacheStarted})
* to actually receive notifications. Objects annotated with this annotation - listeners - can be attached to a
* running {@link org.infinispan.Cache} so users can be notified of {@link org.infinispan.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, {@link org.infinispan.notifications.cachelistener.event.TransactionalEvent#getGlobalTransaction()}
* can be used, along with {@link org.infinispan.notifications.cachelistener.event.TransactionCompletedEvent#isTransactionSuccessful()}
* 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:
* The sync parameter on this annotation 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 {@link org.infinispan.config.GlobalConfiguration#setAsyncListenerExecutorProperties(java.util.Properties)}
* and {@link org.infinispan.config.GlobalConfiguration#setAsyncListenerExecutorFactoryClass(String)}.
*
* Summary of Notification Annotations Annotation Event Description {@link
* org.infinispan.notifications.cachemanagerlistener.annotation.CacheStarted} {@link
* org.infinispan.notifications.cachemanagerlistener.event.CacheStartedEvent} A cache was
* started {@link org.infinispan.notifications.cachemanagerlistener.annotation.CacheStopped}
* {@link org.infinispan.notifications.cachemanagerlistener.event.CacheStoppedEvent} A cache was stopped {@link org.infinispan.notifications.cachelistener.annotation.CacheEntryModified}
* {@link org.infinispan.notifications.cachelistener.event.CacheEntryModifiedEvent} A cache entry was modified {@link
* org.infinispan.notifications.cachelistener.annotation.CacheEntryCreated} {@link
* org.infinispan.notifications.cachelistener.event.CacheEntryCreatedEvent} A cache entry was
* created {@link org.infinispan.notifications.cachelistener.annotation.CacheEntryRemoved}
* {@link org.infinispan.notifications.cachelistener.event.CacheEntryRemovedEvent} A
* cache entry was removed {@link org.infinispan.notifications.cachelistener.annotation.CacheEntryVisited}
* {@link org.infinispan.notifications.cachelistener.event.CacheEntryVisitedEvent} A
* cache entry was visited {@link org.infinispan.notifications.cachelistener.annotation.CacheEntryLoaded}
* {@link org.infinispan.notifications.cachelistener.event.CacheEntryLoadedEvent} A
* cache entry was loaded {@link org.infinispan.notifications.cachelistener.annotation.CacheEntriesEvicted}
* {@link org.infinispan.notifications.cachelistener.event.CacheEntriesEvictedEvent} A
* cache entries were evicted {@link org.infinispan.notifications.cachelistener.annotation.CacheEntryActivated}
* {@link org.infinispan.notifications.cachelistener.event.CacheEntryActivatedEvent} A cache entry was activated {@link
* org.infinispan.notifications.cachelistener.annotation.CacheEntryPassivated} {@link
* org.infinispan.notifications.cachelistener.event.CacheEntryPassivatedEvent} One or more cache entries were
* passivated {@link org.infinispan.notifications.cachemanagerlistener.annotation.ViewChanged}
* {@link org.infinispan.notifications.cachemanagerlistener.event.ViewChangedEvent} A view change event was detected {@link
* org.infinispan.notifications.cachelistener.annotation.TransactionRegistered} {@link
* org.infinispan.notifications.cachelistener.event.TransactionRegisteredEvent} The cache has started
* to participate in a transaction {@link org.infinispan.notifications.cachelistener.annotation.TransactionCompleted}
* {@link org.infinispan.notifications.cachelistener.event.TransactionCompletedEvent} The cache has completed its participation in a transaction {@link
* org.infinispan.notifications.cachelistener.annotation.CacheEntryInvalidated} {@link
* org.infinispan.notifications.cachelistener.event.CacheEntryInvalidatedEvent} 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
*
* @Listener
* public class SingleEventListener
* {
* @CacheStarted
* public void doSomething(Event event)
* {
* System.out.println("Cache started. Details = " + event);
* }
* }
*
*
* Example 2 - Method receiving multiple events
*
* @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
*
* @Listener
* 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.
*
*
* @Listener
* public class EventHandler
* {
* private ConcurrentMap<GlobalTransaction, Queue<Event>> map = new ConcurrentHashMap<GlobalTransaction, Queue<Event>>();
*
* @TransactionRegistered
* public void startTransaction(TransactionRegisteredEvent event)
* {
* map.put(event.getGlobalTransaction(), new ConcurrentLinkedQueue<Event>());
* }
*
* @CacheEntryCreated
* @CacheEntryModified
* @CacheEntryRemoved
* public void addEvent(TransactionalEvent event)
* {
* map.get(event.getGlobalTransaction()).add(event);
* }
*
* @TransactionCompleted
* public 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())
* {
* for(Event e : events)
* {
* System.out.println("Event " + e);
* }
* }
* }
* }
*
*
* @author Manik Surtani
* @author Jason T. Greene
* @see org.infinispan.notifications.cachemanagerlistener.annotation.CacheStarted
* @see org.infinispan.notifications.cachemanagerlistener.annotation.CacheStopped
* @see org.infinispan.notifications.cachelistener.annotation.CacheEntryModified
* @see org.infinispan.notifications.cachelistener.annotation.CacheEntryCreated
* @see org.infinispan.notifications.cachelistener.annotation.CacheEntryRemoved
* @see org.infinispan.notifications.cachelistener.annotation.CacheEntryVisited
* @see org.infinispan.notifications.cachelistener.annotation.CacheEntryLoaded
* @see org.infinispan.notifications.cachelistener.annotation.CacheEntriesEvicted
* @see org.infinispan.notifications.cachelistener.annotation.CacheEntryActivated
* @see org.infinispan.notifications.cachelistener.annotation.CacheEntryPassivated
* @see org.infinispan.notifications.cachemanagerlistener.annotation.ViewChanged
* @see org.infinispan.notifications.cachelistener.annotation.TransactionCompleted
* @see org.infinispan.notifications.cachelistener.annotation.TransactionRegistered
* @see org.infinispan.notifications.cachelistener.annotation.CacheEntryInvalidated
* @see org.infinispan.notifications.cachelistener.annotation.DataRehashed
* @see org.infinispan.notifications.cachelistener.annotation.TopologyChanged
* @since 4.0
*/
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
public @interface Listener {
/**
* 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.
*
* @return 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 4.0
*/
boolean sync() default true;
/**
* 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 {@link org.infinispan.notifications.Listener#clustered()} is true.
* @return 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
*/
boolean primaryOnly() default false;
/**
* Defines whether the annotated listener is clustered or not.
* Important: Clustered listener can only be notified for @CacheEntryRemoved, @CacheEntryCreated,
* @CacheEntryModified and @CacheEntryExpired events.
* @return 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
*/
boolean clustered() default false;
/**
* 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
* {@link org.infinispan.notifications.Listener#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.
* @return 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
**/
boolean includeCurrentState() default false;
Observation observation() default Observation.BOTH;
enum Observation {
PRE() {
@Override
public boolean shouldInvoke(boolean pre) {
return pre;
}
},
POST() {
@Override
public boolean shouldInvoke(boolean pre) {
return !pre;
}
},
BOTH() {
@Override
public boolean shouldInvoke(boolean pre) {
return true;
}
};
public abstract boolean shouldInvoke(boolean pre);
}
}