blob: a079067c101023f66762bace8bc8c286431db799 [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2007, 2009 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
* Genuitec - bug fixes
******************************************************************************/
package org.eclipse.equinox.internal.p2.director;
import java.net.URI;
import java.util.*;
import java.util.Map.Entry;
import org.eclipse.core.runtime.*;
import org.eclipse.equinox.internal.p2.core.helpers.*;
import org.eclipse.equinox.internal.p2.rollback.FormerState;
import org.eclipse.equinox.internal.provisional.p2.core.*;
import org.eclipse.equinox.internal.provisional.p2.director.*;
import org.eclipse.equinox.internal.provisional.p2.engine.*;
import org.eclipse.equinox.internal.provisional.p2.metadata.*;
import org.eclipse.equinox.internal.provisional.p2.metadata.MetadataFactory.InstallableUnitDescription;
import org.eclipse.equinox.internal.provisional.p2.metadata.query.*;
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.*;
import org.eclipse.equinox.internal.provisional.p2.repository.IRepositoryManager;
import org.eclipse.osgi.util.NLS;
public class SimplePlanner implements IPlanner {
private static boolean DEBUG = Tracing.DEBUG_PLANNER_OPERANDS;
private static final int ExpandWork = 12;
private static final String INCLUDE_PROFILE_IUS = "org.eclipse.equinox.p2.internal.profileius"; //$NON-NLS-1$
public static final String INCLUSION_RULES = "org.eclipse.equinox.p2.internal.inclusion.rules"; //$NON-NLS-1$
private static final String ID_IU_FOR_ACTIONS = "org.eclipse.equinox.p2.engine.actions.root"; //$NON-NLS-1$
private static final String EXPLANATION = "org.eclipse.equinox.p2.director.explain"; //$NON-NLS-1$
private static final String CONSIDER_METAREQUIREMENTS = "org.eclipse.equinox.p2.planner.resolveMetaRequirements"; //$NON-NLS-1$
private ProvisioningPlan generateProvisioningPlan(Collection fromState, Collection toState, ProfileChangeRequest changeRequest, ProvisioningPlan installerPlan) {
InstallableUnitOperand[] iuOperands = generateOperations(fromState, toState);
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 (DEBUG) {
for (int i = 0; i < operands.length; i++) {
Tracing.debug(operands[i].toString());
}
}
return new ProvisioningPlan(Status.OK_STATUS, operands, computeActualChangeRequest(toState, changeRequest), null, installerPlan, changeRequest);
}
private Map[] buildDetailedErrors(ProfileChangeRequest changeRequest) {
IInstallableUnit[] added = changeRequest.getAddedInstallableUnits();
IInstallableUnit[] removed = changeRequest.getRemovedInstallableUnits();
Map requestStatus = new HashMap(added.length + removed.length);
for (int i = 0; i < added.length; i++) {
requestStatus.put(added[i], new RequestStatus(added[i], RequestStatus.ADDED, IStatus.ERROR, null));
}
for (int i = 0; i < removed.length; i++) {
requestStatus.put(removed[i], new RequestStatus(removed[i], RequestStatus.REMOVED, IStatus.ERROR, null));
}
return new Map[] {requestStatus, null};
}
private Map[] computeActualChangeRequest(Collection toState, ProfileChangeRequest changeRequest) {
IInstallableUnit[] added = changeRequest.getAddedInstallableUnits();
IInstallableUnit[] removed = changeRequest.getRemovedInstallableUnits();
Map requestStatus = new HashMap(added.length + removed.length);
for (int i = 0; i < added.length; i++) {
if (toState.contains(added[i]))
requestStatus.put(added[i], new RequestStatus(added[i], RequestStatus.ADDED, IStatus.OK, null));
else
requestStatus.put(added[i], new RequestStatus(added[i], RequestStatus.ADDED, IStatus.ERROR, null));
}
for (int i = 0; i < removed.length; i++) {
if (!toState.contains(removed[i]))
requestStatus.put(removed[i], new RequestStatus(removed[i], RequestStatus.REMOVED, IStatus.OK, null));
else
requestStatus.put(removed[i], new RequestStatus(removed[i], RequestStatus.REMOVED, IStatus.ERROR, null));
}
//Compute the side effect changes (e.g. things installed optionally going away)
Collection includedIUs = new HashSet(changeRequest.getProfile().query(new IUProfilePropertyQuery(changeRequest.getProfile(), INCLUSION_RULES, null), new Collector(), null).toCollection());
Map sideEffectStatus = new HashMap(includedIUs.size());
includedIUs.removeAll(toState);
for (Iterator iterator = includedIUs.iterator(); iterator.hasNext();) {
IInstallableUnit removal = (IInstallableUnit) iterator.next();
if (!requestStatus.containsKey(removal))
sideEffectStatus.put(removal, new RequestStatus(removal, RequestStatus.REMOVED, IStatus.INFO, null));
}
return new Map[] {requestStatus, sideEffectStatus};
}
/**
* Converts a set containing a list of resolver explanations into a human-readable status object.
*/
private IStatus convertExplanationToStatus(Set explanations) {
if (explanations == null)
return new Status(IStatus.ERROR, DirectorActivator.PI_DIRECTOR, Messages.Director_Unsatisfied_Dependencies);
MultiStatus root = new MultiStatus(DirectorActivator.PI_DIRECTOR, 1, Messages.Director_Unsatisfied_Dependencies, null);
//try to find a more specific root message if possible
String specificMessage = null;
for (Iterator it = explanations.iterator(); it.hasNext();) {
final Object next = it.next();
if (next instanceof Explanation) {
root.add(((Explanation) next).toStatus());
if (specificMessage == null && next instanceof Explanation.MissingIU)
specificMessage = Messages.Explanation_rootMissing;
else if (specificMessage == null && next instanceof Explanation.Singleton) {
specificMessage = Messages.Explanation_rootSingleton;
}
} else
root.add(new Status(IStatus.ERROR, DirectorActivator.PI_DIRECTOR, next.toString()));
}
//use a more specific root message if available
if (specificMessage != null) {
MultiStatus newRoot = new MultiStatus(DirectorActivator.PI_DIRECTOR, 1, specificMessage, null);
newRoot.merge(root);
root = newRoot;
}
return root;
}
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.entrySet().iterator();
while (iter.hasNext()) {
Map.Entry entry = (Map.Entry) iter.next();
operands.add(new PropertyOperand((String) entry.getKey(), existingProperties.get(entry.getKey()), entry.getValue()));
}
// 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.entrySet().iterator();
while (iter.hasNext()) {
Map.Entry entry = (Map.Entry) iter.next();
IInstallableUnit iu = (IInstallableUnit) entry.getKey();
Map iuPropertyChanges = (Map) entry.getValue();
Iterator iuPropIter = iuPropertyChanges.entrySet().iterator();
while (iuPropIter.hasNext()) {
Map.Entry entry2 = (Map.Entry) iuPropIter.next();
Object oldValue = profile.getInstallableUnitProperty(iu, (String) entry2.getKey());
operands.add(new InstallableUnitPropertyOperand(iu, (String) entry2.getKey(), oldValue, entry2.getValue()));
}
}
// 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.entrySet().iterator();
while (iter.hasNext()) {
Map.Entry entry = (Map.Entry) iter.next();
IInstallableUnit iu = (IInstallableUnit) entry.getKey();
Map existingIUProperties = profile.getInstallableUnitProperties(iu);
List iuPropertyRemovals = (List) entry.getValue();
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) {
return new OperationGenerator().generateOperation(fromState, toState);
}
public ProvisioningPlan getDiffPlan(IProfile currentProfile, IProfile targetProfile, IProgressMonitor monitor) {
SubMonitor sub = SubMonitor.convert(monitor, ExpandWork);
sub.setTaskName(Messages.Director_Task_Resolving_Dependencies);
try {
ProfileChangeRequest profileChangeRequest = FormerState.generateProfileDeltaChangeRequest(currentProfile, targetProfile);
ProvisioningContext context = new ProvisioningContext(new URI[0]);
if (context.getProperty(INCLUDE_PROFILE_IUS) == null)
context.setProperty(INCLUDE_PROFILE_IUS, Boolean.FALSE.toString());
context.setExtraIUs(new ArrayList(targetProfile.available(InstallableUnitQuery.ANY, new Collector(), null).toCollection()));
return getProvisioningPlan(profileChangeRequest, context, sub.newChild(ExpandWork / 2));
} finally {
sub.done();
}
}
public static IInstallableUnit[] findPlannerMarkedIUs(final IProfile profile) {
Query markerQuery = new MatchQuery() {
public boolean isMatch(Object candidate) {
if (!(candidate instanceof IInstallableUnit))
return false;
IInstallableUnit iu = (IInstallableUnit) candidate;
String inclusion = profile.getInstallableUnitProperty(iu, INCLUSION_RULES);
return (inclusion != null);
}
};
return (IInstallableUnit[]) profile.query(markerQuery, new Collector(), null).toArray(IInstallableUnit.class);
}
public static 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 IInstallableUnit[] gatherAvailableInstallableUnits(IInstallableUnit[] additionalSource, URI[] 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(IRepositoryManager.REPOSITORIES_ALL);
SubMonitor sub = SubMonitor.convert(monitor, repositories.length * 200);
for (int i = 0; i < repositories.length; i++) {
try {
if (sub.isCanceled())
throw new OperationCanceledException();
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 static 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;
}
private boolean satisfyMetaRequirements(Map props) {
if (props == null)
return true;
if (props.get(CONSIDER_METAREQUIREMENTS) == null || "true".equalsIgnoreCase((String) props.get(CONSIDER_METAREQUIREMENTS))) //$NON-NLS-1$
return true;
return false;
}
private boolean satisfyMetaRequirements(IProfile p) {
return satisfyMetaRequirements(p.getProperties());
}
//Return the set of IUs representing the complete future state of the profile to satisfy the request or return a ProvisioningPlan when the request can not be satisfied
private Object getSolutionFor(ProfileChangeRequest profileChangeRequest, ProvisioningContext context, IProgressMonitor monitor) {
SubMonitor sub = SubMonitor.convert(monitor, ExpandWork);
sub.setTaskName(Messages.Director_Task_Resolving_Dependencies);
try {
IProfile profile = profileChangeRequest.getProfile();
Object[] updatedPlan = updatePlannerInfo(profileChangeRequest, context);
URI[] 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()));
if (context == null || context.getProperty(INCLUDE_PROFILE_IUS) == null || context.getProperty(INCLUDE_PROFILE_IUS).equalsIgnoreCase(Boolean.TRUE.toString()))
extraIUs.addAll(profile.available(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(new QueryableArray(availableIUs), newSelectionContext, satisfyMetaRequirements(profileChangeRequest.getProfileProperties()));
IQueryable slice = slicer.slice(new IInstallableUnit[] {(IInstallableUnit) updatedPlan[0]}, sub.newChild(ExpandWork / 4));
if (slice == null)
return new ProvisioningPlan(slicer.getStatus(), profileChangeRequest, null);
Projector projector = new Projector(slice, newSelectionContext, satisfyMetaRequirements(profileChangeRequest.getProfileProperties()));
projector.encode((IInstallableUnit) updatedPlan[0], (IInstallableUnit[]) updatedPlan[1], profileChangeRequest.getAddedInstallableUnits(), sub.newChild(ExpandWork / 4));
IStatus s = projector.invokeSolver(sub.newChild(ExpandWork / 4));
if (s.getSeverity() == IStatus.CANCEL)
return new ProvisioningPlan(s, profileChangeRequest, null);
if (s.getSeverity() == IStatus.ERROR) {
sub.setTaskName(Messages.Planner_NoSolution);
if (context != null && !(context.getProperty(EXPLANATION) == null || Boolean.TRUE.toString().equalsIgnoreCase(context.getProperty(EXPLANATION))))
return new ProvisioningPlan(s, profileChangeRequest, null);
//Extract the explanation
Set explanation = projector.getExplanation(sub.newChild(ExpandWork / 4));
IStatus explanationStatus = convertExplanationToStatus(explanation);
return new ProvisioningPlan(explanationStatus, new Operand[0], buildDetailedErrors(profileChangeRequest), new RequestStatus(null, RequestStatus.REMOVED, IStatus.ERROR, explanation), null, profileChangeRequest);
}
//The resolution succeeded. We can forget about the warnings since there is a solution.
if (Tracing.DEBUG && s.getSeverity() != IStatus.OK)
LogHelper.log(s);
s = Status.OK_STATUS;
return projector;
} finally {
sub.done();
}
}
public ProvisioningPlan getProvisioningPlan(ProfileChangeRequest profileChangeRequest, ProvisioningContext context, IProgressMonitor monitor) {
SubMonitor sub = SubMonitor.convert(monitor, ExpandWork);
sub.setTaskName(Messages.Director_Task_Resolving_Dependencies);
try {
//Get the solution for the initial request
Object resolutionResult = getSolutionFor(profileChangeRequest, context, sub.newChild(ExpandWork / 2));
if (resolutionResult instanceof ProvisioningPlan)
return (ProvisioningPlan) resolutionResult;
Collection newState = ((Projector) resolutionResult).extractSolution();
Collection fullState = new ArrayList();
fullState.addAll(newState);
newState = AttachmentHelper.attachFragments(newState, ((Projector) resolutionResult).getFragmentAssociation());
ProvisioningPlan temporaryPlan = generatePlan((Projector) resolutionResult, newState, profileChangeRequest);
//Create a plan for installing necessary pieces to complete the installation (e.g touchpoint actions)
return createInstallerPlan(profileChangeRequest.getProfile(), profileChangeRequest, fullState, newState, temporaryPlan, context, sub.newChild(ExpandWork / 2));
} catch (OperationCanceledException e) {
return new ProvisioningPlan(Status.CANCEL_STATUS, profileChangeRequest, null);
} finally {
sub.done();
}
}
//Verify that all the meta requirements necessary to perform the uninstallation (if necessary) and all t
private Collection areMetaRequirementsSatisfied(IProfile oldProfile, Collection newProfile, ProvisioningPlan initialPlan) {
Collection allMetaRequirements = extractMetaRequirements(newProfile, initialPlan);
for (Iterator iterator = allMetaRequirements.iterator(); iterator.hasNext();) {
IRequiredCapability requirement = (IRequiredCapability) iterator.next();
if (oldProfile.query(new CapabilityQuery(requirement), new HasMatchCollector(), null).isEmpty())
return allMetaRequirements;
}
return null;
}
//Return all the meta requirements for the list of IU specified and all the meta requirements listed necessary to satisfy the uninstallation
private Collection extractMetaRequirements(Collection ius, ProvisioningPlan plan) {
Set allMetaRequirements = new HashSet();
for (Iterator iterator = ius.iterator(); iterator.hasNext();) {
IInstallableUnit iu = (IInstallableUnit) iterator.next();
IRequiredCapability[] reqs = iu.getMetaRequiredCapabilities();
for (int i = 0; i < reqs.length; i++) {
allMetaRequirements.add(reqs[i]);
}
}
Collector c2 = plan.getRemovals().query(InstallableUnitQuery.ANY, new Collector(), null);
for (Iterator iterator = c2.iterator(); iterator.hasNext();) {
IInstallableUnit iu = (IInstallableUnit) iterator.next();
IRequiredCapability[] reqs = iu.getMetaRequiredCapabilities();
for (int i = 0; i < reqs.length; i++) {
allMetaRequirements.add(reqs[i]);
}
}
return allMetaRequirements;
}
private ProvisioningPlan createInstallerPlan(IProfile profile, ProfileChangeRequest initialRequest, Collection unattachedState, Collection expectedState, ProvisioningPlan initialPlan, ProvisioningContext initialContext, IProgressMonitor monitor) {
SubMonitor sub = SubMonitor.convert(monitor, ExpandWork);
try {
sub.setTaskName(Messages.Director_Task_installer_plan);
IProfileRegistry profileRegistry = (IProfileRegistry) ServiceHelper.getService(DirectorActivator.context, IProfileRegistry.class.getName());
if (profileRegistry == null)
return new ProvisioningPlan(new Status(IStatus.ERROR, DirectorActivator.PI_DIRECTOR, Messages.Planner_no_profile_registry), initialRequest, null);
IProfile agentProfile = profileRegistry.getProfile(IProfileRegistry.SELF);
if (agentProfile == null)
return initialPlan;
if (profile.getProfileId().equals(agentProfile.getProfileId())) {
if (profile.getTimestamp() != agentProfile.getTimestamp())
return new ProvisioningPlan(new Status(IStatus.ERROR, DirectorActivator.PI_DIRECTOR, NLS.bind(Messages.Planner_profile_out_of_sync, profile.getProfileId())), initialRequest, null);
return createInstallerPlanForCohostedCase(profile, initialRequest, initialPlan, unattachedState, expectedState, initialContext, sub);
}
if (satisfyMetaRequirements(profile) && !profile.getProfileId().equals(agentProfile.getProfileId())) {
return createInstallerPlanForCohostedCaseFromExternalInstaller(profile, initialRequest, initialPlan, expectedState, initialContext, agentProfile, sub);
}
return createInstallerPlanForExternalInstaller(profile, initialRequest, initialPlan, expectedState, initialContext, agentProfile, sub);
} finally {
sub.done();
}
}
private ProvisioningPlan createInstallerPlanForCohostedCaseFromExternalInstaller(IProfile profile, ProfileChangeRequest initialRequest, ProvisioningPlan initialPlan, Collection newState, ProvisioningContext initialContext, IProfile agentProfile, SubMonitor sub) {
ProvisioningPlan planForProfile = generatePlan(null, newState, initialRequest);
return createInstallerPlanForExternalInstaller(profile, initialRequest, planForProfile, newState, initialContext, agentProfile, sub);
}
//Deal with the case where the agent profile is different than the one being provisioned
private ProvisioningPlan createInstallerPlanForExternalInstaller(IProfile targetedProfile, ProfileChangeRequest initialRequest, ProvisioningPlan initialPlan, Collection expectedState, ProvisioningContext initialContext, IProfile agentProfile, SubMonitor sub) {
Collection metaRequirements = areMetaRequirementsSatisfied(agentProfile, expectedState, initialPlan);
if (metaRequirements == null)
return initialPlan;
IInstallableUnit actionsIU = createIUForMetaRequirements(targetedProfile, metaRequirements);
IInstallableUnit previousActionsIU = getPreviousIUForMetaRequirements(agentProfile, getActionGatheringIUId(targetedProfile), sub);
ProfileChangeRequest agentRequest = new ProfileChangeRequest(agentProfile);
agentRequest.addInstallableUnits(new IInstallableUnit[] {actionsIU});
if (previousActionsIU != null)
agentRequest.removeInstallableUnits(new IInstallableUnit[] {previousActionsIU});
Object externalInstallerPlan = getSolutionFor(agentRequest, initialContext, sub.newChild(10));
if (externalInstallerPlan instanceof ProvisioningPlan && ((ProvisioningPlan) externalInstallerPlan).getStatus().getSeverity() == IStatus.ERROR) {
MultiStatus externalInstallerStatus = new MultiStatus(DirectorActivator.PI_DIRECTOR, 0, Messages.Planner_can_not_install_preq, null);
externalInstallerStatus.add(((ProvisioningPlan) externalInstallerPlan).getStatus());
return new ProvisioningPlan(externalInstallerStatus, initialRequest, new ProvisioningPlan(externalInstallerStatus, agentRequest, null));
}
initialPlan.setInstallerPlan(generatePlan((Projector) externalInstallerPlan, null, agentRequest));
return initialPlan;
}
//Deal with the case where the actions needs to be installed in the same profile than the one we are performing the initial request
//The expectedState represents the result of the initialRequest where the metaRequirements have been satisfied.
private ProvisioningPlan createInstallerPlanForCohostedCase(IProfile profile, ProfileChangeRequest initialRequest, ProvisioningPlan initialPlan, Collection unattachedState, Collection expectedState, ProvisioningContext initialContext, SubMonitor monitor) {
Collection metaRequirements = initialRequest.getRemovedInstallableUnits().length == 0 ? areMetaRequirementsSatisfied(profile, expectedState, initialPlan) : extractMetaRequirements(expectedState, initialPlan);
if (metaRequirements == null || metaRequirements.isEmpty())
return initialPlan;
//Let's compute a plan that satisfy all the metaRequirements. We limit ourselves to only the IUs that were part of the previous solution.
IInstallableUnit metaRequirementIU = createIUForMetaRequirements(profile, metaRequirements);
IInstallableUnit previousMetaRequirementIU = getPreviousIUForMetaRequirements(profile, getActionGatheringIUId(profile), monitor);
//Create an agent request from the initial request
ProfileChangeRequest agentRequest = new ProfileChangeRequest(profile);
for (Iterator it = initialRequest.getPropertiesToAdd().entrySet().iterator(); it.hasNext();) {
Entry entry = (Entry) it.next();
agentRequest.setProfileProperty((String) entry.getKey(), entry.getValue());
}
String[] removedProperties = initialRequest.getPropertiesToRemove();
for (int i = 0; i < removedProperties.length; i++) {
agentRequest.removeProfileProperty(removedProperties[i]);
}
Map removedIUProperties = initialRequest.getInstallableUnitProfilePropertiesToRemove();
for (Iterator iterator = removedIUProperties.entrySet().iterator(); iterator.hasNext();) {
Entry entry = (Entry) iterator.next();
ArrayList value = (ArrayList) entry.getValue();
for (Iterator iterator2 = value.iterator(); iterator2.hasNext();) {
agentRequest.removeInstallableUnitProfileProperty((IInstallableUnit) entry.getKey(), (String) iterator2.next());
}
}
if (previousMetaRequirementIU != null)
agentRequest.removeInstallableUnits(new IInstallableUnit[] {previousMetaRequirementIU});
agentRequest.addInstallableUnits(new IInstallableUnit[] {metaRequirementIU});
ProvisioningContext agentCtx = new ProvisioningContext(new URI[0]);
ArrayList extraIUs = new ArrayList(unattachedState);
agentCtx.setExtraIUs(extraIUs);
Object agentSolution = getSolutionFor(agentRequest, agentCtx, monitor.newChild(3));
if (agentSolution instanceof ProvisioningPlan && ((ProvisioningPlan) agentSolution).getStatus().getSeverity() == IStatus.ERROR) {
MultiStatus agentStatus = new MultiStatus(DirectorActivator.PI_DIRECTOR, 0, Messages.Planner_actions_and_software_incompatible, null);
agentStatus.add(((ProvisioningPlan) agentSolution).getStatus());
return new ProvisioningPlan(agentStatus, initialRequest, new ProvisioningPlan(agentStatus, agentRequest, null));
}
//Compute the installer plan. It is the difference between what is currently in the profile and the solution we just computed
Collection agentState = ((Projector) agentSolution).extractSolution();
agentState.remove(metaRequirementIU); //Remove the fake IU
agentState = AttachmentHelper.attachFragments(agentState, ((Projector) agentSolution).getFragmentAssociation());
ProvisioningContext noRepoContext = createNoRepoContext(initialRequest);
//...This computes the attachment of what is currently in the profile
Object initialSolution = getSolutionFor(new ProfileChangeRequest(new EverythingOptionalProfile(initialRequest.getProfile())), noRepoContext, new NullProgressMonitor());
if (initialSolution instanceof ProvisioningPlan) {
LogHelper.log(new Status(IStatus.ERROR, DirectorActivator.PI_DIRECTOR, "The resolution of the previous state contained in profile " + initialRequest.getProfile().getProfileId() + " version " + initialRequest.getProfile().getTimestamp() + " failed.")); //$NON-NLS-1$//$NON-NLS-2$//$NON-NLS-3$
return (ProvisioningPlan) initialSolution;
}
Collection initialState = initialRequest.getProfile().query(InstallableUnitQuery.ANY, new Collector(), null).toCollection();
initialState = AttachmentHelper.attachFragments(initialState, ((Projector) initialSolution).getFragmentAssociation());
ProvisioningPlan agentPlan = generateProvisioningPlan(initialState, agentState, initialRequest, null);
//Compute the installation plan. It is the difference between the state after the installer plan has run and the expectedState.
return generateProvisioningPlan(agentState, expectedState, initialRequest, agentPlan);
}
//Compute the set of operands based on the solution obtained previously
private ProvisioningPlan generatePlan(Projector newSolution, Collection newState, ProfileChangeRequest request) {
//Compute the attachment of the new state if not provided
if (newState == null) {
newState = newSolution.extractSolution();
newState = AttachmentHelper.attachFragments(newState, newSolution.getFragmentAssociation());
}
ProvisioningContext noRepoContext = createNoRepoContext(request);
//Compute the attachment of the previous state
Object initialSolution = getSolutionFor(new ProfileChangeRequest(new EverythingOptionalProfile(request.getProfile())), noRepoContext, new NullProgressMonitor());
if (initialSolution instanceof ProvisioningPlan) {
LogHelper.log(new Status(IStatus.ERROR, DirectorActivator.PI_DIRECTOR, "The resolution of the previous state contained in profile " + request.getProfile().getProfileId() + " version " + request.getProfile().getTimestamp() + " failed.")); //$NON-NLS-1$//$NON-NLS-2$//$NON-NLS-3$
return (ProvisioningPlan) initialSolution;
}
Collection initialState = request.getProfile().query(InstallableUnitQuery.ANY, new Collector(), null).toCollection();
initialState = AttachmentHelper.attachFragments(initialState, ((Projector) initialSolution).getFragmentAssociation());
//Generate the plan
return generateProvisioningPlan(initialState, newState, request, null);
}
private ProvisioningContext createNoRepoContext(ProfileChangeRequest request) {
ProvisioningContext noRepoContext = new ProvisioningContext(new URI[0]);
noRepoContext.setArtifactRepositories(new URI[0]);
noRepoContext.setProperty(INCLUDE_PROFILE_IUS, Boolean.FALSE.toString());
ArrayList extraIUs = new ArrayList();
extraIUs.addAll(request.getProfile().query(InstallableUnitQuery.ANY, new Collector(), new NullProgressMonitor()).toCollection());
noRepoContext.setExtraIUs(extraIUs);
return noRepoContext;
}
private IInstallableUnit getPreviousIUForMetaRequirements(IProfile profile, String iuId, IProgressMonitor monitor) {
Collector c = profile.query(new InstallableUnitQuery(iuId), new Collector(), monitor);
if (c.size() == 0)
return null;
return (IInstallableUnit) c.toArray(IInstallableUnit.class)[0];
}
private String getActionGatheringIUId(IProfile profile) {
return ID_IU_FOR_ACTIONS + '.' + profile.getProfileId();
}
private IInstallableUnit createIUForMetaRequirements(IProfile profile, Collection metaRequirements) {
InstallableUnitDescription description = new InstallableUnitDescription();
String id = getActionGatheringIUId(profile);
description.setId(id);
Version version = Version.createOSGi(1, 0, 0, Long.toString(profile.getTimestamp()));
description.setVersion(version);
description.addRequiredCapabilities(metaRequirements);
ArrayList providedCapabilities = new ArrayList();
IProvidedCapability providedCapability = MetadataFactory.createProvidedCapability(IInstallableUnit.NAMESPACE_IU_ID, id, version);
providedCapabilities.add(providedCapability);
description.addProvidedCapabilities(providedCapabilities);
IInstallableUnit actionsIU = MetadataFactory.createInstallableUnit(description);
return actionsIU;
}
private IInstallableUnit createIURepresentingTheProfile(ArrayList allRequirements) {
InstallableUnitDescription iud = new MetadataFactory.InstallableUnitDescription();
String time = Long.toString(System.currentTimeMillis());
iud.setId(time);
iud.setVersion(Version.createOSGi(0, 0, 0, time));
iud.setRequiredCapabilities((IRequiredCapability[]) allRequirements.toArray(new IRequiredCapability[allRequirements.size()]));
return MetadataFactory.createInstallableUnit(iud);
}
//The planner uses installable unit properties to keep track of what it has been asked to install. This updates this information
//It returns at index 0 a meta IU representing everything that needs to be installed
//It returns at index 1 all the IUs that are in the profile after the removal have been done, but before the addition have been done
private Object[] updatePlannerInfo(ProfileChangeRequest profileChangeRequest, ProvisioningContext context) {
Collection includedIUs = profileChangeRequest.getProfile().query(new IUProfilePropertyQuery(profileChangeRequest.getProfile(), INCLUSION_RULES, null), new Collector(), null).toCollection();
Collection alreadyInstalled = new HashSet(includedIUs);
IInstallableUnit[] added = profileChangeRequest.getAddedInstallableUnits();
IInstallableUnit[] removed = profileChangeRequest.getRemovedInstallableUnits();
for (Iterator iterator = profileChangeRequest.getInstallableUnitProfilePropertiesToRemove().entrySet().iterator(); iterator.hasNext();) {
Map.Entry object = (Map.Entry) iterator.next();
if (((List) object.getValue()).contains(INCLUSION_RULES))
profileChangeRequest.setInstallableUnitProfileProperty((IInstallableUnit) object.getKey(), INCLUSION_RULES, PlannerHelper.createStrictInclusionRule((IInstallableUnit) object.getKey()));
}
//Remove the iu properties associated to the ius removed and the iu properties being removed as well
if (removed.length != 0) {
for (Iterator iterator = alreadyInstalled.iterator(); iterator.hasNext();) {
IInstallableUnit iu = (IInstallableUnit) iterator.next();
for (int i = 0; i < removed.length; i++) {
if (iu.equals(removed[i])) {
profileChangeRequest.removeInstallableUnitProfileProperty(removed[i], INCLUSION_RULES);
iterator.remove();
break;
}
}
}
}
ArrayList gatheredRequirements = new ArrayList();
//Process all the IUs being added
Map iuPropertiesToAdd = profileChangeRequest.getInstallableUnitProfilePropertiesToAdd();
for (int i = 0; i < added.length; i++) {
Map propertiesForIU = (Map) iuPropertiesToAdd.get(added[i]);
IRequiredCapability profileRequirement = null;
if (propertiesForIU != null) {
profileRequirement = createRequirement(added[i], (String) propertiesForIU.get(INCLUSION_RULES));
}
if (profileRequirement == null) {
profileChangeRequest.setInstallableUnitProfileProperty(added[i], INCLUSION_RULES, PlannerHelper.createStrictInclusionRule(added[i]));
profileRequirement = createStrictRequirement(added[i]);
}
gatheredRequirements.add(profileRequirement);
}
//Process the IUs that were already there
for (Iterator iterator = alreadyInstalled.iterator(); iterator.hasNext();) {
IInstallableUnit iu = (IInstallableUnit) iterator.next();
Map propertiesForIU = (Map) iuPropertiesToAdd.get(iu);
IRequiredCapability profileRequirement = null;
//Test if the value has changed
if (propertiesForIU != null) {
profileRequirement = createRequirement(iu, (String) propertiesForIU.get(INCLUSION_RULES));
}
if (profileRequirement == null) {
profileRequirement = createRequirement(iu, profileChangeRequest.getProfile().getInstallableUnitProperty(iu, INCLUSION_RULES));
}
if (!gatheredRequirements.contains(profileRequirement))
gatheredRequirements.add(profileRequirement);
}
//Now add any other requirement that we need to see satisfied
if (context != null && context.getAdditionalRequirements() != null)
gatheredRequirements.addAll(context.getAdditionalRequirements());
return new Object[] {createIURepresentingTheProfile(gatheredRequirements), (IInstallableUnit[]) alreadyInstalled.toArray(new IInstallableUnit[alreadyInstalled.size()])};
}
private IRequiredCapability createRequirement(IInstallableUnit iu, String rule) {
if (rule == null)
return null;
if (rule.equals(PlannerHelper.createStrictInclusionRule(iu))) {
return createStrictRequirement(iu);
}
if (rule.equals(PlannerHelper.createOptionalInclusionRule(iu))) {
return createOptionalRequirement(iu);
}
return null;
}
private IRequiredCapability createOptionalRequirement(IInstallableUnit iu) {
return MetadataFactory.createRequiredCapability(IInstallableUnit.NAMESPACE_IU_ID, iu.getId(), new VersionRange(iu.getVersion(), true, iu.getVersion(), true), null, true, false, true);
}
private IRequiredCapability createStrictRequirement(IInstallableUnit iu) {
return MetadataFactory.createRequiredCapability(IInstallableUnit.NAMESPACE_IU_ID, iu.getId(), new VersionRange(iu.getVersion(), true, iu.getVersion(), true), null, false, false, true);
}
public IInstallableUnit[] updatesFor(IInstallableUnit toUpdate, ProvisioningContext context, IProgressMonitor monitor) {
Map resultsMap = new HashMap();
IMetadataRepositoryManager repoMgr = (IMetadataRepositoryManager) ServiceHelper.getService(DirectorActivator.context, IMetadataRepositoryManager.class.getName());
URI[] repositories = context.getMetadataRepositories();
if (repositories == null)
repositories = repoMgr.getKnownRepositories(IRepositoryManager.REPOSITORIES_ALL);
SubMonitor sub = SubMonitor.convert(monitor, repositories.length * 200);
for (int i = 0; i < repositories.length; i++) {
try {
if (sub.isCanceled())
throw new OperationCanceledException();
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()]);
}
//helper class to trick the resolver to believe that everything is optional
private static class EverythingOptionalProfile implements IProfile {
private IProfile profile;
public EverythingOptionalProfile(IProfile p) {
profile = p;
}
public Collector available(Query query, Collector collector, IProgressMonitor monitor) {
return profile.available(query, collector, monitor);
}
public Map getInstallableUnitProperties(IInstallableUnit iu) {
return profile.getInstallableUnitProperties(iu);
}
public String getInstallableUnitProperty(IInstallableUnit iu, String key) {
if (INCLUSION_RULES.equals(key))
return PlannerHelper.createOptionalInclusionRule(iu);
return profile.getInstallableUnitProperty(iu, key);
}
public Map getLocalProperties() {
return profile.getLocalProperties();
}
public String getLocalProperty(String key) {
return profile.getLocalProperty(key);
}
public IProfile getParentProfile() {
return profile.getParentProfile();
}
public String getProfileId() {
return profile.getProfileId();
}
public Map getProperties() {
return profile.getProperties();
}
public String getProperty(String key) {
return profile.getProperty(key);
}
public String[] getSubProfileIds() {
return profile.getSubProfileIds();
}
public long getTimestamp() {
return profile.getTimestamp();
}
public boolean hasSubProfiles() {
return profile.hasSubProfiles();
}
public boolean isRootProfile() {
return profile.isRootProfile();
}
public Collector query(Query query, Collector collector, IProgressMonitor monitor) {
return profile.query(query, collector, monitor);
}
}
}