/* * Copyright (C) 2006-2016 DLR, Germany * * All rights reserved * * http://www.rcenvironment.de/ */ package de.rcenvironment.core.component.workflow.execution.internal; import java.util.Collections; import java.util.HashSet; import java.util.Set; import de.rcenvironment.core.component.execution.api.ComponentState; import de.rcenvironment.core.component.execution.api.ConsoleRow; /** * Default implementation of {@link ComponentStatesChangedEntirelyVerifier}. * * @author Doreen Seider */ public class ComponentStatesChangedEntirelyVerifier { private Set<ComponentStatesChangedEntirelyListener> compStatesEntirelyChangedListeners = new HashSet<>(); private final ComponentStatesChangedEntirelyNotifier preparedComponentStateNotifier; private final ComponentStatesChangedEntirelyNotifier pausedComponentStateNotifier; private final ComponentStatesChangedEntirelyNotifier resumedComponentStateNotifier; private final ComponentStatesChangedEntirelyNotifier finishedComponentStateNotifier; private final ComponentStatesChangedEntirelyNotifier finalComponentStateNotifier; private final ComponentStatesChangedEntirelyNotifier disposedComponentStateNotifier; private final Set<String> lostComponents; private final ComponentStatesChangedEntirelyNotifier lastConsoleRowNotifier; protected ComponentStatesChangedEntirelyVerifier(int componentCount) { preparedComponentStateNotifier = new PreparedComponentStateNotifier(componentCount); pausedComponentStateNotifier = new PausedComponentStateNotifier(componentCount); resumedComponentStateNotifier = new ResumedComponentStateNotifier(componentCount); finishedComponentStateNotifier = new FinishedComponentStateNotifier(componentCount); finalComponentStateNotifier = new FinalComponentStateNotifier(componentCount); disposedComponentStateNotifier = new DisposedComponentStateNotifier(componentCount); lostComponents = new HashSet<>(); lastConsoleRowNotifier = new LastConsoleRowNotifier(componentCount); } /** * Adds a {@link ComponentStatesChangedEntirelyListener}. * * @param listener {@link ComponentStatesChangedEntirelyListener} to notify */ public void addListener(ComponentStatesChangedEntirelyListener listener) { compStatesEntirelyChangedListeners.add(listener); } /** * Announces a {@link ComponentState} that should be considered by the underlying notifiers that are responsible to callback the * appropriate {@link ComponentStatesChangedEntirelyListener}. * * @param compExecutionId execution identifier of component affected * @param compState {@link ComponentState} to consider */ public void announceComponentState(String compExecutionId, ComponentState compState) { switch (compState) { case PREPARED: preparedComponentStateNotifier.addComponentInDesiredState(compExecutionId); break; case PAUSED: if (pausedComponentStateNotifier.isEnabled()) { pausedComponentStateNotifier.addComponentInDesiredState(compExecutionId); } break; case FINISHED: case FINISHED_WITHOUT_EXECUTION: finishedComponentStateNotifier.addComponentInDesiredState(compExecutionId); case CANCELED: case FAILED: case RESULTS_REJECTED: finalComponentStateNotifier.addComponentInDesiredState(compExecutionId); if (pausedComponentStateNotifier.isEnabled()) { pausedComponentStateNotifier.addComponentInDesiredState(compExecutionId); } break; case DISPOSED: disposedComponentStateNotifier.addComponentInDesiredState(compExecutionId); break; default: break; } if (resumedComponentStateNotifier.isEnabled() && !ComponentState.RESUMING.equals(compState)) { resumedComponentStateNotifier.addComponentInDesiredState(compExecutionId); } } /** * Announces a final {@link ComponentState} that should be considered by the underlying notifiers that are responsible to callback the * appropriate {@link ComponentStatesChangedEntirelyListener}. * * @param compExecutionId execution identifier of component affected */ public void accounceComponentInAnyFinalState(String compExecutionId) { finalComponentStateNotifier.addComponentInDesiredState(compExecutionId); } /** * Announces the last {@link ConsoleRow} of a component. That event is considered by the underlying notifiers that are responsible to * callback the appropriate {@link ComponentStatesChangedEntirelyListener}. * * @param compExecutionId execution identifier of component affected */ public void announceLastConsoleRow(String compExecutionId) { lastConsoleRowNotifier.addComponentInDesiredState(compExecutionId); } /** * Announces lost component(s). * * @param compExecutionIds execution identifiers of lost components */ public void announceLostComponents(Set<String> compExecutionIds) { for (ComponentStatesChangedEntirelyListener compStatesEntirelyChangedListener : compStatesEntirelyChangedListeners) { compStatesEntirelyChangedListener.onComponentsLost(compExecutionIds); } lostComponents.addAll(compExecutionIds); } /** * Declares components as being in final component state and disposed that are currently known as lost. */ public void declareLostComponentsAsBeingInFinalStateAndDisposed() { finalComponentStateNotifier.addComponentsInDesiredState(lostComponents); disposedComponentStateNotifier.addComponentsInDesiredState(lostComponents); lastConsoleRowNotifier.addComponentsInDesiredState(lostComponents); } /** * @param compExecutionId execution identifier of component affected * @return <code>true</code> if component is or was in a final component state */ public boolean isComponentInFinalState(String compExecutionId) { return finalComponentStateNotifier.isComponentInDesiredState(compExecutionId); } /** * @param compExecutionId execution identifier of component affected * @return <code>true</code> if component is {@link ComponentState#DISPOSED} */ public boolean isComponentDisposed(String compExecutionId) { return disposedComponentStateNotifier.isComponentInDesiredState(compExecutionId); } public Set<String> getLostComponents() { return Collections.unmodifiableSet(lostComponents); } public Set<String> getComponentsInFinalState() { return finalComponentStateNotifier.getComponentsInDesiredState(); } public Set<String> getDisposedComponents() { return disposedComponentStateNotifier.getComponentsInDesiredState(); } /** * Enables verification of {@link ComponentState#PAUSED}. */ public void enablePausedComponentStateVerification() { pausedComponentStateNotifier.clearComponentsInDesiredState(); pausedComponentStateNotifier.setEnabled(true); pausedComponentStateNotifier.addComponentsInDesiredState(finalComponentStateNotifier.getComponentsInDesiredState()); } /** * Enables verification of resumed {@link ComponentState}s. */ public void enableResumedComponentStateVerification() { resumedComponentStateNotifier.clearComponentsInDesiredState(); resumedComponentStateNotifier.setEnabled(true); resumedComponentStateNotifier.addComponentsInDesiredState(finalComponentStateNotifier.getComponentsInDesiredState()); } /** * Abstract implementation of specific component state change notifiers. It accepts component execution identifiers and adds them to an * underlying set. If the size of the set is equal to the component count of the workflow the * {@link ComponentStatesChangedEntirelyNotifier#onComponentStatesChangedEntirely()} is called. * * @author Doreen Seider */ private abstract class ComponentStatesChangedEntirelyNotifier { private final int compCount; private boolean enabled = false; private Set<String> componentsInDesiredState = Collections.synchronizedSet(new HashSet<String>() { private static final long serialVersionUID = 6431615134151724870L; @Override public boolean add(String e) { boolean rv = super.add(e); if (size() == compCount) { onComponentStatesChangedEntirely(); } return rv; }; }); private ComponentStatesChangedEntirelyNotifier(int componentCount, boolean enabled) { this.compCount = componentCount; this.enabled = enabled; } private synchronized void clearComponentsInDesiredState() { componentsInDesiredState.clear(); } private synchronized void addComponentInDesiredState(String executionIdentifier) { if (enabled) { componentsInDesiredState.add(executionIdentifier); } } private synchronized void addComponentsInDesiredState(Set<String> executionIdentifiers) { if (enabled) { componentsInDesiredState.addAll(executionIdentifiers); } } private synchronized Set<String> getComponentsInDesiredState() { return Collections.unmodifiableSet(componentsInDesiredState); } private synchronized boolean isComponentInDesiredState(String executionIdentifier) { return componentsInDesiredState.contains(executionIdentifier); } protected synchronized void setEnabled(boolean enabled) { this.enabled = enabled; } private synchronized boolean isEnabled() { return enabled; } abstract void onComponentStatesChangedEntirely(); } /** * Implementation of {@link ComponentStatesChangedEntirelyNotifier} for {@link ComponentState#PREPARED}. * * @author Doreen Seider */ private final class PreparedComponentStateNotifier extends ComponentStatesChangedEntirelyNotifier { private PreparedComponentStateNotifier(int componentCount) { super(componentCount, true); } @Override protected void onComponentStatesChangedEntirely() { for (ComponentStatesChangedEntirelyListener compStatesEntirelyChangedListener : compStatesEntirelyChangedListeners) { compStatesEntirelyChangedListener.onComponentStatesChangedCompletelyToPrepared(); } } } /** * Implementation of {@link ComponentStatesChangedEntirelyNotifier} for {@link ComponentState#PAUSED}. * * @author Doreen Seider */ private final class PausedComponentStateNotifier extends ComponentStatesChangedEntirelyNotifier { private PausedComponentStateNotifier(int componentCount) { super(componentCount, false); } @Override protected void onComponentStatesChangedEntirely() { for (ComponentStatesChangedEntirelyListener compStatesEntirelyChangedListener : compStatesEntirelyChangedListeners) { compStatesEntirelyChangedListener.onComponentStatesChangedCompletelyToPaused(); } setEnabled(false); } } /** * Implementation of {@link ComponentStatesChangedEntirelyNotifier} to all {@link ComponentState}s a component can be in after it was * paused. * * @author Doreen Seider */ private final class ResumedComponentStateNotifier extends ComponentStatesChangedEntirelyNotifier { private ResumedComponentStateNotifier(int componentCount) { super(componentCount, false); } @Override protected void onComponentStatesChangedEntirely() { for (ComponentStatesChangedEntirelyListener compStatesEntirelyChangedListener : compStatesEntirelyChangedListeners) { compStatesEntirelyChangedListener.onComponentStatesChangedCompletelyToResumed(); } setEnabled(false); } } /** * Implementation of {@link ComponentStatesChangedEntirelyNotifier} for {@link ComponentState#FINISHED} and * {@link ComponentState#FINISHED_WITHOUT_EXECUTION}. * * @author Doreen Seider */ private final class FinishedComponentStateNotifier extends ComponentStatesChangedEntirelyNotifier { private FinishedComponentStateNotifier(int componentCount) { super(componentCount, true); } @Override protected void onComponentStatesChangedEntirely() { for (ComponentStatesChangedEntirelyListener compStatesEntirelyChangedListener : compStatesEntirelyChangedListeners) { compStatesEntirelyChangedListener.onComponentStatesChangedCompletelyToFinished(); } } } /** * Implementation of {@link ComponentStatesChangedEntirelyNotifier} for any final {@link ComponentState} ( * {@link ComponentState#FINISHED}, {@link ComponentState#FINISHED_WITHOUT_EXECUTION}, {@link ComponentState#CANCELED}, * {@link ComponentState#FAILED}). * * @author Doreen Seider */ private final class FinalComponentStateNotifier extends ComponentStatesChangedEntirelyNotifier { private FinalComponentStateNotifier(int componentCount) { super(componentCount, true); } @Override protected void onComponentStatesChangedEntirely() { for (ComponentStatesChangedEntirelyListener compStatesEntirelyChangedListener : compStatesEntirelyChangedListeners) { compStatesEntirelyChangedListener.onComponentStatesChangedCompletelyToAnyFinalState(); } } } /** * Implementation of {@link ComponentStatesChangedEntirelyNotifier} for {@link ComponentState#DISPOSED}. * * @author Doreen Seider */ private final class DisposedComponentStateNotifier extends ComponentStatesChangedEntirelyNotifier { private DisposedComponentStateNotifier(int componentCount) { super(componentCount, true); } @Override protected void onComponentStatesChangedEntirely() { for (ComponentStatesChangedEntirelyListener compStatesEntirelyChangedListener : compStatesEntirelyChangedListeners) { compStatesEntirelyChangedListener.onComponentStatesChangedCompletelyToDisposed(); } } } /** * Implementation of {@link ComponentStatesChangedEntirelyNotifier} for {@link ComponentState#DISPOSED}. * * @author Doreen Seider */ private final class LastConsoleRowNotifier extends ComponentStatesChangedEntirelyNotifier { private LastConsoleRowNotifier(int componentCount) { super(componentCount, true); } @Override protected void onComponentStatesChangedEntirely() { for (ComponentStatesChangedEntirelyListener compStatesEntirelyChangedListener : compStatesEntirelyChangedListeners) { compStatesEntirelyChangedListener.onLastConsoleRowsReceived(); } } } }