org.ow2.proactive.scheduler.common.job.factories.Job2XMLTransformer.java Source code

Java tutorial

Introduction

Here is the source code for org.ow2.proactive.scheduler.common.job.factories.Job2XMLTransformer.java

Source

/*
 * ProActive Parallel Suite(TM):
 * The Open Source library for parallel and distributed
 * Workflows & Scheduling, Orchestration, Cloud Automation
 * and Big Data Analysis on Enterprise Grids & Clouds.
 *
 * Copyright (c) 2007 - 2017 ActiveEon
 * Contact: contact@activeeon.com
 *
 * This library is free software: you can redistribute it and/or
 * modify it under the terms of the GNU Affero General Public License
 * as published by the Free Software Foundation: version 3 of
 * the License.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Affero General Public License for more details.
 *
 * You should have received a copy of the GNU Affero General Public License
 * along with this program. If not, see <http://www.gnu.org/licenses/>.
 *
 * If needed, contact us to obtain a release under GPL Version 2 or 3
 * or a different license than the AGPL.
 */
package org.ow2.proactive.scheduler.common.job.factories;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.io.InputStream;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.concurrent.TimeUnit;

import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.transform.OutputKeys;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerException;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.stream.StreamResult;

import org.apache.commons.io.IOUtils;
import org.apache.log4j.Logger;
import org.objectweb.proactive.extensions.dataspaces.vfs.selector.FileSelector;
import org.ow2.proactive.scheduler.common.job.JobVariable;
import org.ow2.proactive.scheduler.common.job.TaskFlowJob;
import org.ow2.proactive.scheduler.common.task.ForkEnvironment;
import org.ow2.proactive.scheduler.common.task.JavaTask;
import org.ow2.proactive.scheduler.common.task.NativeTask;
import org.ow2.proactive.scheduler.common.task.ParallelEnvironment;
import org.ow2.proactive.scheduler.common.task.ScriptTask;
import org.ow2.proactive.scheduler.common.task.Task;
import org.ow2.proactive.scheduler.common.task.TaskVariable;
import org.ow2.proactive.scheduler.common.task.dataspaces.InputSelector;
import org.ow2.proactive.scheduler.common.task.dataspaces.OutputSelector;
import org.ow2.proactive.scheduler.common.task.flow.FlowActionType;
import org.ow2.proactive.scheduler.common.task.flow.FlowBlock;
import org.ow2.proactive.scheduler.common.task.flow.FlowScript;
import org.ow2.proactive.scheduler.core.properties.PASchedulerProperties;
import org.ow2.proactive.scripting.Script;
import org.ow2.proactive.scripting.SelectionScript;
import org.ow2.proactive.topology.descriptor.ArbitraryTopologyDescriptor;
import org.ow2.proactive.topology.descriptor.BestProximityDescriptor;
import org.ow2.proactive.topology.descriptor.DifferentHostsExclusiveDescriptor;
import org.ow2.proactive.topology.descriptor.MultipleHostsExclusiveDescriptor;
import org.ow2.proactive.topology.descriptor.SingleHostDescriptor;
import org.ow2.proactive.topology.descriptor.SingleHostExclusiveDescriptor;
import org.ow2.proactive.topology.descriptor.ThresholdProximityDescriptor;
import org.ow2.proactive.topology.descriptor.TopologyDescriptor;
import org.w3c.dom.CDATASection;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Text;

/**
 * Helper class to transform a job into its xml representation.
 * <p>
 * The xml produced conforms to the definition in {@link Schemas}.
 * <p>
 * The order of elements is sensitive.
 *
 * @author esalagea
 */
public class Job2XMLTransformer {

    public static Logger logger = Logger.getLogger(Job2XMLTransformer.class);

    public static final String XSD_LOCATION = "urn:proactive:jobdescriptor:dev ../../src/scheduler/src/org/ow2/proactive/scheduler/common/xml/schemas/jobdescriptor/dev/schedulerjob.xsd";

    public Job2XMLTransformer() {

    }

    /**
     * Creates the xml representation of the job in argument
     *
     * @throws TransformerException
     * @throws ParserConfigurationException
     */
    public InputStream jobToxml(TaskFlowJob job) throws TransformerException, ParserConfigurationException {
        DocumentBuilderFactory dbfac = DocumentBuilderFactory.newInstance();
        DocumentBuilder docBuilder = dbfac.newDocumentBuilder();
        Document doc = docBuilder.newDocument();
        doc.setXmlStandalone(true);

        // create the xml tree corresponding to this job
        Element rootJob = createRootJobElement(doc, job);
        doc.appendChild(rootJob);

        // set up a transformer
        TransformerFactory transfac = TransformerFactory.newInstance();
        Transformer trans = transfac.newTransformer();
        trans.setOutputProperty(OutputKeys.OMIT_XML_DECLARATION, "no");
        trans.setOutputProperty(OutputKeys.INDENT, "yes");
        // If the encoding property is set on the client JVM, use it (it has to match the server-side encoding),
        // otherwise use UTF-8
        if (PASchedulerProperties.FILE_ENCODING.isSet()) {
            trans.setOutputProperty(OutputKeys.ENCODING, PASchedulerProperties.FILE_ENCODING.getValueAsString());
        } else {
            trans.setOutputProperty(OutputKeys.ENCODING, "UTF-8");
        }
        trans.setOutputProperty("{http://xml.apache.org/xslt}indent-amount", "2");

        // write the xml
        ByteArrayOutputStream baos = new ByteArrayOutputStream();
        StreamResult result = new StreamResult(baos);
        DOMSource source = new DOMSource(doc);
        trans.transform(source, result);
        byte[] array = baos.toByteArray();
        return new ByteArrayInputStream(array);
    }

