esg.common.shell.ESGFShell.java Source code

Java tutorial

Introduction

Here is the source code for esg.common.shell.ESGFShell.java

Source

/***************************************************************************
*                                                                          *
*  Organization: Lawrence Livermore National Lab (LLNL)                    *
*   Directorate: Computation                                               *
*    Department: Computing Applications and Research                       *
*      Division: S&T Global Security                                       *
*        Matrix: Atmospheric, Earth and Energy Division                    *
*       Program: PCMDI                                                     *
*       Project: Earth Systems Grid Federation (ESGF) Data Node Software   *
*  First Author: Gavin M. Bell (gavin@llnl.gov)                            *
*                                                                          *
****************************************************************************
*                                                                          *
*   Copyright (c) 2009, Lawrence Livermore National Security, LLC.         *
*   Produced at the Lawrence Livermore National Laboratory                 *
*   Written by: Gavin M. Bell (gavin@llnl.gov)                             *
*   LLNL-CODE-420962                                                       *
*                                                                          *
*   All rights reserved. This file is part of the:                         *
*   Earth System Grid Federation (ESGF) Data Node Software Stack           *
*                                                                          *
*   For details, see http://esgf.org/esg-node/                             *
*   Please also read this link                                             *
*    http://esgf.org/LICENSE                                               *
*                                                                          *
*   * Redistribution and use in source and binary forms, with or           *
*   without modification, are permitted provided that the following        *
*   conditions are met:                                                    *
*                                                                          *
*   * Redistributions of source code must retain the above copyright       *
*   notice, this list of conditions and the disclaimer below.              *
*                                                                          *
*   * Redistributions in binary form must reproduce the above copyright    *
*   notice, this list of conditions and the disclaimer (as noted below)    *
*   in the documentation and/or other materials provided with the          *
*   distribution.                                                          *
*                                                                          *
*   Neither the name of the LLNS/LLNL nor the names of its contributors    *
*   may be used to endorse or promote products derived from this           *
*   software without specific prior written permission.                    *
*                                                                          *
*   THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS    *
*   "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT      *
*   LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS      *
*   FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL LAWRENCE    *
*   LIVERMORE NATIONAL SECURITY, LLC, THE U.S. DEPARTMENT OF ENERGY OR     *
*   CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,           *
*   SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT       *
*   LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF       *
*   USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND    *
*   ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,     *
*   OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT     *
*   OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF     *
*   SUCH DAMAGE.                                                           *
*                                                                          *
***************************************************************************/
package esg.common.shell;

/**
   Description:
   Top level class for ESGF Shell implementation...
**/

import static esg.common.CPrint.*;

import jline.*;

import java.io.*;
import java.util.*;

import java.io.File;
import java.io.BufferedReader;
import java.io.FileReader;

import java.util.regex.Pattern;
import java.util.regex.Matcher;

import org.apache.commons.cli.*;

import esg.common.ESGException;
import esg.common.ESGRuntimeException;
import esg.common.shell.cmds.*;
import esg.common.util.ESGFProperties;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.commons.logging.impl.*;

import static esg.common.shell.ESGFEnv.*;

public class ESGFShell {

    private static Log log = LogFactory.getLog(ESGFShell.class);

    public static final Character MASK = '*';
    public static final String PIPE_RE = "\\|";
    public static final String SEMI_RE = ";";

    private static final Pattern commandLineParsingPattern = Pattern
            .compile("((?<=(\"))[\\w-? ]*(?=(\"(\\s|$))))|((?<!\")[-]*[\\w-?]+(?!\"))", Pattern.CASE_INSENSITIVE);
    private final Matcher commandLineMatcher = commandLineParsingPattern.matcher("");

    private static final String commandTypeRegex = "^[ ]*\\[([a-zA-Z]*)\\][ ]*$";
    private static final Pattern commandTypePattern = Pattern.compile(commandTypeRegex, Pattern.CASE_INSENSITIVE);
    private final Matcher typeMatcher = commandTypePattern.matcher("");

    private static final String commandEntryRegex = "[ ]*([a-zA-Z0-9-_]*)[ ]*(?:->|=)[ ]*([a-zA-Z0-9-_./]*)[ ]*$";
    private static final Pattern commandEntryPattern = Pattern.compile(commandEntryRegex, Pattern.CASE_INSENSITIVE);
    private final Matcher entryMatcher = commandEntryPattern.matcher("");

    String commandName = null;
    String resource = null;

    private Map<String, ESGFCommand> commandMap = null;
    private List<Completor> completors = null;

    public ESGFShell(ESGFEnv env) {
        loadCommands();

        completors = new LinkedList<Completor>();
        completors.add(new SimpleCompletor(commandMap.keySet().toArray(new String[] {})));
        env.getReader().addCompletor(new ArgumentCompletor(completors));
    }

