CloseGuard.java revision b3f32d4d15177a0d2c064a8116d5cf5d07a217a9
1/* 2 * Copyright (C) 2010 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17package dalvik.system; 18 19import java.util.logging.Level; 20import java.util.logging.Logger; 21 22/** 23 * CloseGuard is a mechanism for flagging implicit finalizer cleanup of 24 * resources that should have been cleaned up by explicit close 25 * methods (aka "explicit termination methods" in Effective Java). 26 * <p> 27 * A simple example: <pre> {@code 28 * class Foo { 29 * 30 * private final CloseGuard guard = CloseGuard.get(); 31 * 32 * ... 33 * 34 * public Foo() { 35 * ...; 36 * guard.open("cleanup"); 37 * } 38 * 39 * public void cleanup() { 40 * guard.close(); 41 * ...; 42 * } 43 * 44 * protected void finalize() throws Throwable { 45 * try { 46 * if (guard != null) { 47 * guard.warnIfOpen(); 48 * } 49 * cleanup(); 50 * } finally { 51 * super.finalize(); 52 * } 53 * } 54 * } 55 * }</pre> 56 * 57 * In usage where the resource to be explicitly cleaned up are 58 * allocated after object construction, CloseGuard can protection can 59 * be deferred. For example: <pre> {@code 60 * class Bar { 61 * 62 * private final CloseGuard guard = CloseGuard.getUnopened(); 63 * 64 * ... 65 * 66 * public Bar() { 67 * ...; 68 * } 69 * 70 * public void connect() { 71 * ...; 72 * guard.open("cleanup"); 73 * } 74 * 75 * public void cleanup() { 76 * guard.close(); 77 * ...; 78 * } 79 * 80 * protected void finalize() throws Throwable { 81 * try { 82 * if (guard != null) { 83 * guard.warnIfOpen(); 84 * } 85 * cleanup(); 86 * } finally { 87 * super.finalize(); 88 * } 89 * } 90 * } 91 * }</pre> 92 * 93 * When used in a constructor calls to {@code open} should occur at 94 * the end of the constructor since an exception that would cause 95 * abrupt termination of the constructor will mean that the user will 96 * not have a reference to the object to cleanup explicitly. When used 97 * in a method, the call to {@code open} should occur just after 98 * resource acquisition. 99 * 100 * <p> 101 * 102 * Note that the null check on {@code guard} in the finalizer is to 103 * cover cases where a constructor throws an exception causing the 104 * {@code guard} to be uninitialized. 105 * 106 * @hide 107 */ 108public final class CloseGuard { 109 110 /** 111 * Instance used when CloseGuard is disabled to avoid allocation. 112 */ 113 private static final CloseGuard NOOP = new CloseGuard(); 114 115 /** 116 * Enabled by default so we can catch issues early in VM startup 117 */ 118 private static boolean ENABLED = true; 119 120 /** 121 * Returns a CloseGuard instance. If CloseGuard is enabled, {@code 122 * #open(String)} can be used to set up the instance to warn on 123 * failure to close. If CloseGuard is disabled, a non-null no-op 124 * instance is returned. 125 */ 126 public static CloseGuard get() { 127 if (!ENABLED) { 128 return NOOP; 129 } 130 return new CloseGuard(); 131 } 132 133 /** 134 * Used to enable or disable CloseGuard. Note that CloseGuard only 135 * warns if it is enabled for both allocation and finalization. 136 */ 137 public static void setEnabled(boolean enabled) { 138 ENABLED = enabled; 139 } 140 141 private CloseGuard() {} 142 143 /** 144 * If CloseGuard is enabled, {@code open} initializes the instance 145 * with a warning that the caller should have explicitly called the 146 * {@code closer} method instead of relying on finalization. 147 * 148 * @param closer non-null name of explicit termination method 149 * @throws NullPointerException if closer is null, regardless of 150 * whether or not CloseGuard is enabled 151 */ 152 public void open(String closer) { 153 // always perform the check for valid API usage... 154 if (closer == null) { 155 throw new NullPointerException("closer == null"); 156 } 157 // ...but avoid allocating an allocationSite if disabled 158 if (this == NOOP || !ENABLED) { 159 return; 160 } 161 String message = "Explicit termination method '" + closer + "' not called"; 162 allocationSite = new Throwable(message); 163 } 164 165 private Throwable allocationSite; 166 167 /** 168 * Marks this CloseGuard instance as closed to avoid warnings on 169 * finalization. 170 */ 171 public void close() { 172 allocationSite = null; 173 } 174 175 /** 176 * If CloseGuard is enabled, logs a warning if the caller did not 177 * properly cleanup by calling an explicit close method 178 * before finalization. If CloseGuard is disable, no action is 179 * performed. 180 */ 181 public void warnIfOpen() { 182 if (allocationSite == null || !ENABLED) { 183 return; 184 } 185 186 String message = 187 ("A resource was acquired at attached stack trace but never released. " 188 + "See java.io.Closeable for information on avoiding resource leaks."); 189 190 Logger.getLogger(CloseGuard.class.getName()) 191 .log(Level.WARNING, message, allocationSite); 192 } 193} 194