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}