
com.tangosol.net.events.internal.AbstractEventDispatcher Maven / Gradle / Ivy
/*
* Copyright (c) 2000, 2022, Oracle and/or its affiliates.
*
* Licensed under the Universal Permissive License v 1.0 as shown at
* http://oss.oracle.com/licenses/upl.
*/
package com.tangosol.net.events.internal;
import com.oracle.coherence.common.base.Continuation;
import com.oracle.coherence.common.base.Logger;
import com.oracle.coherence.common.base.Predicate;
import com.tangosol.net.events.Event;
import com.tangosol.net.events.EventDispatcher;
import com.tangosol.net.events.EventInterceptor;
import com.tangosol.net.events.NamedEventInterceptor;
import com.tangosol.net.events.annotation.Interceptor.Order;
import com.tangosol.util.Base;
import com.tangosol.util.SubSet;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.Date;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map.Entry;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import java.util.concurrent.CopyOnWriteArrayList;
/**
* Abstract base implementation of an {@link EventDispatcher}.
*
* @author rhan, mwj, nsa, rhl 2011.03.29
* @since Coherence 12.1.2
*/
@SuppressWarnings("unchecked")
public class AbstractEventDispatcher
implements EventDispatcher
{
// ------ constructors --------------------------------------------------
/**
* Default constructor.
*/
public AbstractEventDispatcher()
{
this(new HashSet());
}
/**
* Standard constructor.
*
* @param setTypes the event types supported by this dispatcher
*/
public AbstractEventDispatcher(Set setTypes)
{
f_mapInterceptors = new ConcurrentHashMap>>();
// clone the given set of types adding events supported by the base dispatcher
List listTypes = Arrays.asList(InterceptorRegistrationEvent.Type.values());
Set setAllTypes = new HashSet(setTypes.size() + listTypes.size());
setAllTypes.addAll(setTypes );
setAllTypes.addAll(listTypes);
f_setTypes = Collections.unmodifiableSet(setAllTypes);
}
// ----- EventDispatcher methods ----------------------------------------
/**
* {@inheritDoc}
*/
public > void addEventInterceptor(EventInterceptor incptr)
{
addEventInterceptor(null, incptr);
}
/**
* {@inheritDoc}
*/
public , E extends Event> void addEventInterceptor(String sIdentifier,
EventInterceptor interceptor, Set setTypes, boolean fFirst)
{
String sCacheName = null;
String sServiceName = null;
if (interceptor instanceof NamedEventInterceptor)
{
// given a NamedEventInterceptor we retain the data held by the
// NamedEventInterceptor but not provided by this method's signature
NamedEventInterceptor incptrNamed = (NamedEventInterceptor) interceptor;
interceptor = incptrNamed.getInterceptor();
sCacheName = incptrNamed.getCacheName();
sServiceName = incptrNamed.getServiceName();
}
NamedEventInterceptor incptrNamed = new NamedEventInterceptor(sIdentifier, interceptor,
sCacheName, sServiceName, fFirst ? Order.HIGH : Order.LOW, null, (Set) setTypes);
addEventInterceptor(incptrNamed);
}
/**
* {@inheritDoc}
*/
public > void addEventInterceptor(String sIdentifier, EventInterceptor incptr)
{
NamedEventInterceptor incptrNamed = incptr instanceof NamedEventInterceptor
? (NamedEventInterceptor) incptr
: new NamedEventInterceptor(sIdentifier, incptr);
if (incptrNamed.isAcceptable(this))
{
Set setEventTypes = incptrNamed.getEventTypes();
final Set setTypes;
if (setEventTypes == null)
{
// if the EventInterceptor wants all events we do not include
// the InterceptorRegistrationEvents; the InterceptorRegistrationEvent
// must be explicitly requested
setTypes = f_setTypes;
}
else
{
setTypes = new SubSet(getSupportedTypes());
setTypes.retainAll(setEventTypes);
}
if (!setTypes.isEmpty())
{
final DispatcherInterceptorEvent event = instantiateEvent(
InterceptorRegistrationEvent.Type.INSERTING, incptrNamed, setTypes);
// dispatch an event informing listeners of the new interceptor
// both INSERTING and INSERTED; the pre-event provides an
// opportunity for an EventInterceptor to change the EventInterceptor
// being registered
dispatchEvent(event, new Continuation()
{
public void proceed(Object o)
{
if (o instanceof Throwable)
{
Logger.fine("An EventInterceptor veto'd the registration of " + event.getInterceptor()
+ " for the event types " + event.getEventTypes());
throw Base.ensureRuntimeException((Throwable) o);
}
ConcurrentMap>> mapInterceptors = getInterceptorMap();
NamedEventInterceptor incptrNamed = event.getNamedEventInterceptor();
for (Enum eventType : setTypes)
{
if (!mapInterceptors.containsKey(eventType))
{
mapInterceptors.putIfAbsent(eventType, new CopyOnWriteArrayList>());
}
List> listIncptr = mapInterceptors.get(eventType);
if (incptrNamed.isFirst())
{
listIncptr.add(0, incptrNamed);
}
else
{
listIncptr.add(incptrNamed);
}
}
dispatchEvent(instantiateEvent(InterceptorRegistrationEvent.Type.INSERTED,
incptrNamed, setTypes), null);
}
});
}
}
}
/**
* {@inheritDoc}
*/
public > void removeEventInterceptor(final String sName)
{
removeEventInterceptor(new Predicate>()
{
public boolean evaluate(NamedEventInterceptor incptr)
{
return incptr != null && incptr.getRegisteredName().equals(sName);
}
});
}
/**
* {@inheritDoc}
*/
public > void removeEventInterceptor(final EventInterceptor interceptor)
{
removeEventInterceptor(new Predicate>()
{
public boolean evaluate(NamedEventInterceptor incptr)
{
return incptr != null && (incptr.getInterceptor() == interceptor || incptr == interceptor);
}
});
}
/**
* {@inheritDoc}
*/
public ConcurrentMap>> getInterceptorMap()
{
return f_mapInterceptors;
}
/**
* {@inheritDoc}
*/
public Set getSupportedTypes()
{
return f_setTypes;
}
/**
* Return statistics for this event dispatcher.
*
* @return statistics for this event dispatcher
*/
public EventStats getStats()
{
return m_stats;
}
// ----- AbstractEventDispatcher methods --------------------------------
/**
* Return true iff an interceptor is subscribed to the specified event type.
*
* @param eventType the event type to check against
*
* @return true iff an interceptor is subscribed to the event type
*/
public boolean isSubscribed(Enum eventType)
{
return getInterceptorMap().containsKey(eventType);
}
// ----- internal methods -----------------------------------------------
/**
* Return a {@link Continuation} whose completion will dispatch the specified
* {@link com.tangosol.net.events.Event} and invoke the specified continuation.
*
* @param event the event to be dispatched
* @param continuation the continuation to complete after dispatching
*
* @return a continuation whose completion will dispatch the specified event
*/
protected Continuation getDispatchContinuation(
final AbstractEvent event, final Continuation continuation)
{
return new Continuation()
{
public void proceed(Object oResult)
{
try
{
List> list = getInterceptorMap().get(event.getType());
if (list != null)
{
// dispatch the event
event.dispatch(list);
}
}
catch (RuntimeException e)
{
// this can only mean that event is Vetoable (see AbstractEvent#nextInterceptor)
if (continuation == null)
{
// preserve the original exception as the "cause"
throw e;
}
else
{
// the continuation is responsible for completing the control-flow
// (including exception handling)
oResult = e;
}
}
finally
{
if (continuation != null)
{
continuation.proceed(oResult);
}
}
}
};
}
/**
* Remove an {@link EventInterceptor} from this dispatcher. The {@link Predicate}
* provided will be given a {@link NamedEventInterceptor} and if {@link Predicate#evaluate(Object)
* evaluate} returns true the interceptor is removed from this dispatcher.
*
* @param predicate a Predicate when given a NamedEventInterceptor can
* determine whether the interceptor should be removed
*/
protected > void removeEventInterceptor(Predicate> predicate)
{
Set setEventTypes = new HashSet();
NamedEventInterceptor incptrNamed = null;
for (final Iterator iter = getInterceptorMap().entrySet().iterator(); iter.hasNext(); )
{
Entry entry = (Entry) iter.next();
final List> listInterceptors = (List>) entry.getValue();
// find the appropriate interceptor based on the predicate
for (final NamedEventInterceptor interceptor : listInterceptors)
{
if (predicate.evaluate(interceptor))
{
incptrNamed = interceptor;
setEventTypes.add((Enum) entry.getKey());
listInterceptors.remove(interceptor);
if (listInterceptors.isEmpty())
{
iter.remove();
}
break;
}
}
}
if (incptrNamed != null && !setEventTypes.isEmpty())
{
// dispatch an event informing listeners of the removed interceptor
dispatchEvent(instantiateEvent(InterceptorRegistrationEvent.Type.REMOVED,
incptrNamed, setEventTypes), /*continuation*/ null);
}
}
/**
* Dispatch the provided event.
*
* @param event the event to dispatch
* @param cont the {@link Continuation} to call after the event has been
* delivered
*/
protected void dispatchEvent(AbstractEvent extends Enum> event, Continuation> cont)
{
getDispatchContinuation(event, cont).proceed(null);
}
/**
* Create an {@link InterceptorRegistrationEvent} implementation to notify
* {@link EventInterceptor}s of an impending or enacted un/registration.
*
* @param eventType the type of registration; INSERTING, INSERTED or REMOVED
* @param incptr the {@link NamedEventInterceptor} being registered
* @param setEventTypes the event types being registered against
*
* @param the event type the interceptor is converned with
*
* @return an {@link InterceptorRegistrationEvent} implementation
*/
protected > DispatcherInterceptorEvent instantiateEvent(
InterceptorRegistrationEvent.Type eventType, NamedEventInterceptor incptr,
Set setEventTypes)
{
return new DispatcherInterceptorEvent(this, eventType, incptr, setEventTypes);
}
// ----- inner class: DispatcherInterceptorEvent ------------------------
/**
* An {@link InterceptorRegistrationEvent} implementation allowing interception
* of {@link EventInterceptor} un/registrations.
*
* @param the {@link Event} the interceptor being un/registered will intercept
*
* @since Coherence 12.1.2
*/
public static class DispatcherInterceptorEvent>
extends AbstractEvent
implements InterceptorRegistrationEvent
{
// ----- constructors -----------------------------------------------
/**
* Construct a DispatcherInterceptorEvent using the specified type.
*
* @param dispatcher the event dispatcher that raised this event
* @param eventType the type of {@link Event} raised
* @param incptr the {@link NamedEventInterceptor} being un/registered
* @param setEventTypes the event types the interceptor is being registered
* against
*/
public DispatcherInterceptorEvent(EventDispatcher dispatcher, InterceptorRegistrationEvent.Type eventType,
NamedEventInterceptor incptr, Set setEventTypes)
{
super(dispatcher, eventType);
assert incptr != null;
m_incptr = incptr;
f_setEventTypes = setEventTypes;
}
// ----- AbstractEvent methods --------------------------------------
/**
* {@inheritDoc}
*/
@Override
protected boolean isMutableEvent()
{
return getType() == Type.INSERTING;
}
// ----- DispatcherInterceptorEvent methods -------------------------
/**
* {@inheritDoc}
*/
public String getIdentifier()
{
return m_incptr.getRegisteredName();
}
/**
* {@inheritDoc}
*/
public Set getEventTypes()
{
return f_setEventTypes;
}
/**
* {@inheritDoc}
*/
public EventInterceptor getInterceptor()
{
return m_incptr.getInterceptor();
}
/**
* {@inheritDoc}
*/
public void setInterceptor(EventInterceptor incptr)
{
if (isMutableEvent())
{
m_incptr = new NamedEventInterceptor(incptr, m_incptr);
}
else
{
throw new IllegalStateException("Modifying the interceptor is not permitted for "
+ getType() + " events");
}
}
// ----- helpers ----------------------------------------------------
/**
* Return the {@link NamedEventInterceptor} this event was created with.
*
* @return the NamedEventInterceptor this event was created with
*/
protected NamedEventInterceptor getNamedEventInterceptor()
{
return m_incptr;
}
// ----- data members -----------------------------------------------
/**
* Set of event types the {@link EventInterceptor} is being registered
* against pertinent to this {@link EventDispatcher}.
*/
protected final Set f_setEventTypes;
/**
* The {@link NamedEventInterceptor}, which wraps the user's {@link
* EventInterceptor}, being registered with this {@link EventDispatcher}.
*/
protected NamedEventInterceptor m_incptr;
}
// ----- inner class: EventStats ----------------------------------------
/**
* EventStats is used to track statistics related to {@link Event} dispatching.
*
* @author pp 2011.10.12
*/
public class EventStats
{
// ----- constructors -----------------------------------------------
/**
* EventStats constructor.
*/
public EventStats()
{
}
// ----- public methods ---------------------------------------------
/**
* Register the raising of an exception by an {@link EventInterceptor}.
*
* @param e the exception raised
* @param event the event that was dispatched
* @param interceptor the interceptor that raised the exception
*/
public void registerEventException(Exception e, Event event, EventInterceptor interceptor)
{
m_cExceptions++;
m_sStackTrace = new Date() + "\n" + Base.printStackTrace(e);
}
/**
* Reset the event statistics.
*/
public void reset()
{
m_cExceptions = 0;
m_sStackTrace = null;
}
/**
* Build and return an array of strings to display the event statistics.
*
* @return an array of strings to display event statistics
*/
public String[] toStringArray()
{
List listStats = new ArrayList();
Set setInterceptorNames = new HashSet();
for (List> listInterceptor : getInterceptorMap().values())
{
for (NamedEventInterceptor interceptor : listInterceptor)
{
setInterceptorNames.add(interceptor.getRegisteredName());
}
}
listStats.add("Interceptors: " + setInterceptorNames);
listStats.add("ExceptionCount: " + m_cExceptions);
String sStackTrace = m_sStackTrace;
listStats.add("LastException: " + (sStackTrace == null ? "" : sStackTrace));
return listStats.toArray(new String[listStats.size()]);
}
// ----- object methods ---------------------------------------------
/**
* {@inheritDoc}
*/
public String toString()
{
StringBuilder builder = new StringBuilder();
String[] asDisplay = toStringArray();
for (String s : asDisplay)
{
builder.append(s).append('\n');
}
return builder.toString();
}
// ----- data members ---------------------------------------------------
/**
* Number of exceptions thrown since the last statistics reset.
*/
protected volatile int m_cExceptions;
/**
* The stacktrace of the last exception thrown.
*/
protected volatile String m_sStackTrace;
}
// ----- data members ---------------------------------------------------
/**
* The set of {@link Event} types that this {@link EventDispatcher} may raise.
*/
protected final Set f_setTypes;
/**
* A map of registered {@link EventInterceptor}s keyed by event type.
*/
protected final ConcurrentMap>> f_mapInterceptors;
/**
* Statistics for this event dispatcher.
*/
protected final EventStats m_stats = new EventStats();
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy