blob: d0cd6213b903db4042e54a036507394e3be8c164 [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2007, 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.equinox.internal.p2.director;
import java.net.URL;
import java.util.*;
import java.util.Map.Entry;
import org.eclipse.core.runtime.*;
import org.eclipse.equinox.internal.p2.core.helpers.LogHelper;
import org.eclipse.equinox.internal.p2.core.helpers.ServiceHelper;
import org.eclipse.equinox.internal.p2.resolution.ResolutionHelper;
import org.eclipse.equinox.internal.p2.rollback.FormerState;
import org.eclipse.equinox.internal.provisional.p2.core.ProvisionException;
import org.eclipse.equinox.internal.provisional.p2.director.*;
import org.eclipse.equinox.internal.provisional.p2.engine.*;
import org.eclipse.equinox.internal.provisional.p2.metadata.IInstallableUnit;
import org.eclipse.equinox.internal.provisional.p2.metadata.query.InstallableUnitQuery;
import org.eclipse.equinox.internal.provisional.p2.metadata.query.UpdateQuery;
import org.eclipse.equinox.internal.provisional.p2.metadata.repository.IMetadataRepository;
import org.eclipse.equinox.internal.provisional.p2.metadata.repository.IMetadataRepositoryManager;
import org.eclipse.equinox.internal.provisional.p2.query.Collector;
import org.eclipse.equinox.internal.provisional.p2.query.IQueryable;
import org.eclipse.osgi.service.resolver.VersionRange;
import org.eclipse.osgi.util.NLS;
public class SimplePlanner implements IPlanner {
private static final int ExpandWork = 12;
private static final String PLANNER_MARKER = "private.org.eclipse.equinox.p2.planner.installed"; //$NON-NLS-1$
private IInstallableUnit[] getInstallableUnits(IProfile profile) {
return (IInstallableUnit[]) profile.query(InstallableUnitQuery.ANY, new Collector(), null).toArray(IInstallableUnit.class);
}
private IProfile getProfile(String profileId) {
IProfileRegistry profileRegistry = (IProfileRegistry) ServiceHelper.getService(DirectorActivator.context, IProfileRegistry.class.getName());
if (profileRegistry == null)
return null;
return profileRegistry.getProfile(profileId);
}
private ProvisioningPlan generateProvisioningPlan(IStatus status, Collection fromState, Collection toState, List fromStateOrder, List newStateOrder, ProfileChangeRequest changeRequest) {
InstallableUnitOperand[] iuOperands = generateOperations(fromState, toState, fromStateOrder, newStateOrder);
PropertyOperand[] propertyOperands = generatePropertyOperations(changeRequest);
Operand[] operands = new Operand[iuOperands.length + propertyOperands.length];
System.arraycopy(iuOperands, 0, operands, 0, iuOperands.length);
System.arraycopy(propertyOperands, 0, operands, iuOperands.length, propertyOperands.length);
if (status == null)
status = Status.OK_STATUS;
return new ProvisioningPlan(status, operands);
}
private PropertyOperand[] generatePropertyOperations(ProfileChangeRequest profileChangeRequest) {
IProfile profile = profileChangeRequest.getProfile();
List operands = new ArrayList();
// First deal with profile properties to remove. Only generate an operand if the property was there in the first place
String[] toRemove = profileChangeRequest.getPropertiesToRemove();
Map existingProperties = profile.getProperties();
for (int i = 0; i < toRemove.length; i++) {
if (existingProperties.containsKey(toRemove[i]))
operands.add(new PropertyOperand(toRemove[i], existingProperties.get(toRemove[i]), null));
}
// Now deal with profile property changes/additions
Map propertyChanges = profileChangeRequest.getPropertiesToAdd();
Iterator iter = propertyChanges.keySet().iterator();
while (iter.hasNext()) {
String key = (String) iter.next();
operands.add(new PropertyOperand(key, existingProperties.get(key), propertyChanges.get(key)));
}
// Now deal with iu property changes/additions.
// TODO we aren't yet checking that the IU will exist in the final profile, will the engine do this?
Map allIUPropertyChanges = profileChangeRequest.getInstallableUnitProfilePropertiesToAdd();
iter = allIUPropertyChanges.keySet().iterator();
while (iter.hasNext()) {
IInstallableUnit iu = (IInstallableUnit) iter.next();
Map iuPropertyChanges = (Map) allIUPropertyChanges.get(iu);
Iterator iuPropIter = iuPropertyChanges.keySet().iterator();
while (iuPropIter.hasNext()) {
String key = (String) iuPropIter.next();
Object oldValue = profile.getInstallableUnitProperty(iu, key);
operands.add(new InstallableUnitPropertyOperand(iu, key, oldValue, iuPropertyChanges.get(key)));
}
}
// Now deal with iu property removals.
// TODO we could optimize by not generating property removals for IU's that aren't there or won't be there.
Map allIUPropertyDeletions = profileChangeRequest.getInstallableUnitProfilePropertiesToRemove();
iter = allIUPropertyDeletions.keySet().iterator();
while (iter.hasNext()) {
IInstallableUnit iu = (IInstallableUnit) iter.next();
Map existingIUProperties = profile.getInstallableUnitProperties(iu);
List iuPropertyRemovals = (List) allIUPropertyDeletions.get(iu);
for (Iterator it = iuPropertyRemovals.iterator(); it.hasNext();) {
String key = (String) it.next();
if (existingIUProperties.containsKey(key))
operands.add(new InstallableUnitPropertyOperand(iu, key, existingIUProperties.get(key), null));
}
}
return (PropertyOperand[]) operands.toArray(new PropertyOperand[operands.size()]);
}
private InstallableUnitOperand[] generateOperations(Collection fromState, Collection toState, List fromStateOrder, List newStateOrder) {
return sortOperations(new OperationGenerator().generateOperation(fromState, toState), newStateOrder, fromStateOrder);
}
private InstallableUnitOperand[] sortOperations(InstallableUnitOperand[] toSort, List installOrder, List uninstallOrder) {
List updateOp = new ArrayList();
for (int i = 0; i < toSort.length; i++) {
InstallableUnitOperand op = toSort[i];
if (op.first() == null && op.second() != null) {
installOrder.set(installOrder.indexOf(op.second()), op);
continue;
}
if (op.first() != null && op.second() == null) {
uninstallOrder.set(uninstallOrder.indexOf(op.first()), op);
continue;
}
if (op.first() != null && op.second() != null) {
updateOp.add(op);
continue;
}
}
int i = 0;
for (Iterator iterator = installOrder.iterator(); iterator.hasNext();) {
Object elt = iterator.next();
if (elt instanceof InstallableUnitOperand) {
toSort[i++] = (InstallableUnitOperand) elt;
}
}
for (Iterator iterator = uninstallOrder.iterator(); iterator.hasNext();) {
Object elt = iterator.next();
if (elt instanceof InstallableUnitOperand) {
toSort[i++] = (InstallableUnitOperand) elt;
}
}
for (Iterator iterator = updateOp.iterator(); iterator.hasNext();) {
Object elt = iterator.next();
if (elt instanceof InstallableUnitOperand) {
toSort[i++] = (InstallableUnitOperand) elt;
}
}
return toSort;
}
public ProvisioningPlan getRevertPlan(IInstallableUnit profileSnapshot, ProvisioningContext context, IProgressMonitor monitor) {
SubMonitor sub = SubMonitor.convert(monitor, ExpandWork);
sub.setTaskName(Messages.Director_Task_Resolving_Dependencies);
try {
MultiStatus result = new MultiStatus(DirectorActivator.PI_DIRECTOR, 1, Messages.Director_Revert_Problems, null);
if (!Boolean.valueOf(profileSnapshot.getProperty(IInstallableUnit.PROP_TYPE_PROFILE)).booleanValue()) {
result.add(new Status(IStatus.ERROR, DirectorActivator.PI_DIRECTOR, NLS.bind(Messages.Director_Unexpected_IU, profileSnapshot.getId())));
return new ProvisioningPlan(result);
}
IProfile profile = getProfile(profileSnapshot.getId());
if (profile == null) {
result.add(new Status(IStatus.ERROR, DirectorActivator.PI_DIRECTOR, NLS.bind(Messages.Director_Unexpected_IU, profileSnapshot.getId())));
return new ProvisioningPlan(result);
}
//TODO if the profile changes (locations are being modified, etc), should not we do a full uninstall then an install?
//Maybe it depends on the kind of changes in a profile
//We need to get all the ius that were part of the profile and give that to be what to become
Dictionary snapshotSelectionContext = createSelectionContext(getSnapshotProperties(profileSnapshot));
IInstallableUnit[] availableIUs = gatherAvailableInstallableUnits(new IInstallableUnit[] {profileSnapshot}, context.getMetadataRepositories(), context, sub.newChild(ExpandWork / 2));
NewDependencyExpander toExpander = new NewDependencyExpander(new IInstallableUnit[] {profileSnapshot}, null, availableIUs, snapshotSelectionContext, true);
toExpander.expand(sub.newChild(ExpandWork / 2));
ResolutionHelper newStateHelper = new ResolutionHelper(snapshotSelectionContext, toExpander.getRecommendations());
Collection newState = newStateHelper.attachCUs(toExpander.getAllInstallableUnits());
newState.remove(profileSnapshot);
Collection oldIUs = new HashSet();
for (Iterator it = profile.query(InstallableUnitQuery.ANY, new Collector(), null).iterator(); it.hasNext();) {
oldIUs.add(it.next());
}
Dictionary oldSelectionContext = createSelectionContext(profile.getProperties());
ResolutionHelper oldStateHelper = new ResolutionHelper(oldSelectionContext, null);
Collection oldState = oldStateHelper.attachCUs(oldIUs);
ProfileChangeRequest profileChangeRequest = generateChangeRequest(profile, profileSnapshot, newState);
return generateProvisioningPlan(null, oldState, newState, oldStateHelper.getSorted(), newStateHelper.getSorted(), profileChangeRequest);
} finally {
sub.done();
}
}
private Dictionary createSelectionContext(Map properties) {
Hashtable result = new Hashtable(properties);
String environments = (String) properties.get(IProfile.PROP_ENVIRONMENTS);
if (environments == null)
return result;
for (StringTokenizer tokenizer = new StringTokenizer(environments, ","); tokenizer.hasMoreElements();) { //$NON-NLS-1$
String entry = tokenizer.nextToken();
int i = entry.indexOf('=');
String key = entry.substring(0, i).trim();
String value = entry.substring(i + 1).trim();
result.put(key, value);
}
return result;
}
private Map getSnapshotProperties(IInstallableUnit profileSnapshot) {
Map result = new HashMap();
for (Iterator it = profileSnapshot.getProperties().entrySet().iterator(); it.hasNext();) {
Entry entry = (Entry) it.next();
String key = (String) entry.getKey();
if (IInstallableUnit.PROP_TYPE_PROFILE.equals(key) || key.startsWith(FormerState.IUPROP_PREFIX))
continue;
result.put(key, entry.getValue());
}
return result;
}
// TODO note that this only describes property changes, not the IU changes.
private ProfileChangeRequest generateChangeRequest(IProfile profile, IInstallableUnit iuDescribingNewState, Collection newIUs) {
ProfileChangeRequest request = new ProfileChangeRequest(profile);
Map profileProperties = iuDescribingNewState.getProperties();
for (Iterator iter = profileProperties.keySet().iterator(); iter.hasNext();) {
String key = (String) iter.next();
// ignore the property that confirms this IU is a profile snapshot
if (IInstallableUnit.PROP_TYPE_PROFILE.equals(key))
continue;
if (key.startsWith(FormerState.IUPROP_PREFIX)) {
int postID = key.indexOf(FormerState.IUPROP_POSTFIX, FormerState.IUPROP_PREFIX.length());
String id = key.substring(FormerState.IUPROP_PREFIX.length(), postID);
for (Iterator iuIter = newIUs.iterator(); iuIter.hasNext();) {
IInstallableUnit iu = (IInstallableUnit) iuIter.next();
if (id.equals(iu.getId())) {
String iuPropKey = key.substring(postID + FormerState.IUPROP_POSTFIX.length());
request.setInstallableUnitProfileProperty(iu, iuPropKey, profileProperties.get(key));
continue;
}
}
} else {
request.setProfileProperty(key, profileProperties.get(key));
}
}
// Now process removals, but don't include those that simply changed or were otherwise reset above.
for (Iterator iter = profile.getProperties().keySet().iterator(); iter.hasNext();) {
String key = (String) iter.next();
if (!request.getPropertiesToAdd().containsKey(key))
request.removeProfileProperty(key);
}
IInstallableUnit[] ius = getInstallableUnits(profile);
for (int i = 0; i < ius.length; i++) {
// Get the properties we added so those can be excluded
Iterator iuIter = request.getInstallableUnitProfilePropertiesToAdd().keySet().iterator();
HashMap addedProperties = new HashMap();
while (iuIter.hasNext()) {
IInstallableUnit iu = (IInstallableUnit) iuIter.next();
if (iu.getId().equals(ius[i].getId())) {
addedProperties = (HashMap) request.getInstallableUnitProfilePropertiesToAdd().get(iu);
continue;
}
}
for (Iterator iter = profile.getInstallableUnitProperties(ius[i]).keySet().iterator(); iter.hasNext();) {
String key = (String) iter.next();
if (!addedProperties.containsKey(key))
request.removeInstallableUnitProfileProperty(ius[i], key);
}
}
return request;
}
protected IInstallableUnit[] gatherAvailableInstallableUnits(IInstallableUnit[] additionalSource, URL[] repositories, ProvisioningContext context, IProgressMonitor monitor) {
Map resultsMap = new HashMap();
if (additionalSource != null) {
for (int i = 0; i < additionalSource.length; i++) {
String key = additionalSource[i].getId() + "_" + additionalSource[i].getVersion().toString(); //$NON-NLS-1$
resultsMap.put(key, additionalSource[i]);
}
}
if (context != null) {
for (Iterator iter = context.getExtraIUs().iterator(); iter.hasNext();) {
IInstallableUnit iu = (IInstallableUnit) iter.next();
String key = iu.getId() + '_' + iu.getVersion().toString();
resultsMap.put(key, iu);
}
}
IMetadataRepositoryManager repoMgr = (IMetadataRepositoryManager) ServiceHelper.getService(DirectorActivator.context, IMetadataRepositoryManager.class.getName());
if (repositories == null)
repositories = repoMgr.getKnownRepositories(IMetadataRepositoryManager.REPOSITORIES_ALL);
SubMonitor sub = SubMonitor.convert(monitor, repositories.length * 200);
for (int i = 0; i < repositories.length; i++) {
try {
IMetadataRepository repository = repoMgr.loadRepository(repositories[i], sub.newChild(100));
Collector matches = repository.query(new InstallableUnitQuery(null, VersionRange.emptyRange), new Collector(), sub.newChild(100));
for (Iterator it = matches.iterator(); it.hasNext();) {
IInstallableUnit iu = (IInstallableUnit) it.next();
String key = iu.getId() + "_" + iu.getVersion().toString(); //$NON-NLS-1$
IInstallableUnit currentIU = (IInstallableUnit) resultsMap.get(key);
if (currentIU == null || hasHigherFidelity(iu, currentIU))
resultsMap.put(key, iu);
}
} catch (ProvisionException e) {
//skip unreadable repositories
}
}
sub.done();
Collection results = resultsMap.values();
return (IInstallableUnit[]) results.toArray(new IInstallableUnit[results.size()]);
}
private boolean hasHigherFidelity(IInstallableUnit iu, IInstallableUnit currentIU) {
if (Boolean.valueOf(currentIU.getProperty(IInstallableUnit.PROP_PARTIAL_IU)).booleanValue() && !Boolean.valueOf(iu.getProperty(IInstallableUnit.PROP_PARTIAL_IU)).booleanValue())
return true;
return false;
}
public ProvisioningPlan getProvisioningPlan(ProfileChangeRequest profileChangeRequest, ProvisioningContext context, IProgressMonitor monitor) {
SubMonitor sub = SubMonitor.convert(monitor, ExpandWork);
sub.setTaskName(Messages.Director_Task_Resolving_Dependencies);
try {
IProfile profile = profileChangeRequest.getProfile();
IInstallableUnit[] allIUs = updatePlannerInfo(profileChangeRequest);
URL[] metadataRepositories = (context != null) ? context.getMetadataRepositories() : null;
Dictionary newSelectionContext = createSelectionContext(profileChangeRequest.getProfileProperties());
List extraIUs = new ArrayList(Arrays.asList(profileChangeRequest.getAddedInstallableUnits()));
extraIUs.addAll(Arrays.asList(profileChangeRequest.getRemovedInstallableUnits()));
extraIUs.addAll(profileChangeRequest.getProfile().query(InstallableUnitQuery.ANY, new Collector(), null).toCollection());
IInstallableUnit[] availableIUs = gatherAvailableInstallableUnits((IInstallableUnit[]) extraIUs.toArray(new IInstallableUnit[extraIUs.size()]), metadataRepositories, context, sub.newChild(ExpandWork / 4));
Slicer slicer = new Slicer(allIUs, availableIUs, newSelectionContext);
IQueryable slice = slicer.slice(allIUs, monitor);
if (slice == null)
return new ProvisioningPlan(slicer.getStatus());
Projector projector = new Projector(slice, newSelectionContext);
projector.encode(allIUs, sub.newChild(ExpandWork / 4));
IStatus s = projector.invokeSolver(sub.newChild(ExpandWork / 4));
if (s.getSeverity() == IStatus.ERROR) {
//log the error from the new solver so it is not lost
LogHelper.log(s);
if (!"true".equalsIgnoreCase(context == null ? null : context.getProperty("org.eclipse.equinox.p2.disable.error.reporting"))) {
//We invoke the old resolver to get explanations for now
IStatus oldResolverStatus = new NewDependencyExpander(allIUs, null, availableIUs, newSelectionContext, false).expand(sub.newChild(ExpandWork / 4));
if (!oldResolverStatus.isOK())
s = oldResolverStatus;
}
return new ProvisioningPlan(s);
}
Collection newState = projector.extractSolution();
ResolutionHelper newStateHelper = new ResolutionHelper(newSelectionContext, null);
newState = newStateHelper.attachCUs(newState);
ResolutionHelper oldStateHelper = new ResolutionHelper(createSelectionContext(profile.getProperties()), null);
Collection oldState = oldStateHelper.attachCUs(profile.query(InstallableUnitQuery.ANY, new Collector(), null).toCollection());
return generateProvisioningPlan(s, oldState, newState, oldStateHelper.getSorted(), newStateHelper.getSorted(), profileChangeRequest);
} finally {
sub.done();
}
}
//The planner uses installable unit properties to keep track of what it has been asked to install. This updates this information
private IInstallableUnit[] updatePlannerInfo(ProfileChangeRequest profileChangeRequest) {
Collector alreadyInstalled = profileChangeRequest.getProfile().query(new IUProfilePropertyQuery(profileChangeRequest.getProfile(), PLANNER_MARKER, Boolean.toString(true)), new Collector(), null);
IInstallableUnit[] added = profileChangeRequest.getAddedInstallableUnits();
IInstallableUnit[] removed = profileChangeRequest.getRemovedInstallableUnits();
IInstallableUnit[] allIUs = new IInstallableUnit[alreadyInstalled.size() + added.length + removed.length];
int count = 0;
for (Iterator iterator = alreadyInstalled.iterator(); iterator.hasNext();) {
IInstallableUnit iu = (IInstallableUnit) iterator.next();
boolean found = false;
for (int i = 0; i < removed.length; i++) {
if (iu.equals(removed[i])) {
profileChangeRequest.removeInstallableUnitProfileProperty(removed[i], PLANNER_MARKER);
found = true;
break;
}
}
if (!found)
allIUs[count++] = iu;
}
for (int i = 0; i < added.length; i++) {
allIUs[count++] = added[i];
profileChangeRequest.setInstallableUnitProfileProperty(added[i], PLANNER_MARKER, "true"); //$NON-NLS-1$
}
if (allIUs.length > count) {
IInstallableUnit[] result = new IInstallableUnit[count];
System.arraycopy(allIUs, 0, result, 0, count);
allIUs = result;
}
return allIUs;
}
public IInstallableUnit[] updatesFor(IInstallableUnit toUpdate, ProvisioningContext context, IProgressMonitor monitor) {
Map resultsMap = new HashMap();
IMetadataRepositoryManager repoMgr = (IMetadataRepositoryManager) ServiceHelper.getService(DirectorActivator.context, IMetadataRepositoryManager.class.getName());
URL[] repositories = context.getMetadataRepositories();
if (repositories == null)
repositories = repoMgr.getKnownRepositories(IMetadataRepositoryManager.REPOSITORIES_ALL);
SubMonitor sub = SubMonitor.convert(monitor, repositories.length * 200);
for (int i = 0; i < repositories.length; i++) {
try {
IMetadataRepository repository = repoMgr.loadRepository(repositories[i], sub.newChild(100));
Collector matches = repository.query(new UpdateQuery(toUpdate), new Collector(), sub.newChild(100));
for (Iterator it = matches.iterator(); it.hasNext();) {
IInstallableUnit iu = (IInstallableUnit) it.next();
String key = iu.getId() + "_" + iu.getVersion().toString(); //$NON-NLS-1$
IInstallableUnit currentIU = (IInstallableUnit) resultsMap.get(key);
if (currentIU == null || hasHigherFidelity(iu, currentIU))
resultsMap.put(key, iu);
}
} catch (ProvisionException e) {
//skip unreadable repositories
}
}
sub.done();
Collection results = resultsMap.values();
return (IInstallableUnit[]) results.toArray(new IInstallableUnit[results.size()]);
}
}