Github user nickwallen commented on a diff in the pull request:

    https://github.com/apache/metron/pull/884#discussion_r158654855
  
    --- Diff: 
metron-stellar/stellar-common/src/main/java/org/apache/metron/stellar/common/shell/cli/StellarShell.java
 ---
    @@ -0,0 +1,409 @@
    +/*
    + *
    + *  Licensed to the Apache Software Foundation (ASF) under one
    + *  or more contributor license agreements.  See the NOTICE file
    + *  distributed with this work for additional information
    + *  regarding copyright ownership.  The ASF licenses this file
    + *  to you under the Apache License, Version 2.0 (the
    + *  "License"); you may not use this file except in compliance
    + *  with the License.  You may obtain a copy of the License at
    + *
    + *      http://www.apache.org/licenses/LICENSE-2.0
    + *
    + *  Unless required by applicable law or agreed to in writing, software
    + *  distributed under the License is distributed on an "AS IS" BASIS,
    + *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 
implied.
    + *  See the License for the specific language governing permissions and
    + *  limitations under the License.
    + *
    + */
    +
    +package org.apache.metron.stellar.common.shell.cli;
    +
    +import com.fasterxml.jackson.core.type.TypeReference;
    +import com.google.common.base.Splitter;
    +import com.google.common.collect.Iterables;
    +import org.apache.commons.cli.CommandLine;
    +import org.apache.commons.cli.CommandLineParser;
    +import org.apache.commons.cli.HelpFormatter;
    +import org.apache.commons.cli.Option;
    +import org.apache.commons.cli.Options;
    +import org.apache.commons.cli.PosixParser;
    +import org.apache.commons.lang3.StringUtils;
    +import org.apache.log4j.PropertyConfigurator;
    +import org.apache.metron.stellar.common.shell.DefaultStellarAutoCompleter;
    +import org.apache.metron.stellar.common.shell.DefaultStellarShellExecutor;
    +import org.apache.metron.stellar.common.shell.StellarAutoCompleter;
    +import org.apache.metron.stellar.common.shell.StellarShellExecutor;
    +import org.apache.metron.stellar.common.shell.StellarShellResult;
    +import org.apache.metron.stellar.common.utils.ConversionUtils;
    +import org.apache.metron.stellar.common.utils.JSONUtils;
    +import org.jboss.aesh.complete.CompleteOperation;
    +import org.jboss.aesh.complete.Completion;
    +import org.jboss.aesh.console.AeshConsoleCallback;
    +import org.jboss.aesh.console.Console;
    +import org.jboss.aesh.console.ConsoleOperation;
    +import org.jboss.aesh.console.Prompt;
    +import org.jboss.aesh.console.settings.SettingsBuilder;
    +import org.jboss.aesh.terminal.CharacterType;
    +import org.jboss.aesh.terminal.Color;
    +import org.jboss.aesh.terminal.TerminalCharacter;
    +import org.jboss.aesh.terminal.TerminalColor;
    +
    +import java.io.File;
    +import java.io.FileInputStream;
    +import java.io.IOException;
    +import java.io.InputStream;
    +import java.util.ArrayList;
    +import java.util.List;
    +import java.util.Map;
    +import java.util.Optional;
    +import java.util.Properties;
    +
    +import static org.apache.metron.stellar.dsl.Context.Capabilities.CONSOLE;
    +import static 
org.apache.metron.stellar.dsl.Context.Capabilities.GLOBAL_CONFIG;
    +import static 
org.apache.metron.stellar.dsl.Context.Capabilities.SHELL_VARIABLES;
    +
    +/**
    + * A REPL environment for Stellar.
    + *
    + * Useful for debugging Stellar expressions.
    + */
    +public class StellarShell extends AeshConsoleCallback implements 
