Package com.sun.faban.harness.engine

Source Code of com.sun.faban.harness.engine.RunQ$ComparatorImpl

/* The contents of this file are subject to the terms
* of the Common Development and Distribution License
* (the License). You may not use this file except in
* compliance with the License.
*
* You can obtain a copy of the License at
* http://www.sun.com/cddl/cddl.html or
* install_dir/legal/LICENSE
* See the License for the specific language governing
* permission and limitations under the License.
*
* When distributing Covered Code, include this CDDL
* Header Notice in each file and include the License file
* at install_dir/legal/LICENSE.
* If applicable, add the following below the CDDL Header,
* with the fields enclosed by brackets [] replaced by
* your own identifying information:
* "Portions Copyrighted [year] [name of copyright owner]"
*
* $Id$
*
* Copyright 2005-2009 Sun Microsystems Inc. All Rights Reserved
*/
package com.sun.faban.harness.engine;

import com.sun.faban.common.NameValuePair;
import com.sun.faban.harness.ParamRepository;
import com.sun.faban.harness.common.BenchmarkDescription;
import com.sun.faban.harness.common.Config;
import com.sun.faban.harness.common.Run;
import com.sun.faban.harness.common.RunId;
import com.sun.faban.harness.util.FileHelper;

import java.io.*;
import java.util.Arrays;
import java.util.Comparator;
import java.util.Map;
import java.util.logging.Level;
import java.util.logging.Logger;

/**
* This class implements the Faban RunQ. It provides methods to add a run,
* delete a run and to get a list of all the runs in the RunQ.
*
* @author Ramesh Ramachandran
*/

public class RunQ {

    RunDaemon runDaemon = null;
    RunQLock runqLock;
    static Logger logger = Logger.getLogger(RunQ.class.getName());

    private static RunQ runQ = null;

    /** Run seqeunce field index in the run queue listing. */
    public static final int RUNSEQ = 0;

    /** Benchmark name field index in the run queue listing. */
    public static final int BENCHNAME = 1;

    /** Description field index in the run queue listing. */
    public static final int DESCRIPTION = 2;

    private RunQ() {
        runqLock = new RunQLock();
        runDaemon = new RunDaemon(runqLock);
    }

    /**
     * Singleton initializer for runQ and runDaemon.
     * @return runQ
     */

    public static RunQ getHandle() {
        if(runQ == null)
            runQ = new RunQ();

        return runQ;
    }

