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.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import org.eclipse.osgi.container.Module;
import org.eclipse.osgi.container.ModuleCapability;
import org.eclipse.osgi.container.ModuleLoader;
import org.eclipse.osgi.container.ModuleRequirement;
import org.eclipse.osgi.container.ModuleRevision;
import org.eclipse.osgi.container.ModuleRevisionBuilder;
import org.eclipse.osgi.container.ModuleWire;
import org.eclipse.osgi.container.ModuleWiring;
import org.eclipse.osgi.container.builders.OSGiManifestBuilderFactory;
import org.eclipse.osgi.container.namespaces.EquinoxModuleDataNamespace;
import org.eclipse.osgi.internal.debug.Debug;
import org.eclipse.osgi.internal.framework.EquinoxConfiguration;
import org.eclipse.osgi.internal.framework.EquinoxContainer;
import org.eclipse.osgi.internal.hookregistry.ClassLoaderHook;
import org.eclipse.osgi.internal.loader.buddy.PolicyHandler;
import org.eclipse.osgi.internal.loader.sources.MultiSourcePackage;
import org.eclipse.osgi.internal.loader.sources.NullPackageSource;
import org.eclipse.osgi.internal.loader.sources.PackageSource;
import org.eclipse.osgi.internal.loader.sources.SingleSourcePackage;
import org.eclipse.osgi.storage.BundleInfo.Generation;
import org.eclipse.osgi.util.ManifestElement;
import org.osgi.framework.Bundle;
import org.osgi.framework.BundleContext;
import org.osgi.framework.BundleException;
import org.osgi.framework.Constants;
import org.osgi.framework.FrameworkEvent;
import org.osgi.framework.namespace.BundleNamespace;
import org.osgi.framework.namespace.HostNamespace;
import org.osgi.framework.namespace.PackageNamespace;
import org.osgi.framework.wiring.BundleWiring;
public class BundleLoader extends ModuleLoader {
public final static String DEFAULT_PACKAGE = ".";
public final static String JAVA_PACKAGE = "java.";
public final static ClassContext CLASS_CONTEXT = AccessController.doPrivileged(new PrivilegedAction<ClassContext>() {
@Override
public ClassContext run() {
return new ClassContext();
}
});
public final static ClassLoader FW_CLASSLOADER = getClassLoader(EquinoxContainer.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 Pattern PACKAGENAME_FILTER = Pattern.compile("\\(osgi.wiring.package\\s*=\\s*([^)]+)\\)");
@SuppressWarnings("rawtypes")
private final static Enumeration EMPTY_ENUMERATION = Collections.enumeration(Collections.emptyList());
private final ModuleWiring wiring;
private final EquinoxContainer container;
private final Debug debug;
private final PolicyHandler policy;
private final Collection<String> exportedPackages;
private final BundleLoaderSources exportSources;
private final Map<String, PackageSource> requiredSources = new HashMap<>();
private final Map<String, PackageSource> importedSources = new HashMap<>();
private final List<ModuleWire> requiredBundleWires;
private boolean importsInitialized = false;
private boolean dynamicAllPackages;
private String[] dynamicImportPackageStems;
private String[] dynamicImportPackages;
private final Object classLoaderCreatedMonitor = new Object();
private ModuleClassLoader classLoaderCreated;
private volatile ModuleClassLoader classloader;
private final ClassLoader parent;
private final AtomicBoolean triggerClassLoaded = new AtomicBoolean(false);
private final AtomicBoolean firstUseOfInvalidLoader = new AtomicBoolean(false);
public final static String getPackageName(String name) {
if (name != null) {
int index = name.lastIndexOf('.');
if (index > 0)
return name.substring(0, index);
}
return DEFAULT_PACKAGE;
}
public final static String getResourcePackageName(String name) {
if (name != null) {
int begin = ((name.length() > 1) && (name.charAt(0) == '/')) ? 1 : 0;
int end = name.lastIndexOf('/');
if (end > begin)
return name.substring(begin, end).replace('/', '.');
}
return DEFAULT_PACKAGE;
}
public BundleLoader(ModuleWiring wiring, EquinoxContainer container, ClassLoader parent) {
this.wiring = wiring;
this.container = container;
this.debug = container.getConfiguration().getDebug();
this.parent = parent;
exportSources = new BundleLoaderSources(this);
List<ModuleCapability> exports = wiring.getModuleCapabilities(PackageNamespace.PACKAGE_NAMESPACE);
exports = exports == null ? new ArrayList<ModuleCapability>(0) : exports;
exportedPackages = Collections.synchronizedCollection(exports.size() > 10 ? new HashSet<String>(exports.size()) : new ArrayList<String>(exports.size()));
initializeExports(exports, exportSources, exportedPackages);
addDynamicImportPackage(wiring.getModuleRequirements(PackageNamespace.PACKAGE_NAMESPACE));
List<ModuleWire> currentRequireBundleWires = wiring.getRequiredModuleWires(BundleNamespace.BUNDLE_NAMESPACE);
requiredBundleWires = currentRequireBundleWires == null || currentRequireBundleWires.isEmpty() ? Collections.<ModuleWire> emptyList() : Collections.unmodifiableList(currentRequireBundleWires);
List<ModuleCapability> moduleDatas = wiring.getRevision().getModuleCapabilities(EquinoxModuleDataNamespace.MODULE_DATA_NAMESPACE);
@SuppressWarnings("unchecked")
List<String> buddyList = (List<String>) (moduleDatas.isEmpty() ? null : moduleDatas.get(0).getAttributes().get(EquinoxModuleDataNamespace.CAPABILITY_BUDDY_POLICY));
policy = buddyList != null ? new PolicyHandler(this, buddyList, container.getPackageAdmin(), container.getBootLoader()) : null;
if (policy != null) {
Module systemModule = container.getStorage().getModuleContainer().getModule(0);
Bundle systemBundle = systemModule.getBundle();
policy.open(systemBundle.getBundleContext());
}
}
public ModuleWiring getWiring() {
return wiring;
}
public void addFragmentExports(List<ModuleCapability> exports) {
initializeExports(exports, exportSources, exportedPackages);
}
private static void initializeExports(List<ModuleCapability> exports, BundleLoaderSources sources, Collection<String> exportNames) {
if (exports != null) {
for (ModuleCapability export : exports) {
String name = (String) export.getAttributes().get(PackageNamespace.PACKAGE_NAMESPACE);
if (sources.forceSourceCreation(export)) {
if (!exportNames.contains(name)) {
sources.createPackageSource(export, true);
}
}
exportNames.add(name);
}
}
}
final PackageSource createExportPackageSource(ModuleWire importWire, Collection<BundleLoader> visited) {
String name = (String) importWire.getCapability().getAttributes().get(PackageNamespace.PACKAGE_NAMESPACE);
BundleLoader providerLoader = getProviderLoader(importWire);
if (providerLoader == null) {
return createMultiSource(name, new PackageSource[0]);
}
PackageSource requiredSource = providerLoader.findRequiredSource(name, visited);
PackageSource exportSource = providerLoader.exportSources.createPackageSource(importWire.getCapability(), false);
if (requiredSource == null)
return exportSource;
return createMultiSource(name, new PackageSource[] {requiredSource, exportSource});
}
private static PackageSource createMultiSource(String packageName, PackageSource[] sources) {
if (sources.length == 1)
return sources[0];
List<SingleSourcePackage> sourceList = new ArrayList<>(sources.length);
for (PackageSource source : sources) {
SingleSourcePackage[] innerSources = source.getSuppliers();
for (SingleSourcePackage innerSource : innerSources) {
if (!sourceList.contains(innerSource)) {
sourceList.add(innerSource);
}
}
}
return new MultiSourcePackage(packageName, sourceList.toArray(new SingleSourcePackage[sourceList.size()]));
}
public ModuleClassLoader getModuleClassLoader() {
ModuleClassLoader result = classloader;
if (result != null) {
return result;
}
final List<ClassLoaderHook> hooks = container.getConfiguration().getHookRegistry().getClassLoaderHooks();
final Generation generation = (Generation) wiring.getRevision().getRevisionInfo();
if (System.getSecurityManager() == null) {
result = createClassLoaderPrivledged(parent, generation.getBundleInfo().getStorage().getConfiguration(), this, generation, hooks);
} else {
final ClassLoader cl = parent;
result = AccessController.doPrivileged(new PrivilegedAction<ModuleClassLoader>() {
@Override
public ModuleClassLoader run() {
return createClassLoaderPrivledged(cl, generation.getBundleInfo().getStorage().getConfiguration(), BundleLoader.this, generation, hooks);
}
});
}
synchronized (classLoaderCreatedMonitor) {
if (classLoaderCreated == null) {
classLoaderCreated = result;
final ModuleClassLoader cl = result;
AccessController.doPrivileged(new PrivilegedAction<Object>() {
@Override
public Object run() {
for (ClassLoaderHook hook : hooks) {
hook.classLoaderCreated(cl);
}
return null;
}
});
classloader = classLoaderCreated;
} else {
result = classLoaderCreated;
if (debug.DEBUG_LOADER) {
Debug.println("BundleLoader[" + this + "].getModuleClassLoader() - created duplicate classloader");
}
}
}
return result;
}
@Override
protected void loadFragments(Collection<ModuleRevision> fragments) {
addFragmentExports(wiring.getModuleCapabilities(PackageNamespace.PACKAGE_NAMESPACE));
loadClassLoaderFragments(fragments);
clearManifestLocalizationCache();
}
protected void clearManifestLocalizationCache() {
Generation hostGen = (Generation) wiring.getRevision().getRevisionInfo();
hostGen.clearManifestCache();
List<ModuleWire> hostWires = wiring.getProvidedModuleWires(HostNamespace.HOST_NAMESPACE);
if (hostWires != null) {
for (ModuleWire fragmentWire : hostWires) {
Generation fragGen = (Generation) fragmentWire.getRequirer().getRevisionInfo();
fragGen.clearManifestCache();
}
}
}
void loadClassLoaderFragments(Collection<ModuleRevision> fragments) {
ModuleClassLoader current = classloader;
if (current != null) {
current.loadFragments(fragments);
}
}
static ModuleClassLoader createClassLoaderPrivledged(ClassLoader parent, EquinoxConfiguration configuration, BundleLoader delegate, Generation generation, List<ClassLoaderHook> hooks) {
for (ClassLoaderHook hook : hooks) {
ModuleClassLoader hookClassLoader = hook.createClassLoader(parent, configuration, delegate, generation);
if (hookClassLoader != null) {
return hookClassLoader;
}
}
return new EquinoxClassLoader(parent, configuration, delegate, generation);
}
public void close() {
if (policy != null) {
Module systemModule = container.getStorage().getModuleContainer().getModule(0);
BundleContext context = systemModule.getBundle().getBundleContext();
if (context != null) {
policy.close(context);
}
}
ModuleClassLoader current = classloader;
if (current != null) {
current.close();
}
}
@Override
protected ClassLoader getClassLoader() {
return getModuleClassLoader();
}
public ClassLoader getParentClassLoader() {
return this.parent;
}
final URL getResource(String name) {
return getModuleClassLoader().getResource(name);
}
public Class<?> findLocalClass(String name) throws ClassNotFoundException {
long start = 0;
if (debug.DEBUG_LOADER) {
Debug.println("BundleLoader[" + this + "].findLocalClass(" + name + ")");
start = System.currentTimeMillis();
}
try {
Class<?> clazz = getModuleClassLoader().findLocalClass(name);
if (debug.DEBUG_LOADER && clazz != null) {
long time = System.currentTimeMillis() - start;
Debug.println("BundleLoader[" + this + "] found local class " + name + " " + time + "ms");
}
return clazz;
} catch (ClassNotFoundException e) {
if (e.getCause() instanceof BundleException) {
throw e;
}
return null;
}
}
public Class<?> findClass(String name) throws ClassNotFoundException {
return findClass(name, true);
}
Class<?> findClass(String name, boolean checkParent) throws ClassNotFoundException {
if (checkParent && parent != null && name.startsWith(JAVA_PACKAGE))
return parent.loadClass(name);
return findClassInternal(name, checkParent);
}
private Class<?> findClassInternal(String name, boolean checkParent) throws ClassNotFoundException {
if (debug.DEBUG_LOADER)
Debug.println("BundleLoader[" + this + "].findClassInternal(" + name + ")");
String pkgName = getPackageName(name);
boolean bootDelegation = false;
if (checkParent && parent != null && container.isBootDelegationPackage(pkgName)) {
try {
return parent.loadClass(name);
} catch (ClassNotFoundException cnfe) {
bootDelegation = true;
}
}
Class<?> result = null;
try {
result = (Class<?>) searchHooks(name, PRE_CLASS);
} catch (ClassNotFoundException e) {
throw e;
} catch (FileNotFoundException e) {
}
if (result != null)
return result;
PackageSource source = findImportedSource(pkgName, null);
if (source != null) {
if (debug.DEBUG_LOADER) {
Debug.println("BundleLoader[" + this + "] loading from import package: " + source);
}
result = source.loadClass(name);
if (result != null)
return result;
throw new ClassNotFoundException(name + " cannot be found by " + this);
}
source = findRequiredSource(pkgName, null);
if (source != null) {
if (debug.DEBUG_LOADER) {
Debug.println("BundleLoader[" + this + "] loading from required bundle package: " + source);
}
result = source.loadClass(name);
}
if (result == null)
result = findLocalClass(name);
if (result != null)
return result;
if (source == null) {
source = findDynamicSource(pkgName);
if (source != null) {
result = source.loadClass(name);
if (result != null)
return result;
throw new ClassNotFoundException(name + " cannot be found by " + this);
}
}
if (result == null)
try {
result = (Class<?>) searchHooks(name, POST_CLASS);
} catch (ClassNotFoundException e) {
throw e;
} catch (FileNotFoundException e) {
}
if (result == null && policy != null)
result = policy.doBuddyClassLoading(name);
if (result != null)
return result;
if (parent != null && !bootDelegation && ((checkParent && container.getConfiguration().compatibilityBootDelegation) || isRequestFromVM())) {
try {
return parent.loadClass(name);
} catch (ClassNotFoundException e) {
}
}
throw new ClassNotFoundException(name + " cannot be found by " + this);
}
@SuppressWarnings("unchecked")
private <E> E searchHooks(String name, int type) throws ClassNotFoundException, FileNotFoundException {
List<ClassLoaderHook> loaderHooks = container.getConfiguration().getHookRegistry().getClassLoaderHooks();
if (loaderHooks == null)
return null;
E result = null;
for (ClassLoaderHook hook : loaderHooks) {
switch (type) {
case PRE_CLASS :
result = (E) hook.preFindClass(name, getModuleClassLoader());
break;
case POST_CLASS :
result = (E) hook.postFindClass(name, getModuleClassLoader());
break;
case PRE_RESOURCE :
result = (E) hook.preFindResource(name, getModuleClassLoader());
break;
case POST_RESOURCE :
result = (E) hook.postFindResource(name, getModuleClassLoader());
break;
case PRE_RESOURCES :
result = (E) hook.preFindResources(name, getModuleClassLoader());
break;
case POST_RESOURCES :
result = (E) hook.postFindResources(name, getModuleClassLoader());
break;
}
if (result != null) {
return result;
}
}
return result;
}
private boolean isRequestFromVM() {
if (!container.getConfiguration().contextBootDelegation)
return false;
Class<?>[] context = CLASS_CONTEXT.getClassContext();
if (context == null || context.length < 2)
return false;
for (int i = 1; i < context.length; i++) {
Class<?> clazz = context[i];
if (clazz != BundleLoader.class && !ModuleClassLoader.class.isAssignableFrom(clazz) && clazz != ClassLoader.class && clazz != Class.class && !clazz.getName().equals("java.lang.J9VMInternals")) {
if (Bundle.class.isAssignableFrom(clazz)) {
return false;
}
ClassLoader cl = getClassLoader(clazz);
if (cl != FW_CLASSLOADER) {
ClassLoader last = null;
while (cl != null && cl != last) {
last = cl;
if (cl instanceof ModuleClassLoader) {
return false;
}
cl = getClassLoader(cl.getClass());
}
return true;
}
}
}
return false;
}
private static ClassLoader getClassLoader(final Class<?> clazz) {
if (System.getSecurityManager() == null)
return clazz.getClassLoader();
return AccessController.doPrivileged(new PrivilegedAction<ClassLoader>() {
@Override
public ClassLoader run() {
return clazz.getClassLoader();
}
});
}
public URL findResource(String name) {
if (debug.DEBUG_LOADER)
Debug.println("BundleLoader[" + this + "].findResource(" + name + ")");
if ((name.length() > 1) && (name.charAt(0) == '/'))
name = name.substring(1);
String pkgName = getResourcePackageName(name);
boolean bootDelegation = false;
if (parent != null) {
if (pkgName.startsWith(JAVA_PACKAGE))
return parent.getResource(name);
else if (container.isBootDelegationPackage(pkgName)) {
URL result = parent.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) {
}
if (result != null)
return result;
PackageSource source = findImportedSource(pkgName, null);
if (source != null) {
if (debug.DEBUG_LOADER) {
Debug.println("BundleLoader[" + this + "] loading from import package: " + source);
}
return source.getResource(name);
}
source = findRequiredSource(pkgName, null);
if (source != null) {
if (debug.DEBUG_LOADER) {
Debug.println("BundleLoader[" + this + "] loading from required bundle package: " + source);
}
result = source.getResource(name);
}
if (result == null)
result = findLocalResource(name);
if (result != null)
return result;
if (source == null) {
source = findDynamicSource(pkgName);
if (source != null)
return source.getResource(name);
}
if (result == null)
try {
result = (URL) searchHooks(name, POST_RESOURCE);
} catch (FileNotFoundException e) {
return null;
} catch (ClassNotFoundException e) {
}
if (result == null && policy != null)
result = policy.doBuddyResourceLoading(name);
if (result != null)
return result;
if (parent != null && !bootDelegation && (container.getConfiguration().compatibilityBootDelegation || isRequestFromVM()))
return parent.getResource(name);
return result;
}
public Enumeration<URL> findResources(String name) throws IOException {
if (debug.DEBUG_LOADER)
Debug.println("BundleLoader[" + this + "].findResources(" + name + ")");
if ((name.length() > 1) && (name.charAt(0) == '/'))
name = name.substring(1);
String pkgName = getResourcePackageName(name);
Enumeration<URL> result = emptyEnumeration();
boolean bootDelegation = false;
if (parent != null) {
if (pkgName.startsWith(JAVA_PACKAGE))
return parent.getResources(name);
else if (container.isBootDelegationPackage(pkgName)) {
result = compoundEnumerations(result, parent.getResources(name));
bootDelegation = true;
}
}
try {
Enumeration<URL> hookResources = searchHooks(name, PRE_RESOURCES);
if (hookResources != null) {
return compoundEnumerations(result, hookResources);
}
} catch (ClassNotFoundException e) {
} catch (FileNotFoundException e) {
return result;
}
PackageSource source = findImportedSource(pkgName, null);
if (source != null) {
if (debug.DEBUG_LOADER) {
Debug.println("BundleLoader[" + this + "] loading from import package: " + source);
}
return compoundEnumerations(result, source.getResources(name));
}
source = findRequiredSource(pkgName, null);
if (source != null) {
if (debug.DEBUG_LOADER) {
Debug.println("BundleLoader[" + this + "] loading from required bundle package: " + source);
}
result = compoundEnumerations(result, source.getResources(name));
}
Enumeration<URL> localResults = findLocalResources(name);
result = compoundEnumerations(result, localResults);
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) {
} catch (FileNotFoundException e) {
return null;
}
if (policy != null) {
Enumeration<URL> buddyResult = policy.doBuddyResourcesLoading(name);
result = compoundEnumerations(result, buddyResult);
}
if (!result.hasMoreElements()) {
if (parent != null && !bootDelegation && (container.getConfiguration().compatibilityBootDelegation || isRequestFromVM()))
return parent.getResources(name);
}
return result;
}
private boolean isSubPackage(String parentPackage, String subPackage) {
String prefix = (parentPackage.length() == 0 || parentPackage.equals(DEFAULT_PACKAGE)) ? "" : parentPackage + '.';
return subPackage.startsWith(prefix);
}
@Override
protected Collection<String> listResources(String path, String filePattern, int options) {
String pkgName = getResourcePackageName(path.endsWith("/") ? path : path + '/');
if ((path.length() > 1) && (path.charAt(0) == '/'))
path = path.substring(1);
boolean subPackages = (options & BundleWiring.LISTRESOURCES_RECURSE) != 0;
List<String> packages = new ArrayList<>();
Map<String, PackageSource> importSources = getImportedSources(null);
Collection<PackageSource> imports;
synchronized (importSources) {
imports = new ArrayList<>(importSources.values());
}
for (PackageSource source : imports) {
String id = source.getId();
if (id.equals(pkgName) || (subPackages && isSubPackage(pkgName, id)))
packages.add(id);
}
Collection<BundleLoader> visited = new ArrayList<>();
visited.add(this);
for (ModuleWire bundleWire : requiredBundleWires) {
BundleLoader loader = getProviderLoader(bundleWire);
if (loader != null) {
loader.addProvidedPackageNames(pkgName, packages, subPackages, visited);
}
}
boolean localSearch = (options & BundleWiring.LISTRESOURCES_LOCAL) != 0;
LinkedHashSet<String> result = new LinkedHashSet<>();
Set<String> importedPackages = new HashSet<>(0);
for (String name : packages) {
PackageSource externalSource = findImportedSource(name, null);
if (externalSource != null) {
importedPackages.add(name);
} else {
externalSource = findRequiredSource(name, null);
}
if (externalSource != null && !localSearch) {
String packagePath = name.replace('.', '/');
Collection<String> externalResources = externalSource.listResources(packagePath, filePattern);
for (String resource : externalResources) {
if (!result.contains(resource))
result.add(resource);
}
}
}
Collection<String> localResources = getModuleClassLoader().listLocalResources(path, filePattern, options);
for (String resource : localResources) {
String resourcePkg = getResourcePackageName(resource);
if (!importedPackages.contains(resourcePkg) && !result.contains(resource))
result.add(resource);
}
return result;
}
@Override
protected List<URL> findEntries(String path, String filePattern, int options) {
return getModuleClassLoader().findEntries(path, filePattern, options);
}
public static <E> Enumeration<E> compoundEnumerations(Enumeration<E> list1, Enumeration<E> list2) {
if (list2 == null || !list2.hasMoreElements())
return list1 == null ? BundleLoader.<E> emptyEnumeration() : list1;
if (list1 == null || !list1.hasMoreElements())
return list2 == null ? BundleLoader.<E> emptyEnumeration() : list2;
List<E> compoundResults = new ArrayList<>();
while (list1.hasMoreElements())
compoundResults.add(list1.nextElement());
while (list2.hasMoreElements()) {
E item = list2.nextElement();
if (!compoundResults.contains(item))
compoundResults.add(item);
}
return Collections.enumeration(compoundResults);
}
@SuppressWarnings("unchecked")
private static <E> Enumeration<E> emptyEnumeration() {
return EMPTY_ENUMERATION;
}
public URL findLocalResource(final String name) {
return getModuleClassLoader().findLocalResource(name);
}
public Enumeration<URL> findLocalResources(String name) {
return getModuleClassLoader().findLocalResources(name);
}
@Override
public final String toString() {
ModuleRevision revision = wiring.getRevision();
String name = revision.getSymbolicName();
if (name == null)
name = "unknown";
return name + '_' + revision.getVersion();
}
private final boolean isDynamicallyImported(String pkgname) {
if (this instanceof SystemBundleLoader)
return false;
if (pkgname.startsWith("java."))
return true;
synchronized (importedSources) {
if (dynamicAllPackages)
return true;
if (dynamicImportPackages != null)
for (String dynamicImportPackage : dynamicImportPackages) {
if (pkgname.equals(dynamicImportPackage)) {
return true;
}
}
if (dynamicImportPackageStems != null)
for (String dynamicImportPackageStem : dynamicImportPackageStems) {
if (pkgname.startsWith(dynamicImportPackageStem)) {
return true;
}
}
}
return false;
}
final void addExportedProvidersFor(String packageName, List<PackageSource> result, Collection<BundleLoader> visited) {
if (visited.contains(this))
return;
visited.add(this);
PackageSource local = null;
if (isExportedPackage(packageName))
local = exportSources.getPackageSource(packageName);
else if (isSubstitutedExport(packageName)) {
result.add(findImportedSource(packageName, visited));
return;
}
for (ModuleWire bundleWire : requiredBundleWires) {
if (local != null || BundleNamespace.VISIBILITY_REEXPORT.equals(bundleWire.getRequirement().getDirectives().get(BundleNamespace.REQUIREMENT_VISIBILITY_DIRECTIVE))) {
BundleLoader loader = getProviderLoader(bundleWire);
if (loader != null) {
loader.addExportedProvidersFor(packageName, result, visited);
}
}
}
if (local != null)
result.add(local);
}
private BundleLoader getProviderLoader(ModuleWire wire) {
ModuleWiring provider = wire.getProviderWiring();
if (provider == null) {
if (firstUseOfInvalidLoader.getAndSet(true)) {
String message = "Invalid class loader from a refreshed bundle is being used: " + toString();
container.getEventPublisher().publishFrameworkEvent(FrameworkEvent.ERROR, wiring.getBundle(), new IllegalStateException(message));
}
return null;
}
return (BundleLoader) provider.getModuleLoader();
}
final void addProvidedPackageNames(String packageName, List<String> result, boolean subPackages, Collection<BundleLoader> visited) {
if (visited.contains(this))
return;
visited.add(this);
synchronized (exportedPackages) {
for (String exported : exportedPackages) {
if (exported.equals(packageName) || (subPackages && isSubPackage(packageName, exported))) {
if (!result.contains(exported))
result.add(exported);
}
}
}
for (String substituted : wiring.getSubstitutedNames()) {
if (substituted.equals(packageName) || (subPackages && isSubPackage(packageName, substituted))) {
if (!result.contains(substituted))
result.add(substituted);
}
}
for (ModuleWire bundleWire : requiredBundleWires) {
if (BundleNamespace.VISIBILITY_REEXPORT.equals(bundleWire.getRequirement().getDirectives().get(BundleNamespace.REQUIREMENT_VISIBILITY_DIRECTIVE))) {
BundleLoader loader = getProviderLoader(bundleWire);
if (loader != null) {
loader.addProvidedPackageNames(packageName, result, subPackages, visited);
}
}
}
}
public final boolean isExportedPackage(String name) {
return exportedPackages.contains(name);
}
final boolean isSubstitutedExport(String name) {
return wiring.isSubstitutedPackage(name);
}
private void addDynamicImportPackage(List<ModuleRequirement> packageImports) {
if (packageImports == null || packageImports.isEmpty()) {
return;
}
List<String> dynamicImports = new ArrayList<>(packageImports.size());
for (ModuleRequirement packageImport : packageImports) {
if (PackageNamespace.RESOLUTION_DYNAMIC.equals(packageImport.getDirectives().get(PackageNamespace.REQUIREMENT_RESOLUTION_DIRECTIVE))) {
Matcher matcher = PACKAGENAME_FILTER.matcher(packageImport.getDirectives().get(PackageNamespace.REQUIREMENT_FILTER_DIRECTIVE));
if (matcher.find()) {
String dynamicName = matcher.group(1);
if (dynamicName != null) {
dynamicImports.add(dynamicName);
}
}
}
}
if (dynamicImports.size() > 0)
addDynamicImportPackage(dynamicImports.toArray(new String[dynamicImports.size()]));
}
private void addDynamicImportPackage(String[] packages) {
if (packages == null)
return;
synchronized (importedSources) {
int size = packages.length;
List<String> stems;
if (dynamicImportPackageStems == null) {
stems = new ArrayList<>(size);
} else {
stems = new ArrayList<>(size + dynamicImportPackageStems.length);
for (String dynamicImportPackageStem : dynamicImportPackageStems) {
stems.add(dynamicImportPackageStem);
}
}
List<String> names;
if (dynamicImportPackages == null) {
names = new ArrayList<>(size);
} else {
names = new ArrayList<>(size + dynamicImportPackages.length);
for (String dynamicImportPackage : dynamicImportPackages) {
names.add(dynamicImportPackage);
}
}
for (int i = 0; i < size; i++) {
String name = packages[i];
if (isDynamicallyImported(name))
continue;
if (name.equals("*")) {
dynamicAllPackages = true;
return;
}
if (name.endsWith(".*"))
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]);
}
}
public final void addDynamicImportPackage(ManifestElement[] packages) {
if (packages == null)
return;
List<String> dynamicImports = new ArrayList<>(packages.length);
StringBuilder importSpec = new StringBuilder();
for (ManifestElement dynamicImportElement : packages) {
String[] names = dynamicImportElement.getValueComponents();
Collections.addAll(dynamicImports, names);
if (importSpec.length() > 0) {
importSpec.append(',');
}
importSpec.append(dynamicImportElement.toString());
}
if (dynamicImports.size() > 0) {
addDynamicImportPackage(dynamicImports.toArray(new String[dynamicImports.size()]));
Map<String, String> dynamicImportMap = new HashMap<>();
dynamicImportMap.put(Constants.DYNAMICIMPORT_PACKAGE, importSpec.toString());
try {
ModuleRevisionBuilder builder = OSGiManifestBuilderFactory.createBuilder(dynamicImportMap);
wiring.addDynamicImports(builder);
} catch (BundleException e) {
throw new RuntimeException(e);
}
}
}
private PackageSource findSource(String pkgName) {
if (pkgName == null)
return null;
PackageSource result = findImportedSource(pkgName, null);
if (result != null)
return result;
return findRequiredSource(pkgName, null);
}
private PackageSource findImportedSource(String pkgName, Collection<BundleLoader> visited) {
Map<String, PackageSource> imports = getImportedSources(visited);
synchronized (imports) {
return imports.get(pkgName);
}
}
private Map<String, PackageSource> getImportedSources(Collection<BundleLoader> visited) {
synchronized (importedSources) {
if (importsInitialized) {
return importedSources;
}
List<ModuleWire> importWires = wiring.getRequiredModuleWires(PackageNamespace.PACKAGE_NAMESPACE);
if (importWires != null) {
for (ModuleWire importWire : importWires) {
PackageSource source = createExportPackageSource(importWire, visited);
if (source != null) {
importedSources.put(source.getId(), source);
}
}
}
importsInitialized = true;
return importedSources;
}
}
private PackageSource findDynamicSource(String pkgName) {
if (!isExportedPackage(pkgName) && isDynamicallyImported(pkgName)) {
if (debug.DEBUG_LOADER) {
Debug.println("BundleLoader[" + this + "] attempting to resolve dynamic package: " + pkgName);
}
ModuleRevision revision = wiring.getRevision();
ModuleWire dynamicWire = revision.getRevisions().getModule().getContainer().resolveDynamic(pkgName, revision);
if (dynamicWire != null) {
PackageSource source = createExportPackageSource(dynamicWire, null);
if (debug.DEBUG_LOADER) {
Debug.println("BundleLoader[" + this + "] using dynamic import source: " + source);
}
synchronized (importedSources) {
importedSources.put(source.getId(), source);
}
return source;
}
}
return null;
}
private PackageSource findRequiredSource(String pkgName, Collection<BundleLoader> visited) {
if (requiredBundleWires.isEmpty()) {
return null;
}
synchronized (requiredSources) {
PackageSource result = requiredSources.get(pkgName);
if (result != null)
return result.isNullSource() ? null : result;
}
if (visited == null)
visited = new ArrayList<>();
if (!visited.contains(this))
visited.add(this);
List<PackageSource> result = new ArrayList<>(3);
for (ModuleWire bundleWire : requiredBundleWires) {
BundleLoader loader = getProviderLoader(bundleWire);
if (loader != null) {
loader.addExportedProvidersFor(pkgName, result, visited);
}
}
PackageSource source;
if (result.size() == 0) {
source = NullPackageSource.getNullPackageSource(pkgName);
} else if (result.size() == 1) {
source = result.get(0);
} else {
PackageSource[] srcs = result.toArray(new PackageSource[result.size()]);
source = createMultiSource(pkgName, srcs);
}
synchronized (requiredSources) {
requiredSources.put(source.getId(), source);
}
return source.isNullSource() ? null : source;
}
public final PackageSource getPackageSource(String pkgName) {
PackageSource result = findSource(pkgName);
if (!isExportedPackage(pkgName))
return result;
PackageSource localSource = exportSources.getPackageSource(pkgName);
if (result == null)
return localSource;
if (localSource == null)
return result;
return createMultiSource(pkgName, new PackageSource[] {result, localSource});
}
static final class ClassContext extends SecurityManager {
@Override
public Class<?>[] getClassContext() {
return super.getClassContext();
}
}
@Override
protected boolean getAndSetTrigger() {
return triggerClassLoaded.getAndSet(true);
}
@Override
public boolean isTriggerSet() {
return triggerClassLoaded.get();
}
}