1 /*
   2  * Copyright (c) 2005, Oracle and/or its affiliates. All rights reserved.
   3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
   4  *
   5  * This code is free software; you can redistribute it and/or modify it
   6  * under the terms of the GNU General Public License version 2 only, as
   7  * published by the Free Software Foundation.  Oracle designates this
   8  * particular file as subject to the "Classpath" exception as provided
   9  * by Oracle in the LICENSE file that accompanied this code.
  10  *
  11  * This code is distributed in the hope that it will be useful, but WITHOUT
  12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  14  * version 2 for more details (a copy is included in the LICENSE file that
  15  * accompanied this code).
  16  *
  17  * You should have received a copy of the GNU General Public License version
  18  * 2 along with this work; if not, write to the Free Software Foundation,
  19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  20  *
  21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  22  * or visit www.oracle.com if you need additional information or have any
  23  * questions.
  24  */
  25 
  26 package com.sun.imageio.stream;
  27 
  28 import java.io.IOException;
  29 import java.util.Set;
  30 import java.util.WeakHashMap;
  31 import javax.imageio.stream.ImageInputStream;
  32 
  33 /**
  34  * This class provide means to properly close hanging
  35  * image input/output streams on VM shutdown.
  36  * This might be useful for proper cleanup such as removal
  37  * of temporary files.
  38  *
  39  * Addition of stream do not prevent it from being garbage collected
  40  * if no other references to it exists. Stream can be closed
  41  * explicitly without removal from StreamCloser queue.
  42  * Explicit removal from the queue only helps to save some memory.
  43  */
  44 public class StreamCloser {
  45 
  46     private static WeakHashMap<CloseAction, Object> toCloseQueue;
  47     private static Thread streamCloser;
  48 
  49     public static void addToQueue(CloseAction ca) {
  50         synchronized (StreamCloser.class) {
  51             if (toCloseQueue == null) {
  52                 toCloseQueue =
  53                     new WeakHashMap<CloseAction, Object>();
  54             }
  55 
  56             toCloseQueue.put(ca, null);
  57 
  58             if (streamCloser == null) {
  59                 final Runnable streamCloserRunnable = new Runnable() {
  60                     public void run() {
  61                         if (toCloseQueue != null) {
  62                             synchronized (StreamCloser.class) {
  63                                 Set<CloseAction> set =
  64                                     toCloseQueue.keySet();
  65                                 // Make a copy of the set in order to avoid
  66                                 // concurrent modification (the is.close()
  67                                 // will in turn call removeFromQueue())
  68                                 CloseAction[] actions =
  69                                     new CloseAction[set.size()];
  70                                 actions = set.toArray(actions);
  71                                 for (CloseAction ca : actions) {
  72                                     if (ca != null) {
  73                                         try {
  74                                             ca.performAction();
  75                                         } catch (IOException e) {
  76                                         }
  77                                     }
  78                                 }
  79                             }
  80                         }
  81                     }
  82                 };
  83 
  84                 java.security.AccessController.doPrivileged(
  85                     new java.security.PrivilegedAction() {
  86                         public Object run() {
  87                             /* The thread must be a member of a thread group
  88                              * which will not get GCed before VM exit.
  89                              * Make its parent the top-level thread group.
  90                              */
  91                             ThreadGroup tg =
  92                                 Thread.currentThread().getThreadGroup();
  93                             for (ThreadGroup tgn = tg;
  94                                  tgn != null;
  95                                  tg = tgn, tgn = tg.getParent());
  96                             streamCloser = new Thread(tg, streamCloserRunnable);
  97                             /* Set context class loader to null in order to avoid
  98                              * keeping a strong reference to an application classloader.
  99                              */
 100                             streamCloser.setContextClassLoader(null);
 101                             Runtime.getRuntime().addShutdownHook(streamCloser);
 102                             return null;
 103                         }
 104                     });
 105             }
 106         }
 107     }
 108 
 109     public static void removeFromQueue(CloseAction ca) {
 110         synchronized (StreamCloser.class) {
 111             if (toCloseQueue != null) {
 112                 toCloseQueue.remove(ca);
 113             }
 114         }
 115     }
 116 
 117     public static CloseAction createCloseAction(ImageInputStream iis) {
 118         return new CloseAction(iis);
 119     }
 120 
 121     public static final class CloseAction {
 122         private ImageInputStream iis;
 123 
 124         private CloseAction(ImageInputStream iis) {
 125             this.iis = iis;
 126         }
 127 
 128         public void performAction() throws IOException {
 129             if (iis != null) {
 130                 iis.close();
 131             }
 132         }
 133     }
 134 }