/*
* Copyright (C) 2006-2016 DLR, Germany
*
* All rights reserved
*
* http://www.rcenvironment.de/
*/
package de.rcenvironment.core.component.workflow.execution.headless.internal;
import java.io.File;
import java.io.IOException;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;
import java.util.TreeSet;
import org.apache.commons.io.FileUtils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.codehaus.jackson.map.ObjectMapper;
import org.codehaus.jackson.type.TypeReference;
import de.rcenvironment.core.communication.api.PlatformService;
import de.rcenvironment.core.communication.common.CommunicationException;
import de.rcenvironment.core.communication.common.LogicalNodeId;
import de.rcenvironment.core.communication.common.ResolvableNodeId;
import de.rcenvironment.core.component.api.DistributedComponentKnowledgeService;
import de.rcenvironment.core.component.execution.api.ConsoleRowUtils;
import de.rcenvironment.core.component.execution.api.ExecutionControllerException;
import de.rcenvironment.core.component.model.configuration.api.PlaceholdersMetaDataDefinition;
import de.rcenvironment.core.component.workflow.api.WorkflowConstants;
import de.rcenvironment.core.component.workflow.execution.api.FinalWorkflowState;
import de.rcenvironment.core.component.workflow.execution.api.WorkflowDescriptionValidationResult;
import de.rcenvironment.core.component.workflow.execution.api.WorkflowExecutionContext;
import de.rcenvironment.core.component.workflow.execution.api.WorkflowExecutionContextBuilder;
import de.rcenvironment.core.component.workflow.execution.api.WorkflowExecutionException;
import de.rcenvironment.core.component.workflow.execution.api.WorkflowExecutionInformation;
import de.rcenvironment.core.component.workflow.execution.api.WorkflowExecutionService;
import de.rcenvironment.core.component.workflow.execution.api.WorkflowExecutionUtils;
import de.rcenvironment.core.component.workflow.execution.api.WorkflowFileException;
import de.rcenvironment.core.component.workflow.execution.api.WorkflowPlaceholderHandler;
import de.rcenvironment.core.component.workflow.execution.api.WorkflowState;
import de.rcenvironment.core.component.workflow.execution.api.WorkflowStateNotificationSubscriber;
import de.rcenvironment.core.component.workflow.execution.headless.api.ConsoleRowSubscriber;
import de.rcenvironment.core.component.workflow.execution.headless.api.HeadlessWorkflowDescriptionLoaderCallback;
import de.rcenvironment.core.component.workflow.execution.headless.api.HeadlessWorkflowExecutionContext;
import de.rcenvironment.core.component.workflow.execution.headless.api.HeadlessWorkflowExecutionService;
import de.rcenvironment.core.component.workflow.execution.spi.SingleWorkflowStateChangeListener;
import de.rcenvironment.core.component.workflow.execution.spi.WorkflowDescriptionLoaderCallback;
import de.rcenvironment.core.component.workflow.model.api.WorkflowDescription;
import de.rcenvironment.core.component.workflow.model.api.WorkflowNode;
import de.rcenvironment.core.datamanagement.MetaDataService;
import de.rcenvironment.core.notification.DistributedNotificationService;
import de.rcenvironment.core.utils.common.JsonUtils;
import de.rcenvironment.core.utils.common.StringUtils;
import de.rcenvironment.core.utils.common.rpc.RemoteOperationException;
/**
* Default {@link HeadlessWorkflowExecutionService} implementation.
*
* @author Sascha Zur
* @author Phillip Kroll
* @author Robert Mischke
* @author Doreen Seider
*/
public class HeadlessWorkflowExecutionServiceImpl implements HeadlessWorkflowExecutionService {
private final Log log = LogFactory.getLog(getClass());
private DistributedNotificationService notificationService;
private WorkflowExecutionService workflowExecutionService;
private DistributedComponentKnowledgeService compKnowledgeService;
private PlatformService platformService;
private MetaDataService metaDataService;
@Override
public void validatePlaceholdersFile(File placeholdersFile) throws WorkflowFileException {
verifyFileExistsAndIsReadable(placeholdersFile);
parsePlaceholdersFile(placeholdersFile);
}
@Override
public FinalWorkflowState executeWorkflowSync(HeadlessWorkflowExecutionContext wfExeContext) throws WorkflowExecutionException {
final ExtendedHeadlessWorkflowExecutionContext headlessWfExeCtx = new ExtendedHeadlessWorkflowExecutionContext(wfExeContext);
executeWorkflow(headlessWfExeCtx);
FinalWorkflowState finalState = waitForWorkflowExecutionTermination(headlessWfExeCtx);
disposeOrDeleteWorkflowIfIntended(headlessWfExeCtx, finalState.equals(FinalWorkflowState.FINISHED));
headlessWfExeCtx.unsubscribeNotificationSubscribersQuietly(notificationService);
return finalState;
}
private void executeWorkflow(ExtendedHeadlessWorkflowExecutionContext headlessWfExeCtx) throws WorkflowExecutionException {
headlessWfExeCtx.addOutput(null, StringUtils.format("Loading: '%s'; log directory: %s (full path: %s)",
headlessWfExeCtx.getWorkflowFile().getName(), headlessWfExeCtx.getLogDirectory().getAbsolutePath(),
headlessWfExeCtx.getWorkflowFile().getAbsolutePath()));
final WorkflowDescription workflowDescription = loadWorkflowDescriptionAndPlaceholders(headlessWfExeCtx);
try {
executeWorkflow(workflowDescription, headlessWfExeCtx);
headlessWfExeCtx.addOutput(headlessWfExeCtx.getWorkflowExecutionContext().getExecutionIdentifier(),
StringUtils.format("Executing: '%s'; id: %s (full path: %s)",
headlessWfExeCtx.getWorkflowFile().getName(),
headlessWfExeCtx.getWorkflowExecutionContext().getExecutionIdentifier(),
headlessWfExeCtx.getWorkflowFile().getAbsolutePath()));
} catch (WorkflowExecutionException e) {
headlessWfExeCtx.getTextOutputReceiver().addOutput(StringUtils.format("Failed: '%s'; %s (full path: %s) ",
headlessWfExeCtx.getWorkflowFile().getName(), e.getMessage(), headlessWfExeCtx.getWorkflowFile().getAbsolutePath()));
headlessWfExeCtx.closeResourcesQuietly();
headlessWfExeCtx.unsubscribeNotificationSubscribersQuietly(notificationService);
throw e;
}
}
private FinalWorkflowState waitForWorkflowExecutionTermination(ExtendedHeadlessWorkflowExecutionContext headlessWfExeCtx)
throws WorkflowExecutionException {
WorkflowState finalState = null; // = unknown
try {
finalState = headlessWfExeCtx.waitForTermination();
} catch (InterruptedException e) {
throw new WorkflowExecutionException("Received interruption signal while waiting for workflow to terminate");
}
headlessWfExeCtx.addOutput(StringUtils.format("%s: %s", headlessWfExeCtx.getWorkflowExecutionContext()
.getExecutionIdentifier(), finalState.getDisplayName()), StringUtils.format("%s: '%s'(full path: %s)",
finalState.getDisplayName(), headlessWfExeCtx.getWorkflowFile().getName(),
headlessWfExeCtx.getWorkflowFile().getAbsolutePath()));
headlessWfExeCtx.closeResourcesQuietly();
// map to reduced set of final workflow states (to avoid downstream checking for invalid values)
switch (finalState) {
case FINISHED:
return FinalWorkflowState.FINISHED;
case CANCELLED:
return FinalWorkflowState.CANCELLED;
case FAILED:
return FinalWorkflowState.FAILED;
case RESULTS_REJECTED:
return FinalWorkflowState.RESULTS_REJECTED;
case UNKNOWN:
throw new WorkflowExecutionException(StringUtils.format("Final state of '%s' is %s. "
+ "Most likely because the connection to the workflow host node was interupted. See logs for more details.",
headlessWfExeCtx.getWorkflowFile().getAbsolutePath(), finalState.getDisplayName()));
default:
throw new WorkflowExecutionException(StringUtils.format("Unexpected value '%s' for final state for '%s'",
finalState.getDisplayName(), headlessWfExeCtx.getWorkflowFile().getAbsolutePath()));
}
}
private void afterWorkflowExecutionTerminated(ExtendedHeadlessWorkflowExecutionContext headlessWfExeCtx, boolean behavedAsExpected)
throws WorkflowExecutionException {
disposeOrDeleteWorkflowIfIntended(headlessWfExeCtx, behavedAsExpected);
headlessWfExeCtx.unsubscribeNotificationSubscribersQuietly(notificationService);
}
private void disposeOrDeleteWorkflowIfIntended(ExtendedHeadlessWorkflowExecutionContext wfHeadlessExeCtx, boolean behavedAsExpected) {
final String wfExecutionId = wfHeadlessExeCtx.getWorkflowExecutionContext().getExecutionIdentifier();
boolean dispose = wfHeadlessExeCtx.getDisposalBehavior() == DisposalBehavior.Always
|| (behavedAsExpected
&& wfHeadlessExeCtx.getDisposalBehavior() == DisposalBehavior.OnExpected);
boolean delete = wfHeadlessExeCtx.getDeletionBehavior() == DeletionBehavior.Always
|| (behavedAsExpected
&& wfHeadlessExeCtx.getDeletionBehavior() == DeletionBehavior.OnExpected);
if (delete) {
try {
LogicalNodeId nodeId = wfHeadlessExeCtx.getWorkflowExecutionContext().getNodeId();
delete(workflowExecutionService.getWorkflowDataManagementId(wfExecutionId,
nodeId), nodeId);
dispose(wfExecutionId, nodeId);
wfHeadlessExeCtx.waitForDisposal();
try {
FileUtils.deleteDirectory(wfHeadlessExeCtx.getLogDirectory());
} catch (IOException e) {
log.error("Failed to delete log directory: " + wfHeadlessExeCtx.getLogDirectory(), e);
}
// catching RTE might be obsolete/wrong after ECE was introduced
} catch (ExecutionControllerException | RemoteOperationException | RuntimeException e) {
log.error(StringUtils.format("Failed to delete workflow '%s' (%s) ",
wfHeadlessExeCtx.getWorkflowExecutionContext().getInstanceName(), wfExecutionId), e);
wfHeadlessExeCtx.reportWorkflowDisposed(WorkflowState.FAILED);
} catch (InterruptedException e) {
log.error(StringUtils.format("Received interruption signal while waiting for disposeal of workflow '%s' (%s) ",
wfHeadlessExeCtx.getWorkflowExecutionContext().getInstanceName(), wfExecutionId), e);
}
} else {
if (dispose) {
try {
dispose(wfExecutionId, wfHeadlessExeCtx.getWorkflowExecutionContext().getNodeId());
wfHeadlessExeCtx.waitForDisposal();
} catch (ExecutionControllerException | RemoteOperationException | RuntimeException e) {
log.error(StringUtils.format("Failed to dispose workflow '%s' (%s) ",
wfHeadlessExeCtx.getWorkflowExecutionContext().getInstanceName(), wfExecutionId), e);
} catch (InterruptedException e) {
log.error(StringUtils.format("Received interruption signal while waiting for disposeal of workflow '%s' (%s) ",
wfHeadlessExeCtx.getWorkflowExecutionContext().getInstanceName(), wfExecutionId), e);
}
}
}
}
@Override
public HeadlessWorkflowExecutionVerificationResult executeWorkflowsAndVerify(Set<HeadlessWorkflowExecutionContext> headlessWfExeCtxs,
HeadlessWorkflowExecutionVerificationRecorder wfVerificationResultReorder) {
Set<ExtendedHeadlessWorkflowExecutionContext> extHeadlessWfExeCtxs = new HashSet<>();
Set<ExtendedHeadlessWorkflowExecutionContext> extHeadlessWfExeCtxsExpected = new HashSet<>();
for (HeadlessWorkflowExecutionContext headlessWfExeCtx : headlessWfExeCtxs) {
final ExtendedHeadlessWorkflowExecutionContext extHeadlessWfExeCtx =
new ExtendedHeadlessWorkflowExecutionContext(headlessWfExeCtx);
try {
executeWorkflow(extHeadlessWfExeCtx);
} catch (WorkflowExecutionException e) {
wfVerificationResultReorder.addWorkflowError(headlessWfExeCtx.getWorkflowFile(), e.getMessage());
log.error(e.getMessage(), e);
continue;
}
extHeadlessWfExeCtxs.add(extHeadlessWfExeCtx);
}
for (ExtendedHeadlessWorkflowExecutionContext extHeadlessWfExeCtx : extHeadlessWfExeCtxs) {
try {
FinalWorkflowState finalState = waitForWorkflowExecutionTermination(extHeadlessWfExeCtx);
boolean behavedAsExpected = false;
try {
behavedAsExpected = wfVerificationResultReorder.addWorkflowExecutionResult(extHeadlessWfExeCtx.getWorkflowFile(),
extHeadlessWfExeCtx.getLogFiles(), finalState, extHeadlessWfExeCtx.getExecutionDuration());
} catch (IOException e) {
wfVerificationResultReorder.addWorkflowError(extHeadlessWfExeCtx.getWorkflowFile(), e.getMessage());
}
if (behavedAsExpected) {
extHeadlessWfExeCtxsExpected.add(extHeadlessWfExeCtx);
}
afterWorkflowExecutionTerminated(extHeadlessWfExeCtx, behavedAsExpected);
} catch (WorkflowExecutionException e) {
wfVerificationResultReorder.addWorkflowError(extHeadlessWfExeCtx.getWorkflowFile(), e.getMessage());
}
}
return (HeadlessWorkflowExecutionVerificationResult) wfVerificationResultReorder;
}
private WorkflowDescription loadWorkflowDescriptionAndPlaceholders(ExtendedHeadlessWorkflowExecutionContext headlessWfExeCtx)
throws WorkflowExecutionException {
try {
verifyFileExistsAndIsReadable(headlessWfExeCtx.getWorkflowFile());
boolean abortIfWorkflowUpdateRequired = headlessWfExeCtx.shouldAbortIfWorkflowUpdateRequired();
WorkflowDescription workflowDescription = loadWorkflowDescriptionFromFileConsideringUpdates(headlessWfExeCtx.getWorkflowFile(),
new HeadlessWorkflowDescriptionLoaderCallback(headlessWfExeCtx.getTextOutputReceiver()), abortIfWorkflowUpdateRequired);
if (headlessWfExeCtx.getPlaceholdersFile() != null) {
verifyFileExistsAndIsReadable(headlessWfExeCtx.getPlaceholdersFile());
}
applyPlaceholdersAndVerify(workflowDescription, headlessWfExeCtx.getPlaceholdersFile());
return workflowDescription;
} catch (WorkflowFileException e) {
throw new WorkflowExecutionException("Failed to execute workflow", e);
}
}
private void executeWorkflow(final WorkflowDescription wfDescription, final ExtendedHeadlessWorkflowExecutionContext wfHeadlessExeCtx)
throws WorkflowExecutionException {
setupLogDirectory(wfHeadlessExeCtx);
WorkflowExecutionUtils.replaceNullNodeIdentifiersWithActualNodeIdentifier(wfDescription,
platformService.getLocalDefaultLogicalNodeId(), compKnowledgeService.getCurrentComponentKnowledge());
WorkflowExecutionUtils
.setNodeIdentifiersToTransientInCaseOfLocalOnes(wfDescription, platformService.getLocalDefaultLogicalNodeId());
wfDescription.setName(WorkflowExecutionUtils.generateDefaultNameforExecutingWorkflow(wfHeadlessExeCtx.getWorkflowFile().getName(),
wfDescription));
wfDescription.setFileName(wfHeadlessExeCtx.getWorkflowFile().getName());
if (!validateWorkflowDescription(wfDescription).isSucceeded()) {
throw new WorkflowExecutionException("Workflow description invalid: " + wfHeadlessExeCtx.getWorkflowFile().getAbsolutePath());
}
WorkflowExecutionContextBuilder wfExeCtxBuilder = new WorkflowExecutionContextBuilder(wfDescription);
wfExeCtxBuilder.setInstanceName(wfDescription.getName());
wfExeCtxBuilder.setNodeIdentifierStartedExecution(platformService.getLocalDefaultLogicalNodeId());
if (wfDescription.getAdditionalInformation() != null && !wfDescription.getAdditionalInformation().isEmpty()) {
wfExeCtxBuilder.setAdditionalInformationProvidedAtStart(wfDescription.getAdditionalInformation());
}
final WorkflowExecutionContext wfExeCtx = wfExeCtxBuilder.build();
wfHeadlessExeCtx.setWorkflowExecutionContext(wfExeCtx);
WorkflowStateNotificationSubscriber wfStateChangeListener = createWorkflowStateChangeListener(wfHeadlessExeCtx);
// add workflow state subscriber; only subscribe for this specific workflow (no wildcard)
try {
ExtendedHeadlessWorkflowExecutionContext.NotificationSubscription subscriberContext =
wfHeadlessExeCtx.new NotificationSubscription();
subscriberContext.subscriber = wfStateChangeListener;
subscriberContext.notificationId = WorkflowConstants.STATE_NOTIFICATION_ID + wfExeCtx.getExecutionIdentifier();
subscriberContext.nodeId = wfExeCtx.getNodeId();
notificationService.subscribe(subscriberContext.notificationId, subscriberContext.subscriber, subscriberContext.nodeId);
wfHeadlessExeCtx.registerNotificationSubscriptionsToUnsubscribeOnFinish(subscriberContext);
} catch (RemoteOperationException e) {
String errorMessage = "Failed to execute workflow (error while subscribing for state changes)";
log.error(StringUtils.format("%s: %s", errorMessage, wfHeadlessExeCtx.getWorkflowFile().getAbsolutePath()), e);
throw new WorkflowExecutionException(errorMessage, e);
}
// add console output subscriber
ConsoleRowSubscriber consoleRowSubscriber = new ConsoleRowSubscriber(wfHeadlessExeCtx, wfHeadlessExeCtx.getLogDirectory());
wfHeadlessExeCtx.registerResourceToCloseOnFinish(consoleRowSubscriber);
// subscribe to a console row notification on workflow controller node
try {
ExtendedHeadlessWorkflowExecutionContext.NotificationSubscription subscriberContext =
wfHeadlessExeCtx.new NotificationSubscription();
subscriberContext.subscriber = consoleRowSubscriber;
subscriberContext.notificationId = ConsoleRowUtils.composeConsoleNotificationId(wfExeCtx.getNodeId(),
wfExeCtx.getExecutionIdentifier());
subscriberContext.nodeId = wfExeCtx.getNodeId();
notificationService.subscribe(subscriberContext.notificationId, subscriberContext.subscriber, subscriberContext.nodeId);
wfHeadlessExeCtx.registerNotificationSubscriptionsToUnsubscribeOnFinish(subscriberContext);
} catch (RemoteOperationException e) {
log.error("Failed to subscribe for console row output: " + wfHeadlessExeCtx.getWorkflowFile().getAbsolutePath(), e);
}
WorkflowExecutionInformation wfExeInfo;
try {
wfExeInfo = executeWorkflowAsync(wfExeCtx);
} catch (RemoteOperationException e) {
// consoleRowSubscriber is closed in calling method
throw new WorkflowExecutionException("Failed to execute workflow", e);
}
log.debug(StringUtils.format("Created workflow from file '%s' with name '%s', with id %s on node %s",
wfHeadlessExeCtx.getWorkflowFile().getName(), wfExeInfo.getInstanceName(), wfExeInfo.getExecutionIdentifier(),
wfExeInfo.getNodeId()));
}
private WorkflowStateNotificationSubscriber createWorkflowStateChangeListener(
final ExtendedHeadlessWorkflowExecutionContext wfHeadlessExeCtx) {
final String wfExecutionId = wfHeadlessExeCtx.getWorkflowExecutionContext().getExecutionIdentifier();
WorkflowStateNotificationSubscriber workflowStateChangeListener =
new WorkflowStateNotificationSubscriber(new SingleWorkflowStateChangeListener() {
@Override
public void onWorkflowStateChanged(WorkflowState newState) {
log.debug(StringUtils.format("Received state change event for workflow '%s' (%s): %s",
wfHeadlessExeCtx.getWorkflowExecutionContext().getInstanceName(),
wfExecutionId, newState.getDisplayName()));
switch (newState) {
case CANCELLED:
case FAILED:
case RESULTS_REJECTED:
case FINISHED:
wfHeadlessExeCtx.reportWorkflowTerminated(newState);
break;
case DISPOSED:
wfHeadlessExeCtx.reportWorkflowDisposed(newState);
break;
default:
// ignore
break; // workaround for CheckStyle bug
// (http://sourceforge.net/p/checkstyle/bugs/454/) - misc_ro
}
}
@Override
public void onWorkflowNotAliveAnymore(String errorMessage) {
wfHeadlessExeCtx.reportWorkflowNotAliveAnymore(errorMessage);
}
}, wfExecutionId);
return workflowStateChangeListener;
}
private void applyPlaceholdersAndVerify(final WorkflowDescription workflowDescription,
final File placeholdersFile) throws WorkflowExecutionException, WorkflowFileException {
final Map<String, Map<String, String>> placeholderValues;
if (placeholdersFile != null) {
placeholderValues = parsePlaceholdersFile(placeholdersFile);
log.debug(StringUtils.format("Loaded placeholder values from %s: %s", placeholdersFile.getAbsolutePath(), placeholderValues));
} else {
placeholderValues = new HashMap<>();
}
WorkflowPlaceholderHandler placeholderDescription =
WorkflowPlaceholderHandler.createPlaceholderDescriptionFromWorkflowDescription(workflowDescription, "");
// check for unsupported placeholders
Map<String, Map<String, String>> componentTypePlaceholders = placeholderDescription.getComponentTypePlaceholders();
if (!componentTypePlaceholders.isEmpty()) {
throw new WorkflowExecutionException(
"This workflow uses component *type* placeholders which are not supported in headless execution yet");
}
// get copyInstanceId -> (key->value) map of placeholders
Map<String, Map<String, String>> componentInstancePlaceholders = placeholderDescription.getComponentInstancePlaceholders();
Set<String> missingPlaceholderValues = new TreeSet<>();
// collect required placeholder values
for (WorkflowNode wn : workflowDescription.getWorkflowNodes()) {
if (wn.isEnabled()) {
// extract information
String compInstanceId = wn.getIdentifier();
String componentId = wn.getComponentDescription().getIdentifier();
// first, try component instance specific placeholders definition
Map<String, String> ciPlaceholderValues = placeholderValues.get(createComponentInstancePlaceholderKey(wn));
// then, try component type specific placeholders definition
if (ciPlaceholderValues == null) {
ciPlaceholderValues = placeholderValues.get(componentId);
}
Map<String, String> ciPlaceholders = componentInstancePlaceholders.get(compInstanceId);
// subtract available keys and collect remaining (missing) ones
if (ciPlaceholders != null) {
Set<String> ciPlaceholderKeys = ciPlaceholders.keySet();
Set<String> missingCIPlaceholderKeys = ciPlaceholderKeys;
if (ciPlaceholderValues != null) {
missingCIPlaceholderKeys.removeAll(ciPlaceholderValues.keySet());
}
// explicit fixes for backwards compatibility; ignore placeholders in settings that
// are not actually used
eliminateKnownIrrelevantPlaceholders(wn, missingCIPlaceholderKeys);
for (String missingKey : missingCIPlaceholderKeys) {
missingPlaceholderValues.add(StringUtils.format("\"%s\" -> \"%s\" (%s)", componentId, missingKey, compInstanceId));
}
}
// apply available values
if (ciPlaceholderValues != null && !ciPlaceholderValues.isEmpty()) {
logPlaceholderValues(wn, ciPlaceholderValues);
wn.getComponentDescription().getConfigurationDescription().setPlaceholders(ciPlaceholderValues);
}
}
}
// check if missing set contains entries -> fail if it does
if (!missingPlaceholderValues.isEmpty()) {
throw new WorkflowExecutionException("The workflow requires additional placeholder values "
+ "(listed as <component id>/<version> -> <placeholder key> (<instance id>)): " + missingPlaceholderValues);
}
}
private void setupLogDirectory(ExtendedHeadlessWorkflowExecutionContext wfHeadlessExeCtx) throws WorkflowExecutionException {
File logDirectory = wfHeadlessExeCtx.getLogDirectory();
logDirectory.mkdirs();
if (!logDirectory.isDirectory()) {
throw new WorkflowExecutionException(StringUtils.format("Failed to create log directory '%s' for workflow '%s'"
+ logDirectory.getAbsolutePath(), wfHeadlessExeCtx.getWorkflowFile().getAbsolutePath()));
}
log.debug(StringUtils.format("Writing log files for workflow '%s' to: ",
wfHeadlessExeCtx.getWorkflowFile().getAbsolutePath(), logDirectory.getAbsolutePath()));
}
private String createComponentInstancePlaceholderKey(WorkflowNode wn) {
return wn.getComponentDescription().getIdentifier() + "/" + wn.getName();
}
private void logPlaceholderValues(WorkflowNode wn, Map<String, String> cPlaceholderValues) {
PlaceholdersMetaDataDefinition placeholderMetaDataDefinition = wn.getComponentDescription().getConfigurationDescription()
.getComponentConfigurationDefinition().getPlaceholderMetaDataDefinition();
Map<String, String> cPlaceholderValuesToLog = new HashMap<>();
for (String cPlaceholderKey : cPlaceholderValues.keySet()) {
if (placeholderMetaDataDefinition.decode(cPlaceholderKey)) {
cPlaceholderValuesToLog.put(cPlaceholderKey, "*****");
} else {
cPlaceholderValuesToLog.put(cPlaceholderKey, cPlaceholderValues.get(cPlaceholderKey));
}
}
log.debug(StringUtils.format("Applying %d placeholder value(s) to workflow node %s: %s", cPlaceholderValues.size(), wn,
cPlaceholderValuesToLog));
}
private void eliminateKnownIrrelevantPlaceholders(WorkflowNode wn, Set<String> missingCIPlaceholderKeys) {
Iterator<String> phKeysIterator = missingCIPlaceholderKeys.iterator();
while (phKeysIterator.hasNext()) {
String phKey = phKeysIterator.next();
// Check if the key is active
if (!WorkflowPlaceholderHandler.isActivePlaceholder(phKey, wn.getComponentDescription().getConfigurationDescription())) {
phKeysIterator.remove();
}
}
}
private Map<String, Map<String, String>> parsePlaceholdersFile(File placeholdersFile) throws WorkflowFileException {
ObjectMapper mapper = JsonUtils.getDefaultObjectMapper();
try {
return mapper.readValue(placeholdersFile, new TypeReference<HashMap<String, HashMap<String, String>>>() {
});
} catch (IOException e) {
throw new WorkflowFileException(StringUtils.format("Failed to parse placeholders file: %s",
placeholdersFile.getAbsolutePath()), e);
}
}
private void verifyFileExistsAndIsReadable(File file) throws WorkflowFileException {
if (!file.isFile()) {
throw new WorkflowFileException(StringUtils.format("File doesn't exis: %s", file.getAbsolutePath()));
}
if (!file.canRead()) {
throw new WorkflowFileException(StringUtils.format("File can not be read: %s", file.getAbsolutePath()));
}
}
@Override
public WorkflowDescriptionValidationResult validateWorkflowDescription(WorkflowDescription workflowDescription) {
return workflowExecutionService.validateWorkflowDescription(workflowDescription);
}
@Override
public WorkflowDescription loadWorkflowDescriptionFromFileConsideringUpdates(File wfFile, WorkflowDescriptionLoaderCallback callback)
throws WorkflowFileException {
return workflowExecutionService.loadWorkflowDescriptionFromFileConsideringUpdates(wfFile, callback);
}
@Override
public WorkflowDescription loadWorkflowDescriptionFromFileConsideringUpdates(File wfFile, WorkflowDescriptionLoaderCallback callback,
boolean abortIfWorkflowUpdateRequired) throws WorkflowFileException {
return workflowExecutionService.loadWorkflowDescriptionFromFileConsideringUpdates(wfFile, callback, abortIfWorkflowUpdateRequired);
}
@Override
public WorkflowDescription loadWorkflowDescriptionFromFile(File wfFile, WorkflowDescriptionLoaderCallback callback)
throws WorkflowFileException {
return workflowExecutionService.loadWorkflowDescriptionFromFile(wfFile, callback);
}
@Override
public WorkflowExecutionInformation executeWorkflowAsync(WorkflowExecutionContext executionContext) throws WorkflowExecutionException,
RemoteOperationException {
return workflowExecutionService.executeWorkflowAsync(executionContext);
}
@Override
public void cancel(String executionId, ResolvableNodeId node) throws ExecutionControllerException, RemoteOperationException {
workflowExecutionService.cancel(executionId, node);
}
@Override
public void pause(String executionId, ResolvableNodeId node) throws ExecutionControllerException, RemoteOperationException {
workflowExecutionService.pause(executionId, node);
}
@Override
public void resume(String executionId, ResolvableNodeId node) throws ExecutionControllerException, RemoteOperationException {
workflowExecutionService.resume(executionId, node);
}
@Override
public void dispose(String executionId, ResolvableNodeId node) throws ExecutionControllerException, RemoteOperationException {
workflowExecutionService.dispose(executionId, node);
}
@Override
public void delete(Long wfDataManagementId, ResolvableNodeId nodeId) {
try {
metaDataService.deleteWorkflowRun(wfDataManagementId, nodeId);
} catch (CommunicationException e) {
log.error("Could not delete worklflow run " + wfDataManagementId);
}
}
@Override
public WorkflowState getWorkflowState(String executionId, ResolvableNodeId node) throws ExecutionControllerException,
RemoteOperationException {
return workflowExecutionService.getWorkflowState(executionId, node);
}
@Override
public Long getWorkflowDataManagementId(String executionId, ResolvableNodeId node) throws ExecutionControllerException,
RemoteOperationException {
return workflowExecutionService.getWorkflowDataManagementId(executionId, node);
}
@Override
public Set<WorkflowExecutionInformation> getLocalWorkflowExecutionInformations() {
return workflowExecutionService.getLocalWorkflowExecutionInformations();
}
@Override
public Set<WorkflowExecutionInformation> getWorkflowExecutionInformations() {
return workflowExecutionService.getWorkflowExecutionInformations();
}
@Override
public Set<WorkflowExecutionInformation> getWorkflowExecutionInformations(boolean forceRefresh) {
return workflowExecutionService.getWorkflowExecutionInformations(forceRefresh);
}
/**
* OSGi injection method. For test purposes set to public.
*
* @param newService {@link DistributedNotificationService} instance
*/
public void bindDistributedNotificationService(DistributedNotificationService newService) {
notificationService = newService;
}
/**
* OSGi injection method. For test purposes set to public.
*
* @param newService {@link WorkflowExecutionService} instance
*/
public void bindWorkflowExecutionService(WorkflowExecutionService newService) {
workflowExecutionService = newService;
}
/**
* OSGi injection method. For test purposes set to public.
*
* @param newService {@link PlatformService} instance
*/
public void bindPlatformService(PlatformService newService) {
platformService = newService;
}
protected void bindDistributedComponentKnowledgeService(DistributedComponentKnowledgeService newInstance) {
compKnowledgeService = newInstance;
}
protected void bindMetaDataService(MetaDataService incoming) {
this.metaDataService = incoming;
}
}