| /******************************************************************************* |
| * Copyright (c) 2007, 2008 IBM Corporation and others. |
| * All rights reserved. This program and the accompanying materials |
| * are made available under the terms of the Eclipse Public License v1.0 |
| * which accompanies this distribution, and is available at |
| * http://www.eclipse.org/legal/epl-v10.html |
| * |
| * Contributors: |
| * IBM Corporation - initial API and implementation |
| *******************************************************************************/ |
| package org.eclipse.equinox.p2.engine; |
| |
| import java.util.*; |
| import java.util.Map.Entry; |
| import org.eclipse.core.runtime.*; |
| import org.eclipse.equinox.internal.p2.engine.*; |
| import org.eclipse.equinox.p2.metadata.*; |
| import org.eclipse.osgi.util.NLS; |
| |
| public abstract class Phase { |
| protected static final String PARM_OPERAND = "operand"; //$NON-NLS-1$ |
| protected static final String PARM_TOUCHPOINT = "touchpoint"; //$NON-NLS-1$ |
| protected static final String PARM_PHASE_ID = "phaseId"; //$NON-NLS-1$ |
| protected static final String PARM_PROFILE = "profile"; //$NON-NLS-1$ |
| protected static final String PARM_ARTIFACT_REQUESTS = "artifactRequests"; //$NON-NLS-1$ |
| protected static final String PARM_ARTIFACT = "artifact"; //$NON-NLS-1$ |
| protected static final String PARM_IU = "iu"; //$NON-NLS-1$ |
| |
| protected final String phaseId; |
| protected final int weight; |
| protected int prePerformWork = 1000; |
| protected int mainPerformWork = 10000; |
| protected int postPerformWork = 1000; |
| private Map phaseParameters; |
| private Map touchpointToTouchpointParameters; |
| |
| protected Phase(String phaseId, int weight) { |
| if (phaseId == null || phaseId.length() == 0) |
| throw new IllegalArgumentException("Phase id must be set."); //$NON-NLS-1$ |
| if (weight <= 0) |
| throw new IllegalArgumentException("Phase weight must be positive."); //$NON-NLS-1$ |
| this.weight = weight; |
| this.phaseId = phaseId; |
| } |
| |
| public String toString() { |
| return getClass().getName() + " - " + this.weight; //$NON-NLS-1$ |
| } |
| |
| public final MultiStatus perform(EngineSession session, Profile profile, Operand[] operands, IProgressMonitor monitor) { |
| MultiStatus status = new MultiStatus(EngineActivator.ID, IStatus.OK, null, null); |
| perform(status, session, profile, operands, monitor); |
| if (status.matches(IStatus.CANCEL)) { |
| MultiStatus result = new MultiStatus(EngineActivator.ID, IStatus.CANCEL, Messages.Engine_Operation_Canceled_By_User, null); |
| result.merge(status); |
| return result; |
| } else if (status.matches(IStatus.ERROR)) { |
| MultiStatus result = new MultiStatus(EngineActivator.ID, IStatus.CANCEL, getProblemMessage(), null); |
| result.merge(status); |
| return result; |
| } |
| return status; |
| } |
| |
| void perform(MultiStatus status, EngineSession session, Profile profile, Operand[] operands, IProgressMonitor monitor) { |
| touchpointToTouchpointParameters = new HashMap(); |
| for (int i = 0; i < operands.length; i++) { |
| TouchpointType type = getTouchpointType(operands[i]); |
| Touchpoint touchpoint = TouchpointManager.getInstance().getTouchpoint(type); |
| //abort the entire phase if any required touchpoint is missing |
| if (touchpoint == null) { |
| status.add(new Status(IStatus.ERROR, EngineActivator.ID, NLS.bind(Messages.TouchpointManager_Required_Touchpoint_Not_Found, type), null)); |
| return; |
| } |
| if (!touchpointToTouchpointParameters.containsKey(touchpoint)) { |
| touchpointToTouchpointParameters.put(touchpoint, null); |
| } |
| } |
| |
| SubMonitor subMonitor = SubMonitor.convert(monitor, prePerformWork + mainPerformWork + postPerformWork); |
| prePerform(status, profile, subMonitor.newChild(prePerformWork)); |
| if (status.matches(IStatus.ERROR | IStatus.CANCEL)) |
| return; |
| session.recordPhaseStart(this); |
| |
| subMonitor.setWorkRemaining(mainPerformWork + postPerformWork); |
| mainPerform(status, session, profile, operands, subMonitor.newChild(mainPerformWork)); |
| if (status.matches(IStatus.ERROR | IStatus.CANCEL)) |
| return; |
| |
| session.recordPhaseEnd(this); |
| subMonitor.setWorkRemaining(postPerformWork); |
| postPerform(status, profile, subMonitor.newChild(postPerformWork)); |
| if (status.matches(IStatus.ERROR | IStatus.CANCEL)) |
| return; |
| |
| subMonitor.done(); |
| } |
| |
| void prePerform(MultiStatus status, Profile profile, IProgressMonitor monitor) { |
| phaseParameters = new HashMap(); |
| phaseParameters.put(PARM_PROFILE, profile); |
| phaseParameters.put(PARM_PHASE_ID, phaseId); |
| mergeStatus(status, initializePhase(monitor, profile, phaseParameters)); |
| |
| for (Iterator it = touchpointToTouchpointParameters.entrySet().iterator(); it.hasNext();) { |
| Entry entry = (Entry) it.next(); |
| Touchpoint touchpoint = (Touchpoint) entry.getKey(); |
| Map touchpointParameters = new HashMap(phaseParameters); |
| touchpointParameters.put(PARM_TOUCHPOINT, touchpoint); |
| mergeStatus(status, touchpoint.initializePhase(monitor, profile, phaseId, touchpointParameters)); |
| entry.setValue(touchpointParameters); |
| } |
| } |
| |
| private void mainPerform(MultiStatus status, EngineSession session, Profile profile, Operand[] operands, SubMonitor subMonitor) { |
| subMonitor.beginTask("", operands.length); //$NON-NLS-1$ |
| for (int i = 0; i < operands.length; i++) { |
| subMonitor.setWorkRemaining(operands.length - i); |
| if (subMonitor.isCanceled()) |
| throw new OperationCanceledException(); |
| Operand operand = operands[i]; |
| if (!isApplicable(operand)) |
| continue; |
| |
| ProvisioningAction[] actions; |
| try { |
| actions = getActions(operand); |
| } catch (Throwable t) { |
| //TODO Should never catch throwable. Use SafeRunner if calling third party code |
| status.add(new Status(IStatus.ERROR, phaseId, t.getMessage(), t)); |
| return; |
| } |
| |
| Touchpoint touchpoint = getTouchpoint(operand); |
| Map parameters = (touchpoint != null) ? new HashMap((Map) touchpointToTouchpointParameters.get(touchpoint)) : new HashMap(phaseParameters); |
| parameters.put(PARM_OPERAND, operand); |
| mergeStatus(status, initializeOperand(profile, operand, parameters, subMonitor)); |
| if (touchpoint != null) |
| mergeStatus(status, touchpoint.initializeOperand(profile, phaseId, operand, parameters)); |
| parameters = Collections.unmodifiableMap(parameters); |
| if (actions != null) { |
| for (int j = 0; j < actions.length; j++) { |
| ProvisioningAction action = actions[j]; |
| session.recordAction(action, operand); |
| mergeStatus(status, action.execute(parameters)); |
| if (!status.isOK()) |
| return; |
| } |
| } |
| if (touchpoint != null) |
| mergeStatus(status, touchpoint.completeOperand(profile, phaseId, operand, parameters)); |
| mergeStatus(status, completeOperand(operand, parameters)); |
| subMonitor.worked(1); |
| } |
| } |
| |
| /** |
| * Merges a given IStatus into a MultiStatus |
| */ |
| private void mergeStatus(MultiStatus multi, IStatus status) { |
| if (status != null && !status.isOK()) |
| multi.add(status); |
| } |
| |
| void postPerform(MultiStatus status, Profile profile, IProgressMonitor monitor) { |
| for (Iterator it = touchpointToTouchpointParameters.entrySet().iterator(); it.hasNext();) { |
| Entry entry = (Entry) it.next(); |
| Touchpoint touchpoint = (Touchpoint) entry.getKey(); |
| Map touchpointParameters = (Map) entry.getValue(); |
| mergeStatus(status, touchpoint.completePhase(monitor, profile, phaseId, touchpointParameters)); |
| entry.setValue(null); |
| } |
| mergeStatus(status, completePhase(monitor, profile, phaseParameters)); |
| phaseParameters = null; |
| } |
| |
| void undo(MultiStatus status, EngineSession session, Profile profile, Operand operand, ProvisioningAction[] actions) { |
| Touchpoint touchpoint = getTouchpoint(operand); |
| Map touchpointParameters = (Map) touchpointToTouchpointParameters.get(touchpoint); |
| Map parameters = new HashMap(touchpointParameters); |
| parameters.put(PARM_OPERAND, operand); |
| mergeStatus(status, initializeOperand(profile, operand, parameters, new NullProgressMonitor())); |
| mergeStatus(status, touchpoint.initializeOperand(profile, phaseId, operand, parameters)); |
| parameters = Collections.unmodifiableMap(parameters); |
| for (int j = 0; j < actions.length; j++) { |
| ProvisioningAction action = actions[j]; |
| mergeStatus(status, action.undo(parameters)); |
| // TODO: session.removeAction(...) |
| } |
| mergeStatus(status, touchpoint.completeOperand(profile, phaseId, operand, parameters)); |
| mergeStatus(status, completeOperand(operand, parameters)); |
| } |
| |
| protected final ProvisioningAction[] getActions(IInstallableUnit unit, String key) { |
| String[] instructions = getInstructionsFor(unit, key); |
| if (instructions == null || instructions.length == 0) |
| return null; |
| Touchpoint touchpoint = getTouchpoint(unit); |
| //TODO Likely need to propagate an exception if the touchpoint is not present |
| if (touchpoint == null) |
| return null; |
| InstructionParser parser = new InstructionParser(this, touchpoint); |
| return parser.parseActions(instructions[0]); |
| } |
| |
| protected boolean isApplicable(Operand op) { |
| return true; |
| } |
| |
| protected IStatus initializePhase(IProgressMonitor monitor, Profile profile, Map parameters) { |
| return Status.OK_STATUS; |
| } |
| |
| protected IStatus completePhase(IProgressMonitor monitor, Profile profile, Map parameters) { |
| return Status.OK_STATUS; |
| } |
| |
| protected IStatus completeOperand(Operand operand, Map parameters) { |
| return Status.OK_STATUS; |
| } |
| |
| protected IStatus initializeOperand(Profile profile, Operand operand, Map parameters, IProgressMonitor monitor) { |
| return Status.OK_STATUS; |
| } |
| |
| public ProvisioningAction getAction(String actionId) { |
| return null; |
| } |
| |
| protected abstract ProvisioningAction[] getActions(Operand currentOperand); |
| |
| /** |
| * Returns a human-readable message to be displayed in case of an error performing |
| * this phase. Subclasses should override. |
| */ |
| protected String getProblemMessage() { |
| return Messages.Phase_Error; |
| } |
| |
| /** |
| * Returns the touchpoint corresponding to the operand, or null if no corresponding |
| * touchpoint is available. |
| */ |
| protected static Touchpoint getTouchpoint(Operand operand) { |
| return TouchpointManager.getInstance().getTouchpoint(getTouchpointType(operand)); |
| } |
| |
| /** |
| * Returns the touchpoint type corresponding to the operand. Never returns null. |
| */ |
| protected static TouchpointType getTouchpointType(Operand operand) { |
| IInstallableUnit unit = operand.second(); |
| if (unit == null) |
| unit = operand.first(); |
| return unit.getTouchpointType(); |
| } |
| |
| private static Touchpoint getTouchpoint(IInstallableUnit unit) { |
| return TouchpointManager.getInstance().getTouchpoint(unit.getTouchpointType()); |
| } |
| |
| private static String[] getInstructionsFor(IInstallableUnit unit, String key) { |
| TouchpointData[] data = unit.getTouchpointData(); |
| if (data == null) |
| return null; |
| |
| String[] matches = new String[data.length]; |
| int count = 0; |
| for (int i = 0; i < data.length; i++) { |
| matches[count] = data[i].getInstructions(key); |
| if (matches[count] != null) |
| count++; |
| } |
| if (count == data.length) |
| return matches; |
| String[] result = new String[count]; |
| System.arraycopy(matches, 0, result, 0, count); |
| return result; |
| } |
| } |