    /**
     * Creates the xml representation of the job in argument
     *
     * @throws TransformerException
     * @throws ParserConfigurationException
     */
    public String jobToxmlString(TaskFlowJob job)
            throws TransformerException, ParserConfigurationException, IOException {
        InputStream is = jobToxml(job);
        String answer = IOUtils.toString(is, "UTF-8");
        return answer;
    }

    /**
     * Serializes the given job as xml and writes it to a file.
     *
     * @param job
     *            TaskFlowJob to be serialized
     * @param f
     *            The file where the xml will be written
     * @throws ParserConfigurationException
     * @throws TransformerException
     * @throws IOException
     */
    public void job2xmlFile(TaskFlowJob job, File f)
            throws ParserConfigurationException, TransformerException, IOException {
        String xmlString = jobToxmlString(job);
        FileWriter fw = new FileWriter(f);
        fw.write(xmlString);
        fw.close();
    }

    /**
     * Creates the "job" element <define name="job">
     */
    private Element createRootJobElement(Document doc, TaskFlowJob job) {
        Element rootJob = doc.createElementNS(Schemas.SCHEMA_LATEST.getNamespace(), "job");

        // ********** attributes ***********
        rootJob.setAttributeNS("http://www.w3.org/2001/XMLSchema-instance", "xsi:schemaLocation", XSD_LOCATION);
        setAttribute(rootJob, XMLAttributes.JOB_PROJECT_NAME, job.getProjectName(), true);
        setAttribute(rootJob, XMLAttributes.JOB_PRIORITY, job.getPriority().toString());
        if (job.getOnTaskErrorProperty().isSet()) {
            setAttribute(rootJob, XMLAttributes.COMMON_ON_TASK_ERROR,
                    job.getOnTaskErrorProperty().getValue().toString(), true);
        }
        if (job.getMaxNumberOfExecutionProperty().isSet()) {
            setAttribute(rootJob, XMLAttributes.COMMON_MAX_NUMBER_OF_EXECUTION,
                    Integer.toString(job.getMaxNumberOfExecution()));
        }
        setAttribute(rootJob, XMLAttributes.COMMON_NAME, job.getName(), true);
        if (job.getRestartTaskOnErrorProperty().isSet()) {
            setAttribute(rootJob, XMLAttributes.COMMON_RESTART_TASK_ON_ERROR,
                    job.getRestartTaskOnError().toString());
        }

        // *** elements ***

        // <ref name="variables"/>
        if (job.getVariables() != null && !job.getVariables().isEmpty()) {
            Element variablesE = createJobVariablesElement(doc, job.getVariables());
            rootJob.appendChild(variablesE);
        }

        // <ref name="jobDescription"/>
        if (job.getDescription() != null) {
            Element descrNode = createElement(doc, XMLTags.COMMON_DESCRIPTION.getXMLName(), job.getDescription());
            rootJob.appendChild(descrNode);
        }

        // <ref name="genericInformation"/>
        if ((job.getGenericInformation() != null) && (job.getGenericInformation().size() > 0)) {
            Element genericInfo = createGenericInformation(doc, job.getGenericInformation());
            rootJob.appendChild(genericInfo);
        }

        // <ref name="inputSpace"/>
        if (job.getInputSpace() != null) {
            Element inputspace = createElement(doc, XMLTags.DS_INPUT_SPACE.getXMLName(), null,
                    new Attribute(XMLAttributes.DS_URL.getXMLName(), job.getInputSpace()));
            rootJob.appendChild(inputspace);
        }

        // <ref name="outputSpace"/>
        if (job.getOutputSpace() != null) {
            Element outputSpace = createElement(doc, XMLTags.DS_OUTPUT_SPACE.getXMLName(), null,
                    new Attribute(XMLAttributes.DS_URL.getXMLName(), job.getOutputSpace()));
            rootJob.appendChild(outputSpace);
        }

        // <ref name="globalSpace"/>
        if (job.getGlobalSpace() != null) {
            Element globalSpace = createElement(doc, XMLTags.DS_GLOBAL_SPACE.getXMLName(), null,
                    new Attribute(XMLAttributes.DS_URL.getXMLName(), job.getGlobalSpace()));
            rootJob.appendChild(globalSpace);
        }

        // <ref name="userSpace"/>
        if (job.getUserSpace() != null) {
            Element userSpace = createElement(doc, XMLTags.DS_USER_SPACE.getXMLName(), null,
                    new Attribute(XMLAttributes.DS_URL.getXMLName(), job.getUserSpace()));
            rootJob.appendChild(userSpace);
        }

        // <ref name="taskFlow"/>
        Element taskFlow = createTaskFlowElement(doc, job);
        rootJob.appendChild(taskFlow);

        return rootJob;
    }

