1 /******************************************************************************* 2 * Copyright (c) 2007, 2017 IBM Corporation and others. 3 * 4 * This program and the accompanying materials 5 * are made available under the terms of the Eclipse Public License 2.0 6 * which accompanies this distribution, and is available at 7 * https://www.eclipse.org/legal/epl-2.0/ 8 * 9 * SPDX-License-Identifier: EPL-2.0 10 * 11 * Contributors: 12 * IBM Corporation - initial API and implementation 13 *******************************************************************************/ 14 package org.eclipse.equinox.internal.p2.engine; 15 16 import java.util.*; 17 import org.eclipse.core.runtime.*; 18 import org.eclipse.equinox.p2.engine.*; 19 import org.eclipse.equinox.p2.engine.spi.ProvisioningAction; 20 import org.eclipse.osgi.util.NLS; 21 22 public class PhaseSet implements IPhaseSet { 23 24 private final Phase[] phases; 25 private boolean isRunning = false; 26 private boolean isPaused = false; 27 PhaseSet(Phase[] phases)28 public PhaseSet(Phase[] phases) { 29 if (phases == null) 30 throw new IllegalArgumentException(Messages.null_phases); 31 32 this.phases = phases; 33 } 34 perform(EngineSession session, Operand[] operands, IProgressMonitor monitor)35 public final MultiStatus perform(EngineSession session, Operand[] operands, IProgressMonitor monitor) { 36 MultiStatus status = new MultiStatus(EngineActivator.ID, IStatus.OK, null, null); 37 int[] weights = getProgressWeights(operands); 38 int totalWork = getTotalWork(weights); 39 SubMonitor pm = SubMonitor.convert(monitor, totalWork); 40 try { 41 isRunning = true; 42 for (int i = 0; i < phases.length; i++) { 43 if (pm.isCanceled()) { 44 status.add(Status.CANCEL_STATUS); 45 return status; 46 } 47 Phase phase = phases[i]; 48 phase.actionManager = session.getAgent().getService(ActionManager.class); 49 try { 50 phase.perform(status, session, operands, pm.newChild(weights[i])); 51 } catch (OperationCanceledException e) { 52 // propagate operation cancellation 53 status.add(new Status(IStatus.CANCEL, EngineActivator.ID, e.getMessage(), e)); 54 } catch (RuntimeException e) { 55 // "perform" calls user code and might throw an unchecked exception 56 // we catch the error here to gather information on where the problem occurred. 57 status.add(new Status(IStatus.ERROR, EngineActivator.ID, e.getMessage(), e)); 58 } catch (LinkageError e) { 59 // Catch linkage errors as these are generally recoverable but let other Errors propagate (see bug 222001) 60 status.add(new Status(IStatus.ERROR, EngineActivator.ID, e.getMessage(), e)); 61 } finally { 62 phase.actionManager = null; 63 } 64 if (status.matches(IStatus.CANCEL)) { 65 MultiStatus result = new MultiStatus(EngineActivator.ID, IStatus.CANCEL, Messages.Engine_Operation_Canceled_By_User, null); 66 result.merge(status); 67 return result; 68 } else if (status.matches(IStatus.ERROR)) { 69 MultiStatus result = new MultiStatus(EngineActivator.ID, IStatus.ERROR, phase.getProblemMessage(), null); 70 result.add(new Status(IStatus.ERROR, EngineActivator.ID, session.getContextString(), null)); 71 result.merge(status); 72 return result; 73 } 74 } 75 } finally { 76 pm.done(); 77 isRunning = false; 78 } 79 return status; 80 } 81 pause()82 public synchronized boolean pause() { 83 if (isRunning && !isPaused) { 84 isPaused = true; 85 for (Phase phase : phases) { 86 phase.setPaused(isPaused); 87 } 88 return true; 89 } 90 return false; 91 } 92 resume()93 public synchronized boolean resume() { 94 if (isRunning && isPaused) { 95 isPaused = false; 96 for (Phase phase : phases) { 97 phase.setPaused(isPaused); 98 } 99 return true; 100 } 101 return false; 102 } 103 validate(ActionManager actionManager, IProfile profile, Operand[] operands, ProvisioningContext context, IProgressMonitor monitor)104 public final IStatus validate(ActionManager actionManager, IProfile profile, Operand[] operands, ProvisioningContext context, IProgressMonitor monitor) { 105 Set<MissingAction> missingActions = new HashSet<>(); 106 for (Phase phase2 : phases) { 107 Phase phase = phase2; 108 phase.actionManager = actionManager; 109 try { 110 for (Operand operand : operands) { 111 try { 112 if (!phase.isApplicable(operand)) 113 continue; 114 115 List<ProvisioningAction> actions = phase.getActions(operand); 116 if (actions == null) 117 continue; 118 for (ProvisioningAction action : actions) { 119 if (action instanceof MissingAction) 120 missingActions.add((MissingAction) action); 121 } 122 } catch (RuntimeException e) { 123 // "perform" calls user code and might throw an unchecked exception 124 // we catch the error here to gather information on where the problem occurred. 125 return new Status(IStatus.ERROR, EngineActivator.ID, e.getMessage() + " " + getContextString(profile, phase, operand), e); //$NON-NLS-1$ 126 } catch (LinkageError e) { 127 // Catch linkage errors as these are generally recoverable but let other Errors propagate (see bug 222001) 128 return new Status(IStatus.ERROR, EngineActivator.ID, e.getMessage() + " " + getContextString(profile, phase, operand), e); //$NON-NLS-1$ 129 } 130 } 131 } finally { 132 phase.actionManager = null; 133 } 134 } 135 if (!missingActions.isEmpty()) { 136 MissingAction[] missingActionsArray = missingActions.toArray(new MissingAction[missingActions.size()]); 137 MissingActionsException exception = new MissingActionsException(missingActionsArray); 138 return (new Status(IStatus.ERROR, EngineActivator.ID, exception.getMessage(), exception)); 139 } 140 return Status.OK_STATUS; 141 } 142 getContextString(IProfile profile, Phase phase, Operand operand)143 private String getContextString(IProfile profile, Phase phase, Operand operand) { 144 return NLS.bind(Messages.session_context, new Object[] {profile.getProfileId(), phase.getClass().getName(), operand.toString(), ""}); //$NON-NLS-1$ 145 } 146 getTotalWork(int[] weights)147 private int getTotalWork(int[] weights) { 148 int sum = 0; 149 for (int weight : weights) 150 sum += weight; 151 return sum; 152 } 153 getProgressWeights(Operand[] operands)154 private int[] getProgressWeights(Operand[] operands) { 155 int[] weights = new int[phases.length]; 156 for (int i = 0; i < phases.length; i += 1) { 157 if (operands.length > 0) 158 //alter weights according to the number of operands applicable to that phase 159 weights[i] = (phases[i].weight * countApplicable(phases[i], operands) / operands.length); 160 else 161 weights[i] = phases[i].weight; 162 } 163 return weights; 164 } 165 countApplicable(Phase phase, Operand[] operands)166 private int countApplicable(Phase phase, Operand[] operands) { 167 int count = 0; 168 for (Operand operand : operands) { 169 if (phase.isApplicable(operand)) 170 count++; 171 } 172 return count; 173 } 174 175 @Override getPhaseIds()176 public String[] getPhaseIds() { 177 String[] ids = new String[phases.length]; 178 for (int i = 0; i < ids.length; i++) { 179 ids[i] = phases[i].phaseId; 180 } 181 return ids; 182 } 183 getPhases()184 public Phase[] getPhases() { 185 return phases; 186 } 187 } 188