    /**
       This is where we look through the command list and load up the
       commands made available by this shell
    */
    private void loadCommands() {
        System.out.print("Loading ESGF Builtin Shell Commands ");
        commandMap = new HashMap<String, ESGFCommand>();
        //commandMap.put("test",new esg.common.shell.cmds.ESGFtest()); //now loaded as contrib command
        commandMap.put("clear", new esg.common.shell.cmds.ESGFclear());
        commandMap.put("ls", new esg.common.shell.cmds.ESGFls());

        commandMap.put("set", new esg.common.shell.cmds.ESGFCommand() {
            public String getCommandName() {
                return "set";
            }

            public void doInitOptions() {
            }

            public ESGFEnv doEval(CommandLine line, ESGFEnv env) {
                log.trace("inside the \"set\" command's doEval");
                try {
                    env.putContext(DEFAULT, line.getArgs()[0], line.getArgs()[1]);
                } catch (Throwable t) {
                }
                return env;
            }
        });

        commandMap.put("unset", new esg.common.shell.cmds.ESGFCommand() {
            public String getCommandName() {
                return "unset";
            }

            public void doInitOptions() {
            }

            public ESGFEnv doEval(CommandLine line, ESGFEnv env) {
                log.trace("inside the \"unset\" command's doEval");
                try {
                    env.removeContext(DEFAULT, line.getArgs()[0]);
                } catch (Throwable t) {
                }
                return env;
            }
        });

        //---
        //security / administrative commands
        //(NOTE: Class loading these because they are apart of the esgf-security project... not resident to the node-manager.
        //       Avoids circular dependencies between esgf-security and esgf-node-manager...)
        //See loadCommand method below...
        //---
        loadCommand("useradd   -> esg.node.security.shell.cmds.ESGFuseradd");
        loadCommand("userdel   -> esg.node.security.shell.cmds.ESGFuserdel");
        loadCommand("usermod   -> esg.node.security.shell.cmds.ESGFusermod");
        loadCommand("groupadd  -> esg.node.security.shell.cmds.ESGFgroupadd");
        loadCommand("groupdel  -> esg.node.security.shell.cmds.ESGFgroupdel");
        loadCommand("groupmod  -> esg.node.security.shell.cmds.ESGFgroupmod");
        loadCommand("roleadd   -> esg.node.security.shell.cmds.ESGFroleadd");
        loadCommand("roledel   -> esg.node.security.shell.cmds.ESGFroledel");
        loadCommand("rolemod   -> esg.node.security.shell.cmds.ESGFrolemod");
        loadCommand("associate -> esg.node.security.shell.cmds.ESGFassociate");
        loadCommand("passwd    -> esg.node.security.shell.cmds.ESGFpasswd");
        loadCommand("show      -> esg.node.security.shell.cmds.ESGFshow");

        //---
        //search
        //---
        //This command must live on the index server node...
        loadCommand("ingest -> esg.node.search.shell.cmds.ESGFingest");
        //loadCommand("search -> new esg.common.shell.cmds.search.ESGFsearch");

        //---
        //copy / replication commands
        //---
        //commandMap.put("cpds"     ,new esg.common.shell.cmds.ESGFcpds());
        //commandMap.put("realize"  ,new esg.common.shell.cmds.ESGFrealize());
        //commandMap.put("replicate",new esg.common.shell.cmds.ESGFreplicate());

        //Help command...
        commandMap.put("help", new esg.common.shell.cmds.ESGFCommand() {
            public String getCommandName() {
                return "help";
            }

            public String getInfo() {
                return "prints this command list";
            }

            public void doInitOptions() {
            }

            public ESGFEnv doEval(CommandLine line, ESGFEnv env) {
                log.trace("inside the \"help\" command's doEval");
                try {
                    for (String commandName : ESGFShell.this.commandMap.keySet()) {
                        env.getWriter().println(
                                commandName + "  --- " + ESGFShell.this.commandMap.get(commandName).getInfo());
                        //formatter.printUsage(env.getWriter(),
                        //                     env.getReader().getTermwidth(),
                        //                     commandName,
                        //                     ESGFShell.this.commandMap.get(commandName).getOptions());
                    }
                    env.getWriter().flush();
                } catch (Throwable t) {
                }
                return env;
            }
        });
        commandMap.put("?", commandMap.get("help"));

        System.out.println();
        loadCommandsFromFile();
        System.out.println();
        log.info("(" + commandMap.size() + ") commands loaded");
    }

