Home | History | Annotate | Download | only in system
      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 
     17 package dalvik.system;
     18 
     19 /**
     20  * CloseGuard is a mechanism for flagging implicit finalizer cleanup of
     21  * resources that should have been cleaned up by explicit close
     22  * methods (aka "explicit termination methods" in Effective Java).
     23  * <p>
     24  * A simple example: <pre>   {@code
     25  *   class Foo {
     26  *
     27  *       private final CloseGuard guard = CloseGuard.get();
     28  *
     29  *       ...
     30  *
     31  *       public Foo() {
     32  *           ...;
     33  *           guard.open("cleanup");
     34  *       }
     35  *
     36  *       public void cleanup() {
     37  *          guard.close();
     38  *          ...;
     39  *       }
     40  *
     41  *       protected void finalize() throws Throwable {
     42  *           try {
     43  *               if (guard != null) {
     44  *                   guard.warnIfOpen();
     45  *               }
     46  *               cleanup();
     47  *           } finally {
     48  *               super.finalize();
     49  *           }
     50  *       }
     51  *   }
     52  * }</pre>
     53  *
     54  * In usage where the resource to be explicitly cleaned up are
     55  * allocated after object construction, CloseGuard protection can
     56  * be deferred. For example: <pre>   {@code
     57  *   class Bar {
     58  *
     59  *       private final CloseGuard guard = CloseGuard.get();
     60  *
     61  *       ...
     62  *
     63  *       public Bar() {
     64  *           ...;
     65  *       }
     66  *
     67  *       public void connect() {
     68  *          ...;
     69  *          guard.open("cleanup");
     70  *       }
     71  *
     72  *       public void cleanup() {
     73  *          guard.close();
     74  *          ...;
     75  *       }
     76  *
     77  *       protected void finalize() throws Throwable {
     78  *           try {
     79  *               if (guard != null) {
     80  *                   guard.warnIfOpen();
     81  *               }
     82  *               cleanup();
     83  *           } finally {
     84  *               super.finalize();
     85  *           }
     86  *       }
     87  *   }
     88  * }</pre>
     89  *
     90  * When used in a constructor calls to {@code open} should occur at
     91  * the end of the constructor since an exception that would cause
     92  * abrupt termination of the constructor will mean that the user will
     93  * not have a reference to the object to cleanup explicitly. When used
     94  * in a method, the call to {@code open} should occur just after
     95  * resource acquisition.
     96  *
     97  * <p>
     98  *
     99  * Note that the null check on {@code guard} in the finalizer is to
    100  * cover cases where a constructor throws an exception causing the
    101  * {@code guard} to be uninitialized.
    102  *
    103  * @hide
    104  */
    105 public final class CloseGuard {
    106 
    107     /**
    108      * Instance used when CloseGuard is disabled to avoid allocation.
    109      */
    110     private static final CloseGuard NOOP = new CloseGuard();
    111 
    112     /**
    113      * Enabled by default so we can catch issues early in VM startup.
    114      * Note, however, that Android disables this early in its startup,
    115      * but enables it with DropBoxing for system apps on debug builds.
    116      */
    117     private static volatile boolean ENABLED = true;
    118 
    119     /**
    120      * Hook for customizing how CloseGuard issues are reported.
    121      */
    122     private static volatile Reporter REPORTER = new DefaultReporter();
    123 
    124     /**
    125      * Returns a CloseGuard instance. If CloseGuard is enabled, {@code
    126      * #open(String)} can be used to set up the instance to warn on
    127      * failure to close. If CloseGuard is disabled, a non-null no-op
    128      * instance is returned.
    129      */
    130     public static CloseGuard get() {
    131         if (!ENABLED) {
    132             return NOOP;
    133         }
    134         return new CloseGuard();
    135     }
    136 
    137     /**
    138      * Used to enable or disable CloseGuard. Note that CloseGuard only
    139      * warns if it is enabled for both allocation and finalization.
    140      */
    141     public static void setEnabled(boolean enabled) {
    142         ENABLED = enabled;
    143     }
    144 
    145     /**
    146      * Used to replace default Reporter used to warn of CloseGuard
    147      * violations. Must be non-null.
    148      */
    149     public static void setReporter(Reporter reporter) {
    150         if (reporter == null) {
    151             throw new NullPointerException("reporter == null");
    152         }
    153         REPORTER = reporter;
    154     }
    155 
    156     /**
    157      * Returns non-null CloseGuard.Reporter.
    158      */
    159     public static Reporter getReporter() {
    160         return REPORTER;
    161     }
    162 
    163     private CloseGuard() {}
    164 
    165     /**
    166      * If CloseGuard is enabled, {@code open} initializes the instance
    167      * with a warning that the caller should have explicitly called the
    168      * {@code closer} method instead of relying on finalization.
    169      *
    170      * @param closer non-null name of explicit termination method
    171      * @throws NullPointerException if closer is null, regardless of
    172      * whether or not CloseGuard is enabled
    173      */
    174     public void open(String closer) {
    175         // always perform the check for valid API usage...
    176         if (closer == null) {
    177             throw new NullPointerException("closer == null");
    178         }
    179         // ...but avoid allocating an allocationSite if disabled
    180         if (this == NOOP || !ENABLED) {
    181             return;
    182         }
    183         String message = "Explicit termination method '" + closer + "' not called";
    184         allocationSite = new Throwable(message);
    185     }
    186 
    187     private Throwable allocationSite;
    188 
    189     /**
    190      * Marks this CloseGuard instance as closed to avoid warnings on
    191      * finalization.
    192      */
    193     public void close() {
    194         allocationSite = null;
    195     }
    196 
    197     /**
    198      * If CloseGuard is enabled, logs a warning if the caller did not
    199      * properly cleanup by calling an explicit close method
    200      * before finalization. If CloseGuard is disable, no action is
    201      * performed.
    202      */
    203     public void warnIfOpen() {
    204         if (allocationSite == null || !ENABLED) {
    205             return;
    206         }
    207 
    208         String message =
    209                 ("A resource was acquired at attached stack trace but never released. "
    210                  + "See java.io.Closeable for information on avoiding resource leaks.");
    211 
    212         REPORTER.report(message, allocationSite);
    213     }
    214 
    215     /**
    216      * Interface to allow customization of reporting behavior.
    217      */
    218     public static interface Reporter {
    219         public void report (String message, Throwable allocationSite);
    220     }
    221 
    222     /**
    223      * Default Reporter which reports CloseGuard violations to the log.
    224      */
    225     private static final class DefaultReporter implements Reporter {
    226         public void report (String message, Throwable allocationSite) {
    227             System.logW(message, allocationSite);
    228         }
    229     }
    230 }
    231