You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@accumulo.apache.org by el...@apache.org on 2015/01/14 00:19:18 UTC

[1/6] accumulo git commit: ACCUMULO-3475 Remove configError and use config(String...) retval

Repository: accumulo
Updated Branches:
  refs/heads/1.5 cfb832a1f -> 7651b7772
  refs/heads/1.6 c6e771e9d -> 410e6a2de
  refs/heads/master cb288713f -> 23ce1c7ed


ACCUMULO-3475 Remove configError and use config(String...) retval

The return value of config(String...) is ignored, using the
configError member instead. We can get rid of that member and
use the retval instead to denote the same thing.


Project: http://git-wip-us.apache.org/repos/asf/accumulo/repo
Commit: http://git-wip-us.apache.org/repos/asf/accumulo/commit/7651b777
Tree: http://git-wip-us.apache.org/repos/asf/accumulo/tree/7651b777
Diff: http://git-wip-us.apache.org/repos/asf/accumulo/diff/7651b777

Branch: refs/heads/1.5
Commit: 7651b77724481811b967b36ec6d164119edcd5e1
Parents: cfb832a
Author: Josh Elser <jo...@gmail.com>
Authored: Tue Jan 13 15:18:13 2015 -0500
Committer: Josh Elser <jo...@gmail.com>
Committed: Tue Jan 13 15:47:02 2015 -0500

----------------------------------------------------------------------
 .../accumulo/core/client/mock/MockShell.java    | 13 +++++-----
 .../apache/accumulo/core/util/shell/Shell.java  | 27 +++++++++++---------
 .../shell/command/FormatterCommandTest.java     |  4 ++-
 3 files changed, 25 insertions(+), 19 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/accumulo/blob/7651b777/core/src/main/java/org/apache/accumulo/core/client/mock/MockShell.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/accumulo/core/client/mock/MockShell.java b/core/src/main/java/org/apache/accumulo/core/client/mock/MockShell.java
index 5ff340b..0fbe879 100644
--- a/core/src/main/java/org/apache/accumulo/core/client/mock/MockShell.java
+++ b/core/src/main/java/org/apache/accumulo/core/client/mock/MockShell.java
@@ -44,15 +44,19 @@ public class MockShell extends Shell {
     this.writer = writer;
   }
 
