Package com.sun.faban.harness.util

Source Code of com.sun.faban.harness.util.CmdMap$CmdDetail

/* 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.util;

import com.sun.faban.harness.common.Config;
import com.sun.faban.common.Command;
import com.sun.faban.common.CommandHandle;
import org.xml.sax.Attributes;
import org.xml.sax.SAXException;
import org.xml.sax.helpers.DefaultHandler;

import javax.xml.parsers.SAXParser;
import javax.xml.parsers.SAXParserFactory;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.util.*;
import java.util.logging.Level;
import java.util.logging.Logger;

/**
* Checks the binary paths and the command mapping file and creates the exec
* map.
*
* @author Akara Sucharitakul
*/
public class CmdMap {

    static Logger logger = Logger.getLogger(CmdMap.class.getName());

    /**
     * Scans the bin directories and command map file and returns the command
     * map.
     * @param benchName The name of the benchmark,
     *                  null if the map is not benchmark-specific
     * @return The command map
     * @throws Exception Something went wrong obtaining the command map.
     */
    public static HashMap<String, List<String>> getCmdMap(String benchName)
            throws Exception {
        HashMap<String, List<String>> binMap =
                                        new HashMap<String, List<String>>();

        // The platform-specific and benchmark-specific binaries
        // take precedence, add last to map.
        File binDir = new File(Config.FABAN_HOME + "bin"); // $FABAN_HOME/bin
        addExecMap(binDir, binMap, null);

        logger.finer("Scanning OS-specific binaries in " + Config.OS_DIR);
        File sbinDir = new File(binDir, Config.OS_DIR); // $FABAN_HOME/bin/SunOS
        addExecMap(sbinDir, binMap, null);

        logger.finer("Scanning architecture-specific binaries in " +
                     Config.ARCH_DIR);
        sbinDir = new File(binDir, Config.ARCH_DIR); // $FABAN_HOME/bin/SunOS/sparc
        addExecMap(sbinDir, binMap, null);

        if (benchName != null)
            addResourceMap(binMap, Config.BENCHMARK_DIR, benchName);

        mapPathExt(binMap);

        addCmdMapFile(binMap);

        // Dump the binMap for debugging
        if (logger.isLoggable(Level.FINER)) {
            StringBuilder b = new StringBuilder("Executable map:\n");
            for (Map.Entry<String, List<String>> entry : binMap.entrySet()) {
                b.append(entry.getKey());
                b.append(" :");
                List<String> l = entry.getValue();
                for (String v : l) {
                    b.append(' ').append(v);
                }
                b.append('\n');
            }
            logger.finer(b.toString());
        }
       return binMap;
    }

    /**
     * Obtains the command map for a service/tool deployment.
     * @param serviceName The name of the service
     * @return The command map, if applicable, for that service
     * @throws Exception Something went wrong obtaining the command map.
     */
    public static HashMap<String, List<String>> getServiceBinMap(
                                    String serviceName) throws Exception {
        HashMap<String, List<String>> binMap =
                                        new HashMap<String, List<String>>();

        if (serviceName != null)
            addResourceMap(binMap, Config.SERVICE_DIR, serviceName);

        mapPathExt(binMap);

        addCmdMapFile(binMap);

        // Dump the binMap for debugging
        if (logger.isLoggable(Level.FINER)) {
            StringBuilder b = new StringBuilder("Executable map for service ");
            b.append(serviceName).append(":\n");
            for (Map.Entry<String, List<String>> entry : binMap.entrySet()) {
                b.append(entry.getKey());
                b.append(" :");
                List<String> l = entry.getValue();
                for (String v : l) {
                    b.append(' ').append(v);
                }
                b.append('\n');
            }
            logger.finer(b.toString());
        }
       return binMap;

    }