Completion {
    +
    +  private static final String WELCOME = "Stellar, Go!\n" +
    +          "Functions are loading lazily in the background and will be 
unavailable until loaded fully.";
    +
    +  private List<TerminalCharacter> EXPRESSION_PROMPT = new 
ArrayList<TerminalCharacter>()
    +  {{
    +    add(new TerminalCharacter('[', new TerminalColor(Color.RED, 
Color.DEFAULT)));
    +    add(new TerminalCharacter('S', new TerminalColor(Color.GREEN, 
Color.DEFAULT), CharacterType.BOLD));
    +    add(new TerminalCharacter('t', new TerminalColor(Color.GREEN, 
Color.DEFAULT), CharacterType.BOLD));
    +    add(new TerminalCharacter('e', new TerminalColor(Color.GREEN, 
Color.DEFAULT), CharacterType.BOLD));
    +    add(new TerminalCharacter('l', new TerminalColor(Color.GREEN, 
Color.DEFAULT), CharacterType.BOLD));
    +    add(new TerminalCharacter('l', new TerminalColor(Color.GREEN, 
Color.DEFAULT), CharacterType.BOLD));
    +    add(new TerminalCharacter('a', new TerminalColor(Color.GREEN, 
Color.DEFAULT), CharacterType.BOLD));
    +    add(new TerminalCharacter('r', new TerminalColor(Color.GREEN, 
Color.DEFAULT), CharacterType.BOLD));
    +    add(new TerminalCharacter(']', new TerminalColor(Color.RED, 
Color.DEFAULT)));
    +    add(new TerminalCharacter('>', new TerminalColor(Color.GREEN, 
Color.DEFAULT), CharacterType.UNDERLINE));
    +    add(new TerminalCharacter('>', new TerminalColor(Color.GREEN, 
Color.DEFAULT), CharacterType.UNDERLINE));
    +    add(new TerminalCharacter('>', new TerminalColor(Color.GREEN, 
Color.DEFAULT), CharacterType.UNDERLINE));
    +    add(new TerminalCharacter(' ', new TerminalColor(Color.DEFAULT, 
Color.DEFAULT)));
    +  }};
    +
    +  public static final String ERROR_PROMPT = "[!] ";
    +  public static final String STELLAR_PROPERTIES_FILENAME = 
"stellar.properties";
    +
    +  /**
    +   * Executes Stellar expressions for the shell environment.
    +   */
    +  private StellarShellExecutor executor;
    +
    +  /**
    +   * The Aesh shell console.
    +   */
    +  private Console console;
    +
    +  /**
    +   * Provides auto-complete functionality.
    +   */
    +  private StellarAutoCompleter autoCompleter;
    +
    +  /**
    +   * Execute the Stellar REPL.
    +   */
    +  public static void main(String[] args) throws Exception {
    +    StellarShell shell = new StellarShell(args);
    +    shell.run();
    +  }
    +
    +  /**
    +   * Create a Stellar REPL.
    +   * @param args The commmand-line arguments.
    +   */
    +  public StellarShell(String[] args) throws Exception {
    +
    +    // define valid command-line options
    +    CommandLineParser parser = new PosixParser();
    +    Options options = defineCommandLineOptions();
    +    CommandLine commandLine = parser.parse(options, args);
    +
    +    // print help
    +    if(commandLine.hasOption("h")) {
    +      HelpFormatter formatter = new HelpFormatter();
    +      formatter.printHelp("stellar", options);
    +      System.exit(0);
    +    }
    +
    +    // validate the command line options
    +    try {
    +      StellarShellOptionsValidator.validateOptions(commandLine);
    +
    +    } catch(IllegalArgumentException e){
    +      System.err.println(e.getMessage());
    +      System.exit(1);
    +    }
    +
    +    // setup logging, if specified
    +    if(commandLine.hasOption("l")) {
    +      PropertyConfigurator.configure(commandLine.getOptionValue("l"));
    +    }
    +
    +    console = createConsole(commandLine);
    +    autoCompleter = new DefaultStellarAutoCompleter();
    +    executor = createExecutor(commandLine, console, 
getStellarProperties(commandLine), autoCompleter);
    +
    +    loadVariables(commandLine, executor);
    +    console.setPrompt(new Prompt(EXPRESSION_PROMPT));
    +    console.addCompletion(this);
    +    console.setConsoleCallback(this);
    +  }
    +
    +  /**
    +   * @return The valid command line options.
    +   */
    +  private Options defineCommandLineOptions() {
    +    Options options = new Options();
    +    options.addOption(
    +            "z",
    +            "zookeeper",
    +            true,
    +            "Zookeeper URL fragment in the form 
[HOSTNAME|IPADDRESS]:PORT");
    +    options.addOption(
    +            "v",
    +            "variables",
    +            true,
    +            "File containing a JSON Map of variables");
    +    options.addOption(
    +            "irc",
    +            "inputrc",
    +            true,
    +            "File containing the inputrc if not the default ~/.inputrc");
    +    options.addOption(
    +            "na",
    +            "no_ansi",
    +            false,
    +            "Make the input prompt not use ANSI colors.");
    +    options.addOption(
    +            "h",
    +            "help",
    +            false,
    +            "Print help");
    +    options.addOption(
    +            "p",
    +            "properties",
    +            true,
    +            "File containing Stellar properties");
    +    Option log4j = new Option(
    +            "l",
    +            "log4j",
    +            true,
    +            "The log4j properties file to load");
    +    log4j.setArgName("FILE");
    +    log4j.setRequired(false);
    +    options.addOption(log4j);
    +
    +    return options;
    +  }
    +
    +  /**
    +   * Loads any variables defined in an external file.
    +   * @param commandLine The command line arguments.
    +   * @param executor The stellar executor.
    +   * @throws IOException
    +   */
    +  private static void loadVariables(
    +          CommandLine commandLine,
    +          StellarShellExecutor executor) throws IOException {
    +
    +    if(commandLine.hasOption("v")) {
    +
    +      // load variables defined in a file
    +      String variablePath = commandLine.getOptionValue("v");
    +      Map<String, Object> variables = JSONUtils.INSTANCE.load(
    +              new File(variablePath),
    +              new TypeReference<Map<String, Object>>() {});
    +
    +      // for each variable...
    +      for(Map.Entry<String, Object> kv : variables.entrySet()) {
    +        String variable = kv.getKey();
    +        Object value = kv.getValue();
    +
    +        // define the variable - no expression available
    +        executor.assign(variable, value, Optional.empty());
    +      }
    +    }
    +  }
    +
    +  /**
    +   * Creates the Stellar execution environment.
    +   * @param commandLine The command line arguments.
    +   * @param console The console which drives the REPL.
    +   * @param properties Stellar properties.
    +   */
    +  private StellarShellExecutor createExecutor(
    +          CommandLine commandLine,
    +          Console console,
    +          Properties properties,
    +          StellarAutoCompleter autoCompleter) throws Exception {
    +
    +    Optional<String> zookeeperUrl = Optional.empty();
    +    if(commandLine.hasOption("z")) {
    +      zookeeperUrl = Optional.of(commandLine.getOptionValue("z"));
    +    }
    +
    +    StellarShellExecutor executor = new 
DefaultStellarShellExecutor(properties, zookeeperUrl);
    +
    +    // the CONSOLE capability is only available with the Aesh-driven REPL
    +    executor.getContext().addCapability(CONSOLE, () -> console);
    +
    +    // allows some Stellar functions to access Stellar internals; should 
probably use %magics instead
    +    executor.getContext().addCapability(SHELL_VARIABLES, () -> 
executor.getState());
    +
    +    // register the auto-completer to be notified when needed
    +    executor.addSpecialListener((magic) -> 
autoCompleter.addCandidateFunction(magic.getCommand()));
    +    executor.addFunctionListener((fn) -> 
autoCompleter.addCandidateFunction(fn.getName()));
    +    executor.addVariableListener((name, val) -> 
autoCompleter.addCandidateVariable(name));
    +
    +    executor.init();
    +    return executor;
    +  }
    +
    +  /**
    +   * Creates the REPL's console.
    +   * @param commandLine The command line options.
    +   */
    +  private Console createConsole(CommandLine commandLine) {
    +
    +    // console settings
    +    boolean useAnsi = !commandLine.hasOption("na");
    +    SettingsBuilder settings = new SettingsBuilder().enableAlias(true)
    +                                                    .enableMan(true)
    +                                                    .ansi(useAnsi)
    +                                                    .parseOperators(false)
    +                                                    
.inputStream(PausableInput.INSTANCE);
    +
    +    if(commandLine.hasOption("irc")) {
    +      settings = settings.inputrc(new 
File(commandLine.getOptionValue("irc")));
    +    }
    +
    +    return new Console(settings.create());
    +  }
    +
    +  /**
    +   * Retrieves the Stellar properties. The properties are either loaded 
from a file in
    +   * the classpath or a set of defaults are used.
    +   */
    +  private Properties getStellarProperties(CommandLine commandLine) throws 
IOException {
    +    Properties properties = new Properties();
    +
    +    if (commandLine.hasOption("p")) {
    +      // attempt to load properties from a file specified on the 
command-line
    +      try (InputStream in = new 
FileInputStream(commandLine.getOptionValue("p"))) {
    +        if(in != null) {
    +          properties.load(in);
    +        }
    +      }
    +
    +    } else {
    +      // otherwise attempt to load properties from the classpath
    +      try (InputStream in = 
getClass().getClassLoader().getResourceAsStream(STELLAR_PROPERTIES_FILENAME)) {
    +        if(in != null) {
    +          properties.load(in);
    +        }
    +      }
    +    }
    +
    +    return properties;
    +  }
    +
    +  /**
    +   * Handles the main loop for the REPL.
    +   */
    +  public void run() {
    +    // welcome message
    +    writeLine(WELCOME);
    +
    +    // print the globals if we got 'em
    +    executor.getContext()
    +            .getCapability(GLOBAL_CONFIG, false)
    +            .ifPresent(conf -> writeLine(conf.toString()));
    +
    +    console.start();
    +  }
    +
    +  /**
    +   * Quits the console.
    +   */
    +  private void handleQuit() {
    +    try {
    +      console.stop();
    +    } catch (Throwable e) {
    +      e.printStackTrace();
    +    }
    +  }
    +
    +  private void writeLine(String out) {
    +    console.getShell().out().println(out);
    +  }
    +
    +  @Override
    +  public int execute(ConsoleOperation output) throws InterruptedException {
    +
    +    // grab the expression
    +    String expression = output.getBuffer().trim();
    +    if(StringUtils.isNotBlank(expression) ) {
    +
    +      // execute the expression
    +      StellarShellResult result = executor.execute(expression);
    +
    +      if(result.isSuccess()) {
    +        result.getValue().ifPresent(v -> 
writeLine(ConversionUtils.convert(v, String.class)));
    +
    +      } else if (result.isError()) {
    +        result.getException().ifPresent(e -> writeLine(ERROR_PROMPT + 
e.getMessage()));
    +        result.getException().ifPresent(e -> e.printStackTrace());
    +
    +      } else if(result.isTerminate()) {
    +        handleQuit();
    +
    +      } else {
    +        throw new IllegalStateException("An execution result is neither a 
success nor a failure. Please file a bug report.");
    +      }
    +    }
    +
    +    return 0;
    +  }
    +
    +  /**
    +   * Performs auto-completion for the shell.
    +   * @param completeOperation The auto-complete operation.
    +   */
    +  @Override
    +  public void complete(CompleteOperation completeOperation) {
    +    String buffer = completeOperation.getBuffer();
    --- End diff --
    
    Ties our generic autocompleter to the autocomplete interface required by 
Aesh.


---

Reply via email to