    /**
     * Creates an element and set the value of its attributes
     *
     */
    private Element createElement(Document doc, String tagName, String elementText, Attribute... attribs) {
        Element el = doc.createElementNS(Schemas.SCHEMA_LATEST.getNamespace(), tagName);
        for (Attribute a : attribs) {
            el.setAttribute(a.getName(), a.getValue());
        }
        if (elementText != null) {
            Text text = doc.createTextNode(elementText);
            el.appendChild(text);
        }
        return el;
    }

    /*
     * Creates the job variables element
     */
    private Element createJobVariablesElement(Document doc, Map<String, JobVariable> jobVariables) {
        if (jobVariables == null) {
            return null;
        }
        Element variablesE = doc.createElementNS(Schemas.SCHEMA_LATEST.getNamespace(),
                XMLTags.VARIABLES.getXMLName());
        for (String name : jobVariables.keySet()) {
            Element variableE = createElement(doc, XMLTags.VARIABLE.getXMLName(), null,
                    new Attribute(XMLAttributes.VARIABLE_NAME.getXMLName(), name),
                    new Attribute(XMLAttributes.VARIABLE_VALUE.getXMLName(), jobVariables.get(name).getValue()),
                    new Attribute(XMLAttributes.VARIABLE_MODEL.getXMLName(), jobVariables.get(name).getModel()));
            variablesE.appendChild(variableE);
        }
        return variablesE;
    }

    /**
     * Creates the task variables element
     */
    private Element createTaskVariablesElement(Document doc, Map<String, TaskVariable> variables) {
        if (variables == null) {
            return null;
        }
        Element variablesE = doc.createElementNS(Schemas.SCHEMA_LATEST.getNamespace(),
                XMLTags.VARIABLES.getXMLName());
        for (TaskVariable variable : variables.values()) {
            Element variableE = createElement(doc, XMLTags.VARIABLE.getXMLName(), null,
                    new Attribute(XMLAttributes.VARIABLE_NAME.getXMLName(), variable.getName()),
                    new Attribute(XMLAttributes.VARIABLE_VALUE.getXMLName(), variable.getValue()),
                    new Attribute(XMLAttributes.VARIABLE_MODEL.getXMLName(), variable.getModel()),
                    new Attribute(XMLAttributes.VARIABLE_JOB_INHERITED.getXMLName(),
                            String.valueOf(variable.isJobInherited())));
            variablesE.appendChild(variableE);
        }
        return variablesE;
    }

    /**
     * Creates the generic information element corresponding to <define
     * name="genericInformation">
     *
     */
    private Element createGenericInformation(Document doc, Map<String, String> info) {
        if (info == null)
            return null;

        Element el = doc.createElementNS(Schemas.SCHEMA_LATEST.getNamespace(),
                XMLTags.COMMON_GENERIC_INFORMATION.getXMLName());

        // <oneOrMore>
        // <ref name="info"/>
        // </oneOrMore>
        for (String name : info.keySet()) {
            Element infoElement = createElement(doc, XMLTags.COMMON_INFO.getXMLName(), null,
                    new Attribute(XMLAttributes.COMMON_NAME.getXMLName(), name),
                    new Attribute(XMLAttributes.COMMON_VALUE.getXMLName(), info.get(name)));
            el.appendChild(infoElement);
        }
        return el;
    }

    /**
     * Sets the value of the given attribute to lowerCase, if the value is not
     * null, otherwise it doesn't do anything
     *
     * @param e
     *            element to set the attribute value for
     */
    private void setAttribute(Element e, XMLAttributes attrib, String attribVal) {
        if (attribVal == null)
            return;
        e.setAttribute(attrib.getXMLName(), attribVal.toLowerCase());
    }

    /**
     * Sets the value of the given attribute.
     *
     * @param caseSensitive
     *            if true, the attribVal case is kept, if false, the value is
     *            set as lowercase
     */
    private void setAttribute(Element e, XMLAttributes attrib, String attribVal, boolean caseSensitive) {
        if (attribVal == null)
            return;

        if (caseSensitive) {
            e.setAttribute(attrib.getXMLName(), attribVal);
        } else
            setAttribute(e, attrib, attribVal);
    }

    /**
     * Creates the taskflow element, corresponding to <define name="taskFlow">
     *
     */
    private Element createTaskFlowElement(Document doc, TaskFlowJob job) {
        Element taskFlowElement = doc.createElementNS(Schemas.SCHEMA_LATEST.getNamespace(),
                XMLTags.TASK_FLOW.getXMLName());

        ArrayList<Task> tasks = job.getTasks();

        // <oneOrMore>
        // <ref name="task"/>
        // </oneOrMore>
        for (Task task : tasks) {
            Element taskE = createTaskElement(doc, task);
            taskFlowElement.appendChild(taskE);
        }
        return taskFlowElement;
    }