    private static void addResourceMap(HashMap<String, List<String>> binMap,
                                       String baseDir, String resourceName) {
        // chmod is the way to make a file executable on Unix. Other platforms
        // like Win32 does not have it and uses a different mechanism. So
        // we'll run chmod only if it's there.
        File chmodCmd = new File("/bin/chmod");
        if (!chmodCmd.exists()) {
            chmodCmd = new File("/usr/bin/chmod");
            if (!chmodCmd.exists())
                chmodCmd = null;
        }

        ArrayList<String> chmod = null;
        if (chmodCmd != null) {
            chmod = new ArrayList<String>();
            chmod.add(chmodCmd.getAbsolutePath());
            chmod.add("+x");
        }
        File binDir = new File(baseDir + resourceName + "/bin/");
        boolean emptyList = addExecMap(binDir, binMap, chmod);
        File sbinDir = new File(binDir, Config.OS_DIR);
        emptyList = addExecMap(sbinDir, binMap, chmod) && emptyList;
        sbinDir = new File(binDir, Config.ARCH_DIR);
        emptyList = addExecMap(sbinDir, binMap, chmod) && emptyList;
        if (!emptyList)
            try {
                logger.fine("Changing mode for bin directories.");
                Command cmd = new Command(chmod);
                CommandHandle handle = cmd.execute();
                int exitValue = handle.exitValue();
                if (exitValue != 0)
                    logger.severe("Failed to chmod bin files. Exit value is " +
                                                                    exitValue);
            } catch (IOException e) {
                logger.log(Level.SEVERE, "Cannot change mode on bin files", e);
            } catch (InterruptedException e) {
                logger.log(Level.SEVERE,
                           "Interrupted changing mode on bin files", e);
            }
    }

    private static boolean addExecMap(File binDir,
                                      HashMap<String, List<String>> binMap,
                                      List<String> chmod) {
        boolean emptyList = true;
        if (binDir.isDirectory()) {
            File[] binFiles = binDir.listFiles();
            for (int i = 0; i < binFiles.length; i++)
                if (!binFiles[i].isDirectory()) {
                    String name = binFiles[i].getName();
                    String fullPath = binFiles[i].getAbsolutePath();
                    ArrayList<String> v = new ArrayList<String>();
                    v.add(fullPath);
                    binMap.put(name, v);
                    if (chmod != null) {
                        chmod.add(fullPath);
                        emptyList = false;
                    }
                }
        }
        return emptyList;
    }

    /**
     * Obtains a list of path extensions valid in this environment.
     * @return A string array of valid path extensions
     */
    public static String[] getPathExt() {
        String pathExt = System.getProperty("faban.pathext");

        // Check the environment on a Windows system, in case
        // the faban.pathext property is not set.
        if (pathExt == null && File.separatorChar == '\\')
            pathExt = System.getenv("PATHEXT");

        if (pathExt == null)
            return null;
        pathExt = pathExt.trim();
        if (pathExt.length() == 0)
            return null;

        // Ensure each of the exts are lowercase.
        String[] pathExts = pathExt.split(File.pathSeparator);
        for (int i = 0; i < pathExts.length; i++) {
            pathExts[i] = pathExts[i].toLowerCase();
        }
        return pathExts;
    }

    /**
     * The mapPathExt modifies the binMap according to the Win32
     * conventions. For example, faban.cmd can be called with just faban.
     * But calling it with faban.cmd also works. The PATHEXT needs to be
     * passed to the JVM as a system property faban.pathext. PATHEXT
     * matching is case insensitive. On Win32-like systems, faban.pathext
     * would be set at the JVM invocation. On Unix systems, this property
     * should not be set. This method would be a noop in this case.
     * @param binMap The binMap
     */
    private static void mapPathExt(Map<String, List<String>> binMap) {

        String[] pathExts = getPathExt();
        if (pathExts == null)
            return;

        // Use a separate map so we don't modify binmap while iterating
        HashMap<String, List<String>> pathExtMap =
                                new HashMap<String, List<String>>();

        Set<String> binKeys = binMap.keySet();

        // Scan from back to front so the frontmost one put latest
        // overrides the others that were put before.
        for (int i = pathExts.length - 1; i >= 0; i--)
            for (String key : binKeys)
                if (key.toLowerCase().endsWith(pathExts[i])) {
                    List<String> value = binMap.get(key);
                    String newKey = key.substring(0, key.length() -
                                    pathExts[i].length());
                    pathExtMap.put(newKey, value);
                }

        binMap.putAll(pathExtMap);
    }


