001: /*
002: * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
003: *
004: * Copyright 1997-2007 Sun Microsystems, Inc. All rights reserved.
005: *
006: * The contents of this file are subject to the terms of either the GNU
007: * General Public License Version 2 only ("GPL") or the Common
008: * Development and Distribution License("CDDL") (collectively, the
009: * "License"). You may not use this file except in compliance with the
010: * License. You can obtain a copy of the License at
011: * http://www.netbeans.org/cddl-gplv2.html
012: * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
013: * specific language governing permissions and limitations under the
014: * License. When distributing the software, include this License Header
015: * Notice in each file and include the License file at
016: * nbbuild/licenses/CDDL-GPL-2-CP. Sun designates this
017: * particular file as subject to the "Classpath" exception as provided
018: * by Sun in the GPL Version 2 section of the License file that
019: * accompanied this code. If applicable, add the following below the
020: * License Header, with the fields enclosed by brackets [] replaced by
021: * your own identifying information:
022: * "Portions Copyrighted [year] [name of copyright owner]"
023: *
024: * Contributor(s):
025: *
026: * The Original Software is NetBeans. The Initial Developer of the Original
027: * Software is Sun Microsystems, Inc. Portions Copyright 1997-2006 Sun
028: * Microsystems, Inc. All Rights Reserved.
029: *
030: * If you wish your version of this file to be governed by only the CDDL
031: * or only the GPL Version 2, indicate your decision by adding
032: * "[Contributor] elects to include this software in this distribution
033: * under the [CDDL or GPL Version 2] license." If you do not indicate a
034: * single choice of license, a recipient has the option to distribute
035: * your version of this file under either the CDDL, the GPL Version 2 or
036: * to extend the choice of license to its licensees as provided above.
037: * However, if you add GPL Version 2 code and therefore, elected the GPL
038: * Version 2 license, then the option applies only if the new code is
039: * made subject to such option by the copyright holder.
040: */
041: package org.openide.util;
042:
043: import java.lang.ref.SoftReference;
044:
045: import java.util.Map;
046:
047: /**
048: * A soft reference which is held strongly for a while after last access.
049: * Lifecycle:
050: * <ol>
051: * <li>Created. Referent held strongly. A task is scheduled into the request
052: * processor for some time in the future (currently 30 seconds).</li>
053: * <li>Expired. After the timeout, the reference switches to a normal soft
054: * reference.</li>
055: * <li>Touched. If the value is accessed before it is garbage collected,
056: * whether the reference is expired or not, the reference is "touched".
057: * This means that the referent is again held strongly and the timeout
058: * is started from scratch.</li>
059: * <li>Dead. If after expiry there is no access before the next full GC cycle,
060: * the GC algorithm may reclaim the reference. In this case the reference
061: * of course dies. As a bonus, it will try to remove itself as the value
062: * from a map of your choice, to make it convenient to use these references
063: * as values in a caching map without leaking memory for the key.</li>
064: * </ol>
065: * @author Jesse Glick
066: */
067: final class TimedSoftReference<T> extends SoftReference<T> implements
068: Runnable {
069: private static final int TIMEOUT = 30000;
070: private static final RequestProcessor RP = new RequestProcessor(
071: "TimedSoftReference"); // NOI18N
072: private RequestProcessor.Task task;
073: private T o;
074: private final Map m;
075: private final Object k;
076:
077: /** Time when the object was last time touched */
078: private long touched;
079:
080: /**
081: * Create a soft reference with timeout.
082: * The supplied map serves double duty as a synchronization lock
083: * for the reference's state changes.
084: * @param o the referent
085: * @param m a map in which this reference may serve as a value
086: * @param k the key whose value in <code>m</code> may be this reference
087: */
088: public TimedSoftReference(T o, Map m, Object k) {
089: super (o, Utilities.activeReferenceQueue());
090: this .o = o;
091: this .m = m;
092: this .k = k;
093: task = RP.create(this );
094: task.schedule(TIMEOUT);
095: }
096:
097: public void run() {
098: synchronized (m) {
099: if (o != null) {
100: //System.err.println("Expire " + k);
101: // how long we've really been idle
102: long unused = System.currentTimeMillis() - touched;
103:
104: if (unused > (TIMEOUT / 2)) {
105: o = null;
106: touched = 0;
107: } else {
108: task.schedule(TIMEOUT - (int) unused);
109: }
110: } else {
111: // clean up map ref, we are dead
112: //System.err.println("Die " + k);
113: m.remove(k);
114: }
115: }
116: }
117:
118: public T get() {
119: synchronized (m) {
120: if (o == null) {
121: o = super .get();
122: }
123:
124: if (o != null) {
125: // touch me
126: //System.err.println("Touch " + k);
127: if (touched == 0) {
128: task.schedule(TIMEOUT);
129: }
130:
131: touched = System.currentTimeMillis();
132:
133: return o;
134: } else {
135: return null;
136: }
137: }
138: }
139: }
|