    /**
     * Creates the task element, corressponding to <define name="task">
     *
     */
    private Element createTaskElement(Document doc, Task task) {
        Element taskE = doc.createElementNS(Schemas.SCHEMA_LATEST.getNamespace(), XMLTags.TASK.getXMLName());

        // **** attributes *****
        // **** common attributes ***

        if (task.getOnTaskErrorProperty().isSet()) {
            setAttribute(taskE, XMLAttributes.COMMON_ON_TASK_ERROR,
                    task.getOnTaskErrorProperty().getValue().toString(), true);
        }
        if (task.getMaxNumberOfExecutionProperty().isSet()) {
            setAttribute(taskE, XMLAttributes.COMMON_MAX_NUMBER_OF_EXECUTION,
                    Integer.toString(task.getMaxNumberOfExecution()));
        }
        setAttribute(taskE, XMLAttributes.COMMON_NAME, task.getName(), true);
        if (task.getRestartTaskOnErrorProperty().isSet()) {
            setAttribute(taskE, XMLAttributes.COMMON_RESTART_TASK_ON_ERROR,
                    task.getRestartTaskOnError().toString());
        }

        // *** task attributes ***
        if (task.getWallTime() != 0) {
            setAttribute(taskE, XMLAttributes.TASK_WALLTIME, formatDate(task.getWallTime()));
        }

        if (task.isRunAsMe()) {
            setAttribute(taskE, XMLAttributes.TASK_RUN_AS_ME, "true");
        }

        if (task.isPreciousResult()) {
            setAttribute(taskE, XMLAttributes.TASK_PRECIOUS_RESULT, "true");
        }

        if (task.isPreciousLogs()) {
            setAttribute(taskE, XMLAttributes.TASK_PRECIOUS_LOGS, "true");
        }

        // *** elements ****

        // <ref name="variables"/>
        if (task.getVariables() != null && !task.getVariables().isEmpty()) {
            Element variablesE = createTaskVariablesElement(doc, task.getVariables());
            taskE.appendChild(variablesE);
        }

        // <ref name="taskDescription"/>
        if (task.getDescription() != null) {
            Element descrNode = createElement(doc, XMLTags.COMMON_DESCRIPTION.getXMLName(), task.getDescription());
            taskE.appendChild(descrNode);
        }

        // <ref name="genericInformation"/>
        if ((task.getGenericInformation() != null) && (task.getGenericInformation().size() > 0)) {
            Element genericInfoE = createGenericInformation(doc, task.getGenericInformation());
            taskE.appendChild(genericInfoE);
        }

        // <ref name="depends"/>
        List<Task> dependencies = task.getDependencesList();
        if ((dependencies != null) && (dependencies.size() > 0)) {
            Element dependsE = doc.createElementNS(Schemas.SCHEMA_LATEST.getNamespace(),
                    XMLTags.TASK_DEPENDENCES.getXMLName());
            for (Task dep : dependencies) {
                Element dependsTask = doc.createElementNS(Schemas.SCHEMA_LATEST.getNamespace(),
                        XMLTags.TASK_DEPENDENCES_TASK.getXMLName());
                setAttribute(dependsTask, XMLAttributes.TASK_DEPENDS_REF, dep.getName(), true);
                dependsE.appendChild(dependsTask);
            }
            taskE.appendChild(dependsE);
        } // if has dependencies

        // <ref name="inputFiles"/>
        List<InputSelector> inputFiles = task.getInputFilesList();
        if (inputFiles != null) {
            Element inputFilesE = doc.createElementNS(Schemas.SCHEMA_LATEST.getNamespace(),
                    XMLTags.DS_INPUT_FILES.getXMLName());
            for (InputSelector inputSelector : inputFiles) {
                FileSelector fs = inputSelector.getInputFiles();
                Element filesE = doc.createElementNS(Schemas.SCHEMA_LATEST.getNamespace(),
                        XMLTags.DS_FILES.getXMLName());
                // the xml only supports one value for the includes/excludes
                // pattern
                if (!fs.getIncludes().isEmpty())
                    setAttribute(filesE, XMLAttributes.DS_INCLUDES, fs.getIncludes().iterator().next(), true);
                if (!fs.getExcludes().isEmpty())
                    setAttribute(filesE, XMLAttributes.DS_EXCLUDES, fs.getExcludes().iterator().next(), true);
                if (inputSelector.getMode() != null) {
                    setAttribute(filesE, XMLAttributes.DS_ACCESS_MODE, inputSelector.getMode().toString(), true);
                }
                inputFilesE.appendChild(filesE);
            }
            taskE.appendChild(inputFilesE);
        }

        // <ref name="parallel"/>
        Element parallelEnvE = createParallelEnvironment(doc, task);
        if (parallelEnvE != null)
            taskE.appendChild(parallelEnvE);

        // <ref name="selection"/>
        List<SelectionScript> selectionScripts = task.getSelectionScripts();
        if (selectionScripts != null && selectionScripts.size() > 0) {
            Element selectionE = doc.createElementNS(Schemas.SCHEMA_LATEST.getNamespace(),
                    XMLTags.SCRIPT_SELECTION.getXMLName());
            for (SelectionScript selectionScript : selectionScripts) {
                Element scriptE = createScriptElement(doc, selectionScript);
                selectionE.appendChild(scriptE);
            }
            taskE.appendChild(selectionE);
        }

        // <ref name="forkEnvironment"/>
        if (task.getForkEnvironment() != null) {
            Element forkEnvE = createForkEnvironmentElement(doc, task.getForkEnvironment());
            taskE.appendChild(forkEnvE);
        }

        // <ref name="pre"/>
        Script preScript = task.getPreScript();
        if (preScript != null) {
            Element preE = doc.createElementNS(Schemas.SCHEMA_LATEST.getNamespace(),
                    XMLTags.SCRIPT_PRE.getXMLName());
            Element scriptE = createScriptElement(doc, preScript);
            preE.appendChild(scriptE);
            taskE.appendChild(preE);
        }

        // <ref name="executable"/>
        Element executableE = null;
        if (task instanceof JavaTask) {
            executableE = createJavaExecutableElement(doc, (JavaTask) task);
        } else if (task instanceof NativeTask) {
            executableE = createNativeExecutableElement(doc, (NativeTask) task);
        } else if (task instanceof ScriptTask) {
            executableE = createScriptExecutableElement(doc, (ScriptTask) task);
        }
        taskE.appendChild(executableE);

        // <ref name="flow"/>
        Element controlFlowE = createFlowControlElement(doc, task);
        if (controlFlowE != null)
            taskE.appendChild(controlFlowE);

        // <ref name="post"/>
        Script postScript = task.getPostScript();
        if (postScript != null) {
            Element postE = doc.createElementNS(Schemas.SCHEMA_LATEST.getNamespace(),
                    XMLTags.SCRIPT_POST.getXMLName());
            Element scriptE = createScriptElement(doc, postScript);
            postE.appendChild(scriptE);
            taskE.appendChild(postE);
        }

        // <ref name="cleaning"/>
        Script cleanScript = task.getCleaningScript();
        if (cleanScript != null) {
            Element cleanE = doc.createElementNS(Schemas.SCHEMA_LATEST.getNamespace(),
                    XMLTags.SCRIPT_CLEANING.getXMLName());
            Element scriptE = createScriptElement(doc, cleanScript);
            cleanE.appendChild(scriptE);
            taskE.appendChild(cleanE);
        }

        // <ref name="outputFiles"/>
        List<OutputSelector> outputFiles = task.getOutputFilesList();
        if (outputFiles != null) {
            Element outputFilesE = doc.createElementNS(Schemas.SCHEMA_LATEST.getNamespace(),
                    XMLTags.DS_OUTPUT_FILES.getXMLName());
            for (OutputSelector outputSelector : outputFiles) {
                FileSelector fs = outputSelector.getOutputFiles();
                Element filesE = doc.createElementNS(Schemas.SCHEMA_LATEST.getNamespace(),
                        XMLTags.DS_FILES.getXMLName());
                // the xml only supports one value for the includes/excludes
                // pattern
                if (!fs.getIncludes().isEmpty())
                    setAttribute(filesE, XMLAttributes.DS_INCLUDES, fs.getIncludes().iterator().next(), true);
                if (!fs.getExcludes().isEmpty())
                    setAttribute(filesE, XMLAttributes.DS_EXCLUDES, fs.getExcludes().iterator().next(), true);
                if (outputSelector.getMode() != null) {
                    setAttribute(filesE, XMLAttributes.DS_ACCESS_MODE, outputSelector.getMode().toString(), true);
                }
                outputFilesE.appendChild(filesE);
            }
            taskE.appendChild(outputFilesE);
        }
        return taskE;
    }

