All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.tangosol.util.MapListenerSupport 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.util;


import com.tangosol.net.cache.CacheEvent;

import com.tangosol.util.filter.InKeySetFilter;

import java.util.ArrayList;
import java.util.EventListener;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;


/**
* This class provides support for advanced MapListener functionality.
*
* @author gg  2003.09.16
* @since Coherence 2.3
*/
public class MapListenerSupport
        extends Base
    {
    /**
    * Constructs a new MapListenerSupport object.
    */
    public MapListenerSupport()
        {
        }

    /**
    * Add a map listener that receives events based on a filter evaluation.
    *
    * @param listener  the listener to add
    * @param filter    a filter that will be passed MapEvent objects to
    *                  select from; a MapEvent will be delivered to the
    *                  listener only if the filter evaluates to true for
    *                  that MapEvent; null is equivalent to a filter
    *                  that always returns true
    * @param fLite     true to indicate that the MapEvent objects do
    *                  not have to include the OldValue and NewValue
    *                  property values in order to allow optimizations
    */
    public synchronized void addListener(MapListener listener, Filter filter, boolean fLite)
        {
        if (listener != null)
            {
            Map mapListeners = m_mapListeners;
            if (mapListeners == null)
                {
                mapListeners = m_mapListeners = new LiteMap();
                }
            addSafeListener(mapListeners, filter, listener);

            Map mapStandard = m_mapStandardListeners;
            if (mapStandard == null)
                {
                mapStandard = m_mapStandardListeners = new LiteMap();
                }
            addListenerState(mapStandard, filter, listener, fLite);

            m_nOptimizationPlan = PLAN_NONE;
            m_listenersCached   = null;
            }
        }

    /**
    * Add a map listener that receives events based on a filter evaluation.
    *
    * @param listener  the listener to add
    * @param filter    a filter that will be passed MapEvent objects to
    *                  select from; a MapEvent will be delivered to the
    *                  listener only if the filter evaluates to true for
    *                  that MapEvent; null is equivalent to a filter
    *                  that always returns true
    * @param fLite     true to indicate that the MapEvent objects do
    *                  not have to include the OldValue and NewValue
    *                  property values in order to allow optimizations
    *
    * @return false iff there already existed a "covering" listener for that filter
    *              (either standard or lite for a lite call and standard otherwise)
    */
    public synchronized boolean addListenerWithCheck(MapListener listener, Filter filter, boolean fLite)
        {
        boolean fCovered = !isEmpty(filter) && (fLite || containsStandardListeners(filter));

        addListener(listener, filter, fLite);

        return !fCovered;
        }

    /**
    * Add a map listener for a specific key.
    *
    * @param listener  the listener to add
    * @param oKey      the key that identifies the entry for which to register
    *                  the event listener
    * @param fLite     true to indicate that the MapEvent objects do
    *                  not have to include the OldValue and NewValue
    *                  property values in order to allow optimizations
    */
    public synchronized void addListener(MapListener listener, Object oKey, boolean fLite)
        {
        if (listener != null)
            {
            Map mapListeners = m_mapKeyListeners;
            if (mapListeners == null)
                {
                mapListeners = m_mapKeyListeners = new HashMap();
                }
            addSafeListener(mapListeners, oKey, listener);

            Map mapStandard = m_mapStandardKeyListeners;
            if (mapStandard == null)
                {
                mapStandard = m_mapStandardKeyListeners = new LiteMap();
                }
            addListenerState(mapStandard, oKey, listener, fLite);

            // if the optimization plan was already to optimize for key
            // listeners, and the key listener that we just added is the
            // same as was already present, then keep the current plan,
            // otherwise reset it
            boolean fKeepPlan = false;
            if (m_nOptimizationPlan == PLAN_KEY_LISTENER)
                {
                EventListener[] alistener = m_listenersCached.listeners();
                if (alistener != null && alistener.length == 1 && alistener[0] == listener)
                    {
                    fKeepPlan = true;
                    }
                }

            if (!fKeepPlan)
                {
                m_nOptimizationPlan = PLAN_NONE;
                m_listenersCached   = null;
                }
            }
        }

    /**
    * Add a map listener for a specific key.
    *
    * @param listener  the listener to add
    * @param oKey      the key that identifies the entry for which to register
    *                  the event listener
    * @param fLite     true to indicate that the MapEvent objects do
    *                  not have to include the OldValue and NewValue
    *                  property values in order to allow optimizations
    *
    * @return false iff there already existed a "covering" listener for that key
    *              (either standard or lite for a lite call and standard otherwise)
    */
    public synchronized boolean addListenerWithCheck(MapListener listener, Object oKey, boolean fLite)
        {
        boolean fCovered = !isEmpty(oKey) && (fLite || containsStandardListeners(oKey));

        addListener(listener, oKey, fLite);

        return !fCovered;
        }

    /**
    * Add a map listener for a set of keys.
    *
    * @param listener  the listener to add
    * @param setKey    the key set for which to register the event listener
    * @param fLite     true to indicate that the MapEvent objects do
    *                  not have to include the OldValue and NewValue
    *                  property values in order to allow optimizations
    */
    public synchronized void addListener(MapListener listener, Set setKey, boolean fLite)
        {
        for (Object oKey : setKey)
            {
            addListener(listener, oKey, fLite);
            }
        }

    /**
    * Add a map listener for a set of keys. This method will modify the passed
    * set by removing the keys that already had existing "covering" listeners
    * for them.
    *
    * @param listener  the listener to add
    * @param setKey    the key set for which to register the event listener
    * @param fLite     true to indicate that the MapEvent objects do
    *                  not have to include the OldValue and NewValue
    *                  property values in order to allow optimizations
    */
    public synchronized void addListenerWithCheck(MapListener listener, Set setKey, boolean fLite)
        {
        for (Iterator iter = setKey.iterator(); iter.hasNext(); )
            {
            Object oKey = iter.next();

            if (!addListenerWithCheck(listener, oKey, fLite))
                {
                iter.remove();
                }
            }
        }

    /**
    * Remove a map listener that previously signed up for events based on a
    * filter evaluation.
    *
    * @param listener  the listener to remove
    * @param filter    a filter used to evaluate events
    */
    public synchronized void removeListener(MapListener listener, Filter filter)
        {
        if (listener != null)
            {
            Map mapListeners = m_mapListeners;
            if (mapListeners != null)
                {
                removeSafeListener(mapListeners, filter, listener);
                if (mapListeners.isEmpty())
                    {
                    m_mapListeners = null;
                    }

                Map mapStandard = m_mapStandardListeners;
                if (mapStandard != null)
                    {
                    removeListenerState(mapStandard, filter, listener);
                    if (mapStandard.isEmpty())
                        {
                        m_mapStandardListeners = null;
                        }
                    }
                }

            m_nOptimizationPlan = PLAN_NONE;
            m_listenersCached   = null;
            }
        }

    /**
    * Remove a map listener that previously signed up for events based on a
    * filter evaluation.
    *
    * @param listener  the listener to remove
    * @param filter    a filter used to evaluate events
    *
    * @return true iff there are no longer any listeners for that filter
    */
    public synchronized boolean removeListenerWithCheck(MapListener listener, Filter filter)
        {
        removeListener(listener, filter);

        return isEmpty(filter);
        }

    /**
    * Remove a map listener that previously signed up for events about a
    * specific key.
    *
    * @param listener  the listener to remove
    * @param oKey      the key that identifies the entry for which to unregister
    *                  the event listener
    */
    public synchronized void removeListener(MapListener listener, Object oKey)
        {
        if (listener != null)
            {
            Map mapListeners = m_mapKeyListeners;
            if (mapListeners != null)
                {
                removeSafeListener(mapListeners, oKey, listener);
                if (mapListeners.isEmpty())
                    {
                    m_mapKeyListeners = null;
                    }

                Map mapStandard = m_mapStandardKeyListeners;
                if (mapStandard != null)
                    {
                    removeListenerState(mapStandard, oKey, listener);
                    if (mapStandard.isEmpty())
                        {
                        m_mapStandardKeyListeners = null;
                        }
                    }
                }

            // if the optimization plan was already to optimize for key
            // listeners, and the cached set of key listeners is a set of
            // exactly one listener, and there are still other keys with
            // that same listener registered, then keep the current plan,
            // otherwise reset it
            boolean fKeepPlan = false;
            if (m_nOptimizationPlan == PLAN_KEY_LISTENER)
                {
                EventListener[] alistener = m_listenersCached.listeners();
                if (alistener != null && alistener.length == 1 && alistener[0] == listener)
                    {
                    // keep the plan if there are any keys still being
                    // listened to
                    fKeepPlan = (m_mapKeyListeners != null);
                    }
                }

            if (!fKeepPlan)
                {
                m_nOptimizationPlan = PLAN_NONE;
                m_listenersCached   = null;
                }
            }
        }

    /**
    * Remove a map listener that previously signed up for events about a
    * specific key.
    *
    * @param listener  the listener to remove
    * @param oKey      the key that identifies the entry for which to unregister
    *                  the event listener
    *
    * @return true iff there are no longer any listeners for that key
    */
    public synchronized boolean removeListenerWithCheck(MapListener listener, Object oKey)
        {
        removeListener(listener, oKey);

        return isEmpty(oKey);
        }

    /**
    * Remove a map listener that previously signed up for events about
    * specific keys.
    *
    * @param listener  the listener to remove
    * @param setKey    the set of keys for which to unregister the event listener
    */
    public synchronized void removeListener(MapListener listener, Set setKey)
        {
        for (Object oKey : setKey)
            {
            removeListener(listener, oKey);
            }
        }

    /**
    * Remove a map listener that previously signed up for events about specific
    * keys. This method will modify the passed set by removing the keys that
    * still have existing "covering" listeners for them, so the keys that are
    * retained in the set no longer have any listeners for them.
    *
    * @param listener  the listener to remove
    * @param setKey    the set of keys for which to unregister the event listener
    */
    public synchronized void removeListenerWithCheck(MapListener listener, Set setKey)
        {
        for (Iterator iter = setKey.iterator(); iter.hasNext(); )
            {
            if (!removeListenerWithCheck(listener, iter.next()))
                {
                iter.remove();
                }
            }
        }

    /**
    * Remove all signed up listeners.
    */
    public synchronized void clear()
        {
        m_mapListeners = null;
        m_mapKeyListeners = null;
        m_mapStandardListeners = null;
        m_mapStandardKeyListeners = null;

        m_nOptimizationPlan = PLAN_NO_LISTENERS;
        m_listenersCached   = null;
        }

    /**
    * Checks whether or not this MapListenerSupport object contains
    * any listeners.
    *
    * @return true iff there are no listeners encapsulated by this
    *         MapListenerSupport object
    */
    public boolean isEmpty()
        {
        return m_mapListeners == null && m_mapKeyListeners == null;
        }

    /**
    * Checks whether or not this MapListenerSupport object contains
    * any listeners for a given filter.
    *
    * @param filter  the filter
    *
    * @return true iff there are no listeners for the specified filter
    *              encapsulated by this MapListenerSupport object
    */
    public boolean isEmpty(Filter filter)
        {
        Map mapListeners = m_mapListeners;
        return mapListeners == null || !mapListeners.containsKey(filter);
        }

    /**
    * Checks whether or not this MapListenerSupport object contains
    * any listeners for a given key.
    *
    * @param oKey  the key
    *
    * @return true iff there are no listeners for the specified filter
    *              encapsulated by this MapListenerSupport object
    */
    public boolean isEmpty(Object oKey)
        {
        Map mapListeners = m_mapKeyListeners;
        return mapListeners == null || !mapListeners.containsKey(oKey);
        }

    /**
    * Checks whether or not this MapListenerSupport object contains
    * any standard (not lite) listeners for a given filter.
    *
    * @param filter  the filter
    *
    * @return true iff there are no standard listeners for the specified filter
    *              encapsulated by this MapListenerSupport object
    */
    public boolean containsStandardListeners(Filter filter)
        {
        Map mapStandard = m_mapStandardListeners;
        if (mapStandard == null)
            {
            return false;
            }
        Set setStandard = (Set) mapStandard.get(filter);
        return setStandard != null && !setStandard.isEmpty();
        }

    /**
    * Checks whether or not this MapListenerSupport object contains
    * any standard (not lite) listeners for a given key.
    *
    * @param oKey  the key
    *
    * @return true iff there are no standard listeners for the specified filter
    *              encapsulated by this MapListenerSupport object
    */
    public boolean containsStandardListeners(Object oKey)
        {
        Map mapStandard = m_mapStandardKeyListeners;
        if (mapStandard == null)
            {
            return false;
            }
        Set setStandard = (Set) mapStandard.get(oKey);
        return setStandard != null && !setStandard.isEmpty();
        }

    /**
    * Obtain a set of all filters that have associated global listeners.
    * 

* Note: The returned value must be treated as an immutable. * * @return a set of all filters that have associated global listeners */ public Set getFilterSet() { Map mapListeners = m_mapListeners; return mapListeners == null ? NullImplementation.getSet() : mapListeners.keySet(); } /** * Obtain a set of all keys that have associated key listeners. *

* Note: The returned value must be treated as an immutable. * * @return a set of all keys that have associated key listeners */ public Set getKeySet() { Map mapListeners = m_mapKeyListeners; return mapListeners == null ? NullImplementation.getSet() : mapListeners.keySet(); } /** * Obtain the Listeners object for a given filter. *

* Note: The returned value must be treated as an immutable. * * @param filter the filter * * @return the Listeners object for the filter; null if none exists */ public synchronized Listeners getListeners(Filter filter) { // this method is synchronized because the underlying map implementation // is not thread safe for "get" operations: it could blow up (LiteMap) // or return null (HashMap) while there is a valid entry Map mapListeners = m_mapListeners; return mapListeners == null ? null : (Listeners) mapListeners.get(filter); } /** * Obtain the Listeners object for a given key. *

* Note: The returned value must be treated as an immutable. * * @param oKey the key * * @return the Listeners object for the key; null if none exists */ public synchronized Listeners getListeners(Object oKey) { // this method is synchronized because the underlying map implementation // is not thread safe for "get" operations: it could blow up (LiteMap) // or return null (HashMap) while there is a valid entry Map mapListeners = m_mapKeyListeners; return mapListeners == null ? null : (Listeners) mapListeners.get(oKey); } /** * Collect all Listeners that should be notified for a given event. *

* Note: The returned value must be treated as an immutable. * * @param event the MapEvent object * * @return the Listeners object containing the relevant listeners */ public Listeners collectListeners(MapEvent event) { synchronized (this) { switch (m_nOptimizationPlan) { case PLAN_NONE: default: // put a plan together Map mapAllListeners = m_mapListeners; Map mapKeyListeners = m_mapKeyListeners; if (mapAllListeners == null || mapAllListeners.isEmpty()) { // no standard listeners; check for key listeners if (mapKeyListeners == null || mapKeyListeners.isEmpty()) { m_nOptimizationPlan = PLAN_NO_LISTENERS; m_listenersCached = null; } else { // can only do key optimization if all keys have // the same set of listeners registered EventListener[] alistenerPrev = null; for (Iterator iter = mapKeyListeners.values().iterator(); iter.hasNext(); ) { Listeners listeners = (Listeners) iter.next(); if (alistenerPrev == null) { // assume that they are all the same m_nOptimizationPlan = PLAN_KEY_LISTENER; m_listenersCached = listeners; alistenerPrev = listeners.listeners(); } else { EventListener[] alistenerCur = listeners.listeners(); int cListenersCur = alistenerCur.length; int cListenersPrev = alistenerPrev.length; boolean fOptimize = cListenersCur == cListenersPrev; if (fOptimize) { for (int i = 0; i < cListenersCur; ++i) { if (alistenerCur[i] != alistenerPrev[i]) { // assumption was incorrect -- some // keys have different listeners fOptimize = false; break; } } } if (!fOptimize) { m_nOptimizationPlan = PLAN_NO_OPTIMIZE; m_listenersCached = null; break; } } } } } else // there are "all" listeners { // assume no optimizations m_nOptimizationPlan = PLAN_NO_OPTIMIZE; m_listenersCached = null; // it is possible to optimize if there are no key // listeners AND no filtered listeners if (mapKeyListeners == null || mapKeyListeners.isEmpty()) { // check if there is only one listener and it has // no filter if (mapAllListeners.size() == 1) { Listeners listeners = (Listeners) mapAllListeners.get(null); if (listeners != null) { m_nOptimizationPlan = PLAN_ALL_LISTENER; m_listenersCached = listeners; } } } } azzert(m_nOptimizationPlan != PLAN_NONE); return collectListeners(event); case PLAN_NO_LISTENERS: return NO_LISTENERS; case PLAN_ALL_LISTENER: return m_listenersCached; case PLAN_KEY_LISTENER: return m_mapKeyListeners.containsKey(event.getKey()) ? m_listenersCached : NO_LISTENERS; case PLAN_NO_OPTIMIZE: // fall through to the full implementation } } Listeners listeners = new Listeners(); // add global listeners Map mapListeners = m_mapListeners; if (mapListeners != null) { MapEvent evt = unwrapEvent(event); if (evt instanceof FilterEvent) { FilterEvent evtFilter = (FilterEvent) evt; Filter[] aFilter = evtFilter.getFilter(); listeners.setFilters(aFilter); } Filter[] aFilters = listeners.getFilters(); if (aFilters == null) { // the server sent an event without a specified filter list; // attempt to match it to any registered filter-based listeners Object[] aEntry; synchronized (this) { aEntry = mapListeners.entrySet().toArray(); } List listFilters = null; for (Object o : aEntry) { Map.Entry entry = (Map.Entry) o; Filter filter = (Filter) entry.getKey(); if (filter == null || evaluateEvent(filter, event)) { listeners.addAll((Listeners) entry.getValue()); if (filter != null) { if (listFilters == null) { listFilters = new ArrayList<>(); } listFilters.add(filter); } } } if (listFilters != null) { listeners.setFilters(listFilters.toArray(new Filter[listFilters.size()])); } } else { synchronized (this) { for (Filter filter : aFilters) { listeners.addAll((Listeners) mapListeners.get(filter)); } } } } // add key listeners, only if the event is not transformed (COH-9355) Map mapKeyListeners = m_mapKeyListeners; if (mapKeyListeners != null && !isTransformedEvent(event)) { Listeners lsnrs = (Listeners) mapKeyListeners.get(event.getKey()); if (lsnrs != null) { listeners.addAll(lsnrs); } } return listeners; } /** * Fire the specified map event. * * @param event the map event * @param fStrict if true then any RuntimeException thrown by event * handlers stops all further event processing and the * exception is re-thrown; if false then all exceptions * are logged and the process continues */ public void fireEvent(MapEvent event, boolean fStrict) { Listeners listeners = collectListeners(event); enrichEvent(event, listeners).dispatch(listeners, fStrict); } /** * Convert the specified map event into another MapEvent that ensures the * lazy event data conversion using the specified converters. * * @param event the map event * @param mapConv the source for the converted event * @param convKey (optional) the key Converter * @param convVal (optional) the value converter * * @return the converted MapEvent object */ public static MapEvent convertEvent(MapEvent event, ObservableMap mapConv, Converter convKey, Converter convVal) { if (convKey == null) { convKey = NullImplementation.getConverter(); } if (convVal == null) { convVal = NullImplementation.getConverter(); } return ConverterCollections.getMapEvent(mapConv, event, convKey, convVal); } /** * Transform the given MapEvent into a FilterEvent if it is not already a * FilterEvent and there are matching filters associated with the * specified Listeners object. * * @param event the MapEvent to transform, if necessary * @param listeners the Listeners object * * @return a FilterEvent if the given MapEvent is not a FilterEvent and * the specified Listeners object has associated filters; * otherwise, the given MapEvent */ public static MapEvent enrichEvent(MapEvent event, Listeners listeners) { if (!(event instanceof FilterEvent)) { Filter[] aFilters = listeners.getFilters(); if (aFilters != null) { event = new FilterEvent(event, aFilters); } } return event; } /** * Unwrap the specified map event and return the underlying source event. * * @param evt the event to unwrap * * @return the unwrapped event */ public static MapEvent unwrapEvent(MapEvent evt) { while (evt instanceof ConverterCollections.ConverterMapEvent) { evt = ((ConverterCollections.ConverterMapEvent) evt).getMapEvent(); } return evt; } /** * Check if the given listener is a PrimingListener or if it wraps one. * * @param listener Map listener to check * * @return true iff the listener is a PrimingListener or wraps one */ public static boolean isPrimingListener(MapListener listener) { while (true) { if (listener instanceof PrimingListener) { return true; } if (listener instanceof WrapperListener) { listener = ((WrapperListener) listener).getMapListener(); } else { return false; } } } // ----- internal helpers ----------------------------------------------- /** * Evaluate whether or not the specified event should be delivered to the * listener associated with the specified filter. * * @param filter the filter * @param event the event * * @return true iff the event should be delivered to the corresponding listener */ protected boolean evaluateEvent(Filter filter, MapEvent event) { if (event instanceof CacheEvent && ((CacheEvent) event).getTransformationState() == CacheEvent.TransformationState.NON_TRANSFORMABLE && filter instanceof MapEventTransformer) { // if the event is marked as non-transformable, ensure that it does not // get delivered to listeners associated with transformer-filters return false; } if (filter instanceof InKeySetFilter) { // explicit support for the InKeySetFilter return ((InKeySetFilter) filter).getKeys().contains(event.getKey()); } return filter.evaluate(event); } /** * Return true iff the specified event represents a transformed CacheEvent. * * @param event the event to test * * @return true iff the event has been transformed */ protected boolean isTransformedEvent(MapEvent event) { return event instanceof CacheEvent && ((CacheEvent) event).getTransformationState() == CacheEvent.TransformationState.TRANSFORMED; } /** * Ensure that the specified map has a Listeners object associated * with the specified key and add the specified listener to it. */ protected static void addSafeListener(Map mapListeners, Object anyKey, MapListener listener) { Listeners listeners = (Listeners) mapListeners.get(anyKey); if (listeners == null) { listeners = new Listeners(); mapListeners.put(anyKey, listeners); } listeners.add(listener); } /** * Ensure that the specified map has a Listeners object associated * with the specified Filter and add the specified listener to it. */ protected static void addSafeListener(Map mapListeners, Filter anyFilter, MapListener listener) { Listeners listeners = (Listeners) mapListeners.get(anyFilter); if (listeners == null) { listeners = new Listeners(); if (anyFilter != null) { listeners.setFilters(new Filter[] {anyFilter}); } mapListeners.put(anyFilter, listeners); } listeners.add(listener); } /** * Remove the specified listener from the Listeners object associated * with the specified key. */ protected static void removeSafeListener(Map mapListeners, Object anyKey, MapListener listener) { Listeners listeners = (Listeners) mapListeners.get(anyKey); if (listeners != null) { listeners.remove(listener); if (listeners.isEmpty()) { mapListeners.remove(anyKey); } } } /** * Add a state information (lite or standard) associated with * specified key and listener. */ protected static void addListenerState(Map mapStandardListeners, Object anyKey, MapListener listener, boolean fLite) { Set setStandard = (Set) mapStandardListeners.get(anyKey); if (fLite) { if (setStandard != null) { setStandard.remove(listener); } } else { if (setStandard == null) { setStandard = new LiteSet(); mapStandardListeners.put(anyKey, setStandard); } setStandard.add(listener); } } /** * Remove a state information (lite or standard) associated with * specified key and listener. */ protected static void removeListenerState(Map mapStandardListeners, Object anyKey, MapListener listener) { Set setStandard = (Set) mapStandardListeners.get(anyKey); if (setStandard != null) { setStandard.remove(listener); if (setStandard.isEmpty()) { mapStandardListeners.remove(anyKey); } } } // ----- Object methods ------------------------------------------------- /** * Provide a string representation of the MapListenerSupport object. * * @return a human-readable description of the MapListenerSupport instance */ public synchronized String toString() { StringBuilder sb = new StringBuilder(); sb.append("Global listeners:"); if (m_mapListeners == null) { sb.append(" none"); } else { for (Iterator iter = m_mapListeners.keySet().iterator(); iter.hasNext();) { Filter filter = (Filter) iter.next(); sb.append("\n Filter=") .append(filter) .append("; lite=") .append(!containsStandardListeners(filter)); } } sb.append("\nKey listeners:"); if (m_mapKeyListeners == null) { sb.append(" none"); } else { for (Iterator iter = m_mapKeyListeners.keySet().iterator(); iter.hasNext();) { Object oKey = iter.next(); sb.append("\n Key=") .append(oKey) .append("; lite=") .append(!containsStandardListeners(oKey)); } } return sb.toString(); } // ----- inner classes -------------------------------------------------- /** * An extension of the CacheEvent which may carry no values (old or new), but * instead holds on an array of Filter objects being the "cause" of the event. */ public static class FilterEvent extends CacheEvent { /** * Constructs a new lite (no values are specified) FilterEvent. * * @param map the ObservableMap object that fired the event * @param nId this event's id * @param oKey the key into the map * @param fSynthetic true iff the event is caused by the cache * internal processing such as eviction or loading * @param aFilter an array of filters that caused this event */ public FilterEvent(ObservableMap map, int nId, Object oKey, boolean fSynthetic, Filter[] aFilter) { this(map, nId, oKey, null, null, fSynthetic, false, aFilter); } /** * Constructs a new lite (no values are specified) FilterEvent. * * @param map the ObservableMap object that fired the event * @param nId this event's id * @param oKey the key into the map * @param fSynthetic true iff the event is caused by the cache * internal processing such as eviction or loading * @param fPriming a flag indicating whether or not the event is a priming event * @param aFilter an array of filters that caused this event */ public FilterEvent(ObservableMap map, int nId, Object oKey, boolean fSynthetic, boolean fPriming, Filter[] aFilter) { this(map, nId, oKey, null, null, fSynthetic, fPriming, aFilter); } /** * Constructs a new FilterEvent. * * @param map the ObservableMap object that fired the event * @param nId this event's id * @param oKey the key into the map * @param oValueOld the old value * @param oValueNew the new value * @param fSynthetic true iff the event is caused by the cache * internal processing such as eviction or loading * @param fPriming a flag indicating whether or not the event is a priming event * @param aFilter an array of filters that caused this event */ public FilterEvent(ObservableMap map, int nId, Object oKey, Object oValueOld, Object oValueNew, boolean fSynthetic, boolean fPriming, Filter[] aFilter) { this(map, nId, oKey, oValueOld, oValueNew, fSynthetic, TransformationState.TRANSFORMABLE, fPriming, aFilter); } /** * Constructs a new FilterEvent. * * @param map the ObservableMap object that fired the event * @param nId this event's id * @param oKey the key into the map * @param oValueOld the old value * @param oValueNew the new value * @param fSynthetic true iff the event is caused by the cache * internal processing such as eviction or loading * @param transformState the {@link TransformationState state} describing * how this event has been or should be transformed * @param aFilter an array of filters that caused this event */ public FilterEvent(ObservableMap map, int nId, Object oKey, Object oValueOld, Object oValueNew, boolean fSynthetic, TransformationState transformState, Filter[] aFilter) { super(map, nId, oKey, oValueOld, oValueNew, fSynthetic, transformState, false); azzert(aFilter != null); f_aFilter = aFilter; f_event = null; } /** * Constructs a new FilterEvent. * * @param map the ObservableMap object that fired the event * @param nId this event's id * @param oKey the key into the map * @param oValueOld the old value * @param oValueNew the new value * @param fSynthetic true iff the event is caused by the cache * internal processing such as eviction or loading * @param transformState the {@link TransformationState state} describing * how this event has been or should be transformed * @param fPriming a flag indicating whether or not the event is a priming event * @param aFilter an array of filters that caused this event */ public FilterEvent(ObservableMap map, int nId, Object oKey, Object oValueOld, Object oValueNew, boolean fSynthetic, TransformationState transformState, boolean fPriming, Filter[] aFilter) { this(map, nId, oKey, oValueOld, oValueNew, fSynthetic, transformState, fPriming, false, aFilter); } /** * Constructs a new FilterEvent. * * @param map the ObservableMap object that fired the event * @param nId this event's id * @param oKey the key into the map * @param oValueOld the old value * @param oValueNew the new value * @param fSynthetic true iff the event is caused by the cache * internal processing such as eviction or loading * @param transformState the {@link TransformationState state} describing * how this event has been or should be transformed * @param fPriming a flag indicating whether or not the event is a priming event * @param fExpired true iff the event results from an eviction due to time * @param aFilter an array of filters that caused this event * * @since 22.06 */ public FilterEvent(ObservableMap map, int nId, Object oKey, Object oValueOld, Object oValueNew, boolean fSynthetic, TransformationState transformState, boolean fPriming, boolean fExpired, Filter[] aFilter) { super(map, nId, oKey, oValueOld, oValueNew, fSynthetic, transformState, fPriming, fExpired); azzert(aFilter != null); f_aFilter = aFilter; f_event = null; } /** * Constructs a new FilterEvent that wraps the given MapEvent. * * @param event the wrapped MapEvent * @param aFilter an array of filters that caused this event */ public FilterEvent(MapEvent event, Filter[] aFilter) { super(event.getMap(), event.getId(), null, null, null, event instanceof CacheEvent && ((CacheEvent) event).isSynthetic(), event instanceof CacheEvent ? ((CacheEvent) event).getTransformationState() : TransformationState.TRANSFORMABLE, event instanceof CacheEvent && ((CacheEvent) event).isPriming(), event instanceof CacheEvent && ((CacheEvent) event).isExpired()); azzert(aFilter != null); f_aFilter = aFilter; f_event = event; } /** * Return an array of filters that are the cause of this event. * * @return an array of filters */ public Filter[] getFilter() { return f_aFilter; } /** * Return the wrapped event. * * @return the underlying {@link MapEvent} * * @since 12.2.1.4 */ public MapEvent getMapEvent() { return f_event; } /** * Get the event's description. * * @return this event's description */ protected String getDescription() { return super.getDescription() + ", filters=" + new ImmutableArrayList(f_aFilter); } // ----- MapEvent methods --------------------------------------- /** * {@inheritDoc} */ public Object getKey() { return f_event == null ? super.getKey() : f_event.getKey(); } /** * {@inheritDoc} */ public Object getOldValue() { return f_event == null ? super.getOldValue() : f_event.getOldValue(); } /** * {@inheritDoc} */ public Object getNewValue() { return f_event == null ? super.getNewValue() : f_event.getNewValue(); } // ----- data members ------------------------------------------- /** * Filters that caused the event. */ protected final Filter[] f_aFilter; /** * Optional wrapped MapEvent. */ protected final MapEvent f_event; } /** * A tag interface indicating that tagged MapListener implementation * has to receive the MapEvent notifications in a synchronous manner. *

* Consider a MapListener that subscribes to receive notifications for * distributed (partitioned) cache. All events notifications are received * by the service thread and immediately queued to be processed by the * dedicated event dispatcher thread. This makes it impossible to * differentiate between the event caused by the updates made by this * thread and any other thread (possibly in a different VM). * Forcing the events to be processed on the service thread guarantees * that by the time "put" or "remove" requests return to the caller * all relevant MapEvent notifications raised on the same member as * the caller have been processed (due to the "in order delivery" rule * enforced by the TCMP). *

* This interface should be considered as a very advanced feature, so * a MapListener implementation that is tagged as a SynchronousListener * must exercise extreme caution during event processing since any delay * with return or unhandled exception will cause a delay or complete * shutdown of the corresponding cache service. *

* Note: The contract by the event producer in respect to the * SynchronousListener is somewhat weaker then the general one. * First, the SynchronousListener implementation should make no assumptions * about the event source obtained by {@link MapEvent#getMap()}. * Second, in the event of [automatic] service restart, the listener has * to be re-registered manually (for example, in response to the * {@link com.tangosol.net.MemberEvent#MEMBER_JOINED MEMBER_JOINED} event). * Third, and the most important, no calls against the NamedCache are * allowed during the synchronous event processing (the only exception * being a call to remove the listener itself). */ public interface SynchronousListener extends com.tangosol.util.SynchronousListener, MapListener { } /** * A tag interface indicating that this listener is registered as a * synchronous listener for lite events (carrying only a key) and generates * a "priming" event when registered. */ public interface PrimingListener extends SynchronousListener { } /** * A base class for various wrapper listener classes. */ public abstract static class WrapperListener extends MultiplexingMapListener { /** * Construct WrapperSynchronousListener. * * @param listener the wrapped MapListener */ public WrapperListener(MapListener listener) { azzert(listener != null); f_listener = listener; } /** * {@inheritDoc} */ @Override protected void onMapEvent(MapEvent evt) { evt.dispatch(f_listener); } /** * Return the underlying MapListener. * * @return the underlying MapListener */ public MapListener getMapListener() { return f_listener; } // ----- Object methods ----------------------------------------- /** * Determine a hash value for the WrapperSynchronousListener object * according to the general {@link Object#hashCode()} contract. * * @return an integer hash value for this WrapperSynchronousListener */ public int hashCode() { return f_listener.hashCode(); } /** * Compare the WrapperSynchronousListener with another object to * determine equality. * * @return true iff this WrapperSynchronousListener and the passed * object are equivalent listeners */ public boolean equals(Object o) { if (o != null && o.getClass() == getClass()) { WrapperListener that = (WrapperListener) o; return this.f_listener.equals(that.f_listener); } return false; } /** * {@inheritDoc} */ public String toString() { return ClassHelper.getSimpleName(getClass()) + "{listener=" + f_listener + "}"; } // ----- data members ------------------------------------------- /** * Wrapped MapListener. */ final private MapListener f_listener; } /** * A wrapper class that turns the specified MapListener into * a synchronous listener. */ public static class WrapperSynchronousListener extends WrapperListener implements SynchronousListener { /** * Construct WrapperSynchronousListener. * * @param listener the wrapped MapListener */ public WrapperSynchronousListener(MapListener listener) { super(listener); } } /** * A wrapper class that turns the specified MapListener into * a priming listener. */ public static class WrapperPrimingListener extends WrapperSynchronousListener implements PrimingListener { public WrapperPrimingListener(MapListener listener) { super(listener); } /** * {@inheritDoc} */ @Override protected void onMapEvent(MapEvent evt) { int nId = evt.getId(); switch (nId) { case MapEvent.ENTRY_INSERTED: getMapListener().entryInserted(evt); break; case MapEvent.ENTRY_UPDATED: getMapListener().entryUpdated(evt); break; case MapEvent.ENTRY_DELETED: getMapListener().entryDeleted(evt); break; default: throw new RuntimeException( "Unknown map event id: " + nId); } } } // ----- constants ------------------------------------------------------ /** * A plan has not yet been formed. */ protected static final int PLAN_NONE = 0; /** * There are no listeners. */ protected static final int PLAN_NO_LISTENERS = 1; /** * There is one all-keys non-filtered listener. */ protected static final int PLAN_ALL_LISTENER = 2; /** * There is one key listener (even if for multiple keys). */ protected static final int PLAN_KEY_LISTENER = 3; /** * There is no optimized plan, so just use the default approach. */ protected static final int PLAN_NO_OPTIMIZE = 4; /** * An empty set of Listeners. Because this is a theoretically mutable * object that is used as a return value, it is purposefully not static. */ protected final Listeners NO_LISTENERS = new Listeners(); // ----- data members --------------------------------------------------- /** * The collections of MapListener objects that have signed up for * notifications from an ObservableMap implementation keyed by the * corresponding Filter objects. */ protected Map m_mapListeners; /** * The collections of MapListener objects that have signed up for key based * notifications from an ObservableMap implementation keyed by the * corresponding key objects. */ protected Map m_mapKeyListeners; // consider adding listener tag support to Listeners class, which // would allow getting rid of the following data structures ... /** * The subset of standard (not lite) global listeners. The keys are the * Filter objects, the values are sets of corresponding standard listeners. */ protected Map m_mapStandardListeners; /** * The subset of standard (not lite) key listeners. The keys are the * key objects, the values are sets of corresponding standard listeners. */ protected Map m_mapStandardKeyListeners; /** * The optimization plan which indicates the fastest way to put together a * set of listeners. */ protected int m_nOptimizationPlan; /** * A cached set of Listeners. */ protected Listeners m_listenersCached; }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy