blob: f02307e19fcfb553979bedee947fc4c6d4d01165 [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2003, 2004 IBM Corporation and others.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Common Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/cpl-v10.html
*
* Contributors:
* IBM Corporation - initial API and implementation
*******************************************************************************/
package org.eclipse.osgi.internal.resolver;
import java.util.*;
import org.eclipse.core.dependencies.*;
import org.eclipse.osgi.service.resolver.*;
import org.osgi.framework.Bundle;
public class ResolverImpl implements Resolver {
private State state;
private IDependencySystem dependencies;
public void resolve(BundleDescription[] reRefresh) {
// unresolving the given bundles will force them to be re-resolved
for (int i = 0; i < reRefresh.length; i++)
unresolveBundle(reRefresh[i]);
resolve();
}
/**
* TODO: need to devise a way to report problems (a la IStatus)
*/
public synchronized void resolve() {
if (state == null)
throw new IllegalStateException("RESOLVER_NO_STATE"); //$NON-NLS-1$
if (dependencies == null)
dependencies = ResolverHelper.buildDependencySystem(state, new Eclipse30SelectionPolicy());
IResolutionDelta delta;
try {
delta = dependencies.resolve();
} catch (IDependencySystem.CyclicSystemException e) {
//TODO: this should be logged instead
e.printStackTrace();
return;
}
processInnerDelta(delta);
resolvePackages();
}
public void setState(State newState) {
// to avoid infinite (mutual) recursion
if (state == newState)
return;
// if it was linked to a previous state, unlink first
if (state != null) {
State oldState = state;
state = null;
oldState.setResolver(null);
}
state = newState;
if (newState != null)
state.setResolver(this);
// forget any dependency state created before
flush();
}
/*
* Applies changes in the constraint system to the state object.
*/
private void processInnerDelta(IResolutionDelta delta) {
// now apply changes reported in the inner delta to the state
IElementChange[] changes = delta.getAllChanges();
for (int i = 0; i < changes.length; i++) {
IElement element = changes[i].getElement();
BundleDescription bundle = (BundleDescription) element.getUserObject();
int kind = changes[i].getKind();
if ((kind & IElementChange.RESOLVED) != 0) {
state.resolveBundle(bundle, Bundle.RESOLVED);
resolveConstraints(element, bundle);
} else if ((kind & IElementChange.UNRESOLVED) != 0)
state.resolveBundle(bundle, Bundle.INSTALLED);
else if (kind == IElementChange.LINKAGE_CHANGED)
resolveConstraints(element, bundle);
}
}
private void resolveConstraints(IElement element, BundleDescription bundle) {
// tells the state that some of the constraints have
// changed
IDependency[] dependencies = element.getDependencies();
for (int j = 0; j < dependencies.length; j++) {
if (dependencies[j].getResolvedVersionId() == null)
// an optional requisite that was not resolved
continue;
VersionConstraint constraint = (VersionConstraint) dependencies[j].getUserObject();
Version actualVersion = (Version) dependencies[j].getResolvedVersionId();
BundleDescription supplier = state.getBundle(constraint.getName(), actualVersion);
state.resolveConstraint(constraint, actualVersion, supplier);
}
}
public void bundleAdded(BundleDescription bundle) {
if (dependencies == null)
return;
ResolverHelper.add(bundle, dependencies);
}
public void bundleRemoved(BundleDescription bundle) {
if (dependencies == null)
return;
ResolverHelper.remove(bundle, dependencies);
}
public void bundleUpdated(BundleDescription newDescription, BundleDescription existingDescription) {
if (dependencies == null)
return;
ResolverHelper.update(newDescription, existingDescription, dependencies);
}
public State getState() {
return state;
}
public void flush() {
dependencies = null;
}
/*
* Ensures that all currently resolved bundles have their import-package
* clauses satisfied.
*/
private boolean resolvePackages() {
/* attempt to resolve the proposed bundles */
Map availablePackages;
boolean success;
int tries = 0;
do {
tries++;
success = true;
BundleDescription[] initialBundles = state.getResolvedBundles();
availablePackages = new HashMap(11);
/* do all exports first */
for (int i = 0; i < initialBundles.length; i++) {
PackageSpecification[] required = initialBundles[i].getPackages();
for (int j = 0; j < required.length; j++)
// override previously exported package if any (could
// preserve instead)
if (required[j].isExported()) {
Version toExport = required[j].getVersionSpecification();
PackageSpecification existing = (PackageSpecification) availablePackages.get(required[j].getName());
Version existingVersion = existing == null ? null : existing.getVersionSpecification();
if (existingVersion == null || (toExport != null && toExport.isGreaterThan(existingVersion)))
availablePackages.put(required[j].getName(), required[j]);
}
}
/* now try to resolve imported packages */
for (int i = 0; i < initialBundles.length; i++) {
PackageSpecification[] required = initialBundles[i].getPackages();
for (int j = 0; j < required.length; j++) {
PackageSpecification exported = (PackageSpecification) availablePackages.get(required[j].getName());
Version exportedVersion = exported == null ? null : exported.getVersionSpecification();
Version importedVersion = required[j].getVersionSpecification();
if (exported == null || (importedVersion != null && (exportedVersion == null || !exportedVersion.matchGreaterOrEqualTo(importedVersion)))) {
unresolveRequirementChain(initialBundles[i]);
success = false;
// one missing import is enough to discard this bundle
break;
}
}
}
} while (!success);
/* now bind the exports/imports */
BundleDescription[] resolvedBundles = state.getResolvedBundles();
for (int i = 0; i < resolvedBundles.length; i++) {
PackageSpecification[] required = resolvedBundles[i].getPackages();
for (int j = 0; j < required.length; j++) {
PackageSpecification exported = (PackageSpecification) availablePackages.get(required[j].getName());
state.resolveConstraint(required[j], exported.getVersionSpecification(), exported.getBundle());
}
}
/* return false if a at least one bundle was unresolved during this */
return tries > 1;
}
/*
* Unresolves a bundle and all bundles that require it.
*/
private void unresolveRequirementChain(BundleDescription bundle) {
if (!bundle.isResolved())
return;
state.resolveBundle(bundle, Bundle.INSTALLED);
if (bundle.getUniqueId() == null)
return;
IElementSet bundleElementSet = dependencies.getElementSet(bundle.getUniqueId());
Collection requiring = bundleElementSet.getRequiringElements(bundle.getVersion());
for (Iterator requiringIter = requiring.iterator(); requiringIter.hasNext();) {
IElement requiringElement = (IElement) requiringIter.next();
BundleDescription requiringBundle = state.getBundle((String) requiringElement.getId(), (Version) requiringElement.getVersionId());
if (requiringBundle != null)
unresolveRequirementChain(requiringBundle);
}
}
private void unresolveBundle(BundleDescription bundle) {
if (!bundle.isResolved())
return;
if (dependencies != null)
ResolverHelper.unresolve(bundle, dependencies);
}
}