KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > eclipse > osgi > framework > internal > core > BundleLoader


1 /*******************************************************************************
2  * Copyright (c) 2004, 2006 IBM Corporation and others.
3  * All rights reserved. This program and the accompanying materials
4  * are made available under the terms of the Eclipse Public License v1.0
5  * which accompanies this distribution, and is available at
6  * http://www.eclipse.org/legal/epl-v10.html
7  *
8  * Contributors:
9  * IBM Corporation - initial API and implementation
10  *******************************************************************************/

11
12 package org.eclipse.osgi.framework.internal.core;
13
14 import java.io.IOException JavaDoc;
15 import java.net.URL JavaDoc;
16 import java.security.AccessController JavaDoc;
17 import java.security.PrivilegedAction JavaDoc;
18 import java.util.*;
19 import org.eclipse.osgi.framework.adaptor.*;
20 import org.eclipse.osgi.framework.debug.Debug;
21 import org.eclipse.osgi.framework.util.KeyedHashSet;
22 import org.eclipse.osgi.service.resolver.*;
23 import org.eclipse.osgi.util.ManifestElement;
24 import org.osgi.framework.BundleException;
25 import org.osgi.framework.FrameworkEvent;
26
27 /**
28  * This object is responsible for all classloader delegation for a bundle.
29  * It represents the loaded state of the bundle. BundleLoader objects
30  * are created lazily; care should be taken not to force the creation
31  * of a BundleLoader unless it is necessary.
32  * @see org.eclipse.osgi.framework.internal.core.BundleLoaderProxy
33  */

