Package org.apache.airavata.xbaya.jython.script

Source Code of org.apache.airavata.xbaya.jython.script.JythonScript

/*
*
* 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.xbaya.jython.script;

import java.io.PrintWriter;
import java.io.StringWriter;
import java.net.URI;
import java.util.ArrayList;
import java.util.Collection;
import java.util.LinkedList;
import java.util.List;

import javax.xml.namespace.QName;

import org.apache.airavata.common.utils.StringUtil;
import org.apache.airavata.xbaya.XBayaConfiguration;
import org.apache.airavata.xbaya.XBayaConstants;
import org.apache.airavata.xbaya.XBayaVersion;
import org.apache.airavata.xbaya.component.ws.WSComponent;
import org.apache.airavata.xbaya.graph.Graph;
import org.apache.airavata.xbaya.graph.GraphException;
import org.apache.airavata.xbaya.graph.Node;
import org.apache.airavata.xbaya.graph.Port;
import org.apache.airavata.xbaya.graph.system.ConstantNode;
import org.apache.airavata.xbaya.graph.system.EndifNode;
import org.apache.airavata.xbaya.graph.system.IfNode;
import org.apache.airavata.xbaya.graph.system.InputNode;
import org.apache.airavata.xbaya.graph.system.MemoNode;
import org.apache.airavata.xbaya.graph.system.OutputNode;
import org.apache.airavata.xbaya.graph.util.GraphUtil;
import org.apache.airavata.xbaya.graph.ws.WSNode;
import org.apache.airavata.xbaya.invoker.GenericInvoker;
import org.apache.airavata.xbaya.jython.lib.NotificationSender;
import org.apache.airavata.xbaya.wf.Workflow;

public class JythonScript {

    /**
     * GFAC_VARIABLE
     */
    public static final String GFAC_VARIABLE = "gfacURL";

    /**
     * BROKER_LOC_VARIABLE
     */
    public static final String BROKER_URL_VARIABLE = "brokerURL";

    /**
     * MESSAGE_BOX_URL_VARIABLE
     */
    public static final String MESSAGE_BOX_URL_VARIABLE = "msgBoxURL";

    /**
     * TOPIC_VARIABLE
     */
    public static final String TOPIC_VARIABLE = "topic";

    private static final String INVOKER_CLASS = StringUtil.getClassName(GenericInvoker.class);

    private static final String NOTIFICATION_CLASS = StringUtil.getClassName(NotificationSender.class);

    private static final String WORKFLOW_STARTED_METHOD = "workflowStarted";

    private static final String WORKFLOW_COMPLETED_METHOD = "workflowFinished";

    private static final String WORKFLOW_INCOMPLETED_METHOD = "workflowFailed";

    private static final String SETUP_METHOD = "setup";

    private static final String SET_OPERATION_METHOD = "setOperation";

    private static final String SET_INPUT_METHOD = "setInput";

    private static final String GET_OUTPUT_METHOD = "getOutput";

    private static final String WAIT_METHOD = "waitToFinish";

    private static final String INVOKE_METHOD = "invoke";

    private static final String GET_PROPERTY_METHOD = "getProperty";

    private static final String NOTIFICATION_VARIABLE = "notifier";

    private static final String PROPERTIES_VARIABLE = "properties";

    private static final String INVOKER_SUFFIX = "_invoker";

    private static final String QNAME_SUFFIX = "_qname";

    private static final String VALUE_SUFFIX = "_value";

    private static final String TAB = "    ";

    /**
     * Suffix to put after node ID to create WSDL ID.
     */
    private static final String WSDL_SUFFIX = "_wsdl";

    private XBayaConfiguration configuration;

    private Workflow workflow;

    private Graph graph;

    /**
     * Collection of nodes that are not invoked yet
     */
    private Collection<Node> notYetInvokedNodes;

    /**
     * Collection of nodes that are executing now, and not finished yet.
     */
    private Collection<Node> executingNodes;

    private Collection<InputNode> inputNodes;

    private Collection<OutputNode> outputNodes;

    /**
     * List of command-line arguments
     */
    private List<String> arguments;

    private String scriptString;

    /**
     * Constructs a JythonScript.
     *
     * @param workflow
     * @param configuration
     */
    public JythonScript(Workflow workflow, XBayaConfiguration configuration) {
        this.workflow = workflow;
        this.configuration = configuration;
        this.graph = this.workflow.getGraph();

        this.arguments = new ArrayList<String>();

        this.notYetInvokedNodes = new LinkedList<Node>();
        for (Node node : this.graph.getNodes()) {
            if (!(node instanceof MemoNode)) {
                this.notYetInvokedNodes.add(node);
            }
        }
        this.executingNodes = new LinkedList<Node>();
        this.inputNodes = GraphUtil.getInputNodes(this.graph);
        this.outputNodes = GraphUtil.getOutputNodes(this.graph);
    }

    /**
     * Returns the WSDL ID.
     *
     * @param node
     *
     * @return the WSDL ID
     */
    public static String getWSDLID(Node node) {
        return node.getID() + WSDL_SUFFIX;
    }

    /**
     * @return The Jython script string
     */
    public String getJythonString() {
        return this.scriptString;
    }

    /**
     *
     * @param parameters
     * @return the jython script with prefilled argument
     */
    public String getJythonString(List<String> parameters) {
        int index = this.scriptString.indexOf("# Process command-line arguments.");
        StringBuilder builder = new StringBuilder(this.scriptString.substring(0, index));
        builder.append("sys.argv = [");
        for (String string : parameters) {
            builder.append("'");
            builder.append(string);
            builder.append("',");
        }
        builder.deleteCharAt(builder.length() - 1);
        builder.append("]");
        builder.append("\n");
        builder.append(this.scriptString.substring(index));
        return builder.toString();
    }

    /**
     * @param warnings
     *            returns the warning messages.
     * @return true if the workflow is valid; false otherwise.
     */
    public boolean validate(List<String> warnings) {
        // Empty
        if (this.graph.getNodes().size() == 0) {
            String message = "The workflow is empty.";
            warnings.add(message);
        }

        // Input ports need to be connected.
        Collection<Port> inputPorts = GraphUtil.getPorts(this.graph, Port.Kind.DATA_IN);
        for (Port inputPort : inputPorts) {
            Collection<Port> fromPorts = inputPort.getFromPorts();
            if (fromPorts.size() == 0) {
                Node node = inputPort.getNode();
                String message = node.getID() + " has an unconnected input " + inputPort.getName();
                warnings.add(message);
            }
        }

        // Input nodes need to be connected.
        for (InputNode inputNode : this.inputNodes) {
            if (inputNode.getPort().getToPorts().size() == 0) {
                String message = inputNode.getID() + " is not connected to any service.";
                warnings.add(message);
            }
        }

        // Cycle
        if (GraphUtil.containsCycle(this.graph)) {
            String message = "There is a cycle in the workflow, only acyclic workflows are supported";
            warnings.add(message);
        }

        // Constants are not supported.
        List<ConstantNode> constantNodes = GraphUtil.getNodes(this.graph, ConstantNode.class);
        if (constantNodes.size() > 0) {
            String message = "Constants are not supported for Jython scripts.";
            warnings.add(message);
        }

        // If/endif are not supported.
        List<IfNode> ifNodes = GraphUtil.getNodes(this.graph, IfNode.class);
        List<EndifNode> endifNodes = GraphUtil.getNodes(this.graph, EndifNode.class);
        if (ifNodes.size() > 0 || endifNodes.size() > 0) {
            String message = "If/endif are not supported for Jython scripts.";
            warnings.add(message);
        }

        if (warnings.size() > 0) {
            return false;
        } else {
            // No error.
            return true;
        }
    }

    /**
     * @throws GraphExceptionconcreteWSDL
     */
    public void create() throws GraphException {
        StringWriter stringWriter = new StringWriter();
        PrintWriter printWriter = new PrintWriter(stringWriter);

        writeHeader(printWriter);
        writeParameters(printWriter);
        writeWSDLLocations(printWriter);
        writeCommandLineArguments(printWriter);
        writeSetup(printWriter);
        writeInvocations(printWriter);
        writeOutputs(printWriter);
        writeWaitAll(printWriter);
        writeFooter(printWriter);

        printWriter.close();
        this.scriptString = stringWriter.toString();
    }

    /**
     * @param pw
     */
    private void writeCommandLineArguments(PrintWriter pw) {
        pw.println("def usage():");
        pw.println(TAB + "print '''");
        pw.println("Options: -help");
        pw.println(TAB + TAB + " -f properties_file");
        for (String argument : this.arguments) {
            pw.println(TAB + TAB + " -" + argument + " value");
        }
        pw.println("'''");
        pw.println(TAB + "sys.exit(0)");
        pw.println();

        pw.println("# Process command-line arguments.");
        pw.println("if sys.argv[0][0] != '-':");
        pw.println(TAB + "sys.argv = sys.argv[1:]");
        pw.println("while sys.argv:");
        pw.println(TAB + "if sys.argv[0] == '-f':");
        pw.println(TAB + TAB + "# Read parameters from a file.");
        pw.println(TAB + TAB + "propertyFilename = sys.argv[1]");
        pw.println(TAB + TAB + "inputStream = FileInputStream(propertyFilename)");
        pw.println(TAB + TAB + PROPERTIES_VARIABLE + ".load(inputStream)");

        for (String argument : this.arguments) {
            pw.println(TAB + "elif sys.argv[0] == '-" + argument + "':");
            pw.println(TAB + TAB + PROPERTIES_VARIABLE + ".put('" + argument + "', sys.argv[1])");
        }

        pw.println(TAB + "else:");
        pw.println(TAB + TAB + "usage()");
        pw.println(TAB + "sys.argv = sys.argv[2:]");
        pw.println();
    }

    /**
     * Writes import statements
     *
     * @param pw
     */
    private void writeHeader(PrintWriter pw) {
        pw.println("#");
        pw.println("# This script is automatically generated by " + XBayaConstants.APPLICATION_NAME + " "
                + XBayaVersion.VERSION + ".");
        pw.println("#");
        pw.println();
        pw.println("import sys, thread");
        pw.println("from java.lang import Throwable");
        pw.println("from java.util import Properties");
        pw.println("from java.io import FileInputStream");
        pw.println("from javax.xml.namespace import QName");
        pw.println("from " + GenericInvoker.class.getPackage().getName() + " import " + INVOKER_CLASS);
        pw.println("from " + NotificationSender.class.getPackage().getName() + " import " + NOTIFICATION_CLASS);
        pw.println();
    }

    /**
     * Handles parameters
     *
     * @param pw
     */
    private void writeParameters(PrintWriter pw) {
        pw.println(PROPERTIES_VARIABLE + " = Properties()");
        pw.println();
        pw.println("# Set up defaut parameter values.");

        writeSetProperty(BROKER_URL_VARIABLE, XBayaConstants.DEFAULT_BROKER_URL, pw);
        writeSetProperty(MESSAGE_BOX_URL_VARIABLE, this.configuration.getMessageBoxURL(), pw);
        writeSetProperty(TOPIC_VARIABLE, XBayaConstants.DEFAULT_TOPIC, pw);
        writeSetProperty(GFAC_VARIABLE, this.configuration.getGFacURL(), pw);

        for (InputNode paramNode : this.inputNodes) {
            writeParameter(paramNode, pw);
            this.notYetInvokedNodes.remove(paramNode);
        }

        pw.println();
    }

    /**
     * @param inputNode
     * @param pw
     */
    private void writeParameter(InputNode inputNode, PrintWriter pw) {
        String id = inputNode.getID();
        Object value = inputNode.getDefaultValue();
        String valueString = "";
        if (value instanceof String) {
            valueString = (String) value;
        }
        writeSetProperty(id, valueString, pw);
    }

    /**
     * @param pw
     */
    private void writeWSDLLocations(PrintWriter pw) {
        pw.println("# Set up default WSDL URLs.");
        for (WSNode node : GraphUtil.getWSNodes(this.graph)) {
            writeWSDLLocation(node, pw);
        }
        pw.println();

    }

    /**
     * @param node
     * @param pw
     */
    private void writeWSDLLocation(WSNode node, PrintWriter pw) {
        String defaultWsdlLocation = ""; // TODO
        writeSetProperty(getWSDLID(node), defaultWsdlLocation, pw);
    }

    private void writeSetProperty(String name, URI uri, PrintWriter pw) {
        writeSetProperty(name, StringUtil.toString(uri), pw);
    }

    private void writeSetProperty(String name, String value, PrintWriter pw) {
        if (value == null) {
            value = "";
        }
        pw.println(PROPERTIES_VARIABLE + ".setProperty(");
        pw.println(TAB + TAB + "'" + name + "',");
        pw.println(TAB + TAB + "'" + value + "')");

        // This will be used to read from command-line
        this.arguments.add(name);
    }

    /**
     * @param pw
     */
    private void writeSetup(PrintWriter pw) {
        // Initialize some variables.
        pw.println(GFAC_VARIABLE + " = " + PROPERTIES_VARIABLE + "." + GET_PROPERTY_METHOD + "('" + GFAC_VARIABLE
                + "')");
        pw.println(TOPIC_VARIABLE + " = " + PROPERTIES_VARIABLE + "." + GET_PROPERTY_METHOD + "('" + TOPIC_VARIABLE
                + "')");
        pw.println(BROKER_URL_VARIABLE + " = " + PROPERTIES_VARIABLE + "." + GET_PROPERTY_METHOD + "('"
                + BROKER_URL_VARIABLE + "')");
        pw.println(MESSAGE_BOX_URL_VARIABLE + " = " + PROPERTIES_VARIABLE + "." + GET_PROPERTY_METHOD + "('"
                + MESSAGE_BOX_URL_VARIABLE + "')");

        // Initialize a notification sender.
        pw.println(NOTIFICATION_VARIABLE + " = " + NOTIFICATION_CLASS + "(" + BROKER_URL_VARIABLE + ", "
                + TOPIC_VARIABLE + ")");

        // Send a START_WORKFLOW notification.
        pw.println(NOTIFICATION_VARIABLE + "." + WORKFLOW_STARTED_METHOD + "(");
        boolean first = true;
        for (InputNode inputNode : this.inputNodes) {
            String id = inputNode.getID();
            if (first) {
                first = false;
            } else {
                pw.println(",");
            }
            pw.print(TAB + id + "=" + PROPERTIES_VARIABLE + "." + GET_PROPERTY_METHOD + "('" + id + "')");
        }
        pw.println(")");

        pw.println();

        // The biggining of try
        pw.println("try:");
    }

    /**
     * @param pw
     * @throws GraphException
     */
    private void writeInvocations(PrintWriter pw) throws GraphException {
        Collection<Node> nextNodes = getNextNodes();
        while (nextNodes.size() > 0) {
            // If there are more than one nodes to invoke, they can run
            // concurrently using threads.
            boolean thread = (nextNodes.size() > 1);
            for (Node node : nextNodes) {
                if (node instanceof WSNode) {
                    WSNode wsNode = (WSNode) node;
                    writeInvocation(wsNode, thread, pw);

                } else {
                    // TODO conditions, loops might come here.
                }
                this.notYetInvokedNodes.remove(node);
            }

            nextNodes = getNextNodes();
        }
    }

    /**
     * @param node
     * @param thread
     * @param pw
     */
    private void writeInvocation(WSNode node, boolean thread, PrintWriter pw) {
        String id = node.getID();
        String wsdlID = getWSDLID(node);

        WSComponent component = node.getComponent();
        QName portTypeQName = component.getPortTypeQName();
        String operation = component.getOperationName();

        pw.println(TAB + "# Invoke " + id + ".");
        pw.println(TAB + id + QNAME_SUFFIX + " = QName('" + portTypeQName.getNamespaceURI() + "', '"
                + portTypeQName.getLocalPart() + "')");
        pw.println(TAB + wsdlID + " = " + PROPERTIES_VARIABLE + "." + GET_PROPERTY_METHOD + "('" + wsdlID + "')");
        pw.println(TAB + id + INVOKER_SUFFIX + " = " + INVOKER_CLASS + "(" + id + QNAME_SUFFIX + ", " + wsdlID + ", '"
                + id + "',");
        pw.println(TAB + TAB + MESSAGE_BOX_URL_VARIABLE + ", " + GFAC_VARIABLE + ", " + NOTIFICATION_VARIABLE + ")");

        pw.println(TAB + "def " + INVOKE_METHOD + id + "():");
        pw.println(TAB + TAB + id + INVOKER_SUFFIX + "." + SETUP_METHOD + "()");
        pw.println(TAB + TAB + id + INVOKER_SUFFIX + "." + SET_OPERATION_METHOD + "('" + operation + "')");

        // Ports
        for (Port port : node.getInputPorts()) {
            String portName = port.getName();
            String value;
            Node fromNode = port.getFromNode();
            if (fromNode instanceof InputNode) {
                value = PROPERTIES_VARIABLE + "." + GET_PROPERTY_METHOD + "('" + fromNode.getID() + "')";
            } else {
                Port fromPort = port.getFromPort();
                value = "" + fromNode.getID() + INVOKER_SUFFIX + "." + GET_OUTPUT_METHOD + "('" + fromPort.getName()
                        + "')";

                // This might try to remove a node that has been removed
                // already, but it's OK.
                this.executingNodes.remove(fromNode);
            }
            pw.println(TAB + TAB + portName + VALUE_SUFFIX + " = " + value);
            pw.println(TAB + TAB + id + INVOKER_SUFFIX + "." + SET_INPUT_METHOD + "('" + portName + "', " + portName
                    + VALUE_SUFFIX + ")");
        }

        pw.println(TAB + TAB + "print 'Invoking " + id + ".'");
        pw.println(TAB + TAB + id + INVOKER_SUFFIX + "." + INVOKE_METHOD + "()");

        if (thread) {
            pw.println(TAB + "thread.start_new_thread(" + INVOKE_METHOD + id + ", ())");
        } else {
            pw.println(TAB + INVOKE_METHOD + id + "()");
        }

        pw.println();

        this.executingNodes.add(node);
    }

    private void writeOutputs(PrintWriter pw) throws GraphException {
        for (OutputNode outputNode : this.outputNodes) {
            writeOutput(outputNode, pw);
        }
    }

    private void writeOutput(OutputNode node, PrintWriter pw) throws GraphException {
        String id = node.getID();
        Port port = node.getPort();

        Node fromNode = port.getFromNode();
        if (fromNode == null) {
            throw new GraphException("Output parameter has to be connected to some node.");
        }
        Port fromPort = port.getFromPort();
        if (fromNode instanceof InputNode) {
            // The OutputNode is directly connected to an InputNode.
            pw.println(TAB + id + VALUE_SUFFIX + " = " + PROPERTIES_VARIABLE + "." + GET_PROPERTY_METHOD + "('"
                    + fromNode.getID() + "')");
        } else {
            pw.println(TAB + "# Wait output " + id);
            pw.println(TAB + id + VALUE_SUFFIX + " = " + fromNode.getID() + INVOKER_SUFFIX + "." + GET_OUTPUT_METHOD
                    + "('" + fromPort.getName() + "')");
        }
        pw.println(TAB + "print '" + id + " = ', " + id + VALUE_SUFFIX);

        // This might try to remove a node that has been removed
        // already, but it's OK.
        this.executingNodes.remove(fromNode);

        pw.println();
    }

    /**
     * @param pw
     */
    private void writeWaitAll(PrintWriter pw) {
        pw.println(TAB + "# Wait all executing services.");
        for (Node node : this.executingNodes) {
            writeWait(node, pw);
        }
        pw.println();
    }

    /**
     * @param node
     * @param pw
     */
    private void writeWait(Node node, PrintWriter pw) {
        String id = node.getID();
        pw.println(TAB + "print 'Waiting " + id + " to be done.'");
        pw.println(TAB + id + INVOKER_SUFFIX + "." + WAIT_METHOD + "()");
    }

    /**
     * @param pw
     */
    private void writeFooter(PrintWriter pw) {
        // Send a COMPLETE_WORKFLOW notification.
        pw.println(TAB + NOTIFICATION_VARIABLE + "." + WORKFLOW_COMPLETED_METHOD + "(");
        boolean first = true;
        for (OutputNode node : this.outputNodes) {
            if (first) {
                first = false;
            } else {
                pw.println(",");
            }
            String id = node.getID();
            pw.print(TAB + TAB + id + "=" + id + VALUE_SUFFIX);
        }
        pw.println(")");

        pw.println(TAB + "print 'Everything is done successfully.'");

        pw.println();
        pw.println("except Throwable, e:");
        pw.println(TAB + "print 'Error: ', e");
        pw.println(TAB + NOTIFICATION_VARIABLE + "." + WORKFLOW_INCOMPLETED_METHOD + "(e)");
    }

    private Collection<Node> getNextNodes() throws GraphException {
        Collection<Node> nextNodes = new ArrayList<Node>();
        for (Node node : this.notYetInvokedNodes) {
            if (isNextNode(node)) {
                nextNodes.add(node);
            }
        }
        return nextNodes;
    }

    /**
     * Checks is a specified node can be executed next. A node can be executed if all the previous node are done or
     * there is no input ports.
     *
     * @param node
     *            the specified node
     * @return true if the specified node can be executed next; false otherwise
     * @throws GraphException
     */
    private boolean isNextNode(Node node) throws GraphException {
        if (node instanceof OutputNode) {
            return false;
        }
        for (Port port : node.getInputPorts()) {
            Collection<Node> fromNodes = port.getFromNodes();
            if (fromNodes.isEmpty()) {
                throw new GraphException("There is a port that is not connected to any.");
            } else {
                for (Node fromNode : fromNodes) {
                    if (this.notYetInvokedNodes.contains(fromNode)) {
                        // There is a node that should be executed before this
                        // node.
                        return false;
                    }
                }
            }
        }
        Port port = node.getControlInPort();
        if (port != null) {
            Collection<Node> fromNodes = port.getFromNodes();
            for (Node fromNode : fromNodes) {
                if (this.notYetInvokedNodes.contains(fromNode)) {
                    return false;
                }
            }
        }
        return true;
    }
}
TOP

Related Classes of org.apache.airavata.xbaya.jython.script.JythonScript

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.