    /**
     * Adds a Run to the runq. Creates a directory for the run in the runq
     * directory and stores the parameter repository in it. It also notifies
     * the RunDaemon thread of the newly added run. All operations on the runq
     * directory are synchronized through the use of the LockFileMonitor class.
     * @param user The user name or id if logged in, or null
     * @param profile Profile name for this run
     * @param desc The description of the benchmark to run
     * @return The run id of the run just added
     * @throws IOException There was a problem accessing the run queue directory
     */
    public String addRun(String user, String profile, BenchmarkDescription desc)
            throws IOException {

        RunSequence seq = new RunSequence();
        try {
            // Gets the lock for the runq directory.
            runqLock.grabLock();

            String runId = desc.shortName + '.' + seq.get();

            String runDir = Config.RUNQ_DIR + runId;
            // create Run Directory
            File dir = new File(runDir);
            if(dir.mkdirs())
                logger.fine("Created Run Directory " + runDir);

            // Create the META-INF and copy deployment info.
            File metaInf = new File(dir, "META-INF");
            metaInf.mkdirs();

            String benchMetaInf = Config.BENCHMARK_DIR + File.separator +
                    desc.shortName + File.separator + "META-INF" + File.separator;
            String runqMetaInf = metaInf.getAbsolutePath() + File.separator;
            String sourceBenchDesc = benchMetaInf + "benchmark.xml";
            String sourceFabanDesc = benchMetaInf + "faban.xml";
            String destBenchDesc = null;
            String destFabanDesc = null;
            if (new File(sourceBenchDesc).exists())
                destBenchDesc = runqMetaInf + "benchmark.xml";
            else
                sourceBenchDesc = null;
            if (new File(sourceFabanDesc).exists())
                destFabanDesc = runqMetaInf + "faban.xml";
            else
                sourceFabanDesc = null;

            if (sourceBenchDesc == null && sourceFabanDesc == null) {
                String msg = "No benchmark descriptors found";
                IOException e = new IOException(msg);
                logger.log(Level.SEVERE, msg, e);
                throw e;
            }

            if (!((sourceBenchDesc == null ||
                 FileHelper.copyFile(sourceBenchDesc, destBenchDesc, false)) &
                  (sourceFabanDesc == null ||
                 FileHelper.copyFile(sourceFabanDesc, destFabanDesc, false)))) {
                String msg = "Error copying benchmark descriptors.";
                IOException e = new IOException(msg);
                logger.log(Level.SEVERE, msg, e);
                throw e;
            }


            // Record the user
            if (Config.SECURITY_ENABLED) {
                // Set the submitter
                File submitter = new File(metaInf, "submitter");
                FileHelper.writeStringToFile(user, submitter);

                // Copy ACLs
                String[] aclFiles = { "view.acl", "write.acl" };
                for (int i = 0; i < aclFiles.length; i++) {
                    String benchAcl = Config.CONFIG_DIR + desc.shortName +
                                      File.separator + aclFiles[i];
                    String runAcl = null;
                    if (new File(benchAcl).exists())
                        runAcl = new File(metaInf, aclFiles[i]).getAbsolutePath();
                    else
                        benchAcl = null;

                    if (benchAcl != null &&
                            !FileHelper.copyFile(benchAcl, runAcl, false)) {
                        String msg = "Error copying " + benchAcl + " to " +
                                     runAcl + '.';
                        throw new IOException(msg);
                    }
                }
            }

            // copying the parameter repository file from the user's profile.
            String paramRepFileName =
                    runDir + File.separator + desc.configFileName;
            String paramSourceFileName = Config.PROFILES_DIR + profile +
                    File.separator + desc.configFileName + "." + desc.shortName;

            logger.fine("Copying " +
                    paramSourceFileName + " to " +
                    paramRepFileName);
            FileHelper.copyFile(paramSourceFileName, paramRepFileName, false);

            // copying the tags file from the profile dir
            if (profile != null && !"".equals(profile)){
                String tagsForProfile = null;
                File tagsFile = new File(Config.PROFILES_DIR + profile + "/tags");
                if(tagsFile.exists() && tagsFile.length()>0){
                    tagsForProfile = FileHelper.readContentFromFile(tagsFile).trim();
                }
                if(tagsForProfile != null) {
                    String tagsRepFileName =
                        runDir + File.separator + "META-INF/tags";
                    String tagsSrcFileName =
                        Config.PROFILES_DIR + profile + "/tags";
                    FileHelper.copyFile(tagsSrcFileName, tagsRepFileName, false);
                }
            }
            seq.next();
            runqLock.signal()// Signal a new run is submitted.
            return runId;
        } finally {
            if (seq != null)
                seq.cancel();
            runqLock.releaseLock();
        }
    }

    /**
     * Deletes the run with the specified runId from the runq. Does not take
     * any action if such a run is not found or is already being executed
     * by the runDaemon thread.
     *
     * @param runId the runId of the run to be deleted
     * @return Whether or not the run was successfully deleted from the queue
     */
    public boolean deleteRun(String runId)
    {
        try {
            //   File runqDirPath = new File(Config.RUNQ_DIR);
            logger.warning("Removing run directory " + runId);
            runqLock.grabLock();
            boolean retVal = FileHelper.recursiveDelete(
                    new File(Config.RUNQ_DIR), runId);
            runqLock.releaseLock();
            return retVal;
        }
        catch (Exception e)
        {
            logger.log(Level.WARNING, "Could not delete the run directory "+
                    runId + '.', e);
        }
        return false;
    }


