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 } |
|