/******************************************************************************* * Copyright (c) 2008, 2011 VMware 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: * VMware Inc. - initial contribution * EclipseSource - Bug 358442 Change InstallArtifact graph from a tree to a DAG *******************************************************************************/ package org.eclipse.virgo.kernel.install.pipeline.internal; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.eclipse.virgo.kernel.osgi.framework.UnableToSatisfyBundleDependenciesException; import org.eclipse.virgo.nano.deployer.api.core.DeployerLogEvents; import org.eclipse.virgo.nano.deployer.api.core.DeploymentException; import org.eclipse.virgo.kernel.install.artifact.InstallArtifact; import org.eclipse.virgo.kernel.install.environment.InstallEnvironment; import org.eclipse.virgo.kernel.install.pipeline.Pipeline; import org.eclipse.virgo.kernel.install.pipeline.stage.PipelineStage; import org.eclipse.virgo.util.common.GraphNode; /** * {@link CompensatingPipeline} is a {@link Pipeline} which runs like any other pipeline but if one of its stages throws * an exception, it runs a compensation <code>PipelineStage</code> and then re-throws the exception. * <p /> * For example, consider the following pipeline: * * <pre> * P = beginX -> X1 -> X2 -> X3 -> endX * </pre> * * and suppose we need to drive a failure event using a pipeline stage failX when any of X1, X2, X3 throw an exception. * Then we can use a <code>CompensatingPipeline</code> to produce the required behaviour: * * <pre> * P' = beginX -> CompensatingPipeline(X1 -> X2 -> X3, failX) -> endX * </pre> * <p /> * * <strong>Concurrent Semantics</strong><br /> * * This class is thread safe. * */ final class CompensatingPipeline extends StandardPipeline { private final Logger logger = LoggerFactory.getLogger(this.getClass()); private final PipelineStage compensation; /** * Create a {@link CompensatingPipeline} with no pipeline stages and a given compensation stage. * @param compensation stage for compensation */ public CompensatingPipeline(PipelineStage compensation) { super(); this.compensation = compensation; } /** * {@inheritDoc} */ @Override protected void doProcessGraph(GraphNode<InstallArtifact> installGraph, InstallEnvironment installEnvironment) throws DeploymentException, UnableToSatisfyBundleDependenciesException { try { super.doProcessGraph(installGraph, installEnvironment); } catch (DeploymentException de) { compensate(installGraph, installEnvironment, de); throw de; } catch (UnableToSatisfyBundleDependenciesException utsbde) { compensate(installGraph, installEnvironment, utsbde); throw utsbde; } catch (RuntimeException re) { compensate(installGraph, installEnvironment, re); throw re; } } private void compensate(GraphNode<InstallArtifact> installGraph, InstallEnvironment installEnvironment, Exception e) { try { if (!(e instanceof DeploymentException) || !((DeploymentException)e).isDiagnosed()) { installEnvironment.getInstallLog().logFailure(DeployerLogEvents.INSTALL_FAILURE, e); } else { installEnvironment.getInstallLog().logFailure(DeployerLogEvents.INSTALL_FAILURE, null); } this.compensation.process(installGraph, installEnvironment); } catch (Exception ex) { logger.warn(String.format("exception thrown while compensating for '%s'", e.getMessage()), ex); } } }