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

com.tangosol.util.WrapperObservableMap Maven / Gradle / Ivy

There is a newer version: 24.09
Show newest version
/*
 * Copyright (c) 2000, 2022, Oracle and/or its affiliates.
 *
 * Licensed under the Universal Permissive License v 1.0 as shown at
 * https://oss.oracle.com/licenses/upl.
 */

package com.tangosol.util;


import com.tangosol.net.cache.CacheEvent;
import com.tangosol.net.cache.CacheStatistics;
import com.tangosol.net.cache.SimpleCacheStatistics;

import java.util.Iterator;
import java.util.Map;
import java.util.Set;


/**
* A simple implementation of ObservableMap interface built as a wrapper
* around any Map implementation. It also provides an implementation
* of CacheStatistics interface.
* 

* Note: as of Coherence 3.0 the CacheStatistics implementation has to be * turned on explicitly by calling the {@link #setCollectStats} method. *

* Note: if the underlying (wrapped) Map is an ObservableMap by itself, * as of Coherence 3.2 the WrapperObservableMap implementation does not * translate events generated the wrapped map by default. The translation can * be turned on explicitly by calling the {@link #setTranslateEvents} method. * * @author gg 2003.10.01 * @since Coherence 2.3 */ public class WrapperObservableMap extends AbstractKeySetBasedMap implements ObservableMap { // ----- constructors --------------------------------------------------- /** * Construct an ObservableMap wrapper based on the specified map. *

* Note: it is assumed that while the WrapperObservableMap exists, * the contents of the underlying wrapped map are not directly manipulated. * * @param map the Map that will be wrapped by this WrapperObservableMap */ public WrapperObservableMap(Map map) { this(map, false); } /** * Construct an ObservableMap wrapper based on the specified map. *

* Note: it is assumed that while the WrapperObservableMap exists, * the contents of the underlying wrapped map are not directly manipulated. * * @param map the Map that will be wrapped by this WrapperObservableMap * @param fDeferredEvent true iff if the value contained in the fabricated * cache events could be lazily populated. Deferred * events should only be raised to listeners that will * process events synchronously */ public WrapperObservableMap(Map map, boolean fDeferredEvent) { if (map == null) { throw new IllegalArgumentException("Map must be specified"); } m_map = map; m_fDeferredEvent = fDeferredEvent; } // ----- Map interface -------------------------------------------------- /** * {@inheritDoc} */ public synchronized void clear() { if (isEventFabricator()) { for (Iterator iter = getInternalKeySet().iterator(); iter.hasNext(); ) { // unlike some CacheEvent cases, this event gets fired BEFORE // the entry is removed; moreover, deferring the event // processing (e.g. processing it on a different thread) // may yield the OldValue inaccessible or plain invalid dispatchPendingEvent(iter.next(), MapEvent.ENTRY_DELETED, null, false); } } getMap().clear(); } /** * {@inheritDoc} */ public boolean containsValue(Object oValue) { return getMap().containsValue(oValue); } /** * {@inheritDoc} */ public V get(Object oKey) { Map mapInner = getMap(); if (isCollectStats()) { long ldtStart = getSafeTimeMillis(); V oValue = mapInner.get(oKey); boolean fContains = oValue != null || mapInner.containsKey(oKey); // update statistics if (fContains) { m_stats.registerHit(ldtStart); } else { m_stats.registerMiss(ldtStart); } return oValue; } else { return mapInner.get(oKey); } } /** * {@inheritDoc} */ public V put(K oKey, V oValue) { V oOrig; Map mapInner = getMap(); boolean fStats = isCollectStats(); long ldtStart = fStats ? getSafeTimeMillis() : 0L; if (isEventFabricator()) { int nEvent = mapInner.containsKey(oKey) ? MapEvent.ENTRY_UPDATED : MapEvent.ENTRY_INSERTED; oOrig = mapInner.put(oKey, oValue); dispatchEvent(new CacheEvent(this, nEvent, oKey, oOrig, oValue, false)); } else { oOrig = mapInner.put(oKey, oValue); } if (fStats) { // update statistics m_stats.registerPut(ldtStart); } return oOrig; } /** * {@inheritDoc} */ public void putAll(Map map) { Map mapInner = getMap(); boolean fStats = isCollectStats(); long ldtStart = fStats ? getSafeTimeMillis() : 0L; // issue events if (isEventFabricator()) { for (Map.Entry entry : map.entrySet()) { K oKey = entry.getKey(); int nEvent = mapInner.containsKey(oKey) ? MapEvent.ENTRY_UPDATED : MapEvent.ENTRY_INSERTED; dispatchPendingEvent(oKey, nEvent, entry.getValue(), false); } } mapInner.putAll(map); if (fStats) { // update statistics m_stats.registerPuts(map.size(), ldtStart); } } /** * {@inheritDoc} */ public V remove(Object oKey) { Map mapInner = getMap(); if (isEventFabricator()) { boolean fContained = mapInner.containsKey(oKey); V oOrig = fContained ? mapInner.remove(oKey) : null; if (fContained) { dispatchEvent(new CacheEvent(this, MapEvent.ENTRY_DELETED, oKey, oOrig, null, false)); } return oOrig; } else { return mapInner.remove(oKey); } } // ----- AbstractKeySetBasedMap methods --------------------------------- /** * {@inheritDoc} */ protected Set getInternalKeySet() { return getMap().keySet(); } /** * {@inheritDoc} */ protected boolean isInternalKeySetIteratorMutable() { // this wrapper only needs to do the mutations itself if the inner // map doesn't raise the necessary events return !isEventFabricator(); } /** * {@inheritDoc} */ protected boolean removeBlind(Object oKey) { if (isEventFabricator()) { boolean fRemoved = false; if (getMap().containsKey(oKey)) { dispatchPendingEvent((K) oKey, MapEvent.ENTRY_DELETED, null, false); getMap().keySet().remove(oKey); fRemoved = true; } return fRemoved; } else { return getMap().keySet().remove(oKey); } } // ----- ObservableMap methods ------------------------------------------ /** * {@inheritDoc} */ public synchronized void addMapListener(MapListener listener) { addMapListener(listener, (Filter) null, false); } /** * {@inheritDoc} */ public synchronized void removeMapListener(MapListener listener) { removeMapListener(listener, (Filter) null); } /** * {@inheritDoc} */ public synchronized void addMapListener(MapListener listener, K oKey, boolean fLite) { azzert(listener != null); Map map = getMap(); if (m_fEventBypass || (!isTranslateEvents() && map instanceof ObservableMap)) { // it's possible to completely by-pass the WrapperObservableMap // for event generation by allowing the underlying map to send // the events directly to the listeners ((ObservableMap) map).addMapListener(listener, oKey, fLite); m_fEventBypass = true; } else { MapListenerSupport support = ensureMapListenerSupport(); boolean fWasEmpty = support.isEmpty(oKey); boolean fWasLite = !fWasEmpty && !support.containsStandardListeners(oKey); support.addListener(listener, oKey, fLite); if ((fWasEmpty || (fWasLite && !fLite)) && map instanceof ObservableMap) { ObservableMap mapSource = (ObservableMap) map; MapListener listenerInternal = ensureInternalListener(); if (fWasLite && !fLite) { // previously registered a lite listener mapSource.removeMapListener(listenerInternal, oKey); } mapSource.addMapListener(listenerInternal, oKey, fLite); } } } /** * {@inheritDoc} */ public synchronized void removeMapListener(MapListener listener, K oKey) { azzert(listener != null); Map map = getMap(); if (m_fEventBypass) { // the event delivery was set up to by-pass this map, so // unregister accordingly ((ObservableMap) map).removeMapListener(listener, oKey); } else { MapListenerSupport support = m_listenerSupport; if (support != null) { boolean fWasStandard = support.containsStandardListeners(oKey); support.removeListener(listener, oKey); MapListener listenerInternal = m_listenerInternal; if (listenerInternal != null) { ObservableMap mapSource = (ObservableMap) getMap(); if (support.isEmpty(oKey)) { mapSource.removeMapListener(listenerInternal, oKey); if (support.isEmpty()) { m_listenerSupport = null; m_listenerInternal = null; } } else if (fWasStandard && !support.containsStandardListeners(oKey)) { // replace standard with lite mapSource.removeMapListener(listenerInternal, oKey); mapSource.addMapListener(listenerInternal, oKey, true); } } } } } /** * {@inheritDoc} */ public synchronized void addMapListener(MapListener listener, Filter filter, boolean fLite) { azzert(listener != null); Map map = getMap(); if (m_fEventBypass || (!isTranslateEvents() && map instanceof ObservableMap)) { // it's possible to completely by-pass the WrapperObservableMap // for event generation by allowing the underlying map to send // the events directly to the listeners ((ObservableMap) map).addMapListener(listener, filter, fLite); m_fEventBypass = true; } else { MapListenerSupport support = ensureMapListenerSupport(); boolean fWasEmpty = support.isEmpty(filter); boolean fWasLite = !fWasEmpty && !support.containsStandardListeners(filter); support.addListener(listener, filter, fLite); if ((fWasEmpty || (fWasLite && !fLite)) && map instanceof ObservableMap) { ObservableMap mapSource = (ObservableMap) map; MapListener listenerInternal = ensureInternalListener(); if (fWasLite && !fLite) { // previously registered a lite listener mapSource.removeMapListener(listenerInternal, filter); } mapSource.addMapListener(listenerInternal, filter, fLite); } } } /** * {@inheritDoc} */ public synchronized void removeMapListener(MapListener listener, Filter filter) { azzert(listener != null); Map map = getMap(); if (m_fEventBypass) { // the event delivery was set up to by-pass this map, so // unregister accordingly ((ObservableMap) map).removeMapListener(listener, filter); } else { MapListenerSupport support = m_listenerSupport; if (support != null) { boolean fWasStandard = support.containsStandardListeners(filter); support.removeListener(listener, filter); MapListener listenerInternal = m_listenerInternal; if (listenerInternal != null) { ObservableMap mapSource = (ObservableMap) getMap(); if (support.isEmpty(filter)) { mapSource.removeMapListener(listenerInternal, filter); if (support.isEmpty()) { m_listenerSupport = null; m_listenerInternal = null; } } else if (fWasStandard && !support.containsStandardListeners(filter)) { // replace standard with lite mapSource.removeMapListener(listenerInternal, filter); mapSource.addMapListener(listenerInternal, filter, true); } } } } } // ----- accessors ------------------------------------------------------ /** * Get the Map that is wrapped by this wrapper. *

* Note: direct modifications of the returned map may cause * an unpredictable behavior of the wrapper map. * * @return the wrapped Map */ public Map getMap() { return m_map; } /** * Return the CacheStatistics for this cache. * * @return a CacheStatistics object */ public CacheStatistics getCacheStatistics() { return m_stats; } /** * Check whether or not statistics are collected by the wrapper. * * @return true if this wrapper collects cache statistics; false otherwise * * @since Coherence 3.0 */ public boolean isCollectStats() { return m_fCollectStats; } /** * Specify whether or not statistics are to be collected by the wrapper. * * @param fCollectStats true if this wrapper should collect cache * statistics; false otherwise * * @since Coherence 3.0 */ public void setCollectStats(boolean fCollectStats) { if (fCollectStats != m_fCollectStats) { if (fCollectStats && m_stats == null) { m_stats = new SimpleCacheStatistics(); } m_fCollectStats = fCollectStats; } } /** * Check whether or not an event source has to be translated by the wrapper. *

* Note: this setting is only meaningful if the underlying map is an * ObservableMap itself. * * @return true if this wrapper translates an event source; false otherwise * * @since Coherence 3.3 */ public boolean isTranslateEvents() { return m_fTranslateEvents; } /** * Specify whether or not an event source has to be translated by the * wrapper. *

* Note: this setting is only meaningful if the underlying map is an * ObservableMap itself. * * @param fTranslate true if this wrapper should translate an event * source; false otherwise * * @since Coherence 3.3 */ public void setTranslateEvents(boolean fTranslate) { if (fTranslate != m_fTranslateEvents) { // once listeners have been added, this setting must not be // changed azzert(!hasListeners() && !m_fEventBypass); m_fTranslateEvents = fTranslate; } } /** * Assemble a human-readable description. * * @return a description of this Map */ protected String getDescription() { Map map = getMap(); return "Map {class=" + map.getClass().getName() + ", size=" + map.size() + ", observable=" + (map instanceof ObservableMap) + "}, CollectStats=" + isCollectStats() + ", CacheStatistics=" + getCacheStatistics() + ", hasListeners=" + hasListeners() + ", EventFabricator=" + isEventFabricator(); } // ----- Object methods ------------------------------------------------- /** * {@inheritDoc} */ public String toString() { return "WrapperObservableMap {" + getDescription() + "}"; } // ----- event dispatching ---------------------------------------------- /** * Accessor for the MapListenerSupport for sub-classes. * * @return the MapListenerSupport, or null if there are no listeners */ protected MapListenerSupport getMapListenerSupport() { return m_listenerSupport; } /** * Obtain the MapListenerSupport, creating it if necessary. * * @return the MapListenerSupport; never null */ protected MapListenerSupport ensureMapListenerSupport() { MapListenerSupport support = m_listenerSupport; if (support == null) { m_listenerSupport = support = new MapListenerSupport(); } return support; } /** * Determine if the OverflowMap has any listeners at all. * * @return true iff this OverflowMap has at least one MapListener */ protected boolean hasListeners() { // m_listenerSupport defaults to null, and it is reset to null when // the last listener unregisters return m_listenerSupport != null; } /** * Determine if this ObservableMap has to fabricate events itself. * * @return true if events are expected, but the wrapped Map does not * generate any events itself */ protected boolean isEventFabricator() { // if the wrapped Map doesn't issue its own events, then this Map // has to generate them itself return hasListeners() && m_listenerInternal == null; } /** * Helper method to determine if an event is synthetic. * * @param the key type * @param the value type * @param evt a Map Event * * @return true if the event is a synthetic cache event */ protected static boolean isSynthetic(MapEvent evt) { return evt instanceof CacheEvent && ((CacheEvent) evt).isSynthetic(); } /** * Dispatch an event that has not yet occurred, allowing the cache to * potentially avoid reading of the "original value" information. * * @param oKey the key which the event is related to * @param nId the event ID * @param oNewValue the new value * @param fSynthetic true if the event is synthetic */ protected void dispatchPendingEvent(K oKey, int nId, V oNewValue, boolean fSynthetic) { CacheEvent event = m_fDeferredEvent ? new CacheEvent(this, nId, oKey, null, oNewValue, fSynthetic) { public V getOldValue() { if (isInsert()) { return null; } V oOldValue = m_oOldValue; if (oOldValue == null) { m_oOldValue = oOldValue = WrapperObservableMap.this.get(getKey()); } return oOldValue; } private V m_oOldValue; } : new CacheEvent<>(this, nId, oKey, get(oKey), oNewValue, fSynthetic); dispatchEvent(event); } /** * Dispatch the passed event. * * @param evt a CacheEvent object */ protected void dispatchEvent(MapEvent evt) { MapListenerSupport listenerSupport = getMapListenerSupport(); if (listenerSupport != null) { if (isTranslateEvents() && evt.getMap() != this) { final MapEvent evtOrig = evt; evt = new CacheEvent(this, evt.getId(), evt.getKey(), null, null, false) { public V getOldValue() { return evtOrig.getOldValue(); } public V getNewValue() { return evtOrig.getNewValue(); } public boolean isSynthetic() { return WrapperObservableMap.isSynthetic(evtOrig); } }; } // the events can only be generated while the current thread // holds the monitor on this map synchronized (this) { listenerSupport.fireEvent(evt, false); } } } // ----- Object methods ------------------------------------------------- @Override public int hashCode() { return getMap().hashCode(); } @Override public boolean equals(Object o) { return getMap().equals(o); } // ----- inner class: InternalListener ---------------------------------- /** * Obtain the internal MapListener, creating one if necessary. * * @return an instance of MapListener; never null */ protected MapListener ensureInternalListener() { MapListener listenerInternal = m_listenerInternal; if (listenerInternal == null) { m_listenerInternal = listenerInternal = instantiateInternalListener(); } return listenerInternal; } /** * Instantiate a MapListener to listen to the wrapped map. * * @return an instance of MapListener */ protected MapListener instantiateInternalListener() { return new InternalListener(); } /** * An internal MapListener that listens to the wrapped map. */ protected class InternalListener extends MultiplexingMapListener { /** * {@inheritDoc} */ protected void onMapEvent(MapEvent evt) { WrapperObservableMap.this.dispatchEvent(evt); } } // ----- data fields ---------------------------------------------------- /** * The (wrapped) map containing all the resources. */ protected Map m_map; /** * The event listeners. */ protected MapListenerSupport m_listenerSupport; /** * The MapListener used to listen to the wrapped ObservableMap. */ protected MapListener m_listenerInternal; /** * The CacheStatistics object maintained by this wrapper. */ protected SimpleCacheStatistics m_stats; /** * Specifies whether statistics are to be collected by this wrapper. */ private boolean m_fCollectStats; /** * Specifies whether or not events are translated by this wrapper. */ protected boolean m_fTranslateEvents; /** * Specifies whether or not fabricated events could be deferred. */ protected boolean m_fDeferredEvent; /** * Tracks whether the WrapperObservableMap has explicitly decided to count * on the underlying map to bypass this map when delivering events. */ private boolean m_fEventBypass; }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy