001: /**********************************************************************************
002: * $URL: https://source.sakaiproject.org/svn/memory/tags/sakai_2-4-1/memory-impl/impl/src/java/org/sakaiproject/memory/impl/MultiRefCacheImpl.java $
003: * $Id: MultiRefCacheImpl.java 7103 2006-03-28 04:01:50Z ggolden@umich.edu $
004: ***********************************************************************************
005: *
006: * Copyright (c) 2005, 2006 The Sakai Foundation.
007: *
008: * Licensed under the Educational Community License, Version 1.0 (the "License");
009: * you may not use this file except in compliance with the License.
010: * You may obtain a copy of the License at
011: *
012: * http://www.opensource.org/licenses/ecl1.php
013: *
014: * Unless required by applicable law or agreed to in writing, software
015: * distributed under the License is distributed on an "AS IS" BASIS,
016: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
017: * See the License for the specific language governing permissions and
018: * limitations under the License.
019: *
020: **********************************************************************************/package org.sakaiproject.memory.impl;
021:
022: import java.util.Collection;
023: import java.util.Iterator;
024: import java.util.Map;
025: import java.util.Observable;
026: import java.util.Vector;
027:
028: import org.apache.commons.logging.Log;
029: import org.apache.commons.logging.LogFactory;
030: import org.sakaiproject.authz.cover.AuthzGroupService;
031: import org.sakaiproject.event.api.Event;
032: import org.sakaiproject.event.api.EventTrackingService;
033: import org.sakaiproject.memory.api.MultiRefCache;
034:
035: import EDU.oswego.cs.dl.util.concurrent.ConcurrentReaderHashMap;
036:
037: /**
038: * <p>
039: * MultiRefCacheImpl implements the MultiRefCache.
040: * </p>
041: * <p>
042: * The references that each cache entry are sensitive to are kept in a separate map for easy access.<br />
043: * Manipulation of this map is synchronized. This map is not used for cache access, just when items are added and removed.<br />
044: * The cache map itself becomes synchronized when it's manipulated (not when reads occur), so this added sync. for the refs fits the existing pattern.
045: * </p>
046: */
047: public class MultiRefCacheImpl extends MemCache implements
048: MultiRefCache {
049: /** Our logger. */
050: private static Log M_log = LogFactory
051: .getLog(MultiRefCacheImpl.class);
052:
053: /** Map of reference string -> Collection of cache keys. */
054: protected Map m_refs = null;
055:
056: // /** Enable for runtime cache consistency checking (likely expensive). */
057: // protected final static boolean TEST = true;
058:
059: protected class MultiRefCacheEntry extends CacheEntry {
060: /** These are the entity reference strings that this entry is sensitive to. */
061: protected Collection m_refs = new Vector();
062:
063: /**
064: * Construct to cache the payload for the duration.
065: *
066: * @param payload
067: * The thing to cache.
068: * @param duration
069: * The time (seconds) to keep this cached.
070: * @param ref
071: * One entity reference that, if changed, will invalidate this entry.
072: * @param azgRefs
073: * AuthzGroup refs that, if the changed, will invalidate this entry.
074: */
075: public MultiRefCacheEntry(Object payload, int duration,
076: String ref, Collection azgRefs) {
077: super (payload, duration);
078: if (ref != null)
079: m_refs.add(ref);
080: if (azgRefs != null)
081: m_refs.addAll(azgRefs);
082: }
083:
084: /**
085: * @inheritDoc
086: */
087: public Collection getRefs() {
088: return m_refs;
089: }
090: }
091:
092: /**
093: * Construct the Cache - checks for expiration periodically.
094: */
095: public MultiRefCacheImpl(BasicMemoryService memoryService,
096: EventTrackingService eventTrackingService, long sleep) {
097: super (memoryService, eventTrackingService, sleep, "");
098: m_softRefs = false;
099: m_refs = new ConcurrentReaderHashMap();
100: }
101:
102: /**
103: * @inheritDoc
104: */
105: public void put(Object key, Object payload, int duration,
106: String ref, Collection azgIds) {
107: if (disabled())
108: return;
109:
110: // make refs for any azg ids
111: Collection azgRefs = null;
112: if (azgIds != null) {
113: azgRefs = new Vector();
114: for (Iterator i = azgIds.iterator(); i.hasNext();) {
115: String azgId = (String) i.next();
116: azgRefs.add(AuthzGroupService
117: .authzGroupReference(azgId));
118: }
119: }
120:
121: // create our extended cache entry for the cache map - the reference strings are recorded
122: m_map.put(key, new MultiRefCacheEntry(payload, duration, ref,
123: azgRefs));
124:
125: m_putCount++;
126:
127: synchronized (m_refs) {
128: if (ref != null) {
129: addRefCachedKey(ref, key);
130: }
131:
132: if (azgRefs != null) {
133: for (Iterator i = azgRefs.iterator(); i.hasNext();) {
134: String azgRef = (String) i.next();
135: addRefCachedKey(azgRef, key);
136: }
137: }
138:
139: // perform (likely expensive) cache consistency check
140: // if (TEST) checkState();
141: }
142: }
143:
144: /**
145: * @inheritDoc
146: */
147: public void put(Object key, Object payload, int duration) {
148: put(key, payload, duration, null, null);
149: }
150:
151: /**
152: * @inheritDoc
153: */
154: public void put(Object key, Object payload) {
155: put(key, payload, 0, null, null);
156: }
157:
158: /**
159: * Make sure there's an entry in refs for this ref that includes this key.
160: *
161: * @param ref
162: * The entity reference string.
163: * @param key
164: * The cache entry key dependent on this entity ref.
165: */
166: protected void addRefCachedKey(String ref, Object key) {
167: Collection cachedKeys = (Collection) m_refs.get(ref);
168: if (cachedKeys == null) {
169: cachedKeys = new Vector();
170: m_refs.put(ref, cachedKeys);
171: }
172: if (!cachedKeys.contains(key)) {
173: cachedKeys.add(key);
174: }
175: }
176:
177: /**
178: * @inheritDoc
179: */
180: public void clear() {
181: super .clear();
182: synchronized (m_refs) {
183: m_refs.clear();
184: }
185: }
186:
187: /**
188: * @inheritDoc
189: */
190: public void remove(Object key) {
191: if (disabled())
192: return;
193:
194: MultiRefCacheEntry cachedEntry = (MultiRefCacheEntry) m_map
195: .remove(key);
196: if (cachedEntry == null)
197: return;
198:
199: // remove this key from any of the entity references in m_refs that are dependent on this entry
200: synchronized (m_refs) {
201: for (Iterator iRefs = cachedEntry.getRefs().iterator(); iRefs
202: .hasNext();) {
203: String ref = (String) iRefs.next();
204: Collection keys = (Collection) m_refs.get(ref);
205: if ((keys != null) && (keys.contains(key))) {
206: keys.remove(key);
207:
208: // remove the ref entry if it no longer has any cached keys in its colleciton
209: if (keys.isEmpty()) {
210: m_refs.remove(ref);
211: }
212: }
213: }
214:
215: // perform (likely expensive) cache consistency check
216: // if (TEST) checkState();
217: }
218: }
219:
220: /**********************************************************************************************************************************************************************************************************************************************************
221: * Cacher implementation
222: *********************************************************************************************************************************************************************************************************************************************************/
223:
224: /**
225: * @inheritDoc
226: */
227: public String getDescription() {
228: return "MultiRefCache: " + super .getDescription();
229: }
230:
231: /**********************************************************************************************************************************************************************************************************************************************************
232: * Observer implementation
233: *********************************************************************************************************************************************************************************************************************************************************/
234:
235: /**
236: * @inheritDoc
237: */
238: public void update(Observable o, Object arg) {
239: if (disabled())
240: return;
241:
242: // arg is Event
243: if (!(arg instanceof Event))
244: return;
245: Event event = (Event) arg;
246:
247: // if this is just a read, not a modify event, we can ignore it
248: if (!event.getModify())
249: return;
250:
251: // if we are holding event processing
252: if (m_holdEventProcessing) {
253: m_heldEvents.add(event);
254: return;
255: }
256:
257: continueUpdate(event);
258: }
259:
260: /**
261: * Complete the update, given an event that we know we need to act upon.
262: *
263: * @param event
264: * The event to process.
265: */
266: protected void continueUpdate(Event event) {
267: String ref = event.getResource();
268:
269: if (M_log.isDebugEnabled())
270: M_log.debug("update() [" + m_resourcePattern
271: + "] resource: " + ref + " event: "
272: + event.getEvent());
273:
274: // do we have this in our ref list
275: if (m_refs.containsKey(ref)) {
276: // get the copy of the Collection of cache keys for this reference (the actual collection will be reduced as the removes occur)
277: Collection cachedKeys = new Vector((Collection) m_refs
278: .get(ref));
279:
280: // invalidate all these keys
281: for (Iterator iKeys = cachedKeys.iterator(); iKeys
282: .hasNext();) {
283: remove(iKeys.next());
284: }
285: }
286: }
287:
288: /**
289: * @inheritDoc
290: */
291: public boolean isComplete() {
292: // we do not support being complete
293: return false;
294: }
295:
296: /**
297: * @inheritDoc
298: */
299: public boolean isComplete(String path) {
300: // we do not support being complete
301: return false;
302: }
303:
304: // /**
305: // * Check that the cache and the m_refs are consistent.
306: // */
307: // protected void checkState()
308: // {
309: // // every cache entry's every ref must have an entry in m_refs, and that entry must include the cache entry's key
310: // for (Iterator iEntries = m_map.entrySet().iterator(); iEntries.hasNext();)
311: // {
312: // Map.Entry entry = (Map.Entry) iEntries.next();
313: // MultiRefCacheEntry ce = (MultiRefCacheEntry) entry.getValue();
314: // for (Iterator iRefs = ce.getRefs().iterator(); iRefs.hasNext();)
315: // {
316: // String ref = (String) iRefs.next();
317: // Collection keys = (Collection) m_refs.get(ref);
318: // if (keys == null)
319: // m_logger.warn("** cache entry's ref not found in m_refs: cache key: " + entry.getKey() + " ref: " + ref);
320: // if ((keys != null) && (!keys.contains(entry.getKey())))
321: // m_logger.warn("** cache entry's ref's m_refs entry does not contain cache key: key: " + entry.getKey()
322: // + " ref: " + ref);
323: // }
324: // }
325: //
326: // // every reference in m_refs every key must exist in the cache, and must include the ref in the cache entry's refs
327: // for (Iterator iRefs = m_refs.entrySet().iterator(); iRefs.hasNext();)
328: // {
329: // Map.Entry entry = (Map.Entry) iRefs.next();
330: // Collection keys = (Collection) entry.getValue();
331: // for (Iterator iKeys = keys.iterator(); iKeys.hasNext();)
332: // {
333: // String key = (String) iKeys.next();
334: // if (m_map.get(key) == null)
335: // m_logger.warn("** m_ref's entry's key not found in cache: ref: " + entry.getKey() + " cache key: " + key);
336: // if ((m_map.get(key) != null) && (!(((MultiRefCacheEntry) m_map.get(key)).getRefs().contains(entry.getKey()))))
337: // m_logger.warn("** m_ref's entry's key->cache entry does not have the ref: ref: " + entry.getKey() + " cache key: " + key);
338: // }
339: // }
340: // }
341:
342: }
|