/** * * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.apache.airavata.workflow.model.graph.impl; import java.awt.Point; import java.util.ArrayList; import java.util.Collection; import java.util.Iterator; import java.util.List; import com.google.gson.Gson; import com.google.gson.JsonArray; import com.google.gson.JsonElement; import com.google.gson.JsonObject; import com.google.gson.JsonPrimitive; import org.apache.airavata.common.utils.StringUtil; import org.apache.airavata.common.utils.XMLUtil; import org.apache.airavata.workflow.model.component.Component; import org.apache.airavata.workflow.model.exceptions.WorkflowRuntimeException; import org.apache.airavata.workflow.model.graph.ControlPort; import org.apache.airavata.workflow.model.graph.DataPort; import org.apache.airavata.workflow.model.graph.Edge; import org.apache.airavata.workflow.model.graph.Graph; import org.apache.airavata.workflow.model.graph.GraphException; import org.apache.airavata.workflow.model.graph.GraphSchema; import org.apache.airavata.workflow.model.graph.Node; import org.apache.airavata.workflow.model.graph.Port; import org.apache.airavata.workflow.model.graph.Port.Kind; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.xmlpull.infoset.XmlElement; /** * The abstract implementation of the Node interface. This class should be hidden from the outsize of the package. * */ public abstract class NodeImpl implements Node { private static final Logger logger = LoggerFactory.getLogger(NodeImpl.class); protected String id; /** * A name of the node. */ private String name = ""; private Component component; private List<DataPort> outputPorts; private List<DataPort> inputPorts; private ControlPort controlInPort; private List<ControlPort> controlOutPorts; private PortImpl eprPort; private GraphImpl graph; private Point position; // The followings are used only during parsing the XML. private List<String> inputPortIDs; private List<String> outputPortIDs; private String controlInPortID; private List<String> controlOutPortIDs; private String eprPortID; private boolean breakOnExecution = false; protected String label; protected transient boolean requireJoin = false; private NodeExecutionState state = NodeExecutionState.WAITING; private List<NodeObserver> observers; /** * Creates a Node. */ protected NodeImpl() { // Iinitialized to the empty string to avoid NullPointerException. this.name = ""; this.position = new Point(); this.inputPorts = new ArrayList<DataPort>(); this.outputPorts = new ArrayList<DataPort>(); this.controlOutPorts = new ArrayList<ControlPort>(); this.inputPortIDs = new ArrayList<String>(); this.outputPortIDs = new ArrayList<String>(); this.controlOutPortIDs = new ArrayList<String>(); observers=new ArrayList<Node.NodeObserver>(); } protected NodeImpl(Graph graph) { this(); this.graph = (GraphImpl) graph; this.graph.addNode(this); } /** * Constructs a NodeImpl. * * @param nodeElement * @throws GraphException */ public NodeImpl(XmlElement nodeElement) throws GraphException { this(); parse(nodeElement); } public NodeImpl(JsonObject nodeObject) throws GraphException{ this(); parse(nodeObject); } /** * @return the ID of the node */ public String getID() { return this.id; } /** * Creates unique node ID in the graph that this node belongs to. */ public void createID() { String candidateID = StringUtil.convertToJavaIdentifier(this.name); Node node = this.graph.getNode(candidateID); while (node != null && node != this) { candidateID = StringUtil.incrementName(candidateID); node = this.graph.getNode(candidateID); } this.id = candidateID; for (PortImpl port : getAllPorts()) { port.createID(); } } /** * Returns the name. * * @return The name */ public String getName() { return this.name; } /** * Sets the name. * * @param name * The name */ public void setName(String name) { this.name = name; } /** * Returns the component. * * @return The component */ public Component getComponent() { return this.component; } /** * Sets the component. * * @param component * The component to set. */ public void setComponent(Component component) { this.component = component; } /** * @see org.apache.airavata.workflow.model.graph.Node#getGraph() */ public Graph getGraph() { return this.graph; } /** * @param port */ public void addOutputPort(DataPort port) { port.setKind(PortImpl.Kind.DATA_OUT); this.outputPorts.add(port); addPort(port); } /** * @param port * @throws GraphException */ public void removeOutputPort(PortImpl port) throws GraphException { this.graph.removePort(port); this.outputPorts.remove(port); } /** * @param port */ public void addInputPort(DataPort port) { port.setKind(PortImpl.Kind.DATA_IN); this.inputPorts.add(port); addPort(port); } /** * @param port * @throws GraphException */ public void removeInputPort(PortImpl port) throws GraphException { this.graph.removePort(port); this.inputPorts.remove(port); } /** * Sets the location of the node. * * @param point * The location */ public void setPosition(Point point) { this.position.x = point.x; this.position.y = point.y; } /** * @see org.apache.airavata.workflow.model.graph.Node#getPosition() */ public Point getPosition() { return this.position; } /** * Returns the List of output ports. * * @return the List of output ports */ public List<DataPort> getOutputPorts() { return this.outputPorts; } /** * Returns the List of input ports. * * @return the List of input ports */ public List<DataPort> getInputPorts() { return this.inputPorts; } /** * Returns the output port of the specified index. * * @param index * The specified index * @return the uses port of the specified index */ public DataPort getOutputPort(int index) { if (index < 0 || index >= this.outputPorts.size()) { String message = "index has to be possitive and less than " + this.outputPorts.size(); throw new IllegalArgumentException(message); } return this.outputPorts.get(index); } /** * Returns the input port of the specified index. * * @param index * The specified index * @return the input port of the specified index */ public DataPort getInputPort(int index) { if (index < 0 || index >= this.inputPorts.size()) { throw new IllegalArgumentException(); } return this.inputPorts.get(index); } /** * @return The controlInPort. */ public ControlPort getControlInPort() { return this.controlInPort; } /** * @see org.apache.airavata.workflow.model.graph.Node#getControlOutPorts() */ public List<ControlPort> getControlOutPorts() { return this.controlOutPorts; } /** * @see org.apache.airavata.workflow.model.graph.Node#getEPRPort() */ public PortImpl getEPRPort() { return this.eprPort; } /** * Returns all ports that belong to this node. * * @return All ports that belong to this node. */ public Collection<PortImpl> getAllPorts() { ArrayList<PortImpl> ports = new ArrayList<PortImpl>(); ports.addAll(this.inputPorts); ports.addAll(this.outputPorts); if (this.controlInPort != null) { ports.add(this.controlInPort); } ports.addAll(this.controlOutPorts); if (this.eprPort != null) { ports.add(this.eprPort); } return ports; } /** * Checks if this node contains a specified port. * * @param port * The specified port * @return true if this node contains port; false otherwise */ public boolean containsPort(Port port) { boolean contain = this.inputPorts.contains(port) || this.outputPorts.contains(port); return contain; } /** * @param controlInPort */ public void setControlInPort(ControlPort controlInPort) { controlInPort.setKind(Kind.CONTROL_IN); this.controlInPort = controlInPort; addPort(this.controlInPort); } /** * @param controlOutPort */ public void addControlOutPort(ControlPort controlOutPort) { controlOutPort.setKind(Kind.CONTROL_OUT); this.controlOutPorts.add(controlOutPort); addPort(controlOutPort); } /** * @param eprPort */ public void setEPRPort(PortImpl eprPort) { eprPort.setKind(Kind.EPR); this.eprPort = eprPort; addPort(eprPort); } /** * Sets a graph this node belogs to. * * @param graph * The graph */ protected void setGraph(GraphImpl graph) { this.graph = graph; } protected void indexToPointer() throws GraphException { for (String portID : this.inputPortIDs) { PortImpl port = this.graph.getPort(portID); if (port == null) { throw new GraphException("Port, " + portID + ", does not exist."); } port.setKind(PortImpl.Kind.DATA_IN); port.setNode(this); this.inputPorts.add((DataPort) port); } for (String portID : this.outputPortIDs) { PortImpl port = this.graph.getPort(portID); if (port == null) { throw new GraphException("Port, " + portID + ", does not exist."); } port.setKind(PortImpl.Kind.DATA_OUT); port.setNode(this); this.outputPorts.add((DataPort) port); } if (this.controlInPortID != null) { PortImpl port = this.graph.getPort(this.controlInPortID); if (port == null) { throw new GraphException("Port, " + this.controlInPortID + ", does not exist."); } port.setKind(PortImpl.Kind.CONTROL_IN); port.setNode(this); this.controlInPort = (ControlPort) port; } for (String portID : this.controlOutPortIDs) { PortImpl port = this.graph.getPort(portID); if (port == null) { throw new GraphException("Port, " + portID + ", does not exist."); } port.setKind(PortImpl.Kind.CONTROL_OUT); port.setNode(this); this.controlOutPorts.add((ControlPort) port); } if (this.eprPortID != null) { PortImpl port = this.graph.getPort(this.eprPortID); if (port == null) { throw new GraphException("Port, " + this.eprPortID + ", does not exist."); } port.setKind(PortImpl.Kind.EPR); port.setNode(this); this.eprPort = port; } } /** * @param nodeElement * @throws GraphException */ protected void parse(XmlElement nodeElement) throws GraphException { XmlElement idElement = nodeElement.element(GraphSchema.NODE_ID_TAG); this.id = idElement.requiredText(); XmlElement nameElement = nodeElement.element(GraphSchema.NODE_NAME_TAG); this.name = nameElement.requiredText(); // XmlElement labelElement = nodeElement // .element(GraphSchema.NODE_STREAM_LABEL_TAG); // if (null != labelElement) { // this.label = labelElement.requiredText(); // } Iterable<XmlElement> inputPortElements = nodeElement.elements(null, GraphSchema.NODE_INPUT_PORT_TAG); for (XmlElement inputPort : inputPortElements) { this.inputPortIDs.add(inputPort.requiredText()); } Iterable<XmlElement> outputPortElements = nodeElement.elements(null, GraphSchema.NODE_OUTPUT_PORT_TAG); for (XmlElement outputPort : outputPortElements) { this.outputPortIDs.add(outputPort.requiredText()); } XmlElement controlInPortElement = nodeElement.element(GraphSchema.NODE_CONTROL_IN_PORT_TAG); if (controlInPortElement != null) { this.controlInPortID = controlInPortElement.requiredText(); } Iterable<XmlElement> controlOutPortElements = nodeElement.elements(null, GraphSchema.NODE_CONTROL_OUT_PORT_TAG); for (XmlElement controlOutPort : controlOutPortElements) { this.controlOutPortIDs.add(controlOutPort.requiredText()); } XmlElement eprPortElement = nodeElement.element(GraphSchema.NODE_EPR_PORT_TAG); if (eprPortElement != null) { this.eprPortID = eprPortElement.requiredText(); } XmlElement xElement = nodeElement.element(GraphSchema.NODE_X_LOCATION_TAG); this.position.x = (int) Double.parseDouble(xElement.requiredText()); XmlElement yElement = nodeElement.element(GraphSchema.NODE_Y_LOCATION_TAG); this.position.y = (int) Double.parseDouble(yElement.requiredText()); XmlElement configElement = nodeElement.element(GraphSchema.NODE_CONFIG_TAG); if (configElement != null) { parseConfiguration(configElement); } XmlElement componentElement = nodeElement.element(GraphSchema.NODE_COMPONENT_TAG); if (componentElement != null) { // XXX Not used since the introduction of .xwf parseComponent(componentElement); } } protected void parse(JsonObject nodeObject) { this.id = nodeObject.getAsJsonPrimitive(GraphSchema.NODE_ID_TAG).getAsString(); this.name = nodeObject.getAsJsonPrimitive(GraphSchema.NODE_NAME_TAG).getAsString(); JsonArray jArray; if (nodeObject.get(GraphSchema.NODE_INPUT_PORT_TAG) != null) { jArray = nodeObject.getAsJsonArray(GraphSchema.NODE_INPUT_PORT_TAG); for (JsonElement jsonElement : jArray) { this.inputPortIDs.add(jsonElement.getAsString()); } } if (nodeObject.get(GraphSchema.NODE_OUTPUT_PORT_TAG) != null) { jArray = nodeObject.getAsJsonArray(GraphSchema.NODE_OUTPUT_PORT_TAG); for (JsonElement jsonElement : jArray) { this.outputPortIDs.add(jsonElement.getAsString()); } } JsonElement jElement = nodeObject.get(GraphSchema.NODE_CONTROL_IN_PORT_TAG); if (jElement != null) { this.controlInPortID = jElement.getAsString(); } if (nodeObject.get(GraphSchema.NODE_CONTROL_OUT_PORT_TAG) != null) { jArray = nodeObject.getAsJsonArray(GraphSchema.NODE_CONTROL_OUT_PORT_TAG); for (JsonElement jsonElement : jArray) { this.controlOutPortIDs.add(jsonElement.getAsString()); } } jElement = nodeObject.get(GraphSchema.NODE_EPR_PORT_TAG); if (jElement != null) { this.eprPortID = jElement.getAsString(); } this.position.x = nodeObject.get(GraphSchema.NODE_X_LOCATION_TAG).getAsInt(); this.position.y = nodeObject.get(GraphSchema.NODE_Y_LOCATION_TAG).getAsInt(); // Parse config element not sure why we used it. // Parse component element. JsonObject configObject = nodeObject.getAsJsonObject(GraphSchema.NODE_CONFIG_TAG); if (configObject != null) { parseConfiguration(configObject); } } /** * @param componentElement * @throws GraphException * When the component is in wrong format. This might be thrown by the sub classes. */ @SuppressWarnings("unused") @Deprecated protected void parseComponent(XmlElement componentElement) throws GraphException { logger.debug("Entering:" + componentElement); // Do nothing by default. } protected void parseConfiguration(XmlElement configElement) { logger.debug("Entering:" + configElement); // Do nothing by default. } protected void parseConfiguration(JsonObject configObject) { logger.debug("Entering:" + new Gson().toJson(configObject)); } /** * @return the node xml */ protected XmlElement toXML() { XmlElement nodeElement = XMLUtil.BUILDER.newFragment(GraphSchema.NS, GraphSchema.NODE_TAG); XmlElement idElement = nodeElement.addElement(GraphSchema.NS, GraphSchema.NODE_ID_TAG); idElement.addChild(this.id); XmlElement nameElement = nodeElement.addElement(GraphSchema.NS, GraphSchema.NODE_NAME_TAG); nameElement.addChild(this.name); // if (null != this.label) { // XmlElement labelElement = nodeElement.addElement(GraphSchema.NS, // GraphSchema.NODE_STREAM_LABEL_TAG); // // labelElement.addChild(this.label); // } // Output ports for (PortImpl port : this.outputPorts) { XmlElement portElement = nodeElement.addElement(GraphSchema.NS, GraphSchema.NODE_OUTPUT_PORT_TAG); portElement.addChild(port.getID()); } // Input ports for (PortImpl port : this.inputPorts) { XmlElement portElement = nodeElement.addElement(GraphSchema.NS, GraphSchema.NODE_INPUT_PORT_TAG); portElement.addChild(port.getID()); } // Control-in port if (this.controlInPort != null) { XmlElement portElement = nodeElement.addElement(GraphSchema.NS, GraphSchema.NODE_CONTROL_IN_PORT_TAG); portElement.addChild(this.controlInPort.getID()); } // EPR Port if (this.eprPort != null) { XmlElement portElement = nodeElement.addElement(GraphSchema.NS, GraphSchema.NODE_EPR_PORT_TAG); portElement.addChild(this.eprPort.getID()); } // Control-out ports for (PortImpl port : this.controlOutPorts) { XmlElement portElement = nodeElement.addElement(GraphSchema.NS, GraphSchema.NODE_CONTROL_OUT_PORT_TAG); portElement.addChild(port.getID()); } XmlElement xElement = nodeElement.addElement(GraphSchema.NS, GraphSchema.NODE_X_LOCATION_TAG); xElement.addChild(Integer.toString(this.position.x)); XmlElement yElement = nodeElement.addElement(GraphSchema.NS, GraphSchema.NODE_Y_LOCATION_TAG); yElement.addChild(Integer.toString(this.position.y)); addConfigurationElement(nodeElement); return nodeElement; } protected JsonObject toJSON() { JsonObject nodeObject = new JsonObject(); nodeObject.addProperty(GraphSchema.NODE_ID_TAG, getID()); nodeObject.addProperty(GraphSchema.NODE_NAME_TAG, getName()); if (this.inputPorts.size() > 0) { JsonArray inputPortsArray = new JsonArray(); for (PortImpl inputPort : this.inputPorts) { inputPortsArray.add(new JsonPrimitive(inputPort.getID())); } nodeObject.add(GraphSchema.NODE_INPUT_PORT_TAG, inputPortsArray); } if (this.outputPorts.size() > 0) { JsonArray outputPortsArray = new JsonArray(); for (PortImpl outputPort : this.outputPorts) { outputPortsArray.add(new JsonPrimitive(outputPort.getID())); } nodeObject.add(GraphSchema.NODE_OUTPUT_PORT_TAG, outputPortsArray); } if (this.controlInPort != null) { nodeObject.addProperty(GraphSchema.NODE_CONTROL_IN_PORT_TAG, this.controlInPort.getID()); } if (this.controlOutPorts.size() > 0) { JsonArray controlOutPortArray = new JsonArray(); for (PortImpl controlOutPort : this.controlOutPorts) { controlOutPortArray.add(new JsonPrimitive(controlOutPort.getID())); } nodeObject.add(GraphSchema.NODE_CONTROL_OUT_PORT_TAG, controlOutPortArray); } nodeObject.addProperty(GraphSchema.NODE_X_LOCATION_TAG, Integer.toString(this.position.x)); nodeObject.addProperty(GraphSchema.NODE_Y_LOCATION_TAG, Integer.toString(this.position.y)); addConfigurationElement(nodeObject); return nodeObject; } /** * Adds a configuration element to a specified node element. * * @param nodeElement * The specified node element * @return The configuration element added */ @SuppressWarnings("unused") protected XmlElement addConfigurationElement(XmlElement nodeElement) { // Do nothing by default. return null; } protected JsonObject addConfigurationElement(JsonObject nodeObject) { // Do nothing by default. return null; } /** * Called when an Edge was added. It doesn't do anything by default. * * @param edge * @throws GraphException * When the added edge is not allowed. This might be thrown by subclasses. */ @SuppressWarnings("unused") protected void edgeWasAdded(Edge edge) throws GraphException { // Do nothing } /** * Called when an Edge was removed. It doesn't do anything by default. * * @param edge */ @SuppressWarnings("unused") protected void edgeWasRemoved(Edge edge) { // Do nothing } private void addPort(PortImpl port) { port.setNode(this); this.graph.addPort(port); } public boolean isBreak() { return this.breakOnExecution; } public void setBreak(boolean breakVal) { this.breakOnExecution = breakVal; } /** * */ public boolean isAllInPortsConnected() { for (Iterator<DataPort> iterator = this.inputPorts.iterator(); iterator.hasNext();) { DataPort port = iterator.next(); if (port.getFromNode() == null) { return false; } } return true; } public DataPort getOutputPort(String fromPortID) { for (DataPort port : this.outputPorts) { if (port.getID().equals(fromPortID)) { return port; } } throw new WorkflowRuntimeException("Port with id not found :" + fromPortID); } public DataPort getInputPort(String id) { for (DataPort port : this.inputPorts) { if (port.getID().equals(id)) { return port; } } throw new WorkflowRuntimeException("Port with id not found :" + id); } /** * @return */ public String getLabel() { return this.label; } public void setRequireJoin(boolean join) { this.requireJoin = join; } public boolean getRequireJoin() { return this.requireJoin; } @Override public NodeExecutionState getState() { return state; } @Override public void setState(NodeExecutionState state) { this.state = state; triggerNodeObservers(NodeUpdateType.STATE_CHANGED); } @Override public void registerObserver(NodeObserver o) { observers.add(o); } @Override public void removeObserver(NodeObserver o) { if (observers.contains(o)) { observers.remove(o); } } private void triggerNodeObservers(NodeUpdateType type){ for (NodeObserver o : observers) { try { o.nodeUpdated(type); } catch (Exception e) { logger.error(e.getMessage(), e); } } } }