/******************************************************************************* * Copyright (c) 2008, 2010 VMware Inc. * 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 *******************************************************************************/ package org.eclipse.virgo.kernel.deployer.core.internal; import java.util.ArrayList; import java.util.Collections; import java.util.List; import java.util.Set; import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicInteger; import org.eclipse.virgo.nano.core.AbortableSignal; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * {@link AbortableSignalJunction} provides a collection of signals of a given size that join to drive a given signal. The given * signal is driven for completion when any of the signals in the collection is driven for failure or all the signals in * the collection are driven for successful completion. The given signal is driven at most once. * <p /> * * <strong>Concurrent Semantics</strong><br /> * * This class is thread safe. * */ public final class AbortableSignalJunction { private static final Logger LOGGER = LoggerFactory.getLogger(AbortableSignalJunction.class); private final AbortableSignal signal; private final List<AbortableSignal> subSignals; private final AtomicInteger incompleteCount; private final AtomicBoolean failureSignalled = new AtomicBoolean(false); private final AtomicBoolean abortionSignalled = new AtomicBoolean(false); /** * Constructs a {@link AbortableSignalJunction} of the given size for the given signal. * * @param signal the signal to be controlled * @param size the number of signals in the collection */ public AbortableSignalJunction(AbortableSignal signal, int size) { this.signal = signal; List<AbortableSignal> s = new ArrayList<AbortableSignal>(); for (int i = 0; i < size; i++) { s.add(new SubSignal()); } this.subSignals = Collections.unmodifiableList(s); this.incompleteCount = new AtomicInteger(size); if (size <= 0) { if (this.signal != null) { this.signal.signalSuccessfulCompletion(); } } } /** * Gets the signals in the collection. * * @return an unmodifiable {@link Set} of signals in the collection */ public List<AbortableSignal> getSignals() { return this.subSignals; } public boolean failed() { return this.failureSignalled.get(); } public boolean aborted() { return this.abortionSignalled.get(); } private void subSignalFailed(Throwable cause) { // Ensure the incomplete count is zero. int i = AbortableSignalJunction.this.incompleteCount.get(); LOGGER.debug("SubSignal failed. {} has {} incomplete signals", this, i); while (i > 0 && !AbortableSignalJunction.this.incompleteCount.compareAndSet(i, 0)) { i = AbortableSignalJunction.this.incompleteCount.get(); } // If this invocation took the count to zero, drive failure. if (i > 0) { if (AbortableSignalJunction.this.signal != null) { LOGGER.debug("{} signalling failure", this); AbortableSignalJunction.this.signal.signalFailure(cause); this.failureSignalled.set(true); } } } private void subSignalAborted() { // Ensure the incomplete count is zero. int i = AbortableSignalJunction.this.incompleteCount.get(); LOGGER.debug("SubSignal aborted. {} has {} incomplete signals", this, i); while (i > 0 && !AbortableSignalJunction.this.incompleteCount.compareAndSet(i, 0)) { i = AbortableSignalJunction.this.incompleteCount.get(); } // If this invocation took the count to zero, drive failure. if (i > 0) { if (AbortableSignalJunction.this.signal != null) { LOGGER.debug("{} signalling aborted", this); AbortableSignalJunction.this.signal.signalAborted(); this.abortionSignalled.set(true); } } } private void subSignalSucceeded() { // Decrement the incomplete count. int incomplete = AbortableSignalJunction.this.incompleteCount.decrementAndGet(); LOGGER.debug("SubSignal succeeded. {} now has {} incomplete signals", this, incomplete); if (incomplete == 0) { // If this invocation took the count to zero, drive successful completion. if (AbortableSignalJunction.this.signal != null) { LOGGER.debug("{} has no incomplete signals. Signalling success", this); AbortableSignalJunction.this.signal.signalSuccessfulCompletion(); } } } /** * {@link SubSignal} is a signal that reports completion to its {@link AbortableSignalJunction} once and only once. * */ private class SubSignal implements AbortableSignal { private final AtomicBoolean complete = new AtomicBoolean(false); public void signalFailure(Throwable cause) { if (this.complete.compareAndSet(false, true)) { LOGGER.debug("SubSignal {} signalling failure", this); subSignalFailed(cause); } } public void signalSuccessfulCompletion() { if (this.complete.compareAndSet(false, true)) { LOGGER.debug("SubSignal {} signalling success", this); subSignalSucceeded(); } } public void signalAborted() { if (this.complete.compareAndSet(false, true)) { LOGGER.debug("SubSignal {} signalling abortion", this); subSignalAborted(); } } } }