sf.net.experimaestro.connectors.OARLauncher.java Source code

Java tutorial

Introduction

Here is the source code for sf.net.experimaestro.connectors.OARLauncher.java

Source

/*
 * This file is part of experimaestro.
 * Copyright (c) 2012 B. Piwowarski <benjamin@bpiwowar.net>
 *
 * experimaestro is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * experimaestro 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 General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with experimaestro.  If not, see <http://www.gnu.org/licenses/>.
 */

package sf.net.experimaestro.connectors;

import com.sleepycat.persist.model.Persistent;
import org.apache.commons.vfs2.FileObject;
import org.apache.commons.vfs2.FileSystemException;
import org.w3c.dom.Document;
import sf.net.experimaestro.exceptions.LaunchException;
import sf.net.experimaestro.exceptions.XPMRuntimeException;
import sf.net.experimaestro.scheduler.Job;
import sf.net.experimaestro.utils.Output;
import sf.net.experimaestro.utils.log.Logger;

import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.xpath.XPath;
import javax.xml.xpath.XPathConstants;
import javax.xml.xpath.XPathExpressionException;
import javax.xml.xpath.XPathFactory;
import java.io.*;

/**
 * A command line launcher with OAR
 *
 * @author B. Piwowarski <benjamin@bpiwowar.net>
 */
@Persistent
public class OARLauncher implements Launcher {
    /**
     * Prefix for the PID of the job
     */
    protected static final String OARJOBID_PREFIX = "OAR_JOB_ID=";
    static private final Logger LOGGER = Logger.getLogger();
    /**
     * oarsub command
     */
    private String oarCommand = "oarsub";

    /**
     * Construction from a connector
     */
    public OARLauncher() {
        super();
    }

    /**
     * Helper method that executes a command that produces XML, and returns a DOM document from it
     */
    static private Document exec(SingleHostConnector connector, String command) throws Exception {
        DocumentBuilderFactory dbFactory = DocumentBuilderFactory.newInstance();
        DocumentBuilder dBuilder = dbFactory.newDocumentBuilder();

        AbstractProcessBuilder builder = connector.processBuilder();
        builder.command(command);
        builder.detach(false);
        return dBuilder.parse(builder.start().getInputStream());
    }

    /**
     * Evaluate an XPath to a string
     */
    static private String evaluateXPathToString(String expression, Document document) {
        String value;
        XPath xpath = XPathFactory.newInstance().newXPath();
        try {
            value = (String) xpath.evaluate(expression, document, XPathConstants.STRING);
        } catch (XPathExpressionException e) {
            throw new XPMRuntimeException(e, "Cannot evaluted XPath expression [%s]", xpath);
        }
        return value;
    }

    @Override
    public AbstractProcessBuilder processBuilder(SingleHostConnector connector) throws FileSystemException {
        return new ProcessBuilder(connector);
    }

    @Override
    public XPMScriptProcessBuilder scriptProcessBuilder(SingleHostConnector connector, FileObject scriptFile)
            throws FileSystemException {
        return new UnixScriptProcessBuilder(scriptFile, connector, processBuilder(connector));
    }

    /**
     * Process builder for OAR
     */
    static public class ProcessBuilder extends AbstractProcessBuilder {
        // Command to start
        private String oarCommand = "oarsub";

        // The associated connector
        private SingleHostConnector connector;

        public ProcessBuilder(SingleHostConnector connector) {
            this.connector = connector;
        }

        @Override
        public XPMProcess start() throws LaunchException, IOException {
            final String path = job.getLocator().getPath();
            final String id = UnixScriptProcessBuilder.protect(path, "\"");

            String[] command = new String[] { oarCommand, "--stdout=oar.out", "--stderr=oar.err", id + ".run" };

            LOGGER.info("Running OAR with [%s]", Output.toString(" ", command));

            AbstractProcessBuilder processBuilder = connector.processBuilder();
            processBuilder.command(command);
            processBuilder.redirectOutput(Redirect.PIPE);
            processBuilder.redirectError(Redirect.PIPE);

            // START OAR and retrieves the process ID
            final XPMProcess process = processBuilder.start();

            final BufferedReader reader = new BufferedReader(new InputStreamReader(process.getInputStream()));
            String s;
            String pid = null;
            while ((s = reader.readLine()) != null) {
                if (s.startsWith(OARJOBID_PREFIX))
                    pid = s.substring(OARJOBID_PREFIX.length());
            }

            LOGGER.info("Started OAR job with PID %s", pid);

            return new OARProcess(job, pid, connector);
        }
    }

    /**
     * An OAR process
     */
    @Persistent
    static private class OARProcess extends XPMProcess {
        /**
         * Used for serialization
         */
        public OARProcess() {
        }

        public OARProcess(Job job, String pid, SingleHostConnector connector) {
            super(connector, String.format("oar:%s", connector.getHostName(), pid), job);
            startWaitProcess();
        }

        @Override
        public OutputStream getOutputStream() {
            return null; //To change body of implemented methods use File | Settings | File Templates.
        }

        @Override
        public InputStream getInputStream() {
            return null; //To change body of implemented methods use File | Settings | File Templates.
        }

        @Override
        public InputStream getErrorStream() {
            return null; //To change body of implemented methods use File | Settings | File Templates.
        }

        @Override
        public int waitFor() throws InterruptedException {
            return 0; //To change body of implemented methods use File | Settings | File Templates.
        }

        @Override
        public void destroy() {
            //To change body of implemented methods use File | Settings | File Templates.
        }

        @Override
        public boolean isRunning() {
            final Document document = oarstat(false);
            String state = evaluateXPathToString("//item[@identifier = \"state\"]/text()", document);
            LOGGER.debug("State of OAR process %s is %s", pid, state);
            return "running".equalsIgnoreCase(state);
        }

        @Override
        public int exitValue() {
            final Document document = oarstat(true);
            String state = evaluateXPathToString("//item[@identifier = \"state\"]/text()", document);
            if ("running".equalsIgnoreCase(state))
                throw new IllegalThreadStateException("Job is running - cannot access its exit value");

            String code = evaluateXPathToString("//item[@identifier = \"exit_code\"]/text()", document);

            LOGGER.debug("Exit code of OAR process %s is %s", pid, code);

            if ("".equals(code))
                return -1;
            return Integer.parseInt(code);
        }

        /**
         * Runs oarstat and returns the XML document
         */
        private Document oarstat(boolean full) {
            final Document document;
            try {
                document = exec(connector, String.format("oarstat --xml --job %s %s", full ? "--full" : "", pid));
            } catch (Exception e) {
                throw new XPMRuntimeException(e, "Cannot parse oarstat output");
            }
            return document;
        }

    }
}