+  @Override
   public boolean config(String... args) {
-    configError = super.config(args);
+    // If configuring the shell failed, fail quickly
+    if (!super.config(args)) {
+      return false;
+    }
 
     // Update the ConsoleReader with the input and output "redirected"
     try {
       this.reader = new ConsoleReader(in, writer);
     } catch (Exception e) {
       printException(e);
-      configError = true;
+      return false;
     }
 
     // Don't need this for testing purposes
@@ -61,7 +65,7 @@ public class MockShell extends Shell {
 
     // Make the parsing from the client easier;
     this.verbose = false;
-    return configError;
+    return true;
   }
 
   @Override
@@ -71,9 +75,6 @@ public class MockShell extends Shell {
   }
 
   public int start() throws IOException {
-    if (configError)
-      return 1;
-
     String input;
     if (isVerbose())
       printInfo();

http://git-wip-us.apache.org/repos/asf/accumulo/blob/7651b777/core/src/main/java/org/apache/accumulo/core/util/shell/Shell.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/accumulo/core/util/shell/Shell.java b/core/src/main/java/org/apache/accumulo/core/util/shell/Shell.java
index cc2053f..808d340 100644
--- a/core/src/main/java/org/apache/accumulo/core/util/shell/Shell.java
+++ b/core/src/main/java/org/apache/accumulo/core/util/shell/Shell.java
@@ -185,7 +185,6 @@ public class Shell extends ShellOptions {
   private Token rootToken;
   public final Map<String,Command> commandFactory = new TreeMap<String,Command>();
   public final Map<String,Command[]> commandGrouping = new TreeMap<String,Command[]>();
-  protected boolean configError = false;
 
   // exit if true
   private boolean exit = false;
@@ -215,7 +214,11 @@ public class Shell extends ShellOptions {
     this.writer = writer;
   }
 
-  // Not for client use
+  /**
+   * Configures the shell using the provided options. Not for client use.
+   *
+   * @return true if the shell was successfully configured, false otherwise.
+   */
   public boolean config(String... args) {
 
     CommandLine cl;
@@ -225,9 +228,9 @@ public class Shell extends ShellOptions {
         throw new ParseException("Unrecognized arguments: " + cl.getArgList());
 
       if (cl.hasOption(helpOpt.getOpt())) {
-        configError = true;
         printHelp("shell", SHELL_DESCRIPTION, opts);
-        return true;
+        exitCode = 0;
+        return false;
       }
 
       setDebugging(cl.hasOption(debugOption.getLongOpt()));
@@ -238,10 +241,10 @@ public class Shell extends ShellOptions {
         throw new MissingArgumentException(zooKeeperInstance);
 
     } catch (Exception e) {
-      configError = true;
       printException(e);
       printHelp("shell", SHELL_DESCRIPTION, opts);
-      return true;
+      exitCode = 1;
+      return false;
     }
 
     // get the options that were parsed
@@ -316,7 +319,8 @@ public class Shell extends ShellOptions {
 
     } catch (Exception e) {
       printException(e);
-      configError = true;
+      exitCode = 1;
+      return false;
     }
 
     // decide whether to execute commands from a file and quit
@@ -373,7 +377,7 @@ public class Shell extends ShellOptions {
     for (Command cmd : otherCommands) {
       commandFactory.put(cmd.getName(), cmd);
     }
-    return configError;
+    return true;
   }
 
   protected void setInstance(CommandLine cl) {
@@ -408,15 +412,14 @@ public class Shell extends ShellOptions {
 
   public static void main(String args[]) throws IOException {
     Shell shell = new Shell();
-    shell.config(args);
+    if (!shell.config(args)) {
+      System.exit(shell.getExitCode());
+    }
 
     System.exit(shell.start());
   }
 
   public int start() throws IOException {
-    if (configError)
-      return 1;
-
     String input;
     if (isVerbose())
       printInfo();

http://git-wip-us.apache.org/repos/asf/accumulo/blob/7651b777/core/src/test/java/org/apache/accumulo/core/util/shell/command/FormatterCommandTest.java
----------------------------------------------------------------------
diff --git a/core/src/test/java/org/apache/accumulo/core/util/shell/command/FormatterCommandTest.java b/core/src/test/java/org/apache/accumulo/core/util/shell/command/FormatterCommandTest.java
index b99cf60..c615e48 100644
--- a/core/src/test/java/org/apache/accumulo/core/util/shell/command/FormatterCommandTest.java
+++ b/core/src/test/java/org/apache/accumulo/core/util/shell/command/FormatterCommandTest.java
@@ -16,6 +16,8 @@
  */
 package org.apache.accumulo.core.util.shell.command;
 
+import static org.junit.Assert.assertTrue;
+
 import java.io.IOException;
 import java.io.InputStream;
 import java.io.StringWriter;
@@ -57,7 +59,7 @@ public class FormatterCommandTest {
     writer = new StringWriter();
 
     final MockShell shell = new MockShell(in, writer);
-    shell.config(args);
+    assertTrue("Failed to configure shell without error", shell.config(args));
 
     // Can't call createtable in the shell with MockAccumulo
     shell.getConnector().tableOperations().create("test");


[3/6] accumulo git commit: ACCUMULO-3475 Remove configError and use config(String...) retval

Posted by el...@apache.org.
ACCUMULO-3475 Remove configError and use config(String...) retval

The return value of config(String...) is ignored, using the
configError member instead. We can get rid of that member and
use the retval instead to denote the same thing.


Project: http://git-wip-us.apache.org/repos/asf/accumulo/repo
Commit: http://git-wip-us.apache.org/repos/asf/accumulo/commit/7651b777
Tree: http://git-wip-us.apache.org/repos/asf/accumulo/tree/7651b777
Diff: http://git-wip-us.apache.org/repos/asf/accumulo/diff/7651b777

Branch: refs/heads/master
Commit: 7651b77724481811b967b36ec6d164119edcd5e1
Parents: cfb832a
Author: Josh Elser <jo...@gmail.com>
Authored: Tue Jan 13 15:18:13 2015 -0500
Committer: Josh Elser <jo...@gmail.com>
Committed: Tue Jan 13 15:47:02 2015 -0500

----------------------------------------------------------------------
 .../accumulo/core/client/mock/MockShell.java    | 13 +++++-----
 .../apache/accumulo/core/util/shell/Shell.java  | 27 +++++++++++---------
 .../shell/command/FormatterCommandTest.java     |  4 ++-
 3 files changed, 25 insertions(+), 19 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/accumulo/blob/7651b777/core/src/main/java/org/apache/accumulo/core/client/mock/MockShell.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/accumulo/core/client/mock/MockShell.java b/core/src/main/java/org/apache/accumulo/core/client/mock/MockShell.java
index 5ff340b..0fbe879 100644
--- a/core/src/main/java/org/apache/accumulo/core/client/mock/MockShell.java
+++ b/core/src/main/java/org/apache/accumulo/core/client/mock/MockShell.java
@@ -44,15 +44,19 @@ public class MockShell extends Shell {
     this.writer = writer;
   }
 
+  @Override
   public boolean config(String... args) {
-    configError = super.config(args);
+    // If configuring the shell failed, fail quickly
+    if (!super.config(args)) {
+      return false;
+    }
 
     // Update the ConsoleReader with the input and output "redirected"
     try {
       this.reader = new ConsoleReader(in, writer);
     } catch (Exception e) {
       printException(e);
-      configError = true;
+      return false;
     }
 
     // Don't need this for testing purposes
@@ -61,7 +65,7 @@ public class MockShell extends Shell {
 
     // Make the parsing from the client easier;
     this.verbose = false;
-    return configError;
+    return true;
   }
 
   @Override
@@ -71,9 +75,6 @@ public class MockShell extends Shell {
   }
 
   public int start() throws IOException {
-    if (configError)
-      return 1;
-
     String input;
     if (isVerbose())
       printInfo();

http://git-wip-us.apache.org/repos/asf/accumulo/blob/7651b777/core/src/main/java/org/apache/accumulo/core/util/shell/Shell.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/accumulo/core/util/shell/Shell.java b/core/src/main/java/org/apache/accumulo/core/util/shell/Shell.java
index cc2053f..808d340 100644
--- a/core/src/main/java/org/apache/accumulo/core/util/shell/Shell.java
+++ b/core/src/main/java/org/apache/accumulo/core/util/shell/Shell.java
@@ -185,7 +185,6 @@ public class Shell extends ShellOptions {
   private Token rootToken;
   public final Map<String,Command> commandFactory = new TreeMap<String,Command>();
   public final Map<String,Command[]> commandGrouping = new TreeMap<String,Command[]>();
-  protected boolean configError = false;
 
   // exit if true
   private boolean exit = false;
@@ -215,7 +214,11 @@ public class Shell extends ShellOptions {
     this.writer = writer;
   }
 
-  // Not for client use
+  /**
+   * Configures the shell using the provided options. Not for client use.
+   *
+   * @return true if the shell was successfully configured, false otherwise.
+   */
   public boolean config(String... args) {
 
     CommandLine cl;
@@ -225,9 +228,9 @@ public class Shell extends ShellOptions {
         throw new ParseException("Unrecognized arguments: " + cl.getArgList());
 
       if (cl.hasOption(helpOpt.getOpt())) {
-        configError = true;
         printHelp("shell", SHELL_DESCRIPTION, opts);
-        return true;
+        exitCode = 0;
+        return false;
       }
 
       setDebugging(cl.hasOption(debugOption.getLongOpt()));
@@ -238,10 +241,10 @@ public class Shell extends ShellOptions {
         throw new MissingArgumentException(zooKeeperInstance);
 
     } catch (Exception e) {
-      configError = true;
       printException(e);
       printHelp("shell", SHELL_DESCRIPTION, opts);
-      return true;
+      exitCode = 1;
+      return false;
     }
 
     // get the options that were parsed
@@ -316,7 +319,8 @@ public class Shell extends ShellOptions {
 
     } catch (Exception e) {
       printException(e);
-      configError = true;
+      exitCode = 1;
+      return false;
     }
 
     // decide whether to execute commands from a file and quit
@@ -373,7 +377,7 @@ public class Shell extends ShellOptions {
     for (Command cmd : otherCommands) {
       commandFactory.put(cmd.getName(), cmd);
     }
-    return configError;
+    return true;
   }
 
   protected void setInstance(CommandLine cl) {
@@ -408,15 +412,14 @@ public class Shell extends ShellOptions {
 
   public static void main(String args[]) throws IOException {
     Shell shell = new Shell();
-    shell.config(args);
+    if (!shell.config(args)) {
+      System.exit(shell.getExitCode());
+    }
 
     System.exit(shell.start());
   }
 
   public int start() throws IOException {
-    if (configError)
-      return 1;
-
     String input;
     if (isVerbose())
       printInfo();

http://git-wip-us.apache.org/repos/asf/accumulo/blob/7651b777/core/src/test/java/org/apache/accumulo/core/util/shell/command/FormatterCommandTest.java
----------------------------------------------------------------------
diff --git a/core/src/test/java/org/apache/accumulo/core/util/shell/command/FormatterCommandTest.java b/core/src/test/java/org/apache/accumulo/core/util/shell/command/FormatterCommandTest.java
index b99cf60..c615e48 100644
--- a/core/src/test/java/org/apache/accumulo/core/util/shell/command/FormatterCommandTest.java
+++ b/core/src/test/java/org/apache/accumulo/core/util/shell/command/FormatterCommandTest.java
@@ -16,6 +16,8 @@
  */
 package org.apache.accumulo.core.util.shell.command;
 
+import static org.junit.Assert.assertTrue;
+
 import java.io.IOException;
 import java.io.InputStream;
 import java.io.StringWriter;
@@ -57,7 +59,7 @@ public class FormatterCommandTest {
     writer = new StringWriter();
 
     final MockShell shell = new MockShell(in, writer);
-    shell.config(args);
+    assertTrue("Failed to configure shell without error", shell.config(args));
 
     // Can't call createtable in the shell with MockAccumulo
     shell.getConnector().tableOperations().create("test");


[4/6] accumulo git commit: Merge branch '1.5' into 1.6

Posted by el...@apache.org.
Merge branch '1.5' into 1.6

Conflicts:
	core/src/main/java/org/apache/accumulo/core/util/shell/Shell.java
	core/src/test/java/org/apache/accumulo/core/util/shell/commands/FormatterCommandTest.java


Project: http://git-wip-us.apache.org/repos/asf/accumulo/repo
Commit: http://git-wip-us.apache.org/repos/asf/accumulo/commit/410e6a2d
Tree: http://git-wip-us.apache.org/repos/asf/accumulo/tree/410e6a2d
Diff: http://git-wip-us.apache.org/repos/asf/accumulo/diff/410e6a2d

Branch: refs/heads/master
Commit: 410e6a2de0ed15fa9774795bbd64d43c15e6ad7a
Parents: c6e771e 7651b77
Author: Josh Elser <jo...@gmail.com>
Authored: Tue Jan 13 18:09:51 2015 -0500
Committer: Josh Elser <jo...@gmail.com>
Committed: Tue Jan 13 18:09:51 2015 -0500

----------------------------------------------------------------------
 .../accumulo/core/client/mock/MockShell.java    | 13 ++++---
 .../apache/accumulo/core/util/shell/Shell.java  | 39 +++++++++++---------
 .../core/util/shell/ShellConfigTest.java        | 10 ++---
 .../shell/commands/FormatterCommandTest.java    |  4 +-
 4 files changed, 37 insertions(+), 29 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/accumulo/blob/410e6a2d/core/src/main/java/org/apache/accumulo/core/client/mock/MockShell.java
----------------------------------------------------------------------
diff --cc core/src/main/java/org/apache/accumulo/core/client/mock/MockShell.java
index 3aac8ca,0fbe879..b6c532a
--- a/core/src/main/java/org/apache/accumulo/core/client/mock/MockShell.java
+++ b/core/src/main/java/org/apache/accumulo/core/client/mock/MockShell.java
@@@ -38,41 -36,27 +38,45 @@@ public class MockShell extends Shell 
    private static final String NEWLINE = "\n";
  
    protected InputStream in;
 -  protected Writer writer;
 +  protected OutputStream out;
  
 -  public MockShell(InputStream in, Writer writer) throws IOException {
 +  /**
 +   * Will only be set if you use either the Writer constructor or the setWriter(Writer) method
 +   *
 +   * @deprecated since 1.6.0; use out
 +   */
 +  @Deprecated
 +  protected Writer writer = null;
 +
 +  public MockShell(InputStream in, OutputStream out) throws IOException {
      super();
      this.in = in;
 -    this.writer = writer;
 +    this.out = out;
 +    // we presume they don't use the writer field unless they use the other constructor.
 +  }
 +
 +  /**
 +   * @deprecated since 1.6.0; use OutputStream version
 +   */
 +  @Deprecated
 +  public MockShell(InputStream in, Writer out) throws IOException {
 +    this(in, new WriterOutputStream(out, UTF_8.name()));
 +    this.writer = out;
    }
  
+   @Override
    public boolean config(String... args) {
-     configError = super.config(args);
+     // If configuring the shell failed, fail quickly
+     if (!super.config(args)) {
+       return false;
+     }
  
      // Update the ConsoleReader with the input and output "redirected"
      try {
 -      this.reader = new ConsoleReader(in, writer);
 +      this.reader = new ConsoleReader(in, out);
      } catch (Exception e) {
        printException(e);
-       configError = true;
+       return false;
      }
  
      // Don't need this for testing purposes
@@@ -90,19 -74,7 +94,16 @@@
      instance = new MockInstance();
    }
  
 +  /**
 +   * @deprecated since 1.6.0; use ShellOptionsJC version
 +   */
 +  @Deprecated
 +  protected void setInstance(CommandLine cl) {
 +    // same result as in previous version
 +    setInstance((ShellOptionsJC) null);
 +  }
 +
    public int start() throws IOException {
-     if (configError)
-       return 1;
- 
      String input;
      if (isVerbose())
        printInfo();

http://git-wip-us.apache.org/repos/asf/accumulo/blob/410e6a2d/core/src/main/java/org/apache/accumulo/core/util/shell/Shell.java
----------------------------------------------------------------------
diff --cc core/src/main/java/org/apache/accumulo/core/util/shell/Shell.java
index aae0166,808d340..21e0470
--- a/core/src/main/java/org/apache/accumulo/core/util/shell/Shell.java
+++ b/core/src/main/java/org/apache/accumulo/core/util/shell/Shell.java
@@@ -239,49 -214,51 +238,55 @@@ public class Shell extends ShellOption
      this.writer = writer;
    }
  
-   // Not for client use
+   /**
+    * Configures the shell using the provided options. Not for client use.
+    *
+    * @return true if the shell was successfully configured, false otherwise.
+    */
    public boolean config(String... args) {
 +    ShellOptionsJC options = new ShellOptionsJC();
 +    JCommander jc = new JCommander();
  
 -    CommandLine cl;
 +    jc.setProgramName("accumulo shell");
 +    jc.addObject(options);
      try {
 -      cl = new BasicParser().parse(opts, args);
 -      if (cl.getArgs().length > 0)
 -        throw new ParseException("Unrecognized arguments: " + cl.getArgList());
 -
 -      if (cl.hasOption(helpOpt.getOpt())) {
 -        printHelp("shell", SHELL_DESCRIPTION, opts);
 -        exitCode = 0;
 -        return false;
 -      }
 -
 -      setDebugging(cl.hasOption(debugOption.getLongOpt()));
 -      authTimeout = TimeUnit.MINUTES.toNanos(Integer.parseInt(cl.getOptionValue(authTimeoutOpt.getLongOpt(), DEFAULT_AUTH_TIMEOUT)));
 -      disableAuthTimeout = cl.hasOption(disableAuthTimeoutOpt.getLongOpt());
 +      jc.parse(args);
 +    } catch (ParameterException e) {
-       configError = true;
++      jc.usage();
++      exitCode = 1;
++      return false;
 +    }
  
 -      if (cl.hasOption(zooKeeperInstance.getOpt()) && cl.getOptionValues(zooKeeperInstance.getOpt()).length != 2)
 -        throw new MissingArgumentException(zooKeeperInstance);
 +    if (options.isHelpEnabled()) {
-       configError = true;
++      jc.usage();
++      // Not an error
++      exitCode = 0;
++      return false;
 +    }
  
-     if (!configError && options.getUnrecognizedOptions() != null) {
-       configError = true;
 -    } catch (Exception e) {
 -      printException(e);
 -      printHelp("shell", SHELL_DESCRIPTION, opts);
++    if (options.getUnrecognizedOptions() != null) {
 +      logError("Unrecognized Options: " + options.getUnrecognizedOptions().toString());
-     }
- 
-     if (configError) {
 +      jc.usage();
-       return true;
+       exitCode = 1;
+       return false;
      }
  
 +    setDebugging(options.isDebugEnabled());
 +    authTimeout = TimeUnit.MINUTES.toNanos(options.getAuthTimeout());
 +    disableAuthTimeout = options.isAuthTimeoutDisabled();
 +
      // get the options that were parsed
 -    String sysUser = System.getProperty("user.name");
 -    if (sysUser == null)
 -      sysUser = "root";
 -    String user = cl.getOptionValue(usernameOption.getOpt(), sysUser);
 +    String user = options.getUsername();
 +    String password = options.getPassword();
  
 -    String passw = cl.getOptionValue(passwOption.getOpt(), null);
 -    tabCompletion = !cl.hasOption(tabCompleteOption.getLongOpt());
 -    String[] loginOptions = cl.getOptionValues(loginOption.getOpt());
 +    tabCompletion = !options.isTabCompletionDisabled();
  
      // Use a fake (Mock), ZK, or HdfsZK Accumulo instance
 -    setInstance(cl);
 +    setInstance(options);
 +
 +    // AuthenticationToken options
 +    token = options.getAuthenticationToken();
 +    Map<String,String> loginOptions = options.getTokenProperties();
  
      // process default parameters if unspecified
      try {
@@@ -391,37 -377,25 +397,37 @@@
      for (Command cmd : otherCommands) {
        commandFactory.put(cmd.getName(), cmd);
      }
-     return configError;
+     return true;
    }
  
 -  protected void setInstance(CommandLine cl) {
 -    // should only be one instance option set
 +  /**
 +   * Sets the instance used by the shell based on the given options.
 +   *
 +   * @param options
 +   *          shell options
 +   */
 +  protected void setInstance(ShellOptionsJC options) {
 +    // should only be one set of instance options set
      instance = null;
 -    if (cl.hasOption(fakeOption.getLongOpt())) {
 +    if (options.isFake()) {
        instance = new MockInstance("fake");
 -    } else if (cl.hasOption(hdfsZooInstance.getOpt())) {
 -      @SuppressWarnings("deprecation")
 -      AccumuloConfiguration deprecatedSiteConfiguration = AccumuloConfiguration.getSiteConfiguration();
 -      instance = getDefaultInstance(deprecatedSiteConfiguration);
 -    } else if (cl.hasOption(zooKeeperInstance.getOpt())) {
 -      String[] zkOpts = cl.getOptionValues(zooKeeperInstance.getOpt());
 -      instance = new ZooKeeperInstance(zkOpts[0], zkOpts[1]);
      } else {
 -      @SuppressWarnings("deprecation")
 -      AccumuloConfiguration deprecatedSiteConfiguration = AccumuloConfiguration.getSiteConfiguration();
 -      instance = getDefaultInstance(deprecatedSiteConfiguration);
 +      String instanceName, hosts;
 +      if (options.isHdfsZooInstance()) {
 +        instanceName = hosts = null;
 +      } else if (options.getZooKeeperInstance().size() > 0) {
 +        List<String> zkOpts = options.getZooKeeperInstance();
 +        instanceName = zkOpts.get(0);
 +        hosts = zkOpts.get(1);
 +      } else {
 +        instanceName = options.getZooKeeperInstanceName();
 +        hosts = options.getZooKeeperHosts();
 +      }
 +      try {
 +        instance = getZooInstance(instanceName, hosts, options.getClientConfiguration());
 +      } catch (Exception e) {
 +        throw new IllegalArgumentException("Unable to load client config from " + options.getClientConfigFile(), e);
 +      }
      }
    }
  
@@@ -455,72 -410,13 +461,74 @@@
      return connector;
    }
  
 +  public Instance getInstance() {
 +    return instance;
 +  }
 +
 +  public ClassLoader getClassLoader(final CommandLine cl, final Shell shellState) throws AccumuloException, TableNotFoundException, AccumuloSecurityException,
 +      IOException, FileSystemException {
 +
 +    boolean tables = cl.hasOption(OptUtil.tableOpt().getOpt()) || !shellState.getTableName().isEmpty();
 +    boolean namespaces = cl.hasOption(OptUtil.namespaceOpt().getOpt());
 +
 +    String classpath = null;
 +    Iterable<Entry<String,String>> tableProps;
 +
 +    if (namespaces) {
 +      try {
 +        tableProps = shellState.getConnector().namespaceOperations().getProperties(OptUtil.getNamespaceOpt(cl, shellState));
 +      } catch (NamespaceNotFoundException e) {
 +        throw new IllegalArgumentException(e);
 +      }
 +    } else if (tables) {
 +      tableProps = shellState.getConnector().tableOperations().getProperties(OptUtil.getTableOpt(cl, shellState));
 +    } else {
 +      throw new IllegalArgumentException("No table or namespace specified");
 +    }
 +    for (Entry<String,String> entry : tableProps) {
 +      if (entry.getKey().equals(Property.TABLE_CLASSPATH.getKey())) {
 +        classpath = entry.getValue();
 +      }
 +    }
 +
 +    ClassLoader classloader;
 +
 +    if (classpath != null && !classpath.equals("")) {
 +      shellState.getConnector().instanceOperations().getSystemConfiguration().get(Property.VFS_CONTEXT_CLASSPATH_PROPERTY.getKey() + classpath);
 +
 +      try {
 +        AccumuloVFSClassLoader.getContextManager().setContextConfig(new ContextManager.DefaultContextsConfig(new Iterable<Map.Entry<String,String>>() {
 +          @Override
 +          public Iterator<Entry<String,String>> iterator() {
 +            try {
 +              return shellState.getConnector().instanceOperations().getSystemConfiguration().entrySet().iterator();
 +            } catch (AccumuloException e) {
 +              throw new RuntimeException(e);
 +            } catch (AccumuloSecurityException e) {
 +              throw new RuntimeException(e);
 +            }
 +          }
 +        }));
 +      } catch (IllegalStateException ise) {}
 +
 +      classloader = AccumuloVFSClassLoader.getContextManager().getClassLoader(classpath);
 +    } else {
 +      classloader = AccumuloVFSClassLoader.getClassLoader();
 +    }
 +    return classloader;
 +  }
 +
    public static void main(String args[]) throws IOException {
      Shell shell = new Shell();
-     try {
-       shell.config(args);
 -    if (!shell.config(args)) {
 -      System.exit(shell.getExitCode());
 -    }
++    try{
++      if (!shell.config(args)) {
++        System.exit(shell.getExitCode());
++      }
  
 -    System.exit(shell.start());
 +      System.exit(shell.start());
 +    } finally {
 +      shell.shutdown();
 +    }
    }
  
    public int start() throws IOException {

http://git-wip-us.apache.org/repos/asf/accumulo/blob/410e6a2d/core/src/test/java/org/apache/accumulo/core/util/shell/ShellConfigTest.java
----------------------------------------------------------------------
diff --cc core/src/test/java/org/apache/accumulo/core/util/shell/ShellConfigTest.java
index ccb58fa,0000000..47deba6
mode 100644,000000..100644
--- a/core/src/test/java/org/apache/accumulo/core/util/shell/ShellConfigTest.java
+++ b/core/src/test/java/org/apache/accumulo/core/util/shell/ShellConfigTest.java
@@@ -1,90 -1,0 +1,90 @@@
 +/*
 + * 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.accumulo.core.util.shell;
 +
 +import static org.junit.Assert.assertFalse;
 +import static org.junit.Assert.assertTrue;
 +
 +import java.io.FileDescriptor;
 +import java.io.FileInputStream;
 +import java.io.PrintStream;
 +import java.io.PrintWriter;
 +
 +import jline.console.ConsoleReader;
 +
 +import org.apache.accumulo.core.client.security.tokens.PasswordToken;
 +import org.apache.accumulo.core.util.shell.ShellTest.TestOutputStream;
 +import org.apache.log4j.Level;
 +import org.junit.After;
 +import org.junit.Before;
 +import org.junit.Test;
 +
 +import com.beust.jcommander.ParameterException;
 +
 +public class ShellConfigTest {
 +  TestOutputStream output;
 +  Shell shell;
 +  PrintStream out;
 +
 +  @Before
 +  public void setUp() throws Exception {
 +    Shell.log.setLevel(Level.ERROR);
 +
 +    out = System.out;
 +    output = new TestOutputStream();
 +    System.setOut(new PrintStream(output));
 +
 +    shell = new Shell(new ConsoleReader(new FileInputStream(FileDescriptor.in), output), new PrintWriter(output));
 +    shell.setLogErrorsToConsole();
 +  }
 +
 +  @After
 +  public void teardown() throws Exception {
 +    shell.shutdown();
 +    output.clear();
 +    System.setOut(out);
 +  }
 +
 +  @Test
 +  public void testHelp() {
-     assertTrue(shell.config("--help"));
++    assertFalse(shell.config("--help"));
 +    assertTrue("Did not print usage", output.get().startsWith("Usage"));
 +  }
 +
 +  @Test
 +  public void testBadArg() {
-     assertTrue(shell.config("--bogus"));
++    assertFalse(shell.config("--bogus"));
 +    assertTrue("Did not print usage", output.get().startsWith("Usage"));
 +  }
 +
 +  @Test
 +  public void testTokenWithoutOptions() {
-     assertTrue(shell.config("--fake", "-tc", PasswordToken.class.getCanonicalName()));
++    assertFalse(shell.config("--fake", "-tc", PasswordToken.class.getCanonicalName()));
 +    assertFalse(output.get().contains(ParameterException.class.getCanonicalName()));
 +  }
 +
 +  @Test
 +  public void testTokenAndOption() {
-     assertFalse(shell.config("--fake", "-tc", PasswordToken.class.getCanonicalName(), "-u", "foo", "-l", "password=foo"));
++    assertTrue(shell.config("--fake", "-tc", PasswordToken.class.getCanonicalName(), "-u", "foo", "-l", "password=foo"));
 +  }
 +
 +  @Test
 +  public void testTokenAndOptionAndPassword() {
-     assertTrue(shell.config("--fake", "-tc", PasswordToken.class.getCanonicalName(), "-l", "password=foo", "-p", "bar"));
++    assertFalse(shell.config("--fake", "-tc", PasswordToken.class.getCanonicalName(), "-l", "password=foo", "-p", "bar"));
 +    assertTrue(output.get().contains(ParameterException.class.getCanonicalName()));
 +  }
 +}

http://git-wip-us.apache.org/repos/asf/accumulo/blob/410e6a2d/core/src/test/java/org/apache/accumulo/core/util/shell/commands/FormatterCommandTest.java
----------------------------------------------------------------------
diff --cc core/src/test/java/org/apache/accumulo/core/util/shell/commands/FormatterCommandTest.java
index f970941,0000000..683f11a
mode 100644,000000..100644
--- a/core/src/test/java/org/apache/accumulo/core/util/shell/commands/FormatterCommandTest.java
+++ b/core/src/test/java/org/apache/accumulo/core/util/shell/commands/FormatterCommandTest.java
@@@ -1,184 -1,0 +1,186 @@@
 +/*
 + * 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.accumulo.core.util.shell.commands;
 +
++import static org.junit.Assert.assertTrue;
++
 +import java.io.ByteArrayOutputStream;
 +import java.io.IOException;
 +import java.io.InputStream;
 +import java.util.Iterator;
 +import java.util.Map.Entry;
 +
 +import org.apache.accumulo.core.client.AccumuloException;
 +import org.apache.accumulo.core.client.AccumuloSecurityException;
 +import org.apache.accumulo.core.client.TableExistsException;
 +import org.apache.accumulo.core.client.mock.MockShell;
 +import org.apache.accumulo.core.data.Key;
 +import org.apache.accumulo.core.data.Value;
 +import org.apache.accumulo.core.util.format.Formatter;
 +import org.apache.accumulo.core.util.shell.Shell;
 +import org.apache.log4j.Level;
 +import org.apache.log4j.Logger;
 +import org.junit.Assert;
 +import org.junit.Test;
 +
 +/**
 + * Uses the MockShell to test the shell output with Formatters
 + */
 +public class FormatterCommandTest {
 +  ByteArrayOutputStream out = null;
 +  InputStream in = null;
 +
 +  @Test
 +  public void test() throws IOException, AccumuloException, AccumuloSecurityException, TableExistsException, ClassNotFoundException {
 +    // Keep the Shell AUDIT log off the test output
 +    Logger.getLogger(Shell.class).setLevel(Level.WARN);
 +
 +    final String[] args = new String[] {"--fake", "-u", "root", "-p", ""};
 +
 +    final String[] commands = createCommands();
 +
 +    in = MockShell.makeCommands(commands);
 +    out = new ByteArrayOutputStream();
 +
 +    final MockShell shell = new MockShell(in, out);
-     shell.config(args);
++    assertTrue("Failed to configure shell without error", shell.config(args));
 +
 +    // Can't call createtable in the shell with MockAccumulo
 +    shell.getConnector().tableOperations().create("test");
 +
 +    try {
 +      shell.start();
 +    } catch (Exception e) {
 +      Assert.fail("Exception while running commands: " + e.getMessage());
 +    }
 +
 +    shell.getReader().flush();
 +
 +    final String[] output = new String(out.toByteArray()).split("\n\r");
 +
 +    boolean formatterOn = false;
 +
 +    final String[] expectedDefault = new String[] {"row cf:cq []    1234abcd", "row cf1:cq1 []    9876fedc", "row2 cf:cq []    13579bdf",
 +        "row2 cf1:cq []    2468ace"};
 +
 +    final String[] expectedFormatted = new String[] {"row cf:cq []    0x31 0x32 0x33 0x34 0x61 0x62 0x63 0x64",
 +        "row cf1:cq1 []    0x39 0x38 0x37 0x36 0x66 0x65 0x64 0x63", "row2 cf:cq []    0x31 0x33 0x35 0x37 0x39 0x62 0x64 0x66",
 +        "row2 cf1:cq []    0x32 0x34 0x36 0x38 0x61 0x63 0x65"};
 +
 +    int outputIndex = 0;
 +    while (outputIndex < output.length) {
 +      final String line = output[outputIndex];
 +
 +      if (line.startsWith("root@mock-instance")) {
 +        if (line.contains("formatter")) {
 +          formatterOn = true;
 +        }
 +
 +        outputIndex++;
 +      } else if (line.startsWith("row")) {
 +        int expectedIndex = 0;
 +        String[] comparisonData;
 +
 +        // Pick the type of data we expect (formatted or default)
 +        if (formatterOn) {
 +          comparisonData = expectedFormatted;
 +        } else {
 +          comparisonData = expectedDefault;
 +        }
 +
 +        // Ensure each output is what we expected
 +        while (expectedIndex + outputIndex < output.length && expectedIndex < expectedFormatted.length) {
 +          Assert.assertEquals(comparisonData[expectedIndex].trim(), output[expectedIndex + outputIndex].trim());
 +          expectedIndex++;
 +        }
 +
 +        outputIndex += expectedIndex;
 +      }
 +    }
 +  }
 +
 +  private String[] createCommands() {
 +    return new String[] {"table test", "insert row cf cq 1234abcd", "insert row cf1 cq1 9876fedc", "insert row2 cf cq 13579bdf", "insert row2 cf1 cq 2468ace",
 +        "scan", "formatter -t test -f org.apache.accumulo.core.util.shell.command.FormatterCommandTest$HexFormatter", "scan"};
 +  }
 +
 +  /**
 +   * <p>
 +   * Simple <code>Formatter</code> that will convert each character in the Value from decimal to hexadecimal. Will automatically skip over characters in the
 +   * value which do not fall within the [0-9,a-f] range.
 +   * </p>
 +   *
 +   * <p>
 +   * Example: <code>'0'</code> will be displayed as <code>'0x30'</code>
 +   * </p>
 +   */
 +  public static class HexFormatter implements Formatter {
 +    private Iterator<Entry<Key,Value>> iter = null;
 +    private boolean printTs = false;
 +
 +    private final static String tab = "\t";
 +    private final static String newline = "\n";
 +
 +    public HexFormatter() {}
 +
 +    @Override
 +    public boolean hasNext() {
 +      return this.iter.hasNext();
 +    }
 +
 +    @Override
 +    public String next() {
 +      final Entry<Key,Value> entry = iter.next();
 +
 +      String key;
 +
 +      // Observe the timestamps
 +      if (printTs) {
 +        key = entry.getKey().toString();
 +      } else {
 +        key = entry.getKey().toStringNoTime();
 +      }
 +
 +      final Value v = entry.getValue();
 +
 +      // Approximate how much space we'll need
 +      final StringBuilder sb = new StringBuilder(key.length() + v.getSize() * 5);
 +
 +      sb.append(key).append(tab);
 +
 +      for (byte b : v.get()) {
 +        if ((b >= 48 && b <= 57) || (b >= 97 || b <= 102)) {
 +          sb.append(String.format("0x%x ", Integer.valueOf(b)));
 +        }
 +      }
 +
 +      sb.append(newline);
 +
 +      return sb.toString();
 +    }
 +
 +    @Override
 +    public void remove() {}
 +
 +    @Override
 +    public void initialize(final Iterable<Entry<Key,Value>> scanner, final boolean printTimestamps) {
 +      this.iter = scanner.iterator();
 +      this.printTs = printTimestamps;
 +    }
 +  }
 +
 +}


[5/6] accumulo git commit: Merge branch '1.5' into 1.6

Posted by el...@apache.org.
Merge branch '1.5' into 1.6

Conflicts:
	core/src/main/java/org/apache/accumulo/core/util/shell/Shell.java
	core/src/test/java/org/apache/accumulo/core/util/shell/commands/FormatterCommandTest.java


Project: http://git-wip-us.apache.org/repos/asf/accumulo/repo
Commit: http://git-wip-us.apache.org/repos/asf/accumulo/commit/410e6a2d
Tree: http://git-wip-us.apache.org/repos/asf/accumulo/tree/410e6a2d
Diff: http://git-wip-us.apache.org/repos/asf/accumulo/diff/410e6a2d

Branch: refs/heads/1.6
Commit: 410e6a2de0ed15fa9774795bbd64d43c15e6ad7a
Parents: c6e771e 7651b77
Author: Josh Elser <jo...@gmail.com>
Authored: Tue Jan 13 18:09:51 2015 -0500
Committer: Josh Elser <jo...@gmail.com>
Committed: Tue Jan 13 18:09:51 2015 -0500

----------------------------------------------------------------------
 .../accumulo/core/client/mock/MockShell.java    | 13 ++++---
 .../apache/accumulo/core/util/shell/Shell.java  | 39 +++++++++++---------
 .../core/util/shell/ShellConfigTest.java        | 10 ++---
 .../shell/commands/FormatterCommandTest.java    |  4 +-
 4 files changed, 37 insertions(+), 29 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/accumulo/blob/410e6a2d/core/src/main/java/org/apache/accumulo/core/client/mock/MockShell.java
----------------------------------------------------------------------
diff --cc core/src/main/java/org/apache/accumulo/core/client/mock/MockShell.java
index 3aac8ca,0fbe879..b6c532a
--- a/core/src/main/java/org/apache/accumulo/core/client/mock/MockShell.java
+++ b/core/src/main/java/org/apache/accumulo/core/client/mock/MockShell.java
@@@ -38,41 -36,27 +38,45 @@@ public class MockShell extends Shell 
    private static final String NEWLINE = "\n";
  
    protected InputStream in;
 -  protected Writer writer;
 +  protected OutputStream out;
  
 -  public MockShell(InputStream in, Writer writer) throws IOException {
 +  /**
 +   * Will only be set if you use either the Writer constructor or the setWriter(Writer) method
 +   *
 +   * @deprecated since 1.6.0; use out
 +   */
 +  @Deprecated
 +  protected Writer writer = null;
 +
 +  public MockShell(InputStream in, OutputStream out) throws IOException {
      super();
      this.in = in;
 -    this.writer = writer;
 +    this.out = out;
 +    // we presume they don't use the writer field unless they use the other constructor.
 +  }
 +
 +  /**
 +   * @deprecated since 1.6.0; use OutputStream version
 +   */
 +  @Deprecated
 +  public MockShell(InputStream in, Writer out) throws IOException {
 +    this(in, new WriterOutputStream(out, UTF_8.name()));
 +    this.writer = out;
    }
  
+   @Override
    public boolean config(String... args) {
-     configError = super.config(args);
+     // If configuring the shell failed, fail quickly
+     if (!super.config(args)) {
+       return false;
+     }
  
      // Update the ConsoleReader with the input and output "redirected"
      try {
 -      this.reader = new ConsoleReader(in, writer);
 +      this.reader = new ConsoleReader(in, out);
      } catch (Exception e) {
        printException(e);
-       configError = true;
+       return false;
      }
  
      // Don't need this for testing purposes
@@@ -90,19 -74,7 +94,16 @@@
      instance = new MockInstance();
    }
  
 +  /**
 +   * @deprecated since 1.6.0; use ShellOptionsJC version
 +   */
 +  @Deprecated
 +  protected void setInstance(CommandLine cl) {
 +    // same result as in previous version
 +    setInstance((ShellOptionsJC) null);
 +  }
 +
    public int start() throws IOException {
-     if (configError)
-       return 1;
- 
      String input;
      if (isVerbose())
        printInfo();

http://git-wip-us.apache.org/repos/asf/accumulo/blob/410e6a2d/core/src/main/java/org/apache/accumulo/core/util/shell/Shell.java
----------------------------------------------------------------------
diff --cc core/src/main/java/org/apache/accumulo/core/util/shell/Shell.java
index aae0166,808d340..21e0470
--- a/core/src/main/java/org/apache/accumulo/core/util/shell/Shell.java
+++ b/core/src/main/java/org/apache/accumulo/core/util/shell/Shell.java
@@@ -239,49 -214,51 +238,55 @@@ public class Shell extends ShellOption
      this.writer = writer;
    }
  
-   // Not for client use
+   /**
+    * Configures the shell using the provided options. Not for client use.
+    *
+    * @return true if the shell was successfully configured, false otherwise.
+    */
    public boolean config(String... args) {
 +    ShellOptionsJC options = new ShellOptionsJC();
 +    JCommander jc = new JCommander();
  
 -    CommandLine cl;
 +    jc.setProgramName("accumulo shell");
 +    jc.addObject(options);
      try {
 -      cl = new BasicParser().parse(opts, args);
 -      if (cl.getArgs().length > 0)
 -        throw new ParseException("Unrecognized arguments: " + cl.getArgList());
 -
 -      if (cl.hasOption(helpOpt.getOpt())) {
 -        printHelp("shell", SHELL_DESCRIPTION, opts);
 -        exitCode = 0;
 -        return false;
 -      }
 -
 -      setDebugging(cl.hasOption(debugOption.getLongOpt()));
 -      authTimeout = TimeUnit.MINUTES.toNanos(Integer.parseInt(cl.getOptionValue(authTimeoutOpt.getLongOpt(), DEFAULT_AUTH_TIMEOUT)));
 -      disableAuthTimeout = cl.hasOption(disableAuthTimeoutOpt.getLongOpt());
 +      jc.parse(args);
 +    } catch (ParameterException e) {
-       configError = true;
++      jc.usage();
++      exitCode = 1;
++      return false;
 +    }
  
 -      if (cl.hasOption(zooKeeperInstance.getOpt()) && cl.getOptionValues(zooKeeperInstance.getOpt()).length != 2)
 -        throw new MissingArgumentException(zooKeeperInstance);
 +    if (options.isHelpEnabled()) {
-       configError = true;
++      jc.usage();
++      // Not an error
++      exitCode = 0;
++      return false;
 +    }
  
-     if (!configError && options.getUnrecognizedOptions() != null) {
-       configError = true;
 -    } catch (Exception e) {
 -      printException(e);
 -      printHelp("shell", SHELL_DESCRIPTION, opts);
++    if (options.getUnrecognizedOptions() != null) {
 +      logError("Unrecognized Options: " + options.getUnrecognizedOptions().toString());
-     }
- 
-     if (configError) {
 +      jc.usage();
-       return true;
+       exitCode = 1;
+       return false;
      }
  
 +    setDebugging(options.isDebugEnabled());
 +    authTimeout = TimeUnit.MINUTES.toNanos(options.getAuthTimeout());
 +    disableAuthTimeout = options.isAuthTimeoutDisabled();
 +
      // get the options that were parsed
 -    String sysUser = System.getProperty("user.name");
 -    if (sysUser == null)
 -      sysUser = "root";
 -    String user = cl.getOptionValue(usernameOption.getOpt(), sysUser);
 +    String user = options.getUsername();
 +    String password = options.getPassword();
  
 -    String passw = cl.getOptionValue(passwOption.getOpt(), null);
 -    tabCompletion = !cl.hasOption(tabCompleteOption.getLongOpt());
 -    String[] loginOptions = cl.getOptionValues(loginOption.getOpt());
 +    tabCompletion = !options.isTabCompletionDisabled();
  
      // Use a fake (Mock), ZK, or HdfsZK Accumulo instance
 -    setInstance(cl);
 +    setInstance(options);
 +
 +    // AuthenticationToken options
 +    token = options.getAuthenticationToken();
 +    Map<String,String> loginOptions = options.getTokenProperties();
  
      // process default parameters if unspecified
      try {
@@@ -391,37 -377,25 +397,37 @@@
      for (Command cmd : otherCommands) {
        commandFactory.put(cmd.getName(), cmd);
      }
-     return configError;
+     return true;
    }
  
 -  protected void setInstance(CommandLine cl) {
 -    // should only be one instance option set
 +  /**
 +   * Sets the instance used by the shell based on the given options.
 +   *
 +   * @param options
 +   *          shell options
 +   */
 +  protected void setInstance(ShellOptionsJC options) {
 +    // should only be one set of instance options set
      instance = null;
 -    if (cl.hasOption(fakeOption.getLongOpt())) {
 +    if (options.isFake()) {
        instance = new MockInstance("fake");
 -    } else if (cl.hasOption(hdfsZooInstance.getOpt())) {
 -      @SuppressWarnings("deprecation")
 -      AccumuloConfiguration deprecatedSiteConfiguration = AccumuloConfiguration.getSiteConfiguration();
 -      instance = getDefaultInstance(deprecatedSiteConfiguration);
 -    } else if (cl.hasOption(zooKeeperInstance.getOpt())) {
 -      String[] zkOpts = cl.getOptionValues(zooKeeperInstance.getOpt());
 -      instance = new ZooKeeperInstance(zkOpts[0], zkOpts[1]);
      } else {
 -      @SuppressWarnings("deprecation")
 -      AccumuloConfiguration deprecatedSiteConfiguration = AccumuloConfiguration.getSiteConfiguration();
 -      instance = getDefaultInstance(deprecatedSiteConfiguration);
 +      String instanceName, hosts;
 +      if (options.isHdfsZooInstance()) {
 +        instanceName = hosts = null;
 +      } else if (options.getZooKeeperInstance().size() > 0) {
 +        List<String> zkOpts = options.getZooKeeperInstance();
 +        instanceName = zkOpts.get(0);
 +        hosts = zkOpts.get(1);
 +      } else {
 +        instanceName = options.getZooKeeperInstanceName();
 +        hosts = options.getZooKeeperHosts();
 +      }
 +      try {
 +        instance = getZooInstance(instanceName, hosts, options.getClientConfiguration());
 +      } catch (Exception e) {
 +        throw new IllegalArgumentException("Unable to load client config from " + options.getClientConfigFile(), e);
 +      }
      }
    }
  
@@@ -455,72 -410,13 +461,74 @@@
      return connector;
    }
  
 +  public Instance getInstance() {
 +    return instance;
 +  }
 +
 +  public ClassLoader getClassLoader(final CommandLine cl, final Shell shellState) throws AccumuloException, TableNotFoundException, AccumuloSecurityException,
 +      IOException, FileSystemException {
 +
 +    boolean tables = cl.hasOption(OptUtil.tableOpt().getOpt()) || !shellState.getTableName().isEmpty();
 +    boolean namespaces = cl.hasOption(OptUtil.namespaceOpt().getOpt());
 +
 +    String classpath = null;
 +    Iterable<Entry<String,String>> tableProps;
 +
 +    if (namespaces) {
 +      try {
 +        tableProps = shellState.getConnector().namespaceOperations().getProperties(OptUtil.getNamespaceOpt(cl, shellState));
 +      } catch (NamespaceNotFoundException e) {
 +        throw new IllegalArgumentException(e);
 +      }
 +    } else if (tables) {
 +      tableProps = shellState.getConnector().tableOperations().getProperties(OptUtil.getTableOpt(cl, shellState));
 +    } else {
 +      throw new IllegalArgumentException("No table or namespace specified");
 +    }
 +    for (Entry<String,String> entry : tableProps) {
 +      if (entry.getKey().equals(Property.TABLE_CLASSPATH.getKey())) {
 +        classpath = entry.getValue();
 +      }
 +    }
 +
 +    ClassLoader classloader;
 +
 +    if (classpath != null && !classpath.equals("")) {
 +      shellState.getConnector().instanceOperations().getSystemConfiguration().get(Property.VFS_CONTEXT_CLASSPATH_PROPERTY.getKey() + classpath);
 +
 +      try {
 +        AccumuloVFSClassLoader.getContextManager().setContextConfig(new ContextManager.DefaultContextsConfig(new Iterable<Map.Entry<String,String>>() {
 +          @Override
 +          public Iterator<Entry<String,String>> iterator() {
 +            try {
 +              return shellState.getConnector().instanceOperations().getSystemConfiguration().entrySet().iterator();
 +            } catch (AccumuloException e) {
 +              throw new RuntimeException(e);
 +            } catch (AccumuloSecurityException e) {
 +              throw new RuntimeException(e);
 +            }
 +          }
 +        }));
 +      } catch (IllegalStateException ise) {}
 +
 +      classloader = AccumuloVFSClassLoader.getContextManager().getClassLoader(classpath);
 +    } else {
 +      classloader = AccumuloVFSClassLoader.getClassLoader();
 +    }
 +    return classloader;
 +  }
 +
    public static void main(String args[]) throws IOException {
      Shell shell = new Shell();
-     try {
-       shell.config(args);
 -    if (!shell.config(args)) {
 -      System.exit(shell.getExitCode());
 -    }
++    try{
++      if (!shell.config(args)) {
++        System.exit(shell.getExitCode());
++      }
  
 -    System.exit(shell.start());
 +      System.exit(shell.start());
 +    } finally {
 +      shell.shutdown();
 +    }
    }
  
    public int start() throws IOException {

http://git-wip-us.apache.org/repos/asf/accumulo/blob/410e6a2d/core/src/test/java/org/apache/accumulo/core/util/shell/ShellConfigTest.java
----------------------------------------------------------------------
diff --cc core/src/test/java/org/apache/accumulo/core/util/shell/ShellConfigTest.java
index ccb58fa,0000000..47deba6
mode 100644,000000..100644
--- a/core/src/test/java/org/apache/accumulo/core/util/shell/ShellConfigTest.java
+++ b/core/src/test/java/org/apache/accumulo/core/util/shell/ShellConfigTest.java
@@@ -1,90 -1,0 +1,90 @@@
 +/*
 + * 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.accumulo.core.util.shell;
 +
 +import static org.junit.Assert.assertFalse;
 +import static org.junit.Assert.assertTrue;
 +
 +import java.io.FileDescriptor;
 +import java.io.FileInputStream;
 +import java.io.PrintStream;
 +import java.io.PrintWriter;
 +
 +import jline.console.ConsoleReader;
 +
 +import org.apache.accumulo.core.client.security.tokens.PasswordToken;
 +import org.apache.accumulo.core.util.shell.ShellTest.TestOutputStream;
 +import org.apache.log4j.Level;
 +import org.junit.After;
 +import org.junit.Before;
 +import org.junit.Test;
 +
 +import com.beust.jcommander.ParameterException;
 +
 +public class ShellConfigTest {
 +  TestOutputStream output;
 +  Shell shell;
 +  PrintStream out;
 +
 +  @Before
 +  public void setUp() throws Exception {
 +    Shell.log.setLevel(Level.ERROR);
 +
 +    out = System.out;
 +    output = new TestOutputStream();
 +    System.setOut(new PrintStream(output));
 +
 +    shell = new Shell(new ConsoleReader(new FileInputStream(FileDescriptor.in), output), new PrintWriter(output));
 +    shell.setLogErrorsToConsole();
 +  }
 +
 +  @After
 +  public void teardown() throws Exception {
 +    shell.shutdown();
 +    output.clear();
 +    System.setOut(out);
 +  }
 +
 +  @Test
 +  public void testHelp() {
-     assertTrue(shell.config("--help"));
++    assertFalse(shell.config("--help"));
 +    assertTrue("Did not print usage", output.get().startsWith("Usage"));
 +  }
 +
 +  @Test
 +  public void testBadArg() {
-     assertTrue(shell.config("--bogus"));
++    assertFalse(shell.config("--bogus"));
 +    assertTrue("Did not print usage", output.get().startsWith("Usage"));
 +  }
 +
 +  @Test
 +  public void testTokenWithoutOptions() {
-     assertTrue(shell.config("--fake", "-tc", PasswordToken.class.getCanonicalName()));
++    assertFalse(shell.config("--fake", "-tc", PasswordToken.class.getCanonicalName()));
 +    assertFalse(output.get().contains(ParameterException.class.getCanonicalName()));
 +  }
 +
 +  @Test
 +  public void testTokenAndOption() {
-     assertFalse(shell.config("--fake", "-tc", PasswordToken.class.getCanonicalName(), "-u", "foo", "-l", "password=foo"));
++    assertTrue(shell.config("--fake", "-tc", PasswordToken.class.getCanonicalName(), "-u", "foo", "-l", "password=foo"));
 +  }
 +
 +  @Test
 +  public void testTokenAndOptionAndPassword() {
-     assertTrue(shell.config("--fake", "-tc", PasswordToken.class.getCanonicalName(), "-l", "password=foo", "-p", "bar"));
++    assertFalse(shell.config("--fake", "-tc", PasswordToken.class.getCanonicalName(), "-l", "password=foo", "-p", "bar"));
 +    assertTrue(output.get().contains(ParameterException.class.getCanonicalName()));
 +  }
 +}

http://git-wip-us.apache.org/repos/asf/accumulo/blob/410e6a2d/core/src/test/java/org/apache/accumulo/core/util/shell/commands/FormatterCommandTest.java
----------------------------------------------------------------------
diff --cc core/src/test/java/org/apache/accumulo/core/util/shell/commands/FormatterCommandTest.java
index f970941,0000000..683f11a
mode 100644,000000..100644
--- a/core/src/test/java/org/apache/accumulo/core/util/shell/commands/FormatterCommandTest.java
+++ b/core/src/test/java/org/apache/accumulo/core/util/shell/commands/FormatterCommandTest.java
@@@ -1,184 -1,0 +1,186 @@@
 +/*
 + * 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.accumulo.core.util.shell.commands;
 +
++import static org.junit.Assert.assertTrue;
++
 +import java.io.ByteArrayOutputStream;
 +import java.io.IOException;
 +import java.io.InputStream;
 +import java.util.Iterator;
 +import java.util.Map.Entry;
 +
 +import org.apache.accumulo.core.client.AccumuloException;
 +import org.apache.accumulo.core.client.AccumuloSecurityException;
 +import org.apache.accumulo.core.client.TableExistsException;
 +import org.apache.accumulo.core.client.mock.MockShell;
 +import org.apache.accumulo.core.data.Key;
 +import org.apache.accumulo.core.data.Value;
 +import org.apache.accumulo.core.util.format.Formatter;
 +import org.apache.accumulo.core.util.shell.Shell;
 +import org.apache.log4j.Level;
 +import org.apache.log4j.Logger;
 +import org.junit.Assert;
 +import org.junit.Test;
 +
 +/**
 + * Uses the MockShell to test the shell output with Formatters
 + */
 +public class FormatterCommandTest {
 +  ByteArrayOutputStream out = null;
 +  InputStream in = null;
 +
 +  @Test
 +  public void test() throws IOException, AccumuloException, AccumuloSecurityException, TableExistsException, ClassNotFoundException {
 +    // Keep the Shell AUDIT log off the test output
 +    Logger.getLogger(Shell.class).setLevel(Level.WARN);
 +
 +    final String[] args = new String[] {"--fake", "-u", "root", "-p", ""};
 +
 +    final String[] commands = createCommands();
 +
 +    in = MockShell.makeCommands(commands);
 +    out = new ByteArrayOutputStream();
 +
 +    final MockShell shell = new MockShell(in, out);
-     shell.config(args);
++    assertTrue("Failed to configure shell without error", shell.config(args));
 +
 +    // Can't call createtable in the shell with MockAccumulo
 +    shell.getConnector().tableOperations().create("test");
 +
 +    try {
 +      shell.start();
 +    } catch (Exception e) {
 +      Assert.fail("Exception while running commands: " + e.getMessage());
 +    }
 +
 +    shell.getReader().flush();
 +
 +    final String[] output = new String(out.toByteArray()).split("\n\r");
 +
 +    boolean formatterOn = false;
 +
 +    final String[] expectedDefault = new String[] {"row cf:cq []    1234abcd", "row cf1:cq1 []    9876fedc", "row2 cf:cq []    13579bdf",
 +        "row2 cf1:cq []    2468ace"};
 +
 +    final String[] expectedFormatted = new String[] {"row cf:cq []    0x31 0x32 0x33 0x34 0x61 0x62 0x63 0x64",
 +        "row cf1:cq1 []    0x39 0x38 0x37 0x36 0x66 0x65 0x64 0x63", "row2 cf:cq []    0x31 0x33 0x35 0x37 0x39 0x62 0x64 0x66",
 +        "row2 cf1:cq []    0x32 0x34 0x36 0x38 0x61 0x63 0x65"};
 +
 +    int outputIndex = 0;
 +    while (outputIndex < output.length) {
 +      final String line = output[outputIndex];
 +
 +      if (line.startsWith("root@mock-instance")) {
 +        if (line.contains("formatter")) {
 +          formatterOn = true;
 +        }
 +
 +        outputIndex++;
 +      } else if (line.startsWith("row")) {
 +        int expectedIndex = 0;
 +        String[] comparisonData;
 +
 +        // Pick the type of data we expect (formatted or default)
 +        if (formatterOn) {
 +          comparisonData = expectedFormatted;
 +        } else {
 +          comparisonData = expectedDefault;
 +        }
 +
 +        // Ensure each output is what we expected
 +        while (expectedIndex + outputIndex < output.length && expectedIndex < expectedFormatted.length) {
 +          Assert.assertEquals(comparisonData[expectedIndex].trim(), output[expectedIndex + outputIndex].trim());
 +          expectedIndex++;
 +        }
 +
 +        outputIndex += expectedIndex;
 +      }
 +    }
 +  }
 +
 +  private String[] createCommands() {
 +    return new String[] {"table test", "insert row cf cq 1234abcd", "insert row cf1 cq1 9876fedc", "insert row2 cf cq 13579bdf", "insert row2 cf1 cq 2468ace",
 +        "scan", "formatter -t test -f org.apache.accumulo.core.util.shell.command.FormatterCommandTest$HexFormatter", "scan"};
 +  }
 +
 +  /**
 +   * <p>
 +   * Simple <code>Formatter</code> that will convert each character in the Value from decimal to hexadecimal. Will automatically skip over characters in the
 +   * value which do not fall within the [0-9,a-f] range.
 +   * </p>
 +   *
 +   * <p>
 +   * Example: <code>'0'</code> will be displayed as <code>'0x30'</code>
 +   * </p>
 +   */
 +  public static class HexFormatter implements Formatter {
 +    private Iterator<Entry<Key,Value>> iter = null;
 +    private boolean printTs = false;
 +
 +    private final static String tab = "\t";
 +    private final static String newline = "\n";
 +
 +    public HexFormatter() {}
 +
 +    @Override
 +    public boolean hasNext() {
 +      return this.iter.hasNext();
 +    }
 +
 +    @Override
 +    public String next() {
 +      final Entry<Key,Value> entry = iter.next();
 +
 +      String key;
 +
 +      // Observe the timestamps
 +      if (printTs) {
 +        key = entry.getKey().toString();
 +      } else {
 +        key = entry.getKey().toStringNoTime();
 +      }
 +
 +      final Value v = entry.getValue();
 +
 +      // Approximate how much space we'll need
 +      final StringBuilder sb = new StringBuilder(key.length() + v.getSize() * 5);
 +
 +      sb.append(key).append(tab);
 +
 +      for (byte b : v.get()) {
 +        if ((b >= 48 && b <= 57) || (b >= 97 || b <= 102)) {
 +          sb.append(String.format("0x%x ", Integer.valueOf(b)));
 +        }
 +      }
 +
 +      sb.append(newline);
 +
 +      return sb.toString();
 +    }
 +
 +    @Override
 +    public void remove() {}
 +
 +    @Override
 +    public void initialize(final Iterable<Entry<Key,Value>> scanner, final boolean printTimestamps) {
 +      this.iter = scanner.iterator();
 +      this.printTs = printTimestamps;
 +    }
 +  }
 +
 +}


[2/6] accumulo git commit: ACCUMULO-3475 Remove configError and use config(String...) retval

Posted by el...@apache.org.
ACCUMULO-3475 Remove configError and use config(String...) retval

The return value of config(String...) is ignored, using the
configError member instead. We can get rid of that member and
use the retval instead to denote the same thing.


Project: http://git-wip-us.apache.org/repos/asf/accumulo/repo
Commit: http://git-wip-us.apache.org/repos/asf/accumulo/commit/7651b777
Tree: http://git-wip-us.apache.org/repos/asf/accumulo/tree/7651b777
Diff: http://git-wip-us.apache.org/repos/asf/accumulo/diff/7651b777

Branch: refs/heads/1.6
Commit: 7651b77724481811b967b36ec6d164119edcd5e1
Parents: cfb832a
Author: Josh Elser <jo...@gmail.com>
Authored: Tue Jan 13 15:18:13 2015 -0500
Committer: Josh Elser <jo...@gmail.com>
Committed: Tue Jan 13 15:47:02 2015 -0500

----------------------------------------------------------------------
 .../accumulo/core/client/mock/MockShell.java    | 13 +++++-----
 .../apache/accumulo/core/util/shell/Shell.java  | 27 +++++++++++---------
 .../shell/command/FormatterCommandTest.java     |  4 ++-
 3 files changed, 25 insertions(+), 19 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/accumulo/blob/7651b777/core/src/main/java/org/apache/accumulo/core/client/mock/MockShell.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/accumulo/core/client/mock/MockShell.java b/core/src/main/java/org/apache/accumulo/core/client/mock/MockShell.java
index 5ff340b..0fbe879 100644
--- a/core/src/main/java/org/apache/accumulo/core/client/mock/MockShell.java
+++ b/core/src/main/java/org/apache/accumulo/core/client/mock/MockShell.java
@@ -44,15 +44,19 @@ public class MockShell extends Shell {
     this.writer = writer;
   }
 
+  @Override
   public boolean config(String... args) {
-    configError = super.config(args);
+    // If configuring the shell failed, fail quickly
+    if (!super.config(args)) {
+      return false;
+    }
 
     // Update the ConsoleReader with the input and output "redirected"
     try {
       this.reader = new ConsoleReader(in, writer);
     } catch (Exception e) {
       printException(e);
-      configError = true;
+      return false;
     }
 
     // Don't need this for testing purposes
@@ -61,7 +65,7 @@ public class MockShell extends Shell {
 
     // Make the parsing from the client easier;
     this.verbose = false;
-    return configError;
+    return true;
   }
 
   @Override
@@ -71,9 +75,6 @@ public class MockShell extends Shell {
   }
 
   public int start() throws IOException {
-    if (configError)
-      return 1;
-
     String input;
     if (isVerbose())
       printInfo();

http://git-wip-us.apache.org/repos/asf/accumulo/blob/7651b777/core/src/main/java/org/apache/accumulo/core/util/shell/Shell.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/accumulo/core/util/shell/Shell.java b/core/src/main/java/org/apache/accumulo/core/util/shell/Shell.java
index cc2053f..808d340 100644
--- a/core/src/main/java/org/apache/accumulo/core/util/shell/Shell.java
+++ b/core/src/main/java/org/apache/accumulo/core/util/shell/Shell.java
@@ -185,7 +185,6 @@ public class Shell extends ShellOptions {
   private Token rootToken;
   public final Map<String,Command> commandFactory = new TreeMap<String,Command>();
   public final Map<String,Command[]> commandGrouping = new TreeMap<String,Command[]>();
-  protected boolean configError = false;
 
   // exit if true
   private boolean exit = false;
@@ -215,7 +214,11 @@ public class Shell extends ShellOptions {
     this.writer = writer;
   }
 
-  // Not for client use
+  /**
+   * Configures the shell using the provided options. Not for client use.
+   *
+   * @return true if the shell was successfully configured, false otherwise.
+   */
   public boolean config(String... args) {
 
     CommandLine cl;
@@ -225,9 +228,9 @@ public class Shell extends ShellOptions {
         throw new ParseException("Unrecognized arguments: " + cl.getArgList());
 
       if (cl.hasOption(helpOpt.getOpt())) {
-        configError = true;
         printHelp("shell", SHELL_DESCRIPTION, opts);
-        return true;
+        exitCode = 0;
+        return false;
       }
 
       setDebugging(cl.hasOption(debugOption.getLongOpt()));
@@ -238,10 +241,10 @@ public class Shell extends ShellOptions {
         throw new MissingArgumentException(zooKeeperInstance);
 
     } catch (Exception e) {
-      configError = true;
       printException(e);
       printHelp("shell", SHELL_DESCRIPTION, opts);
-      return true;
+      exitCode = 1;
+      return false;
     }
 
     // get the options that were parsed
@@ -316,7 +319,8 @@ public class Shell extends ShellOptions {
 
     } catch (Exception e) {
       printException(e);
-      configError = true;
+      exitCode = 1;
+      return false;
     }
 
     // decide whether to execute commands from a file and quit
@@ -373,7 +377,7 @@ public class Shell extends ShellOptions {
     for (Command cmd : otherCommands) {
       commandFactory.put(cmd.getName(), cmd);
     }
-    return configError;
+    return true;
   }
 
   protected void setInstance(CommandLine cl) {
@@ -408,15 +412,14 @@ public class Shell extends ShellOptions {
 
   public static void main(String args[]) throws IOException {
     Shell shell = new Shell();
-    shell.config(args);
+    if (!shell.config(args)) {
+      System.exit(shell.getExitCode());
+    }
 
     System.exit(shell.start());
   }
 
   public int start() throws IOException {
-    if (configError)
-      return 1;
-
     String input;
     if (isVerbose())
       printInfo();

http://git-wip-us.apache.org/repos/asf/accumulo/blob/7651b777/core/src/test/java/org/apache/accumulo/core/util/shell/command/FormatterCommandTest.java
----------------------------------------------------------------------
diff --git a/core/src/test/java/org/apache/accumulo/core/util/shell/command/FormatterCommandTest.java b/core/src/test/java/org/apache/accumulo/core/util/shell/command/FormatterCommandTest.java
index b99cf60..c615e48 100644
--- a/core/src/test/java/org/apache/accumulo/core/util/shell/command/FormatterCommandTest.java
+++ b/core/src/test/java/org/apache/accumulo/core/util/shell/command/FormatterCommandTest.java
@@ -16,6 +16,8 @@
  */
 package org.apache.accumulo.core.util.shell.command;
 
+import static org.junit.Assert.assertTrue;
+
 import java.io.IOException;
 import java.io.InputStream;
 import java.io.StringWriter;
@@ -57,7 +59,7 @@ public class FormatterCommandTest {
     writer = new StringWriter();
 
     final MockShell shell = new MockShell(in, writer);
-    shell.config(args);
+    assertTrue("Failed to configure shell without error", shell.config(args));
 
     // Can't call createtable in the shell with MockAccumulo
     shell.getConnector().tableOperations().create("test");


[6/6] accumulo git commit: Merge branch '1.6'

Posted by el...@apache.org.
Merge branch '1.6'


Project: http://git-wip-us.apache.org/repos/asf/accumulo/repo
Commit: http://git-wip-us.apache.org/repos/asf/accumulo/commit/23ce1c7e
Tree: http://git-wip-us.apache.org/repos/asf/accumulo/tree/23ce1c7e
Diff: http://git-wip-us.apache.org/repos/asf/accumulo/diff/23ce1c7e

Branch: refs/heads/master
Commit: 23ce1c7edb63d5b63ecb50efd142aa41aacfc37b
Parents: cb28871 410e6a2
Author: Josh Elser <jo...@gmail.com>
Authored: Tue Jan 13 18:09:57 2015 -0500
Committer: Josh Elser <jo...@gmail.com>
Committed: Tue Jan 13 18:09:57 2015 -0500

----------------------------------------------------------------------
 .../java/org/apache/accumulo/shell/Shell.java   | 39 +++++++++++---------
 .../apache/accumulo/shell/mock/MockShell.java   | 13 ++++---
 .../apache/accumulo/shell/ShellConfigTest.java  | 10 ++---
 .../shell/commands/FormatterCommandTest.java    |  4 +-
 4 files changed, 37 insertions(+), 29 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/accumulo/blob/23ce1c7e/shell/src/main/java/org/apache/accumulo/shell/Shell.java
----------------------------------------------------------------------
diff --cc shell/src/main/java/org/apache/accumulo/shell/Shell.java
index 9697a85,0000000..d897fc3
mode 100644,000000..100644
--- a/shell/src/main/java/org/apache/accumulo/shell/Shell.java
+++ b/shell/src/main/java/org/apache/accumulo/shell/Shell.java
@@@ -1,1227 -1,0 +1,1232 @@@
 +/*
 + * 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.accumulo.shell;
 +
 +import static java.nio.charset.StandardCharsets.ISO_8859_1;
 +import static java.nio.charset.StandardCharsets.UTF_8;
 +
 +import java.io.BufferedWriter;
 +import java.io.File;
 +import java.io.FileNotFoundException;
 +import java.io.FileOutputStream;
 +import java.io.IOException;
 +import java.io.OutputStreamWriter;
 +import java.io.PrintWriter;
 +import java.net.InetAddress;
 +import java.nio.charset.Charset;
 +import java.util.Arrays;
 +import java.util.Collections;
 +import java.util.HashMap;
 +import java.util.HashSet;
 +import java.util.Iterator;
 +import java.util.List;
 +import java.util.Locale;
 +import java.util.Map;
 +import java.util.Map.Entry;
 +import java.util.Set;
 +import java.util.TreeMap;
 +import java.util.UUID;
 +import java.util.concurrent.TimeUnit;
 +
 +import jline.console.ConsoleReader;
 +import jline.console.UserInterruptException;
 +import jline.console.history.FileHistory;
 +
 +import org.apache.accumulo.core.Constants;
 +import org.apache.accumulo.core.client.AccumuloException;
 +import org.apache.accumulo.core.client.AccumuloSecurityException;
 +import org.apache.accumulo.core.client.ClientConfiguration;
 +import org.apache.accumulo.core.client.ClientConfiguration.ClientProperty;
 +import org.apache.accumulo.core.client.Connector;
 +import org.apache.accumulo.core.client.Instance;
 +import org.apache.accumulo.core.client.IteratorSetting;
 +import org.apache.accumulo.core.client.NamespaceNotFoundException;
 +import org.apache.accumulo.core.client.TableNotFoundException;
 +import org.apache.accumulo.core.client.ZooKeeperInstance;
 +import org.apache.accumulo.core.client.impl.ClientContext;
 +import org.apache.accumulo.core.client.impl.Tables;
 +import org.apache.accumulo.core.client.mock.MockInstance;
 +import org.apache.accumulo.core.client.security.tokens.AuthenticationToken;
 +import org.apache.accumulo.core.client.security.tokens.PasswordToken;
 +import org.apache.accumulo.core.conf.AccumuloConfiguration;
 +import org.apache.accumulo.core.conf.Property;
 +import org.apache.accumulo.core.conf.SiteConfiguration;
 +import org.apache.accumulo.core.data.Key;
 +import org.apache.accumulo.core.data.Value;
 +import org.apache.accumulo.core.data.thrift.TConstraintViolationSummary;
 +import org.apache.accumulo.core.tabletserver.thrift.ConstraintViolationException;
 +import org.apache.accumulo.core.trace.DistributedTrace;
 +import org.apache.accumulo.core.util.BadArgumentException;
 +import org.apache.accumulo.core.util.format.BinaryFormatter;
 +import org.apache.accumulo.core.util.format.DefaultFormatter;
 +import org.apache.accumulo.core.util.format.Formatter;
 +import org.apache.accumulo.core.util.format.FormatterFactory;
 +import org.apache.accumulo.core.volume.VolumeConfiguration;
 +import org.apache.accumulo.core.zookeeper.ZooUtil;
 +import org.apache.accumulo.shell.commands.AboutCommand;
 +import org.apache.accumulo.shell.commands.AddAuthsCommand;
 +import org.apache.accumulo.shell.commands.AddSplitsCommand;
 +import org.apache.accumulo.shell.commands.AuthenticateCommand;
 +import org.apache.accumulo.shell.commands.ByeCommand;
 +import org.apache.accumulo.shell.commands.ClasspathCommand;
 +import org.apache.accumulo.shell.commands.ClearCommand;
 +import org.apache.accumulo.shell.commands.CloneTableCommand;
 +import org.apache.accumulo.shell.commands.ClsCommand;
 +import org.apache.accumulo.shell.commands.CompactCommand;
 +import org.apache.accumulo.shell.commands.ConfigCommand;
 +import org.apache.accumulo.shell.commands.ConstraintCommand;
 +import org.apache.accumulo.shell.commands.CreateNamespaceCommand;
 +import org.apache.accumulo.shell.commands.CreateTableCommand;
 +import org.apache.accumulo.shell.commands.CreateUserCommand;
 +import org.apache.accumulo.shell.commands.DUCommand;
 +import org.apache.accumulo.shell.commands.DebugCommand;
 +import org.apache.accumulo.shell.commands.DeleteCommand;
 +import org.apache.accumulo.shell.commands.DeleteIterCommand;
 +import org.apache.accumulo.shell.commands.DeleteManyCommand;
 +import org.apache.accumulo.shell.commands.DeleteNamespaceCommand;
 +import org.apache.accumulo.shell.commands.DeleteRowsCommand;
 +import org.apache.accumulo.shell.commands.DeleteScanIterCommand;
 +import org.apache.accumulo.shell.commands.DeleteShellIterCommand;
 +import org.apache.accumulo.shell.commands.DeleteTableCommand;
 +import org.apache.accumulo.shell.commands.DeleteUserCommand;
 +import org.apache.accumulo.shell.commands.DropTableCommand;
 +import org.apache.accumulo.shell.commands.DropUserCommand;
 +import org.apache.accumulo.shell.commands.EGrepCommand;
 +import org.apache.accumulo.shell.commands.ExecfileCommand;
 +import org.apache.accumulo.shell.commands.ExitCommand;
 +import org.apache.accumulo.shell.commands.ExportTableCommand;
 +import org.apache.accumulo.shell.commands.ExtensionCommand;
 +import org.apache.accumulo.shell.commands.FateCommand;
 +import org.apache.accumulo.shell.commands.FlushCommand;
 +import org.apache.accumulo.shell.commands.FormatterCommand;
 +import org.apache.accumulo.shell.commands.GetAuthsCommand;
 +import org.apache.accumulo.shell.commands.GetGroupsCommand;
 +import org.apache.accumulo.shell.commands.GetSplitsCommand;
 +import org.apache.accumulo.shell.commands.GrantCommand;
 +import org.apache.accumulo.shell.commands.GrepCommand;
 +import org.apache.accumulo.shell.commands.HelpCommand;
 +import org.apache.accumulo.shell.commands.HiddenCommand;
 +import org.apache.accumulo.shell.commands.HistoryCommand;
 +import org.apache.accumulo.shell.commands.ImportDirectoryCommand;
 +import org.apache.accumulo.shell.commands.ImportTableCommand;
 +import org.apache.accumulo.shell.commands.InfoCommand;
 +import org.apache.accumulo.shell.commands.InsertCommand;
 +import org.apache.accumulo.shell.commands.InterpreterCommand;
 +import org.apache.accumulo.shell.commands.ListCompactionsCommand;
 +import org.apache.accumulo.shell.commands.ListIterCommand;
 +import org.apache.accumulo.shell.commands.ListScansCommand;
 +import org.apache.accumulo.shell.commands.ListShellIterCommand;
 +import org.apache.accumulo.shell.commands.MaxRowCommand;
 +import org.apache.accumulo.shell.commands.MergeCommand;
 +import org.apache.accumulo.shell.commands.NamespacePermissionsCommand;
 +import org.apache.accumulo.shell.commands.NamespacesCommand;
 +import org.apache.accumulo.shell.commands.NoTableCommand;
 +import org.apache.accumulo.shell.commands.OfflineCommand;
 +import org.apache.accumulo.shell.commands.OnlineCommand;
 +import org.apache.accumulo.shell.commands.OptUtil;
 +import org.apache.accumulo.shell.commands.PasswdCommand;
 +import org.apache.accumulo.shell.commands.PingCommand;
 +import org.apache.accumulo.shell.commands.QuestionCommand;
 +import org.apache.accumulo.shell.commands.QuitCommand;
 +import org.apache.accumulo.shell.commands.QuotedStringTokenizer;
 +import org.apache.accumulo.shell.commands.RenameNamespaceCommand;
 +import org.apache.accumulo.shell.commands.RenameTableCommand;
 +import org.apache.accumulo.shell.commands.RevokeCommand;
 +import org.apache.accumulo.shell.commands.ScanCommand;
 +import org.apache.accumulo.shell.commands.ScriptCommand;
 +import org.apache.accumulo.shell.commands.SetAuthsCommand;
 +import org.apache.accumulo.shell.commands.SetGroupsCommand;
 +import org.apache.accumulo.shell.commands.SetIterCommand;
 +import org.apache.accumulo.shell.commands.SetScanIterCommand;
 +import org.apache.accumulo.shell.commands.SetShellIterCommand;
 +import org.apache.accumulo.shell.commands.SleepCommand;
 +import org.apache.accumulo.shell.commands.SystemPermissionsCommand;
 +import org.apache.accumulo.shell.commands.TableCommand;
 +import org.apache.accumulo.shell.commands.TablePermissionsCommand;
 +import org.apache.accumulo.shell.commands.TablesCommand;
 +import org.apache.accumulo.shell.commands.TraceCommand;
 +import org.apache.accumulo.shell.commands.UserCommand;
 +import org.apache.accumulo.shell.commands.UserPermissionsCommand;
 +import org.apache.accumulo.shell.commands.UsersCommand;
 +import org.apache.accumulo.shell.commands.WhoAmICommand;
 +import org.apache.accumulo.start.classloader.vfs.AccumuloVFSClassLoader;
 +import org.apache.accumulo.start.classloader.vfs.ContextManager;
 +import org.apache.commons.cli.BasicParser;
 +import org.apache.commons.cli.CommandLine;
 +import org.apache.commons.cli.HelpFormatter;
 +import org.apache.commons.cli.MissingOptionException;
 +import org.apache.commons.cli.Option;
 +import org.apache.commons.cli.Options;
 +import org.apache.commons.cli.ParseException;
 +import org.apache.commons.vfs2.FileSystemException;
 +import org.apache.hadoop.fs.Path;
 +import org.apache.log4j.Level;
 +import org.apache.log4j.Logger;
 +
 +import com.beust.jcommander.JCommander;
 +import com.beust.jcommander.ParameterException;
 +
 +/**
 + * A convenient console interface to perform basic accumulo functions Includes auto-complete, help, and quoted strings with escape sequences
 + */
 +public class Shell extends ShellOptions {
 +  public static final Logger log = Logger.getLogger(Shell.class);
 +  private static final Logger audit = Logger.getLogger(Shell.class.getName() + ".audit");
 +
 +  public static final Charset CHARSET = ISO_8859_1;
 +  public static final int NO_FIXED_ARG_LENGTH_CHECK = -1;
 +  public static final String COMMENT_PREFIX = "#";
 +  public static final String HISTORY_DIR_NAME = ".accumulo";
 +  public static final String HISTORY_FILE_NAME = "shell_history.txt";
 +  private static final String SHELL_DESCRIPTION = "Shell - Apache Accumulo Interactive Shell";
 +
 +  protected int exitCode = 0;
 +  private String tableName;
 +  protected Instance instance;
 +  private Connector connector;
 +  protected ConsoleReader reader;
 +  private String principal;
 +  private AuthenticationToken token;
 +  private final Class<? extends Formatter> defaultFormatterClass = DefaultFormatter.class;
 +  private final Class<? extends Formatter> binaryFormatterClass = BinaryFormatter.class;
 +  public Map<String,List<IteratorSetting>> scanIteratorOptions = new HashMap<String,List<IteratorSetting>>();
 +  public Map<String,List<IteratorSetting>> iteratorProfiles = new HashMap<String,List<IteratorSetting>>();
 +
 +  private Token rootToken;
 +  public final Map<String,Command> commandFactory = new TreeMap<String,Command>();
 +  public final Map<String,Command[]> commandGrouping = new TreeMap<String,Command[]>();
-   protected boolean configError = false;
 +
 +  // exit if true
 +  private boolean exit = false;
 +
 +  // file to execute commands from
 +  protected File execFile = null;
 +  // single command to execute from the command line
 +  protected String execCommand = null;
 +  protected boolean verbose = true;
 +
 +  private boolean tabCompletion;
 +  private boolean disableAuthTimeout;
 +  private long authTimeout;
 +  private long lastUserActivity = System.nanoTime();
 +  private boolean logErrorsToConsole = false;
 +  private PrintWriter writer = null;
 +  private boolean masking = false;
 +
 +  public Shell() throws IOException {
 +    this(new ConsoleReader(), new PrintWriter(new OutputStreamWriter(System.out, System.getProperty("jline.WindowsTerminal.output.encoding",
 +        System.getProperty("file.encoding")))));
 +  }
 +
 +  public Shell(ConsoleReader reader, PrintWriter writer) {
 +    super();
 +    this.reader = reader;
 +    this.writer = writer;
 +  }
 +
-   // Not for client use
++  /**
++   * Configures the shell using the provided options. Not for client use.
++   *
++   * @return true if the shell was successfully configured, false otherwise.
++   */
 +  public boolean config(String... args) {
 +    ShellOptionsJC options = new ShellOptionsJC();
 +    JCommander jc = new JCommander();
 +
 +    jc.setProgramName("accumulo shell");
 +    jc.addObject(options);
 +    try {
 +      jc.parse(args);
 +    } catch (ParameterException e) {
-       configError = true;
++      jc.usage();
++      exitCode = 1;
++      return false;
 +    }
 +
 +    if (options.isHelpEnabled()) {
-       configError = true;
++      jc.usage();
++      // Not an error
++      exitCode = 0;
++      return false;
 +    }
 +
-     if (!configError && options.getUnrecognizedOptions() != null) {
-       configError = true;
++    if (options.getUnrecognizedOptions() != null) {
 +      logError("Unrecognized Options: " + options.getUnrecognizedOptions().toString());
-     }
- 
-     if (configError) {
 +      jc.usage();
-       return true;
++      exitCode = 1;
++      return false;
 +    }
 +
 +    setDebugging(options.isDebugEnabled());
 +    authTimeout = TimeUnit.MINUTES.toNanos(options.getAuthTimeout());
 +    disableAuthTimeout = options.isAuthTimeoutDisabled();
 +
 +    // get the options that were parsed
 +    String user = options.getUsername();
 +    String password = options.getPassword();
 +
 +    tabCompletion = !options.isTabCompletionDisabled();
 +
 +    // Use a fake (Mock), ZK, or HdfsZK Accumulo instance
 +    setInstance(options);
 +
 +    // AuthenticationToken options
 +    token = options.getAuthenticationToken();
 +    Map<String,String> loginOptions = options.getTokenProperties();
 +
 +    // process default parameters if unspecified
 +    try {
 +      final boolean hasToken = (token != null);
 +
 +      if (hasToken && password != null) {
 +        throw new ParameterException("Can not supply '--pass' option with '--tokenClass' option");
 +      }
 +
 +      Runtime.getRuntime().addShutdownHook(new Thread() {
 +        @Override
 +        public void run() {
 +          reader.getTerminal().setEchoEnabled(true);
 +        }
 +      });
 +
 +      if (hasToken) { // implied hasTokenOptions
 +        // Fully qualified name so we don't shadow java.util.Properties
 +        org.apache.accumulo.core.client.security.tokens.AuthenticationToken.Properties props;
 +        // and line wrap it because the package name is so long
 +        props = new org.apache.accumulo.core.client.security.tokens.AuthenticationToken.Properties();
 +
 +        if (!loginOptions.isEmpty()) {
 +          props.putAllStrings(loginOptions);
 +        }
 +        token.init(props);
 +      } else {
 +        // Read password if the user explicitly asked for it, or didn't specify anything at all
 +        if ("stdin".equals(password) || password == null) {
 +          password = reader.readLine("Password: ", '*');
 +        }
 +
 +        if (password == null) {
 +          // User cancel, e.g. Ctrl-D pressed
 +          throw new ParameterException("No password or token option supplied");
 +        } else {
 +          this.token = new PasswordToken(password);
 +        }
 +      }
 +
 +      if (!options.isFake()) {
 +        DistributedTrace.enable(InetAddress.getLocalHost().getHostName(), "shell", options.getClientConfiguration());
 +      }
 +
 +      this.setTableName("");
 +      this.principal = user;
 +      connector = instance.getConnector(this.principal, token);
 +
 +    } catch (Exception e) {
 +      printException(e);
-       configError = true;
++      exitCode = 1;
++      return false;
 +    }
 +
 +    // decide whether to execute commands from a file and quit
 +    if (options.getExecFile() != null) {
 +      execFile = options.getExecFile();
 +      verbose = false;
 +    } else if (options.getExecFileVerbose() != null) {
 +      execFile = options.getExecFileVerbose();
 +      verbose = true;
 +    }
 +    execCommand = options.getExecCommand();
 +    if (execCommand != null) {
 +      verbose = false;
 +    }
 +
 +    rootToken = new Token();
 +
 +    Command[] dataCommands = {new DeleteCommand(), new DeleteManyCommand(), new DeleteRowsCommand(), new EGrepCommand(), new FormatterCommand(),
 +        new InterpreterCommand(), new GrepCommand(), new ImportDirectoryCommand(), new InsertCommand(), new MaxRowCommand(), new ScanCommand()};
 +    Command[] debuggingCommands = {new ClasspathCommand(), new DebugCommand(), new ListScansCommand(), new ListCompactionsCommand(), new TraceCommand(),
 +        new PingCommand()};
 +    Command[] execCommands = {new ExecfileCommand(), new HistoryCommand(), new ExtensionCommand(), new ScriptCommand()};
 +    Command[] exitCommands = {new ByeCommand(), new ExitCommand(), new QuitCommand()};
 +    Command[] helpCommands = {new AboutCommand(), new HelpCommand(), new InfoCommand(), new QuestionCommand()};
 +    Command[] iteratorCommands = {new DeleteIterCommand(), new DeleteScanIterCommand(), new ListIterCommand(), new SetIterCommand(), new SetScanIterCommand(),
 +        new SetShellIterCommand(), new ListShellIterCommand(), new DeleteShellIterCommand()};
 +    Command[] otherCommands = {new HiddenCommand()};
 +    Command[] permissionsCommands = {new GrantCommand(), new RevokeCommand(), new SystemPermissionsCommand(), new TablePermissionsCommand(),
 +        new UserPermissionsCommand(), new NamespacePermissionsCommand()};
 +    Command[] stateCommands = {new AuthenticateCommand(), new ClsCommand(), new ClearCommand(), new FateCommand(), new NoTableCommand(), new SleepCommand(),
 +        new TableCommand(), new UserCommand(), new WhoAmICommand()};
 +    Command[] tableCommands = {new CloneTableCommand(), new ConfigCommand(), new CreateTableCommand(), new DeleteTableCommand(), new DropTableCommand(),
 +        new DUCommand(), new ExportTableCommand(), new ImportTableCommand(), new OfflineCommand(), new OnlineCommand(), new RenameTableCommand(),
 +        new TablesCommand(), new NamespacesCommand(), new CreateNamespaceCommand(), new DeleteNamespaceCommand(), new RenameNamespaceCommand()};
 +    Command[] tableControlCommands = {new AddSplitsCommand(), new CompactCommand(), new ConstraintCommand(), new FlushCommand(), new GetGroupsCommand(),
 +        new GetSplitsCommand(), new MergeCommand(), new SetGroupsCommand()};
 +    Command[] userCommands = {new AddAuthsCommand(), new CreateUserCommand(), new DeleteUserCommand(), new DropUserCommand(), new GetAuthsCommand(),
 +        new PasswdCommand(), new SetAuthsCommand(), new UsersCommand()};
 +    commandGrouping.put("-- Writing, Reading, and Removing Data --", dataCommands);
 +    commandGrouping.put("-- Debugging Commands -------------------", debuggingCommands);
 +    commandGrouping.put("-- Shell Execution Commands -------------", execCommands);
 +    commandGrouping.put("-- Exiting Commands ---------------------", exitCommands);
 +    commandGrouping.put("-- Help Commands ------------------------", helpCommands);
 +    commandGrouping.put("-- Iterator Configuration ---------------", iteratorCommands);
 +    commandGrouping.put("-- Permissions Administration Commands --", permissionsCommands);
 +    commandGrouping.put("-- Shell State Commands -----------------", stateCommands);
 +    commandGrouping.put("-- Table Administration Commands --------", tableCommands);
 +    commandGrouping.put("-- Table Control Commands ---------------", tableControlCommands);
 +    commandGrouping.put("-- User Administration Commands ---------", userCommands);
 +
 +    for (Command[] cmds : commandGrouping.values()) {
 +      for (Command cmd : cmds)
 +        commandFactory.put(cmd.getName(), cmd);
 +    }
 +    for (Command cmd : otherCommands) {
 +      commandFactory.put(cmd.getName(), cmd);
 +    }
-     return configError;
++    return true;
 +  }
 +
 +  /**
 +   * Sets the instance used by the shell based on the given options.
 +   *
 +   * @param options
 +   *          shell options
 +   */
 +  protected void setInstance(ShellOptionsJC options) {
 +    // should only be one set of instance options set
 +    instance = null;
 +    if (options.isFake()) {
 +      instance = new MockInstance("fake");
 +    } else {
 +      String instanceName, hosts;
 +      if (options.isHdfsZooInstance()) {
 +        instanceName = hosts = null;
 +      } else if (options.getZooKeeperInstance().size() > 0) {
 +        List<String> zkOpts = options.getZooKeeperInstance();
 +        instanceName = zkOpts.get(0);
 +        hosts = zkOpts.get(1);
 +      } else {
 +        instanceName = options.getZooKeeperInstanceName();
 +        hosts = options.getZooKeeperHosts();
 +      }
 +      try {
 +        instance = getZooInstance(instanceName, hosts, options.getClientConfiguration());
 +      } catch (Exception e) {
 +        throw new IllegalArgumentException("Unable to load client config from " + options.getClientConfigFile(), e);
 +      }
 +    }
 +  }
 +
 +  /*
 +   * Takes instanceName and keepers as separate arguments, rather than just packaged into the clientConfig, so that we can fail over to accumulo-site.xml or
 +   * HDFS config if they're unspecified.
 +   */
 +  private static Instance getZooInstance(String instanceName, String keepers, ClientConfiguration clientConfig) {
 +    UUID instanceId = null;
 +    if (instanceName == null) {
 +      instanceName = clientConfig.get(ClientProperty.INSTANCE_NAME);
 +    }
 +    if (instanceName == null || keepers == null) {
 +      AccumuloConfiguration conf = SiteConfiguration.getInstance(ClientContext.convertClientConfig(clientConfig));
 +      if (instanceName == null) {
 +        Path instanceDir = new Path(VolumeConfiguration.getVolumeUris(conf)[0], "instance_id");
 +        instanceId = UUID.fromString(ZooUtil.getInstanceIDFromHdfs(instanceDir, conf));
 +      }
 +      if (keepers == null) {
 +        keepers = conf.get(Property.INSTANCE_ZK_HOST);
 +      }
 +    }
 +    if (instanceId != null) {
 +      return new ZooKeeperInstance(clientConfig.withInstance(instanceId).withZkHosts(keepers));
 +    } else {
 +      return new ZooKeeperInstance(clientConfig.withInstance(instanceName).withZkHosts(keepers));
 +    }
 +  }
 +
 +  public Connector getConnector() {
 +    return connector;
 +  }
 +
 +  public Instance getInstance() {
 +    return instance;
 +  }
 +
 +  public ClassLoader getClassLoader(final CommandLine cl, final Shell shellState) throws AccumuloException, TableNotFoundException, AccumuloSecurityException,
 +      IOException, FileSystemException {
 +
 +    boolean tables = cl.hasOption(OptUtil.tableOpt().getOpt()) || !shellState.getTableName().isEmpty();
 +    boolean namespaces = cl.hasOption(OptUtil.namespaceOpt().getOpt());
 +
 +    String classpath = null;
 +    Iterable<Entry<String,String>> tableProps;
 +
 +    if (namespaces) {
 +      try {
 +        tableProps = shellState.getConnector().namespaceOperations().getProperties(OptUtil.getNamespaceOpt(cl, shellState));
 +      } catch (NamespaceNotFoundException e) {
 +        throw new IllegalArgumentException(e);
 +      }
 +    } else if (tables) {
 +      tableProps = shellState.getConnector().tableOperations().getProperties(OptUtil.getTableOpt(cl, shellState));
 +    } else {
 +      throw new IllegalArgumentException("No table or namespace specified");
 +    }
 +    for (Entry<String,String> entry : tableProps) {
 +      if (entry.getKey().equals(Property.TABLE_CLASSPATH.getKey())) {
 +        classpath = entry.getValue();
 +      }
 +    }
 +
 +    ClassLoader classloader;
 +
 +    if (classpath != null && !classpath.equals("")) {
 +      shellState.getConnector().instanceOperations().getSystemConfiguration().get(Property.VFS_CONTEXT_CLASSPATH_PROPERTY.getKey() + classpath);
 +
 +      try {
 +        AccumuloVFSClassLoader.getContextManager().setContextConfig(new ContextManager.DefaultContextsConfig(new Iterable<Map.Entry<String,String>>() {
 +          @Override
 +          public Iterator<Entry<String,String>> iterator() {
 +            try {
 +              return shellState.getConnector().instanceOperations().getSystemConfiguration().entrySet().iterator();
 +            } catch (AccumuloException e) {
 +              throw new RuntimeException(e);
 +            } catch (AccumuloSecurityException e) {
 +              throw new RuntimeException(e);
 +            }
 +          }
 +        }));
 +      } catch (IllegalStateException ise) {}
 +
 +      classloader = AccumuloVFSClassLoader.getContextManager().getClassLoader(classpath);
 +    } else {
 +      classloader = AccumuloVFSClassLoader.getClassLoader();
 +    }
 +    return classloader;
 +  }
 +
 +  public static void main(String args[]) throws IOException {
 +    Shell shell = new Shell();
-     try {
-       shell.config(args);
++    try{
++      if (!shell.config(args)) {
++        System.exit(shell.getExitCode());
++      }
 +
 +      System.exit(shell.start());
 +    } finally {
 +      shell.shutdown();
 +      DistributedTrace.disable();
 +    }
 +  }
 +
 +  public int start() throws IOException {
-     if (configError)
-       return 1;
- 
 +    String input;
 +    if (isVerbose())
 +      printInfo();
 +
 +    String home = System.getProperty("HOME");
 +    if (home == null)
 +      home = System.getenv("HOME");
 +    String configDir = home + "/" + HISTORY_DIR_NAME;
 +    String historyPath = configDir + "/" + HISTORY_FILE_NAME;
 +    File accumuloDir = new File(configDir);
 +    if (!accumuloDir.exists() && !accumuloDir.mkdirs())
 +      log.warn("Unable to make directory for history at " + accumuloDir);
 +    try {
 +      final FileHistory history = new FileHistory(new File(historyPath));
 +      reader.setHistory(history);
 +      // Add shutdown hook to flush file history, per jline javadocs
 +      Runtime.getRuntime().addShutdownHook(new Thread() {
 +        @Override
 +        public void run() {
 +          try {
 +            history.flush();
 +          } catch (IOException e) {
 +            log.warn("Could not flush history to file.");
 +          }
 +        }
 +      });
 +    } catch (IOException e) {
 +      log.warn("Unable to load history file at " + historyPath);
 +    }
 +
 +    // Turn Ctrl+C into Exception instead of JVM exit
 +    reader.setHandleUserInterrupt(true);
 +
 +    ShellCompletor userCompletor = null;
 +
 +    if (execFile != null) {
 +      java.util.Scanner scanner = new java.util.Scanner(execFile, UTF_8.name());
 +      try {
 +        while (scanner.hasNextLine() && !hasExited()) {
 +          execCommand(scanner.nextLine(), true, isVerbose());
 +        }
 +      } finally {
 +        scanner.close();
 +      }
 +    } else if (execCommand != null) {
 +      for (String command : execCommand.split("\n")) {
 +        execCommand(command, true, isVerbose());
 +      }
 +      return exitCode;
 +    }
 +
 +    while (true) {
 +      try {
 +        if (hasExited())
 +          return exitCode;
 +
 +        // If tab completion is true we need to reset
 +        if (tabCompletion) {
 +          if (userCompletor != null)
 +            reader.removeCompleter(userCompletor);
 +
 +          userCompletor = setupCompletion();
 +          reader.addCompleter(userCompletor);
 +        }
 +
 +        reader.setPrompt(getDefaultPrompt());
 +        input = reader.readLine();
 +        if (input == null) {
 +          reader.println();
 +          return exitCode;
 +        } // User Canceled (Ctrl+D)
 +
 +        execCommand(input, disableAuthTimeout, false);
 +      } catch (UserInterruptException uie) {
 +        // User Cancelled (Ctrl+C)
 +        reader.println();
 +
 +        String partialLine = uie.getPartialLine();
 +        if (partialLine == null || "".equals(uie.getPartialLine().trim())) {
 +          // No content, actually exit
 +          return exitCode;
 +        }
 +      } finally {
 +        reader.flush();
 +      }
 +    }
 +  }
 +
 +  public void shutdown() {
 +    if (reader != null) {
 +      reader.shutdown();
 +    }
 +  }
 +
 +  public void printInfo() throws IOException {
 +    reader.print("\n" + SHELL_DESCRIPTION + "\n" + "- \n" + "- version: " + Constants.VERSION + "\n" + "- instance name: "
 +        + connector.getInstance().getInstanceName() + "\n" + "- instance id: " + connector.getInstance().getInstanceID() + "\n" + "- \n"
 +        + "- type 'help' for a list of available commands\n" + "- \n");
 +    reader.flush();
 +  }
 +
 +  public void printVerboseInfo() throws IOException {
 +    StringBuilder sb = new StringBuilder("-\n");
 +    sb.append("- Current user: ").append(connector.whoami()).append("\n");
 +    if (execFile != null)
 +      sb.append("- Executing commands from: ").append(execFile).append("\n");
 +    if (disableAuthTimeout)
 +      sb.append("- Authorization timeout: disabled\n");
 +    else
 +      sb.append("- Authorization timeout: ").append(String.format("%ds%n", TimeUnit.NANOSECONDS.toSeconds(authTimeout)));
 +    sb.append("- Debug: ").append(isDebuggingEnabled() ? "on" : "off").append("\n");
 +    if (!scanIteratorOptions.isEmpty()) {
 +      for (Entry<String,List<IteratorSetting>> entry : scanIteratorOptions.entrySet()) {
 +        sb.append("- Session scan iterators for table ").append(entry.getKey()).append(":\n");
 +        for (IteratorSetting setting : entry.getValue()) {
 +          sb.append("-    Iterator ").append(setting.getName()).append(" options:\n");
 +          sb.append("-        ").append("iteratorPriority").append(" = ").append(setting.getPriority()).append("\n");
 +          sb.append("-        ").append("iteratorClassName").append(" = ").append(setting.getIteratorClass()).append("\n");
 +          for (Entry<String,String> optEntry : setting.getOptions().entrySet()) {
 +            sb.append("-        ").append(optEntry.getKey()).append(" = ").append(optEntry.getValue()).append("\n");
 +          }
 +        }
 +      }
 +    }
 +    sb.append("-\n");
 +    reader.print(sb.toString());
 +  }
 +
 +  public String getDefaultPrompt() {
 +    return connector.whoami() + "@" + connector.getInstance().getInstanceName() + (getTableName().isEmpty() ? "" : " ") + getTableName() + "> ";
 +  }
 +
 +  public void execCommand(String input, boolean ignoreAuthTimeout, boolean echoPrompt) throws IOException {
 +    audit.log(Level.INFO, getDefaultPrompt() + input);
 +    if (echoPrompt) {
 +      reader.print(getDefaultPrompt());
 +      reader.println(input);
 +    }
 +
 +    if (input.startsWith(COMMENT_PREFIX)) {
 +      return;
 +    }
 +
 +    String fields[];
 +    try {
 +      fields = new QuotedStringTokenizer(input).getTokens();
 +    } catch (BadArgumentException e) {
 +      printException(e);
 +      ++exitCode;
 +      return;
 +    }
 +    if (fields.length == 0)
 +      return;
 +
 +    String command = fields[0];
 +    fields = fields.length > 1 ? Arrays.copyOfRange(fields, 1, fields.length) : new String[] {};
 +
 +    Command sc = null;
 +    if (command.length() > 0) {
 +      try {
 +        // Obtain the command from the command table
 +        sc = commandFactory.get(command);
 +        if (sc == null) {
 +          reader.println(String.format("Unknown command \"%s\".  Enter \"help\" for a list possible commands.", command));
 +          reader.flush();
 +          return;
 +        }
 +
 +        long duration = System.nanoTime() - lastUserActivity;
 +        if (!(sc instanceof ExitCommand) && !ignoreAuthTimeout && (duration < 0 || duration > authTimeout)) {
 +          reader.println("Shell has been idle for too long. Please re-authenticate.");
 +          boolean authFailed = true;
 +          do {
 +            String pwd = readMaskedLine("Enter current password for '" + connector.whoami() + "': ", '*');
 +            if (pwd == null) {
 +              reader.println();
 +              return;
 +            } // user canceled
 +
 +            try {
 +              authFailed = !connector.securityOperations().authenticateUser(connector.whoami(), new PasswordToken(pwd));
 +            } catch (Exception e) {
 +              ++exitCode;
 +              printException(e);
 +            }
 +
 +            if (authFailed)
 +              reader.print("Invalid password. ");
 +          } while (authFailed);
 +          lastUserActivity = System.nanoTime();
 +        }
 +
 +        // Get the options from the command on how to parse the string
 +        Options parseOpts = sc.getOptionsWithHelp();
 +
 +        // Parse the string using the given options
 +        CommandLine cl = new BasicParser().parse(parseOpts, fields);
 +
 +        int actualArgLen = cl.getArgs().length;
 +        int expectedArgLen = sc.numArgs();
 +        if (cl.hasOption(helpOption)) {
 +          // Display help if asked to; otherwise execute the command
 +          sc.printHelp(this);
 +        } else if (expectedArgLen != NO_FIXED_ARG_LENGTH_CHECK && actualArgLen != expectedArgLen) {
 +          ++exitCode;
 +          // Check for valid number of fixed arguments (if not
 +          // negative; negative means it is not checked, for
 +          // vararg-like commands)
 +          printException(new IllegalArgumentException(String.format("Expected %d argument%s. There %s %d.", expectedArgLen, expectedArgLen == 1 ? "" : "s",
 +              actualArgLen == 1 ? "was" : "were", actualArgLen)));
 +          sc.printHelp(this);
 +        } else {
 +          int tmpCode = sc.execute(input, cl, this);
 +          exitCode += tmpCode;
 +          reader.flush();
 +        }
 +
 +      } catch (ConstraintViolationException e) {
 +        ++exitCode;
 +        printConstraintViolationException(e);
 +      } catch (TableNotFoundException e) {
 +        ++exitCode;
 +        if (getTableName().equals(e.getTableName()))
 +          setTableName("");
 +        printException(e);
 +      } catch (ParseException e) {
 +        // not really an error if the exception is a missing required
 +        // option when the user is asking for help
 +        if (!(e instanceof MissingOptionException && (Arrays.asList(fields).contains("-" + helpOption) || Arrays.asList(fields).contains("--" + helpLongOption)))) {
 +          ++exitCode;
 +          printException(e);
 +        }
 +        if (sc != null)
 +          sc.printHelp(this);
 +      } catch (UserInterruptException e) {
 +        ++exitCode;
 +      } catch (Exception e) {
 +        ++exitCode;
 +        printException(e);
 +      }
 +    } else {
 +      ++exitCode;
 +      printException(new BadArgumentException("Unrecognized empty command", command, -1));
 +    }
 +    reader.flush();
 +  }
 +
 +  /**
 +   * The command tree is built in reverse so that the references are more easily linked up. There is some code in token to allow forward building of the command
 +   * tree.
 +   */
 +  private ShellCompletor setupCompletion() {
 +    rootToken = new Token();
 +
 +    Set<String> tableNames = null;
 +    try {
 +      tableNames = connector.tableOperations().list();
 +    } catch (Exception e) {
 +      log.debug("Unable to obtain list of tables", e);
 +      tableNames = Collections.emptySet();
 +    }
 +
 +    Set<String> userlist = null;
 +    try {
 +      userlist = connector.securityOperations().listLocalUsers();
 +    } catch (Exception e) {
 +      log.debug("Unable to obtain list of users", e);
 +      userlist = Collections.emptySet();
 +    }
 +
 +    Set<String> namespaces = null;
 +    try {
 +      namespaces = connector.namespaceOperations().list();
 +    } catch (Exception e) {
 +      log.debug("Unable to obtain list of namespaces", e);
 +      namespaces = Collections.emptySet();
 +    }
 +
 +    Map<Command.CompletionSet,Set<String>> options = new HashMap<Command.CompletionSet,Set<String>>();
 +
 +    Set<String> commands = new HashSet<String>();
 +    for (String a : commandFactory.keySet())
 +      commands.add(a);
 +
 +    Set<String> modifiedUserlist = new HashSet<String>();
 +    Set<String> modifiedTablenames = new HashSet<String>();
 +    Set<String> modifiedNamespaces = new HashSet<String>();
 +
 +    for (String a : tableNames)
 +      modifiedTablenames.add(a.replaceAll("([\\s'\"])", "\\\\$1"));
 +    for (String a : userlist)
 +      modifiedUserlist.add(a.replaceAll("([\\s'\"])", "\\\\$1"));
 +    for (String a : namespaces) {
 +      String b = a.replaceAll("([\\s'\"])", "\\\\$1");
 +      modifiedNamespaces.add(b.isEmpty() ? "\"\"" : b);
 +    }
 +
 +    options.put(Command.CompletionSet.USERNAMES, modifiedUserlist);
 +    options.put(Command.CompletionSet.TABLENAMES, modifiedTablenames);
 +    options.put(Command.CompletionSet.NAMESPACES, modifiedNamespaces);
 +    options.put(Command.CompletionSet.COMMANDS, commands);
 +
 +    for (Command[] cmdGroup : commandGrouping.values()) {
 +      for (Command c : cmdGroup) {
 +        c.getOptionsWithHelp(); // prep the options for the command
 +        // so that the completion can
 +        // include them
 +        c.registerCompletion(rootToken, options);
 +      }
 +    }
 +    return new ShellCompletor(rootToken, options);
 +  }
 +
 +  /**
 +   * The Command class represents a command to be run in the shell. It contains the methods to execute along with some methods to help tab completion, and
 +   * return the command name, help, and usage.
 +   */
 +  public static abstract class Command {
 +    // Helper methods for completion
 +    public enum CompletionSet {
 +      TABLENAMES, USERNAMES, COMMANDS, NAMESPACES
 +    }
 +
 +    static Set<String> getCommandNames(Map<CompletionSet,Set<String>> objects) {
 +      return objects.get(CompletionSet.COMMANDS);
 +    }
 +
 +    static Set<String> getTableNames(Map<CompletionSet,Set<String>> objects) {
 +      return objects.get(CompletionSet.TABLENAMES);
 +    }
 +
 +    static Set<String> getUserNames(Map<CompletionSet,Set<String>> objects) {
 +      return objects.get(CompletionSet.USERNAMES);
 +    }
 +
 +    static Set<String> getNamespaces(Map<CompletionSet,Set<String>> objects) {
 +      return objects.get(CompletionSet.NAMESPACES);
 +    }
 +
 +    public void registerCompletionGeneral(Token root, Set<String> args, boolean caseSens) {
 +      Token t = new Token(args);
 +      t.setCaseSensitive(caseSens);
 +
 +      Token command = new Token(getName());
 +      command.addSubcommand(t);
 +
 +      root.addSubcommand(command);
 +    }
 +
 +    public void registerCompletionForTables(Token root, Map<CompletionSet,Set<String>> completionSet) {
 +      registerCompletionGeneral(root, completionSet.get(CompletionSet.TABLENAMES), true);
 +    }
 +
 +    public void registerCompletionForUsers(Token root, Map<CompletionSet,Set<String>> completionSet) {
 +      registerCompletionGeneral(root, completionSet.get(CompletionSet.USERNAMES), true);
 +    }
 +
 +    public void registerCompletionForCommands(Token root, Map<CompletionSet,Set<String>> completionSet) {
 +      registerCompletionGeneral(root, completionSet.get(CompletionSet.COMMANDS), false);
 +    }
 +
 +    public void registerCompletionForNamespaces(Token root, Map<CompletionSet,Set<String>> completionSet) {
 +      registerCompletionGeneral(root, completionSet.get(CompletionSet.NAMESPACES), true);
 +    }
 +
 +    // abstract methods to override
 +    public abstract int execute(String fullCommand, CommandLine cl, Shell shellState) throws Exception;
 +
 +    public abstract String description();
 +
 +    /**
 +     * If the number of arguments is not always zero (not including those arguments handled through Options), make sure to override the {@link #usage()} method.
 +     * Otherwise, {@link #usage()} does need to be overridden.
 +     */
 +    public abstract int numArgs();
 +
 +    // OPTIONAL methods to override:
 +
 +    // the general version of getname uses reflection to get the class name
 +    // and then cuts off the suffix -Command to get the name of the command
 +    public String getName() {
 +      String s = this.getClass().getName();
 +      int st = Math.max(s.lastIndexOf('$'), s.lastIndexOf('.'));
 +      int i = s.indexOf("Command");
 +      return i > 0 ? s.substring(st + 1, i).toLowerCase(Locale.ENGLISH) : null;
 +    }
 +
 +    // The general version of this method adds the name
 +    // of the command to the completion tree
 +    public void registerCompletion(Token root, Map<CompletionSet,Set<String>> completion_set) {
 +      root.addSubcommand(new Token(getName()));
 +    }
 +
 +    // The general version of this method uses the HelpFormatter
 +    // that comes with the apache Options package to print out the help
 +    public final void printHelp(Shell shellState) {
 +      shellState.printHelp(usage(), "description: " + this.description(), getOptionsWithHelp());
 +    }
 +
 +    public final void printHelp(Shell shellState, int width) {
 +      shellState.printHelp(usage(), "description: " + this.description(), getOptionsWithHelp(), width);
 +    }
 +
 +    // Get options with help
 +    public final Options getOptionsWithHelp() {
 +      Options opts = getOptions();
 +      opts.addOption(new Option(helpOption, helpLongOption, false, "display this help"));
 +      return opts;
 +    }
 +
 +    // General usage is just the command
 +    public String usage() {
 +      return getName();
 +    }
 +
 +    // General Options are empty
 +    public Options getOptions() {
 +      return new Options();
 +    }
 +  }
 +
 +  public interface PrintLine {
 +    void print(String s);
 +
 +    void close();
 +  }
 +
 +  public static class PrintShell implements PrintLine {
 +    ConsoleReader reader;
 +
 +    public PrintShell(ConsoleReader reader) {
 +      this.reader = reader;
 +    }
 +
 +    @Override
 +    public void print(String s) {
 +      try {
 +        reader.println(s);
 +      } catch (Exception ex) {
 +        throw new RuntimeException(ex);
 +      }
 +    }
 +
 +    @Override
 +    public void close() {}
 +  };
 +
 +  public static class PrintFile implements PrintLine {
 +    PrintWriter writer;
 +
 +    public PrintFile(String filename) throws FileNotFoundException {
 +      writer = new PrintWriter(new BufferedWriter(new OutputStreamWriter(new FileOutputStream(filename), UTF_8)));
 +    }
 +
 +    @Override
 +    public void print(String s) {
 +      writer.println(s);
 +    }
 +
 +    @Override
 +    public void close() {
 +      writer.close();
 +    }
 +  };
 +
 +  public final void printLines(Iterator<String> lines, boolean paginate) throws IOException {
 +    printLines(lines, paginate, null);
 +  }
 +
 +  public final void printLines(Iterator<String> lines, boolean paginate, PrintLine out) throws IOException {
 +    int linesPrinted = 0;
 +    String prompt = "-- hit any key to continue or 'q' to quit --";
 +    int lastPromptLength = prompt.length();
 +    int termWidth = reader.getTerminal().getWidth();
 +    int maxLines = reader.getTerminal().getHeight();
 +
 +    String peek = null;
 +    while (lines.hasNext()) {
 +      String nextLine = lines.next();
 +      if (nextLine == null)
 +        continue;
 +      for (String line : nextLine.split("\\n")) {
 +        if (out == null) {
 +          if (peek != null) {
 +            reader.println(peek);
 +            if (paginate) {
 +              linesPrinted += peek.length() == 0 ? 0 : Math.ceil(peek.length() * 1.0 / termWidth);
 +
 +              // check if displaying the next line would result in
 +              // scrolling off the screen
 +              if (linesPrinted + Math.ceil(lastPromptLength * 1.0 / termWidth) + Math.ceil(prompt.length() * 1.0 / termWidth)
 +                  + Math.ceil(line.length() * 1.0 / termWidth) > maxLines) {
 +                linesPrinted = 0;
 +                int numdashes = (termWidth - prompt.length()) / 2;
 +                String nextPrompt = repeat("-", numdashes) + prompt + repeat("-", numdashes);
 +                lastPromptLength = nextPrompt.length();
 +                reader.print(nextPrompt);
 +                reader.flush();
 +
 +                if (Character.toUpperCase((char) reader.readCharacter()) == 'Q') {
 +                  reader.println();
 +                  return;
 +                }
 +                reader.println();
 +                termWidth = reader.getTerminal().getWidth();
 +                maxLines = reader.getTerminal().getHeight();
 +              }
 +            }
 +          }
 +          peek = line;
 +        } else {
 +          out.print(line);
 +        }
 +      }
 +    }
 +    if (out == null && peek != null) {
 +      reader.println(peek);
 +    }
 +  }
 +
 +  public final void printRecords(Iterable<Entry<Key,Value>> scanner, boolean printTimestamps, boolean paginate, Class<? extends Formatter> formatterClass,
 +      PrintLine outFile) throws IOException {
 +    printLines(FormatterFactory.getFormatter(formatterClass, scanner, printTimestamps), paginate, outFile);
 +  }
 +
 +  public final void printRecords(Iterable<Entry<Key,Value>> scanner, boolean printTimestamps, boolean paginate, Class<? extends Formatter> formatterClass)
 +      throws IOException {
 +    printLines(FormatterFactory.getFormatter(formatterClass, scanner, printTimestamps), paginate);
 +  }
 +
 +  public final void printBinaryRecords(Iterable<Entry<Key,Value>> scanner, boolean printTimestamps, boolean paginate, PrintLine outFile) throws IOException {
 +    printLines(FormatterFactory.getFormatter(binaryFormatterClass, scanner, printTimestamps), paginate, outFile);
 +  }
 +
 +  public final void printBinaryRecords(Iterable<Entry<Key,Value>> scanner, boolean printTimestamps, boolean paginate) throws IOException {
 +    printLines(FormatterFactory.getFormatter(binaryFormatterClass, scanner, printTimestamps), paginate);
 +  }
 +
 +  public static String repeat(String s, int c) {
 +    StringBuilder sb = new StringBuilder();
 +    for (int i = 0; i < c; i++)
 +      sb.append(s);
 +    return sb.toString();
 +  }
 +
 +  public void checkTableState() {
 +    if (getTableName().isEmpty())
 +      throw new IllegalStateException(
 +          "Not in a table context. Please use 'table <tableName>' to switch to a table, or use '-t' to specify a table if option is available.");
 +  }
 +
 +  private final void printConstraintViolationException(ConstraintViolationException cve) {
 +    printException(cve, "");
 +    int COL1 = 50, COL2 = 14;
 +    int col3 = Math.max(1, Math.min(Integer.MAX_VALUE, reader.getTerminal().getWidth() - COL1 - COL2 - 6));
 +    logError(String.format("%" + COL1 + "s-+-%" + COL2 + "s-+-%" + col3 + "s%n", repeat("-", COL1), repeat("-", COL2), repeat("-", col3)));
 +    logError(String.format("%-" + COL1 + "s | %" + COL2 + "s | %-" + col3 + "s%n", "Constraint class", "Violation code", "Violation Description"));
 +    logError(String.format("%" + COL1 + "s-+-%" + COL2 + "s-+-%" + col3 + "s%n", repeat("-", COL1), repeat("-", COL2), repeat("-", col3)));
 +    for (TConstraintViolationSummary cvs : cve.violationSummaries)
 +      logError(String.format("%-" + COL1 + "s | %" + COL2 + "d | %-" + col3 + "s%n", cvs.constrainClass, cvs.violationCode, cvs.violationDescription));
 +    logError(String.format("%" + COL1 + "s-+-%" + COL2 + "s-+-%" + col3 + "s%n", repeat("-", COL1), repeat("-", COL2), repeat("-", col3)));
 +  }
 +
 +  public final void printException(Exception e) {
 +    printException(e, e.getMessage());
 +  }
 +
 +  private final void printException(Exception e, String msg) {
 +    logError(e.getClass().getName() + (msg != null ? ": " + msg : ""));
 +    log.debug(e.getClass().getName() + (msg != null ? ": " + msg : ""), e);
 +  }
 +
 +  public static final void setDebugging(boolean debuggingEnabled) {
 +    Logger.getLogger(Constants.CORE_PACKAGE_NAME).setLevel(debuggingEnabled ? Level.TRACE : Level.INFO);
 +    Logger.getLogger(Shell.class.getPackage().getName()).setLevel(debuggingEnabled ? Level.TRACE : Level.INFO);
 +  }
 +
 +  public static final boolean isDebuggingEnabled() {
 +    return Logger.getLogger(Constants.CORE_PACKAGE_NAME).isTraceEnabled();
 +  }
 +
 +  private final void printHelp(String usage, String description, Options opts) {
 +    printHelp(usage, description, opts, Integer.MAX_VALUE);
 +  }
 +
 +  private final void printHelp(String usage, String description, Options opts, int width) {
 +    // TODO Use the OutputStream from the JLine ConsoleReader if we can ever get access to it
 +    new HelpFormatter().printHelp(writer, width, usage, description, opts, 2, 5, null, true);
 +    writer.flush();
 +  }
 +
 +  public int getExitCode() {
 +    return exitCode;
 +  }
 +
 +  public void resetExitCode() {
 +    exitCode = 0;
 +  }
 +
 +  public void setExit(boolean exit) {
 +    this.exit = exit;
 +  }
 +
 +  public boolean getExit() {
 +    return this.exit;
 +  }
 +
 +  public boolean isVerbose() {
 +    return verbose;
 +  }
 +
 +  public void setTableName(String tableName) {
 +    this.tableName = (tableName == null || tableName.isEmpty()) ? "" : Tables.qualified(tableName);
 +  }
 +
 +  public String getTableName() {
 +    return tableName;
 +  }
 +
 +  public ConsoleReader getReader() {
 +    return reader;
 +  }
 +
 +  public void updateUser(String principal, AuthenticationToken token) throws AccumuloException, AccumuloSecurityException {
 +    connector = instance.getConnector(principal, token);
 +    this.principal = principal;
 +    this.token = token;
 +  }
 +
 +  public String getPrincipal() {
 +    return principal;
 +  }
 +
 +  public AuthenticationToken getToken() {
 +    return token;
 +  }
 +
 +  /**
 +   * Return the formatter for the current table.
 +   *
 +   * @return the formatter class for the current table
 +   */
 +  public Class<? extends Formatter> getFormatter() {
 +    return getFormatter(this.tableName);
 +  }
 +
 +  /**
 +   * Return the formatter for the given table.
 +   *
 +   * @param tableName
 +   *          the table name
 +   * @return the formatter class for the given table
 +   */
 +  public Class<? extends Formatter> getFormatter(String tableName) {
 +    Class<? extends Formatter> formatter = FormatterCommand.getCurrentFormatter(tableName, this);
 +
 +    if (null == formatter) {
 +      logError("Could not load the specified formatter. Using the DefaultFormatter");
 +      return this.defaultFormatterClass;
 +    } else {
 +      return formatter;
 +    }
 +  }
 +
 +  public void setLogErrorsToConsole() {
 +    this.logErrorsToConsole = true;
 +  }
 +
 +  private void logError(String s) {
 +    log.error(s);
 +    if (logErrorsToConsole) {
 +      try {
 +        reader.println("ERROR: " + s);
 +        reader.flush();
 +      } catch (IOException e) {}
 +    }
 +  }
 +
 +  public String readMaskedLine(String prompt, Character mask) throws IOException {
 +    this.masking = true;
 +    String s = reader.readLine(prompt, mask);
 +    this.masking = false;
 +    return s;
 +  }
 +
 +  public boolean isMasking() {
 +    return masking;
 +  }
 +
 +  public boolean hasExited() {
 +    return exit;
 +  }
 +
 +  public boolean isTabCompletion() {
 +    return tabCompletion;
 +  }
 +
 +}

http://git-wip-us.apache.org/repos/asf/accumulo/blob/23ce1c7e/shell/src/main/java/org/apache/accumulo/shell/mock/MockShell.java
----------------------------------------------------------------------
diff --cc shell/src/main/java/org/apache/accumulo/shell/mock/MockShell.java
index ce70a02,0000000..d340ca2
mode 100644,000000..100644
--- a/shell/src/main/java/org/apache/accumulo/shell/mock/MockShell.java
+++ b/shell/src/main/java/org/apache/accumulo/shell/mock/MockShell.java
@@@ -1,144 -1,0 +1,145 @@@
 +/*
 + * 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.accumulo.shell.mock;
 +
 +import static java.nio.charset.StandardCharsets.UTF_8;
 +
 +import java.io.ByteArrayInputStream;
 +import java.io.IOException;
 +import java.io.InputStream;
 +import java.io.OutputStream;
 +
 +import jline.console.ConsoleReader;
 +
 +import org.apache.accumulo.core.client.mock.MockInstance;
 +import org.apache.accumulo.shell.Shell;
 +import org.apache.accumulo.shell.ShellOptionsJC;
 +
 +/**
 + * An Accumulo Shell implementation that allows a developer to attach an InputStream and Writer to the Shell for testing purposes.
 + */
 +public class MockShell extends Shell {
 +  private static final String NEWLINE = "\n";
 +
 +  protected InputStream in;
 +  protected OutputStream out;
 +
 +  public MockShell(InputStream in, OutputStream out) throws IOException {
 +    super();
 +    this.in = in;
 +    this.out = out;
 +  }
 +
++  @Override
 +  public boolean config(String... args) {
-     configError = super.config(args);
++    // If configuring the shell failed, fail quickly
++    if (!super.config(args)) {
++      return false;
++    }
 +
 +    // Update the ConsoleReader with the input and output "redirected"
 +    try {
 +      this.reader = new ConsoleReader(in, out);
 +    } catch (Exception e) {
 +      printException(e);
-       configError = true;
++      return false;
 +    }
 +
 +    // Don't need this for testing purposes
 +    this.reader.setHistoryEnabled(false);
 +    this.reader.setPaginationEnabled(false);
 +
 +    // Make the parsing from the client easier;
 +    this.verbose = false;
-     return configError;
++    return true;
 +  }
 +
 +  @Override
 +  protected void setInstance(ShellOptionsJC options) {
 +    // We always want a MockInstance for this test
 +    instance = new MockInstance();
 +  }
 +
 +  public int start() throws IOException {
-     if (configError)
-       return 1;
- 
 +    String input;
 +    if (isVerbose())
 +      printInfo();
 +
 +    if (execFile != null) {
 +      java.util.Scanner scanner = new java.util.Scanner(execFile, UTF_8.name());
 +      try {
 +        while (scanner.hasNextLine() && !hasExited()) {
 +          execCommand(scanner.nextLine(), true, isVerbose());
 +        }
 +      } finally {
 +        scanner.close();
 +      }
 +    } else if (execCommand != null) {
 +      for (String command : execCommand.split("\n")) {
 +        execCommand(command, true, isVerbose());
 +      }
 +      return exitCode;
 +    }
 +
 +    while (true) {
 +      if (hasExited())
 +        return exitCode;
 +
 +      reader.setPrompt(getDefaultPrompt());
 +      input = reader.readLine();
 +      if (input == null) {
 +        reader.println();
 +        return exitCode;
 +      } // user canceled
 +
 +      execCommand(input, false, false);
 +    }
 +  }
 +
 +  /**
 +   * @param in
 +   *          the in to set
 +   */
 +  public void setConsoleInputStream(InputStream in) {
 +    this.in = in;
 +  }
 +
 +  /**
 +   * @param out
 +   *          the output stream to set
 +   */
 +  public void setConsoleWriter(OutputStream out) {
 +    this.out = out;
 +  }
 +
 +  /**
 +   * Convenience method to create the byte-array to hand to the console
 +   *
 +   * @param commands
 +   *          An array of commands to run
 +   * @return A byte[] input stream which can be handed to the console.
 +   */
 +  public static ByteArrayInputStream makeCommands(String... commands) {
 +    StringBuilder sb = new StringBuilder(commands.length * 8);
 +
 +    for (String command : commands) {
 +      sb.append(command).append(NEWLINE);
 +    }
 +
 +    return new ByteArrayInputStream(sb.toString().getBytes(UTF_8));
 +  }
 +}

http://git-wip-us.apache.org/repos/asf/accumulo/blob/23ce1c7e/shell/src/test/java/org/apache/accumulo/shell/ShellConfigTest.java
----------------------------------------------------------------------
diff --cc shell/src/test/java/org/apache/accumulo/shell/ShellConfigTest.java
index ca06c22,0000000..922ef8c
mode 100644,000000..100644
--- a/shell/src/test/java/org/apache/accumulo/shell/ShellConfigTest.java
+++ b/shell/src/test/java/org/apache/accumulo/shell/ShellConfigTest.java
@@@ -1,90 -1,0 +1,90 @@@
 +/*
 + * 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.accumulo.shell;
 +
 +import static org.junit.Assert.assertFalse;
 +import static org.junit.Assert.assertTrue;
 +
 +import java.io.FileDescriptor;
 +import java.io.FileInputStream;
 +import java.io.PrintStream;
 +import java.io.PrintWriter;
 +
 +import jline.console.ConsoleReader;
 +
 +import org.apache.accumulo.core.client.security.tokens.PasswordToken;
 +import org.apache.accumulo.shell.ShellTest.TestOutputStream;
 +import org.apache.log4j.Level;
 +import org.junit.After;
 +import org.junit.Before;
 +import org.junit.Test;
 +
 +import com.beust.jcommander.ParameterException;
 +
 +public class ShellConfigTest {
 +  TestOutputStream output;
 +  Shell shell;
 +  PrintStream out;
 +
 +  @Before
 +  public void setUp() throws Exception {
 +    Shell.log.setLevel(Level.ERROR);
 +
 +    out = System.out;
 +    output = new TestOutputStream();
 +    System.setOut(new PrintStream(output));
 +
 +    shell = new Shell(new ConsoleReader(new FileInputStream(FileDescriptor.in), output), new PrintWriter(output));
 +    shell.setLogErrorsToConsole();
 +  }
 +
 +  @After
 +  public void teardown() throws Exception {
 +    shell.shutdown();
 +    output.clear();
 +    System.setOut(out);
 +  }
 +
 +  @Test
 +  public void testHelp() {
-     assertTrue(shell.config("--help"));
++    assertFalse(shell.config("--help"));
 +    assertTrue("Did not print usage", output.get().startsWith("Usage"));
 +  }
 +
 +  @Test
 +  public void testBadArg() {
-     assertTrue(shell.config("--bogus"));
++    assertFalse(shell.config("--bogus"));
 +    assertTrue("Did not print usage", output.get().startsWith("Usage"));
 +  }
 +
 +  @Test
 +  public void testTokenWithoutOptions() {
-     assertTrue(shell.config("--fake", "-tc", PasswordToken.class.getCanonicalName()));
++    assertFalse(shell.config("--fake", "-tc", PasswordToken.class.getCanonicalName()));
 +    assertFalse(output.get().contains(ParameterException.class.getCanonicalName()));
 +  }
 +
 +  @Test
 +  public void testTokenAndOption() {
-     assertFalse(shell.config("--fake", "-tc", PasswordToken.class.getCanonicalName(), "-u", "foo", "-l", "password=foo"));
++    assertTrue(shell.config("--fake", "-tc", PasswordToken.class.getCanonicalName(), "-u", "foo", "-l", "password=foo"));
 +  }
 +
 +  @Test
 +  public void testTokenAndOptionAndPassword() {
-     assertTrue(shell.config("--fake", "-tc", PasswordToken.class.getCanonicalName(), "-l", "password=foo", "-p", "bar"));
++    assertFalse(shell.config("--fake", "-tc", PasswordToken.class.getCanonicalName(), "-l", "password=foo", "-p", "bar"));
 +    assertTrue(output.get().contains(ParameterException.class.getCanonicalName()));
 +  }
 +}

http://git-wip-us.apache.org/repos/asf/accumulo/blob/23ce1c7e/shell/src/test/java/org/apache/accumulo/shell/commands/FormatterCommandTest.java
----------------------------------------------------------------------
diff --cc shell/src/test/java/org/apache/accumulo/shell/commands/FormatterCommandTest.java
index 8a174d2,0000000..577be37
mode 100644,000000..100644
--- a/shell/src/test/java/org/apache/accumulo/shell/commands/FormatterCommandTest.java
+++ b/shell/src/test/java/org/apache/accumulo/shell/commands/FormatterCommandTest.java
@@@ -1,184 -1,0 +1,186 @@@
 +/*
 + * 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.accumulo.shell.commands;
 +
++import static org.junit.Assert.assertTrue;
++
 +import java.io.ByteArrayOutputStream;
 +import java.io.IOException;
 +import java.io.InputStream;
 +import java.util.Iterator;
 +import java.util.Map.Entry;
 +
 +import org.apache.accumulo.core.client.AccumuloException;
 +import org.apache.accumulo.core.client.AccumuloSecurityException;
 +import org.apache.accumulo.core.client.TableExistsException;
 +import org.apache.accumulo.core.data.Key;
 +import org.apache.accumulo.core.data.Value;
 +import org.apache.accumulo.core.util.format.Formatter;
 +import org.apache.accumulo.shell.Shell;
 +import org.apache.accumulo.shell.mock.MockShell;
 +import org.apache.log4j.Level;
 +import org.apache.log4j.Logger;
 +import org.junit.Assert;
 +import org.junit.Test;
 +
 +/**
 + * Uses the MockShell to test the shell output with Formatters
 + */
 +public class FormatterCommandTest {
 +  ByteArrayOutputStream out = null;
 +  InputStream in = null;
 +
 +  @Test
 +  public void test() throws IOException, AccumuloException, AccumuloSecurityException, TableExistsException, ClassNotFoundException {
 +    // Keep the Shell AUDIT log off the test output
 +    Logger.getLogger(Shell.class).setLevel(Level.WARN);
 +
 +    final String[] args = new String[] {"--fake", "-u", "root", "-p", ""};
 +
 +    final String[] commands = createCommands();
 +
 +    in = MockShell.makeCommands(commands);
 +    out = new ByteArrayOutputStream();
 +
 +    final MockShell shell = new MockShell(in, out);
-     shell.config(args);
++    assertTrue("Failed to configure shell without error", shell.config(args));
 +
 +    // Can't call createtable in the shell with MockAccumulo
 +    shell.getConnector().tableOperations().create("test");
 +
 +    try {
 +      shell.start();
 +    } catch (Exception e) {
 +      Assert.fail("Exception while running commands: " + e.getMessage());
 +    }
 +
 +    shell.getReader().flush();
 +
 +    final String[] output = new String(out.toByteArray()).split("\n\r");
 +
 +    boolean formatterOn = false;
 +
 +    final String[] expectedDefault = new String[] {"row cf:cq []    1234abcd", "row cf1:cq1 []    9876fedc", "row2 cf:cq []    13579bdf",
 +        "row2 cf1:cq []    2468ace"};
 +
 +    final String[] expectedFormatted = new String[] {"row cf:cq []    0x31 0x32 0x33 0x34 0x61 0x62 0x63 0x64",
 +        "row cf1:cq1 []    0x39 0x38 0x37 0x36 0x66 0x65 0x64 0x63", "row2 cf:cq []    0x31 0x33 0x35 0x37 0x39 0x62 0x64 0x66",
 +        "row2 cf1:cq []    0x32 0x34 0x36 0x38 0x61 0x63 0x65"};
 +
 +    int outputIndex = 0;
 +    while (outputIndex < output.length) {
 +      final String line = output[outputIndex];
 +
 +      if (line.startsWith("root@mock-instance")) {
 +        if (line.contains("formatter")) {
 +          formatterOn = true;
 +        }
 +
 +        outputIndex++;
 +      } else if (line.startsWith("row")) {
 +        int expectedIndex = 0;
 +        String[] comparisonData;
 +
 +        // Pick the type of data we expect (formatted or default)
 +        if (formatterOn) {
 +          comparisonData = expectedFormatted;
 +        } else {
 +          comparisonData = expectedDefault;
 +        }
 +
 +        // Ensure each output is what we expected
 +        while (expectedIndex + outputIndex < output.length && expectedIndex < expectedFormatted.length) {
 +          Assert.assertEquals(comparisonData[expectedIndex].trim(), output[expectedIndex + outputIndex].trim());
 +          expectedIndex++;
 +        }
 +
 +        outputIndex += expectedIndex;
 +      }
 +    }
 +  }
 +
 +  private String[] createCommands() {
 +    return new String[] {"table test", "insert row cf cq 1234abcd", "insert row cf1 cq1 9876fedc", "insert row2 cf cq 13579bdf", "insert row2 cf1 cq 2468ace",
 +        "scan", "formatter -t test -f org.apache.accumulo.core.util.shell.command.FormatterCommandTest$HexFormatter", "scan"};
 +  }
 +
 +  /**
 +   * <p>
 +   * Simple <code>Formatter</code> that will convert each character in the Value from decimal to hexadecimal. Will automatically skip over characters in the
 +   * value which do not fall within the [0-9,a-f] range.
 +   * </p>
 +   *
 +   * <p>
 +   * Example: <code>'0'</code> will be displayed as <code>'0x30'</code>
 +   * </p>
 +   */
 +  public static class HexFormatter implements Formatter {
 +    private Iterator<Entry<Key,Value>> iter = null;
 +    private boolean printTs = false;
 +
 +    private final static String tab = "\t";
 +    private final static String newline = "\n";
 +
 +    public HexFormatter() {}
 +
 +    @Override
 +    public boolean hasNext() {
 +      return this.iter.hasNext();
 +    }
 +
 +    @Override
 +    public String next() {
 +      final Entry<Key,Value> entry = iter.next();
 +
 +      String key;
 +
 +      // Observe the timestamps
 +      if (printTs) {
 +        key = entry.getKey().toString();
 +      } else {
 +        key = entry.getKey().toStringNoTime();
 +      }
 +
 +      final Value v = entry.getValue();
 +
 +      // Approximate how much space we'll need
 +      final StringBuilder sb = new StringBuilder(key.length() + v.getSize() * 5);
 +
 +      sb.append(key).append(tab);
 +
 +      for (byte b : v.get()) {
 +        if ((b >= 48 && b <= 57) || (b >= 97 || b <= 102)) {
 +          sb.append(String.format("0x%x ", Integer.valueOf(b)));
 +        }
 +      }
 +
 +      sb.append(newline);
 +
 +      return sb.toString();
 +    }
 +
 +    @Override
 +    public void remove() {}
 +
 +    @Override
 +    public void initialize(final Iterable<Entry<Key,Value>> scanner, final boolean printTimestamps) {
 +      this.iter = scanner.iterator();
 +      this.printTs = printTimestamps;
 +    }
 +  }
 +
 +}