    /**
     * Returns a list of the runs currently in the runq. The list is an array
     * of run sequence, benchmark name, and description.
     * @return an array of RunInfo objects.
     *
     */
    public String[][] listRunQ() {
        String[][] data = null;

        try {
            File runqDirPath = new File(Config.RUNQ_DIR);
            String[] list = runqDirPath.list();

            if((list != null) && (list.length > 0)) {
                Arrays.sort(list, new ComparatorImpl());
                data = new String[list.length][3];

                // We do not want to check for new deployments in listRunQ,
                // pass false as getBenchDirMap argument.
                Map<String, BenchmarkDescription> benchMap =
                        BenchmarkDescription.getBenchDirMap(false);
                for (int i = 0; i < list.length; i++) {
                    RunId runId = new RunId(list[i]);
                    String benchName = runId.getBenchName();
                    String runSeq = runId.getRunSeq();
                    data[i][RUNSEQ] = runSeq;
                    data[i][BENCHNAME] = benchName;
                    String paramFile = Config.RUNQ_DIR + list[i]
                            + File.separator + ((BenchmarkDescription)
                            benchMap.get(benchName)).configFileName;
                    String desc = null;
                    if (new File(paramFile).exists()) {
                        ParamRepository par =
                                new ParamRepository(paramFile, false);
                        desc = par.getParameter("fa:runConfig/fh:description");
                    }
                    if((desc == null) || (desc.length() == 0))
                        data[i][DESCRIPTION] = "UNAVAILABLE";
                    else
                        data[i][DESCRIPTION] = desc;
                }
            }
        }
        catch (Exception e) {
            logger.log(Level.SEVERE, "Could not list the runQ.", e);
        }
        return data;
    }

    /**
     * Obtains the list of pending runs in the run queue. This is the list
     * of formatted runids. This is a convenience method and it internally
     * calls listRunQ.
     * @return The list of pending runs
     */
    public static String[] listPending() {
        String[][] pendingA = getHandle().listRunQ();
        String[]  pendingL = null;
        if (pendingA != null) {
            pendingL = new String[pendingA.length];
            for (int i = 0; i < pendingA.length; i++)
                pendingL[i] = pendingA[i][1] + '.' + pendingA[i][0];
        }
        return pendingL;
    }


    /**
     * Reports the status of the RunDaemonThread.
     * @return String representing the current status of the run daemon.
     */
    public String getRunDaemonStatus()
    {
        return "Run Daemon is " + runDaemon.getRunDaemonThreadStatus();
    }

    /**
     * Starts/restarts the run daemon.
     * @return Whether the run daemon is started
     */
    public boolean startRunDaemon()
    {
        return runDaemon.resumeRunDaemonThread();
    }

    /**
     * Stops/suspends the run daemon.
     * @return Whether the run daemon is successfully stopped
     */
    public boolean stopRunDaemon()
    {
        return runDaemon.suspendRunDaemonThread();
    }

    /**
     * Obtains the run id of the current run.
     * @return The run id of the current run,
     *         or null if there is no ccurrent run
     */
    public String getCurrentRunId() {
        return runDaemon.getCurrentRunId();
    }

    /**
     * Obtains the short name of the current benchmark run.
     * @return The benchmark's short name
     */        
    public String getCurrentBenchmark() {
        return runDaemon.getCurrentRunBenchmark();
    }

    /**
     * Method to stop the current benchamark run.
     * @param runId The run id to kill - this is for safety
     * @param user The current user name, or null if security is disabled
     * @return current run name.
     */
    public String killCurrentRun(String runId, String user) {
        return runDaemon.killCurrentRun(runId, user);
    }

    /**
     * Moves the run from the queue into the output directory. This
     * function is normally done by the run daemon with the exception
     * of a submission proxy where the run daemon is not run. The RunRetriever
     * servlet will call to fetch this run to run on a remote system instead.
     * If the given run id does not match the id of the next run, some
     * inconsistencies have happened. The method returns null in this case.
     * @param runId The id of the run in question.
     * @return The run object representing this run
     * @throws RunEntryException There is an error in the run queue entry
     */
    public Run fetchNextRun(String runId) throws RunEntryException, IOException, ClassNotFoundException {
        if (runId == null)
            throw new NullPointerException("Run name cannot be null!");
        return runDaemon.fetchNextRun(runId);
    }