    /**
     * Creates the parallel environment element for the given task. Corresponds
     * to <define name="parallel">
     *
     * @return the {@link XMLTags#PARALLEL_ENV} element if the task has a
     *         parallel environment, null otherwise
     */
    private Element createParallelEnvironment(Document doc, Task task) {
        ParallelEnvironment penv = task.getParallelEnvironment();
        if (penv == null)
            return null;

        Element parallelEnvE = doc.createElementNS(Schemas.SCHEMA_LATEST.getNamespace(),
                XMLTags.PARALLEL_ENV.getXMLName());
        setAttribute(parallelEnvE, XMLAttributes.TASK_NB_NODES, Integer.toString(penv.getNodesNumber()));

        // <ref name="topology"/>
        TopologyDescriptor topologyDescr = penv.getTopologyDescriptor();
        if (topologyDescr != null) {

            // <choice>
            // <ref name="arbitrary"/>
            // <ref name="bestProximity"/>
            // <ref name="thresholdProximity"/>
            // <ref name="singleHost"/>
            // <ref name="singleHostExclusive"/>
            // <ref name="multipleHostsExclusive"/>
            // <ref name="differentHostsExclusive"/>
            // </choice>

            Element topologyE = doc.createElementNS(Schemas.SCHEMA_LATEST.getNamespace(),
                    XMLTags.TOPOLOGY.getXMLName());
            Element topologyDescrE = null;

            if (topologyDescr instanceof ArbitraryTopologyDescriptor) {
                topologyDescrE = doc.createElementNS(Schemas.SCHEMA_LATEST.getNamespace(),
                        XMLTags.TOPOLOGY_ARBITRARY.getXMLName());
            } else if (topologyDescr instanceof ThresholdProximityDescriptor) {
                topologyDescrE = doc.createElementNS(Schemas.SCHEMA_LATEST.getNamespace(),
                        XMLTags.TOPOLOGY_THRESHOLD_PROXIMITY.getXMLName());
                long threshold = ((ThresholdProximityDescriptor) topologyDescr).getThreshold();
                topologyDescrE.setAttribute(XMLAttributes.TOPOLOGY_THRESHOLD.getXMLName(),
                        Long.toString(threshold));
            } else if (topologyDescr instanceof BestProximityDescriptor) {
                topologyDescrE = doc.createElementNS(Schemas.SCHEMA_LATEST.getNamespace(),
                        XMLTags.TOPOLOGY_BEST_PROXIMITY.getXMLName());

            } else if (topologyDescr instanceof SingleHostExclusiveDescriptor) {
                topologyDescrE = doc.createElementNS(Schemas.SCHEMA_LATEST.getNamespace(),
                        XMLTags.TOPOLOGY_SINGLE_HOST_EXCLUSIVE.getXMLName());
            } else if (topologyDescr instanceof SingleHostDescriptor) {
                topologyDescrE = doc.createElementNS(Schemas.SCHEMA_LATEST.getNamespace(),
                        XMLTags.TOPOLOGY_SINGLE_HOST.getXMLName());
            } else if (topologyDescr instanceof MultipleHostsExclusiveDescriptor) {
                topologyDescrE = doc.createElementNS(Schemas.SCHEMA_LATEST.getNamespace(),
                        XMLTags.TOPOLOGY_MULTIPLE_HOSTS_EXCLUSIVE.getXMLName());
            }
            if (topologyDescr instanceof DifferentHostsExclusiveDescriptor) {
                topologyDescrE = doc.createElementNS(Schemas.SCHEMA_LATEST.getNamespace(),
                        XMLTags.TOPOLOGY_DIFFERENT_HOSTS_EXCLUSIVE.getXMLName());
            }

            if (topologyDescrE != null) {
                topologyE.appendChild(topologyDescrE);
            }

            parallelEnvE.appendChild(topologyE);
        }
        return parallelEnvE;
    }

