/*
* Eoulsan development code
*
* This code may be freely distributed and modified under the
* terms of the GNU Lesser General Public License version 2.1 or
* later and CeCILL-C. This should be distributed with the code.
* If you do not have a copy, see:
*
* http://www.gnu.org/licenses/lgpl-2.1.txt
* http://www.cecill.info/licences/Licence_CeCILL-C_V1-en.txt
*
* Copyright for this code is held jointly by the Genomic platform
* of the Institut de Biologie de l'École normale supérieure and
* the individual authors. These should be listed in @author doc
* comments.
*
* For more information on the Eoulsan project and its aims,
* or to join the Eoulsan Google group, visit the home page
* at:
*
* http://outils.genomique.biologie.ens.fr/eoulsan
*
*/
package fr.ens.biologie.genomique.eoulsan.core.workflow;
import static com.google.common.base.Preconditions.checkNotNull;
import static fr.ens.biologie.genomique.eoulsan.EoulsanLogger.getLogger;
import static fr.ens.biologie.genomique.eoulsan.core.Step.StepState.CREATED;
import static fr.ens.biologie.genomique.eoulsan.core.Step.StepState.READY;
import static fr.ens.biologie.genomique.eoulsan.core.Step.StepState.WAITING;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import com.google.common.base.Joiner;
import fr.ens.biologie.genomique.eoulsan.core.Step;
import fr.ens.biologie.genomique.eoulsan.core.Step.StepState;
/**
* This class define an observer for step states.
* @author Laurent Jourdren
* @since 2.0
*/
public class StepStateObserver implements Serializable {
private static final long serialVersionUID = -5734184849291521186L;
private final AbstractStep step;
private StepState stepState = CREATED;
private final Set<AbstractStep> requiredSteps = new HashSet<>();
private final Set<AbstractStep> stepsToInform = new HashSet<>();
/**
* Add a dependency.
* @param step the dependency
*/
public void addDependency(final AbstractStep step) {
this.requiredSteps.add(step);
step.getStepStateObserver().stepsToInform.add(this.step);
}
/**
* Get the required steps.
* @return a set with the required steps
*/
public Set<AbstractStep> getRequiredSteps() {
return Collections.unmodifiableSet(this.requiredSteps);
}
/**
* Get the state of the step.
* @return the state of the step
*/
public StepState getState() {
return this.stepState;
}
/**
* Set the state of the step.
* @param state the new state of the step
*/
public void setState(final StepState state) {
// Do nothing if the state has not changed or if the current state is a
// final state
if (state == null
|| state == CREATED || this.stepState == state
|| this.stepState.isFinalState()) {
return;
}
// Do not change the state to READY if the step is already working
if (state == READY && this.stepState.isWorkingState()) {
return;
}
// Save current state
final StepState previousState = this.stepState;
// If is the root step, there is nothing to wait
synchronized (this) {
if (this.step.getType() == Step.StepType.ROOT_STEP && state == WAITING) {
this.stepState = READY;
} else {
// Set the new state
this.stepState = state;
}
}
// Log the new state of the step
getLogger().fine("Step #"
+ this.step.getNumber() + " " + this.step.getId() + " is now in state "
+ this.stepState + " (previous state was " + previousState + ")");
// Log dependencies when step is in WAITING state
if (this.stepState == WAITING) {
logDependencies();
}
// Inform step that depend of this step
if (this.stepState.isDoneState()) {
for (AbstractStep step : this.stepsToInform) {
step.getStepStateObserver().updateStatus();
}
}
// Start Token manager thread for the step if state is READY
if (this.stepState == READY) {
TokenManagerRegistry.getInstance().getTokenManager(this.step).start();
}
// Inform workflow object
this.step.getAbstractWorkflow().updateStepState(this.step);
// Inform listeners
for (StepObserver o : StepObserverRegistry.getInstance().getObservers()) {
o.notifyStepState(this.step);
}
}
/**
* Update the status of the step to READY if all the dependency of this step
* are in DONE state.
*/
private void updateStatus() {
// Do nothing if the step is already in READY state
if (getState() == READY) {
return;
}
for (AbstractStep step : this.requiredSteps) {
if (!(step.getState().isDoneState())) {
return;
}
}
// Set the step to the READY state
setState(READY);
}
/**
* Log dependencies of step.
*/
void logDependencies() {
String msg = "Step #"
+ this.step.getNumber() + " " + this.step.getId()
+ " has the following dependencies: ";
List<String> list = new ArrayList<>();
for (AbstractStep step : this.requiredSteps) {
list.add("step #" + step.getNumber() + " " + step.getId());
}
if (list.isEmpty()) {
msg += "no dependencies";
} else {
msg += Joiner.on(", ").join(list);
}
getLogger().fine(msg);
}
//
// Constructor
//
/**
* Constructor.
* @param step the step related to the instance
*/
public StepStateObserver(final AbstractStep step) {
checkNotNull(step, "step cannot be null");
this.step = step;
getLogger().fine("Step #"
+ this.step.getNumber() + " " + this.step.getId() + " is now in state "
+ this.stepState);
}
}