blob: 6f2e494d257756f49d9b455c1c4f9e4ab34ece4c [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2003, 2008 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.security.AccessController;
import java.security.PrivilegedAction;
import java.util.ArrayList;
import org.eclipse.osgi.framework.internal.core.*;
import org.eclipse.osgi.framework.internal.core.Constants;
import org.eclipse.osgi.framework.util.KeyedHashSet;
import org.eclipse.osgi.framework.util.SecureAction;
import org.eclipse.osgi.internal.composite.CompositeBase;
import org.eclipse.osgi.service.resolver.BundleDescription;
import org.eclipse.osgi.service.resolver.ExportPackageDescription;
import org.osgi.framework.*;
import org.osgi.service.packageadmin.RequiredBundle;
/*
* The BundleLoaderProxy proxies a BundleLoader object for a Bundle. This
* allows for a Bundle's depedencies to be linked without forcing the
* creating of the BundleLoader or BundleClassLoader objects. This class
* keeps track of the depedencies between the bundles installed in the
* Framework.
*/
public class BundleLoaderProxy implements RequiredBundle {
static SecureAction secureAction = (SecureAction) AccessController.doPrivileged(SecureAction.createSecureAction());
// The BundleLoader that this BundleLoaderProxy is managing
private BundleLoader loader;
// The Bundle that this BundleLoaderProxy is for
final private BundleHost bundle;
// the BundleDescription for the Bundle
final private BundleDescription description;
// Indicates if this BundleLoaderProxy is stale;
// this is true when the bundle is updated or uninstalled.
private boolean stale = false;
// cached of package sources for the bundle
final private KeyedHashSet pkgSources;
public BundleLoaderProxy(BundleHost bundle, BundleDescription description) {
this.bundle = bundle;
this.description = description;
this.pkgSources = new KeyedHashSet(false);
}
public BundleLoader getBundleLoader() {
if (System.getSecurityManager() == null)
return getBundleLoader0();
return (BundleLoader) AccessController.doPrivileged(new PrivilegedAction() {
public Object run() {
return getBundleLoader0();
}
});
}
synchronized BundleLoader getBundleLoader0() {
if (loader != null)
return loader;
if (bundle.isResolved()) {
try {
if (bundle.getBundleId() == 0) // this is the system bundle
loader = new SystemBundleLoader(bundle, this);
else
loader = new BundleLoader(bundle, this);
} catch (BundleException e) {
bundle.getFramework().publishFrameworkEvent(FrameworkEvent.ERROR, bundle, e);
return null;
}
}
return loader;
}
public BundleLoader getBasicBundleLoader() {
return loader;
}
public AbstractBundle getBundleHost() {
return bundle;
}
void setStale() {
stale = true;
}
public boolean isStale() {
return stale;
}
public String toString() {
String symbolicName = bundle.getSymbolicName();
StringBuffer sb = new StringBuffer(symbolicName == null ? bundle.getBundleData().getLocation() : symbolicName);
sb.append("; ").append(Constants.BUNDLE_VERSION_ATTRIBUTE); //$NON-NLS-1$
sb.append("=\"").append(description.getVersion().toString()).append("\""); //$NON-NLS-1$//$NON-NLS-2$
return sb.toString();
}
public org.osgi.framework.Bundle getBundle() {
if (isStale())
return null;
return bundle;
}
public org.osgi.framework.Bundle[] getRequiringBundles() {
if (isStale())
return null;
// This is VERY slow; but never gets called in regular execution.
BundleDescription[] dependents = description.getDependents();
if (dependents == null || dependents.length == 0)
return new Bundle[0];
ArrayList result = new ArrayList(dependents.length);
for (int i = 0; i < dependents.length; i++)
addRequirers(dependents[i], result);
return (Bundle[]) result.toArray(new org.osgi.framework.Bundle[result.size()]);
}
void addRequirers(BundleDescription dependent, ArrayList result) {
if (dependent.getHost() != null) // don't look in fragments.
return;
BundleLoaderProxy dependentProxy = getBundleLoader().getLoaderProxy(dependent);
if (dependentProxy == null)
return; // bundle must have been uninstalled
if (result.contains(dependentProxy.bundle))
return; // prevent endless recusion
BundleLoader dependentLoader = dependentProxy.getBundleLoader();
BundleLoaderProxy[] requiredBundles = dependentLoader.requiredBundles;
int[] reexportTable = dependentLoader.reexportTable;
if (requiredBundles == null)
return;
int size = reexportTable == null ? 0 : reexportTable.length;
int reexportIndex = 0;
for (int i = 0; i < requiredBundles.length; i++) {
if (requiredBundles[i] == this) {
result.add(dependentProxy.bundle);
if (reexportIndex < size && reexportTable[reexportIndex] == i) {
reexportIndex++;
BundleDescription[] dependents = dependent.getDependents();
if (dependents == null)
return;
for (int j = 0; j < dependents.length; j++)
dependentProxy.addRequirers(dependents[j], result);
}
return;
}
}
return;
}
public String getSymbolicName() {
return description.getSymbolicName();
}
public Version getVersion() {
return description.getVersion();
}
public boolean isRemovalPending() {
return description.isRemovalPending();
}
BundleDescription getBundleDescription() {
return description;
}
PackageSource getPackageSource(String pkgName) {
// getByKey is called outside of a synch block because we really do not
// care too much of duplicates getting created. Only the first one will
// successfully get stored into pkgSources
PackageSource pkgSource = (PackageSource) pkgSources.getByKey(pkgName);
if (pkgSource == null) {
pkgSource = new SingleSourcePackage(pkgName, this);
synchronized (pkgSources) {
pkgSources.add(pkgSource);
}
}
return pkgSource;
}
public boolean inUse() {
return (description.getDependents().length > 0) || ((bundle instanceof CompositeBase) && description.getResolvedImports().length > 0);
}
boolean forceSourceCreation(ExportPackageDescription export) {
boolean strict = Constants.STRICT_MODE.equals(secureAction.getProperty(Constants.OSGI_RESOLVER_MODE));
return (export.getDirective(Constants.INCLUDE_DIRECTIVE) != null) || (export.getDirective(Constants.EXCLUDE_DIRECTIVE) != null) || (strict && export.getDirective(Constants.FRIENDS_DIRECTIVE) != null);
}
// creates a PackageSource from an ExportPackageDescription. This is called when initializing
// a BundleLoader to ensure that the proper PackageSource gets created and used for
// filtered and reexport packages. The storeSource flag is used by initialize to indicate
// that the source for special case package sources (filtered or re-exported should be stored
// in the cache. if this flag is set then a normal SinglePackageSource will not be created
// (i.e. it will be created lazily)
public PackageSource createPackageSource(ExportPackageDescription export, boolean storeSource) {
PackageSource pkgSource = null;
// check to see if it is a filtered export
String includes = (String) export.getDirective(Constants.INCLUDE_DIRECTIVE);
String excludes = (String) export.getDirective(Constants.EXCLUDE_DIRECTIVE);
String[] friends = (String[]) export.getDirective(Constants.FRIENDS_DIRECTIVE);
if (friends != null) {
boolean strict = Constants.STRICT_MODE.equals(secureAction.getProperty(Constants.OSGI_RESOLVER_MODE));
if (!strict)
friends = null; // do not pay attention to friends if not in strict mode
}
if (includes != null || excludes != null || friends != null) {
pkgSource = new FilteredSourcePackage(export.getName(), this, includes, excludes, friends);
}
if (storeSource) {
// if the package source is not null then store the source only if it is not already present;
// getByKey is called outside of a synch block because we really do not
// care too much of duplicates getting created. Only the first one will
// successfully get stored into pkgSources
if (pkgSource != null && pkgSources.getByKey(export.getName()) == null)
synchronized (pkgSources) {
pkgSources.add(pkgSource);
}
} else {
// we are not storing the special case sources, but pkgSource == null this means this
// is a normal package source; get it and return it.
if (pkgSource == null) {
pkgSource = getPackageSource(export.getName());
// the first export cached may not be a simple single source like we need.
if (pkgSource.getClass() != SingleSourcePackage.class)
return new SingleSourcePackage(export.getName(), this);
}
}
return pkgSource;
}
}