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 <josh.el...@gmail.com> Authored: Tue Jan 13 18:09:51 2015 -0500 Committer: Josh Elser <josh.el...@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; + } + } + +}