    /**
     * Reads the command map file and adds/modifies the exec map accordingly.
     * @param binMap
     * @throws Exception
     */
    private static void addCmdMapFile(Map<String, List<String>> binMap)
            throws Exception {
        ArrayList<CmdDetail> cmdList = new ArrayList<CmdDetail>();

        File cmdMap = new File(
                            Config.CONFIG_DIR + Config.OS_DIR + "cmdmap.xml");

        if (cmdMap.exists()) {

            FileInputStream is = new FileInputStream(cmdMap);
            parseStream(is, cmdList);

            // Next step is to use the map command path and prefixes to the
            // actual executable.
            for (Iterator<CmdDetail> iter = cmdList.iterator();
                 iter.hasNext();) {
                CmdDetail c = iter.next();
                if (c.exec == null) {
                    c.exec = binMap.get(c.name);
                } else {
                    String cmd = c.exec.get(0);
                    File f = new File(cmd); // The exec can still be in
                    if (!f.isAbsolute()) {     // the Faban path. May need
                        List<String> path = binMap.get(cmd); // another mapping.
                        if (path != null)      // if not absolute path. Just
                            c.exec.set(0, path.get(0)); // ignore if not found in map.
                    }                          // Should be in OS path instead.                       
                }
                if (c.exec == null) {
                    c.exec = new ArrayList<String>();
                    c.exec.add(c.name);
                }
                for (List<String> prefix : c.prefix) {
                    String cmd = prefix.get(0);
                    List<String> path = binMap.get(cmd);
                    if (path != null)
                        replaceFirst(prefix, path);
                }
            }

            for (CmdDetail cmd : cmdList) {
                ArrayList<String> exec = new ArrayList<String>();
                for (List<String> prefix : cmd.prefix)
                    exec.addAll(prefix);
                exec.addAll(cmd.exec);
                binMap.put(cmd.name, exec);
            }
        }
    }

    private static void parseStream(FileInputStream is,
                                    ArrayList<CmdDetail> cmdList)
            throws Exception {
        SAXParserFactory sFact = SAXParserFactory.newInstance();
        sFact.setFeature("http://xml.org/sax/features/validation", false);
        sFact.setFeature("http://apache.org/xml/features/" +
                "allow-java-encodings", true);
        sFact.setFeature("http://apache.org/xml/features/nonvalidating/" +
                "load-dtd-grammar", false);
        sFact.setFeature("http://apache.org/xml/features/nonvalidating/" +
                "load-external-dtd", false);
        SAXParser parser = sFact.newSAXParser();
        parser.parse(is, new MapReaderHandler(cmdList));
    }

    static class CmdDetail {
        String name;
        List<String> exec;
        List<List<String>> prefix;

        public String toString() {
            StringBuilder b = new StringBuilder();
            b.append(name);
            b.append(':');

            for (List<String> p : prefix)
                for (String e : p)
                    b.append(e).append(' ');

            b.append(exec);
            return b.toString();
        }
    }

    static class MapReaderHandler extends DefaultHandler {
        private CmdDetail currentCmd;
        private ArrayList stack = new ArrayList();
        private StringBuilder buffer = new StringBuilder();
        private TreeMap<Integer, String> prefixes =
                new TreeMap<Integer, String>();
        private int currentSequence = Integer.MIN_VALUE;
        private ArrayList<CmdDetail> cmdList;

        MapReaderHandler(ArrayList<CmdDetail> cmdList) {
            this.cmdList = cmdList;
        }

        /**
         * Receive notification of the start of an element.
         * <p/>
         * <p>By default, do nothing.  Application writers may override this
         * method in a subclass to take specific actions at the start of
         * each element (such as allocating a new tree node or writing
         * output to a file).</p>
         *
         * @param uri        The Namespace URI, or the empty string if the
         *                   element has no Namespace URI or if Namespace
         *                   processing is not being performed.
         * @param localName  The local name (without prefix), or the
         *                   empty string if Namespace processing is not being
         *                   performed.
         * @param qName      The qualified name (with prefix), or the
         *                   empty string if qualified names are not available.
         * @param attributes The attributes attached to the element.  If
         *                   there are no attributes, it shall be an empty
         *                   Attributes object.
         * @throws org.xml.sax.SAXException Any SAX exception, possibly
         *                                  wrapping another exception.
         * @see org.xml.sax.ContentHandler#startElement
         */
        public void startElement(String uri, String localName, String qName,
                                 Attributes attributes) throws SAXException {
            stack.add(qName);
            if ("command".equals(qName)) {
                currentCmd = new CmdDetail();
            } else if ("prefix".equals(qName)) {
                String s = attributes.getValue("sequence");
                if (s != null) {
                    currentSequence = Integer.parseInt(s);
                    if (currentSequence < 0)
                        throw new SAXException(
                                "Prefix sequence must be 0 or greater");
                }
            }
        }

