001: /*
002: * Copyright 2002-2007 the original author or authors.
003: *
004: * Licensed under the Apache License, Version 2.0 (the "License");
005: * you may not use this file except in compliance with the License.
006: * You may obtain a copy of the License at
007: *
008: * http://www.apache.org/licenses/LICENSE-2.0
009: *
010: * Unless required by applicable law or agreed to in writing, software
011: * distributed under the License is distributed on an "AS IS" BASIS,
012: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013: * See the License for the specific language governing permissions and
014: * limitations under the License.
015: */
016:
017: package org.springframework.util;
018:
019: import java.lang.ref.Reference;
020: import java.lang.ref.ReferenceQueue;
021: import java.lang.ref.WeakReference;
022: import java.util.Collections;
023: import java.util.HashMap;
024: import java.util.Map;
025:
026: import org.apache.commons.logging.Log;
027: import org.apache.commons.logging.LogFactory;
028:
029: /**
030: * Track references to arbitrary objects using proxy and weak references. To
031: * monitor a handle, one should call {@link #monitor(Object, ReleaseListener)},
032: * with the given handle object usually being a holder that uses the target
033: * object underneath, and the release listener performing cleanup of the
034: * target object once the handle is not strongly referenced anymore.
035: *
036: * <p>When a given handle becomes weakly reachable, the specified listener
037: * will be called by a background thread. This thread will only be started
038: * lazily and will be stopped once no handles are registered for monitoring
039: * anymore, to be restarted if further handles are added.
040: *
041: * <p>Thanks to Tomasz Wysocki for the suggestion and the original
042: * implementation of this class!
043: *
044: * @author Colin Sampaleanu
045: * @author Juergen Hoeller
046: * @since 1.2
047: * @see #monitor
048: */
049: public class WeakReferenceMonitor {
050:
051: private static final Log logger = LogFactory
052: .getLog(WeakReferenceMonitor.class);
053:
054: // Queue receiving reachability events
055: private static final ReferenceQueue handleQueue = new ReferenceQueue();
056:
057: // All tracked entries (WeakReference => ReleaseListener)
058: private static final Map trackedEntries = Collections
059: .synchronizedMap(new HashMap());
060:
061: // Thread polling handleQueue, lazy initialized
062: private static Thread monitoringThread = null;
063:
064: /**
065: * Start to monitor given handle object for becoming weakly reachable.
066: * When the handle isn't used anymore, the given listener will be called.
067: * @param handle the object that will be monitored
068: * @param listener the listener that will be called upon release of the handle
069: */
070: public static void monitor(Object handle, ReleaseListener listener) {
071: if (logger.isDebugEnabled()) {
072: logger.debug("Monitoring handle [" + handle
073: + "] with release listener [" + listener + "]");
074: }
075:
076: // Make weak reference to this handle, so we can say when
077: // handle is not used any more by polling on handleQueue.
078: WeakReference weakRef = new WeakReference(handle, handleQueue);
079:
080: // Add monitored entry to internal map of all monitored entries.
081: addEntry(weakRef, listener);
082: }
083:
084: /**
085: * Add entry to internal map of tracked entries.
086: * Internal monitoring thread is started if not already running.
087: * @param ref reference to tracked handle
088: * @param entry the associated entry
089: */
090: private static void addEntry(Reference ref, ReleaseListener entry) {
091: // Add entry, the key is given reference.
092: trackedEntries.put(ref, entry);
093:
094: // Start monitoring thread lazily.
095: synchronized (WeakReferenceMonitor.class) {
096: if (!isMonitoringThreadRunning()) {
097: monitoringThread = new Thread(new MonitoringProcess(),
098: WeakReferenceMonitor.class.getName());
099: monitoringThread.setDaemon(true);
100: monitoringThread.start();
101: }
102: }
103: }
104:
105: /**
106: * Remove entry from internal map of tracked entries.
107: * @param reference the reference that should be removed
108: * @return entry object associated with given reference
109: */
110: private static ReleaseListener removeEntry(Reference reference) {
111: return (ReleaseListener) trackedEntries.remove(reference);
112: }
113:
114: /**
115: * Check if monitoring thread is currently running.
116: */
117: private static boolean isMonitoringThreadRunning() {
118: synchronized (WeakReferenceMonitor.class) {
119: return (monitoringThread != null);
120: }
121: }
122:
123: /**
124: * Thread implementation that performs the actual monitoring.
125: */
126: private static class MonitoringProcess implements Runnable {
127:
128: public void run() {
129: logger.debug("Starting reference monitor thread");
130: try {
131: // Check if there are any tracked entries left.
132: while (!trackedEntries.isEmpty()) {
133: try {
134: Reference reference = handleQueue.remove();
135: // Stop tracking this reference.
136: ReleaseListener entry = removeEntry(reference);
137: if (entry != null) {
138: // Invoke listener callback.
139: entry.released();
140: }
141: } catch (InterruptedException ex) {
142: logger.debug(
143: "Reference monitor thread interrupted",
144: ex);
145: break;
146: }
147: }
148: } finally {
149: logger.debug("Stopping reference monitor thread");
150: synchronized (WeakReferenceMonitor.class) {
151: monitoringThread = null;
152: }
153: }
154: }
155: }
156:
157: /**
158: * Listener that is notified when the handle is being released.
159: * To be implemented by users of this reference monitor.
160: */
161: public static interface ReleaseListener {
162:
163: /**
164: * This callback method is invoked once the associated handle has been released,
165: * i.e. once there are no monitored strong references to the handle anymore.
166: */
167: void released();
168: }
169:
170: }
|