    private void loadCommandsFromFile() {
        System.out.print("Loading ESGF Contrib Shell Commands ");

        String configDir = null;
        String line = null;
        String commandType = null;

        if (null != (configDir = System.getenv().get("ESGF_HOME"))) {
            configDir = configDir + File.separator + "config";
            BufferedReader in = null;
            try {
                File commandList = new File(configDir + File.separator + "esgf_contrib_commands");
                if (commandList.exists()) {
                    in = new BufferedReader(new FileReader(commandList));
                    try {
                        while ((line = in.readLine()) != null) {
                            line = line.trim();
                            if (line.isEmpty() || line.startsWith("#"))
                                continue; //skip blank and comment lines...

                            //Regex for pulling out [commandType]
                            //if the regex gets a hit, set the commandType accordingly.
                            typeMatcher.reset(line);
                            if (typeMatcher.find()) {
                                String foundCommandType = typeMatcher.group(1);
                                if (foundCommandType != null) {
                                    commandType = foundCommandType;
                                    log.trace("command implementation = [" + commandType + "]");
                                }
                                continue;
                            }
                            if (commandType == null)
                                continue;
                            loadCommand(commandType, line);
                        }
                    } catch (java.io.IOException ex) {
                        log.error(ex);
                    } finally {
                        if (null != in)
                            in.close();
                    }
                } else {
                    log.trace("Could not find command file: [" + commandList.getPath() + "]");
                }
            } catch (Throwable t) {
                log.error(t);
            }
        } else {
            log.warn("ESGF_HOME not found in environment");
        }
    }

    /**
       Loads commands into the shell
     */
    private void loadCommand(String line) {
        this.loadCommand("java", line);
    }

    private void loadCommand(String commandType, String line) {
        //Regex to parse the line for commandName and resource information...
        System.out.print(".");
        entryMatcher.reset(line);
        if (entryMatcher.find()) {
            commandName = entryMatcher.group(1);
            resource = entryMatcher.group(2);
            log.trace("preparing to load [" + commandName + "] -> [" + resource + "]");
        } else {
            log.warn("Malformed shell command entry: [" + line + "]");
            return;
        }

        //-----
        //NOTE: yes yes, I know... there is a sexier way to do this
        //with enums, but I have to Brody this right now son.
        //-----
        if (commandType.equalsIgnoreCase("java")) {
            loadJavaCommand(commandName, resource);
        } else if (commandType.equalsIgnoreCase("clojure")) {
            loadClojureCommand(commandName, resource);
        } else if (commandType.equalsIgnoreCase("scala")) {
            loadScalaCommand(commandName, resource);
        } else if (commandType.equalsIgnoreCase("jython")) {
            loadJythonCommand(commandName, resource);
        } else if (commandType.equalsIgnoreCase("groovy")) {
            loadGroovyCommand(commandName, resource);
        } else if (commandType.equalsIgnoreCase("beanshell")) {
            loadBeanShellCommand(commandName, resource);
        } else {
            log.warn("Unknown command implementation language [" + commandType + "]");
        }
    }

    //Handles (loads)  Java shell command entries
    private void loadJavaCommand(String commandName, String resource) {
        try {
            commandMap.put(commandName, (ESGFCommand) (Class.forName(resource).newInstance()));
        } catch (Exception e) {
            log.trace(" unable to load " + commandName + ": " + e.getMessage());
        }
    }

    private void loadClojureCommand(String commandName, String resource) {
        log.warn("Clojure commands not yet supported [" + commandName + "]->[" + resource + "]");
    }

    private void loadScalaCommand(String commandName, String resource) {
        log.warn("Scala commands not yet supported [" + commandName + "]->[" + resource + "]");
    }

    private void loadJythonCommand(String commandName, String resource) {
        log.warn("Jython commands not yet supported [" + commandName + "]->[" + resource + "]");
    }

    private void loadGroovyCommand(String commandName, String resource) {
        log.warn("Groovy commands not yet supported [" + commandName + "]->[" + resource + "]");
    }

    private void loadBeanShellCommand(String commandName, String resource) {
        log.warn("BeanShell commands not yet supported [" + commandName + "]->[" + resource + "]");
    }

    public static void usage() {
        System.out.println("Usage: java " + ESGFShell.class.getName() + " yadda yadda yadda");
    }

