blob: bcf9129780fc3efc0e81504c183ea5ba95409053 [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2007, 2012 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.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 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<String, Object>();
private Map<Touchpoint, Map<String, Object>> touchpointToTouchpointPhaseParameters = new HashMap<Touchpoint, Map<String, Object>>();
private Map<Touchpoint, Map<String, Object>> touchpointToTouchpointOperandParameters = new HashMap<Touchpoint, Map<String, Object>>();
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;
}
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<String, Object>(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);
}
operandParameters = Collections.unmodifiableMap(operandParameters);
if (actions != 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);
}
IStatus actionStatus = null;
try {
session.recordActionExecute(action, parameters);
actionStatus = action.execute(parameters);
} 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<String, Object>(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<String, Object>(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<String, Object>(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;
}
}