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