    /**
     * Creates a flow control element for the given task <element
     * name="controlFlow">
     *
     * @return the xml Element corresponding to the flow control, if the task
     *         contains a flow control, null otherwise
     */
    private Element createFlowControlElement(Document doc, Task task) {
        Element controlFlowE = null;

        // <ref name="block"/>
        if (task.getFlowBlock() != FlowBlock.NONE) {
            controlFlowE = doc.createElementNS(Schemas.SCHEMA_LATEST.getNamespace(), XMLTags.FLOW.getXMLName());
            setAttribute(controlFlowE, XMLAttributes.FLOW_BLOCK, task.getFlowBlock().toString());
        }

        FlowScript flowScript = task.getFlowScript();
        if (flowScript != null) {

            Element flowActionE = null;

            // flowActionE can be if, loop, replicate or null.
            // if not null, it contains a script element

            // <ref name="actionIf"/>
            // <ref name="actionReplicate"/>
            // <ref name="actionLoop"/>
            // </choice>

            // *** if ***
            // <element name="if">
            if (flowScript.getActionType().equals(FlowActionType.IF.toString())) {
                flowActionE = doc.createElementNS(Schemas.SCHEMA_LATEST.getNamespace(),
                        XMLTags.FLOW_IF.getXMLName());
                setAttribute(flowActionE, XMLAttributes.FLOW_TARGET, flowScript.getActionTarget(), true);
                setAttribute(flowActionE, XMLAttributes.FLOW_ELSE, flowScript.getActionTargetElse(), true);
                setAttribute(flowActionE, XMLAttributes.FLOW_CONTINUATION, flowScript.getActionContinuation(),
                        true);
            }

            // *** loop ***
            // <element name="loop">
            if (flowScript.getActionType().equals(FlowActionType.LOOP.toString())) {
                flowActionE = doc.createElementNS(Schemas.SCHEMA_LATEST.getNamespace(),
                        XMLTags.FLOW_LOOP.getXMLName());
                setAttribute(flowActionE, XMLAttributes.FLOW_TARGET, flowScript.getActionTarget(), true);
            }

            // *** replicate ***
            // <element name="replicate">
            if (flowScript.getActionType().equals(FlowActionType.REPLICATE.toString())) {
                flowActionE = doc.createElementNS(Schemas.SCHEMA_LATEST.getNamespace(),
                        XMLTags.FLOW_REPLICATE.getXMLName());
            }

            if (flowActionE != null) {
                if (controlFlowE == null) {
                    controlFlowE = doc.createElementNS(Schemas.SCHEMA_LATEST.getNamespace(),
                            XMLTags.FLOW.getXMLName());
                }
                Element scriptE = createScriptElement(doc, flowScript);
                flowActionE.appendChild(scriptE);
                controlFlowE.appendChild(flowActionE);
            }
        } // flowScript !=null

        return controlFlowE;

    }

