/* * Copyright (C) 2006-2016 DLR, Germany * * All rights reserved * * http://www.rcenvironment.de/ */ package de.rcenvironment.components.evaluationmemory.execution; import java.io.File; import java.io.IOException; import java.util.Set; import java.util.SortedMap; import java.util.SortedSet; import java.util.TreeMap; import java.util.TreeSet; import org.apache.commons.io.FileUtils; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import de.rcenvironment.components.evaluationmemory.common.EvaluationMemoryComponentConstants; import de.rcenvironment.components.evaluationmemory.common.EvaluationMemoryComponentHistoryDataItem; import de.rcenvironment.components.evaluationmemory.execution.internal.EvaluationMemoryAccess; import de.rcenvironment.components.evaluationmemory.execution.internal.EvaluationMemoryFileAccessService; import de.rcenvironment.core.component.api.ComponentConstants; import de.rcenvironment.core.component.api.ComponentException; import de.rcenvironment.core.component.datamanagement.api.ComponentDataManagementService; import de.rcenvironment.core.component.execution.api.ComponentContext; import de.rcenvironment.core.component.execution.api.ComponentLog; import de.rcenvironment.core.component.model.spi.DefaultComponent; import de.rcenvironment.core.datamodel.api.DataType; import de.rcenvironment.core.datamodel.api.TypedDatum; import de.rcenvironment.core.utils.common.LogUtils; import de.rcenvironment.core.utils.common.StringUtils; /** * Implementation of the Evaluation Memory component. * * @author Doreen Seider */ public class EvaluationMemoryComponent extends DefaultComponent { /** * 'Processing input' modes. * * @author Doreen Seider */ private enum Mode { Check, Store; } private Log log = LogFactory.getLog(getClass()); private ComponentLog componentLog; private EvaluationMemoryFileAccessService memoryFileAccessService; private ComponentDataManagementService dataManagementService; private ComponentContext componentContext; private boolean considerLoopFailures; private EvaluationMemoryAccess memoryAccess; private SortedMap<String, DataType> inputsToEvaluate = new TreeMap<>(); private SortedMap<String, DataType> outputsEvaluationResult = new TreeMap<>(); private SortedMap<String, TypedDatum> valuesToEvaluate; private String memoryFilePath; private File memoryFile; private EvaluationMemoryComponentHistoryDataItem historyData; @Override public void setComponentContext(ComponentContext componentContext) { super.setComponentContext(componentContext); this.componentContext = componentContext; componentLog = componentContext.getLog(); considerLoopFailures = Boolean.valueOf(componentContext.getConfigurationValue( EvaluationMemoryComponentConstants.CONFIG_CONSIDER_LOOP_FAILURES)); } @Override public void start() throws ComponentException { memoryFileAccessService = componentContext.getService(EvaluationMemoryFileAccessService.class); dataManagementService = componentContext.getService(ComponentDataManagementService.class); setInputsAndOutputs(); initializeMemoryFileAccess(getMemoryFilePath()); } private void setInputsAndOutputs() { for (String input : getInputsOfTypeToEvaluateSortedByName()) { inputsToEvaluate.put(input, componentContext.getInputDataType(input)); } for (String output : getOutputsOfTypeEvaluationResultsSortedByName()) { outputsEvaluationResult.put(output, componentContext.getInputDataType(output)); } } private String getMemoryFilePath() { if (Boolean.valueOf(componentContext.getConfigurationValue( EvaluationMemoryComponentConstants.CONFIG_SELECT_AT_WF_START))) { return componentContext.getConfigurationValue(EvaluationMemoryComponentConstants.CONFIG_MEMORY_FILE_WF_START); } else { return componentContext.getConfigurationValue(EvaluationMemoryComponentConstants.CONFIG_MEMORY_FILE); } } private void initializeMemoryFileAccess(String path) throws ComponentException { if (path == null || path.isEmpty()) { throw new ComponentException("No memory file given. Did you forget to configure one?"); } memoryFile = new File(path); memoryFilePath = memoryFile.getAbsolutePath(); try { memoryAccess = memoryFileAccessService.acquireAccessToMemoryFile(memoryFilePath); if (memoryFile.exists() && FileUtils.sizeOf(memoryFile) > 0) { // exists and is not empty memoryAccess.validateEvaluationMemory(inputsToEvaluate, outputsEvaluationResult); } else { memoryFile.createNewFile(); memoryAccess.setInputsOutputsDefinition(inputsToEvaluate, outputsEvaluationResult); } } catch (IOException e) { throw new ComponentException("Failed to access memory file: " + memoryFilePath, e); } } @Override public void processInputs() throws ComponentException { Set<String> inputsWithDatum = componentContext.getInputsWithDatum(); SortedMap<String, TypedDatum> inputValues = getInputValuesSortedByInputsName(inputsWithDatum); switch (getInputProcessingMode(inputsWithDatum)) { case Check: initializeNewHistoryData(); processInputsInCheckMode(inputValues); break; case Store: initializeNewHistoryData(); processInputsInStoreMode(); break; default: break; } try { addMemoryFileToHistoryData(); } catch (IOException e) { String errorMessage = StringUtils.format("Failed to store memory file into the data management for '%s' (%s)", componentContext.getComponentName(), componentContext.getExecutionIdentifier()); String errorId = LogUtils.logExceptionWithStacktraceAndAssignUniqueMarker(log, errorMessage, e); componentLog.componentError(errorMessage, e, errorId); } writeFinalHistoryData(); } private void initializeNewHistoryData() { if (Boolean.valueOf(componentContext.getConfigurationValue(ComponentConstants.CONFIG_KEY_STORE_DATA_ITEM))) { historyData = new EvaluationMemoryComponentHistoryDataItem(EvaluationMemoryComponentConstants.COMPONENT_ID); historyData.setMemoryFilePath(memoryFilePath); } } private void addMemoryFileToHistoryData() throws IOException { if (Boolean.valueOf(componentContext.getConfigurationValue(ComponentConstants.CONFIG_KEY_STORE_DATA_ITEM))) { String memoryFileReference = dataManagementService.createTaggedReferenceFromLocalFile( componentContext, memoryFile, memoryFile.getName()); historyData.setMemoryFileReference(memoryFileReference); } } private void writeFinalHistoryData() { if (historyData != null && Boolean.valueOf(componentContext.getConfigurationValue(ComponentConstants.CONFIG_KEY_STORE_DATA_ITEM))) { componentContext.writeFinalHistoryDataItem(historyData); historyData = null; } } private void processInputsInCheckMode(SortedMap<String, TypedDatum> inputValues) { if (valuesToEvaluate != null) { componentLog.componentWarn(StringUtils.format("Values to evaluate left: '%s' " + "- no result values received (usually in case of component failure in loop) -> skip values", inputValues)); valuesToEvaluate = null; } SortedMap<String, TypedDatum> evaluationResults = null; try { evaluationResults = memoryAccess.getEvaluationResult(inputValues, outputsEvaluationResult); } catch (IOException e) { String errorMessage = StringUtils.format("Failed to get evaluation results for values '%s' from evaluation memory '%s';" + " cause: %s - as it is not workflow-critical, continue with execution...", inputValues, memoryFile, e.getMessage()); log.error(errorMessage, e); componentLog.componentError(errorMessage); } if (evaluationResults == null) { componentLog.componentInfo(StringUtils.format("Forward values '%s' - no evaluation results in memory", inputValues)); forwardValues(inputValues); valuesToEvaluate = inputValues; } else { if (evaluationResultsContainValuesOfTypeNotAValue(evaluationResults) && !considerLoopFailures) { componentLog.componentInfo(StringUtils.format("Forward values '%s' - found evaluation results " + "in memory, but they are ignored as they contain values of type not-a-value (loop failures) and component is " + "configured to not consider loop failures as loop result", inputValues, evaluationResults)); forwardValues(inputValues); valuesToEvaluate = inputValues; } else { componentLog.componentInfo(StringUtils.format("Found evaluation results for values '%s' " + "in memory: %s -> directly feed back", inputValues, evaluationResults)); for (String output : evaluationResults.keySet()) { componentContext.writeOutput(output, evaluationResults.get(output)); } } } } private boolean evaluationResultsContainValuesOfTypeNotAValue(SortedMap<String, TypedDatum> evaluationResults) { for (TypedDatum result : evaluationResults.values()) { if (result.getDataType().equals(DataType.NotAValue)) { return true; } } return false; } private void processInputsInStoreMode() throws ComponentException { Set<String> inputsWithDatum = componentContext.getInputsWithDatum(); SortedMap<String, TypedDatum> evaluationResults = getDynamicInputValuesSortedByInputsName(inputsWithDatum); for (String input : evaluationResults.keySet()) { if (componentContext.isDynamicInput(input)) { componentContext.writeOutput(input, evaluationResults.get(input)); } } if (valuesToEvaluate == null) { throw new ComponentException(StringUtils.format("Failed to store evaluation results in evaluation memory file: %s" + " - no values (to evaluate) stored from a previous run", memoryFilePath)); } SortedMap<String, TypedDatum> values = valuesToEvaluate; try { memoryAccess.addEvaluationValues(values, evaluationResults); componentLog.componentInfo(StringUtils.format("Stored evaluation results for values '%s' " + "in memory: %s", values, evaluationResults)); } catch (IOException e) { String errorMessage = StringUtils.format("Failed to write evaluation values '%s' with '%s' to evaluation memory '%s';" + " cause: %s - as it is not workflow-critical, continue with execution...", values, evaluationResults, memoryFile, e.getMessage()); log.error(errorMessage, e); componentLog.componentError(errorMessage); } valuesToEvaluate = null; } @Override public void completeStartOrProcessInputsAfterFailure() throws ComponentException { writeFinalHistoryData(); } @Override public void tearDown(FinalComponentState state) { if (memoryAccess != null) { if (!memoryFileAccessService.releaseAccessToMemoryFile(memoryFilePath)) { log.warn("Access to memory file wasn't acquired earlier, but access to it should released anyway now: " + memoryFilePath); } } else { log.debug("No need to release access to memory file as it wasn't acquired before: " + memoryFilePath); } } private Mode getInputProcessingMode(Set<String> inputsWithDatum) throws ComponentException { String input = inputsWithDatum.iterator().next(); if (componentContext.isDynamicInput(input)) { if (componentContext.getDynamicInputIdentifier(input).equals(EvaluationMemoryComponentConstants.ENDPOINT_ID_TO_EVALUATE)) { return Mode.Check; } else if (componentContext.getDynamicInputIdentifier(input).equals( EvaluationMemoryComponentConstants.ENDPOINT_ID_EVALUATION_RESULTS)) { return Mode.Store; } } // should never happen throw new ComponentException("Unexpected set of input values"); } private SortedMap<String, TypedDatum> getInputValuesSortedByInputsName(Set<String> inputsWithDatum) { SortedMap<String, TypedDatum> inputValues = new TreeMap<>(); for (String input : inputsWithDatum) { inputValues.put(input, componentContext.readInput(input)); } return inputValues; } private SortedSet<String> getOutputsOfTypeEvaluationResultsSortedByName() { SortedSet<String> outputs = new TreeSet<>(); for (String output: componentContext.getOutputs()) { if (componentContext.isDynamicOutput(output) && componentContext.getDynamicOutputIdentifier(output).equals( EvaluationMemoryComponentConstants.ENDPOINT_ID_EVALUATION_RESULTS)) { outputs.add(output); } } return outputs; } private SortedSet<String> getInputsOfTypeToEvaluateSortedByName() { SortedSet<String> inputs = new TreeSet<>(); for (String input: componentContext.getOutputs()) { if (componentContext.isDynamicInput(input) && componentContext.getDynamicInputIdentifier(input).equals( EvaluationMemoryComponentConstants.ENDPOINT_ID_TO_EVALUATE)) { inputs.add(input); } } return inputs; } private SortedMap<String, TypedDatum> getDynamicInputValuesSortedByInputsName(Set<String> inputsWithDatum) { SortedMap<String, TypedDatum> inputValues = new TreeMap<>(); for (String input : inputsWithDatum) { if (componentContext.isDynamicInput(input)) { inputValues.put(input, componentContext.readInput(input)); } } return inputValues; } private void forwardValues(SortedMap<String, TypedDatum> inputValues) { for (String input : inputValues.keySet()) { componentContext.writeOutput(input, inputValues.get(input)); } } }