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