1 /*
   2  * Copyright (c) 2006, 2011, 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 sun.misc;
  27 
  28 /**
  29  * Provides utility functions related to URLClassLoaders or subclasses of it.
  30  *
  31  *                  W  A  R  N  I  N  G
  32  *
  33  * This class uses undocumented, unpublished, private data structures inside
  34  * java.net.URLClassLoader and sun.misc.URLClassPath.  Use with extreme caution.
  35  *
  36  * @author      tjquinn
  37  */
  38 
  39 
  40 import java.io.IOException;
  41 import java.net.URLClassLoader;
  42 import java.util.*;
  43 import java.util.jar.JarFile;
  44 
  45 public class ClassLoaderUtil {
  46 
  47     /**
  48      * Releases resources held by a URLClassLoader. A new classloader must
  49      * be created before the underlying resources can be accessed again.
  50      * @param classLoader the instance of URLClassLoader (or a subclass)
  51      */
  52     public static void releaseLoader(URLClassLoader classLoader) {
  53         releaseLoader(classLoader, null);
  54     }
  55 
  56     /**
  57      * Releases resources held by a URLClassLoader.  Notably, close the jars
  58      * opened by the loader. Initializes and updates the List of
  59      * jars that have been successfully closed.
  60      * <p>
  61      * @param classLoader the instance of URLClassLoader (or a subclass)
  62      * @param jarsClosed a List of Strings that will contain the names of jars
  63      *  successfully closed; can be null if the caller does not need the information returned
  64      * @return a List of IOExceptions reporting jars that failed to close; null
  65      * indicates that an error other than an IOException occurred attempting to
  66      * release the loader; empty indicates a successful release; non-empty
  67      * indicates at least one error attempting to close an open jar.
  68      */
  69     public static List<IOException> releaseLoader(URLClassLoader classLoader, List<String> jarsClosed) {
  70 
  71         List<IOException> ioExceptions = new LinkedList<IOException>();
  72 
  73         try {
  74             /* Records all IOExceptions thrown while closing jar files. */
  75 
  76             if (jarsClosed != null) {
  77                 jarsClosed.clear();
  78             }
  79 
  80             URLClassPath ucp = SharedSecrets.getJavaNetAccess()
  81                                                 .getURLClassPath(classLoader);
  82             ArrayList<?> loaders = ucp.loaders;
  83             Stack<?> urls = ucp.urls;
  84             HashMap<?,?> lmap = ucp.lmap;
  85 
  86             /*
  87              *The urls variable in the URLClassPath object holds URLs that have not yet
  88              *been used to resolve a resource or load a class and, therefore, do
  89              *not yet have a loader associated with them.  Clear the stack so any
  90              *future requests that might incorrectly reach the loader cannot be
  91              *resolved and cannot open a jar file after we think we've closed
  92              *them all.
  93              */
  94             synchronized(urls) {
  95                 urls.clear();
  96             }
  97 
  98             /*
  99              *Also clear the map of URLs to loaders so the class loader cannot use
 100              *previously-opened jar files - they are about to be closed.
 101              */
 102             synchronized(lmap) {
 103                 lmap.clear();
 104             }
 105 
 106             /*
 107              *The URLClassPath object's path variable records the list of all URLs that are on
 108              *the URLClassPath's class path.  Leave that unchanged.  This might
 109              *help someone trying to debug why a released class loader is still used.
 110              *Because the stack and lmap are now clear, code that incorrectly uses a
 111              *the released class loader will trigger an exception if the
 112              *class or resource would have been resolved by the class
 113              *loader (and no other) if it had not been released.
 114              *
 115              *The list of URLs might provide some hints to the person as to where
 116              *in the code the class loader was set up, which might in turn suggest
 117              *where in the code the class loader needs to stop being used.
 118              *The URLClassPath does not use the path variable to open new jar
 119              *files - it uses the urls Stack for that - so leaving the path variable
 120              *will not by itself allow the class loader to continue handling requests.
 121              */
 122 
 123             /*
 124              *For each loader, close the jar file associated with that loader.
 125              *
 126              *The URLClassPath's use of loaders is sync-ed on the entire URLClassPath
 127              *object.
 128              */
 129             synchronized (ucp) {
 130                 for (Object o : loaders) {
 131                     if (o != null) {
 132                         /*
 133                          *If the loader is a JarLoader inner class and its jarFile
 134                          *field is non-null then try to close that jar file.  Add
 135                          *it to the list of closed files if successful.
 136                          */
 137                         if (o instanceof URLClassPath.JarLoader) {
 138                                 URLClassPath.JarLoader jl = (URLClassPath.JarLoader)o;
 139                                 JarFile jarFile = jl.getJarFile();
 140                                 try {
 141                                     if (jarFile != null) {
 142                                         jarFile.close();
 143                                         if (jarsClosed != null) {
 144                                             jarsClosed.add(jarFile.getName());
 145                                         }
 146                                     }
 147                                 } catch (IOException ioe) {
 148                                     /*
 149                                      *Wrap the IOException to identify which jar
 150                                      *could not be closed and add it to the list
 151                                      *of IOExceptions to be returned to the caller.
 152                                      */
 153                                     String jarFileName = (jarFile == null) ? "filename not available":jarFile.getName();
 154                                     String msg = "Error closing JAR file: " + jarFileName;
 155                                     IOException newIOE = new IOException(msg);
 156                                     newIOE.initCause(ioe);
 157                                     ioExceptions.add(newIOE);
 158                                 }
 159                         }
 160                     }
 161                 }
 162                 /*
 163                  *Now clear the loaders ArrayList.
 164                  */
 165                 loaders.clear();
 166             }
 167         } catch (Throwable t) {
 168             throw new RuntimeException (t);
 169         }
 170         return ioExceptions;
 171     }
 172 }