    /**
     * Corresponds to <element name="script">
     *
     * The schema allows the specification of a script either by writing the
     * script code either by providing a file with arguments. Both will result
     * in the same {@link org.ow2.proactive.scripting.Script} object. In the
     * current translation we will always translate a Script object by inlining
     * the script code using a "codeScript"element (first option).
     *
     * The xml specification does not allow addding arguments to a script
     * defined by its code. Therefore, when we translate the script object to
     * xml, if we encounter arguments, we will insert their value directly in
     * the script's code by inserting a line like:
     * <p/>
     * var args = ["argument_1",...,"argument_n"];
     *
     */
    private Element createScriptElement(Document doc, Script script) {
        Element scriptElement = doc.createElementNS(Schemas.SCHEMA_LATEST.getNamespace(),
                XMLTags.SCRIPT_SCRIPT.getXMLName());
        Element codeE = doc.createElementNS(Schemas.SCHEMA_LATEST.getNamespace(), XMLTags.SCRIPT_CODE.getXMLName());
        setAttribute(codeE, XMLAttributes.LANGUAGE, script.getEngineName(), true);
        String scriptText = script.getScript();
        Serializable[] params = script.getParameters();
        if (params != null) {

            scriptText = inlineScriptParametersInText(scriptText, params);
        }

        CDATASection scriptTextCDATA = doc.createCDATASection(scriptText);
        codeE.appendChild(scriptTextCDATA);

        scriptElement.appendChild(codeE);
        return scriptElement;
    }

    /**
     * Inserts a line, at the beginning of the script code with the text: var
     * args = ["argument_1",...,"argument_n"];
     *
     * where "argument_1",...,"argument_n" are the elements of the input array
     * "params"
     *
     */
    public static String inlineScriptParametersInText(String scriptText, Serializable[] params) {
        String paramsLine = "var args=[";
        for (Serializable param : params) {
            paramsLine += "\"" + param + "\",";
        }
        paramsLine = paramsLine.substring(0, paramsLine.length() - 1) + "];";

        return paramsLine + "\n" + scriptText;
    }

    /**
     * Corresponds to <element name="javaExecutable">
     *
     */
    private Element createJavaExecutableElement(Document doc, JavaTask t) {
        Element executableE = doc.createElementNS(Schemas.SCHEMA_LATEST.getNamespace(),
                XMLTags.JAVA_EXECUTABLE.getXMLName());
        setAttribute(executableE, XMLAttributes.TASK_CLASS_NAME, t.getExecutableClassName(), true);

        // <ref name="javaParameters"/>
        try {
            Map<String, Serializable> args = t.getArguments();
            if ((args != null) && (args.size() > 0)) {
                // <element name="parameter">
                Element paramsE = doc.createElementNS(Schemas.SCHEMA_LATEST.getNamespace(),
                        XMLTags.TASK_PARAMETERS.getXMLName());
                for (String name : args.keySet()) {
                    Serializable val = args.get(name);
                    Element paramE = doc.createElementNS(Schemas.SCHEMA_LATEST.getNamespace(),
                            XMLTags.TASK_PARAMETER.getXMLName());
                    setAttribute(paramE, XMLAttributes.COMMON_NAME, name, true);
                    setAttribute(paramE, XMLAttributes.COMMON_VALUE, val.toString(), true);
                    paramsE.appendChild(paramE);
                }
                executableE.appendChild(paramsE);
            }
        } catch (Exception e) {
            logger.error("Could not add arguments for Java Executable element of task " + t.getName(), e);
        }
        return executableE;
    }

