Lines 7-13
Link Here
|
7 |
* http://www.sun.com/ |
7 |
* http://www.sun.com/ |
8 |
* |
8 |
* |
9 |
* The Original Code is NetBeans. The Initial Developer of the Original |
9 |
* The Original Code is NetBeans. The Initial Developer of the Original |
10 |
* Code is Sun Microsystems, Inc. Portions Copyright 1997-2003 Sun |
10 |
* Code is Sun Microsystems, Inc. Portions Copyright 1997-2002 Sun |
11 |
* Microsystems, Inc. All Rights Reserved. |
11 |
* Microsystems, Inc. All Rights Reserved. |
12 |
*/ |
12 |
*/ |
13 |
|
13 |
|
Lines 20-105
Link Here
|
20 |
import java.security.PrivilegedAction; |
20 |
import java.security.PrivilegedAction; |
21 |
|
21 |
|
22 |
/** |
22 |
/** |
23 |
* A class loader that has multiple parents and uses them for loading |
23 |
* A special class loader optimized for working in the enviroment |
24 |
* classes and resources. It can be used in tree hierarchy, where it |
24 |
* of a deeply nested classloader hierarchy. It uses shared knowledge |
25 |
* can exploit its capability to not throw ClassNotFoundException when |
25 |
* about package population to direct the loading request directly |
26 |
* communicating with other ProxyClassLoader. |
26 |
* to the correct classloader. |
27 |
* It itself doesn't load classes or resources, but allows subclasses |
|
|
28 |
* to add such functionality. |
29 |
* |
27 |
* |
30 |
* @author Petr Nejedly, Jesse Glick |
28 |
* @author Petr Nejedly |
31 |
*/ |
29 |
*/ |
32 |
public class ProxyClassLoader extends ClassLoader { |
30 |
public class ProxyClassLoader extends ClassLoader { |
33 |
/** empty enumeration */ |
31 |
//XXX - add destroy() which will clean system-wide mapping cache |
34 |
private static final Enumeration EMPTY = new ArrayEnumeration (new Object[0]); |
|
|
35 |
|
32 |
|
36 |
/** |
33 |
private static ClassLoader systemCL = ClassLoader.getSystemClassLoader(); |
37 |
* All known package owners. |
|
|
38 |
* Packages are given in format <samp>org/netbeans/modules/foo/</samp>. |
39 |
* Of type <code>Map<String,ClassLoader></code>. |
40 |
*/ |
41 |
private final Map domainsByPackage = new HashMap(); |
42 |
/** All known packages, of type <code>Map<String,Package></code> */ |
43 |
private final Map packages = new HashMap(); |
44 |
|
45 |
/** All parents of this classloader, including their parents recursively */ |
46 |
private ClassLoader[] parents; |
47 |
|
48 |
/** if true, we have been destroyed */ |
49 |
private boolean dead = false; |
50 |
|
51 |
private final boolean transitive; |
52 |
|
53 |
/** Create a multi-parented classloader. |
54 |
* Loads recursively from parents. |
55 |
* @param parents list of parent classloaders. |
56 |
* @throws IllegalArgumentException if there are any nulls or duplicate |
57 |
* parent loaders or cycles. |
58 |
*/ |
59 |
public ProxyClassLoader( ClassLoader[] parents ) { |
60 |
this(parents, true); |
61 |
} |
62 |
|
63 |
/** Create a multi-parented classloader. |
64 |
* @param parents list of parent classloaders. |
65 |
* @param transitive whether other PCLs depending on this one will |
66 |
* automatically search through its parent list |
67 |
* @throws IllegalArgumentException if there are any nulls or duplicate |
68 |
* parent loaders or cycles. |
69 |
* @since org.netbeans.core/1 > 1.6 |
70 |
*/ |
71 |
public ProxyClassLoader(ClassLoader[] parents, boolean transitive) { |
72 |
if (parents.length == 0) { |
73 |
throw new IllegalArgumentException ("ProxyClassLoader must have a parent"); // NOI18N |
74 |
} |
75 |
|
76 |
this.transitive = transitive; |
77 |
|
78 |
Set check = new HashSet(Arrays.asList(parents)); // Set<ClassLoader> |
79 |
if (check.size() < parents.length) throw new IllegalArgumentException("duplicate parents"); // NOI18N |
80 |
if (check.contains(null)) throw new IllegalArgumentException("null parent in " + check); // NOI18N |
81 |
|
34 |
|
82 |
this.parents = coalesceParents(parents); |
35 |
/** A Map<String,Set<ClassLoader>> of all packages known by all classloaders */ |
|
|
36 |
private static Map packageCoverage = new HashMap(); |
37 |
|
38 |
private boolean transitive; |
39 |
private Set parentSet = new HashSet(); |
40 |
private ProxyClassLoader[] parents; |
41 |
private Map packages = new HashMap(); |
42 |
|
43 |
private static Map sclPackages = new HashMap(); |
44 |
|
45 |
/** |
46 |
* @param parents all direct parents of this classloader, except system one. |
47 |
* @param coveredPackages Enumeration of Strings if format "org.something" |
48 |
* containing all packages to be covered by this classloader. |
49 |
*/ |
50 |
public ProxyClassLoader(ProxyClassLoader[] parents, Iterator coveredPackages, boolean transitive) { |
51 |
this.transitive = transitive; |
52 |
|
53 |
this.parents = coalesceParents(parents); |
54 |
parentSet.addAll(Arrays.asList(this.parents)); |
55 |
//System.err.println("Covered packages:"); |
56 |
while (coveredPackages.hasNext()) { |
57 |
String pkg = (String)coveredPackages.next(); |
58 |
//System.err.println(" '" + pkg +"'"); |
59 |
Set delegates = (Set)packageCoverage.get(pkg); |
60 |
if (delegates == null) { |
61 |
delegates = new HashSet(); |
62 |
packageCoverage.put(pkg, delegates); |
63 |
} |
64 |
delegates.add(this); |
65 |
} |
83 |
} |
66 |
} |
84 |
|
67 |
|
85 |
// this is used only by system classloader, maybe we can redesign it a bit |
68 |
// this is used only by system classloader, maybe we can redesign it a bit |
86 |
// to live without this functionality, then destroy may also go away |
69 |
// to live without this functionality, then destroy may also go away |
87 |
/** Add new parents dynamically. |
70 |
/** Add new parents dynamically. |
88 |
* @param parents the new parents to add (append to list) |
71 |
* @param parents the new parents to add (append to list) |
89 |
* @throws IllegalArgumentException in case of a null or cyclic parent (duplicate OK) |
72 |
* @throws IllegalArgumentException in case of a null or cyclic parent (duplicate OK) |
90 |
*/ |
73 |
*/ |
91 |
public synchronized void append(ClassLoader[] nueparents) throws IllegalArgumentException { |
74 |
public synchronized void append(ProxyClassLoader[] nueparents) throws IllegalArgumentException { |
92 |
// XXX should this be synchronized? |
75 |
// XXX should this be synchronized? |
93 |
if (nueparents == null) throw new IllegalArgumentException("null parents array"); // NOI18N |
76 |
if (nueparents == null) throw new IllegalArgumentException("null parents array"); // NOI18N |
94 |
for (int i = 0; i < nueparents.length; i++) { |
77 |
for (int i = 0; i < nueparents.length; i++) { |
95 |
if (nueparents[i] == null) throw new IllegalArgumentException("null parent"); // NOI18N |
78 |
if (nueparents[i] == null) throw new IllegalArgumentException("null parent"); // NOI18N |
96 |
} |
79 |
} |
97 |
|
80 |
|
98 |
parents = coalesceAppend(parents, nueparents); |
81 |
this.parents = coalesceAppend(parents, nueparents); |
|
|
82 |
parentSet.clear(); |
83 |
parentSet.addAll(Arrays.asList(this.parents)); |
99 |
} |
84 |
} |
100 |
|
85 |
|
101 |
|
86 |
|
102 |
|
87 |
//\\ |
103 |
/** Try to destroy this classloader. |
88 |
/** Try to destroy this classloader. |
104 |
* Subsequent attempts to use it will log an error (at most one though). |
89 |
* Subsequent attempts to use it will log an error (at most one though). |
105 |
*/ |
90 |
*/ |
Lines 120-132
Link Here
|
120 |
* Loads the class with the specified name. The implementation of |
105 |
* Loads the class with the specified name. The implementation of |
121 |
* this method searches for classes in the following order:<p> |
106 |
* this method searches for classes in the following order:<p> |
122 |
* <ol> |
107 |
* <ol> |
123 |
* <li> Calls {@link #findLoadedClass(String)} to check if the class has |
108 |
* <li> Looks for a known package and pass the loading to the ClassLoader |
|
|
109 |
for that package. |
110 |
* <li> For unknown packages passes the call directly |
124 |
* already been loaded. |
111 |
* already been loaded. |
125 |
* <li> Checks the caches whether another class from the same package |
|
|
126 |
* was already loaded and uses the same classloader |
127 |
* <li> Tries to find the class using parent loaders in their order. |
128 |
* <li> Calls the {@link #simpleFindClass} method to find |
129 |
* the class using this class loader. |
130 |
* </ol> |
112 |
* </ol> |
131 |
* |
113 |
* |
132 |
* @param name the name of the class |
114 |
* @param name the name of the class |
Lines 136-150
Link Here
|
136 |
*/ |
118 |
*/ |
137 |
protected synchronized final Class loadClass(String name, boolean resolve) |
119 |
protected synchronized final Class loadClass(String name, boolean resolve) |
138 |
throws ClassNotFoundException { |
120 |
throws ClassNotFoundException { |
139 |
zombieCheck(name); |
121 |
Class cls = null; |
140 |
String filename = name.replace('.', '/').concat(".class"); // NOI18N |
122 |
|
141 |
int idx = filename.lastIndexOf('/'); // NOI18N |
123 |
int last = name.lastIndexOf('.'); |
142 |
if (idx == -1) throw new ClassNotFoundException("Will not load classes from default package"); // NOI18N |
124 |
String pkg = (last >= 0) ? name.substring(0, last) : ""; |
143 |
String pkg = filename.substring(0, idx + 1); // "org/netbeans/modules/foo/" |
125 |
|
144 |
Class c = smartLoadClass(name, filename, pkg); |
126 |
Set del = (Set)packageCoverage.get(pkg); |
145 |
if(c == null) throw new ClassNotFoundException(name); |
127 |
|
146 |
if (resolve) resolveClass(c); |
128 |
Boolean boo = (Boolean)sclPackages.get(pkg); |
147 |
return c; |
129 |
if (boo == null || boo.booleanValue()) { |
|
|
130 |
try { |
131 |
cls = systemCL.loadClass(name); |
132 |
if (boo == null) sclPackages.put(pkg, Boolean.TRUE); |
133 |
return cls; // try SCL first |
134 |
} catch (ClassNotFoundException e) { |
135 |
// sclPackages.put(pkg, Boolean.FALSE); // quite brave assumption |
136 |
} |
137 |
} |
138 |
|
139 |
if (del == null) { |
140 |
// uncovered package, go directly to SCL |
141 |
cls = systemCL.loadClass(name); |
142 |
} else if (del.size() == 1) { |
143 |
// simple package coverage |
144 |
ProxyClassLoader pcl = (ProxyClassLoader)del.iterator().next(); |
145 |
if (pcl == this || parentSet.contains(pcl)) { |
146 |
cls = pcl.selfLoadClass(name); |
147 |
//System.err.println("selfLoadClass(" + pcl + "): " + cls ); |
148 |
if (cls != null) sclPackages.put(pkg, Boolean.FALSE); |
149 |
} else { // maybe it is also covered by SCL |
150 |
cls = systemCL.loadClass(name); |
151 |
} |
152 |
} else { |
153 |
// multicovered package, search in order |
154 |
for (int i=0; i<parents.length; i++) { // all our accessible parents |
155 |
if (del.contains(parents[i])) { // that cover given package |
156 |
cls = parents[i].selfLoadClass(name); |
157 |
if (cls != null) break; |
158 |
} |
159 |
} |
160 |
if (cls == null && del.contains(this)) cls = selfLoadClass(name); |
161 |
if (cls != null) sclPackages.put(pkg, Boolean.FALSE); |
162 |
if (cls == null) cls = systemCL.loadClass(name); // XXX |
163 |
} |
164 |
if(cls == null) throw new ClassNotFoundException(name); |
165 |
if (resolve) resolveClass(cls); |
166 |
return cls; |
167 |
} |
168 |
|
169 |
/** May return null */ |
170 |
private synchronized Class selfLoadClass(String name) { |
171 |
Class cls = findLoadedClass(name); |
172 |
if (cls == null) cls = doLoadClass(name); |
173 |
return cls; |
148 |
} |
174 |
} |
149 |
|
175 |
|
150 |
/** This ClassLoader can't load anything itself. Subclasses |
176 |
/** This ClassLoader can't load anything itself. Subclasses |
Lines 153-241
Link Here
|
153 |
* <CODE>null</CODE> if it can't load required class. |
179 |
* <CODE>null</CODE> if it can't load required class. |
154 |
* |
180 |
* |
155 |
* @param name the name of the class |
181 |
* @param name the name of the class |
156 |
* @param fileName the expected filename of the classfile, like |
|
|
157 |
* <CODE>java/lang/Object.class</CODE> for <CODE>java.lang.Object</CODE> |
158 |
* The ClassLoader implementation may or may not use it, depending |
159 |
* whether it is usefull to it. |
160 |
* @param pkg the package name, in the format org/netbeans/modules/foo/ |
161 |
* @return the resulting <code>Class</code> object or <code>null</code> |
182 |
* @return the resulting <code>Class</code> object or <code>null</code> |
162 |
*/ |
183 |
*/ |
163 |
protected Class simpleFindClass(String name, String fileName, String pkg) { |
184 |
protected Class doLoadClass(String name) { |
164 |
return null; |
185 |
return null; |
165 |
} |
186 |
} |
166 |
|
187 |
|
167 |
|
188 |
|
168 |
/** |
189 |
/** |
169 |
* Finds the resource with the given name. The implementation of |
190 |
* Finds the resource with the given name. |
170 |
* this method searches for resources in the following order:<p> |
|
|
171 |
* <ol> |
172 |
* <li> Checks the caches whether another resource or class from the same |
173 |
* package was already loaded and uses the same classloader. |
174 |
* <li> Tries to find the resources using parent loaders in their order. |
175 |
* <li> Calls the {@link #findResource(String)} method to find |
176 |
* the resources using this class loader. |
177 |
* </ol> |
178 |
* |
179 |
* @param name a "/"-separated path name that identifies the resource. |
191 |
* @param name a "/"-separated path name that identifies the resource. |
180 |
* @return a URL for reading the resource, or <code>null</code> if |
192 |
* @return a URL for reading the resource, or <code>null</code> if |
181 |
* the resource could not be found. |
193 |
* the resource could not be found. |
182 |
* @see #findResource(String) |
194 |
* @see #findResource(String) |
183 |
*/ |
195 |
*/ |
184 |
public final URL getResource(final String name) { |
196 |
public final URL getResource(final String name) { |
185 |
zombieCheck(name); |
197 |
URL url = null; |
186 |
|
198 |
|
187 |
final int slashIdx = name.lastIndexOf('/'); |
199 |
int last = name.lastIndexOf('/'); |
188 |
if (slashIdx == -1) return null; // won't load from the default package |
200 |
String pkg = (last >= 0) ? name.substring(0, last).replace('/', '.') : ""; |
189 |
final String pkg = name.substring(0, slashIdx + 1); |
201 |
Set del = (Set)packageCoverage.get(pkg); |
190 |
|
202 |
|
191 |
if (isSpecialResource(pkg)) { |
203 |
if (del == null) { |
192 |
// Disable domain cache for this one, do a simple check. |
204 |
// uncovered package, go directly to SCL |
193 |
for (int i = 0; i < parents.length; i++) { |
205 |
url = systemCL.getResource(name); |
194 |
if (!shouldDelegateResource(pkg, parents[i])) continue; |
206 |
} else if (del.size() == 1) { |
195 |
URL u; |
207 |
// simple package coverage |
196 |
if (parents[i] instanceof ProxyClassLoader) { |
208 |
ProxyClassLoader pcl = (ProxyClassLoader)del.iterator().next(); |
197 |
u = ((ProxyClassLoader)parents[i]).findResource(name); |
209 |
if (pcl == this || parentSet.contains(pcl)) url = pcl.findResource(name); |
198 |
} else { |
210 |
} else { |
199 |
u = parents[i].getResource(name); |
211 |
// multicovered package, search in order |
|
|
212 |
for (int i=0; i<parents.length; i++) { // all our accessible parents |
213 |
if (del.contains(parents[i])) { // that cover given package |
214 |
url = parents[i].findResource(name); |
215 |
if (url != null) break; |
200 |
} |
216 |
} |
201 |
if (u != null) return u; |
|
|
202 |
} |
217 |
} |
203 |
return findResource(name); |
218 |
if (url == null && del.contains(this)) url = findResource(name); |
204 |
} |
219 |
} |
205 |
|
220 |
return url; |
206 |
ClassLoader owner = (ClassLoader)domainsByPackage.get(pkg); |
|
|
207 |
|
208 |
if (owner != null) { // known package |
209 |
// Note that shouldDelegateResource should already be true for this! |
210 |
if (owner instanceof ProxyClassLoader) { |
211 |
return ((ProxyClassLoader)owner).findResource(name); // we have its parents, skip them |
212 |
} else { |
213 |
return owner.getResource(name); // know nothing about this loader and his structure |
214 |
} |
215 |
} |
216 |
|
217 |
// virgin package |
218 |
URL retVal = null; |
219 |
for (int i = 0; i < parents.length; i++) { |
220 |
owner = parents[i]; |
221 |
if (!shouldDelegateResource(pkg, owner)) continue; |
222 |
if (owner instanceof ProxyClassLoader) { |
223 |
retVal = ((ProxyClassLoader)owner).findResource(name); // skip parents (checked already) |
224 |
} else { |
225 |
retVal = owner.getResource(name); // know nothing about this loader and his structure |
226 |
} |
227 |
if (retVal != null) { |
228 |
domainsByPackage.put(pkg, owner); |
229 |
return retVal; |
230 |
} |
231 |
} |
232 |
|
233 |
// try it ourself |
234 |
retVal = findResource(name); |
235 |
if (retVal != null) { |
236 |
domainsByPackage.put(pkg, this); |
237 |
} |
238 |
return retVal; |
239 |
} |
221 |
} |
240 |
|
222 |
|
241 |
/** This ClassLoader can't load anything itself. Subclasses |
223 |
/** This ClassLoader can't load anything itself. Subclasses |
Lines 260-285
Link Here
|
260 |
* @throws IOException if I/O errors occur |
242 |
* @throws IOException if I/O errors occur |
261 |
*/ |
243 |
*/ |
262 |
protected final synchronized Enumeration findResources(String name) throws IOException { |
244 |
protected final synchronized Enumeration findResources(String name) throws IOException { |
263 |
zombieCheck(name); |
|
|
264 |
final int slashIdx = name.lastIndexOf('/'); |
265 |
if (slashIdx == -1) return EMPTY; // won't load from the default package |
266 |
final String pkg = name.substring(0, slashIdx + 1); |
267 |
|
268 |
// Don't bother optimizing this call by domains. |
245 |
// Don't bother optimizing this call by domains. |
269 |
// It is mostly used for resources for which isSpecialResource would be true anyway. |
246 |
Enumeration[] es = new Enumeration[parents.length + 2]; |
270 |
Enumeration[] es = new Enumeration[parents.length + 1]; |
247 |
es[0] = systemCL.getResources(name); |
271 |
for (int i = 0; i < parents.length; i++) { |
248 |
for (int i = 1; i <= parents.length; i++) { |
272 |
if (!shouldDelegateResource(pkg, parents[i])) { |
249 |
es[i] = parents[i-1].simpleFindResources(name); |
273 |
es[i] = EMPTY; |
|
|
274 |
continue; |
275 |
} |
276 |
if (parents[i] instanceof ProxyClassLoader) { |
277 |
es[i] = ((ProxyClassLoader)parents[i]).simpleFindResources(name); |
278 |
} else { |
279 |
es[i] = parents[i].getResources(name); |
280 |
} |
281 |
} |
250 |
} |
282 |
es[parents.length] = simpleFindResources(name); |
251 |
es[parents.length+1] = simpleFindResources(name); |
283 |
// Should not be duplicates, assuming the parent loaders are properly distinct |
252 |
// Should not be duplicates, assuming the parent loaders are properly distinct |
284 |
// from one another and do not overlap in JAR usage, which they ought not. |
253 |
// from one another and do not overlap in JAR usage, which they ought not. |
285 |
// Anyway MetaInfServicesLookup, the most important client of this method, does |
254 |
// Anyway MetaInfServicesLookup, the most important client of this method, does |
Lines 287-301
Link Here
|
287 |
return new AAEnum (es); |
256 |
return new AAEnum (es); |
288 |
} |
257 |
} |
289 |
|
258 |
|
290 |
/** This ClassLoader can't load anything itself. Subclasses |
|
|
291 |
* may override this method to do some resource loading themselves, this |
292 |
* implementation simply delegates to findResources method of the superclass |
293 |
* that should return empty Enumeration. |
294 |
* |
295 |
* @param name the resource name |
296 |
* @return an Enumeration of URLs for the resources |
297 |
* @throws IOException if I/O errors occur |
298 |
*/ |
299 |
protected Enumeration simpleFindResources(String name) throws IOException { |
259 |
protected Enumeration simpleFindResources(String name) throws IOException { |
300 |
return super.findResources(name); |
260 |
return super.findResources(name); |
301 |
} |
261 |
} |
Lines 309-316
Link Here
|
309 |
* @return the Package corresponding to the given name, or null if not found |
269 |
* @return the Package corresponding to the given name, or null if not found |
310 |
*/ |
270 |
*/ |
311 |
protected Package getPackage(String name) { |
271 |
protected Package getPackage(String name) { |
312 |
zombieCheck(name); |
272 |
return getPackageFast(name, true); |
313 |
return getPackageFast(name, name.replace('.', '/') + '/', true); |
|
|
314 |
} |
273 |
} |
315 |
|
274 |
|
316 |
/** |
275 |
/** |
Lines 319-325
Link Here
|
319 |
* @param sname package name in org/netbeans/modules/foo/ format |
278 |
* @param sname package name in org/netbeans/modules/foo/ format |
320 |
* @param recurse whether to also ask parents |
279 |
* @param recurse whether to also ask parents |
321 |
*/ |
280 |
*/ |
322 |
protected Package getPackageFast(String name, String sname, boolean recurse) { |
281 |
protected Package getPackageFast(String name, boolean recurse) { |
323 |
synchronized (packages) { |
282 |
synchronized (packages) { |
324 |
Package pkg = (Package)packages.get(name); |
283 |
Package pkg = (Package)packages.get(name); |
325 |
if (pkg != null) { |
284 |
if (pkg != null) { |
Lines 329-343
Link Here
|
329 |
return null; |
288 |
return null; |
330 |
} |
289 |
} |
331 |
for (int i = 0; i < parents.length; i++) { |
290 |
for (int i = 0; i < parents.length; i++) { |
332 |
ClassLoader par = parents[i]; |
291 |
ProxyClassLoader par = parents[i]; |
333 |
if (par instanceof ProxyClassLoader && shouldDelegateResource(sname, par)) { |
292 |
pkg = par.getPackageFast(name, false); |
334 |
pkg = ((ProxyClassLoader)par).getPackageFast(name, sname, false); |
293 |
if (pkg != null) break; |
335 |
if (pkg != null) { |
|
|
336 |
break; |
337 |
} |
338 |
} |
339 |
} |
294 |
} |
340 |
if (pkg == null && /* #30093 */shouldDelegateResource(sname, getParent())) { |
295 |
if (pkg == null) { |
341 |
// Cannot access either Package.getSystemPackage nor ClassLoader.getPackage |
296 |
// Cannot access either Package.getSystemPackage nor ClassLoader.getPackage |
342 |
// from here, so do the best we can though it will cause unnecessary |
297 |
// from here, so do the best we can though it will cause unnecessary |
343 |
// duplication of the package cache (PCL.packages vs. CL.packages): |
298 |
// duplication of the package cache (PCL.packages vs. CL.packages): |
Lines 374-389
Link Here
|
374 |
* <code>ClassLoader</code> |
329 |
* <code>ClassLoader</code> |
375 |
*/ |
330 |
*/ |
376 |
protected synchronized Package[] getPackages() { |
331 |
protected synchronized Package[] getPackages() { |
377 |
zombieCheck(null); |
332 |
Map all = new HashMap(); // Map<String,Package> |
378 |
Map all = new HashMap(); // Map<String,Package> |
|
|
379 |
// XXX call shouldDelegateResource on each? |
333 |
// XXX call shouldDelegateResource on each? |
380 |
addPackages(all, super.getPackages()); |
334 |
addPackages(all, super.getPackages()); |
381 |
for (int i = 0; i < parents.length; i++) { |
335 |
for (int i = 0; i < parents.length; i++) { |
382 |
ClassLoader par = parents[i]; |
336 |
ProxyClassLoader par = parents[i]; |
383 |
if (par instanceof ProxyClassLoader) { |
337 |
// XXX should ideally use shouldDelegateResource here... |
384 |
// XXX should ideally use shouldDelegateResource here... |
338 |
addPackages(all, par.getPackages()); |
385 |
addPackages(all, ((ProxyClassLoader)par).getPackages()); |
|
|
386 |
} |
387 |
} |
339 |
} |
388 |
synchronized (packages) { |
340 |
synchronized (packages) { |
389 |
Iterator it = all.entrySet().iterator(); |
341 |
Iterator it = all.entrySet().iterator(); |
Lines 416-435
Link Here
|
416 |
* @return optimized list of parents (no nulls or duplicates) |
368 |
* @return optimized list of parents (no nulls or duplicates) |
417 |
* @throws IllegalArgumentException if there are cycles |
369 |
* @throws IllegalArgumentException if there are cycles |
418 |
*/ |
370 |
*/ |
419 |
private ClassLoader[] coalesceParents(ClassLoader[] loaders) throws IllegalArgumentException { |
371 |
private ProxyClassLoader[] coalesceParents(ProxyClassLoader[] loaders) throws IllegalArgumentException { |
420 |
int likelySize = loaders.length * 5 + 10; |
372 |
int likelySize = loaders.length * 5 + 10; |
421 |
Set resultingUnique = new HashSet(likelySize); // Set<ClassLoader> |
373 |
Set resultingUnique = new HashSet(likelySize); // Set<ClassLoader> |
422 |
List resulting = new ArrayList(likelySize); // List<ClassLoader> |
374 |
List resulting = new ArrayList(likelySize); // List<ClassLoader> |
423 |
for (int i = 0; i < loaders.length; i++) { |
375 |
for (int i = 0; i < loaders.length; i++) { |
424 |
addRec(resultingUnique, resulting, loaders[i]); |
376 |
addRec(resultingUnique, resulting, loaders[i]); |
425 |
} |
377 |
} |
426 |
ClassLoader[] ret = (ClassLoader[])resulting.toArray(new ClassLoader[resulting.size()]); |
378 |
ProxyClassLoader[] ret = (ProxyClassLoader[])resulting.toArray(new ProxyClassLoader[resulting.size()]); |
427 |
return ret; |
379 |
return ret; |
428 |
} |
380 |
} |
429 |
|
381 |
|
430 |
/** Coalesce a new set of loaders into the existing ones. |
382 |
/** Coalesce a new set of loaders into the existing ones. |
431 |
*/ |
383 |
*/ |
432 |
private ClassLoader[] coalesceAppend(ClassLoader[] existing, ClassLoader[] appended) throws IllegalArgumentException { |
384 |
private ProxyClassLoader[] coalesceAppend(ProxyClassLoader[] existing, ProxyClassLoader[] appended) throws IllegalArgumentException { |
433 |
int likelySize = existing.length + 3; |
385 |
int likelySize = existing.length + 3; |
434 |
Set resultingUnique = new HashSet(likelySize); |
386 |
Set resultingUnique = new HashSet(likelySize); |
435 |
List existingL = Arrays.asList(existing); |
387 |
List existingL = Arrays.asList(existing); |
Lines 443-457
Link Here
|
443 |
for (int i = 0; i < appended.length; i++) { |
395 |
for (int i = 0; i < appended.length; i++) { |
444 |
addRec(resultingUnique, resulting, appended[i]); |
396 |
addRec(resultingUnique, resulting, appended[i]); |
445 |
} |
397 |
} |
446 |
ClassLoader[] ret = (ClassLoader[])resulting.toArray(new ClassLoader[resulting.size()]); |
398 |
ProxyClassLoader[] ret = (ProxyClassLoader[])resulting.toArray(new ProxyClassLoader[resulting.size()]); |
447 |
return ret; |
399 |
return ret; |
448 |
} |
400 |
} |
449 |
|
401 |
|
450 |
private void addRec(Set resultingUnique, List resulting, ClassLoader loader) throws IllegalArgumentException { |
402 |
private void addRec(Set resultingUnique, List resulting, ProxyClassLoader loader) throws IllegalArgumentException { |
451 |
if (loader == this) throw new IllegalArgumentException("cycle in parents"); // NOI18N |
403 |
if (loader == this) throw new IllegalArgumentException("cycle in parents"); // NOI18N |
452 |
if (resultingUnique.contains(loader)) return; |
404 |
if (resultingUnique.contains(loader)) return; |
453 |
if (loader instanceof ProxyClassLoader && ((ProxyClassLoader)loader).transitive) { |
405 |
if (loader.transitive) { |
454 |
ClassLoader[] parents = ((ProxyClassLoader)loader).parents; |
406 |
ProxyClassLoader[] parents = loader.parents; |
455 |
for (int i = 0; i < parents.length; i++) { |
407 |
for (int i = 0; i < parents.length; i++) { |
456 |
addRec(resultingUnique, resulting, parents[i]); |
408 |
addRec(resultingUnique, resulting, parents[i]); |
457 |
} |
409 |
} |
Lines 460-596
Link Here
|
460 |
resulting.add(loader); |
412 |
resulting.add(loader); |
461 |
} |
413 |
} |
462 |
|
414 |
|
463 |
/** A method that finds a class either in itself or in parents. |
|
|
464 |
* It uses dual signaling for class not found: it can either return null |
465 |
* or throw CNFE itself. |
466 |
* @param name class name, e.g. "org.netbeans.modules.foo.Clazz" |
467 |
* @param fileName resource name, e.g. "org/netbeans/modules/foo/Clazz.class" |
468 |
* @param pkg package component, e.g. "org/netbeans/modules/foo/" |
469 |
* @return a class or null if not found. It can also throw an exception. |
470 |
* @throws ClassNotFoundException in case it doesn't found a class |
471 |
* and a parent eglible for loading it thrown it already. |
472 |
*/ |
473 |
private final Class smartLoadClass(String name, String fileName, String pkg) throws ClassNotFoundException { |
474 |
// First, check if the class has already been loaded |
475 |
Class c = findLoadedClass(name); |
476 |
if(c != null) return c; |
477 |
|
478 |
final ClassLoader owner = isSpecialResource(pkg) ? null : (ClassLoader)domainsByPackage.get(pkg); |
479 |
if (owner == this) { |
480 |
return simpleFindClass(name, fileName, pkg); |
481 |
} |
482 |
if (owner != null) { |
483 |
// Note that shouldDelegateResource should already be true as we hit this pkg before. |
484 |
if (owner instanceof ProxyClassLoader) { |
485 |
return ((ProxyClassLoader)owner).fullFindClass(name, fileName, pkg); |
486 |
} else { |
487 |
return owner.loadClass(name); // May throw CNFE, will be propagated |
488 |
} |
489 |
} |
490 |
|
491 |
// Virgin package, do the parent scan |
492 |
c = loadInOrder(name, fileName, pkg); |
493 |
|
494 |
if (c != null) { |
495 |
final ClassLoader owner2 = getClassClassLoader(c); // who got it? |
496 |
domainsByPackage.put(pkg, owner2); |
497 |
} |
498 |
return c; |
499 |
} |
500 |
|
501 |
// #29844 run as privileged as it may get called by loadClassInternal() used |
502 |
// during class resolving by JVM with arbitrary ProtectionDomain context stack |
503 |
private static ClassLoader getClassClassLoader(final Class c) { |
504 |
return (ClassLoader) AccessController.doPrivileged(new PrivilegedAction() { |
505 |
public Object run() { |
506 |
return c.getClassLoader(); |
507 |
} |
508 |
}); |
509 |
} |
510 |
|
511 |
private final Class loadInOrder( String name, String fileName, String pkg ) throws ClassNotFoundException { |
512 |
ClassNotFoundException cached = null; |
513 |
for (int i = 0; i < parents.length; i++) { |
514 |
ClassLoader par = parents[i]; |
515 |
if (!shouldDelegateResource(pkg, par)) continue; |
516 |
if (par instanceof ProxyClassLoader) { |
517 |
ProxyClassLoader pcl = (ProxyClassLoader)par; |
518 |
Class c = pcl.fullFindClass(name, fileName, pkg); |
519 |
// pcl might have have c in its already-loaded classes even though |
520 |
// it was not the defining class loader. In that case, if pcl was |
521 |
// not transitive (should not expose its own parents), reject this. |
522 |
if (c != null && (pcl.transitive || getClassClassLoader(c) == pcl)) return c; |
523 |
} else { |
524 |
// The following is an optimization, it should not affect semantics: |
525 |
boolean skip = false; |
526 |
if (name.startsWith("org.netbeans.") || // NOI18N |
527 |
name.startsWith("org.openide.") || // NOI18N |
528 |
name.endsWith(".Bundle") || // NOI18N |
529 |
name.endsWith("BeanInfo") || // NOI18N |
530 |
name.endsWith("Editor")) { // NOI18N |
531 |
if (par.getResource(fileName) == null) { |
532 |
// We would just throw CNFE anyway, don't bother! |
533 |
// Avg. (over ten runs after primer, w/ netbeans.close): |
534 |
// before: 13.87s after: 13.40s saved: 1.3% |
535 |
skip = true; |
536 |
} |
537 |
} |
538 |
if (!skip) { |
539 |
try { |
540 |
return par.loadClass(name); |
541 |
} catch( ClassNotFoundException cnfe ) { |
542 |
cached = cnfe; |
543 |
} |
544 |
} |
545 |
} |
546 |
} |
547 |
|
548 |
Class c = simpleFindClass(name, fileName, pkg); // Try it ourselves |
549 |
if (c != null) return c; |
550 |
if (cached != null) throw cached; |
551 |
return null; |
552 |
} |
553 |
|
554 |
private synchronized Class fullFindClass(String name, String fileName, String pkg) { |
555 |
Class c = findLoadedClass(name); |
556 |
return (c == null) ? simpleFindClass(name, fileName, pkg) : c; |
557 |
} |
558 |
|
559 |
private void addPackages(Map all, Package[] pkgs) { |
415 |
private void addPackages(Map all, Package[] pkgs) { |
560 |
// Would be easier if Package.equals() was just defined sensibly... |
416 |
// Would be easier if Package.equals() was just defined sensibly... |
561 |
for (int i = 0; i < pkgs.length; i++) { |
417 |
for (int i = 0; i < pkgs.length; i++) { |
562 |
all.put(pkgs[i].getName(), pkgs[i]); |
418 |
all.put(pkgs[i].getName(), pkgs[i]); |
563 |
} |
419 |
} |
564 |
} |
|
|
565 |
|
566 |
/** Test whether a given resource name is something that any JAR might |
567 |
* have, and for which the domain cache should be disabled. |
568 |
* The result must not change from one call to the next with the same argument. |
569 |
* By default the domain cache is disabled only for META-INF/* JAR information. |
570 |
* @param pkg the package component of the resource path ending with a slash, |
571 |
* e.g. "org/netbeans/modules/foo/" |
572 |
* @return true if it is a special resource, false for normal domain-cached resource |
573 |
* @since org.netbeans.core/1 1.3 |
574 |
*/ |
575 |
protected boolean isSpecialResource(String pkg) { |
576 |
if (pkg.startsWith("META-INF/")) return true; // NOI18N |
577 |
return false; |
578 |
} |
579 |
|
580 |
/** Test whether a given resource request (for a class or not) should be |
581 |
* searched for in the specified parent classloader or not. |
582 |
* The result must not change from one call to the next with the same arguments. |
583 |
* By default, always true. Subclasses may override to "mask" certain |
584 |
* packages from view, possibly according to the classloader chain. |
585 |
* @param pkg the package component of the resource path ending with a slash, |
586 |
* e.g. "org/netbeans/modules/foo/" |
587 |
* @param parent a classloader which is a direct or indirect parent of this one |
588 |
* @return true if the request should be delegated to this parent; false to |
589 |
* only search elsewhere (other parents, this loader's own namespace) |
590 |
* @since org.netbeans.core/1 1.3 |
591 |
*/ |
592 |
protected boolean shouldDelegateResource(String pkg, ClassLoader parent) { |
593 |
return true; |
594 |
} |
420 |
} |
595 |
|
421 |
|
596 |
|
422 |
|