| /******************************************************************************* |
| * Copyright (c) 2004, 2013 IBM Corporation and others. |
| * All rights reserved. This program and the accompanying materials |
| * are made available under the terms of the Eclipse Public License v1.0 |
| * which accompanies this distribution, and is available at |
| * http://www.eclipse.org/legal/epl-v10.html |
| * |
| * Contributors: |
| * IBM Corporation - initial API and implementation |
| *******************************************************************************/ |
| |
| package org.eclipse.osgi.internal.loader; |
| |
| import java.io.FileNotFoundException; |
| import java.io.IOException; |
| import java.net.URL; |
| import java.security.AccessController; |
| import java.security.PrivilegedAction; |
| import java.util.*; |
| import org.eclipse.osgi.framework.adaptor.*; |
| import org.eclipse.osgi.framework.debug.Debug; |
| import org.eclipse.osgi.framework.internal.core.*; |
| import org.eclipse.osgi.framework.internal.core.Constants; |
| import org.eclipse.osgi.framework.util.KeyedElement; |
| import org.eclipse.osgi.framework.util.KeyedHashSet; |
| import org.eclipse.osgi.internal.loader.buddy.PolicyHandler; |
| import org.eclipse.osgi.internal.resolver.StateBuilder; |
| import org.eclipse.osgi.service.resolver.*; |
| import org.eclipse.osgi.util.ManifestElement; |
| import org.osgi.framework.*; |
| import org.osgi.framework.wiring.BundleWiring; |
| |
| /** |
| * This object is responsible for all classloader delegation for a bundle. |
| * It represents the loaded state of the bundle. BundleLoader objects |
| * are created lazily; care should be taken not to force the creation |
| * of a BundleLoader unless it is necessary. |
| * @see org.eclipse.osgi.internal.loader.BundleLoaderProxy |
| */ |
| public class BundleLoader implements ClassLoaderDelegate { |
| public final static String DEFAULT_PACKAGE = "."; //$NON-NLS-1$ |
| public final static String JAVA_PACKAGE = "java."; //$NON-NLS-1$ |
| public final static byte FLAG_IMPORTSINIT = 0x01; |
| public final static byte FLAG_HASDYNAMICIMPORTS = 0x02; |
| public final static byte FLAG_HASDYNAMICEIMPORTALL = 0x04; |
| public final static byte FLAG_CLOSED = 0x08; |
| public final static byte FLAG_LAZYTRIGGER = 0x10; |
| |
| public final static ClassContext CLASS_CONTEXT = AccessController.doPrivileged(new PrivilegedAction<ClassContext>() { |
| public ClassContext run() { |
| return new ClassContext(); |
| } |
| }); |
| public final static ClassLoader FW_CLASSLOADER = getClassLoader(Framework.class); |
| |
| private static final int PRE_CLASS = 1; |
| private static final int POST_CLASS = 2; |
| private static final int PRE_RESOURCE = 3; |
| private static final int POST_RESOURCE = 4; |
| private static final int PRE_RESOURCES = 5; |
| private static final int POST_RESOURCES = 6; |
| private static final int PRE_LIBRARY = 7; |
| private static final int POST_LIBRARY = 8; |
| |
| /* the proxy */ |
| final private BundleLoaderProxy proxy; |
| /* Bundle object */ |
| final BundleHost bundle; |
| final private PolicyHandler policy; |
| /* List of package names that are exported by this BundleLoader */ |
| final private Collection<String> exportedPackages; |
| final private Collection<String> substitutedPackages; |
| /* List of required bundle BundleLoaderProxy objects */ |
| final BundleLoaderProxy[] requiredBundles; |
| /* List of indexes into the requiredBundles list of reexported bundles */ |
| final int[] reexportTable; |
| /* cache of required package sources. Key is packagename, value is PackageSource */ |
| final private KeyedHashSet requiredSources; |
| |
| // note that the following non-final must be access using synchronization |
| /* cache of imported packages. Key is packagename, Value is PackageSource */ |
| private KeyedHashSet importedSources; |
| /* If not null, list of package stems to import dynamically. */ |
| private String[] dynamicImportPackageStems; |
| /* If not null, list of package names to import dynamically. */ |
| private String[] dynamicImportPackages; |
| /* loader flags */ |
| private byte loaderFlags = 0; |
| /* The is the BundleClassLoader for the bundle */ |
| private BundleClassLoader classloader; |
| private ClassLoader parent; |
| |
| /** |
| * Returns the package name from the specified class name. |
| * The returned package is dot seperated. |
| * |
| * @param name Name of a class. |
| * @return Dot separated package name or null if the class |
| * has no package name. |
| */ |
| public final static String getPackageName(String name) { |
| if (name != null) { |
| int index = name.lastIndexOf('.'); /* find last period in class name */ |
| if (index > 0) |
| return name.substring(0, index); |
| } |
| return DEFAULT_PACKAGE; |
| } |
| |
| /** |
| * Returns the package name from the specified resource name. |
| * The returned package is dot seperated. |
| * |
| * @param name Name of a resource. |
| * @return Dot separated package name or null if the resource |
| * has no package name. |
| */ |
| public final static String getResourcePackageName(String name) { |
| if (name != null) { |
| /* check for leading slash*/ |
| int begin = ((name.length() > 1) && (name.charAt(0) == '/')) ? 1 : 0; |
| int end = name.lastIndexOf('/'); /* index of last slash */ |
| if (end > begin) |
| return name.substring(begin, end).replace('/', '.'); |
| } |
| return DEFAULT_PACKAGE; |
| } |
| |
| /** |
| * BundleLoader runtime constructor. This object is created lazily |
| * when the first request for a resource is made to this bundle. |
| * |
| * @param bundle Bundle object for this loader. |
| * @param proxy the BundleLoaderProxy for this loader. |
| * @exception org.osgi.framework.BundleException |
| */ |
| protected BundleLoader(BundleHost bundle, BundleLoaderProxy proxy) throws BundleException { |
| this.bundle = bundle; |
| this.proxy = proxy; |
| try { |
| bundle.getBundleData().open(); /* make sure the BundleData is open */ |
| } catch (IOException e) { |
| throw new BundleException(Msg.BUNDLE_READ_EXCEPTION, e); |
| } |
| BundleDescription description = proxy.getBundleDescription(); |
| // init the require bundles list. |
| BundleDescription[] required = description.getResolvedRequires(); |
| if (required.length > 0) { |
| // get a list of re-exported symbolic names |
| Set<String> reExportSet = new HashSet<String>(required.length); |
| BundleSpecification[] requiredSpecs = description.getRequiredBundles(); |
| if (requiredSpecs != null && requiredSpecs.length > 0) |
| for (int i = 0; i < requiredSpecs.length; i++) |
| if (requiredSpecs[i].isExported()) |
| reExportSet.add(requiredSpecs[i].getName()); |
| |
| requiredBundles = new BundleLoaderProxy[required.length]; |
| int[] reexported = new int[required.length]; |
| int reexportIndex = 0; |
| for (int i = 0; i < required.length; i++) { |
| requiredBundles[i] = getLoaderProxy(required[i]); |
| if (reExportSet.contains(required[i].getSymbolicName())) |
| reexported[reexportIndex++] = i; |
| } |
| if (reexportIndex > 0) { |
| reexportTable = new int[reexportIndex]; |
| System.arraycopy(reexported, 0, reexportTable, 0, reexportIndex); |
| } else { |
| reexportTable = null; |
| } |
| requiredSources = new KeyedHashSet(10, false); |
| } else { |
| requiredBundles = null; |
| reexportTable = null; |
| requiredSources = null; |
| } |
| |
| // init the provided packages set |
| ExportPackageDescription[] exports = description.getSelectedExports(); |
| if (exports != null && exports.length > 0) { |
| exportedPackages = Collections.synchronizedCollection(exports.length > 10 ? new HashSet<String>(exports.length) : new ArrayList<String>(exports.length)); |
| initializeExports(exports, exportedPackages); |
| } else { |
| exportedPackages = Collections.synchronizedCollection(new ArrayList<String>(0)); |
| } |
| |
| ExportPackageDescription substituted[] = description.getSubstitutedExports(); |
| if (substituted.length > 0) { |
| substitutedPackages = substituted.length > 10 ? new HashSet<String>(substituted.length) : new ArrayList<String>(substituted.length); |
| for (int i = 0; i < substituted.length; i++) |
| substitutedPackages.add(substituted[i].getName()); |
| } else { |
| substitutedPackages = null; |
| } |
| |
| //This is the fastest way to access to the description for fragments since the hostdescription.getFragments() is slow |
| BundleFragment[] fragmentObjects = bundle.getFragments(); |
| BundleDescription[] fragments = new BundleDescription[fragmentObjects == null ? 0 : fragmentObjects.length]; |
| for (int i = 0; i < fragments.length; i++) |
| fragments[i] = fragmentObjects[i].getBundleDescription(); |
| // init the dynamic imports tables |
| if (description.hasDynamicImports()) |
| addDynamicImportPackage(description.getImportPackages()); |
| // ...and its fragments |
| for (int i = 0; i < fragments.length; i++) |
| if (fragments[i].isResolved() && fragments[i].hasDynamicImports()) |
| addDynamicImportPackage(fragments[i].getImportPackages()); |
| |
| //Initialize the policy handler |
| String buddyList = null; |
| try { |
| buddyList = bundle.getBundleData().getManifest().get(Constants.BUDDY_LOADER); |
| } catch (BundleException e) { |
| // do nothing; buddyList == null |
| } |
| policy = buddyList != null ? new PolicyHandler(this, buddyList, bundle.getFramework().getPackageAdmin()) : null; |
| if (policy != null) |
| policy.open(bundle.getFramework().getSystemBundleContext()); |
| } |
| |
| private void initializeExports(ExportPackageDescription[] exports, Collection<String> exportNames) { |
| for (int i = 0; i < exports.length; i++) { |
| if (proxy.forceSourceCreation(exports[i])) { |
| if (!exportNames.contains(exports[i].getName())) { |
| // must force filtered and reexport sources to be created early |
| // to prevent lazy normal package source creation. |
| // We only do this for the first export of a package name. |
| proxy.createPackageSource(exports[i], true); |
| } |
| } |
| exportNames.add(exports[i].getName()); |
| } |
| } |
| |
| public synchronized KeyedHashSet getImportedSources(KeyedHashSet visited) { |
| if ((loaderFlags & FLAG_IMPORTSINIT) != 0) |
| return importedSources; |
| BundleDescription bundleDesc = proxy.getBundleDescription(); |
| ExportPackageDescription[] packages = bundleDesc.getResolvedImports(); |
| if (packages != null && packages.length > 0) { |
| if (importedSources == null) |
| importedSources = new KeyedHashSet(packages.length, false); |
| for (int i = 0; i < packages.length; i++) { |
| if (packages[i].getExporter() == bundleDesc) |
| continue; // ignore imports resolved to this bundle |
| PackageSource source = createExportPackageSource(packages[i], visited); |
| if (source != null) |
| importedSources.add(source); |
| } |
| } |
| loaderFlags |= FLAG_IMPORTSINIT; |
| return importedSources; |
| } |
| |
| public synchronized boolean isLazyTriggerSet() { |
| return (loaderFlags & FLAG_LAZYTRIGGER) != 0; |
| } |
| |
| public void setLazyTrigger() throws BundleException { |
| synchronized (this) { |
| loaderFlags |= FLAG_LAZYTRIGGER; |
| } |
| BundleLoaderProxy.secureAction.start(bundle, Bundle.START_TRANSIENT | BundleHost.LAZY_TRIGGER); |
| } |
| |
| final PackageSource createExportPackageSource(ExportPackageDescription export, KeyedHashSet visited) { |
| BundleLoaderProxy exportProxy = getLoaderProxy(export.getExporter()); |
| if (exportProxy == null) |
| // TODO log error!! |
| return null; |
| PackageSource requiredSource = exportProxy.getBundleLoader().findRequiredSource(export.getName(), visited); |
| PackageSource exportSource = exportProxy.createPackageSource(export, false); |
| if (requiredSource == null) |
| return exportSource; |
| return createMultiSource(export.getName(), new PackageSource[] {requiredSource, exportSource}); |
| } |
| |
| private static PackageSource createMultiSource(String packageName, PackageSource[] sources) { |
| if (sources.length == 1) |
| return sources[0]; |
| List<SingleSourcePackage> sourceList = new ArrayList<SingleSourcePackage>(sources.length); |
| for (int i = 0; i < sources.length; i++) { |
| SingleSourcePackage[] innerSources = sources[i].getSuppliers(); |
| for (int j = 0; j < innerSources.length; j++) |
| if (!sourceList.contains(innerSources[j])) |
| sourceList.add(innerSources[j]); |
| } |
| return new MultiSourcePackage(packageName, sourceList.toArray(new SingleSourcePackage[sourceList.size()])); |
| } |
| |
| /* |
| * get the loader proxy for a bundle description |
| */ |
| public final BundleLoaderProxy getLoaderProxy(BundleDescription source) { |
| Object userObject = source.getUserObject(); |
| if (!(userObject instanceof BundleLoaderProxy)) { |
| // may need to force the proxy to be created |
| long exportingID = source.getBundleId(); |
| BundleHost exportingBundle = (BundleHost) bundle.getFramework().getBundle(exportingID); |
| if (exportingBundle == null) |
| return null; |
| userObject = exportingBundle.getLoaderProxy(); |
| } |
| return (BundleLoaderProxy) userObject; |
| } |
| |
| public BundleLoaderProxy getLoaderProxy() { |
| return proxy; |
| } |
| |
| /* |
| * Close the the BundleLoader. |
| * |
| */ |
| synchronized void close() { |
| if ((loaderFlags & FLAG_CLOSED) != 0) |
| return; |
| if (classloader != null) |
| classloader.close(); |
| if (policy != null) |
| policy.close(bundle.getFramework().getSystemBundleContext()); |
| loaderFlags |= FLAG_CLOSED; /* This indicates the BundleLoader is destroyed */ |
| } |
| |
| /** |
| * This method loads a class from the bundle. The class is searched for in the |
| * same manner as it would if it was being loaded from a bundle (i.e. all |
| * hosts, fragments, import, required bundles and local resources are searched. |
| * |
| * @param name the name of the desired Class. |
| * @return the resulting Class |
| * @exception java.lang.ClassNotFoundException if the class definition was not found. |
| */ |
| final public Class<?> loadClass(String name) throws ClassNotFoundException { |
| BundleClassLoader bcl = createClassLoader(); |
| // The instanceof check here is just to be safe. The javadoc contract stated in BundleClassLoader |
| // mandate that BundleClassLoaders be an instance of ClassLoader. |
| if (name.length() > 0 && name.charAt(0) == '[' && bcl instanceof ClassLoader) |
| return Class.forName(name, false, (ClassLoader) bcl); |
| return bcl.loadClass(name); |
| } |
| |
| /** |
| * This method gets a resource from the bundle. The resource is searched |
| * for in the same manner as it would if it was being loaded from a bundle |
| * (i.e. all hosts, fragments, import, required bundles and |
| * local resources are searched). |
| * |
| * @param name the name of the desired resource. |
| * @return the resulting resource URL or null if it does not exist. |
| */ |
| final URL getResource(String name) { |
| return createClassLoader().getResource(name); |
| } |
| |
| public final synchronized ClassLoader getParentClassLoader() { |
| if (parent != null) |
| return parent; |
| createClassLoader(); |
| return parent; |
| } |
| |
| final public synchronized BundleClassLoader createClassLoader() { |
| if (classloader != null) |
| return classloader; |
| String[] classpath; |
| try { |
| classpath = bundle.getBundleData().getClassPath(); |
| } catch (BundleException e) { |
| // no classpath |
| classpath = new String[0]; |
| bundle.getFramework().publishFrameworkEvent(FrameworkEvent.ERROR, bundle, e); |
| } |
| if (classpath == null) { |
| // no classpath |
| classpath = new String[0]; |
| bundle.getFramework().publishFrameworkEvent(FrameworkEvent.ERROR, bundle, new BundleException(Msg.BUNDLE_NO_CLASSPATH_MATCH, BundleException.MANIFEST_ERROR)); |
| } |
| BundleClassLoader bcl = createBCLPrevileged(bundle.getProtectionDomain(), classpath); |
| parent = getParentPrivileged(bcl); |
| classloader = bcl; |
| return classloader; |
| } |
| |
| /** |
| * Finds a class local to this bundle. Only the classloader for this bundle is searched. |
| * @param name The name of the class to find. |
| * @return The loaded Class or null if the class is not found. |
| * @throws ClassNotFoundException |
| */ |
| Class<?> findLocalClass(String name) throws ClassNotFoundException { |
| if (Debug.DEBUG_LOADER) |
| Debug.println("BundleLoader[" + this + "].findLocalClass(" + name + ")"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ |
| try { |
| Class<?> clazz = createClassLoader().findLocalClass(name); |
| if (Debug.DEBUG_LOADER && clazz != null) |
| Debug.println("BundleLoader[" + this + "] found local class " + name); //$NON-NLS-1$ //$NON-NLS-2$ |
| return clazz; |
| } catch (ClassNotFoundException e) { |
| if (e instanceof StatusException) { |
| if ((((StatusException) e).getStatusCode() & StatusException.CODE_ERROR) != 0) |
| throw e; |
| } |
| return null; |
| } |
| } |
| |
| /** |
| * Finds the class for a bundle. This method is used for delegation by the bundle's classloader. |
| */ |
| public Class<?> findClass(String name) throws ClassNotFoundException { |
| return findClass(name, true); |
| } |
| |
| Class<?> findClass(String name, boolean checkParent) throws ClassNotFoundException { |
| ClassLoader parentCL = getParentClassLoader(); |
| if (checkParent && parentCL != null && name.startsWith(JAVA_PACKAGE)) |
| // 1) if startsWith "java." delegate to parent and terminate search |
| // we want to throw ClassNotFoundExceptions if a java.* class cannot be loaded from the parent. |
| return parentCL.loadClass(name); |
| return findClassInternal(name, checkParent, parentCL); |
| } |
| |
| private Class<?> findClassInternal(String name, boolean checkParent, ClassLoader parentCL) throws ClassNotFoundException { |
| if (Debug.DEBUG_LOADER) |
| Debug.println("BundleLoader[" + this + "].loadBundleClass(" + name + ")"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ |
| String pkgName = getPackageName(name); |
| boolean bootDelegation = false; |
| // follow the OSGi delegation model |
| if (checkParent && parentCL != null && bundle.getFramework().isBootDelegationPackage(pkgName)) |
| // 2) if part of the bootdelegation list then delegate to parent and continue of failure |
| try { |
| return parentCL.loadClass(name); |
| } catch (ClassNotFoundException cnfe) { |
| // we want to continue |
| bootDelegation = true; |
| } |
| Class<?> result = null; |
| try { |
| result = (Class<?>) searchHooks(name, PRE_CLASS); |
| } catch (ClassNotFoundException e) { |
| throw e; |
| } catch (FileNotFoundException e) { |
| // will not happen |
| } |
| if (result != null) |
| return result; |
| // 3) search the imported packages |
| PackageSource source = findImportedSource(pkgName, null); |
| if (source != null) { |
| // 3) found import source terminate search at the source |
| result = source.loadClass(name); |
| if (result != null) |
| return result; |
| throw new ClassNotFoundException(name + " cannot be found by " + this); //$NON-NLS-1$ |
| } |
| // 4) search the required bundles |
| source = findRequiredSource(pkgName, null); |
| if (source != null) |
| // 4) attempt to load from source but continue on failure |
| result = source.loadClass(name); |
| // 5) search the local bundle |
| if (result == null) |
| result = findLocalClass(name); |
| if (result != null) |
| return result; |
| // 6) attempt to find a dynamic import source; only do this if a required source was not found |
| if (source == null) { |
| source = findDynamicSource(pkgName); |
| if (source != null) { |
| result = source.loadClass(name); |
| if (result != null) |
| return result; |
| // must throw CNFE if dynamic import source does not have the class |
| throw new ClassNotFoundException(name + " cannot be found by " + this); //$NON-NLS-1$ |
| } |
| } |
| |
| if (result == null) |
| try { |
| result = (Class<?>) searchHooks(name, POST_CLASS); |
| } catch (ClassNotFoundException e) { |
| throw e; |
| } catch (FileNotFoundException e) { |
| // will not happen |
| } |
| // do buddy policy loading |
| if (result == null && policy != null) |
| result = policy.doBuddyClassLoading(name); |
| if (result != null) |
| return result; |
| // hack to support backwards compatibiility for bootdelegation |
| // or last resort; do class context trick to work around VM bugs |
| if (parentCL != null && !bootDelegation && ((checkParent && bundle.getFramework().compatibiltyBootDelegation) || isRequestFromVM())) |
| // we don't need to continue if a CNFE is thrown here. |
| try { |
| return parentCL.loadClass(name); |
| } catch (ClassNotFoundException e) { |
| // we want to generate our own exception below |
| } |
| throw new ClassNotFoundException(name + " cannot be found by " + this); //$NON-NLS-1$ |
| } |
| |
| @SuppressWarnings("unchecked") |
| private <E> E searchHooks(String name, int type) throws ClassNotFoundException, FileNotFoundException { |
| ClassLoaderDelegateHook[] delegateHooks = bundle.getFramework().getDelegateHooks(); |
| if (delegateHooks == null) |
| return null; |
| E result = null; |
| for (int i = 0; i < delegateHooks.length && result == null; i++) { |
| switch (type) { |
| case PRE_CLASS : |
| result = (E) delegateHooks[i].preFindClass(name, createClassLoader(), bundle.getBundleData()); |
| break; |
| case POST_CLASS : |
| result = (E) delegateHooks[i].postFindClass(name, createClassLoader(), bundle.getBundleData()); |
| break; |
| case PRE_RESOURCE : |
| result = (E) delegateHooks[i].preFindResource(name, createClassLoader(), bundle.getBundleData()); |
| break; |
| case POST_RESOURCE : |
| result = (E) delegateHooks[i].postFindResource(name, createClassLoader(), bundle.getBundleData()); |
| break; |
| case PRE_RESOURCES : |
| result = (E) delegateHooks[i].preFindResources(name, createClassLoader(), bundle.getBundleData()); |
| break; |
| case POST_RESOURCES : |
| result = (E) delegateHooks[i].postFindResources(name, createClassLoader(), bundle.getBundleData()); |
| break; |
| case PRE_LIBRARY : |
| result = (E) delegateHooks[i].preFindLibrary(name, createClassLoader(), bundle.getBundleData()); |
| break; |
| case POST_LIBRARY : |
| result = (E) delegateHooks[i].postFindLibrary(name, createClassLoader(), bundle.getBundleData()); |
| break; |
| } |
| } |
| return result; |
| } |
| |
| private boolean isRequestFromVM() { |
| if (bundle.getFramework().isBootDelegationPackage("*") || !bundle.getFramework().contextBootDelegation) //$NON-NLS-1$ |
| return false; |
| // works around VM bugs that require all classloaders to have access to parent packages |
| Class<?>[] context = CLASS_CONTEXT.getClassContext(); |
| if (context == null || context.length < 2) |
| return false; |
| // skip the first class; it is the ClassContext class |
| for (int i = 1; i < context.length; i++) |
| // find the first class in the context which is not BundleLoader or instanceof ClassLoader |
| if (context[i] != BundleLoader.class && !ClassLoader.class.isAssignableFrom(context[i]) && !context[i].getName().equals("java.lang.J9VMInternals")) { //$NON-NLS-1$ |
| // only find in parent if the class is not "Class" (Class#forName case) or if the class is not loaded with a BundleClassLoader |
| ClassLoader cl = getClassLoader(context[i]); |
| if (cl != FW_CLASSLOADER) { // extra check incase an adaptor adds another class into the stack besides an instance of ClassLoader |
| if (Class.class != context[i] && !(cl instanceof BundleClassLoader)) |
| return true; |
| break; |
| } |
| } |
| return false; |
| } |
| |
| private static ClassLoader getClassLoader(final Class<?> clazz) { |
| if (System.getSecurityManager() == null) |
| return clazz.getClassLoader(); |
| return AccessController.doPrivileged(new PrivilegedAction<ClassLoader>() { |
| public ClassLoader run() { |
| return clazz.getClassLoader(); |
| } |
| }); |
| } |
| |
| /** |
| * Finds the resource for a bundle. This method is used for delegation by the bundle's classloader. |
| */ |
| public URL findResource(String name) { |
| if ((name.length() > 1) && (name.charAt(0) == '/')) /* if name has a leading slash */ |
| name = name.substring(1); /* remove leading slash before search */ |
| String pkgName = getResourcePackageName(name); |
| boolean bootDelegation = false; |
| ClassLoader parentCL = getParentClassLoader(); |
| // follow the OSGi delegation model |
| // First check the parent classloader for system resources, if it is a java resource. |
| if (parentCL != null) { |
| if (pkgName.startsWith(JAVA_PACKAGE)) |
| // 1) if startsWith "java." delegate to parent and terminate search |
| // we never delegate java resource requests past the parent |
| return parentCL.getResource(name); |
| else if (bundle.getFramework().isBootDelegationPackage(pkgName)) { |
| // 2) if part of the bootdelegation list then delegate to parent and continue of failure |
| URL result = parentCL.getResource(name); |
| if (result != null) |
| return result; |
| bootDelegation = true; |
| } |
| } |
| |
| URL result = null; |
| try { |
| result = (URL) searchHooks(name, PRE_RESOURCE); |
| } catch (FileNotFoundException e) { |
| return null; |
| } catch (ClassNotFoundException e) { |
| // will not happen |
| } |
| if (result != null) |
| return result; |
| // 3) search the imported packages |
| PackageSource source = findImportedSource(pkgName, null); |
| if (source != null) |
| // 3) found import source terminate search at the source |
| return source.getResource(name); |
| // 4) search the required bundles |
| source = findRequiredSource(pkgName, null); |
| if (source != null) |
| // 4) attempt to load from source but continue on failure |
| result = source.getResource(name); |
| // 5) search the local bundle |
| if (result == null) |
| result = findLocalResource(name); |
| if (result != null) |
| return result; |
| // 6) attempt to find a dynamic import source; only do this if a required source was not found |
| if (source == null) { |
| source = findDynamicSource(pkgName); |
| if (source != null) |
| // must return the result of the dynamic import and do not continue |
| return source.getResource(name); |
| } |
| |
| if (result == null) |
| try { |
| result = (URL) searchHooks(name, POST_RESOURCE); |
| } catch (FileNotFoundException e) { |
| return null; |
| } catch (ClassNotFoundException e) { |
| // will not happen |
| } |
| // do buddy policy loading |
| if (result == null && policy != null) |
| result = policy.doBuddyResourceLoading(name); |
| if (result != null) |
| return result; |
| // hack to support backwards compatibility for bootdelegation |
| // or last resort; do class context trick to work around VM bugs |
| if (parentCL != null && !bootDelegation && (bundle.getFramework().compatibiltyBootDelegation || isRequestFromVM())) |
| // we don't need to continue if the resource is not found here |
| return parentCL.getResource(name); |
| return result; |
| } |
| |
| /** |
| * Finds the resources for a bundle. This method is used for delegation by the bundle's classloader. |
| */ |
| public Enumeration<URL> findResources(String name) throws IOException { |
| // do not delegate to parent because ClassLoader#getResources already did and it is final!! |
| if ((name.length() > 1) && (name.charAt(0) == '/')) /* if name has a leading slash */ |
| name = name.substring(1); /* remove leading slash before search */ |
| String pkgName = getResourcePackageName(name); |
| Enumeration<URL> result = Collections.enumeration(Collections.<URL> emptyList()); |
| boolean bootDelegation = false; |
| ClassLoader parentCL = getParentClassLoader(); |
| // follow the OSGi delegation model |
| // First check the parent classloader for system resources, if it is a java resource. |
| if (parentCL != null) { |
| if (pkgName.startsWith(JAVA_PACKAGE)) |
| // 1) if startsWith "java." delegate to parent and terminate search |
| // we never delegate java resource requests past the parent |
| return parentCL.getResources(name); |
| else if (bundle.getFramework().isBootDelegationPackage(pkgName)) { |
| // 2) if part of the bootdelegation list then delegate to parent and continue |
| result = compoundEnumerations(result, parentCL.getResources(name)); |
| bootDelegation = true; |
| } |
| } |
| try { |
| Enumeration<URL> hookResources = searchHooks(name, PRE_RESOURCES); |
| if (hookResources != null) { |
| return compoundEnumerations(result, hookResources); |
| } |
| } catch (ClassNotFoundException e) { |
| // will not happen |
| } catch (FileNotFoundException e) { |
| return result; |
| } |
| |
| // 3) search the imported packages |
| PackageSource source = findImportedSource(pkgName, null); |
| if (source != null) |
| // 3) found import source terminate search at the source |
| return compoundEnumerations(result, source.getResources(name)); |
| // 4) search the required bundles |
| source = findRequiredSource(pkgName, null); |
| if (source != null) |
| // 4) attempt to load from source but continue on failure |
| result = compoundEnumerations(result, source.getResources(name)); |
| |
| // 5) search the local bundle |
| // compound the required source results with the local ones |
| Enumeration<URL> localResults = findLocalResources(name); |
| result = compoundEnumerations(result, localResults); |
| // 6) attempt to find a dynamic import source; only do this if a required source was not found |
| if (source == null && !result.hasMoreElements()) { |
| source = findDynamicSource(pkgName); |
| if (source != null) |
| return compoundEnumerations(result, source.getResources(name)); |
| } |
| if (!result.hasMoreElements()) |
| try { |
| Enumeration<URL> hookResources = searchHooks(name, POST_RESOURCES); |
| result = compoundEnumerations(result, hookResources); |
| } catch (ClassNotFoundException e) { |
| // will not happen |
| } catch (FileNotFoundException e) { |
| return null; |
| } |
| if (policy != null) { |
| Enumeration<URL> buddyResult = policy.doBuddyResourcesLoading(name); |
| result = compoundEnumerations(result, buddyResult); |
| } |
| // hack to support backwards compatibility for bootdelegation |
| // or last resort; do class context trick to work around VM bugs |
| if (!result.hasMoreElements()) { |
| if (parentCL != null && !bootDelegation && (bundle.getFramework().compatibiltyBootDelegation || isRequestFromVM())) |
| // we don't need to continue if the resource is not found here |
| return parentCL.getResources(name); |
| } |
| return result; |
| } |
| |
| private boolean isSubPackage(String parentPackage, String subPackage) { |
| String prefix = (parentPackage.length() == 0 || parentPackage.equals(DEFAULT_PACKAGE)) ? "" : parentPackage + '.'; //$NON-NLS-1$ |
| return subPackage.startsWith(prefix); |
| } |
| |
| public Collection<String> listResources(String path, String filePattern, int options) { |
| String pkgName = getResourcePackageName(path.endsWith("/") ? path : path + '/'); //$NON-NLS-1$ |
| if ((path.length() > 1) && (path.charAt(0) == '/')) /* if name has a leading slash */ |
| path = path.substring(1); /* remove leading slash before search */ |
| boolean subPackages = (options & BundleWiring.LISTRESOURCES_RECURSE) != 0; |
| List<String> packages = new ArrayList<String>(); |
| // search imported package names |
| KeyedHashSet importSources = getImportedSources(null); |
| if (importSources != null) { |
| KeyedElement[] imports = importSources.elements(); |
| for (KeyedElement keyedElement : imports) { |
| String id = ((PackageSource) keyedElement).getId(); |
| if (id.equals(pkgName) || (subPackages && isSubPackage(pkgName, id))) |
| packages.add(id); |
| } |
| } |
| |
| // now add package names from required bundles |
| if (requiredBundles != null) { |
| KeyedHashSet visited = new KeyedHashSet(false); |
| visited.add(bundle); // always add ourselves so we do not recurse back to ourselves |
| for (BundleLoaderProxy requiredProxy : requiredBundles) { |
| BundleLoader requiredLoader = requiredProxy.getBundleLoader(); |
| requiredLoader.addProvidedPackageNames(requiredProxy.getSymbolicName(), pkgName, packages, subPackages, visited); |
| } |
| } |
| |
| boolean localSearch = (options & BundleWiring.LISTRESOURCES_LOCAL) != 0; |
| List<String> result = new ArrayList<String>(); |
| Set<String> importedPackages = new HashSet<String>(0); |
| for (String name : packages) { |
| // look for import source |
| PackageSource externalSource = findImportedSource(name, null); |
| if (externalSource != null) { |
| // record this package is imported |
| importedPackages.add(name); |
| } else { |
| // look for require bundle source |
| externalSource = findRequiredSource(name, null); |
| } |
| // only add the content of the external source if this is not a localSearch |
| if (externalSource != null && !localSearch) { |
| String packagePath = name.replace('.', '/'); |
| Collection<String> externalResources = externalSource.listResources(packagePath, filePattern); |
| for (String resource : externalResources) { |
| if (!result.contains(resource)) // prevent duplicates; could happen if the package is split or exporter has fragments/multiple jars |
| result.add(resource); |
| } |
| } |
| } |
| |
| // now search locally |
| Collection<String> localResources = createClassLoader().listLocalResources(path, filePattern, options); |
| for (String resource : localResources) { |
| String resourcePkg = getResourcePackageName(resource); |
| if (!importedPackages.contains(resourcePkg) && !result.contains(resource)) |
| result.add(resource); |
| } |
| return result; |
| } |
| |
| public static <E> Enumeration<E> compoundEnumerations(Enumeration<E> list1, Enumeration<E> list2) { |
| if (list2 == null || !list2.hasMoreElements()) |
| return list1; |
| if (list1 == null || !list1.hasMoreElements()) |
| return list2; |
| List<E> compoundResults = new ArrayList<E>(); |
| while (list1.hasMoreElements()) |
| compoundResults.add(list1.nextElement()); |
| while (list2.hasMoreElements()) { |
| E item = list2.nextElement(); |
| if (!compoundResults.contains(item)) //don't add duplicates |
| compoundResults.add(item); |
| } |
| return Collections.enumeration(compoundResults); |
| } |
| |
| /** |
| * Finds a resource local to this bundle. Only the classloader for this bundle is searched. |
| * @param name The name of the resource to find. |
| * @return The URL to the resource or null if the resource is not found. |
| */ |
| URL findLocalResource(final String name) { |
| return createClassLoader().findLocalResource(name); |
| } |
| |
| /** |
| * Returns an Enumeration of URLs representing all the resources with |
| * the given name. Only the classloader for this bundle is searched. |
| * |
| * @param name the resource name |
| * @return an Enumeration of URLs for the resources |
| */ |
| Enumeration<URL> findLocalResources(String name) { |
| return createClassLoader().findLocalResources(name); |
| } |
| |
| /** |
| * Returns the absolute path name of a native library. |
| * |
| * @param name the library name |
| * @return the absolute path of the native library or null if not found |
| */ |
| public String findLibrary(final String name) { |
| if (System.getSecurityManager() == null) |
| return findLocalLibrary(name); |
| return AccessController.doPrivileged(new PrivilegedAction<String>() { |
| public String run() { |
| return findLocalLibrary(name); |
| } |
| }); |
| } |
| |
| final String findLocalLibrary(final String name) { |
| String result = null; |
| try { |
| result = (String) searchHooks(name, PRE_LIBRARY); |
| } catch (FileNotFoundException e) { |
| return null; |
| } catch (ClassNotFoundException e) { |
| // will not happen |
| } |
| if (result != null) |
| return result; |
| result = bundle.getBundleData().findLibrary(name); |
| if (result != null) |
| return result; |
| |
| // look in fragments imports ... |
| BundleFragment[] fragments = bundle.getFragments(); |
| if (fragments != null) |
| for (int i = 0; i < fragments.length; i++) { |
| result = fragments[i].getBundleData().findLibrary(name); |
| if (result != null) |
| return result; |
| } |
| try { |
| return (String) searchHooks(name, POST_LIBRARY); |
| } catch (FileNotFoundException e) { |
| return null; // this is not necessary; but being consistent in case another step is added below |
| } catch (ClassNotFoundException e) { |
| // will not happen |
| } |
| return null; |
| } |
| |
| /* |
| * Return the bundle we are associated with. |
| */ |
| public final AbstractBundle getBundle() { |
| return bundle; |
| } |
| |
| private BundleClassLoader createBCLPrevileged(final BundleProtectionDomain pd, final String[] cp) { |
| // Create the classloader as previleged code if security manager is present. |
| if (System.getSecurityManager() == null) |
| return createBCL(pd, cp); |
| |
| return AccessController.doPrivileged(new PrivilegedAction<BundleClassLoader>() { |
| public BundleClassLoader run() { |
| return createBCL(pd, cp); |
| } |
| }); |
| |
| } |
| |
| BundleClassLoader createBCL(final BundleProtectionDomain pd, final String[] cp) { |
| BundleClassLoader bcl = bundle.getBundleData().createClassLoader(BundleLoader.this, pd, cp); |
| // attach existing fragments to classloader |
| BundleFragment[] fragments = bundle.getFragments(); |
| if (fragments != null) |
| for (int i = 0; i < fragments.length; i++) { |
| try { |
| bcl.attachFragment(fragments[i].getBundleData(), fragments[i].getProtectionDomain(), fragments[i].getBundleData().getClassPath()); |
| } catch (BundleException be) { |
| bundle.getFramework().publishFrameworkEvent(FrameworkEvent.ERROR, bundle, be); |
| } |
| } |
| |
| // finish the initialization of the classloader. |
| bcl.initialize(); |
| return bcl; |
| } |
| |
| /** |
| * Return a string representation of this loader. |
| * @return String |
| */ |
| public final String toString() { |
| BundleData result = bundle.getBundleData(); |
| return result == null ? "BundleLoader.bundledata == null!" : result.toString(); //$NON-NLS-1$ |
| } |
| |
| /** |
| * Return true if the target package name matches |
| * a name in the DynamicImport-Package manifest header. |
| * |
| * @param pkgname The name of the requested class' package. |
| * @return true if the package should be imported. |
| */ |
| private final synchronized boolean isDynamicallyImported(String pkgname) { |
| if (this instanceof SystemBundleLoader) |
| return false; // system bundle cannot dynamically import |
| // must check for startsWith("java.") to satisfy R3 section 4.7.2 |
| if (pkgname.startsWith("java.")) //$NON-NLS-1$ |
| return true; |
| |
| /* quick shortcut check */ |
| if ((loaderFlags & FLAG_HASDYNAMICIMPORTS) == 0) |
| return false; |
| |
| /* "*" shortcut */ |
| if ((loaderFlags & FLAG_HASDYNAMICEIMPORTALL) != 0) |
| return true; |
| |
| /* match against specific names */ |
| if (dynamicImportPackages != null) |
| for (int i = 0; i < dynamicImportPackages.length; i++) |
| if (pkgname.equals(dynamicImportPackages[i])) |
| return true; |
| |
| /* match against names with trailing wildcards */ |
| if (dynamicImportPackageStems != null) |
| for (int i = 0; i < dynamicImportPackageStems.length; i++) |
| if (pkgname.startsWith(dynamicImportPackageStems[i])) |
| return true; |
| |
| return false; |
| } |
| |
| final void addExportedProvidersFor(String symbolicName, String packageName, List<PackageSource> result, KeyedHashSet visited) { |
| if (!visited.add(bundle)) |
| return; |
| |
| // See if we locally provide the package. |
| PackageSource local = null; |
| if (isExportedPackage(packageName)) |
| local = proxy.getPackageSource(packageName); |
| else if (isSubstitutedExport(packageName)) { |
| result.add(findImportedSource(packageName, visited)); |
| return; // should not continue to required bundles in this case |
| } |
| // Must search required bundles that are exported first. |
| if (requiredBundles != null) { |
| int size = reexportTable == null ? 0 : reexportTable.length; |
| int reexportIndex = 0; |
| for (int i = 0; i < requiredBundles.length; i++) { |
| if (local != null) { |
| // always add required bundles first if we locally provide the package |
| // This allows a bundle to provide a package from a required bundle without |
| // re-exporting the whole required bundle. |
| requiredBundles[i].getBundleLoader().addExportedProvidersFor(symbolicName, packageName, result, visited); |
| } else if (reexportIndex < size && reexportTable[reexportIndex] == i) { |
| reexportIndex++; |
| requiredBundles[i].getBundleLoader().addExportedProvidersFor(symbolicName, packageName, result, visited); |
| } |
| } |
| } |
| |
| // now add the locally provided package. |
| if (local != null && local.isFriend(symbolicName)) |
| result.add(local); |
| } |
| |
| final void addProvidedPackageNames(String symbolicName, String packageName, List<String> result, boolean subPackages, KeyedHashSet visitied) { |
| if (!visitied.add(bundle)) |
| return; |
| for (String exported : exportedPackages) { |
| if (exported.equals(packageName) || (subPackages && isSubPackage(packageName, exported))) { |
| if (!result.contains(exported)) |
| result.add(exported); |
| } |
| } |
| if (substitutedPackages != null) |
| for (String substituted : substitutedPackages) { |
| if (substituted.equals(packageName) || (subPackages && isSubPackage(packageName, substituted))) { |
| if (!result.contains(substituted)) |
| result.add(substituted); |
| } |
| } |
| if (requiredBundles != null) { |
| int size = reexportTable == null ? 0 : reexportTable.length; |
| int reexportIndex = 0; |
| for (int i = 0; i < requiredBundles.length; i++) { |
| if (reexportIndex < size && reexportTable[reexportIndex] == i) { |
| reexportIndex++; |
| requiredBundles[i].getBundleLoader().addProvidedPackageNames(symbolicName, packageName, result, subPackages, visitied); |
| } |
| } |
| } |
| } |
| |
| final boolean isExportedPackage(String name) { |
| return exportedPackages.contains(name); |
| } |
| |
| final boolean isSubstitutedExport(String name) { |
| return substitutedPackages == null ? false : substitutedPackages.contains(name); |
| } |
| |
| private void addDynamicImportPackage(ImportPackageSpecification[] packages) { |
| if (packages == null) |
| return; |
| List<String> dynamicImports = new ArrayList<String>(packages.length); |
| for (int i = 0; i < packages.length; i++) |
| if (ImportPackageSpecification.RESOLUTION_DYNAMIC.equals(packages[i].getDirective(Constants.RESOLUTION_DIRECTIVE))) |
| dynamicImports.add(packages[i].getName()); |
| if (dynamicImports.size() > 0) |
| addDynamicImportPackage(dynamicImports.toArray(new String[dynamicImports.size()])); |
| } |
| |
| /** |
| * Adds a list of DynamicImport-Package manifest elements to the dynamic |
| * import tables of this BundleLoader. Duplicate packages are checked and |
| * not added again. This method is not thread safe. Callers should ensure |
| * synchronization when calling this method. |
| * @param packages the DynamicImport-Package elements to add. |
| */ |
| private void addDynamicImportPackage(String[] packages) { |
| if (packages == null) |
| return; |
| |
| loaderFlags |= FLAG_HASDYNAMICIMPORTS; |
| int size = packages.length; |
| List<String> stems; |
| if (dynamicImportPackageStems == null) { |
| stems = new ArrayList<String>(size); |
| } else { |
| stems = new ArrayList<String>(size + dynamicImportPackageStems.length); |
| for (int i = 0; i < dynamicImportPackageStems.length; i++) { |
| stems.add(dynamicImportPackageStems[i]); |
| } |
| } |
| |
| List<String> names; |
| if (dynamicImportPackages == null) { |
| names = new ArrayList<String>(size); |
| } else { |
| names = new ArrayList<String>(size + dynamicImportPackages.length); |
| for (int i = 0; i < dynamicImportPackages.length; i++) { |
| names.add(dynamicImportPackages[i]); |
| } |
| } |
| |
| for (int i = 0; i < size; i++) { |
| String name = packages[i]; |
| if (isDynamicallyImported(name)) |
| continue; |
| if (name.equals("*")) { /* shortcut *///$NON-NLS-1$ |
| loaderFlags |= FLAG_HASDYNAMICEIMPORTALL; |
| return; |
| } |
| |
| if (name.endsWith(".*")) //$NON-NLS-1$ |
| stems.add(name.substring(0, name.length() - 1)); |
| else |
| names.add(name); |
| } |
| |
| size = stems.size(); |
| if (size > 0) |
| dynamicImportPackageStems = stems.toArray(new String[size]); |
| |
| size = names.size(); |
| if (size > 0) |
| dynamicImportPackages = names.toArray(new String[size]); |
| } |
| |
| /** |
| * Adds a list of DynamicImport-Package manifest elements to the dynamic |
| * import tables of this BundleLoader. Duplicate packages are checked and |
| * not added again. |
| * @param packages the DynamicImport-Package elements to add. |
| */ |
| public final synchronized void addDynamicImportPackage(ManifestElement[] packages) { |
| if (packages == null) |
| return; |
| List<String> dynamicImports = new ArrayList<String>(packages.length); |
| List<ImportPackageSpecification> dynamicImportSpecs = new ArrayList<ImportPackageSpecification>(packages.length); |
| for (ManifestElement dynamicImportElement : packages) { |
| String[] names = dynamicImportElement.getValueComponents(); |
| for (String name : names) |
| dynamicImports.add(name); |
| StateBuilder.addImportPackages(dynamicImportElement, dynamicImportSpecs, 2, true); |
| } |
| if (dynamicImports.size() > 0) { |
| addDynamicImportPackage(dynamicImports.toArray(new String[dynamicImports.size()])); |
| BundleDescription revision = getLoaderProxy().getBundleDescription(); |
| State state = revision.getContainingState(); |
| state.addDynamicImportPackages(revision, dynamicImportSpecs.toArray(new ImportPackageSpecification[dynamicImportSpecs.size()])); |
| } |
| } |
| |
| synchronized public void attachFragment(BundleFragment fragment) throws BundleException { |
| ExportPackageDescription[] exports = proxy.getBundleDescription().getSelectedExports(); |
| if (classloader == null) { |
| initializeExports(exports, exportedPackages); |
| return; |
| } |
| String[] classpath = fragment.getBundleData().getClassPath(); |
| if (classpath != null) |
| classloader.attachFragment(fragment.getBundleData(), fragment.getProtectionDomain(), classpath); |
| initializeExports(exports, exportedPackages); |
| } |
| |
| /* |
| * Finds a packagesource that is either imported or required from another bundle. |
| * This will not include an local package source |
| */ |
| private PackageSource findSource(String pkgName) { |
| if (pkgName == null) |
| return null; |
| PackageSource result = findImportedSource(pkgName, null); |
| if (result != null) |
| return result; |
| // Note that dynamic imports are not checked to avoid aggressive wiring (bug 105779) |
| return findRequiredSource(pkgName, null); |
| } |
| |
| private PackageSource findImportedSource(String pkgName, KeyedHashSet visited) { |
| KeyedHashSet imports = getImportedSources(visited); |
| if (imports == null) |
| return null; |
| synchronized (imports) { |
| return (PackageSource) imports.getByKey(pkgName); |
| } |
| } |
| |
| private PackageSource findDynamicSource(String pkgName) { |
| if (isDynamicallyImported(pkgName)) { |
| ExportPackageDescription exportPackage = bundle.getFramework().getAdaptor().getState().linkDynamicImport(proxy.getBundleDescription(), pkgName); |
| if (exportPackage != null) { |
| PackageSource source = createExportPackageSource(exportPackage, null); |
| synchronized (this) { |
| if (importedSources == null) |
| importedSources = new KeyedHashSet(false); |
| } |
| synchronized (importedSources) { |
| importedSources.add(source); |
| } |
| return source; |
| } |
| } |
| return null; |
| } |
| |
| private PackageSource findRequiredSource(String pkgName, KeyedHashSet visited) { |
| if (requiredBundles == null) |
| return null; |
| synchronized (requiredSources) { |
| PackageSource result = (PackageSource) requiredSources.getByKey(pkgName); |
| if (result != null) |
| return result.isNullSource() ? null : result; |
| } |
| if (visited == null) |
| visited = new KeyedHashSet(false); |
| visited.add(bundle); // always add ourselves so we do not recurse back to ourselves |
| List<PackageSource> result = new ArrayList<PackageSource>(3); |
| for (int i = 0; i < requiredBundles.length; i++) { |
| BundleLoader requiredLoader = requiredBundles[i].getBundleLoader(); |
| requiredLoader.addExportedProvidersFor(proxy.getSymbolicName(), pkgName, result, visited); |
| } |
| // found some so cache the result for next time and return |
| PackageSource source; |
| if (result.size() == 0) { |
| // did not find it in our required bundles lets record the failure |
| // so we do not have to do the search again for this package. |
| source = NullPackageSource.getNullPackageSource(pkgName); |
| } else if (result.size() == 1) { |
| // if there is just one source, remember just the single source |
| source = result.get(0); |
| } else { |
| // if there was more than one source, build a multisource and cache that. |
| PackageSource[] srcs = result.toArray(new PackageSource[result.size()]); |
| source = createMultiSource(pkgName, srcs); |
| } |
| synchronized (requiredSources) { |
| requiredSources.add(source); |
| } |
| return source.isNullSource() ? null : source; |
| } |
| |
| /* |
| * Gets the package source for the pkgName. This will include the local package source |
| * if the bundle exports the package. This is used to compare the PackageSource of a |
| * package from two different bundles. |
| */ |
| public final PackageSource getPackageSource(String pkgName) { |
| PackageSource result = findSource(pkgName); |
| if (!isExportedPackage(pkgName)) |
| return result; |
| // if the package is exported then we need to get the local source |
| PackageSource localSource = proxy.getPackageSource(pkgName); |
| if (result == null) |
| return localSource; |
| if (localSource == null) |
| return result; |
| return createMultiSource(pkgName, new PackageSource[] {result, localSource}); |
| } |
| |
| private ClassLoader getParentPrivileged(final BundleClassLoader bcl) { |
| if (System.getSecurityManager() == null) |
| return bcl.getParent(); |
| |
| return AccessController.doPrivileged(new PrivilegedAction<ClassLoader>() { |
| public ClassLoader run() { |
| return bcl.getParent(); |
| } |
| }); |
| } |
| |
| static final class ClassContext extends SecurityManager { |
| // need to make this method public |
| public Class<?>[] getClassContext() { |
| return super.getClassContext(); |
| } |
| } |
| |
| static public void closeBundleLoader(BundleLoaderProxy proxy) { |
| if (proxy == null) |
| return; |
| // First close the BundleLoader |
| BundleLoader loader = proxy.getBasicBundleLoader(); |
| if (loader != null) |
| loader.close(); |
| proxy.setStale(); |
| // if proxy is not null then make sure to unset user object |
| // associated with the proxy in the state |
| BundleDescription description = proxy.getBundleDescription(); |
| // must set it back to the bundle object; not null |
| // need to make sure the user object is a BundleReference |
| description.setUserObject(proxy.getBundleData()); |
| } |
| } |