    /**
     * Obtains the name and age of the next run, in milliseconds
     * since submitted, if the age is more than minAge.
     * @param minAge The minimum run age to return.
     * @return The age of the next run, or null if there is no next run or the
     *         next run is younger than the given age
     */
    public NameValuePair<Long> nextRunAge(long minAge) {
        return runDaemon.nextRunAge(minAge);
    }

    /**
     * Method to stop the run daemon before unloading
     * Faban Engine servlet.
     */
    public void exit() {
        runDaemon.exit();
    }

    /**
     * The RunSequence class assists in generating the run sequence.
     */
    static class RunSequence {

        RandomAccessFile seqRFile;
        String runSeq = null;

        // Gets the sequence for this run from the sequence file. Creates a new
        // sequence file if it does not already exist.
        String get() {

            String runSeqChar, runSeqIntChar;
            File seqFile = new File(Config.SEQUENCE_FILE);

            if (seqFile.exists()) {
                try {
                    seqRFile = new RandomAccessFile(seqFile, "rwd");
                    long size = seqRFile.length();
                    if (size > Integer.MAX_VALUE)
                        throw new IOException(Config.SEQUENCE_FILE +
                                " larger than 2GB not supported!");
                    byte[] buffer = new byte[(int) size];
                    seqRFile.readFully(buffer);
                    if (buffer[buffer.length - 1] == '\n')
                        --size;
                    runSeq = new String(buffer, 0, (int) size);

                    int colonPos = -1;
                    if(runSeq != null &&
                            (colonPos = runSeq.indexOf(":")) != -1) {
                        runSeqChar = runSeq.substring(colonPos + 1);
                        runSeqIntChar = runSeq.substring(0, colonPos);
                        runSeq = runSeqIntChar + runSeqChar;
                    } else {
                        logger.warning("RunQ getRunId: " +
                                "Invalid runSeq in sequence file");
                        runSeq = null;
                    }
                } catch (IOException e) {
                    logger.log(Level.WARNING, e.getMessage(), e);
                }
            }

            // Could not find a valid runSeq
            if(runSeq == null) {
                runSeq = "1A";
            }
            return runSeq;
        }

        // Generate the sequence number for the next run and write it to
        // sequence file.
        void next() throws IOException {

            int length = runSeq.length();

            char seqChar = runSeq.charAt(length - 1);
            String seqIntStr = runSeq.substring(0, length - 1);
            int seqInt = Integer.parseInt(seqIntStr);
            switch (seqChar) {
                case 'z' : // old installs will still use lower case up to the
                           // next number switch. This fix is for win32
                           // compatibility. It is not case sensitive.
                case 'Z' : ++seqInt; seqChar = 'A'; break;
                default  : ++seqChar;
            }

            StringBuilder sb = new StringBuilder();
            sb.append(seqInt).append(':').append(seqChar);

            try {
                if (seqRFile == null)
                    seqRFile = new RandomAccessFile(Config.SEQUENCE_FILE,"rwd");
                else
                    seqRFile.seek(0);
                byte[] buffer = sb.toString().getBytes();
                seqRFile.write(buffer);
                if (seqRFile.length() != buffer.length)
                    seqRFile.setLength(buffer.length);
                seqRFile.close();
            } catch (IOException e) {
                logger.log(Level.SEVERE,
                        "Could not write to the sequence file", e);
                throw e;
            } finally {
                if (seqRFile != null) {
                    seqRFile.close();
                    seqRFile = null;
                }
            }
        }

        /**
         * Cancels any pending sequence operations.
         */
        public void cancel() {
            if (seqRFile != null) {
                try {
                    seqRFile.close();
                } catch (IOException e) {
                    logger.log(Level.WARNING,
                                        "Closing sequence file failed!", e);
                }
                seqRFile = null;
            }
        }
    }

    /**
      * This class serves as the comparator to sort runs in the runq and pick
      * the run with the smallest run sequence.
      *
      */
    private class ComparatorImpl implements Comparator {

        public int compare(Object o1, Object o2) {
            RunId r1 = new RunId((String) o1);
            RunId r2 = new RunId((String) o2);
            return r1.getRunSeq().compareTo(r2.getRunSeq());
        }
    }
}
TOP

Related Classes of com.sun.faban.harness.engine.RunQ$ComparatorImpl

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.