34 public class BundleLoader implements ClassLoaderDelegate {
35     public final static String JavaDoc DEFAULT_PACKAGE = "."; //$NON-NLS-1$
36
public final static String JavaDoc JAVA_PACKAGE = "java."; //$NON-NLS-1$
37
public final static byte FLAG_IMPORTSINIT = 0x01;
38     public final static byte FLAG_HASDYNAMICIMPORTS = 0x02;
39     public final static byte FLAG_HASDYNAMICEIMPORTALL = 0x04;
40     public final static byte FLAG_CLOSED = 0x08;
41
42     public final static ClassContext CLASS_CONTEXT = (ClassContext) AccessController.doPrivileged(new PrivilegedAction JavaDoc() {
43         public Object JavaDoc run() {
44             return new ClassContext();
45         }
46     });
47     public final static ClassLoader JavaDoc FW_CLASSLOADER = getClassLoader(Framework.class);
48
49     private static final boolean USE_GLOBAL_DEADLOCK_AVOIDANCE_LOCK = "true".equals(FrameworkProperties.getProperty("osgi.classloader.singleThreadLoads")); //$NON-NLS-1$//$NON-NLS-2$
50
private static final List waitingList = USE_GLOBAL_DEADLOCK_AVOIDANCE_LOCK ? new ArrayList(0) : null;
51     private static Object JavaDoc lockThread;
52     private static int lockCount = 0;
53
54     /* the proxy */
55     final private BundleLoaderProxy proxy;
56     /* Bundle object */
57     final BundleHost bundle;
58     final private PolicyHandler policy;
59     /* List of package names that are exported by this BundleLoader */
60     final private Collection exportedPackages;
61     /* List of required bundle BundleLoaderProxy objects */
62     final BundleLoaderProxy[] requiredBundles;
63     /* List of indexes into the requiredBundles list of reexported bundles */
64     final int[] reexportTable;
65     /* cache of required package sources. Key is packagename, value is PackageSource */
66     final private KeyedHashSet requiredSources;
67
68     // note that the following non-final must be access using synchronization
69
/* cache of imported packages. Key is packagename, Value is PackageSource */
70     private KeyedHashSet importedSources;
71     /* If not null, list of package stems to import dynamically. */
72     private String JavaDoc[] dynamicImportPackageStems;
73     /* If not null, list of package names to import dynamically. */
74     private String JavaDoc[] dynamicImportPackages;
75     /* loader flags */
76     private byte loaderFlags = 0;
77     /* The is the BundleClassLoader for the bundle */
78     private BundleClassLoader classloader;
79     private ClassLoader JavaDoc parent;
80
81     /**
82      * Returns the package name from the specified class name.
83      * The returned package is dot seperated.
84      *
85      * @param name Name of a class.
86      * @return Dot separated package name or null if the class
87      * has no package name.
88      */

89     public final static String JavaDoc getPackageName(String JavaDoc name) {
90         if (name != null) {
91             int index = name.lastIndexOf('.'); /* find last period in class name */
92             if (index > 0)
93                 return name.substring(0, index);
94         }
95         return DEFAULT_PACKAGE;
96     }
97
98     /**
99      * Returns the package name from the specified resource name.
100      * The returned package is dot seperated.
101      *
102      * @param name Name of a resource.
103      * @return Dot separated package name or null if the resource
104      * has no package name.
105      */

106     public final static String JavaDoc getResourcePackageName(String JavaDoc name) {
107         if (name != null) {
108             /* check for leading slash*/
109             int begin = ((name.length() > 1) && (name.charAt(0) == '/')) ? 1 : 0;
110             int end = name.lastIndexOf('/'); /* index of last slash */
111             if (end > begin)
112                 return name.substring(begin, end).replace('/', '.');
113         }
114         return DEFAULT_PACKAGE;
115     }
116
117     /**
118      * BundleLoader runtime constructor. This object is created lazily
119      * when the first request for a resource is made to this bundle.
120      *
121      * @param bundle Bundle object for this loader.
122      * @param proxy the BundleLoaderProxy for this loader.
123      * @exception org.osgi.framework.BundleException
124      */

125     protected BundleLoader(BundleHost bundle, BundleLoaderProxy proxy) throws BundleException {
126         this.bundle = bundle;
127         this.proxy = proxy;
128         try {
129             bundle.getBundleData().open(); /* make sure the BundleData is open */
130         } catch (IOException JavaDoc e) {
131             throw new BundleException(Msg.BUNDLE_READ_EXCEPTION, e);
132         }
133         BundleDescription description = proxy.getBundleDescription();
134         // init the require bundles list.
135
BundleDescription[] required = description.getResolvedRequires();
136         if (required.length > 0) {
137             // get a list of re-exported symbolic names
138
HashSet reExportSet = new HashSet(required.length);
139             BundleSpecification[] requiredSpecs = description.getRequiredBundles();
140             if (requiredSpecs != null && requiredSpecs.length > 0)
141                 for (int i = 0; i < requiredSpecs.length; i++)
142                     if (requiredSpecs[i].isExported())
143                         reExportSet.add(requiredSpecs[i].getName());
144
145             requiredBundles = new BundleLoaderProxy[required.length];
146             int[] reexported = new int[required.length];
147             int reexportIndex = 0;
148             for (int i = 0; i < required.length; i++) {
149                 requiredBundles[i] = getLoaderProxy(required[i]);
150                 if (reExportSet.contains(required[i].getSymbolicName()))
151                     reexported[reexportIndex++] = i;
152             }
153             if (reexportIndex > 0) {
154                 reexportTable = new int[reexportIndex];
155                 System.arraycopy(reexported, 0, reexportTable, 0, reexportIndex);
156             } else {
157                 reexportTable = null;
158             }
159             requiredSources = new KeyedHashSet(10, false);
160         } else {
161             requiredBundles = null;
162             reexportTable = null;
163             requiredSources = null;
164         }
165
166         // init the provided packages set
167
ExportPackageDescription[] exports = description.getSelectedExports();
168         if (exports != null && exports.length > 0) {
169             exportedPackages = exports.length > 10 ? (Collection) new HashSet(exports.length) : new ArrayList(exports.length);
170             for (int i = 0; i < exports.length; i++) {
171                 if (proxy.forceSourceCreation(exports[i])) {
172                     if (!exportedPackages.contains(exports[i].getName())) {
173                         // must force filtered and reexport sources to be created early
174
// to prevent lazy normal package source creation.
175
// We only do this for the first export of a package name.
176
proxy.createPackageSource(exports[i], true);
177                     }
178                 }
179                 exportedPackages.add(exports[i].getName());
180             }
181         } else {
182             exportedPackages = null;
183         }
184         //This is the fastest way to access to the description for fragments since the hostdescription.getFragments() is slow
185
org.osgi.framework.Bundle[] fragmentObjects = bundle.getFragments();
186         BundleDescription[] fragments = new BundleDescription[fragmentObjects == null ? 0 : fragmentObjects.length];
187         for (int i = 0; i < fragments.length; i++)
188             fragments[i] = ((AbstractBundle) fragmentObjects[i]).getBundleDescription();
189         // init the dynamic imports tables
190
if (description.hasDynamicImports())
191             addDynamicImportPackage(description.getImportPackages());
192         // ...and its fragments
193
for (int i = 0; i < fragments.length; i++)
194             if (fragments[i].isResolved() && fragments[i].hasDynamicImports())
195                 addDynamicImportPackage(fragments[i].getImportPackages());
196
197         //Initialize the policy handler
198
String JavaDoc buddyList = null;
199         try {
200             buddyList = (String JavaDoc) bundle.getBundleData().getManifest().get(Constants.BUDDY_LOADER);
201         } catch (BundleException e) {
202             // do nothing; buddyList == null
203
}
204         policy = buddyList != null ? new PolicyHandler(this, buddyList) : null;
205     }
206
207     private synchronized KeyedHashSet getImportedSources() {
208         if ((loaderFlags & FLAG_IMPORTSINIT) != 0)
209             return importedSources;
210         ExportPackageDescription[] packages = proxy.getBundleDescription().getResolvedImports();
211         if (packages != null && packages.length > 0) {
212             if (importedSources == null)
213                 importedSources = new KeyedHashSet(packages.length, false);
214             for (int i = 0; i < packages.length; i++) {
215                 PackageSource source = createExportPackageSource(packages[i]);
216                 if (source != null)
217                     importedSources.add(source);
218             }
219         }
220         loaderFlags |= FLAG_IMPORTSINIT;
221         return importedSources;
222     }
223
224     final PackageSource createExportPackageSource(ExportPackageDescription export) {
225         BundleLoaderProxy exportProxy = getLoaderProxy(export.getExporter());
226         if (exportProxy == null)
227             // TODO log error!!
228
return null;
229         PackageSource requiredSource = exportProxy.getBundleLoader().findRequiredSource(export.getName());
230         PackageSource exportSource = exportProxy.createPackageSource(export, false);
231         if (requiredSource == null)
232             return exportSource;
233         return createMultiSource(export.getName(), new PackageSource[] {requiredSource, exportSource});
234     }
235
236     private static PackageSource createMultiSource(String JavaDoc packageName, PackageSource[] sources) {
237         if (sources.length == 1)
238             return sources[0];
239         ArrayList sourceList = new ArrayList(sources.length);
240         for (int i = 0; i < sources.length; i++) {
241             SingleSourcePackage[] innerSources = sources[i].getSuppliers();
242             for (int j = 0; j < innerSources.length; j++)
243                 if (!sourceList.contains(innerSources[j]))
244                     sourceList.add(innerSources[j]);
245         }
246         return new MultiSourcePackage(packageName, (SingleSourcePackage[]) sourceList.toArray(new SingleSourcePackage[sourceList.size()]));
247     }
248
249     /*
250      * get the loader proxy for a bundle description
251      */

252     final BundleLoaderProxy getLoaderProxy(BundleDescription source) {
253         BundleLoaderProxy sourceProxy = (BundleLoaderProxy) source.getUserObject();
254         if (sourceProxy == null) {
255             // may need to force the proxy to be created
256
long exportingID = source.getBundleId();
257             BundleHost exportingBundle = (BundleHost) bundle.framework.getBundle(exportingID);
258             if (exportingBundle == null)
259                 return null;
260             sourceProxy = exportingBundle.getLoaderProxy();
261         }
262         return sourceProxy;
263     }
264
265     /*
266      * Close the the BundleLoader.
267      *
268      */

269     synchronized void close() {
270         if ((loaderFlags & FLAG_CLOSED) != 0)
271             return;
272         if (classloader != null)
273             classloader.close();
274         if (policy != null)
275             policy.close();
276         loaderFlags |= FLAG_CLOSED; /* This indicates the BundleLoader is destroyed */
277     }
278
279     /**
280      * This method loads a class from the bundle. The class is searched for in the
281      * same manner as it would if it was being loaded from a bundle (i.e. all
282      * hosts, fragments, import, required bundles and local resources are searched.
283      *
284      * @param name the name of the desired Class.
285      * @return the resulting Class
286      * @exception java.lang.ClassNotFoundException if the class definition was not found.
287      */

288     final Class JavaDoc loadClass(String JavaDoc name) throws ClassNotFoundException JavaDoc {
289         return createClassLoader().loadClass(name);
290     }
291
292     /**
293      * This method gets a resource from the bundle. The resource is searched
294      * for in the same manner as it would if it was being loaded from a bundle
295      * (i.e. all hosts, fragments, import, required bundles and
296      * local resources are searched).
297      *
298      * @param name the name of the desired resource.
299      * @return the resulting resource URL or null if it does not exist.
300      */

301     final URL JavaDoc getResource(String JavaDoc name) {
302         return createClassLoader().getResource(name);
303     }
304
305     final synchronized ClassLoader JavaDoc getParentClassLoader() {
306         if (parent != null)
307             return parent;
308         createClassLoader();
309         return parent;
310     }
311
312     final synchronized BundleClassLoader createClassLoader() {
313         if (classloader != null)
314             return classloader;
315         try {
316             String JavaDoc[] classpath = bundle.getBundleData().getClassPath();
317             if (classpath != null) {
318                 BundleClassLoader bcl = createBCLPrevileged(bundle.getProtectionDomain(), classpath);
319                 parent = getParentPrivileged(bcl);
320                 classloader = bcl;
321             } else {
322                 bundle.framework.publishFrameworkEvent(FrameworkEvent.ERROR, bundle, new BundleException(Msg.BUNDLE_NO_CLASSPATH_MATCH));
323             }
324         } catch (BundleException e) {
325             bundle.framework.publishFrameworkEvent(FrameworkEvent.ERROR, bundle, e);
326         }
327         return classloader;
328     }
329
330     /**
331      * Finds a class local to this bundle. Only the classloader for this bundle is searched.
332      * @param name The name of the class to find.
333      * @return The loaded Class or null if the class is not found.
334      * @throws ClassNotFoundException
335      */

336     Class JavaDoc findLocalClass(String JavaDoc name) throws ClassNotFoundException JavaDoc {
337         if (Debug.DEBUG && Debug.DEBUG_LOADER)
338             Debug.println("BundleLoader[" + this + "].findLocalClass(" + name + ")"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
339
try {
340             Class JavaDoc clazz = createClassLoader().findLocalClass(name);
341             if (Debug.DEBUG && Debug.DEBUG_LOADER && clazz != null)
342                 Debug.println("BundleLoader[" + this + "] found local class " + name); //$NON-NLS-1$ //$NON-NLS-2$
343
return clazz;
344         } catch (ClassNotFoundException JavaDoc e) {
345             if (e instanceof StatusException) {
346                 if ((((StatusException) e).getStatusCode() & StatusException.CODE_ERROR) != 0)
347                     throw e;
348             }
349             return null;
350         }
351     }
352
353     /**
354      * Finds the class for a bundle. This method is used for delegation by the bundle's classloader.
355      */

356     public Class JavaDoc findClass(String JavaDoc name) throws ClassNotFoundException JavaDoc {
357         return findClass(name, true);
358     }
359
360     Class JavaDoc findClass(String JavaDoc name, boolean checkParent) throws ClassNotFoundException JavaDoc {
361         ClassLoader JavaDoc parentCL = getParentClassLoader();
362         if (checkParent && parentCL != null && name.startsWith(JAVA_PACKAGE))
363             // 1) if startsWith "java." delegate to parent and terminate search
364
// we want to throw ClassNotFoundExceptions if a java.* class cannot be loaded from the parent.
365
return parentCL.loadClass(name);
366         try {
367             if (USE_GLOBAL_DEADLOCK_AVOIDANCE_LOCK)
368                 lock(createClassLoader());
369             return findClassInternal(name, checkParent, parentCL);
370         } finally {
371             if (USE_GLOBAL_DEADLOCK_AVOIDANCE_LOCK)
372                 unlock();
373         }
374     }
375
376     private Class JavaDoc findClassInternal(String JavaDoc name, boolean checkParent, ClassLoader JavaDoc parentCL) throws ClassNotFoundException JavaDoc {
377         if (Debug.DEBUG && Debug.DEBUG_LOADER)
378             Debug.println("BundleLoader[" + this + "].loadBundleClass(" + name + ")"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
379
String JavaDoc pkgName = getPackageName(name);
380         boolean bootDelegation = false;
381         // follow the OSGi delegation model
382
if (checkParent && parentCL != null && isBootDelegationPackage(pkgName))
383             // 2) if part of the bootdelegation list then delegate to parent and continue of failure
384
try {
385                 return parentCL.loadClass(name);
386             } catch (ClassNotFoundException JavaDoc cnfe) {
387                 // we want to continue
388
bootDelegation = true;
389             }
390
391         Class JavaDoc result = null;
392         // 3) search the imported packages
393
PackageSource source = findImportedSource(pkgName);
394         if (source != null) {
395             // 3) found import source terminate search at the source
396
result = source.loadClass(name);
397             if (result != null)
398                 return result;
399             throw new ClassNotFoundException JavaDoc(name);
400         }
401         // 4) search the required bundles
402
source = findRequiredSource(pkgName);
403         if (source != null)
404             // 4) attempt to load from source but continue on failure
405
result = source.loadClass(name);
406         // 5) search the local bundle
407
if (result == null)
408             result = findLocalClass(name);
409         if (result != null)
410             return result;
411         // 6) attempt to find a dynamic import source; only do this if a required source was not found
412
if (source == null) {
413             source = findDynamicSource(pkgName);
414             if (source != null) {
415                 result = source.loadClass(name);
416                 if (result != null)
417                     return result;
418                 // must throw CNFE if dynamic import source does not have the class
419
throw new ClassNotFoundException JavaDoc(name);
420             }
421         }
422
423         // do buddy policy loading
424
if (result == null && policy != null)
425             result = policy.doBuddyClassLoading(name);
426         // hack to support backwards compatibiility for bootdelegation
427
if (parentCL != null && checkParent && !bootDelegation && bundle.framework.compatibiltyBootDelegation && result == null && source == null && !isExportedPackage(pkgName))
428             // we don't need to continue if a CNFE is thrown here.
429
return parentCL.loadClass(name);
430         // last resort; do class context trick to work around VM bugs
431
if (parentCL != null && result == null && !bootDelegation && isRequestFromVM())
432             result = parentCL.loadClass(name);
433         if (result == null)
434             throw new ClassNotFoundException JavaDoc(name);
435         return result;
436     }
437
438     private boolean isRequestFromVM() {
439         if (bundle.framework.bootDelegateAll || !bundle.framework.contextBootDelegation)
440             return false;
441         // works around VM bugs that require all classloaders to have access to parent packages
442
Class JavaDoc[] context = CLASS_CONTEXT.getClassContext();
443         if (context == null || context.length < 2)
444             return false;
445         // skip the first class; it is the ClassContext class
446
for (int i = 1; i < context.length; i++)
447             // find the first class in the context which is not BundleLoader or instanceof ClassLoader
448
if (context[i] != BundleLoader.class && !ClassLoader JavaDoc.class.isAssignableFrom(context[i])) {
449                 // only find in parent if the class is not "Class" (Class#forName case) or if the class is not loaded with a BundleClassLoader
450
ClassLoader JavaDoc cl = getClassLoader(context[i]);
451                 if (cl != FW_CLASSLOADER) { // extra check incase an adaptor adds another class into the stack besides an instance of ClassLoader
452
if (Class JavaDoc.class != context[i] && !(cl instanceof BundleClassLoader))
453                         return true;
454                     break;
455                 }
456             }
457         return false;
458     }
459
460     private static ClassLoader JavaDoc getClassLoader(final Class JavaDoc clazz) {
461         if (System.getSecurityManager() == null)
462             return clazz.getClassLoader();
463         return (ClassLoader JavaDoc) AccessController.doPrivileged(new PrivilegedAction JavaDoc() {
464             public Object JavaDoc run() {
465                 return clazz.getClassLoader();
466             }
467         });
468     }
469
470     /**
471      * Finds the resource for a bundle. This method is used for delegation by the bundle's classloader.
472      */

473     public URL JavaDoc findResource(String JavaDoc name) {
474         return findResource(name, true);
475     }
476
477     URL JavaDoc findResource(String JavaDoc name, boolean checkParent) {
478         if ((name.length() > 1) && (name.charAt(0) == '/')) /* if name has a leading slash */
479             name = name.substring(1); /* remove leading slash before search */
480         String JavaDoc pkgName = getResourcePackageName(name);
481         boolean bootDelegation = false;
482         ClassLoader JavaDoc parentCL = getParentClassLoader();
483         // follow the OSGi delegation model
484
// First check the parent classloader for system resources, if it is a java resource.
485
if (checkParent && parentCL != null) {
486             if (pkgName.startsWith(JAVA_PACKAGE))
487                 // 1) if startsWith "java." delegate to parent and terminate search
488
// we never delegate java resource requests past the parent
489
return parentCL.getResource(name);
490             else if (isBootDelegationPackage(pkgName)) {
491                 // 2) if part of the bootdelegation list then delegate to parent and continue of failure
492
URL JavaDoc result = parentCL.getResource(name);
493                 if (result != null)
494                     return result;
495                 bootDelegation = true;
496             }
497         }
498
499         URL JavaDoc result = null;
500         // 3) search the imported packages
501
PackageSource source = findImportedSource(pkgName);
502         if (source != null)
503             // 3) found import source terminate search at the source
504
return source.getResource(name);
505         // 4) search the required bundles
506
source = findRequiredSource(pkgName);
507         if (source != null)
508             // 4) attempt to load from source but continue on failure
509
result = source.getResource(name);
510         // 5) search the local bundle
511
if (result == null)
512             result = findLocalResource(name);
513         if (result != null)
514             return result;
515         // 6) attempt to find a dynamic import source; only do this if a required source was not found
516
if (source == null) {
517             source = findDynamicSource(pkgName);
518             if (source != null)
519                 // must return the result of the dynamic import and do not continue
520
return source.getResource(name);
521         }
522
523         // do buddy policy loading
524
if (result == null && policy != null)
525             result = policy.doBuddyResourceLoading(name);
526         // hack to support backwards compatibiility for bootdelegation
527
if (parentCL != null && checkParent && !bootDelegation && bundle.framework.compatibiltyBootDelegation && result == null && source == null && !isExportedPackage(pkgName))
528             // we don't need to continue if the resource is not found here
529
return parentCL.getResource(name);
530         // last resort; do class context trick to work around VM bugs
531
if (parentCL != null && result == null && !bootDelegation && isRequestFromVM())
532             result = parentCL.getResource(name);
533         return result;
534     }
535
536     boolean isBootDelegationPackage(String JavaDoc name) {
537         if (bundle.framework.bootDelegateAll)
538             return true;
539         if (bundle.framework.bootDelegation != null)
540             for (int i = 0; i < bundle.framework.bootDelegation.length; i++)
541                 if (name.equals(bundle.framework.bootDelegation[i]))
542                     return true;
543         if (bundle.framework.bootDelegationStems != null)
544             for (int i = 0; i < bundle.framework.bootDelegationStems.length; i++)
545                 if (name.startsWith(bundle.framework.bootDelegationStems[i]))
546                     return true;
547         return false;
548     }
549
550     /**
551      * Finds the resources for a bundle. This method is used for delegation by the bundle's classloader.
552      */

553     public Enumeration findResources(String JavaDoc name) throws IOException JavaDoc {
554         // do not delegate to parent because ClassLoader#getResources already did and it is final!!
555
if ((name.length() > 1) && (name.charAt(0) == '/')) /* if name has a leading slash */
556             name = name.substring(1); /* remove leading slash before search */
557         String JavaDoc pkgName = getResourcePackageName(name);
558         Enumeration result = null;
559         // start at step 3 because of the comment above about ClassLoader#getResources
560
// 3) search the imported packages
561
PackageSource source = findImportedSource(pkgName);
562         if (source != null)
563             // 3) found import source terminate search at the source
564
return source.getResources(name);
565         // 4) search the required bundles
566
source = findRequiredSource(pkgName);
567         if (source != null)
568             // 4) attempt to load from source but continue on failure
569
result = source.getResources(name);
570
571         // 5) search the local bundle
572
// compound the required source results with the local ones
573
Enumeration localResults = findLocalResources(name);
574         result = compoundEnumerations(result, localResults);
575         // 6) attempt to find a dynamic import source; only do this if a required source was not found
576
if (result == null && source == null) {
577             source = findDynamicSource(pkgName);
578             if (source != null)
579                 return source.getResources(name);
580         }
581         if (policy != null) {
582             Enumeration buddyResult = policy.doBuddyResourcesLoading(name);
583             result = compoundEnumerations(result, buddyResult);
584         }
585         return result;
586     }
587
588     /*
589      * This method is used by Bundle.getResources to do proper parent delegation.
590      */

591     Enumeration getResources(String JavaDoc name) throws IOException JavaDoc {
592         if ((name.length() > 1) && (name.charAt(0) == '/')) /* if name has a leading slash */
593             name = name.substring(1); /* remove leading slash before search */
594         String JavaDoc pkgName = getResourcePackageName(name);
595         // follow the OSGi delegation model
596
// First check the parent classloader for system resources, if it is a java resource.
597
Enumeration result = null;
598         if (pkgName.startsWith(JAVA_PACKAGE) || isBootDelegationPackage(pkgName)) {
599             // 1) if startsWith "java." delegate to parent and terminate search
600
// 2) if part of the bootdelegation list then delegate to parent and continue of failure
601
ClassLoader JavaDoc parentCL = getParentClassLoader();
602             result = parentCL == null ? null : parentCL.getResources(name);
603             if (pkgName.startsWith(JAVA_PACKAGE))
604                 return result;
605         }
606         return compoundEnumerations(result, findResources(name));
607     }
608
609     static Enumeration compoundEnumerations(Enumeration list1, Enumeration list2) {
610         if (list2 == null || !list2.hasMoreElements())
611             return list1;
612         if (list1 == null || !list1.hasMoreElements())
613             return list2;
614         Vector compoundResults = new Vector();
615         while (list1.hasMoreElements())
616             compoundResults.add(list1.nextElement());
617         while (list2.hasMoreElements()) {
618             Object JavaDoc item = list2.nextElement();
619             if (!compoundResults.contains(item)) //don't add duplicates
620
compoundResults.add(item);
621         }
622         return compoundResults.elements();
623     }
624
625     /**
626      * Finds a resource local to this bundle. Only the classloader for this bundle is searched.
627      * @param name The name of the resource to find.
628      * @return The URL to the resource or null if the resource is not found.
629      */

630     URL JavaDoc findLocalResource(final String JavaDoc name) {
631         return createClassLoader().findLocalResource(name);
632     }
633
634     /**
635      * Returns an Enumeration of URLs representing all the resources with
636      * the given name. Only the classloader for this bundle is searched.
637      *
638      * @param name the resource name
639      * @return an Enumeration of URLs for the resources
640      */

641     Enumeration findLocalResources(String JavaDoc name) {
642         return createClassLoader().findLocalResources(name);
643     }
644
645     /**
646      * Returns the absolute path name of a native library.
647      *
648      * @param name the library name
649      * @return the absolute path of the native library or null if not found
650      */

651     public String JavaDoc findLibrary(final String JavaDoc name) {
652         if (System.getSecurityManager() == null)
653             return findLocalLibrary(name);
654         return (String JavaDoc) AccessController.doPrivileged(new PrivilegedAction JavaDoc() {
655             public Object JavaDoc run() {
656                 return findLocalLibrary(name);
657             }
658         });
659     }
660
661     final String JavaDoc findLocalLibrary(final String JavaDoc name) {
662         String JavaDoc result = bundle.getBundleData().findLibrary(name);
663         if (result != null)
664             return result;
665
666         org.osgi.framework.Bundle[] fragments = bundle.getFragments();
667         if (fragments == null || fragments.length == 0)
668             return null;
669
670         // look in fragments imports ...
671
for (int i = 0; i < fragments.length; i++) {
672             result = ((AbstractBundle) fragments[i]).getBundleData().findLibrary(name);
673             if (result != null)
674                 return result;
675         }
676         return result;
677     }
678
679     /*
680      * Return the bundle we are associated with.
681      */

682     final AbstractBundle getBundle() {
683         return bundle;
684     }
685
686     private BundleClassLoader createBCLPrevileged(final BundleProtectionDomain pd, final String JavaDoc[] cp) {
687         // Create the classloader as previleged code if security manager is present.
688
if (System.getSecurityManager() == null)
689             return createBCL(pd, cp);
690
691         return (BundleClassLoader) AccessController.doPrivileged(new PrivilegedAction JavaDoc() {
692             public Object JavaDoc run() {
693                 return createBCL(pd, cp);
694             }
695         });
696
697     }
698
699     BundleClassLoader createBCL(final BundleProtectionDomain pd, final String JavaDoc[] cp) {
700         BundleClassLoader bcl = bundle.getBundleData().createClassLoader(BundleLoader.this, pd, cp);
701         // attach existing fragments to classloader
702
org.osgi.framework.Bundle[] fragments = bundle.getFragments();
703         if (fragments != null)
704             for (int i = 0; i < fragments.length; i++) {
705                 AbstractBundle fragment = (AbstractBundle) fragments[i];
706                 try {
707                     bcl.attachFragment(fragment.getBundleData(), fragment.domain, fragment.getBundleData().getClassPath());
708                 } catch (BundleException be) {
709                     bundle.framework.publishFrameworkEvent(FrameworkEvent.ERROR, bundle, be);
710                 }
711             }
712
713         // finish the initialization of the classloader.
714
bcl.initialize();
715         return bcl;
716     }
717
718     /**
719      * Return a string representation of this loader.
720      * @return String
721      */

722     public final String JavaDoc toString() {
723         BundleData result = bundle.getBundleData();
724         return result == null ? "BundleLoader.bundledata == null!" : result.toString(); //$NON-NLS-1$
725
}
726
727     /**
728      * Return true if the target package name matches
729      * a name in the DynamicImport-Package manifest header.
730      *
731      * @param pkgname The name of the requested class' package.
732      * @return true if the package should be imported.
733      */

734     private final synchronized boolean isDynamicallyImported(String JavaDoc pkgname) {
735         if (this instanceof SystemBundleLoader)
736             return false; // system bundle cannot dynamically import
737
// must check for startsWith("java.") to satisfy R3 section 4.7.2
738
if (pkgname.startsWith("java.")) //$NON-NLS-1$
739
return true;
740
741         /* quick shortcut check */
742         if ((loaderFlags & FLAG_HASDYNAMICIMPORTS) == 0)
743             return false;
744
745         /* "*" shortcut */
746         if ((loaderFlags & FLAG_HASDYNAMICEIMPORTALL) != 0)
747             return true;
748
749         /* match against specific names */
750         if (dynamicImportPackages != null)
751             for (int i = 0; i < dynamicImportPackages.length; i++)
752                 if (pkgname.equals(dynamicImportPackages[i]))
753                     return true;
754
755         /* match against names with trailing wildcards */
756         if (dynamicImportPackageStems != null)
757             for (int i = 0; i < dynamicImportPackageStems.length; i++)
758                 if (pkgname.startsWith(dynamicImportPackageStems[i]))
759                     return true;
760
761         return false;
762     }
763
764     final void addExportedProvidersFor(String JavaDoc symbolicName, String JavaDoc packageName, ArrayList result, KeyedHashSet visited) {
765         if (!visited.add(bundle))
766             return;
767
768         // See if we locally provide the package.
769
PackageSource local = null;
770         if (isExportedPackage(packageName))
771             local = proxy.getPackageSource(packageName);
772         // Must search required bundles that are exported first.
773
if (requiredBundles != null) {
774             int size = reexportTable == null ? 0 : reexportTable.length;
775             int reexportIndex = 0;
776             for (int i = 0; i < requiredBundles.length; i++) {
777                 if (local != null) {
778                     // always add required bundles first if we locally provide the package
779
// This allows a bundle to provide a package from a required bundle without
780
// re-exporting the whole required bundle.
781
requiredBundles[i].getBundleLoader().addExportedProvidersFor(symbolicName, packageName, result, visited);
782                 } else if (reexportIndex < size && reexportTable[reexportIndex] == i) {
783                     reexportIndex++;
784                     requiredBundles[i].getBundleLoader().addExportedProvidersFor(symbolicName, packageName, result, visited);
785                 }
786             }
787         }
788
789         // now add the locally provided package.
790
if (local != null && local.isFriend(symbolicName)) {
791             if (local instanceof BundleLoaderProxy.ReexportPackageSource)
792                 local = new SingleSourcePackage(packageName, -1, proxy);
793             result.add(local);
794         }
795     }
796
797     final boolean isExportedPackage(String JavaDoc name) {
798         return exportedPackages == null ? false : exportedPackages.contains(name);
799     }
800
801     private void addDynamicImportPackage(ImportPackageSpecification[] packages) {
802         if (packages == null)
803             return;
804         ArrayList dynamicImports = new ArrayList(packages.length);
805         for (int i = 0; i < packages.length; i++)
806             if (ImportPackageSpecification.RESOLUTION_DYNAMIC.equals(packages[i].getDirective(Constants.RESOLUTION_DIRECTIVE)))
807                 dynamicImports.add(packages[i].getName());
808         if (dynamicImports.size() > 0)
809             addDynamicImportPackage((String JavaDoc[]) dynamicImports.toArray(new String JavaDoc[dynamicImports.size()]));
810     }
811
812     /**
813      * Adds a list of DynamicImport-Package manifest elements to the dynamic
814      * import tables of this BundleLoader. Duplicate packages are checked and
815      * not added again. This method is not thread safe. Callers should ensure
816      * synchronization when calling this method.
817      * @param packages the DynamicImport-Package elements to add.
818      */

819     private void addDynamicImportPackage(String JavaDoc[] packages) {
820         if (packages == null)
821             return;
822
823         loaderFlags |= FLAG_HASDYNAMICIMPORTS;
824         int size = packages.length;
825         ArrayList stems;
826         if (dynamicImportPackageStems == null) {
827             stems = new ArrayList(size);
828         } else {
829             stems = new ArrayList(size + dynamicImportPackageStems.length);
830             for (int i = 0; i < dynamicImportPackageStems.length; i++) {
831                 stems.add(dynamicImportPackageStems[i]);
832             }
833         }
834
835         ArrayList names;
836         if (dynamicImportPackages == null) {
837             names = new ArrayList(size);
838         } else {
839             names = new ArrayList(size + dynamicImportPackages.length);
840             for (int i = 0; i < dynamicImportPackages.length; i++) {
841                 names.add(dynamicImportPackages[i]);
842             }
843         }
844
845         for (int i = 0; i < size; i++) {
846             String JavaDoc name = packages[i];
847             if (isDynamicallyImported(name))
848                 continue;
849             if (name.equals("*")) { /* shortcut *///$NON-NLS-1$
850
loaderFlags |= FLAG_HASDYNAMICEIMPORTALL;
851                 return;
852             }
853
854             if (name.endsWith(".*")) //$NON-NLS-1$
855
stems.add(name.substring(0, name.length() - 1));
856             else
857                 names.add(name);
858         }
859
860         size = stems.size();
861         if (size > 0)
862             dynamicImportPackageStems = (String JavaDoc[]) stems.toArray(new String JavaDoc[size]);
863
864         size = names.size();
865         if (size > 0)
866             dynamicImportPackages = (String JavaDoc[]) names.toArray(new String JavaDoc[size]);
867     }
868
869     /**
870      * Adds a list of DynamicImport-Package manifest elements to the dynamic
871      * import tables of this BundleLoader. Duplicate packages are checked and
872      * not added again.
873      * @param packages the DynamicImport-Package elements to add.
874      */

875     public final synchronized void addDynamicImportPackage(ManifestElement[] packages) {
876         if (packages == null)
877             return;
878         ArrayList dynamicImports = new ArrayList(packages.length);
879         for (int i = 0; i < packages.length; i++)
880             dynamicImports.add(packages[i].getValue());
881         if (dynamicImports.size() > 0)
882             addDynamicImportPackage((String JavaDoc[]) dynamicImports.toArray(new String JavaDoc[dynamicImports.size()]));
883     }
884
885     final synchronized void attachFragment(BundleFragment fragment) throws BundleException {
886         if (classloader == null)
887             return;
888         String JavaDoc[] classpath = fragment.getBundleData().getClassPath();
889         if (classpath != null)
890             classloader.attachFragment(fragment.getBundleData(), fragment.domain, classpath);
891     }
892
893     /*
894      * Finds a packagesource that is either imported or required from another bundle.
895      * This will not include an local package source
896      */

897     private PackageSource findSource(String JavaDoc pkgName) {
898         if (pkgName == null)
899             return null;
900         PackageSource result = findImportedSource(pkgName);
901         if (result != null)
902             return result;
903         // Note that dynamic imports are not checked to avoid aggressive wiring (bug 105779)
904
return findRequiredSource(pkgName);
905     }
906
907     private PackageSource findImportedSource(String JavaDoc pkgName) {
908         KeyedHashSet imports = getImportedSources();
909         if (imports == null)
910             return null;
911         synchronized (imports) {
912             return (PackageSource) imports.getByKey(pkgName);
913         }
914     }
915
916     private PackageSource findDynamicSource(String JavaDoc pkgName) {
917         if (isDynamicallyImported(pkgName)) {
918             ExportPackageDescription exportPackage = bundle.framework.adaptor.getState().linkDynamicImport(proxy.getBundleDescription(), pkgName);
919             if (exportPackage != null) {
920                 PackageSource source = createExportPackageSource(exportPackage);
921                 synchronized (this) {
922                     if (importedSources == null)
923                         importedSources = new KeyedHashSet(false);
924                 }
925                 synchronized (importedSources) {
926                     importedSources.add(source);
927                 }
928                 return source;
929             }
930         }
931         return null;
932     }
933
934     private PackageSource findRequiredSource(String JavaDoc pkgName) {
935         if (requiredBundles == null)
936             return null;
937         synchronized (requiredSources) {
938             PackageSource result = (PackageSource) requiredSources.getByKey(pkgName);
939             if (result != null)
940                 return result.isNullSource() ? null : result;
941         }
942         KeyedHashSet visited = new KeyedHashSet(false);
943         visited.add(bundle); // always add ourselves so we do not recurse back to ourselves
944
ArrayList result = new ArrayList(3);
945         for (int i = 0; i < requiredBundles.length; i++) {
946             BundleLoader requiredLoader = requiredBundles[i].getBundleLoader();
947             requiredLoader.addExportedProvidersFor(proxy.getSymbolicName(), pkgName, result, visited);
948         }
949         // found some so cache the result for next time and return
950
PackageSource source;
951         if (result.size() == 0) {
952             // did not find it in our required bundles lets record the failure
953
// so we do not have to do the search again for this package.
954
source = NullPackageSource.getNullPackageSource(pkgName);
955         } else if (result.size() == 1) {
956             // if there is just one source, remember just the single source
957
source = (PackageSource) result.get(0);
958         } else {
959             // if there was more than one source, build a multisource and cache that.
960
PackageSource[] srcs = (PackageSource[]) result.toArray(new PackageSource[result.size()]);
961             source = createMultiSource(pkgName, srcs);
962         }
963         synchronized (requiredSources) {
964             requiredSources.add(source);
965         }
966         return source.isNullSource() ? null : source;
967     }
968
969     /*
970      * Gets the package source for the pkgName. This will include the local package source
971      * if the bundle exports the package. This is used to compare the PackageSource of a
972      * package from two different bundles.
973      */

974     final PackageSource getPackageSource(String JavaDoc pkgName) {
975         PackageSource result = findSource(pkgName);
976         if (!isExportedPackage(pkgName))
977             return result;
978         // if the package is exported then we need to get the local source
979
PackageSource localSource = proxy.getPackageSource(pkgName);
980         if (localSource instanceof BundleLoaderProxy.ReexportPackageSource)
981             localSource = new SingleSourcePackage(pkgName, -1, proxy);
982         if (result == null)
983             return localSource;
984         if (localSource == null)
985             return result;
986         return createMultiSource(pkgName, new PackageSource[] {result, localSource});
987     }
988
989     private ClassLoader JavaDoc getParentPrivileged(final BundleClassLoader bcl) {
990         if (System.getSecurityManager() == null)
991             return bcl.getParent();
992
993         return (ClassLoader JavaDoc) AccessController.doPrivileged(new PrivilegedAction JavaDoc() {
994             public Object JavaDoc run() {
995                 return bcl.getParent();
996             }
997         });
998     }
999
1000    static final class ClassContext extends SecurityManager JavaDoc {
1001        // need to make this method public
1002
public Class JavaDoc[] getClassContext() {
1003            return super.getClassContext();
1004        }
1005    }
1006
1007    /*
1008     * see bug 121737
1009     * To ensure that we do not enter a deadly embrace between classloader cycles
1010     * we attempt to obtain a global lock before do normal osgi delegation.
1011     * This approach ensures that only one thread has a classloader locked at a time
1012     */

1013    private static void lock(Object JavaDoc loader) {
1014        Thread JavaDoc currentThread = Thread.currentThread();
1015        boolean interrupted = false;
1016        synchronized (loader) {
1017            if (tryLock(currentThread, loader))
1018                return; // this thread has the lock
1019

1020            do {
1021                try {
1022                    // we wait on the loader object here to release its lock incase we have it.
1023
// we do not way to wait while holding this lock because that will cause deadlock
1024
loader.wait();
1025                } catch (InterruptedException JavaDoc e) {
1026                    interrupted = true;
1027                    // we still want to try again
1028
}
1029            } while (!tryLock(currentThread));
1030        }
1031        if (interrupted)
1032            currentThread.interrupt();
1033    }
1034
1035    /*
1036     * returns true if this thread can obtain the global lock or already has the lock;
1037     * otherwise this loader and thread are added to the waitingList
1038     */

1039    private synchronized static boolean tryLock(Thread JavaDoc currentThread, Object JavaDoc loader) {
1040        if (lockThread == currentThread) {
1041            lockCount++;
1042            return true;
1043        }
1044        if (lockThread == null) {
1045            lockCount++;
1046            lockThread = currentThread;
1047            return true;
1048        }
1049        waitingList.add(new Object JavaDoc[] {currentThread, loader});
1050        return false;
1051    }
1052
1053    /*
1054     * returns true if this thread already has the global lock
1055     */

1056    private synchronized static boolean tryLock(Thread JavaDoc currentThread) {
1057        if (lockThread == currentThread) {
1058            lockCount++;
1059            return true;
1060        }
1061        return false;
1062    }
1063
1064    /*
1065     * unlocks the global lock and notifies the first waiting thread that they
1066     * now have the lock
1067     */

1068    private static void unlock() {
1069        Thread JavaDoc waitingThread = null;
1070        Object JavaDoc loader = null;
1071        synchronized (BundleLoader.class) {
1072            lockCount--;
1073            if (lockCount != 0)
1074                return;
1075
1076            if (waitingList.isEmpty()) {
1077                lockThread = null;
1078                return;
1079            }
1080
1081            Object JavaDoc[] waiting = (Object JavaDoc[]) waitingList.get(0);
1082            waitingThread = (Thread JavaDoc) waiting[0];
1083            loader = waiting[1];
1084        }
1085        synchronized (loader) {
1086            synchronized (BundleLoader.class) {
1087                lockThread = waitingThread;
1088                waitingList.remove(0);
1089                loader.notifyAll();
1090            }
1091        }
1092    }
1093
1094}
1095
Popular Tags