    /**
     * Corresponds to <element name="forkEnvironment">
     *
     */
    private Element createForkEnvironmentElement(Document doc, ForkEnvironment fe) {
        Element forkEnvE = doc.createElementNS(Schemas.SCHEMA_LATEST.getNamespace(),
                XMLTags.FORK_ENVIRONMENT.getXMLName());
        // attributes
        setAttribute(forkEnvE, XMLAttributes.TASK_WORKDING_DIR, fe.getWorkingDir(), true);
        setAttribute(forkEnvE, XMLAttributes.FORK_JAVA_HOME, fe.getJavaHome(), true);

        // <ref name="sysProps"/>
        if ((fe.getSystemEnvironment() != null) && (fe.getSystemEnvironment().keySet().size() > 0)) {
            // <element name="SystemEnvironment">
            Element sysEnvE = doc.createElementNS(Schemas.SCHEMA_LATEST.getNamespace(),
                    XMLTags.FORK_SYSTEM_PROPERTIES.getXMLName());
            if (fe.getSystemEnvironment() != null) {

                // <oneOrMore>
                // <ref name="sysProp"/>
                // </oneOrMore>
                for (Map.Entry<String, String> entry : fe.getSystemEnvironment().entrySet()) {
                    Element variableE = doc.createElementNS(Schemas.SCHEMA_LATEST.getNamespace(),
                            XMLTags.VARIABLE.getXMLName());
                    setAttribute(variableE, XMLAttributes.COMMON_NAME, entry.getKey());
                    setAttribute(variableE, XMLAttributes.COMMON_VALUE, entry.getValue());

                    sysEnvE.appendChild(variableE);
                }
            }
            forkEnvE.appendChild(sysEnvE);
        }

        // <ref name="jvmArgs"/>
        List<String> args = fe.getJVMArguments();
        if (args != null && (args.size() > 0)) {
            Element jvmArgsE = doc.createElementNS(Schemas.SCHEMA_LATEST.getNamespace(),
                    XMLTags.FORK_JVM_ARGS.getXMLName());
            for (String arg : args) {
                Element argE = doc.createElementNS(Schemas.SCHEMA_LATEST.getNamespace(),
                        XMLTags.FORK_JVM_ARG.getXMLName());
                setAttribute(argE, XMLAttributes.COMMON_VALUE, arg, true);
                jvmArgsE.appendChild(argE);
            }
            forkEnvE.appendChild(jvmArgsE);
        }

        // <ref name="additionalClasspath"/>
        List<String> additionalCP = fe.getAdditionalClasspath();
        if ((additionalCP != null) && (additionalCP.size() > 0)) {
            Element additionalCPE = doc.createElementNS(Schemas.SCHEMA_LATEST.getNamespace(),
                    XMLTags.FORK_ADDITIONAL_CLASSPATH.getXMLName());
            for (String pathElement : additionalCP) {
                Element pathE = doc.createElementNS(Schemas.SCHEMA_LATEST.getNamespace(),
                        XMLTags.FORK_PATH_ELEMENT.getXMLName());
                setAttribute(pathE, XMLAttributes.PATH, pathElement, true);
                additionalCPE.appendChild(pathE);
            }

            forkEnvE.appendChild(additionalCPE);
        }

        // <ref name="envScript"/>
        if (fe.getEnvScript() != null) {
            Element envScriptE = doc.createElementNS(Schemas.SCHEMA_LATEST.getNamespace(),
                    XMLTags.SCRIPT_ENV.getXMLName());
            Element scriptElement = createScriptElement(doc, fe.getEnvScript());
            envScriptE.appendChild(scriptElement);
            forkEnvE.appendChild(envScriptE);
        }
        return forkEnvE;
    }

    /**
     * Corresponds to <element name="nativeExecutable">
     */
    private Element createNativeExecutableElement(Document doc, NativeTask t) {
        Element nativeExecE = doc.createElementNS(Schemas.SCHEMA_LATEST.getNamespace(),
                XMLTags.NATIVE_EXECUTABLE.getXMLName());

        // <choice>
        // <ref name="staticCommand"/>
        // <ref name="dynamicCommand"/>
        // </choice>
        String[] cmd = t.getCommandLine();
        if (cmd != null && cmd.length > 0) {
            // <element name="staticCommand">
            Element staticCmdE = doc.createElementNS(Schemas.SCHEMA_LATEST.getNamespace(),
                    XMLTags.NATIVE_TASK_STATIC_COMMAND.getXMLName());
            setAttribute(staticCmdE, XMLAttributes.TASK_COMMAND_VALUE, cmd[0], true);

            // <ref name="commandArguments"/>
            if (cmd.length > 1) {
                // <element name="arguments">
                Element argsE = doc.createElementNS(Schemas.SCHEMA_LATEST.getNamespace(),
                        XMLTags.NATIVE_TASK_ARGUMENTS.getXMLName());
                for (int i = 1; i < cmd.length; i++) {
                    // <element name="argument">
                    Element argE = doc.createElementNS(Schemas.SCHEMA_LATEST.getNamespace(),
                            XMLTags.NATIVE_TASK_ARGUMENT.getXMLName());
                    setAttribute(argE, XMLAttributes.COMMON_VALUE, cmd[i], true);
                    argsE.appendChild(argE);
                }
                staticCmdE.appendChild(argsE);
            }
            nativeExecE.appendChild(staticCmdE);
        } else {
            logger.error("The task " + t.getName() + " does not define a command");
        }

        return nativeExecE;
    }

    private Element createScriptExecutableElement(Document doc, ScriptTask t) {
        Element scriptExecE = doc.createElementNS(Schemas.SCHEMA_LATEST.getNamespace(),
                XMLTags.SCRIPT_EXECUTABLE.getXMLName());
        Element scriptE = createScriptElement(doc, t.getScript());
        scriptExecE.appendChild(scriptE);
        return scriptExecE;
    }

    private static String formatDate(long millis) {
        long hours = TimeUnit.MILLISECONDS.toHours(millis);
        millis -= TimeUnit.HOURS.toMillis(hours);
        long minutes = TimeUnit.MILLISECONDS.toMinutes(millis);
        millis -= TimeUnit.MINUTES.toMillis(minutes);
        long seconds = TimeUnit.MILLISECONDS.toSeconds(millis);
        String formatted = String.format("%02d:%02d:%02d", hours, minutes, seconds);
        // replace heading 00: as it's not accepted by the schema validation
        return (formatted.replaceFirst("^(00:)+", ""));
    }
}

class Attribute {
    private String name;

    private String value;

    public Attribute(String name, String value) {
        this.name = name;
        this.value = value;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getValue() {
        return value;
    }

    public void setValue(String value) {
        this.value = value;
    }

}