diff options
51 files changed, 2752 insertions, 375 deletions
diff --git a/bundles/org.eclipse.equinox.p2.director/META-INF/MANIFEST.MF b/bundles/org.eclipse.equinox.p2.director/META-INF/MANIFEST.MF index f9ad8f358..f9d9aae1a 100644 --- a/bundles/org.eclipse.equinox.p2.director/META-INF/MANIFEST.MF +++ b/bundles/org.eclipse.equinox.p2.director/META-INF/MANIFEST.MF @@ -23,10 +23,10 @@ Export-Package: org.eclipse.equinox.internal.p2.director; org.eclipse.equinox.p2.installer", org.eclipse.equinox.p2.planner;version="2.0.0" Require-Bundle: org.eclipse.equinox.common;bundle-version="[3.3.0,4.0.0)", - org.sat4j.core;bundle-version="[2.2.0,2.4.0)", - org.sat4j.pb;bundle-version="[2.2.0,2.4.0)", org.eclipse.core.jobs;bundle-version="[3.3.0,4.0.0)", - org.eclipse.equinox.p2.metadata;bundle-version="[2.0.0,3.0.0)" + org.eclipse.equinox.p2.metadata;bundle-version="[2.0.0,3.0.0)", + org.sat4j.pb;bundle-version="[2.3.3,3.0.0)", + org.sat4j.core;bundle-version="[2.3.3,3.0.0)" Bundle-RequiredExecutionEnvironment: J2SE-1.5, J2SE-1.4, CDC-1.1/Foundation-1.1 diff --git a/bundles/org.eclipse.equinox.p2.director/src/org/eclipse/equinox/internal/p2/director/OptimizationFunction.java b/bundles/org.eclipse.equinox.p2.director/src/org/eclipse/equinox/internal/p2/director/OptimizationFunction.java index 55cc3811f..c29fe8811 100644 --- a/bundles/org.eclipse.equinox.p2.director/src/org/eclipse/equinox/internal/p2/director/OptimizationFunction.java +++ b/bundles/org.eclipse.equinox.p2.director/src/org/eclipse/equinox/internal/p2/director/OptimizationFunction.java @@ -23,17 +23,14 @@ public class OptimizationFunction { private IQueryable<IInstallableUnit> picker; private IInstallableUnit selectionContext; - private Map<String, Map<Version, IInstallableUnit>> slice; //The IUs that have been considered to be part of the problem - private int numberOfInstalledIUs; + protected Map<String, Map<Version, IInstallableUnit>> slice; //The IUs that have been considered to be part of the problem + private int numberOfInstalledIUs; //TODO this should be renamed to consideredIUs or sliceSize private IQueryable<IInstallableUnit> lastState; - private List<AbstractVariable> abstractVariables; + private List<AbstractVariable> optionalRequirementVariable; - // private IInstallableUnit[] alreadyExistingRoots; - // private IQueryable<IInstallableUnit> beingInstalled; - - public OptimizationFunction(IQueryable<IInstallableUnit> lastState, List<AbstractVariable> abstractVariables, IQueryable<IInstallableUnit> picker, IInstallableUnit selectionContext, Map<String, Map<Version, IInstallableUnit>> slice) { + public OptimizationFunction(IQueryable<IInstallableUnit> lastState, List<AbstractVariable> abstractVariables, List<AbstractVariable> optionalRequirementVariable, IQueryable<IInstallableUnit> picker, IInstallableUnit selectionContext, Map<String, Map<Version, IInstallableUnit>> slice) { this.lastState = lastState; - this.abstractVariables = abstractVariables; + this.optionalRequirementVariable = optionalRequirementVariable; this.picker = picker; this.selectionContext = selectionContext; this.slice = slice; @@ -44,7 +41,7 @@ public class OptimizationFunction { numberOfInstalledIUs = sizeOf(lastState); List<WeightedObject<? extends Object>> weightedObjects = new ArrayList<WeightedObject<? extends Object>>(); - Set<IInstallableUnit> transitiveClosure; + Set<IInstallableUnit> transitiveClosure; //The transitive closure of the IUs we are adding (this also means updating) if (newRoots.isEmpty()) { transitiveClosure = CollectionUtils.emptySet(); } else { @@ -63,13 +60,15 @@ public class OptimizationFunction { for (Entry<String, Map<Version, IInstallableUnit>> entry : s) { List<IInstallableUnit> conflictingEntries = new ArrayList<IInstallableUnit>(entry.getValue().values()); if (conflictingEntries.size() == 1) { + //Only one IU exists with the namespace. IInstallableUnit iu = conflictingEntries.get(0); if (iu != metaIu) { weightedObjects.add(WeightedObject.newWO(iu, POWER)); } continue; } - // IUs are sorted from highest version to lowest. + + // Set the weight such that things that are already installed are not updated Collections.sort(conflictingEntries, Collections.reverseOrder()); BigInteger weight = POWER; // have we already found a version that is already installed? @@ -96,14 +95,15 @@ public class OptimizationFunction { // maximal weight used so far. maxWeight = maxWeight.multiply(POWER).multiply(BigInteger.valueOf(s.size())); - // Add the abstract variables - BigInteger abstractWeight = maxWeight.negate(); - for (AbstractVariable var : abstractVariables) { - weightedObjects.add(WeightedObject.newWO(var, abstractWeight)); + // Add the optional variables + BigInteger optionalVarWeight = maxWeight.negate(); + for (AbstractVariable var : optionalRequirementVariable) { + weightedObjects.add(WeightedObject.newWO(var, optionalVarWeight)); } maxWeight = maxWeight.multiply(POWER).add(BigInteger.ONE); + //Now we deal the optional IUs, BigInteger optionalWeight = maxWeight.negate(); long countOptional = 1; List<IInstallableUnit> requestedPatches = new ArrayList<IInstallableUnit>(); @@ -117,12 +117,11 @@ public class OptimizationFunction { if (match instanceof IInstallableUnitPatch) { requestedPatches.add(match); countOptional = countOptional + 1; - } else { - weightedObjects.add(WeightedObject.newWO(match, optionalWeight)); } } } + // and we make sure that patches are always favored BigInteger patchWeight = maxWeight.multiply(POWER).multiply(BigInteger.valueOf(countOptional)).negate(); for (Iterator<IInstallableUnit> iterator = requestedPatches.iterator(); iterator.hasNext();) { weightedObjects.add(WeightedObject.newWO(iterator.next(), patchWeight)); diff --git a/bundles/org.eclipse.equinox.p2.director/src/org/eclipse/equinox/internal/p2/director/Projector.java b/bundles/org.eclipse.equinox.p2.director/src/org/eclipse/equinox/internal/p2/director/Projector.java index 59e8eb271..ba401bbd0 100644 --- a/bundles/org.eclipse.equinox.p2.director/src/org/eclipse/equinox/internal/p2/director/Projector.java +++ b/bundles/org.eclipse.equinox.p2.director/src/org/eclipse/equinox/internal/p2/director/Projector.java @@ -11,6 +11,7 @@ * Jed Anderson - switch from opb files to API calls to DependencyHelper in bug 200380 * Sonatype, Inc. - ongoing development * Rapicorp, Inc. - split the optimization function + * Red Hat, Inc. - support for remediation page ******************************************************************************/ package org.eclipse.equinox.internal.p2.director; @@ -27,9 +28,10 @@ import org.eclipse.equinox.p2.metadata.*; import org.eclipse.equinox.p2.metadata.expression.IMatchExpression; import org.eclipse.equinox.p2.query.*; import org.eclipse.osgi.util.NLS; +import org.sat4j.minisat.restarts.LubyRestarts; import org.sat4j.pb.*; -import org.sat4j.pb.tools.DependencyHelper; -import org.sat4j.pb.tools.WeightedObject; +import org.sat4j.pb.core.PBSolverResolution; +import org.sat4j.pb.tools.*; import org.sat4j.specs.*; /** @@ -53,7 +55,7 @@ public class Projector { private IQueryable<IInstallableUnit> picker; private QueryableArray patches; - private Map<IInstallableUnit, AbstractVariable> noopVariables; //key IU, value AbstractVariable + private List<AbstractVariable> allOptionalAbstractRequirements; private List<AbstractVariable> abstractVariables; private Map<String, Map<Version, IInstallableUnit>> slice; //The IUs that have been considered to be part of the problem @@ -78,6 +80,9 @@ public class Projector { private Map<IInstallableUnit, AbstractVariable> nonGreedyVariables = new HashMap<IInstallableUnit, AbstractVariable>(); private Map<AbstractVariable, List<Object>> nonGreedyProvider = new HashMap<AbstractVariable, List<Object>>(); //Keeps track of all the "object" that provide an IU that is non greedly requested + private boolean emptyBecauseFiltered; + private boolean userDefinedFunction; + static class AbstractVariable { // private String name; @@ -154,24 +159,19 @@ public class Projector { public Projector(IQueryable<IInstallableUnit> q, Map<String, String> context, Set<IInstallableUnit> nonGreedyIUs, boolean considerMetaRequirements) { picker = q; - noopVariables = new HashMap<IInstallableUnit, AbstractVariable>(); slice = new HashMap<String, Map<Version, IInstallableUnit>>(); selectionContext = InstallableUnit.contextIU(context); abstractVariables = new ArrayList<AbstractVariable>(); + allOptionalAbstractRequirements = new ArrayList<AbstractVariable>(); result = new MultiStatus(DirectorActivator.PI_DIRECTOR, IStatus.OK, Messages.Planner_Problems_resolving_plan, null); assumptions = new ArrayList<Object>(); this.nonGreedyIUs = nonGreedyIUs; this.considerMetaRequirements = considerMetaRequirements; } - // protected boolean isInstalled(IInstallableUnit iu) { - // return !lastState.query(QueryUtil.createIUQuery(iu), null).isEmpty(); - // } - @SuppressWarnings("unchecked") public void encode(IInstallableUnit entryPointIU, IInstallableUnit[] alreadyExistingRoots, IQueryable<IInstallableUnit> installedIUs, Collection<IInstallableUnit> newRoots, IProgressMonitor monitor) { alreadyInstalledIUs = Arrays.asList(alreadyExistingRoots); - // numberOfInstalledIUs = sizeOf(installedIUs); lastState = installedIUs; this.entryPoint = entryPointIU; try { @@ -184,7 +184,14 @@ public class Projector { if (DEBUG_ENCODING) { solver = new UserFriendlyPBStringSolver<Object>(); } else { - solver = SolverFactory.newEclipseP2(); + if (userDefinedFunction) { + PBSolverResolution mysolver = SolverFactory.newCompetPBResLongWLMixedConstraintsObjectiveExpSimp(); + mysolver.setSimplifier(mysolver.SIMPLE_SIMPLIFICATION); + mysolver.setRestartStrategy(new LubyRestarts(512)); + solver = mysolver; + } else { + solver = SolverFactory.newEclipseP2(); + } } int timeout = DEFAULT_SOLVER_TIMEOUT; String timeoutString = null; @@ -204,10 +211,13 @@ public class Projector { solver.setTimeoutOnConflicts(timeout); IQueryResult<IInstallableUnit> queryResult = picker.query(QueryUtil.createIUAnyQuery(), null); if (DEBUG_ENCODING) { - dependencyHelper = new DependencyHelper<Object, Explanation>(solver, false); + dependencyHelper = new LexicoHelper<Object, Explanation>(solver, false); ((UserFriendlyPBStringSolver<Object>) solver).setMapping(dependencyHelper.getMappingToDomain()); } else { - dependencyHelper = new DependencyHelper<Object, Explanation>(solver); + if (userDefinedFunction) + dependencyHelper = new LexicoHelper<Object, Explanation>(solver); + else + dependencyHelper = new DependencyHelper<Object, Explanation>(solver); } List<IInstallableUnit> iusToOrder = new ArrayList<IInstallableUnit>(queryResult.toSet()); Collections.sort(iusToOrder); @@ -255,13 +265,28 @@ public class Projector { } + private void createOptimizationFunction(IInstallableUnit entryPointIU, Collection<IInstallableUnit> newRoots) { + if (!userDefinedFunction) { + createStandardOptimizationFunction(entryPointIU, newRoots); + } else { + createUserDefinedOptimizationFunction(entryPointIU, newRoots); + } + } + //Create an optimization function favoring the highest version of each IU - private void createOptimizationFunction(IInstallableUnit metaIu, Collection<IInstallableUnit> newRoots) { - List<WeightedObject<? extends Object>> weights = new OptimizationFunction(lastState, abstractVariables, picker, selectionContext, slice).createOptimizationFunction(metaIu, newRoots); + private void createStandardOptimizationFunction(IInstallableUnit entryPointIU, Collection<IInstallableUnit> newRoots) { + List<WeightedObject<? extends Object>> weights = new OptimizationFunction(lastState, abstractVariables, allOptionalAbstractRequirements, picker, selectionContext, slice).createOptimizationFunction(entryPointIU, newRoots); + createObjectiveFunction(weights); + } + + private void createUserDefinedOptimizationFunction(IInstallableUnit entryPointIU, Collection<IInstallableUnit> newRoots) { + List<WeightedObject<? extends Object>> weights = new UserDefinedOptimizationFunction(lastState, abstractVariables, allOptionalAbstractRequirements, picker, selectionContext, slice, dependencyHelper, alreadyInstalledIUs).createOptimizationFunction(entryPointIU, newRoots); createObjectiveFunction(weights); } private void createObjectiveFunction(List<WeightedObject<? extends Object>> weightedObjects) { + if (weightedObjects == null) + return; if (DEBUG) { StringBuffer b = new StringBuffer(); for (WeightedObject<? extends Object> object : weightedObjects) { @@ -399,6 +424,7 @@ public class Projector { addNonGreedyProvider(getNonGreedyVariable(current), abs); } } + optionalAbstractRequirements.add(abs); } else { abs = getAbstractVariable(req, false); List<Object> newConstraint = new ArrayList<Object>(); @@ -408,7 +434,6 @@ public class Projector { } createImplication(new Object[] {abs, iu}, newConstraint, Explanation.OPTIONAL_REQUIREMENT); } - optionalAbstractRequirements.add(abs); } } } @@ -425,11 +450,9 @@ public class Projector { private void expandRequirements(Collection<IRequirement> reqs, IInstallableUnit iu, boolean isRootIu) throws ContradictionException { if (reqs.isEmpty()) return; - List<AbstractVariable> optionalAbstractRequirements = new ArrayList<AbstractVariable>(); for (IRequirement req : reqs) { - expandRequirement(req, iu, optionalAbstractRequirements, isRootIu); + expandRequirement(req, iu, allOptionalAbstractRequirements, isRootIu); } - createOptionalityExpression(iu, optionalAbstractRequirements); } public void processIU(IInstallableUnit iu, boolean isRootIU) throws ContradictionException { @@ -452,7 +475,7 @@ public class Projector { expandRequirements(getRequiredCapabilities(iu), iu, isRootIU); } else { //Patches are applicable to the IU - expandRequirementsWithPatches(iu, applicablePatches, isRootIU); + expandRequirementsWithPatches(iu, applicablePatches, allOptionalAbstractRequirements, isRootIU); } } @@ -480,7 +503,7 @@ public class Projector { Object left; } - private void expandRequirementsWithPatches(IInstallableUnit iu, IQueryResult<IInstallableUnit> applicablePatches, boolean isRootIu) throws ContradictionException { + private void expandRequirementsWithPatches(IInstallableUnit iu, IQueryResult<IInstallableUnit> applicablePatches, List<AbstractVariable> optionalAbstractRequirements, boolean isRootIu) throws ContradictionException { //Unmodified dependencies Collection<IRequirement> iuRequirements = getRequiredCapabilities(iu); Map<IRequirement, List<IInstallableUnitPatch>> unchangedRequirements = new HashMap<IRequirement, List<IInstallableUnitPatch>>(iuRequirements.size()); @@ -496,7 +519,6 @@ public class Projector { // noop(IU)-> ~ABS // IU -> (noop(IU) or ABS) // Therefore we only need one optional requirement statement per IU - List<AbstractVariable> optionalAbstractRequirements = new ArrayList<AbstractVariable>(); for (int i = 0; i < reqs.length; i++) { //The requirement is unchanged if (reqs[i][0] == reqs[i][1]) { @@ -568,6 +590,7 @@ public class Projector { addNonGreedyProvider(getNonGreedyVariable(current), abs); } } + optionalAbstractRequirements.add(abs); } else { abs = getAbstractVariable(req, false); List<Object> newConstraint = new ArrayList<Object>(matches.size()); @@ -577,7 +600,6 @@ public class Projector { } createImplication(new Object[] {patch, abs, iu}, newConstraint, Explanation.OPTIONAL_REQUIREMENT); } - optionalAbstractRequirements.add(abs); } } } @@ -683,7 +705,6 @@ public class Projector { } } } - createOptionalityExpression(iu, optionalAbstractRequirements); } // Fix: now create the pending non-patch requirements based on the full set of patches @@ -691,7 +712,6 @@ public class Projector { createImplication(pending.left, pending.matches, pending.explanation); } - List<AbstractVariable> optionalAbstractRequirements = new ArrayList<AbstractVariable>(); for (Entry<IRequirement, List<IInstallableUnitPatch>> entry : unchangedRequirements.entrySet()) { List<IInstallableUnitPatch> patchesApplied = entry.getValue(); Iterator<IInstallableUnit> allPatches = applicablePatches.iterator(); @@ -779,7 +799,6 @@ public class Projector { } } } - createOptionalityExpression(iu, optionalAbstractRequirements); } private void expandLifeCycle(IInstallableUnit iu, boolean isRootIu) throws ContradictionException { @@ -797,8 +816,6 @@ public class Projector { createNegation(iu, req); } - private boolean emptyBecauseFiltered; - /** * @param req * @return a list of mandatory requirements if any, an empty list if req.isOptional(). @@ -828,7 +845,7 @@ public class Projector { for (int i = 0; i < changes.size(); i++) { IRequirementChange change = changes.get(i); for (int j = 0; j < originalRequirements.length; j++) { - if (originalRequirements[j] != null && change.matches((IRequiredCapability) originalRequirements[j])) { + if (originalRequirements[j] != null && safeMatch(originalRequirements, change, j)) { found = true; if (change.newValue() != null) rrr.add(new IRequirement[] {originalRequirements[j], change.newValue()}); @@ -850,24 +867,12 @@ public class Projector { return rrr.toArray(new IRequirement[rrr.size()][]); } - /** - * Optional requirements are encoded via: - * ABS -> (match1(req) or match2(req) or ... or matchN(req)) - * noop(IU)-> ~ABS - * IU -> (noop(IU) or ABS) - * @param iu - * @param optionalRequirements - * @throws ContradictionException - */ - private void createOptionalityExpression(IInstallableUnit iu, List<AbstractVariable> optionalRequirements) throws ContradictionException { - if (optionalRequirements.isEmpty()) - return; - AbstractVariable noop = getNoOperationVariable(iu); - for (AbstractVariable abs : optionalRequirements) { - createIncompatibleValues(abs, noop); + private boolean safeMatch(IRequirement[] originalRequirements, IRequirementChange change, int j) { + try { + return change.matches((IRequiredCapability) originalRequirements[j]); + } catch (ClassCastException e) { + return false; } - optionalRequirements.add(noop); - createImplication(iu, optionalRequirements, Explanation.OPTIONAL_REQUIREMENT); } //This will create as many implication as there is element in the right argument @@ -948,18 +953,6 @@ public class Projector { dependencyHelper.atMost(1, (Object[]) ius).named(new Explanation.Singleton(ius)); } - private void createIncompatibleValues(AbstractVariable v1, AbstractVariable v2) throws ContradictionException { - AbstractVariable[] vars = {v1, v2}; - if (DEBUG) { - StringBuffer b = new StringBuffer(); - for (int i = 0; i < vars.length; i++) { - b.append(vars[i].toString()); - } - Tracing.debug("At most 1 of " + b); //$NON-NLS-1$ - } - dependencyHelper.atMost(1, (Object[]) vars).named(Explanation.OPTIONAL_REQUIREMENT); - } - private AbstractVariable getAbstractVariable(IRequirement req) { return getAbstractVariable(req, true); } @@ -972,15 +965,6 @@ public class Projector { return abstractVariable; } - private AbstractVariable getNoOperationVariable(IInstallableUnit iu) { - AbstractVariable v = noopVariables.get(iu); - if (v == null) { - v = DEBUG_ENCODING ? new AbstractVariable("Noop_" + iu.toString()) : new AbstractVariable(); //$NON-NLS-1$ - noopVariables.put(iu, v); - } - return v; - } - private AbstractVariable getNonGreedyVariable(IInstallableUnit iu) { AbstractVariable v = nonGreedyVariables.get(iu); if (v == null) { @@ -1007,7 +991,7 @@ public class Projector { backToIU(); long stop = System.currentTimeMillis(); if (DEBUG) - Tracing.debug("Solver solution found: " + (stop - start)); //$NON-NLS-1$ + Tracing.debug("Solver solution found in: " + (stop - start) + " ms."); //$NON-NLS-1$ } else { long stop = System.currentTimeMillis(); if (DEBUG) { @@ -1029,6 +1013,7 @@ public class Projector { private void backToIU() { solution = new ArrayList<IInstallableUnit>(); + //TODO WORK AROUND BECAUE OF A BUG IN SAT4J IVec<Object> sat4jSolution = dependencyHelper.getSolution(); for (Iterator<Object> iter = sat4jSolution.iterator(); iter.hasNext();) { Object var = iter.next(); @@ -1111,4 +1096,8 @@ public class Projector { } existingMatches.retainAll(matches); } + + public void setUserDefined(boolean containsKey) { + userDefinedFunction = containsKey; + } }
\ No newline at end of file diff --git a/bundles/org.eclipse.equinox.p2.director/src/org/eclipse/equinox/internal/p2/director/SimplePlanner.java b/bundles/org.eclipse.equinox.p2.director/src/org/eclipse/equinox/internal/p2/director/SimplePlanner.java index f733b2719..7c5729336 100644 --- a/bundles/org.eclipse.equinox.p2.director/src/org/eclipse/equinox/internal/p2/director/SimplePlanner.java +++ b/bundles/org.eclipse.equinox.p2.director/src/org/eclipse/equinox/internal/p2/director/SimplePlanner.java @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2007, 2010 IBM Corporation and others. All rights reserved. This + * Copyright (c) 2007, 2013 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 @@ -8,6 +8,7 @@ * IBM Corporation - initial API and implementation * Genuitec - bug fixes * Sonatype, Inc. - ongoing development + * Red Hat, Inc. - support for remediation page ******************************************************************************/ package org.eclipse.equinox.internal.p2.director; @@ -152,16 +153,20 @@ public class SimplePlanner implements IPlanner { 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; + int errorCode = 0; for (Explanation next : explanations) { root.add(next.toStatus()); - if (specificMessage == null && next instanceof Explanation.MissingIU) + if (specificMessage == null && next instanceof Explanation.MissingIU) { specificMessage = Messages.Explanation_rootMissing; - else if (specificMessage == null && next instanceof Explanation.Singleton) + errorCode = 10053; + } else if (specificMessage == null && next instanceof Explanation.Singleton) { specificMessage = Messages.Explanation_rootSingleton; + errorCode = 10054; + } } //use a more specific root message if available if (specificMessage != null) { - MultiStatus newRoot = new MultiStatus(DirectorActivator.PI_DIRECTOR, 1, specificMessage, null); + MultiStatus newRoot = new MultiStatus(DirectorActivator.PI_DIRECTOR, errorCode, specificMessage, null); newRoot.merge(root); root = newRoot; } @@ -333,6 +338,7 @@ public class SimplePlanner implements IPlanner { final IQueryable<IInstallableUnit>[] queryables = new IQueryable[] {slice, new QueryableArray(profileChangeRequest.getAdditions().toArray(new IInstallableUnit[profileChangeRequest.getAdditions().size()]))}; slice = new CompoundQueryable<IInstallableUnit>(queryables); Projector projector = new Projector(slice, newSelectionContext, slicer.getNonGreedyIUs(), satisfyMetaRequirements(profileChangeRequest.getProfileProperties())); + projector.setUserDefined(profileChangeRequest.getPropertiesToAdd().containsKey("_internal_user_defined_")); projector.encode((IInstallableUnit) updatedPlan[0], (IInstallableUnit[]) updatedPlan[1], profile, profileChangeRequest.getAdditions(), sub.newChild(ExpandWork / 4)); IStatus s = projector.invokeSolver(sub.newChild(ExpandWork / 4)); if (s.getSeverity() == IStatus.CANCEL) { @@ -747,7 +753,8 @@ public class SimplePlanner implements IPlanner { //Now add any other requirement that we need to see satisfied if (profileChangeRequest.getExtraRequirements() != null) gatheredRequirements.addAll(profileChangeRequest.getExtraRequirements()); - return new Object[] {createIURepresentingTheProfile(gatheredRequirements), alreadyInstalled.toArray(IInstallableUnit.class)}; + IInstallableUnit[] existingRoots = profileChangeRequest.getProfile().query(new IUProfilePropertyQuery(INCLUSION_RULES, IUProfilePropertyQuery.ANY), null).toArray(IInstallableUnit.class); + return new Object[] {createIURepresentingTheProfile(gatheredRequirements), existingRoots}; } private IRequirement createRequirement(IInstallableUnit iu, String rule) { diff --git a/bundles/org.eclipse.equinox.p2.director/src/org/eclipse/equinox/internal/p2/director/UserDefinedOptimizationFunction.java b/bundles/org.eclipse.equinox.p2.director/src/org/eclipse/equinox/internal/p2/director/UserDefinedOptimizationFunction.java new file mode 100644 index 000000000..6a8d69cc4 --- /dev/null +++ b/bundles/org.eclipse.equinox.p2.director/src/org/eclipse/equinox/internal/p2/director/UserDefinedOptimizationFunction.java @@ -0,0 +1,198 @@ +/******************************************************************************* + * Copyright (c) 2009, 2013 Daniel Le Berre 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: + * Daniel Le Berre - initial API and implementation + * Red Hat, Inc. - support for remediation page + ******************************************************************************/ +package org.eclipse.equinox.internal.p2.director; + +import java.math.BigInteger; +import java.util.*; +import org.eclipse.equinox.internal.p2.director.Projector.AbstractVariable; +import org.eclipse.equinox.p2.metadata.*; +import org.eclipse.equinox.p2.query.*; +import org.sat4j.pb.tools.*; +import org.sat4j.specs.ContradictionException; + +public class UserDefinedOptimizationFunction extends OptimizationFunction { + private Collection<IInstallableUnit> alreadyExistingRoots; + private LexicoHelper<Object, Explanation> dependencyHelper; + private IQueryable<IInstallableUnit> picker; + private List changeVariables = new ArrayList(); + private List removalVariables = new ArrayList(); + private List newVariables = new ArrayList(); + + public UserDefinedOptimizationFunction(IQueryable<IInstallableUnit> lastState, List<AbstractVariable> abstractVariables, List<AbstractVariable> optionalVariables, IQueryable<IInstallableUnit> picker, IInstallableUnit selectionContext, Map<String, Map<Version, IInstallableUnit>> slice, DependencyHelper<Object, Explanation> dependencyHelper, Collection<IInstallableUnit> alreadyInstalledIUs) { + super(lastState, abstractVariables, optionalVariables, picker, selectionContext, slice); + this.picker = picker; + this.slice = slice; + this.dependencyHelper = (LexicoHelper<Object, Explanation>) dependencyHelper; + this.alreadyExistingRoots = alreadyInstalledIUs; + } + + public List<WeightedObject<? extends Object>> createOptimizationFunction(IInstallableUnit metaIu, Collection<IInstallableUnit> newRoots) { + List weightedObjects = new ArrayList(); + List objects = new ArrayList(); + BigInteger weight = BigInteger.valueOf(slice.size() + 1); + String[] criteria = new String[] {"+new", "-removed", "-changed"}; + BigInteger currentWeight = weight.pow(criteria.length - 1); + int formermaxvarid = dependencyHelper.getSolver().nextFreeVarId(false); + int newmaxvarid; + boolean maximizes; + Object thing; + for (int i = 0; i < criteria.length; i++) { + if (criteria[i].endsWith("new")) { + weightedObjects.clear(); + newRoots(weightedObjects, criteria[i].startsWith("+") ? currentWeight.negate() : currentWeight, metaIu); + currentWeight = currentWeight.divide(weight); + } else if (criteria[i].endsWith("removed")) { + weightedObjects.clear(); + removedRoots(weightedObjects, criteria[i].startsWith("+") ? currentWeight.negate() : currentWeight, metaIu); + currentWeight = currentWeight.divide(weight); + // } else if (criteria[i].endsWith("notuptodate")) { + // weightedObjects.clear(); + // notuptodate(weightedObjects, criteria[i].startsWith("+") ? currentWeight.negate() : currentWeight, metaIu); + // currentWeight = currentWeight.divide(weight); + // } else if (criteria[i].endsWith("unsat_recommends")) { + // weightedObjects.clear(); + // optional(weightedObjects, criteria[i].startsWith("+") ? currentWeight.negate() : currentWeight, metaIu); + // currentWeight = currentWeight.divide(weight); + // } else if (criteria[i].endsWith("versionchanged")) { + // weightedObjects.clear(); + // versionChanged(weightedObjects, criteria[i].startsWith("+") ? currentWeight.negate() : currentWeight, metaIu); + } else if (criteria[i].endsWith("changed")) { + weightedObjects.clear(); + changedRoots(weightedObjects, criteria[i].startsWith("+") ? currentWeight.negate() : currentWeight, metaIu); + currentWeight = currentWeight.divide(weight); + // } else if (criteria[i].contains("sum")) { + // weightedObjects.clear(); + // sum(weightedObjects, criteria[i].charAt(0) == '-', metaIu, Options.extractSumProperty(criteria[i])); + // dependencyHelper.addWeightedCriterion(weightedObjects); + // System.out.println("# criteria " + criteria[i].substring(1) + " size is " + weightedObjects.size()); + // continue; + // } else { + // System.out.println("Skipping unknown criteria:" + criteria[i]); + } + objects.clear(); + maximizes = criteria[i].startsWith("+"); + for (Iterator it = weightedObjects.iterator(); it.hasNext();) { + thing = ((WeightedObject) it.next()).thing; + if (maximizes) { + thing = dependencyHelper.not(thing); + } + objects.add(thing); + } + dependencyHelper.addCriterion(objects); + newmaxvarid = dependencyHelper.getSolver().nextFreeVarId(false); + // System.out.println("# criteria " + criteria[i].substring(1) + " size is " + objects.size() + " using new vars " + formermaxvarid + " to " + newmaxvarid); + formermaxvarid = newmaxvarid; + } + weightedObjects.clear(); + return null; + } + + protected void changedRoots(List<WeightedObject<?>> weightedObjects, BigInteger weight, IInstallableUnit entryPointIU) { + Collection<IRequirement> requirements = entryPointIU.getRequirements(); + for (IRequirement req : requirements) { + IQuery<IInstallableUnit> query = QueryUtil.createMatchQuery(req.getMatches()); + IQueryResult<IInstallableUnit> matches = picker.query(query, null); + Object[] changed = new Object[matches.toUnmodifiableSet().size()]; + int i = 0; + for (IInstallableUnit match : matches) { + changed[i++] = isInstalledAsRoot(match) ? dependencyHelper.not(match) : match; + } + try { + Projector.AbstractVariable abs = new Projector.AbstractVariable("CHANGED"); //TODO + changeVariables.add(abs); + // abs <=> iuv1 or not iuv2 or ... or not iuvn + dependencyHelper.or(FakeExplanation.getInstance(), abs, changed); + weightedObjects.add(WeightedObject.newWO(abs, weight)); + } catch (ContradictionException e) { + // TODO Auto-generated catch block TODO + e.printStackTrace(); + } + } + } + + protected void newRoots(List<WeightedObject<?>> weightedObjects, BigInteger weight, IInstallableUnit entryPointIU) { + Collection<IRequirement> requirements = entryPointIU.getRequirements(); + for (IRequirement req : requirements) { + IQuery<IInstallableUnit> query = QueryUtil.createMatchQuery(req.getMatches()); + IQueryResult<IInstallableUnit> matches = picker.query(query, null); + boolean oneInstalled = false; + for (IInstallableUnit match : matches) { + oneInstalled = oneInstalled || isInstalledAsRoot(match); + } + if (!oneInstalled) { + try { + Projector.AbstractVariable abs = new Projector.AbstractVariable("NEW"); //TODO + newVariables.add(abs); + // a <=> iuv1 or ... or iuvn + dependencyHelper.or(FakeExplanation.getInstance(), abs, matches.toArray(IInstallableUnit.class)); + weightedObjects.add(WeightedObject.newWO(abs, weight)); + } catch (ContradictionException e) { + // should not happen + e.printStackTrace(); + } + } + } + } + + protected void removedRoots(List<WeightedObject<?>> weightedObjects, BigInteger weight, IInstallableUnit entryPointIU) { + Collection<IRequirement> requirements = entryPointIU.getRequirements(); + for (IRequirement req : requirements) { + IQuery<IInstallableUnit> query = QueryUtil.createMatchQuery(req.getMatches()); + IQueryResult<IInstallableUnit> matches = picker.query(query, null); + boolean installed = false; + Object[] literals = new Object[matches.toUnmodifiableSet().size()]; + int i = 0; + for (IInstallableUnit match : matches) { + installed = installed || isInstalledAsRoot(match); + literals[i++] = dependencyHelper.not(match); + } + if (installed) { + try { + Projector.AbstractVariable abs = new Projector.AbstractVariable("REMOVED"); //TODO + removalVariables.add(abs); + // abs <=> not iuv1 and ... and not iuvn + dependencyHelper.and(FakeExplanation.getInstance(), abs, literals); + weightedObjects.add(WeightedObject.newWO(abs, weight)); + } catch (ContradictionException e) { + // should not happen TODO + e.printStackTrace(); + } + } + } + } + + private static class FakeExplanation extends Explanation { + private static Explanation singleton = new FakeExplanation(); + + public static Explanation getInstance() { + return singleton; + } + + protected int orderValue() { + return Explanation.OTHER_REASON; + } + + @Override + public int shortAnswer() { + return 0; + } + + } + + private boolean isInstalledAsRoot(IInstallableUnit isInstalled) { + for (IInstallableUnit installed : alreadyExistingRoots) { + if (isInstalled.equals(installed)) + return true; + } + return false; + } + +} diff --git a/bundles/org.eclipse.equinox.p2.metadata/src/org/eclipse/equinox/p2/query/IQueryResult.java b/bundles/org.eclipse.equinox.p2.metadata/src/org/eclipse/equinox/p2/query/IQueryResult.java index 82f336387..acdbc5e74 100644 --- a/bundles/org.eclipse.equinox.p2.metadata/src/org/eclipse/equinox/p2/query/IQueryResult.java +++ b/bundles/org.eclipse.equinox.p2.metadata/src/org/eclipse/equinox/p2/query/IQueryResult.java @@ -17,7 +17,7 @@ import java.util.Set; * * @since 2.0 */ -public interface IQueryResult<T> extends IQueryable<T> { +public interface IQueryResult<T> extends IQueryable<T>, Iterable<T> { /** * Returns whether this QueryResult is empty. * @return <code>true</code> if this QueryResult has accepted any results, diff --git a/bundles/org.eclipse.equinox.p2.operations/META-INF/MANIFEST.MF b/bundles/org.eclipse.equinox.p2.operations/META-INF/MANIFEST.MF index cf4c946a3..a585e4215 100644 --- a/bundles/org.eclipse.equinox.p2.operations/META-INF/MANIFEST.MF +++ b/bundles/org.eclipse.equinox.p2.operations/META-INF/MANIFEST.MF @@ -25,6 +25,7 @@ Import-Package: org.eclipse.equinox.internal.p2.core.helpers, org.eclipse.equinox.p2.engine;version="[2.0.0,3.0.0)", org.eclipse.equinox.p2.engine.query;version="[2.0.0,3.0.0)", org.eclipse.equinox.p2.metadata;version="[2.0.0,3.0.0)", + org.eclipse.equinox.p2.metadata.expression;version="2.0.0", org.eclipse.equinox.p2.planner;version="[2.0.0,3.0.0)", org.eclipse.equinox.p2.query;version="[2.0.0,3.0.0)", org.eclipse.equinox.p2.repository;version="[2.0.0,3.0.0)", diff --git a/bundles/org.eclipse.equinox.p2.operations/src/org/eclipse/equinox/internal/p2/operations/IStatusCodes.java b/bundles/org.eclipse.equinox.p2.operations/src/org/eclipse/equinox/internal/p2/operations/IStatusCodes.java index 61bdb13b1..580cdf4f9 100644 --- a/bundles/org.eclipse.equinox.p2.operations/src/org/eclipse/equinox/internal/p2/operations/IStatusCodes.java +++ b/bundles/org.eclipse.equinox.p2.operations/src/org/eclipse/equinox/internal/p2/operations/IStatusCodes.java @@ -45,6 +45,7 @@ public interface IStatusCodes { public static final int UNEXPECTED_NOTHING_TO_DO = 10050; public static final int EXPECTED_NOTHING_TO_DO = 10051; public static final int OPERATION_ALREADY_IN_PROGRESS = 10052; + public static final int MISSING_REQUIREMENTS = 10053; // Status codes associated with repositories public static final int INVALID_REPOSITORY_LOCATION = 10100; diff --git a/bundles/org.eclipse.equinox.p2.operations/src/org/eclipse/equinox/internal/p2/operations/Messages.java b/bundles/org.eclipse.equinox.p2.operations/src/org/eclipse/equinox/internal/p2/operations/Messages.java index 918011323..792778bdb 100644 --- a/bundles/org.eclipse.equinox.p2.operations/src/org/eclipse/equinox/internal/p2/operations/Messages.java +++ b/bundles/org.eclipse.equinox.p2.operations/src/org/eclipse/equinox/internal/p2/operations/Messages.java @@ -66,8 +66,9 @@ public class Messages extends NLS { public static String UpdateOperation_ResolveJobName; public static String UpdateOperation_UpdateJobName; - public static String RelaxedUpdateOperation_ResolveJobName; - public static String RelaxedUpdateOperation_UpdateJobName; + public static String RemediationOperation_ProfileChangeRequestProgress; + public static String RemediationOperation_ResolveJobName; + public static String RemediationOperation_RemediationJobName; public static String OperationFactory_noAgent; public static String OperationFactory_noIUFound; diff --git a/bundles/org.eclipse.equinox.p2.operations/src/org/eclipse/equinox/internal/p2/operations/PlanAnalyzer.java b/bundles/org.eclipse.equinox.p2.operations/src/org/eclipse/equinox/internal/p2/operations/PlanAnalyzer.java index f32511f0b..552e46240 100644 --- a/bundles/org.eclipse.equinox.p2.operations/src/org/eclipse/equinox/internal/p2/operations/PlanAnalyzer.java +++ b/bundles/org.eclipse.equinox.p2.operations/src/org/eclipse/equinox/internal/p2/operations/PlanAnalyzer.java @@ -129,6 +129,15 @@ public class PlanAnalyzer { report.addSummaryStatus(fail); } } + } else { + if (plannerStatus.getCode() == IStatusCodes.MISSING_REQUIREMENTS) { + IStatus existingSummaryStatus = report.getSummaryStatus(); + MultiStatus newSummaryStatus = new MultiStatus(Activator.ID, IStatusCodes.MISSING_REQUIREMENTS, Messages.ResolutionResult_SummaryStatus, null); + if (!existingSummaryStatus.isOK()) { + newSummaryStatus.addAll(existingSummaryStatus); + report.setSummaryStatus(newSummaryStatus); + } + } } // Now process the side effects diff --git a/bundles/org.eclipse.equinox.p2.operations/src/org/eclipse/equinox/internal/p2/operations/RequestFlexer.java b/bundles/org.eclipse.equinox.p2.operations/src/org/eclipse/equinox/internal/p2/operations/RequestFlexer.java new file mode 100644 index 000000000..94ff05068 --- /dev/null +++ b/bundles/org.eclipse.equinox.p2.operations/src/org/eclipse/equinox/internal/p2/operations/RequestFlexer.java @@ -0,0 +1,344 @@ +/******************************************************************************* + * Copyright (c) 2013 Red Hat, Inc. 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: + * Red Hat, Inc. - initial API and implementation + *******************************************************************************/ +package org.eclipse.equinox.internal.p2.operations; + +import java.util.*; +import java.util.Map.Entry; +import org.eclipse.core.runtime.IProgressMonitor; +import org.eclipse.core.runtime.NullProgressMonitor; +import org.eclipse.equinox.internal.p2.director.ProfileChangeRequest; +import org.eclipse.equinox.p2.engine.*; +import org.eclipse.equinox.p2.engine.query.IUProfilePropertyQuery; +import org.eclipse.equinox.p2.metadata.*; +import org.eclipse.equinox.p2.metadata.expression.ExpressionUtil; +import org.eclipse.equinox.p2.metadata.expression.IMatchExpression; +import org.eclipse.equinox.p2.planner.IPlanner; +import org.eclipse.equinox.p2.planner.IProfileChangeRequest; +import org.eclipse.equinox.p2.query.*; + +public class RequestFlexer { + final String INCLUSION_RULES = "org.eclipse.equinox.p2.internal.inclusion.rules"; //$NON-NLS-1$ + final String INCLUSION_OPTIONAL = "OPTIONAL"; //$NON-NLS-1$ + final String INCLUSION_STRICT = "STRICT"; //$NON-NLS-1$ + + IPlanner planner; + + private boolean allowInstalledUpdate = false; + private boolean allowInstalledRemoval = false; + private boolean allowDifferentVersion = false; + private boolean allowPartialInstall = false; + private ProvisioningContext provisioningContext; + + Set<IRequirement> requirementsForElementsBeingInstalled = new HashSet<IRequirement>(); + Set<IRequirement> requirementsForElementsAlreadyInstalled = new HashSet<IRequirement>(); + Map<IRequirement, Map> propertiesPerRequirement = new HashMap(); + Map<IRequirement, List<String>> removedPropertiesPerRequirement = new HashMap(); + + IProfile profile; + + private boolean foundDifferentVersionsForElementsToInstall = false; + private boolean foundDifferentVersionsForElementsInstalled = false; + private Set<IInstallableUnit> futureOptionalIUs; + + public RequestFlexer(IPlanner planner) { + this.planner = planner; + } + + public void setAllowInstalledElementChange(boolean allow) { + allowInstalledUpdate = allow; + } + + public void setAllowInstalledElementRemoval(boolean allow) { + allowInstalledRemoval = allow; + } + + public void setAllowDifferentVersion(boolean allow) { + allowDifferentVersion = allow; + } + + public void setAllowPartialInstall(boolean allow) { + allowPartialInstall = allow; + } + + public void setProvisioningContext(ProvisioningContext context) { + provisioningContext = context; + } + + public IProfileChangeRequest getChangeRequest(IProfileChangeRequest request, IProfile prof, IProgressMonitor monitor) { + this.profile = prof; + IProfileChangeRequest loosenedRequest = computeLooseRequest(request); + if (canShortCircuit(request)) { + return null; + } + IProvisioningPlan intermediaryPlan = resolve(loosenedRequest); + if (!intermediaryPlan.getStatus().isOK()) + return null; + if (intermediaryPlan.getAdditions().query(QueryUtil.ALL_UNITS, new NullProgressMonitor()).isEmpty() && intermediaryPlan.getRemovals().query(QueryUtil.ALL_UNITS, new NullProgressMonitor()).isEmpty()) + //No changes, we can't return anything + return null; + IProfileChangeRequest effectiveRequest = computeEffectiveChangeRequest(intermediaryPlan, loosenedRequest, request); + if (effectiveRequest.getAdditions().isEmpty() && effectiveRequest.getRemovals().isEmpty()) + return null; + return effectiveRequest; + } + + private boolean canShortCircuit(IProfileChangeRequest originalRequest) { + //Case where the user is asking to install only some of the requested IUs but there is only one IU to install. + if (allowPartialInstall) + if (originalRequest.getAdditions().size() == 1 && originalRequest.getRemovals().isEmpty()) + return true; + + //When we can find a different version of the IU but the only version available is the one the user is asking to install + if (allowDifferentVersion && !allowPartialInstall && !allowInstalledRemoval && !allowInstalledUpdate) + if (!foundDifferentVersionsForElementsToInstall) + return true; + + if (allowInstalledUpdate && !allowDifferentVersion && !allowPartialInstall && !allowInstalledRemoval) + if (!foundDifferentVersionsForElementsInstalled) + return true; + + return false; + } + + //From the loosened request and the plan resulting from its resolution, create a new profile change request representing the delta between where the profile currently is + // and the plan returned. + //To perform this efficiently, this relies on a traversal of the requirements that are part of the loosened request. + private IProfileChangeRequest computeEffectiveChangeRequest(IProvisioningPlan intermediaryPlan, IProfileChangeRequest loosenedRequest, IProfileChangeRequest originalRequest) { + IProfileChangeRequest finalChangeRequest = planner.createChangeRequest(profile); + + for (IRequirement beingInstalled : requirementsForElementsBeingInstalled) { + IQuery<IInstallableUnit> query = QueryUtil.createMatchQuery(beingInstalled.getMatches()); + IQueryResult<IInstallableUnit> matches = intermediaryPlan.getFutureState().query(QueryUtil.createLatestQuery(query), null); + IInstallableUnit replacementIU = null; + if (!matches.isEmpty()) { + replacementIU = matches.iterator().next(); + finalChangeRequest.add(replacementIU); + adaptIUPropertiesToNewIU(beingInstalled, replacementIU, finalChangeRequest); + } + } + + for (IRequirement alreadyInstalled : requirementsForElementsAlreadyInstalled) { + IQuery<IInstallableUnit> query = QueryUtil.createMatchQuery(alreadyInstalled.getMatches()); + IQueryResult<IInstallableUnit> matches = intermediaryPlan.getFutureState().query(QueryUtil.createLatestQuery(query), null); + IInstallableUnit potentialRootChange = null; + if (!matches.isEmpty()) + potentialRootChange = matches.iterator().next(); + + IQueryResult<IInstallableUnit> iuAlreadyInstalled = profile.available(query, new NullProgressMonitor()); + + if (!iuAlreadyInstalled.isEmpty()) {//This deals with the case where the root has not changed + if (potentialRootChange != null && iuAlreadyInstalled.toUnmodifiableSet().contains(potentialRootChange)) + continue; + } + finalChangeRequest.removeAll(iuAlreadyInstalled.toUnmodifiableSet()); + if (potentialRootChange != null) { + if (!finalChangeRequest.getAdditions().contains(potentialRootChange)) {//So we don't add the same IU twice for addition + finalChangeRequest.add(potentialRootChange); + adaptIUPropertiesToNewIU(alreadyInstalled, potentialRootChange, finalChangeRequest); + } + } + } + + finalChangeRequest.removeAll(originalRequest.getRemovals()); + if (originalRequest.getExtraRequirements() != null) + finalChangeRequest.addExtraRequirements(originalRequest.getExtraRequirements()); + return finalChangeRequest; + } + + private void adaptIUPropertiesToNewIU(IRequirement beingInstalled, IInstallableUnit newIU, IProfileChangeRequest finalChangeRequest) { + Map<String, String> associatedProperties = propertiesPerRequirement.get(beingInstalled); + if (associatedProperties != null) { + Set<Entry<String, String>> entries = associatedProperties.entrySet(); + for (Entry<String, String> entry : entries) { + finalChangeRequest.setInstallableUnitProfileProperty(newIU, entry.getKey(), entry.getValue()); + } + } + List<String> removedProperties = removedPropertiesPerRequirement.get(beingInstalled); + if (removedProperties != null) { + for (String toRemove : removedProperties) { + finalChangeRequest.removeInstallableUnitProfileProperty(newIU, toRemove); + } + } + } + + //Create a request where the original requirements are "loosened" according to flags specified in this instance + //The resulting profile change request uses the requirements specified using p2QL and those appear in the extraRequirements. + private IProfileChangeRequest computeLooseRequest(IProfileChangeRequest originalRequest) { + IProfileChangeRequest loosenedRequest = planner.createChangeRequest(profile); + loosenUpOriginalRequest(loosenedRequest, originalRequest); + loosenUpInstalledSoftware(loosenedRequest, originalRequest); + return loosenedRequest; + } + + private boolean removalRequested(IInstallableUnit removalRequested, IProfileChangeRequest request) { + return request.getRemovals().contains(removalRequested); + } + + private IProvisioningPlan resolve(IProfileChangeRequest temporaryRequest) { + temporaryRequest.setProfileProperty("_internal_user_defined_", "true"); + return planner.getProvisioningPlan(temporaryRequest, provisioningContext, null); + } + + //Loosen the request originally emitted. + //For example if the user said "install A 1.0", then a new Requirement is added saying (install A 1.0 or install A 2.0), this depending on the configuration flags + private void loosenUpOriginalRequest(IProfileChangeRequest newRequest, IProfileChangeRequest originalRequest) { + //First deal with the IUs that are being added + Collection<IInstallableUnit> requestedAdditions = originalRequest.getAdditions(); + for (IInstallableUnit addedIU : requestedAdditions) { + Collection<IInstallableUnit> potentialUpdates = allowDifferentVersion ? findAllVersionsAvailable(addedIU) : new ArrayList(); + foundDifferentVersionsForElementsToInstall = (foundDifferentVersionsForElementsToInstall || (potentialUpdates.size() == 0 ? false : true)); + potentialUpdates.add(addedIU); //Make sure that we include the IU that we were initially trying to install + + Collection<IRequirement> newRequirement = new ArrayList<IRequirement>(1); + IRequirement req = createORRequirement(potentialUpdates, allowPartialInstall || isRequestedInstallationOptional(addedIU, originalRequest)); + newRequirement.add(req); + newRequest.addExtraRequirements(newRequirement); + requirementsForElementsBeingInstalled.addAll(newRequirement); + rememberIUProfileProperties(addedIU, req, originalRequest, false); + } + + //Deal with the IUs requested for removal + newRequest.removeAll(originalRequest.getRemovals()); + + //Deal with extra requirements that could have been specified + if (originalRequest.getExtraRequirements() != null) + newRequest.addExtraRequirements(originalRequest.getExtraRequirements()); + } + + //This keeps track for each requirement created (those created to loosen the constraint), of the original IU and the properties associated with it in the profile + //This is used for more easily construct the final profile change request + private void rememberIUProfileProperties(IInstallableUnit iu, IRequirement req, IProfileChangeRequest originalRequest, boolean includeProfile) { + Map<String, String> allProperties = new HashMap<String, String>(); + if (includeProfile) { + Map<String, String> tmp = new HashMap(profile.getInstallableUnitProperties(iu)); + List<String> propertiesToRemove = ((ProfileChangeRequest) originalRequest).getInstallableUnitProfilePropertiesToRemove().get(iu); + if (propertiesToRemove != null) { + for (String toRemove : propertiesToRemove) { + tmp.remove(toRemove); + } + } + allProperties.putAll(tmp); + } + + Map<String, String> propertiesInRequest = ((ProfileChangeRequest) originalRequest).getInstallableUnitProfilePropertiesToAdd().get(iu); + if (propertiesInRequest != null) + allProperties.putAll(propertiesInRequest); + + propertiesPerRequirement.put(req, allProperties); + + List<String> removalInRequest = ((ProfileChangeRequest) originalRequest).getInstallableUnitProfilePropertiesToRemove().get(iu); + if (removalInRequest != null) + removedPropertiesPerRequirement.put(req, removalInRequest); + } + + private boolean isRequestedInstallationOptional(IInstallableUnit iu, IProfileChangeRequest originalRequest) { + Map<String, String> match = ((ProfileChangeRequest) originalRequest).getInstallableUnitProfilePropertiesToAdd().get(iu); + if (match == null) + return false; + return INCLUSION_OPTIONAL.equals(match.get(INCLUSION_RULES)); + } + + private Collection<IInstallableUnit> findAllVersionsAvailable(IInstallableUnit iu) { + Collection<IInstallableUnit> allVersions = new HashSet(); + allVersions.addAll(findIUsWithSameId(iu)); + allVersions.addAll(findUpdates(iu)); + return allVersions; + } + + private Collection<IInstallableUnit> findIUsWithSameId(IInstallableUnit iu) { + return provisioningContext.getMetadata(null).query(QueryUtil.createIUQuery(iu.getId()), null).toUnmodifiableSet(); + } + + private Collection<IInstallableUnit> findUpdates(IInstallableUnit iu) { + Collection<IInstallableUnit> availableUpdates = new HashSet<IInstallableUnit>(); + IQueryResult<IInstallableUnit> updatesAvailable = planner.updatesFor(iu, provisioningContext, null); + for (Iterator<IInstallableUnit> iterator = updatesAvailable.iterator(); iterator.hasNext();) { + availableUpdates.add(iterator.next()); + } + return availableUpdates; + } + + //Create an OR expression that is matching all the entries from the given collection + private IRequirement createORRequirement(Collection<IInstallableUnit> findUpdates, boolean optional) { + StringBuffer expression = new StringBuffer(); + Object[] expressionParameters = new Object[findUpdates.size() * 2]; + int count = 0; + for (IInstallableUnit iu : findUpdates) { + expression.append("(id == $").append(count * 2).append(" && version == $").append(count * 2 + 1).append(')'); //$NON-NLS-1$//$NON-NLS-2$ + if (findUpdates.size() > 1 && count < findUpdates.size() - 1) + expression.append(" || "); //$NON-NLS-1$ + expressionParameters[count * 2] = iu.getId(); + expressionParameters[count * 2 + 1] = iu.getVersion(); + count++; + } + IMatchExpression<IInstallableUnit> iuMatcher = ExpressionUtil.getFactory().<IInstallableUnit> matchExpression(ExpressionUtil.parse(expression.toString()), expressionParameters); + return MetadataFactory.createRequirement(iuMatcher, null, optional ? 0 : 1, 1, true); + } + + //Loosen up the IUs that are already part of the profile + //Given how we are creating our request, this needs to take into account the removal from the original request as well as the change in inclusion + private IProfileChangeRequest loosenUpInstalledSoftware(IProfileChangeRequest request, IProfileChangeRequest originalRequest) { + IQueryResult<IInstallableUnit> allRoots = profile.query(new IUProfilePropertyQuery(INCLUSION_RULES, IUProfilePropertyQuery.ANY), null); + + for (IInstallableUnit existingIU : allRoots) { + Collection<IInstallableUnit> potentialUpdates = allowInstalledUpdate ? findUpdates(existingIU) : new HashSet(); + foundDifferentVersionsForElementsInstalled = (foundDifferentVersionsForElementsInstalled || (potentialUpdates.size() == 0 ? false : true)); + potentialUpdates.add(existingIU); + Collection<IRequirement> newRequirement = new ArrayList<IRequirement>(1); + //when the element is requested for removal or is installed optionally we make sure to mark it optional, otherwise the removal woudl fail + IRequirement req = createORRequirement(potentialUpdates, allowInstalledRemoval || removalRequested(existingIU, originalRequest) || isOptionallyInstalled(existingIU, originalRequest)); + newRequirement.add(req); + request.addExtraRequirements(newRequirement); + requirementsForElementsAlreadyInstalled.addAll(newRequirement); + request.remove(existingIU); + rememberIUProfileProperties(existingIU, req, originalRequest, true); + } + + return request; + } + + //This return whether or not the given IU is installed optionally or not. + //This also take into account the future state + private boolean isOptionallyInstalled(IInstallableUnit existingIU, IProfileChangeRequest request) { + return computeFutureStateOfInclusion((ProfileChangeRequest) request).contains(existingIU); + } + + //Given the change request, this returns the collection of optional IUs + private Set<IInstallableUnit> computeFutureStateOfInclusion(ProfileChangeRequest profileChangeRequest) { + if (futureOptionalIUs != null) + return futureOptionalIUs; + + futureOptionalIUs = profileChangeRequest.getProfile().query(new IUProfilePropertyQuery(INCLUSION_RULES, INCLUSION_OPTIONAL), null).toSet(); + + Set<Entry<IInstallableUnit, List<String>>> propertiesBeingRemoved = profileChangeRequest.getInstallableUnitProfilePropertiesToRemove().entrySet(); + for (Entry<IInstallableUnit, List<String>> propertyRemoved : propertiesBeingRemoved) { + if (propertyRemoved.getValue().contains(INCLUSION_RULES)) { + futureOptionalIUs.remove(propertyRemoved.getKey()); + } + } + + Set<Entry<IInstallableUnit, Map<String, String>>> propertiesBeingAdded = profileChangeRequest.getInstallableUnitProfilePropertiesToAdd().entrySet(); + for (Entry<IInstallableUnit, Map<String, String>> propertyBeingAdded : propertiesBeingAdded) { + String inclusionRule = propertyBeingAdded.getValue().get(INCLUSION_RULES); + if (inclusionRule == null) { + continue; + } + if (INCLUSION_STRICT.equals(inclusionRule)) { + futureOptionalIUs.remove(propertyBeingAdded.getKey()); + } + if (INCLUSION_OPTIONAL.equals(inclusionRule)) { + futureOptionalIUs.add(propertyBeingAdded.getKey()); + } + } + return futureOptionalIUs; + + } +} diff --git a/bundles/org.eclipse.equinox.p2.operations/src/org/eclipse/equinox/internal/p2/operations/ResolutionResult.java b/bundles/org.eclipse.equinox.p2.operations/src/org/eclipse/equinox/internal/p2/operations/ResolutionResult.java index 20c174083..16d360cf2 100644 --- a/bundles/org.eclipse.equinox.p2.operations/src/org/eclipse/equinox/internal/p2/operations/ResolutionResult.java +++ b/bundles/org.eclipse.equinox.p2.operations/src/org/eclipse/equinox/internal/p2/operations/ResolutionResult.java @@ -33,6 +33,10 @@ public class ResolutionResult { return Status.OK_STATUS; } + public void setSummaryStatus(MultiStatus status) { + summaryStatus = status; + } + public void addSummaryStatus(IStatus status) { if (summaryStatus == null) { summaryStatus = new MultiStatus(Activator.ID, 0, Messages.ResolutionResult_SummaryStatus, null); diff --git a/bundles/org.eclipse.equinox.p2.operations/src/org/eclipse/equinox/internal/p2/operations/messages.properties b/bundles/org.eclipse.equinox.p2.operations/src/org/eclipse/equinox/internal/p2/operations/messages.properties index 2d2253ea5..0147c0507 100644 --- a/bundles/org.eclipse.equinox.p2.operations/src/org/eclipse/equinox/internal/p2/operations/messages.properties +++ b/bundles/org.eclipse.equinox.p2.operations/src/org/eclipse/equinox/internal/p2/operations/messages.properties @@ -11,6 +11,8 @@ InstallOperation_ComputeProfileChangeProgress=Checking the install request InstallOperation_InstallJobName=Installing Software InstallOperation_ResolveJobName=Computing install requirements +RemediationOperation_InstallJobName=Installing Software +RemediationOperation_ResolveJobName=Computing remediation requirements ProfileChangeOperation_NoProfileChangeRequest=Could not interpret the installation request. ProfileChangeOperation_ResolveTaskName=Analyzing the install operation ProvisioningJob_GenericErrorStatusMessage=Error encountered while running {0} @@ -42,8 +44,10 @@ UninstallOperation_ResolveJobName=Calculating Uninstall Validity UpdateOperation_ProfileChangeRequestProgress=Computing update request UpdateOperation_ResolveJobName=Computing Update Requirements UpdateOperation_UpdateJobName=Updating Software -RelaxedUpdateOperation_ResolveJobName=Computing Update Requirements -RelaxedUpdateOperation_UpdateJobName=Updating Software +RemediationOperation_ProvisioningJobName=Cannot complete the operation, searching alternate solutions +RemediationOperation_ProfileChangeRequestProgress=Computing alternate solutions +RemediationOperation_ResolveJobName=Cannot complete the operation, searching alternate solutions +RemediationOperation_RemediationJobName=Installing Software OperationFactory_noAgent=This installation has not been configured properly. No provisioning agent can be found. diff --git a/bundles/org.eclipse.equinox.p2.operations/src/org/eclipse/equinox/p2/operations/RelaxedUpdateInstallOperation.java b/bundles/org.eclipse.equinox.p2.operations/src/org/eclipse/equinox/p2/operations/RelaxedUpdateInstallOperation.java deleted file mode 100644 index 8605f0f83..000000000 --- a/bundles/org.eclipse.equinox.p2.operations/src/org/eclipse/equinox/p2/operations/RelaxedUpdateInstallOperation.java +++ /dev/null @@ -1,112 +0,0 @@ -package org.eclipse.equinox.p2.operations; - -import java.util.*; -import org.eclipse.core.runtime.IProgressMonitor; -import org.eclipse.core.runtime.MultiStatus; -import org.eclipse.equinox.internal.p2.director.ProfileChangeRequest; -import org.eclipse.equinox.internal.p2.metadata.query.UpdateQuery; -import org.eclipse.equinox.internal.p2.operations.Messages; -import org.eclipse.equinox.p2.engine.*; -import org.eclipse.equinox.p2.engine.query.IUProfilePropertyQuery; -import org.eclipse.equinox.p2.metadata.*; -import org.eclipse.equinox.p2.planner.*; -import org.eclipse.equinox.p2.query.*; - -/** - * An operation that updates IUs with relaxed p2 constraints - * - * @noinstantiate This class is not intended to be instantiated by clients. - * @noreference - * @since 2.2 - */ -public class RelaxedUpdateInstallOperation extends ProfileChangeOperation { - - public RelaxedUpdateInstallOperation(ProvisioningSession session) { - super(session); - } - - @Override - protected void computeProfileChangeRequest(MultiStatus status, IProgressMonitor monitor) { - IProfileRegistry profileRegistry = (IProfileRegistry) session.getProvisioningAgent().getService(IProfileRegistry.SERVICE_NAME); - IPlanner plan = (IPlanner) session.getProvisioningAgent().getService(IPlanner.SERVICE_NAME); - IProfile prof = profileRegistry.getProfile(getProfileId()); - - final String INCLUSION_RULES = "org.eclipse.equinox.p2.internal.inclusion.rules"; //$NON-NLS-1$ - final String INCLUSION_OPTIONAL = "OPTIONAL"; //$NON-NLS-1$ - final String INCLUSION_STRICT = "STRICT"; //$NON-NLS-1$ - - IQueryResult<IInstallableUnit> strictRoots = prof.query(new IUProfilePropertyQuery(INCLUSION_RULES, INCLUSION_STRICT), null); - IQueryResult<IInstallableUnit> optionalRoots = prof.query(new IUProfilePropertyQuery(INCLUSION_RULES, INCLUSION_OPTIONAL), null); - Set<IInstallableUnit> tmpRoots = new HashSet<IInstallableUnit>(strictRoots.toUnmodifiableSet()); - tmpRoots.addAll(optionalRoots.toUnmodifiableSet()); - CollectionResult<IInstallableUnit> allRoots = new CollectionResult<IInstallableUnit>(tmpRoots); - - request = (ProfileChangeRequest) plan.createChangeRequest(prof); - Collection<IRequirement> limitingRequirements = new ArrayList<IRequirement>(); - - for (Iterator<IInstallableUnit> iterator = allRoots.query(QueryUtil.ALL_UNITS, null).iterator(); iterator.hasNext();) { - IInstallableUnit currentlyInstalled = iterator.next(); - - //find all the potential updates for the currentlyInstalled iu - IQueryResult<IInstallableUnit> updatesAvailable = plan.updatesFor(currentlyInstalled, context, null); - for (Iterator<IInstallableUnit> iterator2 = updatesAvailable.iterator(); iterator2.hasNext();) { - IInstallableUnit update = iterator2.next(); - request.add(update); - request.setInstallableUnitInclusionRules(update, ProfileInclusionRules.createOptionalInclusionRule(update)); - } - if (!updatesAvailable.isEmpty()) { - //force the original IU to optional, but make sure that the solution at least includes it - request.setInstallableUnitInclusionRules(currentlyInstalled, ProfileInclusionRules.createOptionalInclusionRule(currentlyInstalled)); - limitingRequirements.add(MetadataFactory.createRequirement(IInstallableUnit.NAMESPACE_IU_ID, currentlyInstalled.getId(), new VersionRange(currentlyInstalled.getVersion(), true, Version.MAX_VERSION, true), null, false, false)); - } - } - - IProvisioningPlan updateFinderPlan = plan.getProvisioningPlan(request, context, null); - if (updateFinderPlan.getAdditions().query(QueryUtil.ALL_UNITS, null).isEmpty()) { - return; - } - - //Take into account all the removals - IProfileChangeRequest finalChangeRequest = plan.createChangeRequest(prof); - IQueryResult<IInstallableUnit> removals = updateFinderPlan.getRemovals().query(QueryUtil.ALL_UNITS, null); - for (Iterator<IInstallableUnit> iterator = removals.iterator(); iterator.hasNext();) { - IInstallableUnit iu = iterator.next(); - if (!allRoots.query(QueryUtil.createIUQuery(iu), null).isEmpty()) { - finalChangeRequest.remove(iu); - } - } - - //Take into account the additions for stricts - for (Iterator<IInstallableUnit> iterator = strictRoots.iterator(); iterator.hasNext();) { - IInstallableUnit formerRoot = iterator.next(); - IQueryResult<IInstallableUnit> update = updateFinderPlan.getAdditions().query(new UpdateQuery(formerRoot), null); - if (!update.isEmpty()) - finalChangeRequest.addAll(update.toUnmodifiableSet()); - } - - //Take into account the additions for optionals - for (Iterator<IInstallableUnit> iterator = optionalRoots.iterator(); iterator.hasNext();) { - IInstallableUnit formerRoot = iterator.next(); - IQueryResult<IInstallableUnit> update = updateFinderPlan.getAdditions().query(new UpdateQuery(formerRoot), null); - if (!update.isEmpty()) { - for (Iterator<IInstallableUnit> it = update.iterator(); it.hasNext();) { - IInstallableUnit updatedOptionalIU = it.next(); - finalChangeRequest.add(updatedOptionalIU); - finalChangeRequest.setInstallableUnitInclusionRules(updatedOptionalIU, ProfileInclusionRules.createOptionalInclusionRule(updatedOptionalIU)); - } - } - } - request = (ProfileChangeRequest) finalChangeRequest; - } - - @Override - protected String getResolveJobName() { - return Messages.RelaxedUpdateOperation_ResolveJobName; - } - - @Override - protected String getProvisioningJobName() { - return Messages.RelaxedUpdateOperation_UpdateJobName; - } - -}
\ No newline at end of file diff --git a/bundles/org.eclipse.equinox.p2.operations/src/org/eclipse/equinox/p2/operations/RemediationOperation.java b/bundles/org.eclipse.equinox.p2.operations/src/org/eclipse/equinox/p2/operations/RemediationOperation.java new file mode 100644 index 000000000..db3d6084f --- /dev/null +++ b/bundles/org.eclipse.equinox.p2.operations/src/org/eclipse/equinox/p2/operations/RemediationOperation.java @@ -0,0 +1,218 @@ +/******************************************************************************* + * Copyright (c) 2013 Red Hat, Inc. 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: + * Red Hat, Inc. - initial API and implementation + *******************************************************************************/ +package org.eclipse.equinox.p2.operations; + +import java.util.*; +import org.eclipse.core.runtime.*; +import org.eclipse.equinox.internal.p2.director.ProfileChangeRequest; +import org.eclipse.equinox.internal.p2.operations.Messages; +import org.eclipse.equinox.internal.p2.operations.RequestFlexer; +import org.eclipse.equinox.p2.engine.IProfile; +import org.eclipse.equinox.p2.planner.IPlanner; +import org.eclipse.equinox.p2.planner.IProfileChangeRequest; + +//TODO Javadoc +public class RemediationOperation extends ProfileChangeOperation { + + private static int ZERO_WEIGHT = 0; + private static int LOW_WEIGHT = 1; + private static int MEDIUM_WEIGHT = 2; + private static int HIGH_WEIGHT = 3; + private List<Remedy> remedies; + private Remedy bestSolutionChangingTheRequest; + private Remedy bestSolutionChangingWhatIsInstalled; + private Remedy userSelectedRemedy; + private IProfileChangeRequest originalRequest; + private boolean isCheckForUpdates; + + public boolean isCheckForUpdates() { + return isCheckForUpdates; + } + + public RemediationOperation(ProvisioningSession session, IProfileChangeRequest iProfileChangeRequest) { + this(session, iProfileChangeRequest, null); + + } + + public RemediationOperation(ProvisioningSession session, IProfileChangeRequest originalRequest, List<RemedyConfig> configuration) { + this(session, originalRequest, false); + } + + public RemediationOperation(ProvisioningSession session, IProfileChangeRequest originalRequest, boolean isCheckForUpdates) { + super(session); + this.originalRequest = originalRequest; + remedies = new ArrayList<Remedy>(); + this.isCheckForUpdates = isCheckForUpdates; + } + + public Remedy bestSolutionChangingTheRequest() { + return bestSolutionChangingTheRequest; + } + + public Remedy bestSolutionChangingWhatIsInstalled() { + return bestSolutionChangingWhatIsInstalled; + } + + public List<Remedy> getRemedies() { + return remedies; + } + + @Override + protected void computeProfileChangeRequest(MultiStatus status, IProgressMonitor monitor) { + if (userSelectedRemedy != null) { + request = userSelectedRemedy.getRequest(); + return; + } + + try { + if (isCheckForUpdates) + status.add(computeCheckForUpdates(monitor)); + else + status.add(computeAllRemediations(monitor)); + } catch (OperationCanceledException e) { + status.add(Status.CANCEL_STATUS); + } + if (!isCheckForUpdates) + determineBestSolutions(); + } + + private IStatus computeCheckForUpdates(IProgressMonitor monitor) { + RemedyConfig config = new RemedyConfig(); + config.allowDifferentVersion = true; + config.allowInstalledRemoval = false; + config.allowInstalledUpdate = true; + config.allowPartialInstall = false; + Remedy remedy = computeRemedy(config, monitor); + if (remedy != null) { + remedies.add(remedy); + } + return Status.OK_STATUS; + } + + private IStatus computeAllRemediations(IProgressMonitor monitor) { + RemedyConfig[] remedyConfigs = RemedyConfig.getAllRemdyConfigs(); + SubMonitor sub = SubMonitor.convert(monitor, remedyConfigs.length); + sub.setTaskName("Looking for alternate solutions"); + List<Remedy> tmpRemedies = new ArrayList<Remedy>(remedyConfigs.length); + try { + for (int i = 0; i < remedyConfigs.length; i++) { + sub.subTask(i + " out of " + remedyConfigs.length); //$NON-NLS-1$ + if (sub.isCanceled()) + return Status.CANCEL_STATUS; + Remedy remedy = computeRemedy(remedyConfigs[i], sub.newChild(1, SubMonitor.SUPPRESS_ALL_LABELS)); + if (remedy != null) { + tmpRemedies.add(remedy); + } + } + } finally { + sub.done(); + } + remedies = tmpRemedies; + return Status.OK_STATUS; + } + + private void determineBestSolutions() { + int beingInstalledWeight = 0; + int installationWeight = 0; + for (Iterator<Remedy> iterator = remedies.iterator(); iterator.hasNext();) { + Remedy remedy = iterator.next(); + if (remedy.getRequest() != null) { + if (remedy.getBeingInstalledRelaxedWeight() > beingInstalledWeight && remedy.getInstallationRelaxedWeight() == 0) { + bestSolutionChangingTheRequest = remedy; + beingInstalledWeight = remedy.getBeingInstalledRelaxedWeight(); + continue; + } + if (remedy.getInstallationRelaxedWeight() > installationWeight && remedy.getBeingInstalledRelaxedWeight() == 0) { + bestSolutionChangingWhatIsInstalled = remedy; + installationWeight = remedy.getInstallationRelaxedWeight(); + continue; + } + request = remedy.getRequest(); + } + } + if (bestSolutionChangingTheRequest != null) + request = bestSolutionChangingTheRequest.getRequest(); + else if (bestSolutionChangingWhatIsInstalled != null) + request = bestSolutionChangingWhatIsInstalled.getRequest(); + } + + private Remedy computeRemedy(RemedyConfig configuration, IProgressMonitor monitor) { + Remedy remedy = new Remedy(); + remedy.setConfig(configuration); + IPlanner planner = session.getPlanner(); + IProfile profile = session.getProfileRegistry().getProfile(profileId); + //request = (ProfileChangeRequest) originalRequest.clone(); + RequestFlexer av = new RequestFlexer(planner); + av.setAllowDifferentVersion(configuration.allowDifferentVersion); + av.setAllowInstalledElementChange(configuration.allowInstalledUpdate); + av.setAllowInstalledElementRemoval(configuration.allowInstalledRemoval); + av.setAllowPartialInstall(configuration.allowPartialInstall); + av.setProvisioningContext(getProvisioningContext()); + remedy.setRequest((ProfileChangeRequest) av.getChangeRequest(originalRequest, profile, monitor)); + if (remedy.getRequest() == null) + return null; + + if (configuration.allowInstalledUpdate && !configuration.allowInstalledRemoval) { + remedy.setInstallationRelaxedWeight(HIGH_WEIGHT); + } else if (!configuration.allowInstalledUpdate && configuration.allowInstalledRemoval) { + remedy.setInstallationRelaxedWeight(MEDIUM_WEIGHT); + } else if (configuration.allowInstalledUpdate && configuration.allowInstalledRemoval) { + remedy.setInstallationRelaxedWeight(LOW_WEIGHT); + } else + remedy.setInstallationRelaxedWeight(ZERO_WEIGHT); + + if (configuration.allowDifferentVersion && !configuration.allowPartialInstall) { + remedy.setBeingInstalledRelaxedWeight(HIGH_WEIGHT); + } else if (!configuration.allowDifferentVersion && configuration.allowPartialInstall) { + remedy.setBeingInstalledRelaxedWeight(MEDIUM_WEIGHT); + } else if (configuration.allowDifferentVersion && configuration.allowPartialInstall) { + remedy.setBeingInstalledRelaxedWeight(LOW_WEIGHT); + } else { + remedy.setBeingInstalledRelaxedWeight(ZERO_WEIGHT); + } + return remedy; + } + + @Override + protected String getResolveJobName() { + return Messages.RemediationOperation_ResolveJobName; + } + + @Override + protected String getProvisioningJobName() { + return Messages.RemediationOperation_RemediationJobName; + } + + public void setSelectedRemedy(Remedy remedy) { + this.userSelectedRemedy = remedy; + } + + public ProvisioningJob getProvisioningJob(IProgressMonitor monitor) { + IStatus status = getResolutionResult(); + // planner.resolve(); + if (status.getSeverity() != IStatus.CANCEL && status.getSeverity() != IStatus.ERROR) { + if (job.getProvisioningPlan() != null) { + ProfileModificationJob pJob = new ProfileModificationJob(getProvisioningJobName(), session, profileId, job.getProvisioningPlan(), job.getActualProvisioningContext()); + pJob.setAdditionalProgressMonitor(monitor); + return pJob; + } + } + return null; + } + + public boolean hasRemedies() { + return (remedies != null && remedies.size() > 0); + } + + public ProfileChangeRequest getOriginalRequest() { + return (ProfileChangeRequest) originalRequest; + } +} diff --git a/bundles/org.eclipse.equinox.p2.operations/src/org/eclipse/equinox/p2/operations/Remedy.java b/bundles/org.eclipse.equinox.p2.operations/src/org/eclipse/equinox/p2/operations/Remedy.java new file mode 100644 index 000000000..15f2a065c --- /dev/null +++ b/bundles/org.eclipse.equinox.p2.operations/src/org/eclipse/equinox/p2/operations/Remedy.java @@ -0,0 +1,55 @@ +/******************************************************************************* + * Copyright (c) 2013 Red Hat, Inc. 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: + * Red Hat, Inc. - initial API and implementation + *******************************************************************************/ +package org.eclipse.equinox.p2.operations; + +import org.eclipse.equinox.internal.p2.director.ProfileChangeRequest; + +//TODO Javadoc +public class Remedy { + + private RemedyConfig config; + private ProfileChangeRequest request; + private int beingInstalledRelaxedWeight; + private int installationRelaxedWeight; + + public RemedyConfig getConfig() { + return config; + } + + public void setConfig(RemedyConfig config) { + this.config = config; + } + + public ProfileChangeRequest getRequest() { + return request; + } + + public void setRequest(ProfileChangeRequest request) { + this.request = request; + } + + public int getBeingInstalledRelaxedWeight() { + return beingInstalledRelaxedWeight; + } + + public void setBeingInstalledRelaxedWeight(int beingInstalledRelaxedWeight) { + this.beingInstalledRelaxedWeight = beingInstalledRelaxedWeight; + } + + public int getInstallationRelaxedWeight() { + return installationRelaxedWeight; + } + + public void setInstallationRelaxedWeight(int installationRelaxedWeight) { + this.installationRelaxedWeight = installationRelaxedWeight; + } + +} diff --git a/bundles/org.eclipse.equinox.p2.operations/src/org/eclipse/equinox/p2/operations/RemedyConfig.java b/bundles/org.eclipse.equinox.p2.operations/src/org/eclipse/equinox/p2/operations/RemedyConfig.java new file mode 100644 index 000000000..d19ca65ca --- /dev/null +++ b/bundles/org.eclipse.equinox.p2.operations/src/org/eclipse/equinox/p2/operations/RemedyConfig.java @@ -0,0 +1,57 @@ +/******************************************************************************* + * Copyright (c) 2013 Red Hat, Inc. 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: + * Red Hat, Inc. - initial API and implementation + *******************************************************************************/ +package org.eclipse.equinox.p2.operations; + +import java.util.ArrayList; +import java.util.Collection; + +//TODO Javadoc +public class RemedyConfig { + + public boolean allowInstalledUpdate = false; + public boolean allowInstalledRemoval = false; + public boolean allowDifferentVersion = false; + public boolean allowPartialInstall = false; + + public RemedyConfig() { + + } + + public static RemedyConfig[] getAllRemdyConfigs() { + Collection<RemedyConfig> remedyConfigs = new ArrayList<RemedyConfig>(); + int allMasks = (1 << 4); + for (int i = 1; i < allMasks; i++) { + RemedyConfig remedyConfig = new RemedyConfig(); + for (int j = 0; j < 4; j++) { + if ((i & (1 << j)) > 0) { + switch (j) { + case 0 : + remedyConfig.allowPartialInstall = true; + break; + case 1 : + remedyConfig.allowDifferentVersion = true; + break; + case 2 : + remedyConfig.allowInstalledUpdate = true; + break; + case 3 : + remedyConfig.allowInstalledRemoval = true; + break; + } + } + + } + remedyConfigs.add(remedyConfig); + } + RemedyConfig[] test = remedyConfigs.toArray(new RemedyConfig[remedyConfigs.size()]); + return test; + } +} diff --git a/bundles/org.eclipse.equinox.p2.tests.ui/src/org/eclipse/equinox/p2/tests/ui/AbstractProvisioningUITest.java b/bundles/org.eclipse.equinox.p2.tests.ui/src/org/eclipse/equinox/p2/tests/ui/AbstractProvisioningUITest.java index 3087875bb..3ee1b7d86 100644 --- a/bundles/org.eclipse.equinox.p2.tests.ui/src/org/eclipse/equinox/p2/tests/ui/AbstractProvisioningUITest.java +++ b/bundles/org.eclipse.equinox.p2.tests.ui/src/org/eclipse/equinox/p2/tests/ui/AbstractProvisioningUITest.java @@ -58,8 +58,8 @@ public abstract class AbstractProvisioningUITest extends AbstractProvisioningTes protected IInstallableUnit upgrade; protected IInstallableUnit uninstalled; protected IInstallableUnit category; - private ProvisioningUI ui; - private ServiceRegistration regLicenseManager; + protected ProvisioningUI ui; + protected ServiceRegistration regLicenseManager; protected void setUp() throws Exception { super.setUp(); diff --git a/bundles/org.eclipse.equinox.p2.tests.ui/src/org/eclipse/equinox/p2/tests/ui/dialogs/RemediationTest.java b/bundles/org.eclipse.equinox.p2.tests.ui/src/org/eclipse/equinox/p2/tests/ui/dialogs/RemediationTest.java new file mode 100644 index 000000000..1b8905e80 --- /dev/null +++ b/bundles/org.eclipse.equinox.p2.tests.ui/src/org/eclipse/equinox/p2/tests/ui/dialogs/RemediationTest.java @@ -0,0 +1,157 @@ +/******************************************************************************* + * Copyright (c) 2008, 2013 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 + * Ericsson AB (Hamdan Msheik) - Bypass install license wizard page via plugin_customization + *******************************************************************************/ +package org.eclipse.equinox.p2.tests.ui.dialogs; + +import java.io.File; +import java.net.URI; +import java.util.Dictionary; +import java.util.Hashtable; +import org.eclipse.equinox.internal.p2.metadata.InstallableUnit; +import org.eclipse.equinox.internal.p2.ui.ProvUI; +import org.eclipse.equinox.internal.p2.ui.dialogs.*; +import org.eclipse.equinox.internal.p2.ui.model.ProfileElement; +import org.eclipse.equinox.internal.p2.ui.sdk.SimpleLicenseManager; +import org.eclipse.equinox.p2.metadata.*; +import org.eclipse.equinox.p2.operations.Update; +import org.eclipse.equinox.p2.operations.UpdateOperation; +import org.eclipse.equinox.p2.repository.artifact.IArtifactRepositoryManager; +import org.eclipse.equinox.p2.repository.metadata.IMetadataRepositoryManager; +import org.eclipse.equinox.p2.tests.*; +import org.eclipse.equinox.p2.ui.*; +import org.eclipse.jface.wizard.WizardDialog; +import org.osgi.framework.Constants; + +/** + * Tests for the install wizard + */ +public class RemediationTest extends WizardTest { + + // private static final String AVAILABLE_SOFTWARE_PAGE = "AvailableSoftwarePage"; + // private static final String MAIN_IU = "MainIU"; + public static final int INSTALLATION_SUCCEEDED = 1; + public static final int INSTALLATION_REMEDIATED = 2; + public static final int INSTALLATION_FAILED = 3; + public static final int CHECK_FOR_UPDATES = 4; + public static final int UPDATE_ONE_IU = 5; + private String name = "PROFILE_"; + + @IUDescription(content = "package: jboss \n" + "singleton: true\n" + "version: 6 \n" + "depends: m2e = 2") + public IInstallableUnit jboss60; + + @IUDescription(content = "package: jboss \n" + "singleton: true\n" + "version: 5 \n" + "depends: m2e = 1") + public IInstallableUnit jboss55; + + @IUDescription(content = "package: m2e \n" + "singleton: true\n" + "version: 1 \n") + public IInstallableUnit m2e11; + + @IUDescription(content = "package: m2e \n" + "singleton: true\n" + "version: 2 \n") + public IInstallableUnit m2e12; + + IInstallableUnit toInstall; + + public void setUp() throws Exception { + } + + public void visibleSetup(int type) throws Exception { + //Clearout repositories + name = "PROFILE_" + type; + URI[] repos = getMetadataRepositoryManager().getKnownRepositories(IMetadataRepositoryManager.REPOSITORIES_ALL); + for (URI uri : repos) { + getMetadataRepositoryManager().removeRepository(uri); + } + + // create test profile + profile = createProfile(name); + + // copy of provisioning UI that uses a different profile + ui = ProvisioningUI.getDefaultUI(); + ui = new ProvisioningUI(ui.getSession(), name, ui.getPolicy()); + ui.getOperationRunner().suppressRestart(true); + ui.getPolicy().setRepositoriesVisible(false); + + // register alternate services + SimpleLicenseManager manager = new SimpleLicenseManager(name); + Dictionary<String, Object> properties = new Hashtable<String, Object>(5); + properties.put(Constants.SERVICE_RANKING, new Integer(1)); + regLicenseManager = TestActivator.getContext().registerService(LicenseManager.class.getName(), manager, properties); + profileElement = new ProfileElement(null, name); + IULoader.loadIUs(this); + ILicense[] licenses = new ILicense[] {MetadataFactory.createLicense(URI.create("http://eclipse.org"), "license text"), MetadataFactory.createLicense(URI.create("http://apache.org"), "license text2")}; + ((InstallableUnit) jboss60).setLicenses(licenses); + switch (type) { + case INSTALLATION_SUCCEEDED : + createTestMetdataRepository(new IInstallableUnit[] {jboss60, m2e11, m2e12}); + install(m2e12, true, false); + toInstall = jboss60; + break; + case INSTALLATION_REMEDIATED : + createTestMetdataRepository(new IInstallableUnit[] {jboss60, m2e11, m2e12}); + install(m2e11, true, false); + toInstall = jboss60; + break; + case INSTALLATION_FAILED : + createTestMetdataRepository(new IInstallableUnit[] {jboss60, m2e11}); + install(m2e11, true, false); + toInstall = jboss60; + break; + case CHECK_FOR_UPDATES : + createTestMetdataRepository(new IInstallableUnit[] {jboss60, m2e11, m2e12}); + install(m2e11, true, false); + break; + case UPDATE_ONE_IU : + createTestMetdataRepository(new IInstallableUnit[] {jboss55, jboss60, m2e11, m2e12}); + install(jboss55, true, false); + install(m2e11, true, false); + break; + default : + createTestMetdataRepository(new IInstallableUnit[] {jboss60, m2e11}); + install(m2e11, true, false); + + } + + metaManager = (IMetadataRepositoryManager) getAgent().getService(IMetadataRepositoryManager.SERVICE_NAME); + artifactManager = (IArtifactRepositoryManager) getAgent().getService(IArtifactRepositoryManager.SERVICE_NAME); + File site = new File(TestActivator.getTestDataFolder().toString(), TEST_REPO_PATH); + testRepoLocation = site.toURI(); + metaManager.addRepository(testRepoLocation); + artifactManager.addRepository(testRepoLocation); + } + + public void testInstallWizard() throws Exception { + LoadMetadataRepositoryJob job = new LoadMetadataRepositoryJob(getProvisioningUI()); + getPolicy().setGroupByCategory(false); + getPolicy().setShowLatestVersionsOnly(false); + job.runModal(getMonitor()); + InstallWizard wizard = new InstallWizard(getProvisioningUI(), null, null, job); + WizardDialog dialog = new ProvisioningWizardDialog(ProvUI.getDefaultParentShell(), wizard); + + dialog.create(); + dialog.setBlockOnOpen(false); + dialog.open(); + + } + + public void testUpdateWizard() throws Exception { + LoadMetadataRepositoryJob job = new LoadMetadataRepositoryJob(getProvisioningUI()); + getPolicy().setGroupByCategory(false); + getPolicy().setShowLatestVersionsOnly(false); + job.runModal(getMonitor()); + UpdateOperation op = new UpdateOperation(getSession()); + op.setProfileId(name); + op.resolveModal(getMonitor()); + UpdateWizard wizard = new UpdateWizard(getProvisioningUI(), op, new Update[] {}, job); + WizardDialog dialog = new ProvisioningWizardDialog(ProvUI.getDefaultParentShell(), wizard); + dialog.create(); + dialog.setBlockOnOpen(false); + dialog.open(); + } +} diff --git a/bundles/org.eclipse.equinox.p2.tests/src/org/eclipse/equinox/p2/tests/AbstractProvisioningTest.java b/bundles/org.eclipse.equinox.p2.tests/src/org/eclipse/equinox/p2/tests/AbstractProvisioningTest.java index 118d5cf2e..7a64aeb17 100644 --- a/bundles/org.eclipse.equinox.p2.tests/src/org/eclipse/equinox/p2/tests/AbstractProvisioningTest.java +++ b/bundles/org.eclipse.equinox.p2.tests/src/org/eclipse/equinox/p2/tests/AbstractProvisioningTest.java @@ -1350,6 +1350,10 @@ public abstract class AbstractProvisioningTest extends TestCase { assertNotContains(null, result, value); } + // public void assertContains(IQueryable<IInstallableUnit> queryable, IInstallableUnit iu) { + // assertFalse("Missing IU " + iu.toString(), queryable.query(QueryUtil.ALL_UNITS, null).isEmpty()); + // } + public static void assertContains(String message, IQueryResult result, Object value) { Iterator itor = result.iterator(); while (itor.hasNext()) @@ -1715,4 +1719,9 @@ public abstract class AbstractProvisioningTest extends TestCase { //ignore } } + + public void assertResolve(IProfileChangeRequest request, IPlanner planner) { + IProvisioningPlan plan = planner.getProvisioningPlan(request, null, null); + assertOK(plan.getStatus()); + } } diff --git a/bundles/org.eclipse.equinox.p2.tests/src/org/eclipse/equinox/p2/tests/ReducedCUDFParser.java b/bundles/org.eclipse.equinox.p2.tests/src/org/eclipse/equinox/p2/tests/ReducedCUDFParser.java index 1f2917fb0..22b1262ce 100644 --- a/bundles/org.eclipse.equinox.p2.tests/src/org/eclipse/equinox/p2/tests/ReducedCUDFParser.java +++ b/bundles/org.eclipse.equinox.p2.tests/src/org/eclipse/equinox/p2/tests/ReducedCUDFParser.java @@ -218,6 +218,7 @@ public class ReducedCUDFParser { // if (currentIU.isInstalled()) { // keepRequests.addAll(currentKeepRequests); // } + currentIU.setProperty(InstallableUnitDescription.PROP_TYPE_GROUP, "true"); allIUs.add(MetadataFactory.createInstallableUnit(currentIU)); // reset to be ready for the next stanza currentIU = null; diff --git a/bundles/org.eclipse.equinox.p2.tests/src/org/eclipse/equinox/p2/tests/planner/AllAnyVersionTests.java b/bundles/org.eclipse.equinox.p2.tests/src/org/eclipse/equinox/p2/tests/planner/AllAnyVersionTests.java new file mode 100644 index 000000000..cf742e742 --- /dev/null +++ b/bundles/org.eclipse.equinox.p2.tests/src/org/eclipse/equinox/p2/tests/planner/AllAnyVersionTests.java @@ -0,0 +1,27 @@ +/******************************************************************************* + * Copyright (c) 2013 Red Hat, Inc. 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: + * Red Hat, Inc. - initial API and implementation + *******************************************************************************/ +package org.eclipse.equinox.p2.tests.planner; + +import junit.framework.*; + +public class AllAnyVersionTests extends TestCase { + + public static Test suite() { + TestSuite suite = new TestSuite(AllAnyVersionTests.class.getName()); + suite.addTestSuite(TestRequestFlexerIUProperties.class); + suite.addTestSuite(TestRequestFlexerOneInstalledOneBeingInstalled.class); + suite.addTestSuite(TestRequestFlexerOneInstalledReplacingIt.class); + suite.addTestSuite(TestRequestFlexerOneInstalledTwoBeingInstalled.class); + suite.addTestSuite(TestRequestFlexerRequestWithOptionalInstall.class); + suite.addTestSuite(TestRequestFlexerRequestWithRemoval.class); + return suite; + } +} diff --git a/bundles/org.eclipse.equinox.p2.tests/src/org/eclipse/equinox/p2/tests/planner/Bug306279c.java b/bundles/org.eclipse.equinox.p2.tests/src/org/eclipse/equinox/p2/tests/planner/Bug306279c.java index 20d5b75ea..6adb2ad24 100644 --- a/bundles/org.eclipse.equinox.p2.tests/src/org/eclipse/equinox/p2/tests/planner/Bug306279c.java +++ b/bundles/org.eclipse.equinox.p2.tests/src/org/eclipse/equinox/p2/tests/planner/Bug306279c.java @@ -27,7 +27,7 @@ public class Bug306279c extends AbstractProvisioningTest { IInstallableUnit x, y, a1, a2, b; x = createIU("X"); - //Y -ng-> X + //Y -ng & op-> X IRequirement[] reqY = new IRequirement[1]; reqY[0] = MetadataFactory.createRequirement(IInstallableUnit.NAMESPACE_IU_ID, "X", VersionRange.emptyRange, null, true, false, false); y = createIU("Y", Version.create("1.0.0"), reqY); diff --git a/bundles/org.eclipse.equinox.p2.tests/src/org/eclipse/equinox/p2/tests/planner/JBoss.java b/bundles/org.eclipse.equinox.p2.tests/src/org/eclipse/equinox/p2/tests/planner/JBoss.java new file mode 100644 index 000000000..5b48b2f73 --- /dev/null +++ b/bundles/org.eclipse.equinox.p2.tests/src/org/eclipse/equinox/p2/tests/planner/JBoss.java @@ -0,0 +1,56 @@ +/******************************************************************************* + * Copyright (c) 2011 Sonatype, Inc. 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: + * Sonatype, Inc. - initial implementation and ideas + ******************************************************************************/ +package org.eclipse.equinox.p2.tests.planner; + +import org.eclipse.equinox.internal.p2.director.ProfileChangeRequest; +import org.eclipse.equinox.p2.engine.*; +import org.eclipse.equinox.p2.metadata.IInstallableUnit; +import org.eclipse.equinox.p2.planner.IPlanner; +import org.eclipse.equinox.p2.planner.IProfileChangeRequest; +import org.eclipse.equinox.p2.tests.*; + +public class JBoss extends AbstractProvisioningTest { + @IUDescription(content = "package: jboss \n" + "singleton: true\n" + "version: 6 \n" + "depends: m2e = 12") + public IInstallableUnit jboss; + + @IUDescription(content = "package: m2e \n" + "singleton: true\n" + "version: 11 \n") + public IInstallableUnit m2e11; + + @IUDescription(content = "package: m2e \n" + "singleton: true\n" + "version: 12 \n") + public IInstallableUnit m2e12; + + IProfile profile = createProfile("TestProfile." + getClass().getSimpleName()); + + private IPlanner planner; + + private IEngine engine; + + @Override + protected void setUp() throws Exception { + super.setUp(); + IULoader.loadIUs(this); + createTestMetdataRepository(new IInstallableUnit[] {jboss, m2e11, m2e12}); + planner = createPlanner(); + engine = createEngine(); + assertOK(install(profile, new IInstallableUnit[] {m2e11}, true, planner, engine)); + } + + public void testInstallJBoss() { + ProfileChangeRequest installJBoss = new ProfileChangeRequest(profile); + installJBoss.add(jboss); + assertNotOK(install(installJBoss, planner, engine)); + + IProfileChangeRequest res = new LuckyHelper().computeProfileChangeRequest(profile, planner, installJBoss, new ProvisioningContext(getAgent()), getMonitor()); + assertTrue(res.getAdditions().contains(m2e12)); + assertTrue(res.getRemovals().contains(m2e11)); + + } + +} diff --git a/bundles/org.eclipse.equinox.p2.tests/src/org/eclipse/equinox/p2/tests/planner/TestRequestFlexerIUProperties.java b/bundles/org.eclipse.equinox.p2.tests/src/org/eclipse/equinox/p2/tests/planner/TestRequestFlexerIUProperties.java new file mode 100644 index 000000000..598cc39d1 --- /dev/null +++ b/bundles/org.eclipse.equinox.p2.tests/src/org/eclipse/equinox/p2/tests/planner/TestRequestFlexerIUProperties.java @@ -0,0 +1,198 @@ +/******************************************************************************* + * Copyright (c) 2013 Red Hat, Inc. 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: + * Red Hat, Inc. - initial API and implementation + *******************************************************************************/ +package org.eclipse.equinox.p2.tests.planner; + +import java.util.Map; +import org.eclipse.core.runtime.NullProgressMonitor; +import org.eclipse.equinox.internal.p2.director.ProfileChangeRequest; +import org.eclipse.equinox.internal.p2.operations.RequestFlexer; +import org.eclipse.equinox.p2.engine.*; +import org.eclipse.equinox.p2.metadata.IInstallableUnit; +import org.eclipse.equinox.p2.planner.*; +import org.eclipse.equinox.p2.tests.*; + +public class TestRequestFlexerIUProperties extends AbstractProvisioningTest { + final String INCLUSION_RULES = "org.eclipse.equinox.p2.internal.inclusion.rules"; //$NON-NLS-1$ + final String INCLUSION_OPTIONAL = "OPTIONAL"; //$NON-NLS-1$ + final String INCLUSION_STRICT = "STRICT"; //$NON-NLS-1$ + + @IUDescription(content = "package: sdk \n" + "singleton: true\n" + "version: 1 \n" + "depends: platform = 1") + public IInstallableUnit sdk1; + + @IUDescription(content = "package: platform \n" + "singleton: true\n" + "version: 1 \n") + public IInstallableUnit platform1; + + @IUDescription(content = "package: sdk \n" + "singleton: true\n" + "version: 2 \n" + "depends: platform = 2") + public IInstallableUnit sdk2; + + @IUDescription(content = "package: platform \n" + "singleton: true\n" + "version: 2 \n") + public IInstallableUnit platform2; + + @IUDescription(content = "package: egit \n" + "singleton: true\n" + "version: 1 \n" + "depends: platform = 1") + public IInstallableUnit egit1; + + @IUDescription(content = "package: egit \n" + "singleton: true\n" + "version: 2 \n" + "depends: platform = 2") + public IInstallableUnit egit2; + + @IUDescription(content = "package: egit \n" + "singleton: true\n" + "version: 3 \n" + "depends: platform = 3") + public IInstallableUnit egit3; + + IProfile profile; + + private IPlanner planner; + + private IEngine engine; + + private ProvisioningContext context; + + @Override + protected void setUp() throws Exception { + super.setUp(); + IULoader.loadIUs(this); + createTestMetdataRepository(new IInstallableUnit[] {sdk1, platform1}); + planner = createPlanner(); + engine = createEngine(); + context = new ProvisioningContext(getAgent()); + } + + public void testWithChanginRootFromOptionalToStrict() { + profile = createProfile("TestProfile." + getName()); + assertOK(installAsRoots(profile, new IInstallableUnit[] {sdk1}, false, planner, engine)); + createTestMetdataRepository(new IInstallableUnit[] {sdk1, platform1, sdk2, platform2, egit1, egit2}); + + IProfileChangeRequest originalRequest = planner.createChangeRequest(profile); + originalRequest.add(egit2); + originalRequest.setInstallableUnitInclusionRules(egit2, ProfileInclusionRules.createStrictInclusionRule(egit2)); + originalRequest.setInstallableUnitInclusionRules(sdk1, ProfileInclusionRules.createStrictInclusionRule(sdk1)); + assertNotOK(planner.getProvisioningPlan(originalRequest, null, null).getStatus()); + + { + //sdk1 is requested to be marked strict, so since sdk1 and egit can't be installed together and we don't have any flexibility, we get null + RequestFlexer av = new RequestFlexer(planner); + av.setProvisioningContext(context); + IProfileChangeRequest realRequest = av.getChangeRequest(originalRequest, profile, new NullProgressMonitor()); + assertNull(realRequest); + } + + { + //Verify that it is possible to install egit2 because the sdk1 is optional + RequestFlexer av = new RequestFlexer(planner); + av.setAllowInstalledElementChange(true); + av.setProvisioningContext(context); + IProfileChangeRequest realRequest = av.getChangeRequest(originalRequest, profile, new NullProgressMonitor()); + assertTrue(realRequest.getAdditions().contains(egit2)); + assertTrue(realRequest.getAdditions().contains(sdk2)); + assertTrue(realRequest.getRemovals().contains(sdk1)); + assertEquals(2, realRequest.getAdditions().size()); + assertEquals(1, realRequest.getRemovals().size()); + } + } + + public void testWithChanginRootFromOptionalToStrictByRemovingIUProperty() { + profile = createProfile("TestProfile." + getName()); + assertOK(installAsRoots(profile, new IInstallableUnit[] {sdk1}, false, planner, engine)); + createTestMetdataRepository(new IInstallableUnit[] {sdk1, platform1, sdk2, platform2, egit1, egit2}); + + IProfileChangeRequest originalRequest = planner.createChangeRequest(profile); + originalRequest.add(egit2); + originalRequest.setInstallableUnitInclusionRules(egit2, ProfileInclusionRules.createStrictInclusionRule(egit2)); + originalRequest.removeInstallableUnitInclusionRules(sdk1); + assertNotOK(planner.getProvisioningPlan(originalRequest, null, null).getStatus()); + + { + //Verify that it is possible to install egit2 because the sdk1 is optional + RequestFlexer av = new RequestFlexer(planner); + av.setAllowInstalledElementChange(true); + av.setProvisioningContext(context); + IProfileChangeRequest realRequest = av.getChangeRequest(originalRequest, profile, new NullProgressMonitor()); + assertTrue(realRequest.getAdditions().contains(egit2)); + assertTrue(realRequest.getAdditions().contains(sdk2)); + assertTrue(realRequest.getRemovals().contains(sdk1)); + assertEquals(2, realRequest.getAdditions().size()); + assertEquals(1, realRequest.getRemovals().size()); + } + } + + public void testWithChanginRootFromStrictToOptional() { + profile = createProfile("TestProfile." + getName()); + assertOK(installAsRoots(profile, new IInstallableUnit[] {sdk1}, true, planner, engine)); + createTestMetdataRepository(new IInstallableUnit[] {sdk1, platform1, sdk2, platform2, egit1, egit2}); + + IProfileChangeRequest originalRequest = planner.createChangeRequest(profile); + originalRequest.add(egit2); + originalRequest.setInstallableUnitInclusionRules(egit2, ProfileInclusionRules.createStrictInclusionRule(egit2)); + originalRequest.setInstallableUnitInclusionRules(sdk1, ProfileInclusionRules.createOptionalInclusionRule(sdk1)); + assertOK(planner.getProvisioningPlan(originalRequest, null, null).getStatus()); + + { + //Verify that it is possible to install egit2 because the sdk1 inclusion is change to optional + RequestFlexer av = new RequestFlexer(planner); + av.setProvisioningContext(context); + IProfileChangeRequest realRequest = av.getChangeRequest(originalRequest, profile, new NullProgressMonitor()); + assertTrue(realRequest.getAdditions().contains(egit2)); + assertTrue(realRequest.getRemovals().contains(sdk1)); + assertEquals(1, realRequest.getAdditions().size()); + assertEquals(1, realRequest.getRemovals().size()); + assertResolve(realRequest, planner); + } + + { + //Verify that it is possible to install egit2 because the sdk1 inclusion is change to optional + RequestFlexer av = new RequestFlexer(planner); + av.setAllowInstalledElementChange(true); + av.setProvisioningContext(context); + IProfileChangeRequest realRequest = av.getChangeRequest(originalRequest, profile, new NullProgressMonitor()); + assertTrue(realRequest.getAdditions().contains(egit2)); + assertTrue(realRequest.getAdditions().contains(sdk2)); + assertTrue(realRequest.getRemovals().contains(sdk1)); + assertEquals(2, realRequest.getAdditions().size()); + assertEquals(1, realRequest.getRemovals().size()); + assertResolve(realRequest, planner); + assertTrue(isOptionallyBeingInstalled(sdk2, realRequest)); + } + + } + + public void testRandomIUProperty() { + profile = createProfile("TestProfile." + getName()); + assertOK(installAsRoots(profile, new IInstallableUnit[] {sdk1}, true, planner, engine)); + createTestMetdataRepository(new IInstallableUnit[] {sdk1, platform1, sdk2, platform2, egit1, egit2}); + + IProfileChangeRequest originalRequest = planner.createChangeRequest(profile); + originalRequest.add(egit3); + originalRequest.setInstallableUnitInclusionRules(egit3, ProfileInclusionRules.createStrictInclusionRule(egit3)); + originalRequest.setInstallableUnitProfileProperty(egit3, "MYKEY", "MYVALUE"); + assertNotOK(planner.getProvisioningPlan(originalRequest, null, null).getStatus()); + + //Verify that it is possible to install egit2 because the sdk1 inclusion is change to optional + RequestFlexer av = new RequestFlexer(planner); + av.setAllowDifferentVersion(true); + av.setProvisioningContext(context); + IProfileChangeRequest realRequest = av.getChangeRequest(originalRequest, profile, new NullProgressMonitor()); + assertTrue(realRequest.getAdditions().contains(egit1)); + assertEquals(1, realRequest.getAdditions().size()); + assertResolve(realRequest, planner); + assertKeyValue(egit1, "MYKEY", "MYVALUE", realRequest); + } + + private void assertKeyValue(IInstallableUnit iu, String key, String value, IProfileChangeRequest request) { + Map<String, String> match = ((ProfileChangeRequest) request).getInstallableUnitProfilePropertiesToAdd().get(iu); + assertNotNull(match); + assertEquals(value, match.get(key)); + } + + private boolean isOptionallyBeingInstalled(IInstallableUnit iu, IProfileChangeRequest request) { + Map<String, String> match = ((ProfileChangeRequest) request).getInstallableUnitProfilePropertiesToAdd().get(iu); + if (match == null) + return false; + return INCLUSION_OPTIONAL.equals(match.get(INCLUSION_RULES)); + } +} diff --git a/bundles/org.eclipse.equinox.p2.tests/src/org/eclipse/equinox/p2/tests/planner/TestRequestFlexerOneInstalledOneBeingInstalled.java b/bundles/org.eclipse.equinox.p2.tests/src/org/eclipse/equinox/p2/tests/planner/TestRequestFlexerOneInstalledOneBeingInstalled.java new file mode 100644 index 000000000..6c903a9a4 --- /dev/null +++ b/bundles/org.eclipse.equinox.p2.tests/src/org/eclipse/equinox/p2/tests/planner/TestRequestFlexerOneInstalledOneBeingInstalled.java @@ -0,0 +1,175 @@ +/******************************************************************************* + * Copyright (c) 2013 Red Hat, Inc. 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: + * Red Hat, Inc. - initial API and implementation + *******************************************************************************/ +package org.eclipse.equinox.p2.tests.planner; + +import org.eclipse.core.runtime.NullProgressMonitor; +import org.eclipse.equinox.internal.p2.operations.RequestFlexer; +import org.eclipse.equinox.p2.engine.*; +import org.eclipse.equinox.p2.metadata.IInstallableUnit; +import org.eclipse.equinox.p2.planner.*; +import org.eclipse.equinox.p2.tests.*; + +public class TestRequestFlexerOneInstalledOneBeingInstalled extends AbstractProvisioningTest { + @IUDescription(content = "package: sdk \n" + "singleton: true\n" + "version: 1 \n" + "depends: platform = 1") + public IInstallableUnit sdk1; + + @IUDescription(content = "package: platform \n" + "singleton: true\n" + "version: 1 \n") + public IInstallableUnit platform1; + + @IUDescription(content = "package: sdk \n" + "singleton: true\n" + "version: 2 \n" + "depends: platform = 2") + public IInstallableUnit sdk2; + + @IUDescription(content = "package: platform \n" + "singleton: true\n" + "version: 2 \n") + public IInstallableUnit platform2; + + @IUDescription(content = "package: egit \n" + "singleton: true\n" + "version: 1 \n" + "depends: platform = 1") + public IInstallableUnit egit1; + + @IUDescription(content = "package: egit \n" + "singleton: true\n" + "version: 2 \n" + "depends: platform = 2") + public IInstallableUnit egit2; + + @IUDescription(content = "package: svn1 \n" + "singleton: true\n" + "version: 1 \n" + "depends: platform = 1") + public IInstallableUnit svn1; + + @IUDescription(content = "package: svn2 \n" + "singleton: true\n" + "version: 2 \n" + "depends: platform = 2") + public IInstallableUnit svn2; + + IProfile profile; + + private IPlanner planner; + + private IEngine engine; + + private IProfileChangeRequest originalRequest; + + private ProvisioningContext context; + + @Override + protected void setUp() throws Exception { + super.setUp(); + profile = createProfile("TestProfile." + getName()); + IULoader.loadIUs(this); + createTestMetdataRepository(new IInstallableUnit[] {sdk1, platform1, sdk2, platform2, egit1, egit2}); + planner = createPlanner(); + engine = createEngine(); + assertOK(install(profile, new IInstallableUnit[] {sdk1}, true, planner, engine)); + + originalRequest = planner.createChangeRequest(profile); + originalRequest.add(egit2); + originalRequest.setInstallableUnitInclusionRules(egit2, ProfileInclusionRules.createStrictInclusionRule(egit2)); + assertNotOK(planner.getProvisioningPlan(originalRequest, context, null).getStatus()); + + { + //Make sure that the goal we are after can actually be reached + IProfileChangeRequest validateGoalRequest = planner.createChangeRequest(profile); + validateGoalRequest.add(egit2); + validateGoalRequest.setInstallableUnitInclusionRules(egit2, ProfileInclusionRules.createStrictInclusionRule(egit2)); + validateGoalRequest.add(sdk2); + validateGoalRequest.setInstallableUnitInclusionRules(sdk2, ProfileInclusionRules.createStrictInclusionRule(sdk2)); + validateGoalRequest.remove(sdk1); + assertOK(planner.getProvisioningPlan(validateGoalRequest, context, null).getStatus()); + } + + { + //Make sure that the goal we are after can actually be reached + IProfileChangeRequest validateGoalRequest = planner.createChangeRequest(profile); + validateGoalRequest.add(egit1); + validateGoalRequest.setInstallableUnitInclusionRules(egit1, ProfileInclusionRules.createStrictInclusionRule(egit1)); + assertOK(planner.getProvisioningPlan(validateGoalRequest, context, null).getStatus()); + } + context = new ProvisioningContext(getAgent()); + + } + + @Override + protected void tearDown() throws Exception { + super.tearDown(); + getProfileRegistry().removeProfile(profile.getProfileId()); + } + + //Found a solution by installing a previous version + public void testInstallAnotherVersion() { + RequestFlexer av = new RequestFlexer(planner); + av.setAllowDifferentVersion(true); + av.setProvisioningContext(context); + IProfileChangeRequest realRequest = av.getChangeRequest(originalRequest, profile, new NullProgressMonitor()); + assertTrue(realRequest.getAdditions().contains(egit1)); + assertEquals(1, realRequest.getAdditions().size()); + assertTrue(realRequest.getRemovals().isEmpty()); + assertOK(install(realRequest, planner, engine)); + } + + public void testInstallPartial() { + RequestFlexer av = new RequestFlexer(planner); + av.setAllowPartialInstall(true); + av.setProvisioningContext(context); + IProfileChangeRequest realRequest = av.getChangeRequest(originalRequest, profile, new NullProgressMonitor()); + assertNull(realRequest); + } + + public void testInstallSomeElementsWithUpdates() { + RequestFlexer av = new RequestFlexer(planner); + av.setAllowPartialInstall(true); + av.setAllowDifferentVersion(true); + av.setProvisioningContext(context); + IProfileChangeRequest realRequest = av.getChangeRequest(originalRequest, profile, new NullProgressMonitor()); + assertTrue(realRequest.getAdditions().contains(egit1)); + assertEquals(1, realRequest.getAdditions().size()); + assertTrue(realRequest.getRemovals().isEmpty()); + assertOK(install(realRequest, planner, engine)); + } + + //Found a solution by changing the base + public void testUpdateBase() { + RequestFlexer av = new RequestFlexer(planner); + av.setAllowInstalledElementChange(true); + av.setProvisioningContext(context); + IProfileChangeRequest realRequest = av.getChangeRequest(originalRequest, profile, new NullProgressMonitor()); + assertTrue(realRequest.getAdditions().contains(egit2)); + assertTrue(realRequest.getAdditions().contains(sdk2)); + assertEquals(2, realRequest.getAdditions().size()); + assertTrue(realRequest.getRemovals().contains(sdk1)); + assertEquals(1, realRequest.getRemovals().size()); + assertOK(install(realRequest, planner, engine)); + } + + //Found a solution by removing the base - in this case only the new element is installed + public void testRemoveBase() { + RequestFlexer av = new RequestFlexer(planner); + av.setAllowInstalledElementRemoval(true); + av.setProvisioningContext(context); + IProfileChangeRequest realRequest = av.getChangeRequest(originalRequest, profile, new NullProgressMonitor()); + assertTrue(realRequest.getAdditions().contains(egit2)); + assertTrue(realRequest.getRemovals().contains(sdk1)); + assertOK(install(realRequest, planner, engine)); + } + + public void testAllowEverything() { + RequestFlexer av = new RequestFlexer(planner); + av.setAllowPartialInstall(true); + av.setAllowDifferentVersion(true); + av.setAllowInstalledElementRemoval(true); + av.setAllowInstalledElementChange(true); + av.setProvisioningContext(context); + IProfileChangeRequest realRequest = av.getChangeRequest(originalRequest, profile, new NullProgressMonitor()); + //We only sdk1 because our goal is maximize the number of installed element and minimize the number of changes + assertTrue(realRequest.getAdditions().contains(egit1)); + assertTrue(realRequest.getRemovals().isEmpty()); + assertOK(install(realRequest, planner, engine)); + } + + public void testDoNothing() { + RequestFlexer av = new RequestFlexer(planner); + av.setProvisioningContext(context); + IProfileChangeRequest realRequest = av.getChangeRequest(originalRequest, profile, new NullProgressMonitor()); + assertNull(realRequest); + } +} diff --git a/bundles/org.eclipse.equinox.p2.tests/src/org/eclipse/equinox/p2/tests/planner/TestRequestFlexerOneInstalledReplacingIt.java b/bundles/org.eclipse.equinox.p2.tests/src/org/eclipse/equinox/p2/tests/planner/TestRequestFlexerOneInstalledReplacingIt.java new file mode 100644 index 000000000..b57f5a333 --- /dev/null +++ b/bundles/org.eclipse.equinox.p2.tests/src/org/eclipse/equinox/p2/tests/planner/TestRequestFlexerOneInstalledReplacingIt.java @@ -0,0 +1,102 @@ +/******************************************************************************* + * Copyright (c) 2013 Red Hat, Inc. 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: + * Red Hat, Inc. - initial API and implementation + *******************************************************************************/ +package org.eclipse.equinox.p2.tests.planner; + +import org.eclipse.core.runtime.NullProgressMonitor; +import org.eclipse.equinox.internal.p2.operations.RequestFlexer; +import org.eclipse.equinox.p2.engine.*; +import org.eclipse.equinox.p2.metadata.IInstallableUnit; +import org.eclipse.equinox.p2.planner.*; +import org.eclipse.equinox.p2.tests.*; + +public class TestRequestFlexerOneInstalledReplacingIt extends AbstractProvisioningTest { + @IUDescription(content = "package: sdk \n" + "singleton: true\n" + "version: 1 \n" + "depends: platform = 1") + public IInstallableUnit sdk1; + + @IUDescription(content = "package: platform \n" + "singleton: true\n" + "version: 1 \n") + public IInstallableUnit platform1; + + @IUDescription(content = "package: sdk \n" + "singleton: true\n" + "version: 2 \n" + "depends: platform = 2") + public IInstallableUnit sdk2; + + @IUDescription(content = "package: platform \n" + "singleton: true\n" + "version: 2 \n") + public IInstallableUnit platform2; + IProfile profile; + + private IPlanner planner; + + private IEngine engine; + + private IProfileChangeRequest originalRequest; + + private ProvisioningContext context; + + @Override + protected void setUp() throws Exception { + super.setUp(); + profile = createProfile("TestProfile." + getName()); + IULoader.loadIUs(this); + createTestMetdataRepository(new IInstallableUnit[] {sdk1, platform1, sdk2, platform2}); + planner = createPlanner(); + engine = createEngine(); + assertOK(install(profile, new IInstallableUnit[] {sdk1}, true, planner, engine)); + + originalRequest = planner.createChangeRequest(profile); + originalRequest.add(sdk2); + originalRequest.setInstallableUnitInclusionRules(sdk2, ProfileInclusionRules.createStrictInclusionRule(sdk2)); + assertNotOK(planner.getProvisioningPlan(originalRequest, context, null).getStatus()); + + context = new ProvisioningContext(getAgent()); + + } + + public void testRemoveInstalledElement() { + IProfileChangeRequest requestToTest = planner.createChangeRequest(profile); + requestToTest.add(sdk2); + RequestFlexer av = new RequestFlexer(planner); + av.setAllowInstalledElementRemoval(true); + av.setProvisioningContext(context); + IProfileChangeRequest realRequest = av.getChangeRequest(requestToTest, profile, new NullProgressMonitor()); + assertTrue(realRequest.getRemovals().contains(sdk1)); + assertTrue(realRequest.getAdditions().contains(sdk2)); + assertEquals(1, realRequest.getRemovals().size()); + assertOK(install(realRequest, planner, engine)); + } + + public void testUpdateInstalledElement() { + IProfileChangeRequest requestToTest = planner.createChangeRequest(profile); + requestToTest.add(sdk2); + RequestFlexer av = new RequestFlexer(planner); + av.setAllowInstalledElementChange(true); + av.setProvisioningContext(context); + IProfileChangeRequest realRequest = av.getChangeRequest(requestToTest, profile, new NullProgressMonitor()); + assertTrue(realRequest.getRemovals().contains(sdk1)); + assertTrue(realRequest.getAdditions().contains(sdk2)); + assertEquals(1, realRequest.getRemovals().size()); + assertEquals(1, realRequest.getAdditions().size()); + assertOK(install(realRequest, planner, engine)); + } + + public void testUDifferentVersionAndUpdateInstalledElement() { + IProfileChangeRequest requestToTest = planner.createChangeRequest(profile); + requestToTest.add(sdk2); + RequestFlexer av = new RequestFlexer(planner); + av.setAllowDifferentVersion(true); + av.setAllowInstalledElementChange(true); + av.setProvisioningContext(context); + IProfileChangeRequest realRequest = av.getChangeRequest(requestToTest, profile, new NullProgressMonitor()); + assertTrue(realRequest.getRemovals().contains(sdk1)); + assertTrue(realRequest.getAdditions().contains(sdk2)); + assertEquals(1, realRequest.getRemovals().size()); + assertEquals(1, realRequest.getAdditions().size()); + assertOK(install(realRequest, planner, engine)); + } +} diff --git a/bundles/org.eclipse.equinox.p2.tests/src/org/eclipse/equinox/p2/tests/planner/TestRequestFlexerOneInstalledTwoBeingInstalled.java b/bundles/org.eclipse.equinox.p2.tests/src/org/eclipse/equinox/p2/tests/planner/TestRequestFlexerOneInstalledTwoBeingInstalled.java new file mode 100644 index 000000000..7b950d364 --- /dev/null +++ b/bundles/org.eclipse.equinox.p2.tests/src/org/eclipse/equinox/p2/tests/planner/TestRequestFlexerOneInstalledTwoBeingInstalled.java @@ -0,0 +1,171 @@ +/******************************************************************************* + * Copyright (c) 2013 Red Hat, Inc. 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: + * Red Hat, Inc. - initial API and implementation + *******************************************************************************/ +package org.eclipse.equinox.p2.tests.planner; + +import org.eclipse.core.runtime.NullProgressMonitor; +import org.eclipse.equinox.internal.p2.operations.RequestFlexer; +import org.eclipse.equinox.p2.engine.*; +import org.eclipse.equinox.p2.metadata.IInstallableUnit; +import org.eclipse.equinox.p2.planner.*; +import org.eclipse.equinox.p2.tests.*; + +public class TestRequestFlexerOneInstalledTwoBeingInstalled extends AbstractProvisioningTest { + @IUDescription(content = "package: sdk \n" + "singleton: true\n" + "version: 1 \n" + "depends: platform = 1") + public IInstallableUnit sdk1; + + @IUDescription(content = "package: platform \n" + "singleton: true\n" + "version: 1 \n") + public IInstallableUnit platform1; + + @IUDescription(content = "package: sdk \n" + "singleton: true\n" + "version: 2 \n" + "depends: platform = 2") + public IInstallableUnit sdk2; + + @IUDescription(content = "package: platform \n" + "singleton: true\n" + "version: 2 \n") + public IInstallableUnit platform2; + + @IUDescription(content = "package: egit \n" + "singleton: true\n" + "version: 1 \n" + "depends: platform = 1") + public IInstallableUnit egit1; + + @IUDescription(content = "package: egit \n" + "singleton: true\n" + "version: 2 \n" + "depends: platform = 2") + public IInstallableUnit egit2; + + @IUDescription(content = "package: egit \n" + "singleton: true\n" + "version: 3 \n" + "depends: platform = 3") + public IInstallableUnit egit3; + + @IUDescription(content = "package: svn \n" + "singleton: true\n" + "version: 1 \n" + "depends: platform = 1") + public IInstallableUnit svn1; + + @IUDescription(content = "package: svn \n" + "singleton: true\n" + "version: 2 \n" + "depends: platform = 2") + public IInstallableUnit svn2; + + @IUDescription(content = "package: svn \n" + "singleton: true\n" + "version: 3 \n" + "depends: platform = 3") + public IInstallableUnit svn3; + + IProfile profile; + + private IPlanner planner; + + private IEngine engine; + + private ProvisioningContext context; + + @Override + protected void setUp() throws Exception { + super.setUp(); + profile = createProfile("TestProfile." + getName()); + IULoader.loadIUs(this); + createTestMetdataRepository(new IInstallableUnit[] {sdk1, platform1}); + planner = createPlanner(); + engine = createEngine(); + context = new ProvisioningContext(getAgent()); + assertOK(install(profile, new IInstallableUnit[] {sdk1}, true, planner, engine)); + } + + public void testAssumptions() { + IProfileChangeRequest originalRequest = planner.createChangeRequest(profile); + originalRequest.add(egit2); + originalRequest.setInstallableUnitInclusionRules(egit2, ProfileInclusionRules.createStrictInclusionRule(egit2)); + originalRequest.add(svn1); + originalRequest.setInstallableUnitInclusionRules(svn1, ProfileInclusionRules.createStrictInclusionRule(svn1)); + assertNotOK(planner.getProvisioningPlan(originalRequest, context, null).getStatus()); + } + + @Override + protected void tearDown() throws Exception { + super.tearDown(); + getProfileRegistry().removeProfile(profile.getProfileId()); + } + + public void testConflictWithTheBase() { + createTestMetdataRepository(new IInstallableUnit[] {sdk1, platform1, sdk2, platform2, egit1, egit2, egit3, svn1, svn2, svn3}); + + IProfileChangeRequest originalRequest = planner.createChangeRequest(profile); + originalRequest.add(egit2); + originalRequest.setInstallableUnitInclusionRules(egit2, ProfileInclusionRules.createStrictInclusionRule(egit2)); + originalRequest.add(svn1); + originalRequest.setInstallableUnitInclusionRules(svn1, ProfileInclusionRules.createStrictInclusionRule(svn1)); + + { + RequestFlexer av = new RequestFlexer(planner); + av.setAllowDifferentVersion(true); + av.setProvisioningContext(context); + IProfileChangeRequest realRequest = av.getChangeRequest(originalRequest, profile, new NullProgressMonitor()); + assertTrue(realRequest.getAdditions().contains(egit1)); + assertTrue(realRequest.getAdditions().contains(svn1)); + assertEquals(2, realRequest.getAdditions().size()); + assertTrue(realRequest.getRemovals().isEmpty()); + assertResolve(realRequest, planner); + } + + { + RequestFlexer av = new RequestFlexer(planner); + av.setAllowPartialInstall(true); + av.setProvisioningContext(context); + IProfileChangeRequest realRequest = av.getChangeRequest(originalRequest, profile, new NullProgressMonitor()); + assertTrue(realRequest.getAdditions().contains(svn1)); + assertEquals(1, realRequest.getAdditions().size()); + assertTrue(realRequest.getRemovals().isEmpty()); + } + + { + RequestFlexer av = new RequestFlexer(planner); + av.setAllowInstalledElementChange(true); + av.setAllowDifferentVersion(true); + av.setProvisioningContext(context); + IProfileChangeRequest realRequest = av.getChangeRequest(originalRequest, profile, new NullProgressMonitor()); + assertTrue(realRequest.getAdditions().contains(svn1)); + assertTrue(realRequest.getAdditions().contains(egit1)); + assertEquals(2, realRequest.getAdditions().size()); + assertTrue(realRequest.getRemovals().isEmpty()); + } + } + + // public void testOneElementWithMissingRequirement() { + // createTestMetdataRepository(new IInstallableUnit[] {sdk1, platform1, sdk2, platform2, egit1, egit2, egit3, svn1, svn2}); + // + // IProfileChangeRequest originalRequest = planner.createChangeRequest(profile); + // originalRequest.add(egit3); //This is the element for which we have a missing requirement + // originalRequest.setInstallableUnitInclusionRules(egit3, ProfileInclusionRules.createStrictInclusionRule(egit3)); + // originalRequest.add(svn1); + // originalRequest.setInstallableUnitInclusionRules(svn1, ProfileInclusionRules.createStrictInclusionRule(svn1)); + // + // { + // AnyVersion2 av = new AnyVersion2(planner); + // av.setAllowPartialInstall(true); + // av.setProvisioningContext(context); + // IProfileChangeRequest realRequest = av.getChangeRequest(originalRequest, profile, new NullProgressMonitor()); + // assertTrue(realRequest.getAdditions().contains(svn1)); + // assertEquals(1, realRequest.getAdditions().size()); + // assertTrue(realRequest.getRemovals().isEmpty()); + // } + // + // { + // AnyVersion2 av = new AnyVersion2(planner); + // av.setAllowDifferentVersion(true); + // av.setProvisioningContext(context); + // IProfileChangeRequest realRequest = av.getChangeRequest(originalRequest, profile, new NullProgressMonitor()); + // assertTrue(realRequest.getAdditions().contains(svn2)); + // assertTrue(realRequest.getAdditions().contains(egit2)); + // assertTrue(realRequest.getRemovals().isEmpty()); + // } + // + // { + // AnyVersion2 av = new AnyVersion2(planner); + // av.setAllowInstalledElementChange(true); + // av.setAllowDifferentVersion(true); + // av.setProvisioningContext(context); + // IProfileChangeRequest realRequest = av.getChangeRequest(originalRequest, profile, new NullProgressMonitor()); + // assertTrue(realRequest.getAdditions().contains(svn2)); + // assertTrue(realRequest.getAdditions().contains(egit2)); + // assertTrue(realRequest.getRemovals().isEmpty()); + // } + // } + +} diff --git a/bundles/org.eclipse.equinox.p2.tests/src/org/eclipse/equinox/p2/tests/planner/TestRequestFlexerRequestWithOptionalInstall.java b/bundles/org.eclipse.equinox.p2.tests/src/org/eclipse/equinox/p2/tests/planner/TestRequestFlexerRequestWithOptionalInstall.java new file mode 100644 index 000000000..f09b8488e --- /dev/null +++ b/bundles/org.eclipse.equinox.p2.tests/src/org/eclipse/equinox/p2/tests/planner/TestRequestFlexerRequestWithOptionalInstall.java @@ -0,0 +1,122 @@ +/******************************************************************************* + * Copyright (c) 2013 Red Hat, Inc. 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: + * Red Hat, Inc. - initial API and implementation + *******************************************************************************/ +package org.eclipse.equinox.p2.tests.planner; + +import java.util.Map; +import org.eclipse.core.runtime.NullProgressMonitor; +import org.eclipse.equinox.internal.p2.director.ProfileChangeRequest; +import org.eclipse.equinox.internal.p2.operations.RequestFlexer; +import org.eclipse.equinox.p2.engine.*; +import org.eclipse.equinox.p2.metadata.IInstallableUnit; +import org.eclipse.equinox.p2.planner.*; +import org.eclipse.equinox.p2.tests.*; + +public class TestRequestFlexerRequestWithOptionalInstall extends AbstractProvisioningTest { + final String INCLUSION_RULES = "org.eclipse.equinox.p2.internal.inclusion.rules"; //$NON-NLS-1$ + final String INCLUSION_OPTIONAL = "OPTIONAL"; //$NON-NLS-1$ + final String INCLUSION_STRICT = "STRICT"; //$NON-NLS-1$ + + @IUDescription(content = "package: sdk \n" + "singleton: true\n" + "version: 1 \n" + "depends: platform = 1") + public IInstallableUnit sdk1; + + @IUDescription(content = "package: platform \n" + "singleton: true\n" + "version: 1 \n") + public IInstallableUnit platform1; + + @IUDescription(content = "package: sdk \n" + "singleton: true\n" + "version: 2 \n" + "depends: platform = 2") + public IInstallableUnit sdk2; + + @IUDescription(content = "package: platform \n" + "singleton: true\n" + "version: 2 \n") + public IInstallableUnit platform2; + + @IUDescription(content = "package: egit \n" + "singleton: true\n" + "version: 1 \n" + "depends: platform = 1") + public IInstallableUnit egit1; + + @IUDescription(content = "package: egit \n" + "singleton: true\n" + "version: 2 \n" + "depends: platform = 2") + public IInstallableUnit egit2; + + @IUDescription(content = "package: egit \n" + "singleton: true\n" + "version: 3 \n" + "depends: platform = 3") + public IInstallableUnit egit3; + + IProfile profile; + + private IPlanner planner; + + private IEngine engine; + + private ProvisioningContext context; + + @Override + protected void setUp() throws Exception { + super.setUp(); + profile = createProfile("TestProfile." + getName()); + IULoader.loadIUs(this); + createTestMetdataRepository(new IInstallableUnit[] {sdk1, platform1}); + planner = createPlanner(); + engine = createEngine(); + context = new ProvisioningContext(getAgent()); + assertOK(installAsRoots(profile, new IInstallableUnit[] {sdk1}, true, planner, engine)); + } + + public void testWithOptionalInstall() { + createTestMetdataRepository(new IInstallableUnit[] {sdk1, platform1, sdk2, platform2, egit1, egit2, egit3}); + + IProfileChangeRequest originalRequest = planner.createChangeRequest(profile); + originalRequest.add(egit2); + originalRequest.setInstallableUnitInclusionRules(egit2, ProfileInclusionRules.createOptionalInclusionRule(egit2)); + + { + //The request is unsatisfiable is no flexibility is given + RequestFlexer av = new RequestFlexer(planner); + av.setProvisioningContext(context); + IProfileChangeRequest realRequest = av.getChangeRequest(originalRequest, profile, new NullProgressMonitor()); + assertNull(realRequest); + } + + { + //The base is allowed to change so egit2 will install fine + RequestFlexer av = new RequestFlexer(planner); + av.setAllowInstalledElementChange(true); + av.setProvisioningContext(context); + IProfileChangeRequest realRequest = av.getChangeRequest(originalRequest, profile, new NullProgressMonitor()); + assertTrue(realRequest.getAdditions().contains(egit2)); + assertTrue(realRequest.getAdditions().contains(sdk2)); + assertEquals(2, realRequest.getAdditions().size()); + assertTrue(realRequest.getRemovals().contains(sdk1)); + assertResolve(realRequest, planner); + } + + { + //We allow for a different version to be installed, so egit1 will be installed + RequestFlexer av = new RequestFlexer(planner); + av.setAllowDifferentVersion(true); + av.setProvisioningContext(context); + IProfileChangeRequest realRequest = av.getChangeRequest(originalRequest, profile, new NullProgressMonitor()); + assertTrue(realRequest.getAdditions().contains(egit1)); + assertEquals(1, realRequest.getAdditions().size()); + assertTrue(isOptionallyBeingInstalled(egit1, realRequest)); + assertResolve(realRequest, planner); + } + } + + private boolean isOptionallyBeingInstalled(IInstallableUnit iu, IProfileChangeRequest originalRequest) { + Map<String, String> match = ((ProfileChangeRequest) originalRequest).getInstallableUnitProfilePropertiesToAdd().get(iu); + if (match == null) + return false; + return INCLUSION_OPTIONAL.equals(match.get(INCLUSION_RULES)); + } + + @Override + protected void tearDown() throws Exception { + super.tearDown(); + getProfileRegistry().removeProfile(profile.getProfileId()); + } + +} diff --git a/bundles/org.eclipse.equinox.p2.tests/src/org/eclipse/equinox/p2/tests/planner/TestRequestFlexerRequestWithRemoval.java b/bundles/org.eclipse.equinox.p2.tests/src/org/eclipse/equinox/p2/tests/planner/TestRequestFlexerRequestWithRemoval.java new file mode 100644 index 000000000..571d68c7a --- /dev/null +++ b/bundles/org.eclipse.equinox.p2.tests/src/org/eclipse/equinox/p2/tests/planner/TestRequestFlexerRequestWithRemoval.java @@ -0,0 +1,86 @@ +/******************************************************************************* + * Copyright (c) 2013 Red Hat, Inc. 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: + * Red Hat, Inc. - initial API and implementation + *******************************************************************************/ +package org.eclipse.equinox.p2.tests.planner; + +import org.eclipse.core.runtime.NullProgressMonitor; +import org.eclipse.equinox.internal.p2.operations.RequestFlexer; +import org.eclipse.equinox.p2.engine.*; +import org.eclipse.equinox.p2.metadata.IInstallableUnit; +import org.eclipse.equinox.p2.planner.*; +import org.eclipse.equinox.p2.tests.*; + +public class TestRequestFlexerRequestWithRemoval extends AbstractProvisioningTest { + @IUDescription(content = "package: sdk \n" + "singleton: true\n" + "version: 1 \n" + "depends: platform = 1") + public IInstallableUnit sdk1; + + @IUDescription(content = "package: platform \n" + "singleton: true\n" + "version: 1 \n") + public IInstallableUnit platform1; + + @IUDescription(content = "package: sdk \n" + "singleton: true\n" + "version: 2 \n" + "depends: platform = 2") + public IInstallableUnit sdk2; + + @IUDescription(content = "package: platform \n" + "singleton: true\n" + "version: 2 \n") + public IInstallableUnit platform2; + + @IUDescription(content = "package: egit \n" + "singleton: true\n" + "version: 1 \n" + "depends: platform = 1") + public IInstallableUnit egit1; + + @IUDescription(content = "package: egit \n" + "singleton: true\n" + "version: 2 \n" + "depends: platform = 2") + public IInstallableUnit egit2; + + @IUDescription(content = "package: egit \n" + "singleton: true\n" + "version: 3 \n" + "depends: platform = 3") + public IInstallableUnit egit3; + + IProfile profile; + + private IPlanner planner; + + private IEngine engine; + + private ProvisioningContext context; + + @Override + protected void setUp() throws Exception { + super.setUp(); + profile = createProfile("TestProfile." + getName()); + IULoader.loadIUs(this); + createTestMetdataRepository(new IInstallableUnit[] {sdk1, platform1}); + planner = createPlanner(); + engine = createEngine(); + context = new ProvisioningContext(getAgent()); + assertOK(install(profile, new IInstallableUnit[] {sdk1}, true, planner, engine)); + } + + @Override + protected void tearDown() throws Exception { + super.tearDown(); + getProfileRegistry().removeProfile(profile.getProfileId()); + } + + public void testWithRemovalInChangeRequest() { + createTestMetdataRepository(new IInstallableUnit[] {sdk1, platform1, sdk2, platform2, egit1, egit2, egit3}); + + IProfileChangeRequest originalRequest = planner.createChangeRequest(profile); + originalRequest.add(egit2); + originalRequest.setInstallableUnitInclusionRules(egit2, ProfileInclusionRules.createStrictInclusionRule(egit2)); + originalRequest.remove(sdk1); + + { + RequestFlexer av = new RequestFlexer(planner); + av.setProvisioningContext(context); + IProfileChangeRequest realRequest = av.getChangeRequest(originalRequest, profile, new NullProgressMonitor()); + assertTrue(realRequest.getAdditions().contains(egit2)); + assertTrue(realRequest.getRemovals().contains(sdk1)); + assertEquals(1, realRequest.getAdditions().size()); + assertResolve(realRequest, planner); + } + } +} diff --git a/bundles/org.eclipse.equinox.p2.ui.sdk/META-INF/MANIFEST.MF b/bundles/org.eclipse.equinox.p2.ui.sdk/META-INF/MANIFEST.MF index 0cea35291..028ac3aa1 100644 --- a/bundles/org.eclipse.equinox.p2.ui.sdk/META-INF/MANIFEST.MF +++ b/bundles/org.eclipse.equinox.p2.ui.sdk/META-INF/MANIFEST.MF @@ -19,6 +19,7 @@ Import-Package: javax.xml.parsers, org.eclipse.equinox.p2.engine.query;version="[2.0.0,3.0.0)", org.eclipse.equinox.p2.metadata;version="[2.0.0,3.0.0)", org.eclipse.equinox.p2.operations;version="[2.0.0,3.0.0)", + org.eclipse.equinox.p2.planner;version="2.0.0", org.eclipse.equinox.p2.query;version="[2.0.0,3.0.0)", org.eclipse.osgi.util;version="1.1.0", org.osgi.framework;version="1.6.0", diff --git a/bundles/org.eclipse.equinox.p2.ui.sdk/src/org/eclipse/equinox/internal/p2/ui/sdk/UpdateHandler.java b/bundles/org.eclipse.equinox.p2.ui.sdk/src/org/eclipse/equinox/internal/p2/ui/sdk/UpdateHandler.java index f2ce43ad5..a416a17fa 100644 --- a/bundles/org.eclipse.equinox.p2.ui.sdk/src/org/eclipse/equinox/internal/p2/ui/sdk/UpdateHandler.java +++ b/bundles/org.eclipse.equinox.p2.ui.sdk/src/org/eclipse/equinox/internal/p2/ui/sdk/UpdateHandler.java @@ -11,10 +11,13 @@ package org.eclipse.equinox.internal.p2.ui.sdk; import org.eclipse.core.runtime.*; -import org.eclipse.equinox.p2.operations.RepositoryTracker; -import org.eclipse.equinox.p2.operations.UpdateOperation; +import org.eclipse.core.runtime.jobs.IJobChangeEvent; +import org.eclipse.core.runtime.jobs.JobChangeAdapter; +import org.eclipse.equinox.p2.operations.*; import org.eclipse.equinox.p2.ui.LoadMetadataRepositoryJob; import org.eclipse.jface.dialogs.MessageDialog; +import org.eclipse.ui.PlatformUI; +import org.eclipse.ui.statushandlers.StatusManager; /** * UpdateHandler invokes the check for updates UI @@ -39,7 +42,33 @@ public class UpdateHandler extends PreloadingRepositoryHandler { // Report any missing repositories. job.reportAccumulatedStatus(); if (getProvisioningUI().getPolicy().continueWorkingWithOperation(operation, getShell())) { - getProvisioningUI().openUpdateWizard(false, operation, job); + + if (operation.getResolutionResult() == Status.OK_STATUS) { + getProvisioningUI().openUpdateWizard(false, operation, job); + } else { + + final RemediationOperation remediationOperation = new RemediationOperation(getProvisioningUI().getSession(), operation.getProfileChangeRequest(), true); + ProvisioningJob job2 = new ProvisioningJob("Searching alternate solutions...", getProvisioningUI().getSession()) { + @Override + public IStatus runModal(IProgressMonitor monitor) { + monitor.beginTask("Some items cannot be at the highest version. Searching for the highest common denominator ...", RemedyConfig.getAllRemdyConfigs().length); + return remediationOperation.resolveModal(monitor); + } + }; + job2.addJobChangeListener(new JobChangeAdapter() { + public void done(IJobChangeEvent event) { + if (PlatformUI.isWorkbenchRunning()) { + PlatformUI.getWorkbench().getDisplay().asyncExec(new Runnable() { + public void run() { + getProvisioningUI().openUpdateWizard(true, operation, remediationOperation, null); + } + }); + } + } + + }); + getProvisioningUI().schedule(job2, StatusManager.SHOW | StatusManager.LOG); + } } } diff --git a/bundles/org.eclipse.equinox.p2.ui/icons/obj/iu_add.gif b/bundles/org.eclipse.equinox.p2.ui/icons/obj/iu_add.gif Binary files differnew file mode 100644 index 000000000..252d7ebcb --- /dev/null +++ b/bundles/org.eclipse.equinox.p2.ui/icons/obj/iu_add.gif diff --git a/bundles/org.eclipse.equinox.p2.ui/icons/obj/iu_remove.gif b/bundles/org.eclipse.equinox.p2.ui/icons/obj/iu_remove.gif Binary files differnew file mode 100644 index 000000000..b6922ac11 --- /dev/null +++ b/bundles/org.eclipse.equinox.p2.ui/icons/obj/iu_remove.gif diff --git a/bundles/org.eclipse.equinox.p2.ui/src/org/eclipse/equinox/internal/p2/ui/ProvUIActivator.java b/bundles/org.eclipse.equinox.p2.ui/src/org/eclipse/equinox/internal/p2/ui/ProvUIActivator.java index 6341f4148..b1dbad8eb 100644 --- a/bundles/org.eclipse.equinox.p2.ui/src/org/eclipse/equinox/internal/p2/ui/ProvUIActivator.java +++ b/bundles/org.eclipse.equinox.p2.ui/src/org/eclipse/equinox/internal/p2/ui/ProvUIActivator.java @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2007, 2011 IBM Corporation and others. + * Copyright (c) 2007, 2013 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 @@ -7,6 +7,7 @@ * * Contributors: * IBM Corporation - initial API and implementation + * Red Hat, Inc. - support for remediation page *******************************************************************************/ package org.eclipse.equinox.internal.p2.ui; @@ -108,6 +109,8 @@ public class ProvUIActivator extends AbstractUIPlugin { createImageDescriptor(ProvUIImages.IMG_IU, reg); createImageDescriptor(ProvUIImages.IMG_DISABLED_IU, reg); createImageDescriptor(ProvUIImages.IMG_UPDATED_IU, reg); + createImageDescriptor(ProvUIImages.IMG_ADDED_IU, reg); + createImageDescriptor(ProvUIImages.IMG_REMOVED_IU, reg); createImageDescriptor(ProvUIImages.IMG_PATCH_IU, reg); createImageDescriptor(ProvUIImages.IMG_DISABLED_PATCH_IU, reg); createImageDescriptor(ProvUIImages.IMG_CATEGORY, reg); diff --git a/bundles/org.eclipse.equinox.p2.ui/src/org/eclipse/equinox/internal/p2/ui/ProvUIImages.java b/bundles/org.eclipse.equinox.p2.ui/src/org/eclipse/equinox/internal/p2/ui/ProvUIImages.java index 050ce6b8c..c68aafb27 100644 --- a/bundles/org.eclipse.equinox.p2.ui/src/org/eclipse/equinox/internal/p2/ui/ProvUIImages.java +++ b/bundles/org.eclipse.equinox.p2.ui/src/org/eclipse/equinox/internal/p2/ui/ProvUIImages.java @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2007, 2011 IBM Corporation and others. + * Copyright (c) 2007, 2013 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 @@ -7,6 +7,7 @@ * * Contributors: * IBM Corporation - initial API and implementation + * Red Hat, Inc. - support for remediation page *******************************************************************************/ package org.eclipse.equinox.internal.p2.ui; @@ -39,6 +40,8 @@ public class ProvUIImages { public final static String IMG_IU = "obj/iu_obj.gif"; //$NON-NLS-1$ public final static String IMG_DISABLED_IU = "obj/iu_disabled_obj.gif"; //$NON-NLS-1$ public final static String IMG_UPDATED_IU = "obj/iu_update_obj.gif"; //$NON-NLS-1$ + public final static String IMG_ADDED_IU = "obj/iu_add.gif"; //$NON-NLS-1$ + public final static String IMG_REMOVED_IU = "obj/iu_remove.gif"; //$NON-NLS-1$ public final static String IMG_PATCH_IU = "obj/iu_patch_obj.gif"; //$NON-NLS-1$ public final static String IMG_DISABLED_PATCH_IU = "obj/iu_disabled_patch_obj.gif"; //$NON-NLS-1$ public final static String IMG_PROFILE = "obj/profile_obj.gif"; //$NON-NLS-1$ diff --git a/bundles/org.eclipse.equinox.p2.ui/src/org/eclipse/equinox/internal/p2/ui/ProvUIMessages.java b/bundles/org.eclipse.equinox.p2.ui/src/org/eclipse/equinox/internal/p2/ui/ProvUIMessages.java index bb0b3d9e5..70defec81 100644 --- a/bundles/org.eclipse.equinox.p2.ui/src/org/eclipse/equinox/internal/p2/ui/ProvUIMessages.java +++ b/bundles/org.eclipse.equinox.p2.ui/src/org/eclipse/equinox/internal/p2/ui/ProvUIMessages.java @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2007, 2011 IBM Corporation and others. + * Copyright (c) 2007, 2013 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 @@ -7,6 +7,7 @@ * * Contributors: * IBM Corporation - initial API and implementation + * Red Hat, Inc - support for remediation page *******************************************************************************/ package org.eclipse.equinox.internal.p2.ui; @@ -234,6 +235,21 @@ public class ProvUIMessages extends NLS { public static String RollbackProfileElement_CurrentInstallation; public static String SelectableIUsPage_Select_All; public static String SelectableIUsPage_Deselect_All; + public static String InstallRemediationPage_Title; + public static String InstallRemediationPage_Description; + public static String UpdateRemediationPage_Title; + public static String UpdateRemediationPage_Description; + public static String RemediationPage_SubDescription; + public static String RemediationPage_NoSolutionFound; + public static String RemediationPage_BeingInstalledSection; + public static String RemediationPage_InstalledSection; + public static String RemediationPage_BeingInstalledSection_AllowPartialInstall; + public static String RemediationPage_BeingInstalledSection_AllowDifferentVersion; + public static String RemediationPage_InstalledSection_AllowInstalledUpdate; + public static String RemediationPage_InstalledSection_AllowInstalledRemoval; + public static String RemediationPage_BestSolutionBeingInstalledRelaxed; + public static String RemediationPage_BestSolutionInstallationRelaxed; + public static String RemediationPage_BestSolutionBuilt; public static String TrustCertificateDialog_Details; public static String TrustCertificateDialog_Title; diff --git a/bundles/org.eclipse.equinox.p2.ui/src/org/eclipse/equinox/internal/p2/ui/actions/ProfileModificationAction.java b/bundles/org.eclipse.equinox.p2.ui/src/org/eclipse/equinox/internal/p2/ui/actions/ProfileModificationAction.java index 31d2fa5ca..1380cbce5 100644 --- a/bundles/org.eclipse.equinox.p2.ui/src/org/eclipse/equinox/internal/p2/ui/actions/ProfileModificationAction.java +++ b/bundles/org.eclipse.equinox.p2.ui/src/org/eclipse/equinox/internal/p2/ui/actions/ProfileModificationAction.java @@ -91,7 +91,8 @@ public abstract class ProfileModificationAction extends ProvisioningAction { } }); - getProvisioningUI().schedule(job, StatusManager.SHOW | StatusManager.LOG); + // Don't show the error dialog anymore since the remediation operation should be computed + getProvisioningUI().schedule(job, StatusManager.NONE | StatusManager.LOG); } // Since we are resolving asynchronously, our job is done. Setting this allows // callers to decide to close the launching window. diff --git a/bundles/org.eclipse.equinox.p2.ui/src/org/eclipse/equinox/internal/p2/ui/actions/UpdateAction.java b/bundles/org.eclipse.equinox.p2.ui/src/org/eclipse/equinox/internal/p2/ui/actions/UpdateAction.java index 289c52375..2fcc0f0b4 100644 --- a/bundles/org.eclipse.equinox.p2.ui/src/org/eclipse/equinox/internal/p2/ui/actions/UpdateAction.java +++ b/bundles/org.eclipse.equinox.p2.ui/src/org/eclipse/equinox/internal/p2/ui/actions/UpdateAction.java @@ -14,15 +14,20 @@ package org.eclipse.equinox.internal.p2.ui.actions; import java.util.ArrayList; import java.util.Collection; +import org.eclipse.core.runtime.*; +import org.eclipse.core.runtime.jobs.IJobChangeEvent; +import org.eclipse.core.runtime.jobs.JobChangeAdapter; +import org.eclipse.equinox.internal.p2.director.ProfileChangeRequest; import org.eclipse.equinox.internal.p2.ui.ProvUI; import org.eclipse.equinox.internal.p2.ui.ProvUIMessages; import org.eclipse.equinox.internal.p2.ui.model.IUElementListRoot; import org.eclipse.equinox.p2.engine.IProfile; import org.eclipse.equinox.p2.metadata.IInstallableUnit; -import org.eclipse.equinox.p2.operations.ProfileChangeOperation; -import org.eclipse.equinox.p2.operations.UpdateOperation; +import org.eclipse.equinox.p2.operations.*; import org.eclipse.equinox.p2.ui.ProvisioningUI; import org.eclipse.jface.viewers.ISelectionProvider; +import org.eclipse.ui.PlatformUI; +import org.eclipse.ui.statushandlers.StatusManager; public class UpdateAction extends ExistingIUInProfileAction { @@ -66,7 +71,41 @@ public class UpdateAction extends ExistingIUInProfileAction { /* (non-Javadoc) * @see org.eclipse.equinox.internal.p2.ui.actions.ProfileModificationAction#performAction(org.eclipse.equinox.p2.operations.ProfileChangeOperation, org.eclipse.equinox.internal.provisional.p2.metadata.IInstallableUnit[]) */ - protected int performAction(ProfileChangeOperation operation, Collection<IInstallableUnit> ius) { - return ui.openUpdateWizard(skipSelectionPage, (UpdateOperation) operation, null); + protected int performAction(final ProfileChangeOperation operation, Collection<IInstallableUnit> ius) { + if (operation.getResolutionResult() == Status.OK_STATUS) + return ui.openUpdateWizard(skipSelectionPage, (UpdateOperation) operation, null); + + final RemediationOperation remediationOperation = new RemediationOperation(getSession(), (ProfileChangeRequest) operation.getProfileChangeRequest()); + ProvisioningJob job = new ProvisioningJob("Searching alternate solutions...", getSession()) { + @Override + public IStatus runModal(IProgressMonitor monitor) { + monitor.beginTask("The update operation cannot be completed as requested. Searching alternate solutions ...", RemedyConfig.getAllRemdyConfigs().length); + return remediationOperation.resolveModal(monitor); + } + }; + job.addJobChangeListener(new JobChangeAdapter() { + public void done(IJobChangeEvent event) { + if (PlatformUI.isWorkbenchRunning()) { + PlatformUI.getWorkbench().getDisplay().asyncExec(new Runnable() { + public void run() { + ui.openUpdateWizard(skipSelectionPage, (UpdateOperation) operation, remediationOperation, null); + // UpdateWizard wizard = new UpdateWizard(ui, (UpdateOperation) operation, ((UpdateOperation) operation).getSelectedUpdates(), null); + // wizard.setSkipSelectionsPage(skipSelectionPage); + // wizard.setRemediationOperation(remediationOperation); + // WizardDialog dialog = new ProvisioningWizardDialog(ProvUI.getDefaultParentShell(), wizard); + // dialog.create(); + // PlatformUI.getWorkbench().getHelpSystem().setHelp(dialog.getShell(), IProvHelpContextIds.UPDATE_WIZARD); + // if (wizard.getCurrentStatus().getSeverity() == IStatus.ERROR) { + // wizard.deselectLockedIUs(); + // } + // dialog.open(); + } + }); + } + } + + }); + getProvisioningUI().schedule(job, StatusManager.SHOW | StatusManager.LOG); + return 1; } } diff --git a/bundles/org.eclipse.equinox.p2.ui/src/org/eclipse/equinox/internal/p2/ui/dialogs/InstallWizard.java b/bundles/org.eclipse.equinox.p2.ui/src/org/eclipse/equinox/internal/p2/ui/dialogs/InstallWizard.java index 142b78875..57949d802 100644 --- a/bundles/org.eclipse.equinox.p2.ui/src/org/eclipse/equinox/internal/p2/ui/dialogs/InstallWizard.java +++ b/bundles/org.eclipse.equinox.p2.ui/src/org/eclipse/equinox/internal/p2/ui/dialogs/InstallWizard.java @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2007, 2010 IBM Corporation and others. + * Copyright (c) 2007, 2013 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 @@ -8,6 +8,7 @@ * Contributors: * IBM Corporation - initial API and implementation * Sonatype, Inc. - ongoing development + * Red Hat, Inc. - support for remediation page *******************************************************************************/ package org.eclipse.equinox.internal.p2.ui.dialogs; @@ -18,8 +19,7 @@ import org.eclipse.equinox.internal.p2.ui.*; import org.eclipse.equinox.internal.p2.ui.model.*; import org.eclipse.equinox.p2.engine.ProvisioningContext; import org.eclipse.equinox.p2.metadata.IInstallableUnit; -import org.eclipse.equinox.p2.operations.InstallOperation; -import org.eclipse.equinox.p2.operations.ProfileChangeOperation; +import org.eclipse.equinox.p2.operations.*; import org.eclipse.equinox.p2.ui.LoadMetadataRepositoryJob; import org.eclipse.equinox.p2.ui.ProvisioningUI; import org.eclipse.jface.dialogs.IDialogConstants; @@ -64,18 +64,24 @@ public class InstallWizard extends WizardWithLicenses { if (selectedElements == null) return; root = new IUElementListRoot(); - ArrayList<AvailableIUElement> list = new ArrayList<AvailableIUElement>(selectedElements.length); - ArrayList<AvailableIUElement> selections = new ArrayList<AvailableIUElement>(selectedElements.length); - for (int i = 0; i < selectedElements.length; i++) { - IInstallableUnit iu = ElementUtils.getIU(selectedElements[i]); - if (iu != null) { - AvailableIUElement element = new AvailableIUElement(root, iu, getProfileId(), shouldShowProvisioningPlanChildren()); - list.add(element); - selections.add(element); + if (operation instanceof RemediationOperation) { + ArrayList<AvailableIUElement> list = remediationPage.transformIUstoIUElements(); + root.setChildren(list.toArray()); + planSelections = list.toArray(); + } else { + ArrayList<AvailableIUElement> list = new ArrayList<AvailableIUElement>(selectedElements.length); + ArrayList<AvailableIUElement> selections = new ArrayList<AvailableIUElement>(selectedElements.length); + for (int i = 0; i < selectedElements.length; i++) { + IInstallableUnit iu = ElementUtils.getIU(selectedElements[i]); + if (iu != null) { + AvailableIUElement element = new AvailableIUElement(root, iu, getProfileId(), shouldShowProvisioningPlanChildren()); + list.add(element); + selections.add(element); + } } + root.setChildren(list.toArray()); + planSelections = selections.toArray(); } - root.setChildren(list.toArray()); - planSelections = selections.toArray(); } /* @@ -116,6 +122,11 @@ public class InstallWizard extends WizardWithLicenses { return errorReportingPage; } + protected RemediationPage createRemediationPage() { + remediationPage = new RemediationPage(ui, this, root, operation); + return remediationPage; + } + /* (non-Javadoc) * @see org.eclipse.equinox.internal.p2.ui.dialogs.ProvisioningOperationWizard#getProfileChangeOperation(java.lang.Object[]) */ diff --git a/bundles/org.eclipse.equinox.p2.ui/src/org/eclipse/equinox/internal/p2/ui/dialogs/InstallWizardPage.java b/bundles/org.eclipse.equinox.p2.ui/src/org/eclipse/equinox/internal/p2/ui/dialogs/InstallWizardPage.java index d50b2f6ab..b4f3bc5e2 100644 --- a/bundles/org.eclipse.equinox.p2.ui/src/org/eclipse/equinox/internal/p2/ui/dialogs/InstallWizardPage.java +++ b/bundles/org.eclipse.equinox.p2.ui/src/org/eclipse/equinox/internal/p2/ui/dialogs/InstallWizardPage.java @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2007, 2010 IBM Corporation and others. + * Copyright (c) 2007, 2013 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 @@ -7,17 +7,18 @@ * * Contributors: * IBM Corporation - initial API and implementation + * Red Hat, Inc. - support for remediation page *******************************************************************************/ package org.eclipse.equinox.internal.p2.ui.dialogs; import org.eclipse.equinox.internal.p2.ui.ProvUIMessages; import org.eclipse.equinox.internal.p2.ui.model.IUElementListRoot; -import org.eclipse.equinox.p2.operations.InstallOperation; +import org.eclipse.equinox.p2.operations.ProfileChangeOperation; import org.eclipse.equinox.p2.ui.ProvisioningUI; public class InstallWizardPage extends SizeComputingWizardPage { - public InstallWizardPage(ProvisioningUI ui, ProvisioningOperationWizard wizard, IUElementListRoot root, InstallOperation operation) { + public InstallWizardPage(ProvisioningUI ui, ProvisioningOperationWizard wizard, IUElementListRoot root, ProfileChangeOperation operation) { super(ui, wizard, root, operation); setTitle(ProvUIMessages.InstallWizardPage_Title); setDescription(ProvUIMessages.InstallWizardPage_NoCheckboxDescription); diff --git a/bundles/org.eclipse.equinox.p2.ui/src/org/eclipse/equinox/internal/p2/ui/dialogs/PreselectedIUInstallWizard.java b/bundles/org.eclipse.equinox.p2.ui/src/org/eclipse/equinox/internal/p2/ui/dialogs/PreselectedIUInstallWizard.java index aa77d0086..7d4518823 100644 --- a/bundles/org.eclipse.equinox.p2.ui/src/org/eclipse/equinox/internal/p2/ui/dialogs/PreselectedIUInstallWizard.java +++ b/bundles/org.eclipse.equinox.p2.ui/src/org/eclipse/equinox/internal/p2/ui/dialogs/PreselectedIUInstallWizard.java @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2009, 2011 IBM Corporation and others. + * Copyright (c) 2009, 2013 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 @@ -8,6 +8,7 @@ * Contributors: * IBM Corporation - initial API and implementation * Sonatype, Inc. - ongoing development + * Red Hat, Inc. - support for remediation page *******************************************************************************/ package org.eclipse.equinox.internal.p2.ui.dialogs; @@ -84,4 +85,11 @@ public class PreselectedIUInstallWizard extends WizardWithLicenses { // op.setRootMarkerKey(getRootMarkerKey()); return op; } + + @Override + protected RemediationPage createRemediationPage() { + // TODO Auto-generated method stub + return null; + } + } diff --git a/bundles/org.eclipse.equinox.p2.ui/src/org/eclipse/equinox/internal/p2/ui/dialogs/ProvisioningOperationWizard.java b/bundles/org.eclipse.equinox.p2.ui/src/org/eclipse/equinox/internal/p2/ui/dialogs/ProvisioningOperationWizard.java index efe2f3f75..6df089cd1 100644 --- a/bundles/org.eclipse.equinox.p2.ui/src/org/eclipse/equinox/internal/p2/ui/dialogs/ProvisioningOperationWizard.java +++ b/bundles/org.eclipse.equinox.p2.ui/src/org/eclipse/equinox/internal/p2/ui/dialogs/ProvisioningOperationWizard.java @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2008, 2011 IBM Corporation and others. + * Copyright (c) 2008, 2013 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 @@ -8,6 +8,7 @@ * Contributors: * IBM Corporation - initial API and implementation * Sonatype, Inc. - ongoing development + * Red Hat, Inc. - support for remediation page *******************************************************************************/ package org.eclipse.equinox.internal.p2.ui.dialogs; @@ -15,12 +16,13 @@ import java.lang.reflect.InvocationTargetException; import java.util.HashSet; import org.eclipse.core.runtime.*; import org.eclipse.core.runtime.jobs.*; +import org.eclipse.equinox.internal.p2.director.ProfileChangeRequest; import org.eclipse.equinox.internal.p2.ui.*; import org.eclipse.equinox.internal.p2.ui.model.ElementUtils; import org.eclipse.equinox.internal.p2.ui.model.IUElementListRoot; import org.eclipse.equinox.p2.engine.ProvisioningContext; import org.eclipse.equinox.p2.metadata.IInstallableUnit; -import org.eclipse.equinox.p2.operations.ProfileChangeOperation; +import org.eclipse.equinox.p2.operations.*; import org.eclipse.equinox.p2.ui.*; import org.eclipse.jface.operation.IRunnableContext; import org.eclipse.jface.operation.IRunnableWithProgress; @@ -38,11 +40,11 @@ import org.eclipse.ui.statushandlers.StatusManager; public abstract class ProvisioningOperationWizard extends Wizard { private static final String WIZARD_SETTINGS_SECTION = "WizardSettings"; //$NON-NLS-1$ - protected ProvisioningUI ui; protected IUElementListRoot root; protected ProfileChangeOperation operation; protected Object[] planSelections; + protected RemediationPage remediationPage; protected ISelectableIUsPage mainPage; protected IResolutionErrorReportingPage errorPage; protected ResolutionResultsWizardPage resolutionPage; @@ -50,8 +52,8 @@ public abstract class ProvisioningOperationWizard extends Wizard { protected LoadMetadataRepositoryJob repoPreloadJob; IStatus couldNotResolveStatus = Status.OK_STATUS; // we haven't tried and failed boolean resolveWithRelaxedConstraints = false; - boolean waitingForOtherJobs = false; + protected RemediationOperation remediationOperation; public ProvisioningOperationWizard(ProvisioningUI ui, ProfileChangeOperation operation, Object[] initialSelections, LoadMetadataRepositoryJob job) { super(); @@ -66,6 +68,14 @@ public abstract class ProvisioningOperationWizard extends Wizard { } } + public void setRemediationOperation(RemediationOperation remediationOperation) { + this.remediationOperation = remediationOperation; + } + + public RemediationOperation getRemediationOperation() { + return remediationOperation; + } + /* * (non-Javadoc) * @see org.eclipse.jface.wizard.Wizard#addPages() @@ -76,10 +86,15 @@ public abstract class ProvisioningOperationWizard extends Wizard { errorPage = createErrorReportingPage(); if (errorPage != mainPage) addPage(errorPage); + remediationPage = createRemediationPage(); + if (remediationPage != null) + addPage(remediationPage); resolutionPage = createResolutionPage(); addPage(resolutionPage); } + protected abstract RemediationPage createRemediationPage(); + protected abstract IResolutionErrorReportingPage createErrorReportingPage(); protected abstract ISelectableIUsPage createMainPage(IUElementListRoot input, Object[] selections); @@ -96,16 +111,48 @@ public abstract class ProvisioningOperationWizard extends Wizard { /* * (non-Javadoc) + * @see org.eclipse.jface.wizard.Wizard#getPreviousPage(org.eclipse.jface.wizard.IWizardPage) + * + */ + public IWizardPage getPreviousPage(IWizardPage page) { + if (page == errorPage) { + return mainPage; + } + return super.getPreviousPage(page); + } + + /* + * (non-Javadoc) * @see org.eclipse.jface.wizard.Wizard#getNextPage(org.eclipse.jface.wizard.IWizardPage) + * */ public IWizardPage getNextPage(IWizardPage page) { // If we are moving from the main page or error page, we may need to resolve before // advancing. - if (page == mainPage || page == errorPage) { + + if (page == remediationPage) { + remediationOperation.setSelectedRemedy(remediationPage.getSelectedRemedy()); + try { + getContainer().run(true, false, new IRunnableWithProgress() { + public void run(IProgressMonitor monitor) { + remediationOperation.resolveModal(monitor); + } + }); + } catch (InterruptedException e) { + // Nothing to report if thread was interrupted + } catch (InvocationTargetException e) { + ProvUI.handleException(e.getCause(), null, StatusManager.SHOW | StatusManager.LOG); + couldNotResolve(null); + } + operation = remediationOperation; + initializeResolutionModelElements(remediationPage.transformIUstoIUElements().toArray()); + planChanged(); + return resolutionPage; + } else if (page == mainPage || page == errorPage) { ISelectableIUsPage currentPage = (ISelectableIUsPage) page; // Do we need to resolve? if (operation == null || (operation != null && shouldRecomputePlan(currentPage))) { - recomputePlan(getContainer()); + recomputePlan(getContainer(), true); } else { // the selections have not changed from an IU point of view, but we want // to reinitialize the resolution model elements to ensure they are up to @@ -114,10 +161,22 @@ public abstract class ProvisioningOperationWizard extends Wizard { } IStatus status = operation.getResolutionResult(); if (status == null || status.getSeverity() == IStatus.ERROR) { - return errorPage; + if (page == mainPage) { + if (remediationOperation != null && remediationOperation.hasRemedies() && remediationOperation.getRemedies().size() == 1) { + planChanged(); + remediationPage.setSelectedRemedy(remediationOperation.getRemedies().get(0)); + return getNextPage(remediationPage); + } else if (remediationOperation != null && remediationOperation.hasRemedies()) { + planChanged(); + return remediationPage; + } + return errorPage; + } } else if (status.getSeverity() == IStatus.CANCEL) { return page; } else { + if (remediationPage != null) + remediationPage.setPageComplete(true); return resolutionPage; } } @@ -172,6 +231,10 @@ public abstract class ProvisioningOperationWizard extends Wizard { } protected void planChanged() { + IWizardPage currentPage = ((WizardDialog) getContainer()).getCurrentPage(); + if ((currentPage == null || currentPage == mainPage) && remediationPage != null && remediationOperation != null && remediationOperation.hasRemedies()) { + remediationPage.updateStatus(root, operation, planSelections); + } resolutionPage.updateStatus(root, operation); if (errorPage != resolutionPage) { IUElementListRoot newRoot = shouldUpdateErrorPageModelOnPlanChange() ? root : null; @@ -192,31 +255,16 @@ public abstract class ProvisioningOperationWizard extends Wizard { return new ProvisioningContext(ui.getSession().getProvisioningAgent()); } - public void recomputePlanWithRelaxedConstraints(IRunnableContext runnableContext) { - couldNotResolveStatus = Status.OK_STATUS; - provisioningContext = getProvisioningContext(); - initializeResolutionModelElements(getOperationSelections()); - if (planSelections.length == 0) { - operation = null; - couldNotResolve(ProvUIMessages.ResolutionWizardPage_NoSelections); - } else { - operation = ui.getRelaxedUpdateOperation(provisioningContext); - operation.setProvisioningContext(provisioningContext); - try { - runnableContext.run(true, true, new IRunnableWithProgress() { - public void run(IProgressMonitor monitor) { - operation.resolveModal(monitor); - } - }); + public void recomputePlan(IRunnableContext runnableContext) { + recomputePlan(runnableContext, false); + } - } catch (InterruptedException e) { - // Nothing to report if thread was interrupted - } catch (InvocationTargetException e) { - ProvUI.handleException(e.getCause(), null, StatusManager.SHOW | StatusManager.LOG); - couldNotResolve(null); - } - } - planChanged(); + public void computeRemediationOperation(ProfileChangeOperation operation, ProvisioningUI ui, IProgressMonitor monitor) { + SubMonitor sub = SubMonitor.convert(monitor, "remediationoperation", RemedyConfig.getAllRemdyConfigs().length); + monitor.setTaskName("compute remediation operation"); + remediationOperation = new RemediationOperation(ui.getSession(), (ProfileChangeRequest) operation.getProfileChangeRequest()); + remediationOperation.getResolveJob(monitor); + sub.done(); } /** @@ -225,11 +273,7 @@ public abstract class ProvisioningOperationWizard extends Wizard { * * @param runnableContext */ - public void recomputePlan(IRunnableContext runnableContext) { - if (resolveWithRelaxedConstraints) { - recomputePlanWithRelaxedConstraints(runnableContext); - return; - } + public void recomputePlan(IRunnableContext runnableContext, final boolean withRemediation) { couldNotResolveStatus = Status.OK_STATUS; provisioningContext = getProvisioningContext(); initializeResolutionModelElements(getOperationSelections()); @@ -243,9 +287,14 @@ public abstract class ProvisioningOperationWizard extends Wizard { runnableContext.run(true, true, new IRunnableWithProgress() { public void run(IProgressMonitor monitor) { operation.resolveModal(monitor); + if (withRemediation) { + IStatus status = operation.getResolutionResult(); + if (remediationPage != null && (status == null || (status.getSeverity() == IStatus.ERROR && status.getCode() != 10053))) { + computeRemediationOperation(operation, ui, monitor); + } + } } }); - } catch (InterruptedException e) { // Nothing to report if thread was interrupted } catch (InvocationTargetException e) { diff --git a/bundles/org.eclipse.equinox.p2.ui/src/org/eclipse/equinox/internal/p2/ui/dialogs/RemediationPage.java b/bundles/org.eclipse.equinox.p2.ui/src/org/eclipse/equinox/internal/p2/ui/dialogs/RemediationPage.java new file mode 100644 index 000000000..e97929321 --- /dev/null +++ b/bundles/org.eclipse.equinox.p2.ui/src/org/eclipse/equinox/internal/p2/ui/dialogs/RemediationPage.java @@ -0,0 +1,343 @@ +package org.eclipse.equinox.internal.p2.ui.dialogs; + +import java.util.*; +import java.util.List; +import org.eclipse.equinox.internal.p2.ui.ProvUIMessages; +import org.eclipse.equinox.internal.p2.ui.model.*; +import org.eclipse.equinox.internal.p2.ui.viewers.*; +import org.eclipse.equinox.p2.metadata.IInstallableUnit; +import org.eclipse.equinox.p2.operations.*; +import org.eclipse.equinox.p2.ui.ProvisioningUI; +import org.eclipse.jface.dialogs.Dialog; +import org.eclipse.jface.viewers.TreeViewer; +import org.eclipse.swt.SWT; +import org.eclipse.swt.custom.SashForm; +import org.eclipse.swt.custom.StackLayout; +import org.eclipse.swt.layout.GridData; +import org.eclipse.swt.layout.GridLayout; +import org.eclipse.swt.widgets.*; + +public class RemediationPage extends ResolutionStatusPage { + private static final int ALLOWPARTIALINSTALL_INDEX = 0; + private static final int ALLOWDIFFERENTVERSION_INDEX = 1; + private static final int ALLOWINSTALLEDUPDATE_INDEX = 2; + private static final int ALLOWINSTALLEDREMOVAL_INDEX = 3; + private static final HashMap<String, String[]> CONSTRAINTS = new HashMap<String, String[]>() { + { + put(ProvUIMessages.RemediationPage_BeingInstalledSection, new String[] {ProvUIMessages.RemediationPage_BeingInstalledSection_AllowPartialInstall, ProvUIMessages.RemediationPage_BeingInstalledSection_AllowDifferentVersion}); + put(ProvUIMessages.RemediationPage_InstalledSection, new String[] {ProvUIMessages.RemediationPage_InstalledSection_AllowInstalledUpdate, ProvUIMessages.RemediationPage_InstalledSection_AllowInstalledRemoval}); + } + }; + + private Composite mainComposite; + private Composite resultComposite; + private Composite resultNotFoundComposite; + private Composite resultErrorComposite; + private StackLayout switchResultLayout; + private Composite resultFoundComposite; + protected IUElementListRoot input; + private TreeViewer treeViewer; + private Remedy selectedRemedy; + + private IUDetailsGroup iuDetailsGroup; + private Listener bestSolutionlistener; + private Button bestBeingInstalledRelaxedButton; + private Button bestInstalledRelaxedButton; + private Button buildMyOwnSolution; + private ArrayList<Button> checkboxes; + + protected RemediationPage(ProvisioningUI ui, ProvisioningOperationWizard wizard, IUElementListRoot input, ProfileChangeOperation operation) { + super("RemediationPage", ui, wizard); //$NON-NLS-1$ + if (getWizard() instanceof UpdateWizard) { + setTitle(ProvUIMessages.InstallRemediationPage_Title); + setDescription(ProvUIMessages.InstallRemediationPage_Description); + } else { + setTitle(ProvUIMessages.UpdateRemediationPage_Title); + setDescription(ProvUIMessages.UpdateRemediationPage_Description); + } + } + + public void createControl(Composite parent) { + mainComposite = new Composite(parent, SWT.NONE); + checkboxes = new ArrayList<Button>(); + mainComposite.setLayout(new GridLayout()); + + Label descriptionLabel = new Label(mainComposite, SWT.NONE); + descriptionLabel.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, false)); + descriptionLabel.setText(ProvUIMessages.RemediationPage_SubDescription); + + bestSolutionlistener = new Listener() { + public void handleEvent(Event e) { + Button btn = (Button) e.widget; + Remedy remedy = (btn.getData() == null ? null : (Remedy) btn.getData()); + checkboxes.get(ALLOWPARTIALINSTALL_INDEX).setSelection(remedy != null && remedy.getConfig().allowPartialInstall); + checkboxes.get(ALLOWDIFFERENTVERSION_INDEX).setSelection(remedy != null && remedy.getConfig().allowDifferentVersion); + checkboxes.get(ALLOWINSTALLEDUPDATE_INDEX).setSelection(remedy != null && remedy.getConfig().allowInstalledUpdate); + checkboxes.get(ALLOWINSTALLEDREMOVAL_INDEX).setSelection(remedy != null && remedy.getConfig().allowInstalledRemoval); + for (Iterator<Button> iterator = checkboxes.iterator(); iterator.hasNext();) { + Button btn1 = iterator.next(); + btn1.setEnabled(remedy == null); + } + refreshResultComposite(); + } + }; + + bestBeingInstalledRelaxedButton = new Button(mainComposite, SWT.RADIO); + bestBeingInstalledRelaxedButton.setText(ProvUIMessages.RemediationPage_BestSolutionBeingInstalledRelaxed); + bestBeingInstalledRelaxedButton.addListener(SWT.Selection, bestSolutionlistener); + + bestInstalledRelaxedButton = new Button(mainComposite, SWT.RADIO); + bestInstalledRelaxedButton.setText(ProvUIMessages.RemediationPage_BestSolutionInstallationRelaxed); + bestInstalledRelaxedButton.addListener(SWT.Selection, bestSolutionlistener); + + buildMyOwnSolution = new Button(mainComposite, SWT.RADIO); + buildMyOwnSolution.setText(ProvUIMessages.RemediationPage_BestSolutionBuilt); + buildMyOwnSolution.addListener(SWT.Selection, bestSolutionlistener); + + Listener relaxedConstraintlistener = new Listener() { + public void handleEvent(Event e) { + refreshResultComposite(); + } + }; + + GridData gd = new GridData(SWT.FILL, SWT.FILL, true, false); + gd.horizontalIndent = 30; + Iterator<String> iter = CONSTRAINTS.keySet().iterator(); + while (iter.hasNext()) { + String key = iter.next(); + String[] values = CONSTRAINTS.get(key); + for (int i = 0; i < values.length; i++) { + Button checkBtn = new Button(mainComposite, SWT.CHECK); + checkBtn.setText(values[i]); + checkBtn.setData(values[i]); + checkBtn.setLayoutData(gd); + checkBtn.addListener(SWT.Selection, relaxedConstraintlistener); + checkboxes.add(checkBtn); + } + + } + + resultComposite = new Composite(mainComposite, SWT.NONE); + switchResultLayout = new StackLayout(); + resultComposite.setLayout(switchResultLayout); + GridData data = new GridData(GridData.FILL_BOTH); + resultComposite.setLayoutData(data); + + resultErrorComposite = new Composite(resultComposite, SWT.NONE); + resultErrorComposite.setLayout(new GridLayout()); + + resultNotFoundComposite = new Composite(resultComposite, SWT.NONE); + resultNotFoundComposite.setLayout(new GridLayout()); + Label resultNotFoundLabel = new Label(resultNotFoundComposite, SWT.NONE); + resultNotFoundLabel.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, false)); + resultNotFoundLabel.setText(ProvUIMessages.RemediationPage_NoSolutionFound); + + resultFoundComposite = new Composite(resultComposite, SWT.NONE); + GridLayout gridLayout = new GridLayout(); + gridLayout.marginWidth = 0; + gridLayout.marginHeight = 0; + resultFoundComposite.setLayout(gridLayout); + + treeViewer = new TreeViewer(resultFoundComposite, SWT.BORDER | SWT.MULTI | SWT.FULL_SELECTION); + data = new GridData(GridData.FILL_BOTH); + data.heightHint = convertHeightInCharsToPixels(ILayoutConstants.DEFAULT_TABLE_HEIGHT); + data.widthHint = convertWidthInCharsToPixels(ILayoutConstants.DEFAULT_TABLE_WIDTH); + Tree tree = treeViewer.getTree(); + tree.setLayoutData(data); + tree.setHeaderVisible(true); + activateCopy(tree); + IUColumnConfig[] columns = getColumnConfig(); + for (int i = 0; i < columns.length; i++) { + TreeColumn tc = new TreeColumn(tree, SWT.LEFT, i); + tc.setResizable(true); + tc.setText(columns[i].getColumnTitle()); + tc.setWidth(columns[i].getWidthInPixels(tree)); + } + + // Filters and sorters before establishing content, so we don't refresh unnecessarily. + IUComparator comparator = new IUComparator(IUComparator.IU_NAME); + comparator.useColumnConfig(getColumnConfig()); + treeViewer.setComparator(comparator); + treeViewer.setComparer(new ProvElementComparer()); + + ProvElementContentProvider contentProvider = new ProvElementContentProvider(); + treeViewer.setContentProvider(contentProvider); + IUDetailsLabelProvider labelProvider = new IUDetailsLabelProvider(null, getColumnConfig(), getShell()); + treeViewer.setLabelProvider(labelProvider); + resultComposite.setVisible(false); + iuDetailsGroup = new IUDetailsGroup(resultErrorComposite, treeViewer, convertWidthInCharsToPixels(ILayoutConstants.DEFAULT_TABLE_WIDTH), true); + + setControl(mainComposite); + setPageComplete(false); + + Dialog.applyDialogFont(mainComposite); + + } + + public boolean canFlipToNextPage() { + return isPageComplete(); + } + + public void updateStatus(IUElementListRoot newRoot, ProfileChangeOperation operation, Object[] planSelections) { + setDetailText(operation); + checkboxes.get(ALLOWPARTIALINSTALL_INDEX).setEnabled(planSelections.length > 1); + RemediationOperation remediationOperation = ((ProvisioningOperationWizard) getWizard()).getRemediationOperation(); + boolean isSelected = false; + if (remediationOperation.bestSolutionChangingTheRequest() != null) { + bestBeingInstalledRelaxedButton.setData(remediationOperation.bestSolutionChangingTheRequest()); + bestBeingInstalledRelaxedButton.setSelection(true); + bestBeingInstalledRelaxedButton.notifyListeners(SWT.Selection, new Event()); + isSelected = true; + } + bestBeingInstalledRelaxedButton.setEnabled(remediationOperation.bestSolutionChangingTheRequest() != null); + + if (remediationOperation.bestSolutionChangingWhatIsInstalled() != null) { + bestInstalledRelaxedButton.setData(remediationOperation.bestSolutionChangingWhatIsInstalled()); + bestInstalledRelaxedButton.setSelection(isSelected == false); + if (!isSelected) + bestInstalledRelaxedButton.notifyListeners(SWT.Selection, new Event()); + isSelected = true; + } + bestInstalledRelaxedButton.setEnabled(remediationOperation.bestSolutionChangingWhatIsInstalled() != null); + buildMyOwnSolution.setSelection(isSelected == false); + if (!isSelected) + buildMyOwnSolution.notifyListeners(SWT.Selection, new Event()); + } + + @Override + protected void updateCaches(IUElementListRoot root, ProfileChangeOperation resolvedOperation) { + // TODO Auto-generated method stub + + } + + @Override + protected boolean isCreated() { + return false; + } + + @Override + protected IUDetailsGroup getDetailsGroup() { + return iuDetailsGroup; + } + + @Override + protected IInstallableUnit getSelectedIU() { + // Not applicable + return null; + } + + @Override + protected Object[] getSelectedElements() { + return new Object[] {};//((ProvisioningOperationWizard) getWizard()).mainPage.getSelectedIUElements(); + } + + public ArrayList<AvailableIUElement> transformIUstoIUElements() { + ArrayList<AvailableIUElement> temp = new ArrayList<AvailableIUElement>(); + ArrayList<String> updateIds = new ArrayList<String>(); + IUElementListRoot root = new IUElementListRoot(); + for (IInstallableUnit addedIU : selectedRemedy.getRequest().getAdditions()) { + AvailableIUElement element = new AvailableIUElement(root, addedIU, getProfileId(), true); + for (IInstallableUnit removedIU : selectedRemedy.getRequest().getRemovals()) { + if (removedIU.getId().equals(addedIU.getId())) { + //if (remediationOperation.isCheckForUpdates()) { + // for (IInstallableUnit originalUpdatedIU : remediationOperation.getOriginalRequest().getAdditions()) { + // int q = addedIU.getVersion().compareTo(originalUpdatedIU.getVersion()); + // + // if (addedIU.getId() == originalUpdatedIU.getId() && addedIU.getVersion().compareTo(originalUpdatedIU.getVersion()) < 0) { + // } + // } + // } + element.setIsUpdate(true); + updateIds.add(addedIU.getId()); + break; + } + } + temp.add(element); + } + for (IInstallableUnit removedIU : selectedRemedy.getRequest().getRemovals()) { + if (!updateIds.contains(removedIU.getId())) { + AvailableIUElement element = new AvailableIUElement(root, removedIU, getProfileId(), false); + element.setIsUninstall(true); + temp.add(element); + } + } + return temp; + } + + private boolean isContraintOK(int btnIndex, boolean value) { + return (checkboxes.get(btnIndex).getSelection() && value) || (!checkboxes.get(btnIndex).getSelection() && !value); + } + + public void refreshResultComposite() { + resultComposite.setVisible(true); + selectedRemedy = null; + if (!checkboxes.get(ALLOWPARTIALINSTALL_INDEX).getSelection() && !checkboxes.get(ALLOWDIFFERENTVERSION_INDEX).getSelection() && !checkboxes.get(ALLOWINSTALLEDUPDATE_INDEX).getSelection() && !checkboxes.get(ALLOWINSTALLEDREMOVAL_INDEX).getSelection()) { + switchResultLayout.topControl = resultErrorComposite; + } else { + RemediationOperation remediationOperation = ((ProvisioningOperationWizard) getWizard()).getRemediationOperation(); + List<Remedy> remedies = remediationOperation.getRemedies(); + for (Iterator<Remedy> iterator = remedies.iterator(); iterator.hasNext();) { + Remedy remedy = iterator.next(); + if (isContraintOK(ALLOWPARTIALINSTALL_INDEX, remedy.getConfig().allowPartialInstall) && isContraintOK(ALLOWDIFFERENTVERSION_INDEX, remedy.getConfig().allowDifferentVersion) && isContraintOK(ALLOWINSTALLEDUPDATE_INDEX, remedy.getConfig().allowInstalledUpdate) && isContraintOK(ALLOWINSTALLEDREMOVAL_INDEX, remedy.getConfig().allowInstalledRemoval)) { + if (remedy.getRequest() != null) { + selectedRemedy = remedy; + break; + } + } + } + if (selectedRemedy == null) { + switchResultLayout.topControl = resultNotFoundComposite; + } else { + input = new IUElementListRoot(); + ArrayList<AvailableIUElement> ius = new ArrayList<AvailableIUElement>(); + ius.addAll(transformIUstoIUElements()); + if (ius.size() == 0) { + switchResultLayout.topControl = resultNotFoundComposite; + selectedRemedy = null; + } else { + input.setChildren(ius.toArray()); + treeViewer.setInput(input); + switchResultLayout.topControl = resultFoundComposite; + } + } + } + setPageComplete(selectedRemedy != null); + resultComposite.layout(); + } + + @Override + protected String getDialogSettingsName() { + // TODO Auto-generated method stub + return null; + } + + @Override + protected SashForm getSashForm() { + // TODO Auto-generated method stub + return null; + } + + @Override + protected int getColumnWidth(int index) { + // TODO Auto-generated method stub + return 0; + } + + @Override + protected String getClipboardText(Control control) { + // TODO Auto-generated method stub + return null; + } + + protected Collection<IInstallableUnit> getIUs() { + return ElementUtils.elementsToIUs(input.getChildren(input)); + } + + public Remedy getSelectedRemedy() { + return selectedRemedy; + } + + public void setSelectedRemedy(Remedy selectedRemedy) { + this.selectedRemedy = selectedRemedy; + } +}
\ No newline at end of file diff --git a/bundles/org.eclipse.equinox.p2.ui/src/org/eclipse/equinox/internal/p2/ui/dialogs/ResolutionResultsWizardPage.java b/bundles/org.eclipse.equinox.p2.ui/src/org/eclipse/equinox/internal/p2/ui/dialogs/ResolutionResultsWizardPage.java index 8f823d698..e0f719e98 100644 --- a/bundles/org.eclipse.equinox.p2.ui/src/org/eclipse/equinox/internal/p2/ui/dialogs/ResolutionResultsWizardPage.java +++ b/bundles/org.eclipse.equinox.p2.ui/src/org/eclipse/equinox/internal/p2/ui/dialogs/ResolutionResultsWizardPage.java @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2007, 2010 IBM Corporation and others. + * Copyright (c) 2007, 2013 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 @@ -9,13 +9,13 @@ * IBM Corporation - initial API and implementation * EclipseSource - ongoing development * Sonatype, Inc. - ongoing development + * Red Hat, Inc. - support for remediation page *******************************************************************************/ package org.eclipse.equinox.internal.p2.ui.dialogs; import java.util.Collection; import org.eclipse.core.runtime.*; import org.eclipse.equinox.internal.p2.ui.ProvUIActivator; -import org.eclipse.equinox.internal.p2.ui.ProvUIMessages; import org.eclipse.equinox.internal.p2.ui.model.*; import org.eclipse.equinox.internal.p2.ui.viewers.*; import org.eclipse.equinox.p2.engine.IProvisioningPlan; @@ -30,8 +30,6 @@ import org.eclipse.jface.operation.IRunnableWithProgress; import org.eclipse.jface.viewers.*; import org.eclipse.swt.SWT; import org.eclipse.swt.custom.SashForm; -import org.eclipse.swt.events.SelectionAdapter; -import org.eclipse.swt.events.SelectionEvent; import org.eclipse.swt.layout.*; import org.eclipse.swt.widgets.*; import org.eclipse.ui.statushandlers.StatusManager; @@ -55,7 +53,6 @@ public abstract class ResolutionResultsWizardPage extends ResolutionStatusPage { protected Display display; private IUDetailsGroup iuDetailsGroup; SashForm sashForm; - Button relaxConstraints; protected ResolutionResultsWizardPage(ProvisioningUI ui, ProvisioningOperationWizard wizard, IUElementListRoot input, ProfileChangeOperation operation) { super("ResolutionPage", ui, wizard); //$NON-NLS-1$ @@ -134,8 +131,6 @@ public abstract class ResolutionResultsWizardPage extends ResolutionStatusPage { GridData gd = new GridData(SWT.FILL, SWT.FILL, true, false); controlsComposite.setLayoutData(gd); - createViewControlsArea(controlsComposite); - final Runnable runnable = new Runnable() { public void run() { treeViewer.addSelectionChangedListener(new ISelectionChangedListener() { @@ -167,31 +162,6 @@ public abstract class ResolutionResultsWizardPage extends ResolutionStatusPage { @Override public void updateStatus(IUElementListRoot newRoot, ProfileChangeOperation op) { super.updateStatus(newRoot, op); - IStatus currentStatus = getProvisioningWizard().getCurrentStatus(); - if (relaxConstraints != null) - relaxConstraints.setEnabled(currentStatus != null && !currentStatus.isOK()); - } - - private void createViewControlsArea(Composite controlsComposite) { - relaxConstraints = new Button(controlsComposite, SWT.CHECK); - relaxConstraints.setVisible(false); - relaxConstraints.setText(ProvUIMessages.ResolutionWizardPage_RelaxedConstraints); - relaxConstraints.setToolTipText(ProvUIMessages.ResolutionWizardPage_RelaxedConstraintsTip); - relaxConstraints.setSelection(((ProvisioningOperationWizard) getWizard()).getRelaxedResoltion()); - relaxConstraints.addSelectionListener(new SelectionAdapter() { - @Override - public void widgetSelected(SelectionEvent e) { - ((ProvisioningOperationWizard) getWizard()).setRelaxedResolution(relaxConstraints.getSelection()); - setPageComplete(true); - } - }); - } - - @Override - public void setVisible(boolean visible) { - super.setVisible(visible); - if (visible) - relaxConstraints.setSelection(((ProvisioningOperationWizard) getWizard()).getRelaxedResoltion()); } protected void createSizingInfo(Composite parent) { diff --git a/bundles/org.eclipse.equinox.p2.ui/src/org/eclipse/equinox/internal/p2/ui/dialogs/SelectableIUsPage.java b/bundles/org.eclipse.equinox.p2.ui/src/org/eclipse/equinox/internal/p2/ui/dialogs/SelectableIUsPage.java index 54536c91d..a2bddf191 100644 --- a/bundles/org.eclipse.equinox.p2.ui/src/org/eclipse/equinox/internal/p2/ui/dialogs/SelectableIUsPage.java +++ b/bundles/org.eclipse.equinox.p2.ui/src/org/eclipse/equinox/internal/p2/ui/dialogs/SelectableIUsPage.java @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2008, 2011 IBM Corporation and others. + * Copyright (c) 2008, 2013 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 @@ -9,11 +9,11 @@ * IBM Corporation - initial API and implementation * EclipseSource - ongoing development * Sonatype, Inc. - ongoing development + * Red Hat, Inc. - support for remediation page *******************************************************************************/ package org.eclipse.equinox.internal.p2.ui.dialogs; import java.util.ArrayList; -import org.eclipse.core.runtime.IStatus; import org.eclipse.equinox.internal.p2.ui.ProvUI; import org.eclipse.equinox.internal.p2.ui.ProvUIMessages; import org.eclipse.equinox.internal.p2.ui.model.*; @@ -28,8 +28,6 @@ import org.eclipse.jface.viewers.*; import org.eclipse.jface.wizard.IWizardPage; import org.eclipse.swt.SWT; import org.eclipse.swt.custom.SashForm; -import org.eclipse.swt.events.SelectionAdapter; -import org.eclipse.swt.events.SelectionEvent; import org.eclipse.swt.layout.*; import org.eclipse.swt.widgets.*; @@ -56,7 +54,6 @@ public class SelectableIUsPage extends ResolutionStatusPage implements IResoluti protected Display display; protected Policy policy; SashForm sashForm; - Button relaxConstraints; public SelectableIUsPage(ProvisioningUI ui, ProvisioningOperationWizard wizard, IUElementListRoot root, Object[] initialSelections) { super("IUSelectionPage", ui, wizard); //$NON-NLS-1$ @@ -163,8 +160,6 @@ public class SelectableIUsPage extends ResolutionStatusPage implements IResoluti GridData gd = new GridData(SWT.FILL, SWT.FILL, true, false); controlsComposite.setLayoutData(gd); - createViewControlsArea(controlsComposite); - // The text area shows a description of the selected IU, or error detail if applicable. iuDetailsGroup = new IUDetailsGroup(sashForm, tableViewer, convertWidthInCharsToPixels(ILayoutConstants.DEFAULT_TABLE_WIDTH), true); @@ -174,36 +169,6 @@ public class SelectableIUsPage extends ResolutionStatusPage implements IResoluti Dialog.applyDialogFont(sashForm); } - @Override - public void updateStatus(IUElementListRoot newRoot, ProfileChangeOperation op) { - super.updateStatus(newRoot, op); - IStatus currentStatus = getProvisioningWizard().getCurrentStatus(); - if (relaxConstraints != null) - relaxConstraints.setEnabled(currentStatus != null && !currentStatus.isOK()); - } - - private void createViewControlsArea(Composite controlsComposite) { - relaxConstraints = new Button(controlsComposite, SWT.CHECK); - relaxConstraints.setVisible(false); - relaxConstraints.setText(ProvUIMessages.ResolutionWizardPage_RelaxedConstraints); - relaxConstraints.setToolTipText(ProvUIMessages.ResolutionWizardPage_RelaxedConstraintsTip); - relaxConstraints.setSelection(((ProvisioningOperationWizard) getWizard()).getRelaxedResoltion()); - relaxConstraints.addSelectionListener(new SelectionAdapter() { - @Override - public void widgetSelected(SelectionEvent e) { - ((ProvisioningOperationWizard) getWizard()).setRelaxedResolution(relaxConstraints.getSelection()); - setPageComplete(true); - } - }); - } - - @Override - public void setVisible(boolean visible) { - super.setVisible(visible); - if (visible) - relaxConstraints.setSelection(((ProvisioningOperationWizard) getWizard()).getRelaxedResoltion()); - } - private void createSelectButtons(Composite parent) { Composite buttonParent = new Composite(parent, SWT.NONE); GridLayout gridLayout = new GridLayout(); diff --git a/bundles/org.eclipse.equinox.p2.ui/src/org/eclipse/equinox/internal/p2/ui/dialogs/UninstallWizard.java b/bundles/org.eclipse.equinox.p2.ui/src/org/eclipse/equinox/internal/p2/ui/dialogs/UninstallWizard.java index db6f78bcd..9743c96f8 100644 --- a/bundles/org.eclipse.equinox.p2.ui/src/org/eclipse/equinox/internal/p2/ui/dialogs/UninstallWizard.java +++ b/bundles/org.eclipse.equinox.p2.ui/src/org/eclipse/equinox/internal/p2/ui/dialogs/UninstallWizard.java @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2007, 2010 IBM Corporation and others. + * Copyright (c) 2007, 2013 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 @@ -8,6 +8,7 @@ * Contributors: * IBM Corporation - initial API and implementation * Sonatype, Inc. - ongoing development + * Red Hat, Inc. - support for remediation page *******************************************************************************/ package org.eclipse.equinox.internal.p2.ui.dialogs; @@ -88,4 +89,11 @@ public class UninstallWizard extends ProvisioningOperationWizard { op.setProvisioningContext(getProvisioningContext()); return op; } + + @Override + protected RemediationPage createRemediationPage() { + // TODO Auto-generated method stub + return null; + } + } diff --git a/bundles/org.eclipse.equinox.p2.ui/src/org/eclipse/equinox/internal/p2/ui/dialogs/UpdateWizard.java b/bundles/org.eclipse.equinox.p2.ui/src/org/eclipse/equinox/internal/p2/ui/dialogs/UpdateWizard.java index 47281e679..835b48258 100644 --- a/bundles/org.eclipse.equinox.p2.ui/src/org/eclipse/equinox/internal/p2/ui/dialogs/UpdateWizard.java +++ b/bundles/org.eclipse.equinox.p2.ui/src/org/eclipse/equinox/internal/p2/ui/dialogs/UpdateWizard.java @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2007, 2011 IBM Corporation and others. + * Copyright (c) 2007, 2013 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 @@ -9,6 +9,7 @@ * IBM Corporation - initial API and implementation * Genuitec, LLC - added license support * Sonatype, Inc. - ongoing development + * Red Hat, Inc. - support for remediation page *******************************************************************************/ package org.eclipse.equinox.internal.p2.ui.dialogs; @@ -16,8 +17,7 @@ import java.util.*; import org.eclipse.core.runtime.Assert; import org.eclipse.equinox.internal.p2.ui.ProvUIImages; import org.eclipse.equinox.internal.p2.ui.ProvUIMessages; -import org.eclipse.equinox.internal.p2.ui.model.AvailableUpdateElement; -import org.eclipse.equinox.internal.p2.ui.model.IUElementListRoot; +import org.eclipse.equinox.internal.p2.ui.model.*; import org.eclipse.equinox.p2.engine.IProfile; import org.eclipse.equinox.p2.engine.IProfileRegistry; import org.eclipse.equinox.p2.metadata.IInstallableUnit; @@ -114,24 +114,32 @@ public class UpdateWizard extends WizardWithLicenses { } protected void initializeResolutionModelElements(Object[] selectedElements) { + if (selectedElements == null) + return; root = new IUElementListRoot(); - ArrayList<AvailableUpdateElement> list = new ArrayList<AvailableUpdateElement>(selectedElements.length); - ArrayList<AvailableUpdateElement> selected = new ArrayList<AvailableUpdateElement>(selectedElements.length); - for (int i = 0; i < selectedElements.length; i++) { - if (selectedElements[i] instanceof AvailableUpdateElement) { - AvailableUpdateElement element = (AvailableUpdateElement) selectedElements[i]; - AvailableUpdateElement newElement = new AvailableUpdateElement(root, element.getIU(), element.getIUToBeUpdated(), getProfileId(), shouldShowProvisioningPlanChildren()); - list.add(newElement); - selected.add(newElement); - } else if (selectedElements[i] instanceof Update) { - Update update = (Update) selectedElements[i]; - AvailableUpdateElement newElement = new AvailableUpdateElement(root, update.replacement, update.toUpdate, getProfileId(), shouldShowProvisioningPlanChildren()); - list.add(newElement); - selected.add(newElement); + if (operation instanceof RemediationOperation) { + ArrayList<AvailableIUElement> list = remediationPage.transformIUstoIUElements(); + root.setChildren(list.toArray()); + planSelections = list.toArray(); + } else { + ArrayList<AvailableUpdateElement> list = new ArrayList<AvailableUpdateElement>(selectedElements.length); + ArrayList<AvailableUpdateElement> selected = new ArrayList<AvailableUpdateElement>(selectedElements.length); + for (int i = 0; i < selectedElements.length; i++) { + if (selectedElements[i] instanceof AvailableUpdateElement) { + AvailableUpdateElement element = (AvailableUpdateElement) selectedElements[i]; + AvailableUpdateElement newElement = new AvailableUpdateElement(root, element.getIU(), element.getIUToBeUpdated(), getProfileId(), shouldShowProvisioningPlanChildren()); + list.add(newElement); + selected.add(newElement); + } else if (selectedElements[i] instanceof Update) { + Update update = (Update) selectedElements[i]; + AvailableUpdateElement newElement = new AvailableUpdateElement(root, update.replacement, update.toUpdate, getProfileId(), shouldShowProvisioningPlanChildren()); + list.add(newElement); + selected.add(newElement); + } } + root.setChildren(list.toArray()); + planSelections = selected.toArray(); } - root.setChildren(list.toArray()); - planSelections = selected.toArray(); } protected IResolutionErrorReportingPage createErrorReportingPage() { @@ -186,4 +194,11 @@ public class UpdateWizard extends WizardWithLicenses { } return firstPageRoot; } + + @Override + protected RemediationPage createRemediationPage() { + remediationPage = new RemediationPage(ui, this, root, operation); + return remediationPage; + } + } diff --git a/bundles/org.eclipse.equinox.p2.ui/src/org/eclipse/equinox/internal/p2/ui/messages.properties b/bundles/org.eclipse.equinox.p2.ui/src/org/eclipse/equinox/internal/p2/ui/messages.properties index fc021bf52..79843cc58 100644 --- a/bundles/org.eclipse.equinox.p2.ui/src/org/eclipse/equinox/internal/p2/ui/messages.properties +++ b/bundles/org.eclipse.equinox.p2.ui/src/org/eclipse/equinox/internal/p2/ui/messages.properties @@ -214,6 +214,22 @@ AvailableIUsPage_ShowLatestVersions=Show only the &latest versions of available AvailableIUsPage_SingleSelectionCount={0} item selected AvailableIUsPage_Title=Available Software AvailableIUWrapper_AllAreInstalled=All items are installed +InstallRemediationPage_Title=Install Remediation Page +InstallRemediationPage_Description=The installation cannot be completed as requested. +UpdateRemediationPage_Title=Update Remediation Page +UpdateRemediationPage_Description=The update cannot be completed as requested. +RemediationPage_SubDescription=Chose one of the following alternate solutions: +RemediationPage_NoSolutionFound=No solution found. +RemediationPage_BeingInstalledSection=How do you want to change the software being installed? +RemediationPage_InstalledSection=How do you want to alter the current installation? +RemediationPage_BeingInstalledSection_AllowPartialInstall=install less item than originally requested +RemediationPage_BeingInstalledSection_AllowDifferentVersion=install different version than originally requested +RemediationPage_InstalledSection_AllowInstalledUpdate=update items already installed +RemediationPage_InstalledSection_AllowInstalledRemoval=remove items already installed +RemediationPage_BestSolutionBeingInstalledRelaxed=Keep my installation the same and modify the items being installed to be compatible +RemediationPage_BestSolutionInstallationRelaxed=Update my installation to handle the items being installed +RemediationPage_BestSolutionBuilt=Build my own solution + IUViewQueryContext_AllAreInstalledDescription=You can uncheck the 'Hide items that are already installed' check box to see all items. Label_Profiles=All Software Profiles Label_Repositories=Known Repositories diff --git a/bundles/org.eclipse.equinox.p2.ui/src/org/eclipse/equinox/internal/p2/ui/model/AvailableIUElement.java b/bundles/org.eclipse.equinox.p2.ui/src/org/eclipse/equinox/internal/p2/ui/model/AvailableIUElement.java index 4cb5b9dcf..21499f3f6 100644 --- a/bundles/org.eclipse.equinox.p2.ui/src/org/eclipse/equinox/internal/p2/ui/model/AvailableIUElement.java +++ b/bundles/org.eclipse.equinox.p2.ui/src/org/eclipse/equinox/internal/p2/ui/model/AvailableIUElement.java @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2007, 2011 IBM Corporation and others. + * Copyright (c) 2007, 2013 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 @@ -8,6 +8,7 @@ * Contributors: * IBM Corporation - initial API and implementation * Sonatype, Inc. - ongoing development + * Red Hat, Inc. - support for remediation page *******************************************************************************/ package org.eclipse.equinox.internal.p2.ui.model; @@ -38,6 +39,7 @@ public class AvailableIUElement extends QueriedElement implements IIUElement { boolean isInstalled = false; boolean isUpdate = false; boolean isPatch = false; + boolean isUninstall = false; // Currently this variable is not settable due to the // poor performance of sizing, but it is kept here for future improvement. @@ -68,6 +70,8 @@ public class AvailableIUElement extends QueriedElement implements IIUElement { return isInstalled ? ProvUIImages.IMG_DISABLED_PATCH_IU : ProvUIImages.IMG_PATCH_IU; else if (isInstalled) return ProvUIImages.IMG_DISABLED_IU; + else if (isUninstall) + return ProvUIImages.IMG_REMOVED_IU; return ProvUIImages.IMG_IU; } @@ -199,6 +203,14 @@ public class AvailableIUElement extends QueriedElement implements IIUElement { return isUpdate; } + public void setIsUninstall(boolean isUninstall) { + this.isUninstall = isUninstall; + } + + public boolean isUninstall() { + return isUninstall; + } + public void setIsPatch(boolean isPatch) { this.isPatch = isPatch; } diff --git a/bundles/org.eclipse.equinox.p2.ui/src/org/eclipse/equinox/p2/ui/ProvisioningUI.java b/bundles/org.eclipse.equinox.p2.ui/src/org/eclipse/equinox/p2/ui/ProvisioningUI.java index bccd352c8..6b05df26b 100644 --- a/bundles/org.eclipse.equinox.p2.ui/src/org/eclipse/equinox/p2/ui/ProvisioningUI.java +++ b/bundles/org.eclipse.equinox.p2.ui/src/org/eclipse/equinox/p2/ui/ProvisioningUI.java @@ -133,16 +133,6 @@ public class ProvisioningUI { } /** - * @since 2.2 - */ - public RelaxedUpdateInstallOperation getRelaxedUpdateOperation(ProvisioningContext context) { - RelaxedUpdateInstallOperation luckyOperation = new RelaxedUpdateInstallOperation(getSession()); - luckyOperation.setProfileId(getProfileId()); - luckyOperation.setProvisioningContext(context); - return luckyOperation; - } - - /** * Return an install operation that describes installing the specified IInstallableUnits from the * provided list of repositories. * @@ -237,6 +227,23 @@ public class ProvisioningUI { * @return the wizard return code */ public int openUpdateWizard(boolean skipSelectionsPage, UpdateOperation operation, LoadMetadataRepositoryJob job) { + return openUpdateWizard(skipSelectionsPage, operation, null, job); + + } + + /** + * Open an update wizard for the specified update operation and remediationOperation. + * + * @param skipSelectionsPage <code>true</code> if the selection page should be skipped so that the user is + * viewing the resolution results. <code>false</code> if the update selection page should be shown first. + * @param operation the operation describing the proposed update. Must not be <code>null</code>. + * @param remediationOperation the alternate operations if the proposed update failed. May be <code>null</code>. + * @param job a repository load job that is loading or has already loaded the repositories. Can be used to pass along + * an in-memory repository reference to the wizard. + * + * @return the wizard return code + */ + public int openUpdateWizard(boolean skipSelectionsPage, UpdateOperation operation, RemediationOperation remediationOperation, LoadMetadataRepositoryJob job) { if (getPolicy().getUpdateWizardStyle() == Policy.UPDATE_STYLE_SINGLE_IUS && UpdateSingleIUWizard.validFor(operation)) { UpdateSingleIUWizard wizard = new UpdateSingleIUWizard(this, operation); WizardDialog dialog = new WizardDialog(ProvUI.getDefaultParentShell(), wizard); @@ -245,6 +252,7 @@ public class ProvisioningUI { return dialog.open(); } UpdateWizard wizard = new UpdateWizard(this, operation, operation.getSelectedUpdates(), job); + wizard.setRemediationOperation(remediationOperation); wizard.setSkipSelectionsPage(skipSelectionsPage); WizardDialog dialog = new ProvisioningWizardDialog(ProvUI.getDefaultParentShell(), wizard); dialog.create(); |