use of org.knime.core.node.port.PortObjectSpec in project knime-core by knime.
the class SingleNodeContainer method configure.
// ////////////////////////////////
// Handle State Transitions
// ////////////////////////////////
/**
* Configure underlying node and update state accordingly.
*
* @param inObjectSpecs input table specifications
* @param keepNodeMessage If true the previous message is kept (unless an error needs to be shown)
* @return true if output specs have changed.
* @throws IllegalStateException in case of illegal entry state.
*/
final boolean configure(final PortObjectSpec[] inObjectSpecs, final boolean keepNodeMessage) {
synchronized (m_nodeMutex) {
final NodeMessage oldMessage = getNodeMessage();
// remember old specs
PortObjectSpec[] prevSpecs = new PortObjectSpec[getNrOutPorts()];
for (int i = 0; i < prevSpecs.length; i++) {
prevSpecs[i] = getOutPort(i).getPortObjectSpec();
}
boolean prevInactivity = isInactive();
// perform action
switch(getInternalState()) {
case IDLE:
if (callNodeConfigure(inObjectSpecs, keepNodeMessage)) {
setInternalState(InternalNodeContainerState.CONFIGURED);
} else {
setInternalState(InternalNodeContainerState.IDLE);
}
break;
case UNCONFIGURED_MARKEDFOREXEC:
if (callNodeConfigure(inObjectSpecs, keepNodeMessage)) {
setInternalState(InternalNodeContainerState.CONFIGURED_MARKEDFOREXEC);
} else {
setInternalState(InternalNodeContainerState.UNCONFIGURED_MARKEDFOREXEC);
}
break;
case CONFIGURED:
// m_node.reset();
boolean success = callNodeConfigure(inObjectSpecs, keepNodeMessage);
if (success) {
setInternalState(InternalNodeContainerState.CONFIGURED);
} else {
// m_node.reset();
setInternalState(InternalNodeContainerState.IDLE);
}
break;
case CONFIGURED_MARKEDFOREXEC:
// these are dangerous - otherwise re-queued loop-ends are
// reset!
// m_node.reset();
success = callNodeConfigure(inObjectSpecs, keepNodeMessage);
if (success) {
setInternalState(InternalNodeContainerState.CONFIGURED_MARKEDFOREXEC);
} else {
// m_node.reset();
setInternalState(InternalNodeContainerState.UNCONFIGURED_MARKEDFOREXEC);
}
break;
case // this should only happen during load
EXECUTINGREMOTELY:
success = callNodeConfigure(inObjectSpecs, keepNodeMessage);
if (!success) {
setInternalState(InternalNodeContainerState.IDLE);
}
break;
default:
throwIllegalStateException();
}
if (keepNodeMessage) {
setNodeMessage(NodeMessage.merge(oldMessage, getNodeMessage()));
}
// fake a state change to make sure it's displayed properly
if (prevInactivity != isInactive()) {
InternalNodeContainerState oldSt = this.getInternalState();
setInternalState(InternalNodeContainerState.IDLE.equals(oldSt) ? InternalNodeContainerState.CONFIGURED : InternalNodeContainerState.IDLE);
setInternalState(oldSt);
return true;
}
// compare old and new specs
for (int i = 0; i < prevSpecs.length; i++) {
PortObjectSpec newSpec = getOutPort(i).getPortObjectSpec();
if (newSpec != null) {
if (!newSpec.equals(prevSpecs[i])) {
return true;
}
} else if (prevSpecs[i] != null) {
// newSpec is null!
return true;
}
}
// all specs & inactivity stayed the same!
return false;
}
}
use of org.knime.core.node.port.PortObjectSpec in project knime-core by knime.
the class Node method setOutPortObjects.
/**
* Called after execute in order to put the computed result into the
* outports. It will do a sequence of sanity checks whether the argument
* is valid (non-null, correct type, etc.)
* @param newOutData The computed output data
* @param tolerateNullOutports used e.g. when loop is continued (outports may not yet be available)
* @param tolerateDifferentSpecs used e.g. when re-executing a node (table may be different from configure)
* @return Whether that is successful (false in case of incompatible port objects)
*/
private boolean setOutPortObjects(final PortObject[] newOutData, final boolean tolerateNullOutports, final boolean tolerateDifferentSpecs) {
CheckUtils.checkArgumentNotNull(newOutData, "Port object array is null");
if (newOutData.length != getNrOutPorts()) {
throw new IndexOutOfBoundsException("Array is expected to be of " + "length " + getNrOutPorts() + ": " + newOutData.length);
}
// check for compatible output PortObjects
for (int i = 0; i < newOutData.length; i++) {
PortType thisType = m_outputs[i].type;
if (newOutData[i] == null && !tolerateNullOutports) {
createErrorMessageAndNotify("Output at port " + i + " is null");
return false;
}
if (newOutData[i] != null) {
if (newOutData[i] instanceof InactiveBranchPortObject) {
// allow PO coming from inactive branch
// TODO ensure model was skipped during configure?
} else if (!thisType.getPortObjectClass().isInstance(newOutData[i])) {
createErrorMessageAndNotify("Invalid output port object at port " + i);
LOGGER.error(" (Wanted: " + thisType.getPortObjectClass().getName() + ", " + "actual: " + newOutData[i].getClass().getName() + ")");
return false;
}
PortObjectSpec spec;
try {
spec = newOutData[i].getSpec();
} catch (Throwable t) {
createErrorMessageAndNotify("PortObject \"" + newOutData[i].getClass().getName() + "\" threw " + t.getClass().getSimpleName() + " on #getSpec() ", t);
return false;
}
if (spec == null) {
createErrorMessageAndNotify("Implementation Error: PortObject \"" + newOutData[i].getClass().getName() + "\" must not" + " have null spec (output port " + i + ").");
return false;
}
}
}
for (int p = 0; p < getNrOutPorts(); p++) {
if (newOutData[p] instanceof BufferedDataTable) {
BufferedDataTable thisTable = (BufferedDataTable) newOutData[p];
DataTableSpec portSpec = (DataTableSpec) (m_outputs[p].spec);
DataTableSpec newPortSpec = thisTable.getDataTableSpec();
if ((portSpec != null) && !tolerateDifferentSpecs) {
if (!portSpec.equalStructure(newPortSpec)) {
// this used to be an ERROR but is now more frequently encountered due to file reader nodes
// caching their output spec to avoid in I/O in #configure
final String warningMsg = "DataSpec generated by configure does not match spec after execution.";
LOGGER.coding(warningMsg);
createWarningMessageAndNotify(warningMsg);
}
}
BufferedDataTable t = thisTable;
t.setOwnerRecursively(this);
m_outputs[p].object = t;
m_outputs[p].summary = t.getSummary();
m_outputs[p].spec = newPortSpec;
} else {
m_outputs[p].object = newOutData[p];
if (newOutData[p] != null) {
m_outputs[p].spec = newOutData[p].getSpec();
m_outputs[p].summary = newOutData[p].getSummary();
} else {
m_outputs[p].summary = null;
}
}
}
return true;
}
use of org.knime.core.node.port.PortObjectSpec in project knime-core by knime.
the class Node method createNodeExecutionResult.
/**
* Creates an execution result containing all calculated values in a
* execution. The returned value is suitable to be used in
* {@link #loadDataAndInternals(
* NodeContentPersistor, ExecutionMonitor, LoadResult)}.
* If this node is not executed, it will assign null values to the fields
* in the returned execution result.
* @param exec For progress information.
* @return A new execution result containing the values being calculated.
* @throws CanceledExecutionException If canceled
*/
public NodeExecutionResult createNodeExecutionResult(final ExecutionMonitor exec) throws CanceledExecutionException {
NodeExecutionResult result = new NodeExecutionResult();
result.setWarningMessage(m_model.getWarningMessage());
if (hasContent()) {
File internTempDir;
try {
internTempDir = FileUtil.createTempDir("knime_node_internDir");
exec.setMessage("Saving internals");
saveInternals(internTempDir, exec.createSubProgress(0.0));
result.setNodeInternDir(new ReferencedFile(internTempDir));
} catch (IOException ioe) {
LOGGER.error("Unable to save internals", ioe);
}
}
if (m_internalHeldPortObjects != null) {
PortObject[] internalHeldPortObjects = Arrays.copyOf(m_internalHeldPortObjects, m_internalHeldPortObjects.length);
result.setInternalHeldPortObjects(internalHeldPortObjects);
}
PortObject[] pos = new PortObject[getNrOutPorts()];
PortObjectSpec[] poSpecs = new PortObjectSpec[getNrOutPorts()];
for (int i = 0; i < pos.length; i++) {
PortObject po = getOutputObject(i);
if (po != null) {
pos[i] = po;
poSpecs[i] = po.getSpec();
}
}
result.setPortObjects(pos);
result.setPortObjectSpecs(poSpecs);
// Add the outgoing flow variables to the execution result
FlowObjectStack outgoingStack = m_model.getOutgoingFlowObjectStack();
List<FlowVariable> nodeFlowVars = outgoingStack.getAvailableFlowVariables().values().stream().filter(f -> f.getScope().equals(FlowVariable.Scope.Flow)).collect(Collectors.toList());
// the bottom most element should remain at the bottom of the stack
Collections.reverse(nodeFlowVars);
result.setFlowVariables(nodeFlowVars);
return result;
}
use of org.knime.core.node.port.PortObjectSpec in project knime-core by knime.
the class Node method configure.
/**
* Allows passing an object that may modify the specs created by the
* {@link NodeModel}, for example in case the node is wrapped and the
* output is modified.
*
* @param rawInSpecs table specs from the predecessors
* @param configureHelper object called after node model calculated output
* specs
* @return true if configure finished successfully.
* @noreference This method is not intended to be referenced by clients.
*/
public boolean configure(final PortObjectSpec[] rawInSpecs, final NodeConfigureHelper configureHelper) {
boolean success = false;
LOGGER.assertLog(NodeContext.getContext() != null, "No node context available, please check call hierarchy and fix it");
synchronized (m_configureLock) {
// reset message object
clearNodeMessageAndNotify();
// copy input port object specs, ignoring the 0-variable port:
PortObjectSpec[] inSpecs = Arrays.copyOfRange(rawInSpecs, 1, rawInSpecs.length);
// clean output spec
for (int p = 0; p < m_outputs.length; p++) {
// update data table spec
m_outputs[p].spec = null;
}
PortObjectSpec[] newOutSpec = new PortObjectSpec[getNrOutPorts() - 1];
try {
// check the inspecs against null
for (int i = 0; i < inSpecs.length; i++) {
if (inSpecs[i] == null) {
if (m_inputs[i + 1].getType().isOptional()) {
// ignore, unconnected optional input
} else {
return false;
}
// TODO: did we really need a warning here??
// throw new InvalidSettingsException(
// "Node is not executable until all predecessors "
// + "are configured and/or executed.");
}
}
// check if the node is part of a skipped branch and return
// appropriate specs without actually configuring the node.
// Note that we must also check the incoming variable port!
boolean isInactive = false;
if (!isInactiveBranchConsumer()) {
for (int i = 0; i < rawInSpecs.length; i++) {
if (rawInSpecs[i] instanceof InactiveBranchPortObjectSpec) {
isInactive = true;
break;
}
}
} else {
FlowLoopContext flc = getFlowObjectStack().peek(FlowLoopContext.class);
if (flc != null && flc.isInactiveScope()) {
isInactive = true;
}
}
if (isInactive) {
for (int j = 0; j < m_outputs.length; j++) {
m_outputs[j].spec = InactiveBranchPortObjectSpec.INSTANCE;
}
if (success) {
LOGGER.debug("Configure skipped. (" + getName() + " in inactive branch.)");
}
return true;
}
if (configureHelper != null) {
configureHelper.preConfigure();
}
// call configure model to create output table specs
// guaranteed to return non-null, correct-length array
newOutSpec = invokeNodeModelConfigure(inSpecs);
if (configureHelper != null) {
newOutSpec = configureHelper.postConfigure(inSpecs, newOutSpec);
}
// find out if we are in the middle of executing a loop and this is a LoopEnd node
boolean isIntermediateRunningLoop = false;
if (isModelCompatibleTo(LoopEndNode.class)) {
if ((getLoopContext() != null) && !getPauseLoopExecution()) {
FlowLoopContext flc = m_model.getFlowObjectStack().peek(FlowLoopContext.class);
if ((flc != null) && (flc.getIterationIndex() > 0)) {
// don't treat first iteration as "in the middle":
isIntermediateRunningLoop = true;
}
}
}
if (!isIntermediateRunningLoop) {
// update data table specs
for (int p = 0; p < newOutSpec.length; p++) {
m_outputs[p + 1].spec = newOutSpec[p];
}
} else {
// on the loop end node (avoids costly configure calls on remainder of workflow).
for (int p = 0; p < newOutSpec.length; p++) {
if (newOutSpec[p] instanceof DataTableSpec) {
// remove domain before assigning spec to outputs
DataTableSpecCreator dtsCreator = new DataTableSpecCreator((DataTableSpec) newOutSpec[p]);
dtsCreator.dropAllDomains();
m_outputs[p + 1].spec = dtsCreator.createSpec();
} else {
// no domain to clean in PortObjectSpecs
m_outputs[p + 1].spec = newOutSpec[p];
}
}
}
m_outputs[0].spec = FlowVariablePortObjectSpec.INSTANCE;
success = true;
} catch (InvalidSettingsException ise) {
Throwable cause = ise.getCause();
if (cause == null) {
createWarningMessageAndNotify(ise.getMessage());
} else {
createWarningMessageAndNotify(ise.getMessage(), ise);
}
} catch (Throwable t) {
String error = "Configure failed (" + t.getClass().getSimpleName() + "): " + t.getMessage();
createErrorMessageAndNotify(error, t);
}
}
if (success) {
LOGGER.debug("Configure succeeded. (" + this.getName() + ")");
}
return success;
}
use of org.knime.core.node.port.PortObjectSpec in project knime-core by knime.
the class FileNodePersistor method loadPort.
void loadPort(final Node node, final ReferencedFile portDir, final NodeSettingsRO settings, final ExecutionMonitor exec, final int portIdx, final Map<Integer, BufferedDataTable> loadTblRep, final WorkflowDataRepository dataRepository) throws IOException, InvalidSettingsException, CanceledExecutionException {
final String specClass = settings.getString("port_spec_class");
final String objectClass = loadPortObjectClassName(settings);
PortType designatedType = node.getOutputType(portIdx);
PortObjectSpec spec = null;
PortObject object = null;
// this cannot be simplified as BDT must be loaded as BDT even if
// the port type is not BDT (but general PortObject)
boolean isBDT = (BufferedDataTable.TYPE.getPortObjectClass().getName().equals(objectClass) && BufferedDataTable.TYPE.getPortObjectSpecClass().getName().equals(specClass)) || designatedType.equals(BufferedDataTable.TYPE);
// an InactiveBranchPortObjectSpec can be put into any port!
boolean isInactive = InactiveBranchPortObjectSpec.class.getName().equals(specClass);
if (isBDT && !isInactive) {
if (specClass != null && !specClass.equals(BufferedDataTable.TYPE.getPortObjectSpecClass().getName())) {
throw new IOException("Actual spec class \"" + specClass + "\", expected \"" + BufferedDataTable.TYPE.getPortObjectSpecClass().getName() + "\"");
}
if (objectClass != null && !objectClass.equals(BufferedDataTable.TYPE.getPortObjectClass().getName())) {
throw new IOException("Actual object class \"" + objectClass + "\", expected \"" + BufferedDataTable.TYPE.getPortObjectClass().getName() + "\"");
}
if (objectClass != null) {
object = loadBufferedDataTable(portDir, exec, loadTblRep, dataRepository);
((BufferedDataTable) object).setOwnerRecursively(node);
spec = ((BufferedDataTable) object).getDataTableSpec();
} else if (specClass != null) {
spec = BufferedDataTable.loadSpec(portDir);
}
} else {
object = loadPortObject(portDir, settings, exec, dataRepository).orElse(null);
spec = object != null ? object.getSpec() : null;
}
if (spec != null) {
if (!designatedType.getPortObjectSpecClass().isInstance(spec) && !isInactive) {
throw new IOException("Actual port spec type (\"" + spec.getClass().getSimpleName() + "\") does not match designated one (\"" + designatedType.getPortObjectSpecClass().getSimpleName() + "\")");
}
}
String summary = null;
if (object != null) {
if (!designatedType.getPortObjectClass().isInstance(object) && !isInactive) {
throw new IOException("Actual port object type (\"" + object.getClass().getSimpleName() + "\") does not match designated one (\"" + designatedType.getPortObjectClass().getSimpleName() + "\")");
}
summary = settings.getString("port_object_summary", null);
if (summary == null) {
summary = object.getSummary();
}
}
setPortObjectSpec(portIdx, spec);
setPortObject(portIdx, object);
setPortObjectSummary(portIdx, summary);
}
Aggregations