001/*
002 * Copyright 2002-2017 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 *      https://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
017package org.springframework.util;
018
019import java.lang.ref.Reference;
020import java.lang.ref.ReferenceQueue;
021import java.lang.ref.WeakReference;
022import java.util.HashMap;
023import java.util.Map;
024
025import org.apache.commons.logging.Log;
026import org.apache.commons.logging.LogFactory;
027
028/**
029 * Track references to arbitrary objects using proxy and weak references. To
030 * monitor a handle, one should call {@link #monitor(Object, ReleaseListener)},
031 * with the given handle object usually being a holder that uses the target
032 * object underneath, and the release listener performing cleanup of the
033 * target object once the handle is not strongly referenced anymore.
034 *
035 * <p>When a given handle becomes weakly reachable, the specified listener
036 * will be called by a background thread. This thread will only be started
037 * lazily and will be stopped once no handles are registered for monitoring
038 * anymore, to be restarted if further handles are added.
039 *
040 * <p>Thanks to Tomasz Wysocki for the suggestion and the original
041 * implementation of this class!
042 *
043 * @author Colin Sampaleanu
044 * @author Juergen Hoeller
045 * @since 1.2
046 * @see #monitor
047 * @deprecated as of Spring Framework 4.3.6
048 */
049@Deprecated
050public class WeakReferenceMonitor {
051
052        private static final Log logger = LogFactory.getLog(WeakReferenceMonitor.class);
053
054        // Queue receiving reachability events
055        private static final ReferenceQueue<Object> handleQueue = new ReferenceQueue<Object>();
056
057        // All tracked entries (WeakReference => ReleaseListener)
058        private static final Map<Reference<?>, ReleaseListener> trackedEntries = new HashMap<Reference<?>, ReleaseListener>();
059
060        // Thread polling handleQueue, lazy initialized
061        private static Thread monitoringThread = null;
062
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 + "] with release listener [" + listener + "]");
073                }
074
075                // Make weak reference to this handle, so we can say when
076                // handle is not used any more by polling on handleQueue.
077                WeakReference<Object> weakRef = new WeakReference<Object>(handle, handleQueue);
078
079                // Add monitored entry to internal map of all monitored entries.
080                addEntry(weakRef, listener);
081        }
082
083        /**
084         * Add entry to internal map of tracked entries.
085         * Internal monitoring thread is started if not already running.
086         * @param ref reference to tracked handle
087         * @param entry the associated entry
088         */
089        private static void addEntry(Reference<?> ref, ReleaseListener entry) {
090                synchronized (WeakReferenceMonitor.class) {
091                        // Add entry, the key is given reference.
092                        trackedEntries.put(ref, entry);
093
094                        // Start monitoring thread lazily.
095                        if (monitoringThread == null) {
096                                monitoringThread = new Thread(new MonitoringProcess(), WeakReferenceMonitor.class.getName());
097                                monitoringThread.setDaemon(true);
098                                monitoringThread.start();
099                        }
100                }
101        }
102
103        /**
104         * Remove entry from internal map of tracked entries.
105         * @param reference the reference that should be removed
106         * @return entry object associated with given reference
107         */
108        private static ReleaseListener removeEntry(Reference<?> reference) {
109                synchronized (WeakReferenceMonitor.class) {
110                        return trackedEntries.remove(reference);
111                }
112        }
113
114        /**
115         * Check whether to keep the monitoring thread alive,
116         * i.e. whether there are still entries being tracked.
117         */
118        private static boolean keepMonitoringThreadAlive() {
119                synchronized (WeakReferenceMonitor.class) {
120                        if (!trackedEntries.isEmpty()) {
121                                return true;
122                        }
123                        else {
124                                logger.debug("No entries left to track - stopping reference monitor thread");
125                                monitoringThread = null;
126                                return false;
127                        }
128                }
129        }
130
131
132        /**
133         * Thread implementation that performs the actual monitoring.
134         */
135        private static class MonitoringProcess implements Runnable {
136
137                @Override
138                public void run() {
139                        logger.debug("Starting reference monitor thread");
140                        // Check if there are any tracked entries left.
141                        while (keepMonitoringThreadAlive()) {
142                                try {
143                                        Reference<?> reference = handleQueue.remove();
144                                        // Stop tracking this reference.
145                                        ReleaseListener entry = removeEntry(reference);
146                                        if (entry != null) {
147                                                // Invoke listener callback.
148                                                try {
149                                                        entry.released();
150                                                }
151                                                catch (Throwable ex) {
152                                                        logger.warn("Reference release listener threw exception", ex);
153                                                }
154                                        }
155                                }
156                                catch (InterruptedException ex) {
157                                        synchronized (WeakReferenceMonitor.class) {
158                                                monitoringThread = null;
159                                        }
160                                        logger.debug("Reference monitor thread interrupted", ex);
161                                        break;
162                                }
163                        }
164                }
165        }
166
167
168        /**
169         * Listener that is notified when the handle is being released.
170         * To be implemented by users of this reference monitor.
171         */
172        public static interface ReleaseListener {
173
174                /**
175                 * This callback method is invoked once the associated handle has been released,
176                 * i.e. once there are no monitored strong references to the handle anymore.
177                 */
178                void released();
179        }
180
181}