diff options
author | Tobias Schwarz | 2012-02-29 06:44:31 +0000 |
---|---|---|
committer | Uwe Stieber | 2012-02-29 06:44:31 +0000 |
commit | 7609fc7616cfe2b2e8aaf04a52ca10a34558ca13 (patch) | |
tree | a14a188f9752ff72c44752fe21593672693ae531 /target_explorer/plugins/org.eclipse.tcf.te.runtime.stepper/src/org/eclipse/tcf/te/runtime/stepper/stepper/Stepper.java | |
parent | bc2bdd576dcdfe79d0dd4f4ed14d486b16853fbc (diff) | |
download | org.eclipse.tcf-7609fc7616cfe2b2e8aaf04a52ca10a34558ca13.tar.gz org.eclipse.tcf-7609fc7616cfe2b2e8aaf04a52ca10a34558ca13.tar.xz org.eclipse.tcf-7609fc7616cfe2b2e8aaf04a52ca10a34558ca13.zip |
Target Explorer: Cherry-Pick: Remove stepper and variant delegate extension points
Diffstat (limited to 'target_explorer/plugins/org.eclipse.tcf.te.runtime.stepper/src/org/eclipse/tcf/te/runtime/stepper/stepper/Stepper.java')
-rw-r--r-- | target_explorer/plugins/org.eclipse.tcf.te.runtime.stepper/src/org/eclipse/tcf/te/runtime/stepper/stepper/Stepper.java | 1520 |
1 files changed, 783 insertions, 737 deletions
diff --git a/target_explorer/plugins/org.eclipse.tcf.te.runtime.stepper/src/org/eclipse/tcf/te/runtime/stepper/stepper/Stepper.java b/target_explorer/plugins/org.eclipse.tcf.te.runtime.stepper/src/org/eclipse/tcf/te/runtime/stepper/stepper/Stepper.java index 846b9ad6c..7c455cac7 100644 --- a/target_explorer/plugins/org.eclipse.tcf.te.runtime.stepper/src/org/eclipse/tcf/te/runtime/stepper/stepper/Stepper.java +++ b/target_explorer/plugins/org.eclipse.tcf.te.runtime.stepper/src/org/eclipse/tcf/te/runtime/stepper/stepper/Stepper.java @@ -1,737 +1,783 @@ -/******************************************************************************* - * Copyright (c) 2011, 2012 Wind River Systems, 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: - * Wind River Systems - initial API and implementation - *******************************************************************************/ -package org.eclipse.tcf.te.runtime.stepper.stepper; - -import java.text.MessageFormat; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Date; -import java.util.List; - -import org.eclipse.core.runtime.Assert; -import org.eclipse.core.runtime.CoreException; -import org.eclipse.core.runtime.IProgressMonitor; -import org.eclipse.core.runtime.IStatus; -import org.eclipse.core.runtime.MultiStatus; -import org.eclipse.core.runtime.OperationCanceledException; -import org.eclipse.core.runtime.Status; -import org.eclipse.osgi.util.NLS; -import org.eclipse.tcf.te.runtime.callback.Callback; -import org.eclipse.tcf.te.runtime.concurrent.util.ExecutorsUtil; -import org.eclipse.tcf.te.runtime.extensions.ExecutableExtension; -import org.eclipse.tcf.te.runtime.interfaces.ISharedConstants; -import org.eclipse.tcf.te.runtime.interfaces.properties.IPropertiesContainer; -import org.eclipse.tcf.te.runtime.stepper.StepperManager; -import org.eclipse.tcf.te.runtime.stepper.activator.CoreBundleActivator; -import org.eclipse.tcf.te.runtime.stepper.extensions.StepExecutor; -import org.eclipse.tcf.te.runtime.stepper.interfaces.IExtendedStep; -import org.eclipse.tcf.te.runtime.stepper.interfaces.IFullQualifiedId; -import org.eclipse.tcf.te.runtime.stepper.interfaces.IStep; -import org.eclipse.tcf.te.runtime.stepper.interfaces.IStepContext; -import org.eclipse.tcf.te.runtime.stepper.interfaces.IStepExecutor; -import org.eclipse.tcf.te.runtime.stepper.interfaces.IStepGroup; -import org.eclipse.tcf.te.runtime.stepper.interfaces.IStepGroupIterator; -import org.eclipse.tcf.te.runtime.stepper.interfaces.IStepGroupable; -import org.eclipse.tcf.te.runtime.stepper.interfaces.IStepper; -import org.eclipse.tcf.te.runtime.stepper.interfaces.IStepperProperties; -import org.eclipse.tcf.te.runtime.stepper.interfaces.tracing.ITraceIds; -import org.eclipse.tcf.te.runtime.stepper.nls.Messages; -import org.eclipse.tcf.te.runtime.utils.ProgressHelper; -import org.eclipse.tcf.te.runtime.utils.StatusHelper; - -/** - * An abstract stepper implementation. - */ -public class Stepper extends ExecutableExtension implements IStepper { - - private boolean initialized = false; - private boolean finished = false; - private IPropertiesContainer data = null; - private IFullQualifiedId fullQualifiedId = null; - private IProgressMonitor monitor = null; - private IStepContext context = null; - private boolean cancelable = true; - - /** - * Internal helper describing a fully executed step. - */ - protected final class ExecutedContextStep { - final IFullQualifiedId id; - final IStep step; - - public ExecutedContextStep(IFullQualifiedId id, IStep step) { - this.id = id; - this.step = step; - } - } - - /** - * Constructor. - */ - public Stepper() { - super(); - } - - /** - * Returns a name to describe what is executed by the stepper. - * - * @return A name. - */ - protected String getName() { - return getData() != null ? getData().getStringProperty(IStepperProperties.PROP_NAME) : null; - } - - /** - * Returns the id of the step group to execute by the stepper. - * - * @return The step group id. - */ - protected String getStepGroupId() { - return getData() != null ? getData().getStringProperty(IStepperProperties.PROP_STEP_GROUP_ID) : null; - } - - /** - * Returns the step group for the given step group id. - * - * @param The step group id. Must not be <code>null</code>: - * @return The step group or <code>null</code>. - */ - protected IStepGroup getStepGroup(String id) { - Assert.isNotNull(id); - - CoreBundleActivator.getTraceHandler().trace("SingleContextStepper#getStepGroup:" //$NON-NLS-1$ - + " id = '" + id + "'", //$NON-NLS-1$ //$NON-NLS-2$ - 0, ITraceIds.TRACE_STEPPING, IStatus.WARNING, this); - - return StepperManager.getInstance().getStepGroupExtManager().getStepGroup(id, false); - } - - /** - * Creates a new instance of the step executor to use for executing a step. - * - * @param step The step. Must not be <code>null</code>. - * @param secondaryId The secondary id or <code>null</code>. - * @param fullQualifiedStepId The fully qualified step id. Must not be <code>null</code>. - * - * @return The step executor instance. - */ - protected IStepExecutor doCreateStepExecutor(IStep step, String secondaryId, IFullQualifiedId fullQualifiedStepId) { - Assert.isNotNull(step); - Assert.isNotNull(fullQualifiedStepId); - return new StepExecutor(); - } - - /* (non-Javadoc) - * @see org.eclipse.tcf.te.runtime.stepper.interfaces.IStepper#initialize(org.eclipse.tcf.te.runtime.interfaces.properties.IPropertiesContainer, org.eclipse.tcf.te.runtime.stepper.interfaces.IFullQualifiedId, org.eclipse.core.runtime.IProgressMonitor) - */ - @Override - public final void initialize(IStepContext context, IPropertiesContainer data, IFullQualifiedId fullQualifiedId, IProgressMonitor monitor) throws IllegalStateException { - Assert.isNotNull(context); - Assert.isNotNull(data); - Assert.isNotNull(fullQualifiedId); - Assert.isNotNull(monitor); - - // Assert stepper is not in use - if (isInitialized()) { - throw new IllegalStateException("Stepper instance already initialized!"); //$NON-NLS-1$ - } - - // set the initial stepper attributes - this.context = context; - this.data = data; - this.monitor = monitor; - this.fullQualifiedId = fullQualifiedId; - - // but not finished yet - this.finished = false; - - // call the hook for the subclasses to initialize themselves - onInitialize(data, fullQualifiedId, monitor); - - setInitialized(); - - CoreBundleActivator.getTraceHandler().trace("Stepper#initialize:" //$NON-NLS-1$ - + " data = " + data, //$NON-NLS-1$ - 0, ITraceIds.TRACE_STEPPING, IStatus.WARNING, this); - } - - /** - * Hook for subclasses to overwrite if subclasses wants to initialize their own state. - * - * @param data The data. Must not be <code>null</code>. - * @param fullQualifiedId The full qualified id of this stepper. - * @param monitor The progress monitor. Must not be <code>null</code>. - */ - protected void onInitialize(IPropertiesContainer data, IFullQualifiedId fullQualifiedId, IProgressMonitor monitor) { - Assert.isNotNull(data); - Assert.isNotNull(fullQualifiedId); - Assert.isNotNull(monitor); - } - - /** - * Marks the stepper to be fully initialized. - */ - protected final void setInitialized() { - initialized = true; - } - - /* (non-Javadoc) - * @see org.eclipse.tcf.te.runtime.stepper.interfaces.IStepper#isInitialized() - */ - @Override - public final boolean isInitialized() { - return initialized; - } - - /** - * Sets the cancelable state of the stepper. - * - * @param cancelable <code>True</code> if the stepper shall be cancelable, <code>false</code> if not. - */ - protected final void setCancelable(boolean cancelable) { - this.cancelable = cancelable; - } - - /** - * Returns the cancelable state of the stepper. - * - * @return <code>True</code> if the stepper is cancelable, <code>false</code> if not. - */ - protected final boolean isCancelable() { - return cancelable; - } - - /** - * Get the active context. - * - * @return The active context or <code>null</code>. - */ - protected IStepContext getContext() { - return context; - } - - /** - * Get the context id. - * - * @return The context id or <code>null</code>. - */ - protected String getContextId() { - return context != null ? context.getId() : null; - } - - /** - * Returns the currently associated data. The method returns <code>null</code> if the stepper is - * not in initialized state. - * - * @return The data or <code>null</code> - */ - protected final IPropertiesContainer getData() { - return isInitialized() ? data : null; - } - - /** - * Returns the full qualified id for this stepper. - * - * @return The full qualified stepper id. - */ - protected final IFullQualifiedId getFullQualifiedId() { - return fullQualifiedId; - } - - /** - * Returns the currently associated progress monitor. The method returns - * <code>null</code> if the stepper is not in initialized state. - * - * @return The progress monitor or <code>null</code> - */ - protected final IProgressMonitor getMonitor() { - return isInitialized() ? monitor : null; - } - - /** - * Marks the stepper to be finished. - */ - protected final void setFinished() { - finished = true; - } - - /* (non-Javadoc) - * @see org.eclipse.tcf.te.runtime.stepper.interfaces.IStepper#isFinished() - */ - @Override - public final boolean isFinished() { - return finished; - } - - /* (non-Javadoc) - * @see org.eclipse.tcf.te.runtime.stepper.interfaces.IStepper#cleanup() - */ - @Override - public void cleanup() { - // Set the progress monitor done here in any case - if (getMonitor() != null) { - getMonitor().done(); - } - - // Reset the initial stepper attributes - context = null; - data = null; - monitor = null; - fullQualifiedId = null; - finished = false; - initialized = false; - } - - /* (non-Javadoc) - * @see java.lang.Object#toString() - */ - @Override - public String toString() { - StringBuilder buffer = new StringBuilder(getClass().getSimpleName()); - buffer.append(" (" + getLabel() + ")"); //$NON-NLS-1$ //$NON-NLS-2$ - buffer.append(": "); //$NON-NLS-1$ - buffer.append("id = " + getId()); //$NON-NLS-1$ - return buffer.toString(); - } - - /* (non-Javadoc) - * @see org.eclipse.tcf.te.runtime.stepper.interfaces.IStepper#execute() - */ - @Override - public final void execute() throws CoreException { - long startTime = System.currentTimeMillis(); - - CoreBundleActivator.getTraceHandler().trace("Stepper#execute: *** ENTERED", //$NON-NLS-1$ - 0, ITraceIds.TRACE_STEPPING, IStatus.WARNING, this); - CoreBundleActivator.getTraceHandler().trace(" [" + ISharedConstants.TIME_FORMAT.format(new Date(startTime)) + "]" //$NON-NLS-1$ //$NON-NLS-2$ - + " ***", //$NON-NLS-1$ - 0, ITraceIds.PROFILE_STEPPING, IStatus.WARNING, this); - - try { - // stepper must be initialized before executing - if (!isInitialized()) { - throw new CoreException(new Status(IStatus.ERROR, CoreBundleActivator.getUniqueIdentifier(), - Messages.Stepper_error_initializeNotCalled)); - } - - // Create a container for collecting the non-severe status objects - // during the step execution. Non-severe status objects will - // be hold back till the execution completed or stopped with an error. - // Severe status objects are errors or cancellation. - List<IStatus> statusContainer = new ArrayList<IStatus>(); - - // start execution - internalExecute(statusContainer); - - // If the warnings container is not empty, create a new status and - // throw a core exception - if (!statusContainer.isEmpty()) { - IStatus status = null; - - // Check if we need a multi status - if (statusContainer.size() > 1) { - MultiStatus multiStatus = - new MultiStatus(CoreBundleActivator.getUniqueIdentifier(), 0, - NLS.bind(Messages.Stepper_multiStatus_finishedWithWarnings, getName()), null); - for (IStatus subStatus : statusContainer) { - multiStatus.merge(subStatus); - } - status = multiStatus; - } - else { - status = statusContainer.get(0); - } - - throw new CoreException(status); - } - } - finally { - // Mark the stepper finished - setFinished(); - - long endTime = System.currentTimeMillis(); - CoreBundleActivator.getTraceHandler().trace("Stepper#execute: *** DONE", //$NON-NLS-1$ - 0, ITraceIds.TRACE_STEPPING, IStatus.WARNING, this); - CoreBundleActivator.getTraceHandler().trace(" [" + ISharedConstants.TIME_FORMAT.format(new Date(endTime)) //$NON-NLS-1$ - + " , delay = " + (endTime - startTime) + " ms]" //$NON-NLS-1$ //$NON-NLS-2$ - + " ***", //$NON-NLS-1$ - 0, ITraceIds.PROFILE_STEPPING, IStatus.WARNING, this); - } - } - - /** - * Executes a step or step group. - * - * @param statusContainer The status container. Must not be <code>null</code>. - * @throws CoreException If the execution fails. - */ - protected void internalExecute(List<IStatus> statusContainer) throws CoreException { - Assert.isNotNull(statusContainer); - - // Get the step group id - String stepGroupId = getStepGroupId(); - - // If no step group id is available, throw an exception - if (stepGroupId == null) { - throw new CoreException(new Status(IStatus.ERROR, CoreBundleActivator.getUniqueIdentifier(), - NLS.bind(Messages.Stepper_error_missingStepGroupId, getName()))); - } - - // Get the step group - IStepGroup stepGroup = getStepGroup(stepGroupId); - - // If no step group could be found for any of the valid variants, throw an exception - if (stepGroup == null) { - throw new CoreException(new Status(IStatus.ERROR, CoreBundleActivator.getUniqueIdentifier(), - NLS.bind(Messages.Stepper_error_missingStepGroup, stepGroupId))); - } - - // Initialize the progress monitor - getMonitor().beginTask(stepGroup.getLabel(), calculateTotalWork(stepGroup)); - - IFullQualifiedId fullQualifiedId = getFullQualifiedId().createChildId(ID_TYPE_CONTEXT_ID, getContextId(), null); - fullQualifiedId = fullQualifiedId.createChildId(ID_TYPE_STEP_GROUP_ID, stepGroup.getId(), null); - // Execute the step group - executeStepGroup(stepGroup, statusContainer, new ArrayList<ExecutedContextStep>(), fullQualifiedId); - } - - /** - * Executes a step group. - * - * @param stepGroup The step group. Must not be <code>null</code>. - * @param statusContainer A list holding the warnings occurred during the execution. Must not be <code>null</code>. - * @param executedSteps A list holding the id's of the steps executed before. Must not be <code>null</code>. - * @param fullQualifiedGroupId The hierarchy of all parent step group id's separated by "::". Must not be <code>null</code>. - * - * @throws CoreException If the execution fails. - */ - private void executeStepGroup(IStepGroup stepGroup, List<IStatus> statusContainer, List<ExecutedContextStep> executedSteps, IFullQualifiedId fullQualifiedGroupId) throws CoreException { - Assert.isNotNull(stepGroup); - Assert.isNotNull(statusContainer); - Assert.isNotNull(executedSteps); - Assert.isNotNull(fullQualifiedGroupId); - - // Return immediately if the user canceled the monitor in the meanwhile - if (isCancelable() && getMonitor().isCanceled()) { - rollback(executedSteps, Status.CANCEL_STATUS, getMonitor()); - throw new CoreException(StatusHelper.getStatus(new OperationCanceledException())); - } - - CoreBundleActivator.getTraceHandler().trace("Stepper#executeStepGroup: step group: '" + stepGroup.getLabel() + "'", //$NON-NLS-1$ //$NON-NLS-2$ - 0, ITraceIds.TRACE_STEPPING, IStatus.WARNING, this); - - // Resolve the steps to execute - IStepGroupable[] groupables = stepGroup.getSteps(getContext()); - - IStepGroupIterator iterator = stepGroup.getStepGroupIterator(); - IFullQualifiedId fullQualifiedIterationId = fullQualifiedGroupId; - int iteration = 0; - - if (iterator != null) { - iterator.initialize(getContext(), getData(), fullQualifiedGroupId, getMonitor()); - } - boolean next = iterator == null || iterator.hasNext(getContext(), getData(), fullQualifiedGroupId, getMonitor()); - - while (next) { - if (iterator != null) { - fullQualifiedIterationId = fullQualifiedGroupId.createChildId(ID_TYPE_STEP_GROUP_ITERATION_ID, iterator.getId(), ""+iteration); //$NON-NLS-1$ - iterator.next(getContext(), getData(), fullQualifiedIterationId, getMonitor()); - } - // Execute the steps or step groups. - for (IStepGroupable groupable : groupables) { - executeGroupable(groupable, statusContainer, executedSteps, fullQualifiedIterationId); - } - iteration++; - next = iterator != null && iterator.hasNext(getContext(), getData(), fullQualifiedGroupId, getMonitor()); - } - } - - /** - * Executes a step groupable. The groupable might encapsulate a - * step or a step group. - * - * @param step The step groupable. Must not be <code>null</code>. - * @param statusContainer A list holding the warnings occurred during the execution. Must not be <code>null</code>. - * @param executedSteps A list holding the id's of the steps executed before. Must not be <code>null</code>. - * @param fullQualifiedParentId The hierarchy of all parent step group id's separated by "::". Must not be <code>null</code>. - * - * @throws CoreException If the execution failed. - */ - private void executeGroupable(IStepGroupable groupable, List<IStatus> statusContainer, List<ExecutedContextStep> executedSteps, IFullQualifiedId fullQualifiedParentId) throws CoreException { - Assert.isNotNull(groupable); - Assert.isNotNull(statusContainer); - Assert.isNotNull(executedSteps); - Assert.isNotNull(fullQualifiedParentId); - - // Return immediately if the user canceled the monitor in the meanwhile - if (isCancelable() && getMonitor() != null && getMonitor().isCanceled()) { - rollback(executedSteps, Status.CANCEL_STATUS, getMonitor()); - throw new CoreException(StatusHelper.getStatus(new OperationCanceledException())); - } - - // If the passed in groupable is disabled -> we are done immediately - if (groupable.isDisabled()) { - CoreBundleActivator.getTraceHandler().trace("Stepper#executeGroupable: DROPPED DISABLED groupable: id = '" + groupable.getExtension().getId() + "'" //$NON-NLS-1$ //$NON-NLS-2$ - + ", secondaryId = '" + groupable.getSecondaryId() + "'", //$NON-NLS-1$ //$NON-NLS-2$ - 0, ITraceIds.TRACE_STEPPING, IStatus.WARNING, this); - return; - } - - // Check if all dependencies of the groupable have been executed before - checkForDependenciesExecuted(groupable, executedSteps); - - if (groupable.getExtension() instanceof IStepGroup) { - IFullQualifiedId id = fullQualifiedParentId.createChildId(ID_TYPE_STEP_GROUP_ID, groupable.getExtension().getId(), groupable.getSecondaryId()); - // If the passed in groupable is associated with a step group - // -> get the groupable from that group and execute them - executeStepGroup((IStepGroup)groupable.getExtension(), statusContainer, executedSteps, id); - } - else if (groupable.getExtension() instanceof IStep) { - // If the passed in groupable is associated with a step - // -> check if the required steps have been executed before, - // create a step executor and invoke the executor. - IStep step = (IStep)groupable.getExtension(); - - IFullQualifiedId id = fullQualifiedParentId.createChildId(ID_TYPE_STEP_ID, step.getId(), groupable.getSecondaryId()); - - // Create the step executor now - IStepExecutor executor = doCreateStepExecutor(step, groupable.getSecondaryId(), id); - Assert.isNotNull(executor); - - try { - executedSteps.add(new ExecutedContextStep(id, step)); - // Invoke the executor now - executor.execute(step, id, getContext(), getData(), getMonitor()); - } - catch (Exception e) { - // Catch the CoreException first hand as we need to continue the - // stepping if the step returned with warnings or information only. - CoreException coreException = normalizeStatus(e, statusContainer); - // If the exception has been not eaten, rollback previously executed - // steps and re-throw the exception. - if (coreException != null) { - // Rollback everything, if the step(s) are supporting this and - // the cleanup hasn't been done already. - if (isInitialized()) { - rollback(executedSteps, coreException.getStatus(), getMonitor()); - } - - // Re-throw the exception - throw coreException; - } - } - } - } - - /** - * Checks if all required dependencies have been executed before. If not, the method - * will throw an error status. - * - * @param groupable The groupable. Must not be <code>null</code>. - * @param executedSteps A list holding the id's of the steps executed before. Must not be <code>null</code>. - * - * @throws CoreException If a dependency has not been executed before. - */ - protected void checkForDependenciesExecuted(IStepGroupable groupable, List<ExecutedContextStep> executedSteps) throws CoreException { - Assert.isNotNull(groupable); - Assert.isNotNull(executedSteps); - - // Build up the complete list of dependencies. - List<String> dependencies = new ArrayList<String>(Arrays.asList(groupable.getDependencies())); - // If the groupable wraps a step, the step can have additional dependencies to check - if (groupable.getExtension() instanceof IStep) { - dependencies.addAll(Arrays.asList(((IStep)groupable.getExtension()).getDependencies())); - } - - // Check each dependency now. - for (String dependency : dependencies) { - // The dependencies might be fully qualified. Split out the primary id. - String[] splitted = dependency.split("##", 2); //$NON-NLS-1$ - String primaryId = splitted.length == 2 ? splitted[0] : dependency; - - // Check if the id is in the list of executed steps. As the list contains - // the fully qualified id's, we cannot just check for contained - boolean requiredStepExecuted = false; - for (ExecutedContextStep step : executedSteps) { - if (step.step.getId().equals(primaryId)) { - requiredStepExecuted = true; - break; - } - } - - if (!requiredStepExecuted) { - throw new CoreException(new Status(IStatus.ERROR, CoreBundleActivator.getUniqueIdentifier(), - MessageFormat.format(Messages.Stepper_error_requiredStepNotExecuted, - NLS.bind(groupable.getExtension() instanceof IStep ? Messages.Stepper_error_step : Messages.Stepper_error_step, groupable.getExtension().getId()), - NLS.bind(Messages.Stepper_error_requiredStepOrGroup, dependency), ""))); //$NON-NLS-1$ - } - - // Recursive checking is not necessary here as the step or step group - // id's would have made it the executed steps list of they missed required - // steps or step groups. - } - - } - - /** - * Rollback the steps previously executed to the failed step. The rollback - * is executed in reverse order and the step must be of type {@link IExtendedStep} - * to participate in the rollback. - * - * @param executedSteps - * @param progress - */ - protected final void rollback(final List<ExecutedContextStep> executedSteps, final IStatus rollBackStatus, IProgressMonitor progress) { - Assert.isNotNull(executedSteps); - - final IProgressMonitor rollbackProgress = ProgressHelper.getProgressMonitor(progress, 1); - ProgressHelper.beginTask(rollbackProgress, "Cancel", executedSteps.size()); //$NON-NLS-1$ - final Callback finalCallback = new Callback(); - final Callback rollbackCallback = new Callback() { - @Override - protected void internalDone(Object caller, IStatus status) { - if (!executedSteps.isEmpty()) { - setProperty(PROPERTY_IS_DONE, false); - ExecutedContextStep executedStep = executedSteps.remove(executedSteps.size()-1); - if (executedStep.step instanceof IExtendedStep) { - IExtendedStep step = (IExtendedStep)executedStep.step; - step.rollback(getContext(), getData(), rollBackStatus, executedStep.id, rollbackProgress, this); - } - else { - this.done(this, status); - } - } - else { - finalCallback.done(this, Status.OK_STATUS); - } - } - }; - - rollbackCallback.done(this, rollBackStatus); - ExecutorsUtil.waitAndExecute(0, finalCallback.getDoneConditionTester(null)); - } - - /** - * Calculates the total work required for the step group. The total - * work is the sum of the total work of each sub step. If one of the - * steps returns {@link IProgressMonitor#UNKNOWN}, the total work will - * be unknown for the whole step group. - * - * @param stepGroup The step group. Must not be <code>null</code>. - * @return The total work required or {@link IProgressMonitor#UNKNOWN}. - * - * @throws CoreException If the total work of the step group cannot be determined. - */ - protected int calculateTotalWork(IStepGroup stepGroup) throws CoreException { - Assert.isNotNull(stepGroup); - - int totalWork = 0; - - // Loop the group steps and summarize the returned total work - IStepGroupable[] groupables = stepGroup.getSteps(getContext()); - for (IStepGroupable groupable : groupables) { - int work = groupable.getExtension() instanceof IStep - ? ((IStep)groupable.getExtension()).getTotalWork(getContext(), getData()) - : groupable.getExtension() instanceof IStepGroup - ? calculateTotalWork((IStepGroup)groupable.getExtension()) - : IProgressMonitor.UNKNOWN; - - if (work == IProgressMonitor.UNKNOWN) { - totalWork = IProgressMonitor.UNKNOWN; - break; - } - - totalWork += work; - } - - return totalWork; - } - - /** - * Normalize the associated status object of the given {@link CoreException}. - * <p> - * If the associated status contains only WARNING or INFORMATION status objects, - * the objects are added to the passed in status container. The passed in exception - * is dropped and the method will return <code>null</code>. - * <p> - * If the associated status contains only OK status objects, the passed in - * exception and the associated status are dropped and the method will return - * <code>null</code>. - * <p> - * If the associated status contain ERROR status objects, the passed in exception - * and the associated status objects are returned if the passed in status container - * is empty. If the status container is not empty, a new exception and multi status - * object is created and returned. The multi status object will contain all status - * objects from the status container and all objects of the originally associated - * status. - * <p> - * If the associated status contains a CANCEL status object, the passed in - * exception and the associated status objects are returned unmodified. - * - * @param e The core exception. Must not be <code>null</code>. - * @param statusContainer The list of non-severe status objects. Must not be <code>null</code>. - * @return The exception to re-throw or <code>null</code>. - */ - private CoreException normalizeStatus(Exception e, List<IStatus> statusContainer) { - Assert.isNotNull(statusContainer); - - CoreException coreException = null; - - IStatus status = Status.OK_STATUS; - // Get the associated status from the exception - if (e instanceof CoreException) { - status = ((CoreException)e).getStatus(); - coreException = (CoreException)e; - } - else if (e instanceof OperationCanceledException) { - status = new Status(IStatus.CANCEL, CoreBundleActivator.getUniqueIdentifier(), e.getLocalizedMessage(), e); - coreException = new CoreException(status); - } - else if (e != null) { - status = new Status(IStatus.ERROR, CoreBundleActivator.getUniqueIdentifier(), e.getLocalizedMessage(), e); - coreException = new CoreException(status); - } - - // Check the severity - // PS: MultiStatus.getSeverity() returns always the highest severity. - if (status.getSeverity() == IStatus.OK) { - // OK -> drop completely and return null - coreException = null; - } - else if (status.getSeverity() == IStatus.CANCEL) { - // CANCEL -> Check monitor to be canceled. - if (isCancelable()) { - if (getMonitor() != null && !getMonitor().isCanceled()) { - getMonitor().setCanceled(true); - } - } - } - else if (status.getSeverity() == IStatus.WARNING || status.getSeverity() == IStatus.INFO) { - // WARNING or INFORMATION -> add to the list and return null - statusContainer.add(status); - coreException = null; - } - else if (status.getSeverity() == IStatus.ERROR) { - // Error -> If the warnings container not empty, create - // a new MultiStatus. - if (!statusContainer.isEmpty()) { - MultiStatus multiStatus = new MultiStatus(status.getPlugin(), status.getCode(), - NLS.bind(Messages.Stepper_multiStatus_finishedWithErrors, getName()), null); - for (IStatus stat : statusContainer) { - multiStatus.merge(stat); - } - // Re-throw via a new CoreException - coreException = new CoreException(multiStatus); - } - } - - return coreException; - } -} +/*******************************************************************************
+ * Copyright (c) 2011, 2012 Wind River Systems, 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:
+ * Wind River Systems - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.tcf.te.runtime.stepper.stepper;
+
+import java.text.MessageFormat;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Date;
+import java.util.List;
+
+import org.eclipse.core.runtime.Assert;
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.core.runtime.IStatus;
+import org.eclipse.core.runtime.MultiStatus;
+import org.eclipse.core.runtime.OperationCanceledException;
+import org.eclipse.core.runtime.Status;
+import org.eclipse.osgi.util.NLS;
+import org.eclipse.tcf.te.runtime.callback.Callback;
+import org.eclipse.tcf.te.runtime.concurrent.util.ExecutorsUtil;
+import org.eclipse.tcf.te.runtime.interfaces.ISharedConstants;
+import org.eclipse.tcf.te.runtime.interfaces.properties.IPropertiesContainer;
+import org.eclipse.tcf.te.runtime.stepper.StepperManager;
+import org.eclipse.tcf.te.runtime.stepper.activator.CoreBundleActivator;
+import org.eclipse.tcf.te.runtime.stepper.extensions.StepExecutor;
+import org.eclipse.tcf.te.runtime.stepper.interfaces.IExtendedStep;
+import org.eclipse.tcf.te.runtime.stepper.interfaces.IFullQualifiedId;
+import org.eclipse.tcf.te.runtime.stepper.interfaces.IStep;
+import org.eclipse.tcf.te.runtime.stepper.interfaces.IStepContext;
+import org.eclipse.tcf.te.runtime.stepper.interfaces.IStepExecutor;
+import org.eclipse.tcf.te.runtime.stepper.interfaces.IStepGroup;
+import org.eclipse.tcf.te.runtime.stepper.interfaces.IStepGroupIterator;
+import org.eclipse.tcf.te.runtime.stepper.interfaces.IStepGroupable;
+import org.eclipse.tcf.te.runtime.stepper.interfaces.IStepper;
+import org.eclipse.tcf.te.runtime.stepper.interfaces.IStepperProperties;
+import org.eclipse.tcf.te.runtime.stepper.interfaces.tracing.ITraceIds;
+import org.eclipse.tcf.te.runtime.stepper.nls.Messages;
+import org.eclipse.tcf.te.runtime.utils.ProgressHelper;
+import org.eclipse.tcf.te.runtime.utils.StatusHelper;
+
+/**
+ * An abstract stepper implementation.
+ */
+public class Stepper implements IStepper {
+
+ private boolean initialized = false;
+ private boolean finished = false;
+ private IPropertiesContainer data = null;
+ private IFullQualifiedId fullQualifiedId = null;
+ private IProgressMonitor monitor = null;
+ private IStepContext context = null;
+ private boolean cancelable = true;
+
+ /**
+ * Internal helper describing a fully executed step.
+ */
+ protected final class ExecutedContextStep {
+ final IFullQualifiedId id;
+ final IStep step;
+
+ public ExecutedContextStep(IFullQualifiedId id, IStep step) {
+ this.id = id;
+ this.step = step;
+ }
+ }
+
+ /**
+ * Constructor.
+ */
+ public Stepper() {
+ super();
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see org.eclipse.tcf.te.runtime.stepper.interfaces.IStepper#getId()
+ */
+ @Override
+ public String getId() {
+ return getClass().getName();
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see org.eclipse.tcf.te.runtime.stepper.interfaces.IStepper#getLabel()
+ */
+ @Override
+ public String getLabel() {
+ return getClass().getName();
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see org.eclipse.tcf.te.runtime.stepper.interfaces.IStepper#getDescription()
+ */
+ @Override
+ public String getDescription() {
+ return null;
+ }
+
+ /**
+ * Returns the id of the step group to execute by the stepper.
+ *
+ * @return The step group id.
+ */
+ protected String getStepGroupId() {
+ return getData() != null ? getData()
+ .getStringProperty(IStepperProperties.PROP_STEP_GROUP_ID) : null;
+ }
+
+ /**
+ * Returns the step group for the given step group id.
+ *
+ * @param The step group id. Must not be <code>null</code>:
+ * @return The step group or <code>null</code>.
+ */
+ protected IStepGroup getStepGroup(String id) {
+ Assert.isNotNull(id);
+
+ CoreBundleActivator.getTraceHandler().trace("SingleContextStepper#getStepGroup:" //$NON-NLS-1$
+ + " id = '" + id + "'", //$NON-NLS-1$ //$NON-NLS-2$
+ 0, ITraceIds.TRACE_STEPPING, IStatus.WARNING, this);
+
+ return StepperManager.getInstance().getStepGroupExtManager().getStepGroup(id, false);
+ }
+
+ /**
+ * Creates a new instance of the step executor to use for executing a step.
+ *
+ * @param step The step. Must not be <code>null</code>.
+ * @param secondaryId The secondary id or <code>null</code>.
+ * @param fullQualifiedStepId The fully qualified step id. Must not be <code>null</code>.
+ *
+ * @return The step executor instance.
+ */
+ protected IStepExecutor doCreateStepExecutor(IStep step, String secondaryId, IFullQualifiedId fullQualifiedStepId) {
+ Assert.isNotNull(step);
+ Assert.isNotNull(fullQualifiedStepId);
+ return new StepExecutor();
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see
+ * org.eclipse.tcf.te.runtime.stepper.interfaces.IStepper#initialize(org.eclipse.tcf.te.runtime
+ * .interfaces.properties.IPropertiesContainer,
+ * org.eclipse.tcf.te.runtime.stepper.interfaces.IFullQualifiedId,
+ * org.eclipse.core.runtime.IProgressMonitor)
+ */
+ @Override
+ public final void initialize(IStepContext context, IPropertiesContainer data, IFullQualifiedId fullQualifiedId, IProgressMonitor monitor) throws IllegalStateException {
+ Assert.isNotNull(context);
+ Assert.isNotNull(data);
+ Assert.isNotNull(fullQualifiedId);
+ Assert.isNotNull(monitor);
+
+ // Assert stepper is not in use
+ if (isInitialized()) {
+ throw new IllegalStateException("Stepper instance already initialized!"); //$NON-NLS-1$
+ }
+
+ // set the initial stepper attributes
+ this.context = context;
+ this.data = data;
+ this.monitor = monitor;
+ this.fullQualifiedId = fullQualifiedId;
+
+ // but not finished yet
+ this.finished = false;
+
+ // call the hook for the subclasses to initialize themselves
+ onInitialize(data, fullQualifiedId, monitor);
+
+ setInitialized();
+
+ CoreBundleActivator.getTraceHandler().trace("Stepper#initialize:" //$NON-NLS-1$
+ + " data = " + data, //$NON-NLS-1$
+ 0, ITraceIds.TRACE_STEPPING, IStatus.WARNING, this);
+ }
+
+ /**
+ * Hook for subclasses to overwrite if subclasses wants to initialize their own state.
+ *
+ * @param data The data. Must not be <code>null</code>.
+ * @param fullQualifiedId The full qualified id of this stepper.
+ * @param monitor The progress monitor. Must not be <code>null</code>.
+ */
+ protected void onInitialize(IPropertiesContainer data, IFullQualifiedId fullQualifiedId, IProgressMonitor monitor) {
+ Assert.isNotNull(data);
+ Assert.isNotNull(fullQualifiedId);
+ Assert.isNotNull(monitor);
+ }
+
+ /**
+ * Marks the stepper to be fully initialized.
+ */
+ protected final void setInitialized() {
+ initialized = true;
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see org.eclipse.tcf.te.runtime.stepper.interfaces.IStepper#isInitialized()
+ */
+ @Override
+ public final boolean isInitialized() {
+ return initialized;
+ }
+
+ /**
+ * Sets the cancelable state of the stepper.
+ *
+ * @param cancelable <code>True</code> if the stepper shall be cancelable, <code>false</code> if
+ * not.
+ */
+ protected final void setCancelable(boolean cancelable) {
+ this.cancelable = cancelable;
+ }
+
+ /**
+ * Returns the cancelable state of the stepper.
+ *
+ * @return <code>True</code> if the stepper is cancelable, <code>false</code> if not.
+ */
+ protected final boolean isCancelable() {
+ return cancelable;
+ }
+
+ /**
+ * Get the active context.
+ *
+ * @return The active context or <code>null</code>.
+ */
+ protected IStepContext getContext() {
+ return context;
+ }
+
+ /**
+ * Get the context id.
+ *
+ * @return The context id or <code>null</code>.
+ */
+ protected String getContextId() {
+ return context != null ? context.getId() : null;
+ }
+
+ /**
+ * Returns the currently associated data. The method returns <code>null</code> if the stepper is
+ * not in initialized state.
+ *
+ * @return The data or <code>null</code>
+ */
+ protected final IPropertiesContainer getData() {
+ return isInitialized() ? data : null;
+ }
+
+ /**
+ * Returns the full qualified id for this stepper.
+ *
+ * @return The full qualified stepper id.
+ */
+ protected final IFullQualifiedId getFullQualifiedId() {
+ return fullQualifiedId;
+ }
+
+ /**
+ * Returns the currently associated progress monitor. The method returns <code>null</code> if
+ * the stepper is not in initialized state.
+ *
+ * @return The progress monitor or <code>null</code>
+ */
+ protected final IProgressMonitor getMonitor() {
+ return isInitialized() ? monitor : null;
+ }
+
+ /**
+ * Marks the stepper to be finished.
+ */
+ protected final void setFinished() {
+ finished = true;
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see org.eclipse.tcf.te.runtime.stepper.interfaces.IStepper#isFinished()
+ */
+ @Override
+ public final boolean isFinished() {
+ return finished;
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see org.eclipse.tcf.te.runtime.stepper.interfaces.IStepper#cleanup()
+ */
+ @Override
+ public void cleanup() {
+ // Set the progress monitor done here in any case
+ if (getMonitor() != null) {
+ getMonitor().done();
+ }
+
+ // Reset the initial stepper attributes
+ context = null;
+ data = null;
+ monitor = null;
+ fullQualifiedId = null;
+ finished = false;
+ initialized = false;
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see java.lang.Object#toString()
+ */
+ @Override
+ public String toString() {
+ StringBuilder buffer = new StringBuilder(getClass().getSimpleName());
+ buffer.append(" (" + getLabel() + ")"); //$NON-NLS-1$ //$NON-NLS-2$
+ buffer.append(": "); //$NON-NLS-1$
+ buffer.append("id = " + getId()); //$NON-NLS-1$
+ return buffer.toString();
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see org.eclipse.tcf.te.runtime.stepper.interfaces.IStepper#execute()
+ */
+ @Override
+ public final void execute() throws CoreException {
+ long startTime = System.currentTimeMillis();
+
+ CoreBundleActivator.getTraceHandler().trace("Stepper#execute: *** ENTERED", //$NON-NLS-1$
+ 0, ITraceIds.TRACE_STEPPING, IStatus.WARNING, this);
+ CoreBundleActivator
+ .getTraceHandler()
+ .trace(" [" + ISharedConstants.TIME_FORMAT.format(new Date(startTime)) + "]" //$NON-NLS-1$ //$NON-NLS-2$
+ + " ***", //$NON-NLS-1$
+ 0, ITraceIds.PROFILE_STEPPING, IStatus.WARNING, this);
+
+ try {
+ // stepper must be initialized before executing
+ if (!isInitialized()) {
+ throw new CoreException(new Status(IStatus.ERROR, CoreBundleActivator.getUniqueIdentifier(), Messages.Stepper_error_initializeNotCalled));
+ }
+
+ // Create a container for collecting the non-severe status objects
+ // during the step execution. Non-severe status objects will
+ // be hold back till the execution completed or stopped with an error.
+ // Severe status objects are errors or cancellation.
+ List<IStatus> statusContainer = new ArrayList<IStatus>();
+
+ // start execution
+ internalExecute(statusContainer);
+
+ // If the warnings container is not empty, create a new status and
+ // throw a core exception
+ if (!statusContainer.isEmpty()) {
+ IStatus status = null;
+
+ // Check if we need a multi status
+ if (statusContainer.size() > 1) {
+ MultiStatus multiStatus = new MultiStatus(CoreBundleActivator.getUniqueIdentifier(), 0, NLS
+ .bind(Messages.Stepper_multiStatus_finishedWithWarnings, getLabel()), null);
+ for (IStatus subStatus : statusContainer) {
+ multiStatus.merge(subStatus);
+ }
+ status = multiStatus;
+ }
+ else {
+ status = statusContainer.get(0);
+ }
+
+ throw new CoreException(status);
+ }
+ }
+ finally {
+ // Mark the stepper finished
+ setFinished();
+
+ long endTime = System.currentTimeMillis();
+ CoreBundleActivator.getTraceHandler().trace("Stepper#execute: *** DONE", //$NON-NLS-1$
+ 0, ITraceIds.TRACE_STEPPING, IStatus.WARNING, this);
+ CoreBundleActivator.getTraceHandler()
+ .trace(" [" + ISharedConstants.TIME_FORMAT.format(new Date(endTime)) //$NON-NLS-1$
+ + " , delay = " + (endTime - startTime) + " ms]" //$NON-NLS-1$ //$NON-NLS-2$
+ + " ***", //$NON-NLS-1$
+ 0, ITraceIds.PROFILE_STEPPING, IStatus.WARNING, this);
+ }
+ }
+
+ /**
+ * Executes a step or step group.
+ *
+ * @param statusContainer The status container. Must not be <code>null</code>.
+ * @throws CoreException If the execution fails.
+ */
+ protected void internalExecute(List<IStatus> statusContainer) throws CoreException {
+ Assert.isNotNull(statusContainer);
+
+ // Get the step group id
+ String stepGroupId = getStepGroupId();
+
+ // If no step group id is available, throw an exception
+ if (stepGroupId == null) {
+ throw new CoreException(new Status(IStatus.ERROR, CoreBundleActivator.getUniqueIdentifier(), NLS
+ .bind(Messages.Stepper_error_missingStepGroupId, getLabel())));
+ }
+
+ // Get the step group
+ IStepGroup stepGroup = getStepGroup(stepGroupId);
+
+ // If no step group could be found, throw an exception
+ if (stepGroup == null) {
+ throw new CoreException(new Status(IStatus.ERROR, CoreBundleActivator.getUniqueIdentifier(), NLS
+ .bind(Messages.Stepper_error_missingStepGroup, stepGroupId)));
+ }
+
+ // Initialize the progress monitor
+ getMonitor().beginTask(stepGroup.getLabel(), calculateTotalWork(stepGroup));
+
+ IFullQualifiedId fullQualifiedId = getFullQualifiedId()
+ .createChildId(ID_TYPE_CONTEXT_ID, getContextId(), null);
+ fullQualifiedId = fullQualifiedId
+ .createChildId(ID_TYPE_STEP_GROUP_ID, stepGroup.getId(), null);
+ // Execute the step group
+ executeStepGroup(stepGroup, statusContainer, new ArrayList<ExecutedContextStep>(), fullQualifiedId);
+ }
+
+ /**
+ * Executes a step group.
+ *
+ * @param stepGroup The step group. Must not be <code>null</code>.
+ * @param statusContainer A list holding the warnings occurred during the execution. Must not be
+ * <code>null</code>.
+ * @param executedSteps A list holding the id's of the steps executed before. Must not be
+ * <code>null</code>.
+ * @param fullQualifiedGroupId The hierarchy of all parent step group id's separated by "::".
+ * Must not be <code>null</code>.
+ *
+ * @throws CoreException If the execution fails.
+ */
+ private void executeStepGroup(IStepGroup stepGroup, List<IStatus> statusContainer, List<ExecutedContextStep> executedSteps, IFullQualifiedId fullQualifiedGroupId) throws CoreException {
+ Assert.isNotNull(stepGroup);
+ Assert.isNotNull(statusContainer);
+ Assert.isNotNull(executedSteps);
+ Assert.isNotNull(fullQualifiedGroupId);
+
+ // Return immediately if the user canceled the monitor in the meanwhile
+ if (isCancelable() && getMonitor().isCanceled()) {
+ rollback(executedSteps, Status.CANCEL_STATUS, getMonitor());
+ throw new CoreException(StatusHelper.getStatus(new OperationCanceledException()));
+ }
+
+ CoreBundleActivator
+ .getTraceHandler()
+ .trace("Stepper#executeStepGroup: step group: '" + stepGroup.getLabel() + "'", //$NON-NLS-1$ //$NON-NLS-2$
+ 0, ITraceIds.TRACE_STEPPING, IStatus.WARNING, this);
+
+ // Resolve the steps to execute
+ IStepGroupable[] groupables = stepGroup.getSteps(getContext());
+
+ IStepGroupIterator iterator = stepGroup.getStepGroupIterator();
+ IFullQualifiedId fullQualifiedIterationId = fullQualifiedGroupId;
+ int iteration = 0;
+
+ if (iterator != null) {
+ iterator.initialize(getContext(), getData(), fullQualifiedGroupId, getMonitor());
+ }
+ boolean next = iterator == null || iterator
+ .hasNext(getContext(), getData(), fullQualifiedGroupId, getMonitor());
+
+ while (next) {
+ if (iterator != null) {
+ fullQualifiedIterationId = fullQualifiedGroupId
+ .createChildId(ID_TYPE_STEP_GROUP_ITERATION_ID, iterator.getId(), "" + iteration); //$NON-NLS-1$
+ iterator.next(getContext(), getData(), fullQualifiedIterationId, getMonitor());
+ }
+ // Execute the steps or step groups.
+ for (IStepGroupable groupable : groupables) {
+ executeGroupable(groupable, statusContainer, executedSteps, fullQualifiedIterationId);
+ }
+ iteration++;
+ next = iterator != null && iterator
+ .hasNext(getContext(), getData(), fullQualifiedGroupId, getMonitor());
+ }
+ }
+
+ /**
+ * Executes a step groupable. The groupable might encapsulate a step or a step group.
+ *
+ * @param step The step groupable. Must not be <code>null</code>.
+ * @param statusContainer A list holding the warnings occurred during the execution. Must not be
+ * <code>null</code>.
+ * @param executedSteps A list holding the id's of the steps executed before. Must not be
+ * <code>null</code>.
+ * @param fullQualifiedParentId The hierarchy of all parent step group id's separated by "::".
+ * Must not be <code>null</code>.
+ *
+ * @throws CoreException If the execution failed.
+ */
+ private void executeGroupable(IStepGroupable groupable, List<IStatus> statusContainer, List<ExecutedContextStep> executedSteps, IFullQualifiedId fullQualifiedParentId) throws CoreException {
+ Assert.isNotNull(groupable);
+ Assert.isNotNull(statusContainer);
+ Assert.isNotNull(executedSteps);
+ Assert.isNotNull(fullQualifiedParentId);
+
+ // Return immediately if the user canceled the monitor in the meanwhile
+ if (isCancelable() && getMonitor() != null && getMonitor().isCanceled()) {
+ rollback(executedSteps, Status.CANCEL_STATUS, getMonitor());
+ throw new CoreException(StatusHelper.getStatus(new OperationCanceledException()));
+ }
+
+ // If the passed in groupable is disabled -> we are done immediately
+ if (groupable.isDisabled()) {
+ CoreBundleActivator
+ .getTraceHandler()
+ .trace("Stepper#executeGroupable: DROPPED DISABLED groupable: id = '" + groupable.getExtension().getId() + "'" //$NON-NLS-1$ //$NON-NLS-2$
+ + ", secondaryId = '" + groupable.getSecondaryId() + "'", //$NON-NLS-1$ //$NON-NLS-2$
+ 0, ITraceIds.TRACE_STEPPING, IStatus.WARNING, this);
+ return;
+ }
+
+ // Check if all dependencies of the groupable have been executed before
+ checkForDependenciesExecuted(groupable, executedSteps);
+
+ if (groupable.getExtension() instanceof IStepGroup) {
+ IFullQualifiedId id = fullQualifiedParentId
+ .createChildId(ID_TYPE_STEP_GROUP_ID, groupable.getExtension().getId(), groupable
+ .getSecondaryId());
+ // If the passed in groupable is associated with a step group
+ // -> get the groupable from that group and execute them
+ executeStepGroup((IStepGroup) groupable.getExtension(), statusContainer, executedSteps, id);
+ }
+ else if (groupable.getExtension() instanceof IStep) {
+ // If the passed in groupable is associated with a step
+ // -> check if the required steps have been executed before,
+ // create a step executor and invoke the executor.
+ IStep step = (IStep) groupable.getExtension();
+
+ IFullQualifiedId id = fullQualifiedParentId
+ .createChildId(ID_TYPE_STEP_ID, step.getId(), groupable
+ .getSecondaryId());
+
+ // Create the step executor now
+ IStepExecutor executor = doCreateStepExecutor(step, groupable.getSecondaryId(), id);
+ Assert.isNotNull(executor);
+
+ try {
+ executedSteps.add(new ExecutedContextStep(id, step));
+ // Invoke the executor now
+ executor.execute(step, id, getContext(), getData(), getMonitor());
+ }
+ catch (Exception e) {
+ // Catch the CoreException first hand as we need to continue the
+ // stepping if the step returned with warnings or information only.
+ CoreException coreException = normalizeStatus(e, statusContainer);
+ // If the exception has been not eaten, rollback previously executed
+ // steps and re-throw the exception.
+ if (coreException != null) {
+ // Rollback everything, if the step(s) are supporting this and
+ // the cleanup hasn't been done already.
+ if (isInitialized()) {
+ rollback(executedSteps, coreException.getStatus(), getMonitor());
+ }
+
+ // Re-throw the exception
+ throw coreException;
+ }
+ }
+ }
+ }
+
+ /**
+ * Checks if all required dependencies have been executed before. If not, the method will throw
+ * an error status.
+ *
+ * @param groupable The groupable. Must not be <code>null</code>.
+ * @param executedSteps A list holding the id's of the steps executed before. Must not be
+ * <code>null</code>.
+ *
+ * @throws CoreException If a dependency has not been executed before.
+ */
+ protected void checkForDependenciesExecuted(IStepGroupable groupable, List<ExecutedContextStep> executedSteps) throws CoreException {
+ Assert.isNotNull(groupable);
+ Assert.isNotNull(executedSteps);
+
+ // Build up the complete list of dependencies.
+ List<String> dependencies = new ArrayList<String>(Arrays.asList(groupable.getDependencies()));
+ // If the groupable wraps a step, the step can have additional dependencies to check
+ if (groupable.getExtension() instanceof IStep) {
+ dependencies.addAll(Arrays.asList(((IStep) groupable.getExtension()).getDependencies()));
+ }
+
+ // Check each dependency now.
+ for (String dependency : dependencies) {
+ // The dependencies might be fully qualified. Split out the primary id.
+ String[] splitted = dependency.split("##", 2); //$NON-NLS-1$
+ String primaryId = splitted.length == 2 ? splitted[0] : dependency;
+
+ // Check if the id is in the list of executed steps. As the list contains
+ // the fully qualified id's, we cannot just check for contained
+ boolean requiredStepExecuted = false;
+ for (ExecutedContextStep step : executedSteps) {
+ if (step.step.getId().equals(primaryId)) {
+ requiredStepExecuted = true;
+ break;
+ }
+ }
+
+ if (!requiredStepExecuted) {
+ throw new CoreException(new Status(IStatus.ERROR, CoreBundleActivator.getUniqueIdentifier(), MessageFormat
+ .format(Messages.Stepper_error_requiredStepNotExecuted, NLS.bind(groupable
+ .getExtension() instanceof IStep ? Messages.Stepper_error_step : Messages.Stepper_error_step, groupable
+ .getExtension().getId()), NLS
+ .bind(Messages.Stepper_error_requiredStepOrGroup, dependency), ""))); //$NON-NLS-1$
+ }
+
+ // Recursive checking is not necessary here as the step or step group
+ // id's would have made it the executed steps list of they missed required
+ // steps or step groups.
+ }
+
+ }
+
+ /**
+ * Rollback the steps previously executed to the failed step. The rollback is executed in
+ * reverse order and the step must be of type {@link IExtendedStep} to participate in the
+ * rollback.
+ *
+ * @param executedSteps
+ * @param progress
+ */
+ protected final void rollback(final List<ExecutedContextStep> executedSteps, final IStatus rollBackStatus, IProgressMonitor progress) {
+ Assert.isNotNull(executedSteps);
+
+ final IProgressMonitor rollbackProgress = ProgressHelper.getProgressMonitor(progress, 1);
+ ProgressHelper.beginTask(rollbackProgress, "Cancel", executedSteps.size()); //$NON-NLS-1$
+ final Callback finalCallback = new Callback();
+ final Callback rollbackCallback = new Callback() {
+ @Override
+ protected void internalDone(Object caller, IStatus status) {
+ if (!executedSteps.isEmpty()) {
+ setProperty(PROPERTY_IS_DONE, false);
+ ExecutedContextStep executedStep = executedSteps
+ .remove(executedSteps.size() - 1);
+ if (executedStep.step instanceof IExtendedStep) {
+ IExtendedStep step = (IExtendedStep) executedStep.step;
+ step.rollback(getContext(), getData(), rollBackStatus, executedStep.id, rollbackProgress, this);
+ }
+ else {
+ this.done(this, status);
+ }
+ }
+ else {
+ finalCallback.done(this, Status.OK_STATUS);
+ }
+ }
+ };
+
+ rollbackCallback.done(this, rollBackStatus);
+ ExecutorsUtil.waitAndExecute(0, finalCallback.getDoneConditionTester(null));
+ }
+
+ /**
+ * Calculates the total work required for the step group. The total work is the sum of the total
+ * work of each sub step. If one of the steps returns {@link IProgressMonitor#UNKNOWN}, the
+ * total work will be unknown for the whole step group.
+ *
+ * @param stepGroup The step group. Must not be <code>null</code>.
+ * @return The total work required or {@link IProgressMonitor#UNKNOWN}.
+ *
+ * @throws CoreException If the total work of the step group cannot be determined.
+ */
+ protected int calculateTotalWork(IStepGroup stepGroup) throws CoreException {
+ Assert.isNotNull(stepGroup);
+
+ int totalWork = 0;
+
+ // Loop the group steps and summarize the returned total work
+ IStepGroupable[] groupables = stepGroup.getSteps(getContext());
+ for (IStepGroupable groupable : groupables) {
+ int work = groupable.getExtension() instanceof IStep ? ((IStep) groupable
+ .getExtension()).getTotalWork(getContext(), getData()) : groupable
+ .getExtension() instanceof IStepGroup ? calculateTotalWork((IStepGroup) groupable
+ .getExtension()) : IProgressMonitor.UNKNOWN;
+
+ if (work == IProgressMonitor.UNKNOWN) {
+ totalWork = IProgressMonitor.UNKNOWN;
+ break;
+ }
+
+ totalWork += work;
+ }
+
+ return totalWork;
+ }
+
+ /**
+ * Normalize the associated status object of the given {@link CoreException}.
+ * <p>
+ * If the associated status contains only WARNING or INFORMATION status objects, the objects are
+ * added to the passed in status container. The passed in exception is dropped and the method
+ * will return <code>null</code>.
+ * <p>
+ * If the associated status contains only OK status objects, the passed in exception and the
+ * associated status are dropped and the method will return <code>null</code>.
+ * <p>
+ * If the associated status contain ERROR status objects, the passed in exception and the
+ * associated status objects are returned if the passed in status container is empty. If the
+ * status container is not empty, a new exception and multi status object is created and
+ * returned. The multi status object will contain all status objects from the status container
+ * and all objects of the originally associated status.
+ * <p>
+ * If the associated status contains a CANCEL status object, the passed in exception and the
+ * associated status objects are returned unmodified.
+ *
+ * @param e The core exception. Must not be <code>null</code>.
+ * @param statusContainer The list of non-severe status objects. Must not be <code>null</code>.
+ * @return The exception to re-throw or <code>null</code>.
+ */
+ private CoreException normalizeStatus(Exception e, List<IStatus> statusContainer) {
+ Assert.isNotNull(statusContainer);
+
+ CoreException coreException = null;
+
+ IStatus status = Status.OK_STATUS;
+ // Get the associated status from the exception
+ if (e instanceof CoreException) {
+ status = ((CoreException) e).getStatus();
+ coreException = (CoreException) e;
+ }
+ else if (e instanceof OperationCanceledException) {
+ status = new Status(IStatus.CANCEL, CoreBundleActivator.getUniqueIdentifier(), e.getLocalizedMessage(), e);
+ coreException = new CoreException(status);
+ }
+ else if (e != null) {
+ status = new Status(IStatus.ERROR, CoreBundleActivator.getUniqueIdentifier(), e.getLocalizedMessage(), e);
+ coreException = new CoreException(status);
+ }
+
+ // Check the severity
+ // PS: MultiStatus.getSeverity() returns always the highest severity.
+ if (status.getSeverity() == IStatus.OK) {
+ // OK -> drop completely and return null
+ coreException = null;
+ }
+ else if (status.getSeverity() == IStatus.CANCEL) {
+ // CANCEL -> Check monitor to be canceled.
+ if (isCancelable()) {
+ if (getMonitor() != null && !getMonitor().isCanceled()) {
+ getMonitor().setCanceled(true);
+ }
+ }
+ }
+ else if (status.getSeverity() == IStatus.WARNING || status.getSeverity() == IStatus.INFO) {
+ // WARNING or INFORMATION -> add to the list and return null
+ statusContainer.add(status);
+ coreException = null;
+ }
+ else if (status.getSeverity() == IStatus.ERROR) {
+ // Error -> If the warnings container not empty, create
+ // a new MultiStatus.
+ if (!statusContainer.isEmpty()) {
+ MultiStatus multiStatus = new MultiStatus(status.getPlugin(), status.getCode(), NLS.bind(Messages.Stepper_multiStatus_finishedWithErrors, getLabel()), null);
+ for (IStatus stat : statusContainer) {
+ multiStatus.merge(stat);
+ }
+ // Re-throw via a new CoreException
+ coreException = new CoreException(multiStatus);
+ }
+ }
+
+ return coreException;
+ }
+}
|