/******************************************************************************* * Copyright (c) 2007, 2017 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 * Landmark Graphics Corporation - bug 397183 *******************************************************************************/ package org.eclipse.equinox.internal.p2.engine; import java.util.*; import org.eclipse.core.runtime.*; import org.eclipse.equinox.internal.p2.core.helpers.LogHelper; import org.eclipse.equinox.internal.provisional.p2.core.eventbus.IProvisioningEventBus; import org.eclipse.equinox.p2.engine.IProfile; import org.eclipse.equinox.p2.engine.ProvisioningContext; import org.eclipse.equinox.p2.engine.spi.ProvisioningAction; import org.eclipse.equinox.p2.engine.spi.Touchpoint; 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_PHASE_ID = "phaseId"; //$NON-NLS-1$ protected static final String PARM_PROFILE = "profile"; //$NON-NLS-1$ protected static final String PARM_PROFILE_DATA_DIRECTORY = "profileDataDirectory"; //$NON-NLS-1$ protected static final String PARM_CONTEXT = "context"; //$NON-NLS-1$ /** * Internal property. */ protected static final String PARM_AGENT = "agent"; //$NON-NLS-1$ protected static final String PARM_FORCED = "forced"; //$NON-NLS-1$ protected static final String PARM_TOUCHPOINT = "touchpoint"; //$NON-NLS-1$ protected static final String LAST_RESULT_INTERNAL_NAME = "_p2_internal_last_result_variable_"; //$NON-NLS-1$ // protected static final String LAST_RESULT_PUBLIC_NAME = "lastResult"; //$NON-NLS-1$ protected final String phaseId; protected final int weight; protected final boolean forced; protected int prePerformWork = 1000; protected int mainPerformWork = 10000; protected int postPerformWork = 1000; private Map<String, Object> operandParameters = null; private Map<String, Object> phaseParameters = new HashMap<>(); private Map<Touchpoint, Map<String, Object>> touchpointToTouchpointPhaseParameters = new HashMap<>(); private Map<Touchpoint, Map<String, Object>> touchpointToTouchpointOperandParameters = new HashMap<>(); ActionManager actionManager; // injected from phaseset protected boolean isPaused = false; protected Phase(String phaseId, int weight, boolean forced) { if (phaseId == null || phaseId.length() == 0) throw new IllegalArgumentException(Messages.phaseid_not_set); if (weight <= 0) throw new IllegalArgumentException(Messages.phaseid_not_positive); this.weight = weight; this.phaseId = phaseId; this.forced = forced; } protected Phase(String phaseId, int weight) { this(phaseId, weight, false); } final protected ActionManager getActionManager() { return actionManager; } @Override public String toString() { return getClass().getName() + " - " + this.weight; //$NON-NLS-1$ } private void broadcastPhaseEvent(EngineSession session, Operand[] operands, int type) { IProvisioningEventBus bus = (IProvisioningEventBus) session.getAgent().getService(IProvisioningEventBus.SERVICE_NAME); if (bus != null) { bus.publishEvent(getPhaseEvent(operands, type)); } } protected PhaseEvent getPhaseEvent(final Operand[] operands, int type) { return new PhaseEvent(phaseId, operands, type); } void perform(MultiStatus status, EngineSession session, Operand[] operands, IProgressMonitor monitor) { SubMonitor subMonitor = SubMonitor.convert(monitor, prePerformWork + mainPerformWork + postPerformWork); session.recordPhaseEnter(this); broadcastPhaseEvent(session, operands, PhaseEvent.TYPE_START); prePerform(status, session, subMonitor.newChild(prePerformWork)); if (status.matches(IStatus.ERROR | IStatus.CANCEL)) return; session.recordPhaseStart(this); subMonitor.setWorkRemaining(mainPerformWork + postPerformWork); mainPerform(status, session, operands, subMonitor.newChild(mainPerformWork)); if (status.matches(IStatus.ERROR | IStatus.CANCEL)) return; session.recordPhaseEnd(this); subMonitor.setWorkRemaining(postPerformWork); postPerform(status, session, subMonitor.newChild(postPerformWork)); phaseParameters.clear(); if (status.matches(IStatus.ERROR | IStatus.CANCEL)) return; broadcastPhaseEvent(session, operands, PhaseEvent.TYPE_END); session.recordPhaseExit(this); subMonitor.done(); } void prePerform(MultiStatus status, EngineSession session, IProgressMonitor monitor) { IProfile profile = session.getProfile(); phaseParameters.put(PARM_PROFILE, profile); phaseParameters.put(PARM_PROFILE_DATA_DIRECTORY, session.getProfileDataDirectory()); phaseParameters.put(PARM_CONTEXT, session.getProvisioningContext()); phaseParameters.put(PARM_PHASE_ID, phaseId); phaseParameters.put(PARM_FORCED, Boolean.toString(forced)); phaseParameters.put(PARM_AGENT, session.getAgent()); mergeStatus(status, initializePhase(monitor, profile, phaseParameters)); } private void mainPerform(MultiStatus status, EngineSession session, Operand[] operands, SubMonitor subMonitor) { IProfile profile = session.getProfile(); subMonitor.beginTask(null, operands.length); for (int i = 0; i < operands.length; i++) { subMonitor.setWorkRemaining(operands.length - i); if (subMonitor.isCanceled()) throw new OperationCanceledException(); while (isPaused) { try { Thread.sleep(1000); } catch (InterruptedException e) { mergeStatus(status, new Status(IStatus.ERROR, EngineActivator.ID, NLS.bind(Messages.phase_thread_interrupted_error, phaseId), e)); return; } if (subMonitor.isCanceled()) throw new OperationCanceledException(); } Operand operand = operands[i]; if (!isApplicable(operand)) continue; session.recordOperandStart(operand); List<ProvisioningAction> actions = getActions(operand); operandParameters = new HashMap<>(phaseParameters); operandParameters.put(PARM_OPERAND, operand); mergeStatus(status, initializeOperand(profile, operand, operandParameters, subMonitor)); if (status.matches(IStatus.ERROR | IStatus.CANCEL)) { operandParameters = null; return; } Touchpoint operandTouchpoint = (Touchpoint) operandParameters.get(PARM_TOUCHPOINT); if (operandTouchpoint != null) { mergeStatus(status, initializeTouchpointParameters(profile, operand, operandTouchpoint, subMonitor)); if (status.matches(IStatus.ERROR | IStatus.CANCEL)) return; operandParameters = touchpointToTouchpointOperandParameters.get(operandTouchpoint); } if (actions != null) { Object lastResult = null; for (int j = 0; j < actions.size(); j++) { ProvisioningAction action = actions.get(j); Map<String, Object> parameters = operandParameters; Touchpoint touchpoint = action.getTouchpoint(); if (touchpoint != null) { mergeStatus(status, initializeTouchpointParameters(profile, operand, touchpoint, subMonitor)); if (status.matches(IStatus.ERROR | IStatus.CANCEL)) return; parameters = touchpointToTouchpointOperandParameters.get(touchpoint); } if (lastResult != null) { parameters = new HashMap<>(parameters); parameters.put(LAST_RESULT_INTERNAL_NAME, lastResult); } parameters = Collections.unmodifiableMap(parameters); IStatus actionStatus = null; try { session.recordActionExecute(action, parameters); actionStatus = action.execute(parameters); lastResult = action.getResult(); } catch (RuntimeException e) { if (!forced) throw e; // "action.execute" calls user code and might throw an unchecked exception // we catch the error here to gather information on where the problem occurred. actionStatus = new Status(IStatus.ERROR, EngineActivator.ID, NLS.bind(Messages.forced_action_execute_error, action.getClass().getName()), e); } catch (LinkageError e) { if (!forced) throw e; // Catch linkage errors as these are generally recoverable but let other Errors propagate (see bug 222001) actionStatus = new Status(IStatus.ERROR, EngineActivator.ID, NLS.bind(Messages.forced_action_execute_error, action.getClass().getName()), e); } if (forced && actionStatus != null && actionStatus.matches(IStatus.ERROR)) { MultiStatus result = new MultiStatus(EngineActivator.ID, IStatus.ERROR, getProblemMessage(), null); result.add(new Status(IStatus.ERROR, EngineActivator.ID, session.getContextString(this, operand, action), null)); LogHelper.log(result); actionStatus = Status.OK_STATUS; } mergeStatus(status, actionStatus); if (status.matches(IStatus.ERROR | IStatus.CANCEL)) return; } } mergeStatus(status, touchpointCompleteOperand(profile, operand, operandParameters, subMonitor)); mergeStatus(status, completeOperand(profile, operand, operandParameters, subMonitor)); if (status.matches(IStatus.ERROR | IStatus.CANCEL)) return; operandParameters = null; session.recordOperandEnd(operand); subMonitor.worked(1); } } private IStatus initializeTouchpointParameters(IProfile profile, Operand operand, Touchpoint touchpoint, IProgressMonitor monitor) { if (touchpointToTouchpointOperandParameters.containsKey(touchpoint)) return Status.OK_STATUS; Map<String, Object> touchpointPhaseParameters = touchpointToTouchpointPhaseParameters.get(touchpoint); if (touchpointPhaseParameters == null) { touchpointPhaseParameters = new HashMap<>(phaseParameters); IStatus status = touchpoint.initializePhase(monitor, profile, phaseId, touchpointPhaseParameters); if (status != null && status.matches(IStatus.ERROR | IStatus.CANCEL)) return status; touchpointToTouchpointPhaseParameters.put(touchpoint, touchpointPhaseParameters); } Map<String, Object> touchpointOperandParameters = new HashMap<>(touchpointPhaseParameters); touchpointOperandParameters.putAll(operandParameters); IStatus status = touchpoint.initializeOperand(profile, touchpointOperandParameters); if (status != null && status.matches(IStatus.ERROR | IStatus.CANCEL)) return status; touchpointToTouchpointOperandParameters.put(touchpoint, touchpointOperandParameters); return Status.OK_STATUS; } /** * Merges a given IStatus into a MultiStatus */ protected static void mergeStatus(MultiStatus multi, IStatus status) { if (status != null && !status.isOK()) multi.merge(status); } void postPerform(MultiStatus status, EngineSession session, IProgressMonitor monitor) { IProfile profile = session.getProfile(); mergeStatus(status, touchpointCompletePhase(monitor, profile, phaseParameters)); mergeStatus(status, completePhase(monitor, profile, phaseParameters)); } void undo(MultiStatus status, EngineSession session, IProfile profile, Operand operand, ProvisioningAction[] actions, ProvisioningContext context) { if (operandParameters == null) { operandParameters = new HashMap<>(phaseParameters); operandParameters.put(PARM_OPERAND, operand); mergeStatus(status, initializeOperand(profile, operand, operandParameters, new NullProgressMonitor())); Touchpoint operandTouchpoint = (Touchpoint) operandParameters.get(PARM_TOUCHPOINT); if (operandTouchpoint != null) { mergeStatus(status, initializeTouchpointParameters(profile, operand, operandTouchpoint, new NullProgressMonitor())); if (status.matches(IStatus.ERROR | IStatus.CANCEL)) return; operandParameters = touchpointToTouchpointOperandParameters.get(operandTouchpoint); } operandParameters = Collections.unmodifiableMap(operandParameters); } for (int j = 0; j < actions.length; j++) { ProvisioningAction action = actions[j]; Map<String, Object> parameters = operandParameters; Touchpoint touchpoint = action.getTouchpoint(); if (touchpoint != null) { mergeStatus(status, initializeTouchpointParameters(profile, operand, touchpoint, new NullProgressMonitor())); if (status.matches(IStatus.ERROR)) return; parameters = touchpointToTouchpointOperandParameters.get(touchpoint); } IStatus actionStatus = null; try { session.recordActionUndo(action, parameters); actionStatus = action.undo(parameters); } catch (RuntimeException e) { // "action.undo" calls user code and might throw an unchecked exception // we catch the error here to gather information on where the problem occurred. actionStatus = new Status(IStatus.ERROR, EngineActivator.ID, NLS.bind(Messages.action_undo_error, action.getClass().getName()), e); } catch (LinkageError e) { // Catch linkage errors as these are generally recoverable but let other Errors propagate (see bug 222001) actionStatus = new Status(IStatus.ERROR, EngineActivator.ID, NLS.bind(Messages.action_undo_error, action.getClass().getName()), e); } if (actionStatus != null && actionStatus.matches(IStatus.ERROR)) { MultiStatus result = new MultiStatus(EngineActivator.ID, IStatus.ERROR, getProblemMessage(), null); result.add(new Status(IStatus.ERROR, EngineActivator.ID, session.getContextString(this, operand, action), null)); result.merge(actionStatus); } } mergeStatus(status, touchpointCompleteOperand(profile, operand, operandParameters, new NullProgressMonitor())); mergeStatus(status, completeOperand(profile, operand, operandParameters, new NullProgressMonitor())); operandParameters = null; } public boolean isApplicable(Operand operand) { return true; } protected IStatus initializePhase(IProgressMonitor monitor, IProfile profile, Map<String, Object> parameters) { return Status.OK_STATUS; } protected IStatus completePhase(IProgressMonitor monitor, IProfile profile, Map<String, Object> parameters) { return Status.OK_STATUS; } IStatus touchpointCompletePhase(IProgressMonitor monitor, IProfile profile, Map<String, Object> parameters) { if (touchpointToTouchpointPhaseParameters.isEmpty()) return Status.OK_STATUS; MultiStatus status = new MultiStatus(EngineActivator.ID, IStatus.OK, null, null); for (Map.Entry<Touchpoint, Map<String, Object>> entry : touchpointToTouchpointPhaseParameters.entrySet()) { Touchpoint touchpoint = entry.getKey(); Map<String, Object> touchpointParameters = entry.getValue(); mergeStatus(status, touchpoint.completePhase(monitor, profile, phaseId, touchpointParameters)); } touchpointToTouchpointPhaseParameters.clear(); return status; } protected IStatus completeOperand(IProfile profile, Operand operand, Map<String, Object> parameters, IProgressMonitor monitor) { return Status.OK_STATUS; } IStatus touchpointCompleteOperand(IProfile profile, Operand operand, Map<String, Object> parameters, IProgressMonitor monitor) { if (touchpointToTouchpointOperandParameters.isEmpty()) return Status.OK_STATUS; MultiStatus status = new MultiStatus(EngineActivator.ID, IStatus.OK, null, null); for (Map.Entry<Touchpoint, Map<String, Object>> entry : touchpointToTouchpointOperandParameters.entrySet()) { Touchpoint touchpoint = entry.getKey(); Map<String, Object> touchpointParameters = entry.getValue(); mergeStatus(status, touchpoint.completeOperand(profile, touchpointParameters)); } touchpointToTouchpointOperandParameters.clear(); return status; } protected IStatus initializeOperand(IProfile profile, Operand operand, Map<String, Object> parameters, IProgressMonitor monitor) { return Status.OK_STATUS; } protected abstract List<ProvisioningAction> getActions(Operand operand); /** * Returns a human-readable message to be displayed in case of an error performing * this phase. Subclasses should override. */ protected String getProblemMessage() { return NLS.bind(Messages.phase_error, getClass().getName()); } protected void setPaused(boolean isPaused) { this.isPaused = isPaused; } }