        /**
         * Receive notification of the end of an element.
         * <p/>
         * <p>By default, do nothing.  Application writers may override this
         * method in a subclass to take specific actions at the end of
         * each element (such as finalising a tree node or writing
         * output to a file).</p>
         *
         * @param uri       The Namespace URI, or the empty string if the
         *                  element has no Namespace URI or if Namespace
         *                  processing is not being performed.
         * @param localName The local name (without prefix), or the
         *                  empty string if Namespace processing is not being
         *                  performed.
         * @param qName     The qualified name (with prefix), or the
         *                  empty string if qualified names are not available.
         * @throws org.xml.sax.SAXException Any SAX exception, possibly
         *                                  wrapping another exception.
         * @see org.xml.sax.ContentHandler#endElement
         */
        public void endElement(String uri, String localName, String qName)
                throws SAXException {

            int depth = stack.size();

            if (!stack.remove(depth - 1).equals(qName))
                throw new SAXException("endElement mismatch: " + qName);

            if ("command".equals(qName)) {
                if (currentCmd.name == null)
                    throw new SAXException("Command name not specified");
                // Get all the prefixes
                int prefixSize = prefixes.size();
                currentCmd.prefix = new ArrayList<List<String>>();
                if (prefixSize > 0) {
                    for (Map.Entry<Integer, String> entry : prefixes.entrySet())
                        currentCmd.prefix.add(Command.parseArgs(
                                                            entry.getValue()));
                }
                prefixes.clear();
                cmdList.add(currentCmd);

            } else if ("name".equals(qName)) {
                currentCmd.name = buffer.toString().trim();
            } else if ("exec".equals(qName)) {
                currentCmd.exec = Command.parseArgs(buffer.toString().trim());
            } else if ("prefix".equals(qName)) {
                if (currentSequence < 0)
                    currentSequence = -1;
                String oldPrefix =
                        prefixes.put(currentSequence, buffer.toString().trim());
                if (oldPrefix != null) {
                    if (currentSequence == -1)
                        throw new SAXException("Need to specify prefix " +
                                "sequence for more than one prefix");
                    else
                        throw new SAXException("Duplicate prefix sequence " +
                                "found");
                }
                currentSequence = Integer.MIN_VALUE;
            }
            buffer.setLength(0);
        }

        /**
         * Receive notification of character data inside an element.
         * <p/>
         * <p>By default, do nothing.  Application writers may override this
         * method to take specific actions for each chunk of character data
         * (such as adding the data to a node or buffer, or printing it to
         * a file).</p>
         *
         * @param ch     The characters.
         * @param start  The start position in the character array.
         * @param length The number of characters to use from the
         *               character array.
         * @see org.xml.sax.ContentHandler#characters
         */
        public void characters(char ch[], int start, int length) {
            buffer.append(ch, start, length);
        }
    }

    /**
     * Replace the first element of the command list with the relacement.
     * The original list will likely grow.
     * @param orig The original command list
     * @param replacement The replacement for the first entry in the list
     */
    public static void replaceFirst(List orig, List replacement) {
        ArrayList tmp = new ArrayList();
        tmp.addAll(orig);
        orig.clear();
        orig.addAll(replacement);
        for (int i = 1; i < tmp.size(); i++) {
            orig.add(tmp.get(i));
        }
    }

    /**
     * Main to test the command map.
     * @param args The command line argument
     * @throws Exception If the mapping shows any errors
     */
    public static void main(String[] args) throws Exception {
        ArrayList<CmdDetail> cmdList = new ArrayList<CmdDetail>();
        HashMap<String, List<String>> binMap =
                new HashMap<String, List<String>>();
        FileInputStream is = new FileInputStream("cmdmap.xml");
        parseStream(is, cmdList);

        // Next step is to use the map command path and prefixes to the
        // actual executable.
        for (CmdDetail c : cmdList) {
            if (c.exec == null)
                c.exec = binMap.get(c.name);
            if (c.exec == null) {
                c.exec = new ArrayList<String>();
                c.exec.add(c.name);
            }

            // Try to map exec cmd back to the binMap
            String exec = c.exec.get(0);

            // Search cmd for path separators ('/')
            int pathSepIdx = exec.indexOf(File.separator);

            // If not found, still a relative path
            if (pathSepIdx < 0) {
                // Map it
                List<String> execList = binMap.get(exec);
                // And combine back with args
                if (execList != null)
                    replaceFirst(c.exec, execList);
            }

            for (List<String> prefix : c.prefix) {
                String cmd = prefix.get(0);
                List<String> execList = binMap.get(cmd);
                if (execList != null) {
                    replaceFirst(prefix, execList);
                }
            }
        }

        for (Iterator<CmdDetail> iter = cmdList.iterator();
             iter.hasNext();) {
             CmdDetail c = iter.next();
             System.out.println(c.toString());
        }
    }
}
TOP

Related Classes of com.sun.faban.harness.util.CmdMap$CmdDetail

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.