Search in sources :

Example 1 with FlowTryCatchContext

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 };
}
Also used : InactiveBranchPortObject(org.knime.core.node.port.inactive.InactiveBranchPortObject) FlowTryCatchContext(org.knime.core.node.workflow.FlowTryCatchContext) FlowVariablePortObject(org.knime.core.node.port.flowvariable.FlowVariablePortObject) PortObject(org.knime.core.node.port.PortObject) InactiveBranchPortObject(org.knime.core.node.port.inactive.InactiveBranchPortObject)

Example 2 with FlowTryCatchContext

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;
}
Also used : DataContainerException(org.knime.core.data.container.DataContainerException) InactiveBranchPortObject(org.knime.core.node.port.inactive.InactiveBranchPortObject) FlowObjectStack(org.knime.core.node.workflow.FlowObjectStack) FlowLoopContext(org.knime.core.node.workflow.FlowLoopContext) StringWriter(java.io.StringWriter) FlowScopeContext(org.knime.core.node.workflow.FlowScopeContext) FlowTryCatchContext(org.knime.core.node.workflow.FlowTryCatchContext) ScopeStartNode(org.knime.core.node.workflow.ScopeStartNode) ScopeEndNode(org.knime.core.node.workflow.ScopeEndNode) PortObject(org.knime.core.node.port.PortObject) FileStorePortObject(org.knime.core.data.filestore.FileStorePortObject) FlowVariablePortObject(org.knime.core.node.port.flowvariable.FlowVariablePortObject) InactiveBranchPortObject(org.knime.core.node.port.inactive.InactiveBranchPortObject) FlowVariable(org.knime.core.node.workflow.FlowVariable) PrintWriter(java.io.PrintWriter)

Aggregations

PortObject (org.knime.core.node.port.PortObject)2 FlowVariablePortObject (org.knime.core.node.port.flowvariable.FlowVariablePortObject)2 InactiveBranchPortObject (org.knime.core.node.port.inactive.InactiveBranchPortObject)2 FlowTryCatchContext (org.knime.core.node.workflow.FlowTryCatchContext)2 PrintWriter (java.io.PrintWriter)1 StringWriter (java.io.StringWriter)1 DataContainerException (org.knime.core.data.container.DataContainerException)1 FileStorePortObject (org.knime.core.data.filestore.FileStorePortObject)1 FlowLoopContext (org.knime.core.node.workflow.FlowLoopContext)1 FlowObjectStack (org.knime.core.node.workflow.FlowObjectStack)1 FlowScopeContext (org.knime.core.node.workflow.FlowScopeContext)1 FlowVariable (org.knime.core.node.workflow.FlowVariable)1 ScopeEndNode (org.knime.core.node.workflow.ScopeEndNode)1 ScopeStartNode (org.knime.core.node.workflow.ScopeStartNode)1