use of org.knime.core.node.workflow.FlowTryCatchContext in project knime-core by knime.
the class GenericCatchNodeModel method execute.
/**
* {@inheritDoc}
*/
@Override
protected PortObject[] execute(final PortObject[] inData, final ExecutionContext exec) throws Exception {
if (!(inData[0] instanceof InactiveBranchPortObject)) {
// main branch is active - no failure so far...
return new PortObject[] { inData[0], FlowVariablePortObject.INSTANCE };
}
// main branch inactive, grab spec from alternative (default) input
// and push error reasons on stack (they come from the ScopeObject
// which will we removed after this node, closing the scope).
FlowTryCatchContext ftcc = getFlowContext();
if ((ftcc != null) && (ftcc.hasErrorCaught())) {
pushFlowVariableString("FailingNode", ftcc.getNode());
pushFlowVariableString("FailingNodeMessage", ftcc.getReason());
pushFlowVariableString("FailingNodeStackTrace", ftcc.getStacktrace());
} else if (m_alwaysPopulate.getBooleanValue()) {
pushFlowVariableString("FailingNode", m_defaultVariable.getStringValue());
pushFlowVariableString("FailingNodeMessage", m_defaultText.getStringValue());
pushFlowVariableString("FailingNodeStackTrace", m_defaultStackTrace.getStringValue());
}
return new PortObject[] { inData[1], FlowVariablePortObject.INSTANCE };
}
use of org.knime.core.node.workflow.FlowTryCatchContext in project knime-core by knime.
the class Node method execute.
/**
* Starts executing this node. If the node has been executed already, it
* does nothing - just returns <code>true</code>.
*
* Otherwise, the procedure starts executing all predecessor nodes connected
* to an input port (which in turn recursively trigger their predecessors)
* and calls the function <code>#execute()</code> in the model after all
* connected nodes return successfully. If a port is not connected this
* function returns false without executing itself (it may have executed
* some predecessor nodes though). If a predecessor node returns false this
* method also returns false without executing this node or any further
* connected node.
*
* @param rawInData the data from the predecessor, includes flow variable port.
* @param exEnv the environment for the execution.
* @param exec The execution monitor.
* @return <code>true</code> if execution was successful otherwise
* <code>false</code>.
* @see NodeModel#execute(BufferedDataTable[],ExecutionContext)
* @noreference This method is not intended to be referenced by clients.
* @since 2.8
*/
public boolean execute(final PortObject[] rawInData, final ExecutionEnvironment exEnv, final ExecutionContext exec) {
LOGGER.assertLog(NodeContext.getContext() != null, "No node context available, please check call hierarchy and fix it");
// clear the message object
clearNodeMessageAndNotify();
// loops that override the resetAndConfigureLoopBody (returning true)
// will not call reset between successive executions
// => force a clear of the model's content here
m_model.setHasContent(false);
// check if the node is part of a skipped branch and return appropriate objects without actually executing
// the node. We also need to make sure that we don't run InactiveBranchConsumers if they are in the middle of
// an inactive scope or loop so this check is not trivial...
// are we not a consumer and any of the incoming branches are inactive?
boolean isInactive = !isInactiveBranchConsumer() && containsInactiveObjects(rawInData);
// are we a consumer but in the middle of an inactive scope?
FlowObjectStack inStack = getFlowObjectStack();
FlowScopeContext peekfsc = inStack.peek(FlowScopeContext.class);
if (peekfsc != null) {
isInactive = isInactive || peekfsc.isInactiveScope();
}
PortObject[] newOutData;
if (isInactive) {
// just a normal node: skip execution and fill output ports with inactive markers
newOutData = new PortObject[getNrOutPorts()];
Arrays.fill(newOutData, InactiveBranchPortObject.INSTANCE);
} else {
PortObject[] newInData = new PortObject[rawInData.length];
// flow variable port (or inactive)
newInData[0] = rawInData[0];
// check for existence of all input tables
for (int i = 1; i < rawInData.length; i++) {
if (rawInData[i] == null && !m_inputs[i].getType().isOptional()) {
createErrorMessageAndNotify("Couldn't get data from predecessor (Port No." + i + ").");
return false;
}
if (rawInData[i] == null) {
// optional input
// (checked above)
newInData[i] = null;
} else if (rawInData[i] instanceof BufferedDataTable) {
newInData[i] = rawInData[i];
} else {
exec.setMessage("Copying input object at port " + i);
ExecutionContext subExec = exec.createSubExecutionContext(0.0);
try {
newInData[i] = copyPortObject(rawInData[i], subExec);
} catch (CanceledExecutionException e) {
createWarningMessageAndNotify("Execution canceled");
return false;
} catch (Throwable e) {
createErrorMessageAndNotify("Unable to clone input data at port " + i + " (" + m_inputs[i].getName() + "): " + e.getMessage(), e);
return false;
}
}
}
PortObject[] rawOutData;
try {
// INVOKE MODEL'S EXECUTE
// (warnings will now be processed "automatically" - we listen)
rawOutData = invokeFullyNodeModelExecute(exec, exEnv, newInData);
} catch (Throwable th) {
boolean isCanceled = th instanceof CanceledExecutionException;
isCanceled = isCanceled || th instanceof InterruptedException;
// TODO this can all be shortened to exec.isCanceled()?
isCanceled = isCanceled || exec.isCanceled();
// writing to a buffer is done asynchronously -- if this thread
// is interrupted while waiting for the IO thread to flush we take
// it as a graceful exit
isCanceled = isCanceled || (th instanceof DataContainerException && th.getCause() instanceof InterruptedException);
if (isCanceled) {
// clear the flag so that the ThreadPool does not kill the thread
Thread.interrupted();
reset();
createWarningMessageAndNotify("Execution canceled");
return false;
} else {
// check if we are inside a try-catch block (only if it was a real
// error - not when canceled!)
FlowObjectStack flowObjectStack = getFlowObjectStack();
FlowTryCatchContext tcslc = flowObjectStack.peek(FlowTryCatchContext.class);
if ((tcslc != null) && (!tcslc.isInactiveScope())) {
// failure inside an active try-catch:
// make node inactive but preserve error message.
reset();
PortObject[] outs = new PortObject[getNrOutPorts()];
Arrays.fill(outs, InactiveBranchPortObject.INSTANCE);
setOutPortObjects(outs, false, false);
createErrorMessageAndNotify("Execution failed in Try-Catch block: " + th.getMessage());
// and store information catch-node can report it
FlowObjectStack fos = getNodeModel().getOutgoingFlowObjectStack();
fos.push(new FlowVariable(FlowTryCatchContext.ERROR_FLAG, 1));
fos.push(new FlowVariable(FlowTryCatchContext.ERROR_NODE, getName()));
fos.push(new FlowVariable(FlowTryCatchContext.ERROR_REASON, th.getMessage()));
StringWriter thstack = new StringWriter();
th.printStackTrace(new PrintWriter(thstack));
tcslc.setError(getName(), th.getMessage(), thstack.toString());
fos.push(new FlowVariable(FlowTryCatchContext.ERROR_STACKTRACE, thstack.toString()));
return true;
}
}
String message = EXECUTE_FAILED_PREFIX;
if (th.getMessage() != null && th.getMessage().length() >= 5) {
message = message.concat(th.getMessage());
} else {
message = message.concat("(\"" + th.getClass().getSimpleName() + "\"): " + th.getMessage());
}
reset();
createErrorMessageAndNotify(message, th);
return false;
}
// copy to new array to prevent later modification in client code
newOutData = Arrays.copyOf(rawOutData, rawOutData.length);
if (newOutData[0] instanceof InactiveBranchPortObject) {
Arrays.fill(newOutData, InactiveBranchPortObject.INSTANCE);
isInactive = true;
}
}
if (isInactive) {
if (m_model instanceof ScopeStartNode) {
// inactive scope start node must indicate to their scope
// end node that they were inactive...
FlowScopeContext fsc = getOutgoingFlowObjectStack().peek(FlowScopeContext.class);
assert fsc != null;
fsc.inactiveScope(true);
}
if (m_model instanceof ScopeEndNode) {
// inactive as well (which we should see in the scope context object).
if (peekfsc == null) {
createErrorMessageAndNotify("Missing Scope Start Node in inactive branch.");
return false;
}
if (!peekfsc.isInactiveScope()) {
// we cannot handle this case: the End scope node needs
// to trigger re-execution which it won't in an inactive
// branch
createErrorMessageAndNotify("Active Scope End node in inactive branch not allowed.");
return false;
} else {
// also the scope start node is inactive, so the entire
// loop is inactive.
// Pop Scope object
// => this is done in configure, not needed here! (MB: Hittisau 2013)
// getOutgoingFlowObjectStack().pop(FlowScopeContext.class);
}
}
assert !m_model.hasContent() : "Inactive node should have no content in node model";
} else {
for (int p = 1; p < getNrOutPorts(); p++) {
m_outputs[p].hiliteHdl = m_model.getOutHiLiteHandler(p - 1);
}
}
// check if we see a loop status in the NodeModel
FlowLoopContext slc = m_model.getLoopContext();
// cannot be true for inactive nodes, see getLoopContext method
boolean continuesLoop = (slc != null);
boolean tolerateOutSpecDiff = (exEnv != null) && (exEnv.reExecute());
if (!setOutPortObjects(newOutData, continuesLoop, tolerateOutSpecDiff)) {
return false;
}
/* #assignInternalHeldObjects does some clean-up for previous internal tables in a loop. if the branch
* containing the loop is set to inactive, #cleanOutPorts is invoked via WorkflowManager#resetNodeAndSuccessors.
* This entails that the internal tables are cleared. Consequently, there is no need to do further clean-up in
* #assignInternalHeldObjects if the node has been set to inactive before. We are therefore OK with not calling
* #assignInternalHeldObjects when this node is inactive. */
if (!isInactive) {
assignInternalHeldObjects(rawInData, exEnv, exec, newOutData);
}
return true;
}
Aggregations