001: /*
002:
003: ============================================================================
004: The Apache Software License, Version 1.1
005: ============================================================================
006:
007: Copyright (C) 1999-2003 The Apache Software Foundation. All rights reserved.
008:
009: Redistribution and use in source and binary forms, with or without modifica-
010: tion, are permitted provided that the following conditions are met:
011:
012: 1. Redistributions of source code must retain the above copyright notice,
013: this list of conditions and the following disclaimer.
014:
015: 2. Redistributions in binary form must reproduce the above copyright notice,
016: this list of conditions and the following disclaimer in the documentation
017: and/or other materials provided with the distribution.
018:
019: 3. The end-user documentation included with the redistribution, if any, must
020: include the following acknowledgment: "This product includes software
021: developed by the Apache Software Foundation (http://www.apache.org/)."
022: Alternately, this acknowledgment may appear in the software itself, if
023: and wherever such third-party acknowledgments normally appear.
024:
025: 4. The names "Batik" and "Apache Software Foundation" must not be
026: used to endorse or promote products derived from this software without
027: prior written permission. For written permission, please contact
028: apache@apache.org.
029:
030: 5. Products derived from this software may not be called "Apache", nor may
031: "Apache" appear in their name, without prior written permission of the
032: Apache Software Foundation.
033:
034: THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES,
035: INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
036: FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
037: APACHE SOFTWARE FOUNDATION OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
038: INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLU-
039: DING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
040: OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
041: ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
042: (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
043: THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
044:
045: This software consists of voluntary contributions made by many individuals
046: on behalf of the Apache Software Foundation. For more information on the
047: Apache Software Foundation, please see <http://www.apache.org/>.
048:
049: */
050:
051: package org.apache.batik.util;
052:
053: import java.lang.ref.SoftReference;
054: import java.util.HashMap;
055:
056: /**
057: * This class manages a cache of soft references to objects that may
058: * take some time to load or create, such as images loaded from the
059: * network.
060: *
061: * Adding an object is two fold: <br />
062: * + First you add the key, this lets the cache know that someone is
063: * working on that key. <br />
064: * + Then when the completed object is ready you put it into the cache.<P>
065: *
066: * If someone requests a key after it has been added but before it has
067: * been put they will be blocked until the put.
068: */
069:
070: public class SoftReferenceCache {
071: HashMap map = new HashMap();
072:
073: /**
074: * Let people create there own caches.
075: */
076: protected SoftReferenceCache() {
077: }
078:
079: /**
080: * Let people flush the cache (remove any cached data). Pending
081: * requests will be treated as though clear() was called on the
082: * key, this should cause them to go and re-read the data.
083: */
084: public synchronized void flush() {
085: map.clear();
086: this .notifyAll();
087: }
088:
089: /**
090: * Check if <tt>request(key)</tt> will return with an Object
091: * (not putting you on the hook for it). Note that it is possible
092: * that this will return true but between this call and the call
093: * to request the soft-reference will be cleared. So it
094: * is still possible for request to return NULL, just much less
095: * likely (you can always call 'clear' in that case).
096: */
097: protected final synchronized boolean isPresentImpl(Object key) {
098: if (!map.containsKey(key))
099: return false;
100:
101: Object o = map.get(key);
102: if (o == null)
103: // It's been requested but hasn't been 'put' yet.
104: return true;
105:
106: // It's been put let's make sure the soft reference hasn't
107: // been cleared.
108: SoftReference sr = (SoftReference) o;
109: o = sr.get();
110: if (o != null)
111: return true;
112:
113: // Soft reference was cleared, so remove our record of key.
114: clearImpl(key);
115: return false;
116: }
117:
118: /**
119: * Check if <tt>request(key)</tt> will return immediately with the
120: * Object. Note that it is possible that this will return
121: * true but between this call and the call to request the
122: * soft-reference will be cleared.
123: */
124: protected final synchronized boolean isDoneImpl(Object key) {
125: Object o = map.get(key);
126: if (o == null)
127: return false;
128: SoftReference sr = (SoftReference) o;
129: o = sr.get();
130: if (o != null)
131: return true;
132:
133: // Soft reference was cleared
134: clearImpl(key);
135: return false;
136: }
137:
138: /**
139: * If this returns null then you are now 'on the hook'.
140: * to put the Object associated with key into the
141: * cache. */
142: protected final synchronized Object requestImpl(Object key) {
143: if (map.containsKey(key)) {
144:
145: Object o = map.get(key);
146: while (o == null) {
147: try {
148: // When something is cleared or put we will be notified.
149: wait();
150: } catch (InterruptedException ie) {
151: }
152:
153: // check if key was cleared, if so it will most likely
154: // never be 'put'.
155: if (!map.containsKey(key))
156: break;
157:
158: // Let's see if it was put...
159: o = map.get(key);
160: }
161: if (o != null) {
162: SoftReference sr = (SoftReference) o;
163: o = sr.get();
164: if (o != null)
165: return o;
166: }
167: }
168:
169: // So now the caller get's the hot potato.
170: map.put(key, null);
171: return null;
172: }
173:
174: /**
175: * Clear the entry for key.
176: * This is the easiest way to 'get off the hook'.
177: * if you didn't indend to get on it.
178: */
179: protected final synchronized void clearImpl(Object key) {
180: map.remove(key);
181: this .notifyAll();
182: }
183:
184: /**
185: * Associate object with key. 'object' is only referenced through
186: * a soft reference so don't rely on the cache to keep it
187: * around. If the map no longer contains our url it was
188: * probably cleared or flushed since we were put on the hook
189: * for it, so in that case we will do nothing.
190: */
191: protected final synchronized void putImpl(Object key, Object object) {
192: if (map.containsKey(key)) {
193: SoftReference ref = new SoftRefKey(object, key);
194: map.put(key, ref);
195: this .notifyAll();
196: }
197: }
198:
199: class SoftRefKey extends CleanerThread.SoftReferenceCleared {
200: Object key;
201:
202: public SoftRefKey(Object o, Object key) {
203: super (o);
204: this .key = key;
205: }
206:
207: public void cleared() {
208: SoftReferenceCache cache = SoftReferenceCache.this ;
209: if (cache == null)
210: return; // Can't really happen.
211: synchronized (cache) {
212: Object o = cache.map.remove(key);
213: if (this != o)
214: // Must not have been ours put it back...
215: // Can happen if a clear is done.
216: cache.map.put(key, o);
217: }
218: }
219: }
220: }
|