    private void eval(String[] commands, ESGFEnv env) throws ESGException, IOException {

        //-------------------------
        // "quit/exit" command
        //-------------------------
        if (commands[0].equalsIgnoreCase("quit") || commands[0].equalsIgnoreCase("exit")) {
            if (getMode(env) == null) {
                System.exit(0);
            } else {
                clearMode(env);
                clearUserName(env);
                env.removeContext(SYS, "auth");
                return;
            }
        }

        //-------------------------
        // "su" command
        //-------------------------
        if (commands[0].compareTo("su") == 0) {
            String password = null;
            while ((password = env.getReader().readLine("password> ", MASK)) != null) {
                if (env.getEnv().getAdminPassword().equals(password) /*password.equals("foobar")*/) {
                    env.putContext(SYS, "user.name", "rootAdmin");
                    env.putContext(SYS, "auth", true);
                    env.putContext(USER, "mode", "admin");
                    break;
                } else {
                    env.getWriter().println("incorrect password :-(");
                    env.getWriter().flush();
                }
            }
            env.getWriter().println();
            env.getWriter().flush();
            return;
        }

        //-------------------------
        // "id" command
        //-------------------------
        if (commands[0].compareTo("id") == 0) {
            env.getWriter().println(getUserName(env) + ":" + env.getContext(SYS, "auth"));
            env.getWriter().flush();
            return;
        }

        //-------------------------
        // show env object
        //-------------------------
        if (commands[0].compareTo("env") == 0) {
            env.getWriter().println(env);
            env.getWriter().flush();
            return;
        }

        //-------------------------
        // reload commands
        //-------------------------
        if (commands[0].compareTo("rehash") == 0) {
            loadCommands();
            return;
        }

        //-------------------------

        for (String commandLine : commands) {
            log.trace("======> commandLine [" + commandLine + "] ");
            commandLineMatcher.reset(commandLine);

            List<String> argsList = new ArrayList<String>();
            String commandName = null;
            for (int i = 0; commandLineMatcher.find(); i++) {
                if (i == 0) {
                    commandName = commandLineMatcher.group();
                    log.trace("Command: " + commandName);
                } else {
                    argsList.add(commandLineMatcher.group());
                    log.trace("arg(" + (i - 1) + "): " + argsList.get(i - 1));
                }
            }

            if ((commandName == null) || (commandName.equals("")))
                continue;
            log.trace("======> command [" + commandName + "] args " + argsList);

            ESGFCommand command = commandMap.get(commandName);
            if (null == command) {
                env.getWriter().println(commandName + ": command not found :-(");
                continue;
            }

            command.init(env);
            command.eval(argsList.toArray(new String[] {}), env);
        }
        env.getWriter().flush();
    }

    //------------------------
    // Helper Methods for common tasks...
    //------------------------

    //helper method to encapsulate common task of getting username
    public String getUserName(ESGFEnv env) {
        String whoami = null;
        if ((whoami = (String) env.getContext(SYS, "user.name")) == null) {
            whoami = clearUserName(env);
        }
        return whoami;
    }

    //helper method to encapsulate common task of clearing (resetting) username
    public String clearUserName(ESGFEnv env) {
        String whoami = System.getProperty("user.name");
        env.putContext(SYS, "user.name", whoami);
        return whoami;
    }

    //helper method to encapsulate common task of getting mode
    public String getMode(ESGFEnv env) {
        String mode = null;
        if ((mode = (String) env.getContext(USER, "mode")) == null) {
            clearMode(env);
        }
        return mode;
    }

    //helper method to encapsulate common task of clearing mode
    public void clearMode(ESGFEnv env) {
        env.putContext(USER, "mode", null);
    }

    //------------------------
    //------------------------

    public static void main(String[] args) throws IOException {
        if ((args.length > 0) && (args[0].equals("--help"))) {
            usage();
            return;
        }

        String hostname = "<?>";
        try {
            hostname = java.net.InetAddress.getLocalHost().getHostName().split("\\.", 2)[0];
        } catch (java.net.UnknownHostException e) {
            log.error(e);
        }

        ConsoleReader reader = new ConsoleReader();
        reader.setBellEnabled(false);
        //String debugFile = System.getProperty("java.io.tmpdir")+File.separator+"writer.debug";
        //log.trace("("+debugFile+")");
        //reader.setDebug(new PrintWriter(new FileWriter(debugFile, true)));

        PrintWriter writer = new PrintWriter(System.out);
        ESGFProperties esgfProperties = null;
        try {
            esgfProperties = new ESGFProperties();
        } catch (Throwable t) {
            System.out.println(t.getMessage());
        }
        ESGFEnv env = new ESGFEnv(reader, writer, esgfProperties);
        ESGFShell shell = new ESGFShell(env);

        String mode = null;
        String line = null;

        while ((line = reader.readLine(yellow(shell.getUserName(env) + "@" + hostname) + ":[" + red("esgf-sh") + "]"
                + (((mode = shell.getMode(env)) == null) ? "" : ":[" + green(mode) + "]")
                + white_b("> "))) != null) {

            try {
                shell.eval(line.trim().split(SEMI_RE), env);
            } catch (Throwable t) {
                System.out.println(t.getMessage());
                //t.printStackTrace();
                env.getWriter().flush();
            }
        }
    }
}