/*
* Copyright (C) 2006-2016 DLR, Germany
*
* All rights reserved
*
* http://www.rcenvironment.de/
*/
package de.rcenvironment.core.component.execution.internal;
import java.util.Collections;
import java.util.Deque;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicReference;
import org.apache.commons.logging.LogFactory;
import de.rcenvironment.core.component.api.ComponentConstants;
import de.rcenvironment.core.component.api.LoopComponentConstants;
import de.rcenvironment.core.component.execution.api.ComponentExecutionContext;
import de.rcenvironment.core.component.execution.api.ComponentExecutionException;
import de.rcenvironment.core.component.model.endpoint.api.EndpointDatum;
import de.rcenvironment.core.component.model.endpoint.api.EndpointDefinition;
import de.rcenvironment.core.component.model.endpoint.api.EndpointDescription;
import de.rcenvironment.core.component.model.endpoint.api.EndpointDescriptionsManager;
import de.rcenvironment.core.component.model.endpoint.api.EndpointGroupDefinition;
import de.rcenvironment.core.component.model.endpoint.api.EndpointGroupDescription;
import de.rcenvironment.core.component.model.endpoint.impl.EndpointDatumImpl;
import de.rcenvironment.core.datamodel.api.DataType;
import de.rcenvironment.core.datamodel.api.TypedDatumConverter;
import de.rcenvironment.core.datamodel.api.TypedDatumFactory;
import de.rcenvironment.core.datamodel.api.TypedDatumService;
import de.rcenvironment.core.datamodel.types.api.NotAValueTD;
import de.rcenvironment.core.utils.common.StringUtils;
/**
* Defines the scheduling state of a component. Inputs received are intended to be add immediately. There are validated and queued. If the
* {@link ComponentExecutionScheduler} is enabled, the scheduling state is calculated on each new input. If disabled, nothing is calculated
* when a new input was queued. The calculation starts immediately when the {@link ComponentExecutionScheduler} is enabled again.
*
* If input values are fetched, they are removed from the stack of input values except if the input is defined as constant.
*
* @author Doreen Seider
*/
public class ComponentExecutionScheduler {
private static TypedDatumFactory typedDatumFactory;
private static TypedDatumConverter typedDatumConverter;
private ComponentExecutionRelatedInstances compExeRelatedInstances;
private final Deque<EndpointDatum> validatedEndpointDatumsToProcess = new LinkedList<>();
private final Map<String, DataType> endpointDataTypes = new HashMap<>();
private final Map<String, EndpointGroupDescription> endpointGroupDescriptions = new HashMap<>();
private final Map<String, EndpointDatum> inputsOccupied = Collections.synchronizedMap(new HashMap<String, EndpointDatum>());
private final Map<String, Deque<EndpointDatum>> endpointDatums = new HashMap<>();
private final Set<String> queuedConsumingInputs = new HashSet<>();
private final Set<String> consumingInputs = new HashSet<>();
private final Set<String> constantInputs = new HashSet<>();
private final Set<String> constantInputsProcessed = Collections.synchronizedSet(new HashSet<String>());
private final Set<String> requiredInputsOrGroups = new HashSet<>();
private final Set<String> notRequiredInputs = new HashSet<>();
private final Set<String> inputsWithValue = new HashSet<>();
private final Map<String, Set<String>> groups = new HashMap<>();
private final Set<String> finishedInputs = new HashSet<>();
private final Map<String, Set<String>> idsOfNotAValueDatumsReceived = new HashMap<>();
private final Set<String> idsNotAValueDatumsSent = Collections.synchronizedSet(new HashSet<String>());
private final AtomicBoolean loopResetRequested = new AtomicBoolean(false);
private final Set<String> resetDataIdsSent = Collections.synchronizedSet(new HashSet<String>());
private final AtomicBoolean loopReset = new AtomicBoolean(false);
private final AtomicReference<EndpointDatum> resetDatumToFoward = new AtomicReference<>(null);
private final AtomicReference<EndpointDatum> failureDatumToFoward = new AtomicReference<>(null);
private int inputsCount = 0;
private Set<String> inputsConsideredForFinished = new HashSet<>();
private AtomicReference<State> state = new AtomicReference<>(State.IDLING);
private boolean isEnabled = false;
private volatile boolean schedulingFailed = false;
/**
* States a component can be form a scheduling perspective.
*
* @author Doreen Seider
*/
protected enum State {
IDLING,
PROCESS_INPUT_DATA,
PROCESS_INPUT_DATA_WITH_NOT_A_VALUE_DATA,
FINISHED,
RESET,
FAILURE_FORWARD,
LOOP_RESET;
}
@Deprecated
public ComponentExecutionScheduler() {}
protected ComponentExecutionScheduler(ComponentExecutionRelatedInstances compExeRelatedInstances) {
this.compExeRelatedInstances = compExeRelatedInstances;
}
protected void initialize(ComponentExecutionContext compExeContext) throws ComponentExecutionException {
EndpointDescriptionsManager inputDescriptionsManager = compExeContext.getComponentDescription().getInputDescriptionsManager();
for (EndpointGroupDescription groupDescription : inputDescriptionsManager.getEndpointGroupDescriptions()) {
endpointGroupDescriptions.put(groupDescription.getName(), groupDescription);
}
int inputsOuterCount = 0;
Set<String> inputsSame = new HashSet<>();
for (EndpointDescription endpointDescription : inputDescriptionsManager.getEndpointDescriptions()) {
switch (endpointDescription.getEndpointDefinition().getEndpointCharacter()) {
case OUTER_LOOP:
inputsOuterCount++;
break;
case SAME_LOOP:
inputsSame.add(endpointDescription.getName());
break;
default:
throw new IllegalArgumentException(
"Endpoint type unknown: " + endpointDescription.getEndpointDefinition().getEndpointCharacter());
}
endpointDataTypes.put(endpointDescription.getName(), endpointDescription.getDataType());
endpointDatums.put(endpointDescription.getName(), new LinkedList<EndpointDatum>());
Map<String, String> metaData = endpointDescription.getMetaData();
String inputHandling = metaData.get(ComponentConstants.INPUT_METADATA_KEY_INPUT_DATUM_HANDLING);
if (inputHandling == null) {
inputHandling = endpointDescription.getEndpointDefinition().getDefaultInputDatumHandling().name();
}
if (inputHandling.equals(EndpointDefinition.InputDatumHandling.Constant.name())) {
constantInputs.add(endpointDescription.getName());
} else {
consumingInputs.add(endpointDescription.getName());
if (inputHandling.equals(EndpointDefinition.InputDatumHandling.Queue.name())) {
queuedConsumingInputs.add(endpointDescription.getName());
}
}
String inputExecutionConstraint = metaData.get(ComponentConstants.INPUT_METADATA_KEY_INPUT_EXECUTION_CONSTRAINT);
if (inputExecutionConstraint == null) {
inputExecutionConstraint = endpointDescription.getEndpointDefinition()
.getDefaultInputExecutionConstraint().name();
}
if (inputExecutionConstraint.equals(EndpointDefinition.InputExecutionContraint.RequiredIfConnected.name())) {
if (endpointDescription.isConnected()) {
addToRequiredInputsOrGroups(inputDescriptionsManager, endpointDescription);
}
} else if (inputExecutionConstraint.equals(EndpointDefinition.InputExecutionContraint.None.name())) {
if (endpointDescription.getParentGroupName() == null) {
throw new ComponentExecutionException(StringUtils.format(
"Input '%s' of component '%s' is declared as not required, but it is not part of an input group of type 'or'",
endpointDescription.getName(), compExeContext.getInstanceName()));
} else if (endpointDescription.isConnected()) {
addToRequiredInputsOrGroups(inputDescriptionsManager, endpointDescription);
}
} else if (inputExecutionConstraint.equals(EndpointDefinition.InputExecutionContraint.NotRequired.name())) {
if (endpointDescription.isConnected()) {
addToNotRequiredInputs(endpointDescription);
}
} else {
if (!endpointDescription.isConnected()) {
throw new ComponentExecutionException(StringUtils.format("The execution constraint of input '%s' of component '%s' "
+ "is declared as 'required', but the input is not connected to an output. Either connect it to an output or "
+ "alter its execution constraint (e.g., to 'required if connected') or delete the input at all. Note: "
+ "The two latter options might not be applicable in this particular case.",
endpointDescription.getName(), compExeContext.getInstanceName()));
}
addToRequiredInputsOrGroups(inputDescriptionsManager, endpointDescription);
}
}
if (!isDriver(compExeContext) && inputsOuterCount > 0 || isNestedDriver(compExeContext)) {
inputsConsideredForFinished.removeAll(inputsSame);
}
}
boolean isNestedDriver(ComponentExecutionContext compExeContext) {
return Boolean.valueOf(compExeContext.getComponentDescription().getConfigurationDescription()
.getConfigurationValue(LoopComponentConstants.CONFIG_KEY_IS_NESTED_LOOP));
}
boolean isDriver(ComponentExecutionContext compExeContext) {
return compExeContext.getComponentDescription().getComponentInterface().getIsLoopDriver();
}
protected synchronized void validateAndQueueEndpointDatum(EndpointDatum datum) {
try {
checkDataType(datum);
checkIfConstantAndSingleConstraintIsMatched(datum);
validatedEndpointDatumsToProcess.add(datum);
} catch (ComponentExecutionException e) {
postSchedulingFailedEvent(e);
return;
}
if (isEnabled) {
updateSchedulingState();
}
}
private void postSchedulingFailedEvent(ComponentExecutionException e) {
if (!schedulingFailed) {
compExeRelatedInstances.compStateMachine
.postEvent(new ComponentStateMachineEvent(ComponentStateMachineEventType.SCHEDULING_FAILED, e));
schedulingFailed = true;
isEnabled = false;
}
}
private void postNewSchedulingStateEvent() {
compExeRelatedInstances.compStateMachine
.postEvent(new ComponentStateMachineEvent(ComponentStateMachineEventType.NEW_SCHEDULING_STATE));
isEnabled = false;
}
protected synchronized boolean isEnabled() {
return isEnabled;
}
protected synchronized void enable() {
if (isEnabled) { // sanity check
LogFactory.getLog(getClass())
.warn("Component execution scheduler was requested to get enabled even if it is already enabled; ignored enabling request");
return;
}
setEnabled(true);
}
protected synchronized void disable() {
setEnabled(false);
}
private void setEnabled(boolean enabled) {
this.isEnabled = enabled;
if (isEnabled) {
updateSchedulingState();
}
}
protected State getSchedulingState() {
return state.get();
}
private void checkDataType(EndpointDatum endpointDatum) throws ComponentExecutionException {
DataType sourceDataType = endpointDatum.getValue().getDataType();
DataType targetDataType = endpointDataTypes.get(endpointDatum.getInputName());
if (sourceDataType != DataType.Internal && sourceDataType != DataType.NotAValue && sourceDataType != targetDataType
&& !typedDatumConverter.isConvertibleTo(sourceDataType, targetDataType)) {
throw new ComponentExecutionException(StringUtils.format(
"Value of type '%s' at input '%s' received that is not convertible to expected data type '%s'", sourceDataType,
endpointDatum.getInputName(), targetDataType));
}
}
private void checkIfConstantAndSingleConstraintIsMatched(EndpointDatum endpointDatum) throws ComponentExecutionException {
if (endpointDatum.getValue().getDataType() != DataType.Internal) {
synchronized (inputsOccupied) {
if (inputsOccupied.containsKey(endpointDatum.getInputName())) {
if (constantInputs.contains(endpointDatum.getInputName())) {
throw new ComponentExecutionException(StringUtils.format(
"A second value at input '%s' of type 'constant' received. Only one value is allowed. "
+ "First: %s. Second: %s. (Except in inner loops. There, one value is allowed for each inner loop run.)",
endpointDatum.getInputName(), inputsOccupied.get(endpointDatum.getInputName()), endpointDatum));
} else if (!queuedConsumingInputs.contains(endpointDatum.getInputName())) {
throw new ComponentExecutionException(StringUtils.format(
"A new value at input '%s' of type 'single' received, but the current one was not consumed yet. "
+ "Current: %s. New: %s. Queue of values is not allowed at inputs of type 'single'. "
+ "Use input type 'queue' if queuing is allowed and intended.",
endpointDatum.getInputName(), inputsOccupied.get(endpointDatum.getInputName()), endpointDatum));
}
} else {
if (constantInputs.contains(endpointDatum.getInputName())
|| !queuedConsumingInputs.contains(endpointDatum.getInputName())) {
inputsOccupied.put(endpointDatum.getInputName(), endpointDatum);
}
}
}
}
}
private void addToNotRequiredInputs(EndpointDescription endpointDescription) {
increaseInputCount(endpointDescription);
notRequiredInputs.add(endpointDescription.getName());
}
private void addToRequiredInputsOrGroups(EndpointDescriptionsManager endpointDescriptionsManager,
EndpointDescription endpointDescription) {
increaseInputCount(endpointDescription);
if (endpointDescription.getParentGroupName() == null || endpointDescription.getParentGroupName().equals("null")) {
requiredInputsOrGroups.add(endpointDescription.getName());
} else {
requiredInputsOrGroups.add(getTopLevelGroup(endpointDescriptionsManager, endpointDescription.getParentGroupName()));
fillGroups(endpointDescriptionsManager, endpointDescription.getName(), endpointDescription.getParentGroupName());
}
}
private void increaseInputCount(EndpointDescription endpointDescription) {
inputsCount++;
inputsConsideredForFinished.add(endpointDescription.getName());
}
private void fillGroups(EndpointDescriptionsManager endpointDescriptionsManager, String inputOrGroupName, String groupName) {
if (!groups.containsKey(groupName)) {
groups.put(groupName, new HashSet<String>());
}
groups.get(groupName).add(inputOrGroupName);
if (endpointDescriptionsManager.getEndpointGroupDescription(groupName).getParentGroupName() != null) {
fillGroups(endpointDescriptionsManager, groupName, endpointDescriptionsManager
.getEndpointGroupDescription(groupName).getParentGroupName());
}
}
private String getTopLevelGroup(EndpointDescriptionsManager endpointDescriptionsManager, String groupName) {
if (endpointDescriptionsManager.getEndpointGroupDescription(groupName).getParentGroupName() != null) {
return getTopLevelGroup(endpointDescriptionsManager, endpointDescriptionsManager
.getEndpointGroupDescription(groupName).getParentGroupName());
} else {
return groupName;
}
}
private void updateSchedulingState() {
try {
State newState = calculateSchedulingState();
while (!validatedEndpointDatumsToProcess.isEmpty() && newState == State.IDLING) {
addEndpointDatum(validatedEndpointDatumsToProcess.poll());
newState = calculateSchedulingState();
}
state.set(newState);
if (newState != State.IDLING) {
postNewSchedulingStateEvent();
}
} catch (ComponentExecutionException e) {
postSchedulingFailedEvent(e);
}
}
protected synchronized Map<String, EndpointDatum> fetchEndpointDatums() {
Map<String, EndpointDatum> datums = new HashMap<>();
for (String inputName : inputsWithValue) {
datums.put(inputName, getEndpointDatumReturnedForExecution(inputName));
}
for (String inputName : notRequiredInputs) {
if (!endpointDatums.get(inputName).isEmpty()) {
datums.put(inputName, getEndpointDatumReturnedForExecution(inputName));
}
}
inputsWithValue.clear();
return datums;
}
private EndpointDatum getEndpointDatumReturnedForExecution(String inputName) {
if (constantInputs.contains(inputName)) {
constantInputsProcessed.add(inputName);
return endpointDatums.get(inputName).peekFirst();
} else {
if (!queuedConsumingInputs.contains(inputName)) {
synchronized (inputsOccupied) {
inputsOccupied.remove(inputName);
}
}
EndpointDatum pollFirst = endpointDatums.get(inputName).pollFirst();
return pollFirst;
}
}
protected InternalTDImpl getResetDatum() {
InternalTDImpl resetDatum = (InternalTDImpl) resetDatumToFoward.get().getValue();
resetDatumToFoward.set(null);
return resetDatum;
}
protected InternalTDImpl getFailureDatum() {
InternalTDImpl failureDatum = (InternalTDImpl) failureDatumToFoward.get().getValue();
failureDatumToFoward.set(null);
return failureDatum;
}
private void addEndpointDatum(EndpointDatum endpointDatum) throws ComponentExecutionException {
performFirstSanityCheckForEndpointDatumAdded(endpointDatum);
switch (endpointDatum.getValue().getDataType()) {
case Internal:
handleInternalEndpointDatumAdded(endpointDatum);
break;
default:
handleNonInternalEndpointDatumAdded(endpointDatum);
break;
}
}
private void performFirstSanityCheckForEndpointDatumAdded(EndpointDatum endpointDatum) throws ComponentExecutionException {
if (loopResetRequested.get() && (endpointDatum.getValue().getDataType() != DataType.Internal
|| ((InternalTDImpl) endpointDatum.getValue()).getType() != InternalTDImpl.InternalTDType.NestedLoopReset)) {
if (endpointDatum.getValue().getDataType() == DataType.Internal) {
throw new ComponentExecutionException(StringUtils.format(
"Received input at '%s' of type 'Internal (Finished)', but component is waiting for datums of type 'Internal (Reset)'."
+ " Review the connections of your (nested) loop(s). Refer to the user guide if in doubt.",
endpointDatum.getInputName()));
} else {
throw new ComponentExecutionException(StringUtils.format(
"Received input at '%s' of type '%s', but component is waiting for datums of type 'Internal (Reset)'."
+ " Review the connections of your (nested) loop(s). Refer to the user guide if in doubt.",
endpointDatum.getInputName(), endpointDatum.getValue().getDataType().getDisplayName()));
}
}
}
private void handleInternalEndpointDatumAdded(EndpointDatum endpointDatum) throws ComponentExecutionException {
InternalTDImpl internalDatum = (InternalTDImpl) endpointDatum.getValue();
switch (internalDatum.getType()) {
case WorkflowFinish:
finishedInputs.add(endpointDatum.getInputName());
break;
case FailureInLoop:
if (internalDatum.getHopsToTraverse().isEmpty()) { // final component
handleNonInternalEndpointDatumAdded(convertEndpointDatum(endpointDatum, Long.valueOf(internalDatum.getPayload())));
} else if (!internalDatum.getHopsToTraverse().peek().getHopExecutionIdentifier()
.equals(compExeRelatedInstances.compExeCtx.getExecutionIdentifier())) { // sanity check
throw new ComponentExecutionException("Internal error: Received failure datum, but component is not the recipient,"
+ " , there are still hops to traverse left: " + internalDatum.getHopsToTraverse());
} else {
failureDatumToFoward.set(endpointDatum);
}
break;
case NestedLoopReset:
if (loopResetRequested.get()) {
if (!internalDatum.getHopsToTraverse().isEmpty()) { // not final component
LogFactory.getLog(getClass()).warn("Internal error: Initiated reset, received own reset datum, but component"
+ " is not the final recipient, there are still hops to traverse left: " + internalDatum.getHopsToTraverse());
}
if (!resetDataIdsSent.remove(internalDatum.getIdentifier())) {
throw new ComponentExecutionException(StringUtils.format(
"Internal error: Received unexpected (wrong identifier) input at '%s' of type '%s'",
endpointDatum.getInputName(), endpointDatum.getValue().getDataType().getDisplayName()));
}
if (resetDataIdsSent.isEmpty()) {
loopReset.set(true);
}
} else {
if (internalDatum.getHopsToTraverse().isEmpty()) { // sanity check
throw new ComponentExecutionException("Internal error: Received reset datum and component is the final recipient,"
+ " but no loop reset was requested");
} else if (!internalDatum.getHopsToTraverse().peek().getHopExecutionIdentifier()
.equals(compExeRelatedInstances.compExeCtx.getExecutionIdentifier())) { // sanity check
throw new ComponentExecutionException("Internal error: Received reset datum, but component is not the final"
+ " recipient; there are still hops to traverse left: " + internalDatum.getHopsToTraverse());
} else {
resetDatumToFoward.set(endpointDatum);
}
}
break;
default:
break;
}
}
private EndpointDatum convertEndpointDatum(EndpointDatum endpointDatumToConvert, Long dmId) {
EndpointDatumImpl endpointDatumToAdd = new EndpointDatumImpl();
endpointDatumToAdd.setEndpointDatumRecipient(endpointDatumToConvert.getEndpointDatumRecipient());
endpointDatumToAdd.setValue(typedDatumFactory.createNotAValue(
((InternalTDImpl) endpointDatumToConvert.getValue()).getIdentifier(), NotAValueTD.Cause.Failure));
endpointDatumToAdd.setDataManagementId(dmId);
endpointDatumToAdd.setWorkflowNodeId(endpointDatumToConvert.getWorkflowNodeId());
endpointDatumToAdd.setOutputsComponentExecutionIdentifier(endpointDatumToConvert.getOutputsComponentExecutionIdentifier());
endpointDatumToAdd.setOutputsNodeId(endpointDatumToConvert.getOutputsNodeId());
endpointDatumToAdd.setWorkflowExecutionIdentifier(endpointDatumToConvert.getWorkflowExecutionIdentifier());
return endpointDatumToAdd;
}
private void handleNonInternalEndpointDatumAdded(EndpointDatum endpointDatum) throws ComponentExecutionException {
if (endpointDatum.getValue().getDataType().equals(DataType.NotAValue)) {
NotAValueTD datum = (NotAValueTD) endpointDatum.getValue();
if (idsOfNotAValueDatumsReceived.containsKey(endpointDatum.getInputName())
&& idsOfNotAValueDatumsReceived.get(endpointDatum.getInputName()).contains(datum.getIdentifier())) {
throw new ComponentExecutionException("Internal error: Received 'not a value' datum twice"
+ " I.e., no component handled it appropriately within this loop.");
} else if (idsNotAValueDatumsSent.contains(datum.getIdentifier())) {
throw new ComponentExecutionException("Received own 'not a value' datum"
+ " I.e., no component handled it appropriately within this loop"
+ "; Review the components and connections of your (nested) loop(s). Refer to the user guide if in doubt.");
} else {
if (!idsOfNotAValueDatumsReceived.containsKey(endpointDatum.getInputName())) {
idsOfNotAValueDatumsReceived.put(endpointDatum.getInputName(), new HashSet<String>());
}
idsOfNotAValueDatumsReceived.get(endpointDatum.getInputName()).add(datum.getIdentifier());
}
}
finishedInputs.remove(endpointDatum.getInputName());
endpointDatums.get(endpointDatum.getInputName()).add(endpointDatum);
}
protected void addNotAValueDatumSent(String identifier) {
idsNotAValueDatumsSent.add(identifier);
}
protected void addResetDataIdSent(String identifier) {
resetDataIdsSent.add(identifier);
loopResetRequested.set(true);
}
protected boolean isLoopResetRequested() {
return loopResetRequested.get();
}
private State calculateSchedulingState() throws ComponentExecutionException {
State newState;
if (isExecutable()) {
newState = checkForNotAValueDatums();
} else if (finishedInputs.containsAll(inputsConsideredForFinished)) {
checkIfDatumAtConsumingInputsLeft();
newState = State.FINISHED;
} else if (resetDatumToFoward.get() != null) {
checkIfDatumAtConsumingInputsLeft();
resetConstantInputs();
newState = State.RESET;
} else if (failureDatumToFoward.get() != null) {
newState = State.FAILURE_FORWARD;
} else if (loopReset.get()) {
loopReset.set(false);
loopResetRequested.set(false);
resetConstantInputs();
newState = State.LOOP_RESET;
} else {
newState = State.IDLING;
}
return newState;
}
private void resetConstantInputs() {
for (String constantInputName : constantInputs) {
if (!finishedInputs.contains(constantInputName)) {
endpointDatums.get(constantInputName).clear();
constantInputsProcessed.remove(constantInputName);
synchronized (inputsOccupied) {
inputsOccupied.remove(constantInputName);
}
}
}
}
private void checkIfDatumAtConsumingInputsLeft() throws ComponentExecutionException {
String logMessage = null;
for (String inputName : endpointDatums.keySet()) {
if (consumingInputs.contains(inputName)
&& !endpointDatums.get(inputName).isEmpty()) {
// log here as the relevant typed datums are not intended to leave this class
StringBuffer buffer = new StringBuffer();
for (EndpointDatum datum : endpointDatums.get(inputName)) {
buffer.append(datum.getValue().toString());
buffer.append(", ");
}
buffer.delete(buffer.length() - 3, buffer.length() - 1);
logMessage = StringUtils.format("Component is finished or reset, "
+ "but there are values for input '%s' left that are not processed yet: %s", inputName, buffer.toString());
if (notRequiredInputs.contains(inputName)) {
LogFactory.getLog(ComponentExecutionScheduler.class).warn(logMessage);
logMessage = null;
} else {
LogFactory.getLog(ComponentExecutionScheduler.class).error(logMessage);
}
}
}
if (logMessage != null) {
throw new ComponentExecutionException(logMessage);
}
}
private boolean isExecutable() {
return constantInputsProcessed.size() < inputsCount && isExecutableWithAndCondition(requiredInputsOrGroups);
}
private boolean isExecutableWithAndCondition(Set<String> inputsOrGroupIds) {
if (inputsOrGroupIds.isEmpty()) {
return false;
}
for (String identifier : inputsOrGroupIds) {
if (endpointGroupDescriptions.containsKey(identifier)) {
if (!checkGroupForExecutable(identifier)) {
inputsWithValue.clear();
return false;
}
} else {
if (endpointDatums.get(identifier).isEmpty()) {
inputsWithValue.clear();
return false;
} else {
inputsWithValue.add(identifier);
}
}
}
return true;
}
private boolean isExecutableWithOrCondition(Set<String> inputsOrGroupIds) {
for (String identifier : inputsOrGroupIds) {
if (endpointGroupDescriptions.containsKey(identifier)) {
if (checkGroupForExecutable(identifier)) {
return true;
}
} else if (!endpointDatums.get(identifier).isEmpty()
&& (!constantInputs.contains(identifier) || !constantInputsProcessed.contains(identifier))) {
inputsWithValue.add(identifier);
return true;
}
}
inputsWithValue.clear();
return false;
}
private boolean checkGroupForExecutable(String groupName) {
EndpointGroupDescription groupDescription = endpointGroupDescriptions.get(groupName);
if (groupDescription.getEndpointGroupDefinition().getLogicOperation().equals(EndpointGroupDefinition.LogicOperation.And)) {
return isExecutableWithAndCondition(groups.get(groupName));
} else {
return isExecutableWithOrCondition(groups.get(groupName));
}
}
private State checkForNotAValueDatums() {
State newState = State.PROCESS_INPUT_DATA;
for (String inputIdentifier : endpointDatums.keySet()) {
if (!endpointDatums.get(inputIdentifier).isEmpty()
&& endpointDatums.get(inputIdentifier).getFirst().getValue().getDataType() == DataType.NotAValue) {
newState = State.PROCESS_INPUT_DATA_WITH_NOT_A_VALUE_DATA;
break;
}
}
return newState;
}
protected void bindTypedDatumService(TypedDatumService typedDatumService) {
ComponentExecutionScheduler.typedDatumFactory = typedDatumService.getFactory();
ComponentExecutionScheduler.typedDatumConverter = typedDatumService.getConverter();
}
}