Lines 18-25
Link Here
|
18 |
import java.io.IOException; |
18 |
import java.io.IOException; |
19 |
import org.openide.ErrorManager; |
19 |
import org.openide.ErrorManager; |
20 |
import org.openide.util.WeakSet; |
20 |
import org.openide.util.WeakSet; |
21 |
import org.openide.util.enum.ArrayEnumeration; |
21 |
import org.openide.util.enum.*; |
22 |
import org.openide.util.enum.SequenceEnumeration; |
|
|
23 |
|
22 |
|
24 |
/** |
23 |
/** |
25 |
* A class loader that has multiple parents and uses them for loading |
24 |
* A class loader that has multiple parents and uses them for loading |
Lines 34-39
Link Here
|
34 |
public class ProxyClassLoader extends ClassLoader { |
33 |
public class ProxyClassLoader extends ClassLoader { |
35 |
|
34 |
|
36 |
// Map<String,ClassLoader> |
35 |
// Map<String,ClassLoader> |
|
|
36 |
// packages are given in format "org/netbeans/modules/foo/" |
37 |
private final Map domainsByPackage = new HashMap(); |
37 |
private final Map domainsByPackage = new HashMap(); |
38 |
// Map<String,Package> |
38 |
// Map<String,Package> |
39 |
private HashMap packages = new HashMap(); |
39 |
private HashMap packages = new HashMap(); |
Lines 116-124
Link Here
|
116 |
protected synchronized final Class loadClass(String name, boolean resolve) |
116 |
protected synchronized final Class loadClass(String name, boolean resolve) |
117 |
throws ClassNotFoundException { |
117 |
throws ClassNotFoundException { |
118 |
zombieCheck(name); |
118 |
zombieCheck(name); |
119 |
final int dotIdx = name.lastIndexOf('.'); |
119 |
// XXX this section is a candidate for local optimization: |
120 |
if (dotIdx == -1) throw new ClassNotFoundException("Will not load classes from default package"); // NOI18N |
120 |
String filename = name.replace('.', '/').concat(".class"); // NOI18N |
121 |
Class c = smartLoadClass(name, dotIdx); |
121 |
int idx = filename.lastIndexOf('/'); // NOI18N |
|
|
122 |
if (idx == -1) throw new ClassNotFoundException("Will not load classes from default package"); // NOI18N |
123 |
String pkg = filename.substring(0, idx + 1); // "org/netbeans/modules/foo/" |
124 |
Class c = smartLoadClass(name, filename, pkg); |
122 |
if(c == null) throw new ClassNotFoundException(name); |
125 |
if(c == null) throw new ClassNotFoundException(name); |
123 |
if (resolve) resolveClass(c); |
126 |
if (resolve) resolveClass(c); |
124 |
return c; |
127 |
return c; |
Lines 160-168
Link Here
|
160 |
public final URL getResource(final String name) { |
163 |
public final URL getResource(final String name) { |
161 |
zombieCheck(name); |
164 |
zombieCheck(name); |
162 |
|
165 |
|
163 |
if (isSpecialResource(name)) { |
166 |
final int slashIdx = name.lastIndexOf('/'); |
|
|
167 |
if (slashIdx == -1) return null; // won't load from the default package |
168 |
final String pkg = name.substring(0, slashIdx + 1); |
169 |
|
170 |
if (isSpecialResource(pkg)) { |
164 |
// Disable domain cache for this one, do a simple check. |
171 |
// Disable domain cache for this one, do a simple check. |
165 |
for (int i = 0; i < parents.length; i++) { |
172 |
for (int i = 0; i < parents.length; i++) { |
|
|
173 |
if (!shouldDelegateResource(pkg, parents[i])) continue; |
166 |
URL u; |
174 |
URL u; |
167 |
if (parents[i] instanceof ProxyClassLoader) { |
175 |
if (parents[i] instanceof ProxyClassLoader) { |
168 |
u = ((ProxyClassLoader)parents[i]).findResource(name); |
176 |
u = ((ProxyClassLoader)parents[i]).findResource(name); |
Lines 174-185
Link Here
|
174 |
return findResource(name); |
182 |
return findResource(name); |
175 |
} |
183 |
} |
176 |
|
184 |
|
177 |
final int slashIdx = name.lastIndexOf('/'); |
|
|
178 |
if (slashIdx == -1) return null; // won't load from the default package |
179 |
final String pkg = name.substring(0, slashIdx); |
180 |
ClassLoader owner = (ClassLoader)domainsByPackage.get(pkg); |
185 |
ClassLoader owner = (ClassLoader)domainsByPackage.get(pkg); |
181 |
|
186 |
|
182 |
if (owner != null) { // known package |
187 |
if (owner != null) { // known package |
|
|
188 |
// Note that shouldDelegateResource should already be true for this! |
183 |
if (owner instanceof ProxyClassLoader) { |
189 |
if (owner instanceof ProxyClassLoader) { |
184 |
return ((ProxyClassLoader)owner).findResource(name); // we have its parents, skip them |
190 |
return ((ProxyClassLoader)owner).findResource(name); // we have its parents, skip them |
185 |
} else { |
191 |
} else { |
Lines 191-203
Link Here
|
191 |
URL retVal = null; |
197 |
URL retVal = null; |
192 |
for (int i = 0; i < parents.length; i++) { |
198 |
for (int i = 0; i < parents.length; i++) { |
193 |
owner = parents[i]; |
199 |
owner = parents[i]; |
|
|
200 |
if (!shouldDelegateResource(pkg, owner)) continue; |
194 |
if (owner instanceof ProxyClassLoader) { |
201 |
if (owner instanceof ProxyClassLoader) { |
195 |
retVal = ((ProxyClassLoader)owner).findResource(name); // skip parents (checked already) |
202 |
retVal = ((ProxyClassLoader)owner).findResource(name); // skip parents (checked already) |
196 |
} else { |
203 |
} else { |
197 |
retVal = owner.getResource(name); // know nothing about this loader and his structure |
204 |
retVal = owner.getResource(name); // know nothing about this loader and his structure |
198 |
} |
205 |
} |
199 |
if (retVal != null) { |
206 |
if (retVal != null) { |
200 |
domainsByPackage.put(pkg.replace('/', '.'), owner); |
|
|
201 |
domainsByPackage.put(pkg, owner); |
207 |
domainsByPackage.put(pkg, owner); |
202 |
return retVal; |
208 |
return retVal; |
203 |
} |
209 |
} |
Lines 206-212
Link Here
|
206 |
// try it ourself |
212 |
// try it ourself |
207 |
retVal = findResource(name); |
213 |
retVal = findResource(name); |
208 |
if (retVal != null) { |
214 |
if (retVal != null) { |
209 |
domainsByPackage.put(pkg.replace('/', '.'), this); |
|
|
210 |
domainsByPackage.put(pkg, this); |
215 |
domainsByPackage.put(pkg, this); |
211 |
} |
216 |
} |
212 |
return retVal; |
217 |
return retVal; |
Lines 235-244
Link Here
|
235 |
*/ |
240 |
*/ |
236 |
protected final synchronized Enumeration findResources(String name) throws IOException { |
241 |
protected final synchronized Enumeration findResources(String name) throws IOException { |
237 |
zombieCheck(name); |
242 |
zombieCheck(name); |
|
|
243 |
final int slashIdx = name.lastIndexOf('/'); |
244 |
if (slashIdx == -1) return EmptyEnumeration.EMPTY; // won't load from the default package |
245 |
final String pkg = name.substring(0, slashIdx + 1); |
246 |
|
238 |
// Don't bother optimizing this call by domains. |
247 |
// Don't bother optimizing this call by domains. |
239 |
// It is mostly used for resources for which isSpecialResource would be true anyway. |
248 |
// It is mostly used for resources for which isSpecialResource would be true anyway. |
240 |
Enumeration[] es = new Enumeration[parents.length + 1]; |
249 |
Enumeration[] es = new Enumeration[parents.length + 1]; |
241 |
for (int i = 0; i < parents.length; i++) { |
250 |
for (int i = 0; i < parents.length; i++) { |
|
|
251 |
if (!shouldDelegateResource(pkg, parents[i])) { |
252 |
es[i] = EmptyEnumeration.EMPTY; |
253 |
continue; |
254 |
} |
242 |
if (parents[i] instanceof ProxyClassLoader) { |
255 |
if (parents[i] instanceof ProxyClassLoader) { |
243 |
es[i] = ((ProxyClassLoader)parents[i]).simpleFindResources(name); |
256 |
es[i] = ((ProxyClassLoader)parents[i]).simpleFindResources(name); |
244 |
} else { |
257 |
} else { |
Lines 276-288
Link Here
|
276 |
*/ |
289 |
*/ |
277 |
protected Package getPackage(String name) { |
290 |
protected Package getPackage(String name) { |
278 |
zombieCheck(name); |
291 |
zombieCheck(name); |
|
|
292 |
|
293 |
int idx = name.lastIndexOf('.'); |
294 |
if (idx == -1) return null; |
295 |
String spkg = name.substring(0, idx + 1).replace('.', '/'); |
296 |
|
279 |
synchronized (packages) { |
297 |
synchronized (packages) { |
280 |
Package pkg = (Package)packages.get(name); |
298 |
Package pkg = (Package)packages.get(name); |
281 |
if (pkg != null) return pkg; |
299 |
if (pkg != null) return pkg; |
282 |
|
300 |
|
283 |
for (int i = 0; i < parents.length; i++) { |
301 |
for (int i = 0; i < parents.length; i++) { |
284 |
ClassLoader par = parents[i]; |
302 |
ClassLoader par = parents[i]; |
285 |
if (par instanceof ProxyClassLoader) { |
303 |
if (par instanceof ProxyClassLoader && shouldDelegateResource(spkg, par)) { |
286 |
pkg = ((ProxyClassLoader)par).getPackage(name); |
304 |
pkg = ((ProxyClassLoader)par).getPackage(name); |
287 |
if(pkg != null) break; |
305 |
if(pkg != null) break; |
288 |
} |
306 |
} |
Lines 322-327
Link Here
|
322 |
for (int i = 0; i < parents.length; i++) { |
340 |
for (int i = 0; i < parents.length; i++) { |
323 |
ClassLoader par = parents[i]; |
341 |
ClassLoader par = parents[i]; |
324 |
if (par instanceof ProxyClassLoader) { |
342 |
if (par instanceof ProxyClassLoader) { |
|
|
343 |
// XXX should ideally use shouldDelegateResource here... |
325 |
addPackages(all, ((ProxyClassLoader)par).getPackages()); |
344 |
addPackages(all, ((ProxyClassLoader)par).getPackages()); |
326 |
} |
345 |
} |
327 |
} |
346 |
} |
Lines 404-429
Link Here
|
404 |
/** A method that finds a class either in itself or in parents. |
423 |
/** A method that finds a class either in itself or in parents. |
405 |
* It uses dual signaling for class not found: it can either return null |
424 |
* It uses dual signaling for class not found: it can either return null |
406 |
* or throw CNFE itself. |
425 |
* or throw CNFE itself. |
407 |
* |
426 |
* @param name class name, e.g. "org.netbeans.modules.foo.Clazz" |
|
|
427 |
* @param fileName resource name, e.g. "org/netbeans/modules/foo/Clazz.class" |
428 |
* @param pkg package component, e.g. "org/netbeans/modules/foo/" |
408 |
* @return a class or null if not found. It can also throw an exception. |
429 |
* @return a class or null if not found. It can also throw an exception. |
409 |
* @throws ClassNotFoundException in case it doesn't found a class |
430 |
* @throws ClassNotFoundException in case it doesn't found a class |
410 |
* and a parent eglible for loading it thrown it already. |
431 |
* and a parent eglible for loading it thrown it already. |
411 |
*/ |
432 |
*/ |
412 |
private final Class smartLoadClass(String name, final int dotIdx) throws ClassNotFoundException { |
433 |
private final Class smartLoadClass(String name, String fileName, String pkg) throws ClassNotFoundException { |
413 |
// First, check if the class has already been loaded |
434 |
// First, check if the class has already been loaded |
414 |
Class c = findLoadedClass(name); |
435 |
Class c = findLoadedClass(name); |
415 |
if(c != null) return c; |
436 |
if(c != null) return c; |
416 |
|
437 |
|
417 |
// next check the package caches |
438 |
final ClassLoader owner = isSpecialResource(pkg) ? null : (ClassLoader)domainsByPackage.get(pkg); |
418 |
final String pkg = name.substring(0, dotIdx); |
439 |
if (owner == this) { |
419 |
String fileName = name.replace('.', '/').concat(".class"); // NOI18N |
440 |
return simpleFindClass(name,fileName); |
420 |
|
441 |
} |
421 |
final ClassLoader owner = isSpecialResource(fileName) ? null : (ClassLoader)domainsByPackage.get(pkg); |
442 |
if (owner != null) { |
422 |
if (owner != null) { |
443 |
// Note that shouldDelegateResource should already be true as we hit this pkg before. |
423 |
if (owner instanceof ProxyClassLoader) { |
444 |
if (owner instanceof ProxyClassLoader) { |
424 |
if(owner == this) { |
|
|
425 |
return simpleFindClass(name,fileName); |
426 |
} |
427 |
return ((ProxyClassLoader)owner).fullFindClass(name,fileName); |
445 |
return ((ProxyClassLoader)owner).fullFindClass(name,fileName); |
428 |
} else { |
446 |
} else { |
429 |
return owner.loadClass(name); // May throw CNFE, will be propagated |
447 |
return owner.loadClass(name); // May throw CNFE, will be propagated |
Lines 431-451
Link Here
|
431 |
} |
449 |
} |
432 |
|
450 |
|
433 |
// Virgin package, do the parent scan |
451 |
// Virgin package, do the parent scan |
434 |
c = loadInOrder(name,fileName); |
452 |
c = loadInOrder(name, fileName, pkg); |
435 |
|
453 |
|
436 |
if (c != null) { |
454 |
if (c != null) { |
437 |
final ClassLoader owner2 = c.getClassLoader(); // who got it? |
455 |
final ClassLoader owner2 = c.getClassLoader(); // who got it? |
438 |
domainsByPackage.put(pkg, owner2); |
456 |
domainsByPackage.put(pkg, owner2); |
439 |
domainsByPackage.put(pkg.replace('.', '/'), owner2); |
|
|
440 |
} |
457 |
} |
441 |
return c; |
458 |
return c; |
442 |
} |
459 |
} |
443 |
|
460 |
|
444 |
|
461 |
|
445 |
private final Class loadInOrder( String name, String fileName ) throws ClassNotFoundException { |
462 |
private final Class loadInOrder( String name, String fileName, String pkg ) throws ClassNotFoundException { |
446 |
ClassNotFoundException cached = null; |
463 |
ClassNotFoundException cached = null; |
447 |
for (int i = 0; i < parents.length; i++) { |
464 |
for (int i = 0; i < parents.length; i++) { |
448 |
ClassLoader par = parents[i]; |
465 |
ClassLoader par = parents[i]; |
|
|
466 |
if (!shouldDelegateResource(pkg, par)) continue; |
449 |
if (par instanceof ProxyClassLoader) { |
467 |
if (par instanceof ProxyClassLoader) { |
450 |
Class c = ((ProxyClassLoader)par).fullFindClass(name,fileName); |
468 |
Class c = ((ProxyClassLoader)par).fullFindClass(name,fileName); |
451 |
if (c != null) return c; |
469 |
if (c != null) return c; |
Lines 478-495
Link Here
|
478 |
|
496 |
|
479 |
/** Test whether a given resource name is something that any JAR might |
497 |
/** Test whether a given resource name is something that any JAR might |
480 |
* have, and for which the domain cache should be disabled. |
498 |
* have, and for which the domain cache should be disabled. |
481 |
* @param the tested resource name |
499 |
* The result must not change from one call to the next with the same argument. |
|
|
500 |
* By default the domain cache is disabled only for META-INF/* JAR information. |
501 |
* @param pkg the package component of the resource path ending with a slash, |
502 |
* e.g. "org/netbeans/modules/foo/" |
482 |
* @return true if it is a special resource, false for normal domain-cached resource |
503 |
* @return true if it is a special resource, false for normal domain-cached resource |
|
|
504 |
* @since org.netbeans.core/1 1.3 |
483 |
*/ |
505 |
*/ |
484 |
protected boolean isSpecialResource(String name) { |
506 |
protected boolean isSpecialResource(String pkg) { |
485 |
if (name.startsWith("META-INF/")) return true; // NOI18N |
507 |
if (pkg.startsWith("META-INF/")) return true; // NOI18N |
486 |
// XXX fix later! |
|
|
487 |
// Currently ICE browser JAR includes a copy of many XML classes, including |
488 |
// two classes in org.w3c.dom.events which are not in Xerces for some reason. |
489 |
// This is a temporary workaround to permit these two classes to be loaded |
490 |
// by the ICE browser even though there is package skew going on. |
491 |
if (name.startsWith("org/w3c/dom/events/")) return true; // NOI18N |
492 |
return false; |
508 |
return false; |
|
|
509 |
} |
510 |
|
511 |
/** Test whether a given resource request (for a class or not) should be |
512 |
* searched for in the specified parent classloader or not. |
513 |
* The result must not change from one call to the next with the same arguments. |
514 |
* By default, always true. Subclasses may override to "mask" certain |
515 |
* packages from view, possibly according to the classloader chain. |
516 |
* @param pkg the package component of the resource path ending with a slash, |
517 |
* e.g. "org/netbeans/modules/foo/" |
518 |
* @param parent a classloader which is a direct or indirect parent of this one |
519 |
* @return true if the request should be delegated to this parent; false to |
520 |
* only search elsewhere (other parents, this loader's own namespace) |
521 |
* @since org.netbeans.core/1 1.3 |
522 |
*/ |
523 |
protected boolean shouldDelegateResource(String pkg, ClassLoader parent) { |
524 |
return true; |
493 |
} |
525 |
} |
494 |
|
526 |
|
495 |
} |
527 |
} |