ACCUMULO-3514 Use auto-service for start

Use @AutoService annotations and Java's ServiceLoader mechanism to discover
classes which are executable by Accumulo's "start" jar with a keyword.

This replaces manual intervention whenever we add a new option to the
bin/accumulo script and also auto-populates the usage for that script.


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

Branch: refs/heads/master
Commit: 8005e82c7ea8599ddbfe4b04e21f1f4dd44b5644
Parents: a2d272c
Author: Christopher Tubbs <ctubb...@apache.org>
Authored: Fri Jan 23 20:32:33 2015 -0500
Committer: Christopher Tubbs <ctubb...@apache.org>
Committed: Wed Feb 4 15:14:07 2015 -0500

----------------------------------------------------------------------
 core/pom.xml                                    |   5 +
 .../accumulo/core/file/rfile/PrintInfo.java     |  16 +-
 .../apache/accumulo/core/util/Classpath.java    |  35 +++
 .../apache/accumulo/core/util/CreateToken.java  |  19 +-
 .../org/apache/accumulo/core/util/Help.java     |  35 +++
 .../java/org/apache/accumulo/core/util/Jar.java |  59 ++++
 .../org/apache/accumulo/core/util/Version.java  |  22 +-
 minicluster/pom.xml                             |   5 +
 .../minicluster/MiniAccumuloRunner.java         |   2 +-
 .../minicluster/impl/MiniClusterExecutable.java |  37 +++
 pom.xml                                         |   5 +
 proxy/pom.xml                                   |   5 +
 .../java/org/apache/accumulo/proxy/Proxy.java   |  17 +-
 server/base/pom.xml                             |   5 +
 .../apache/accumulo/server/init/Initialize.java |  36 ++-
 .../org/apache/accumulo/server/util/Admin.java  |  31 +-
 .../org/apache/accumulo/server/util/Info.java   |  20 +-
 .../accumulo/server/util/LoginProperties.java   |  21 +-
 .../accumulo/server/util/ZooKeeperMain.java     |  15 +-
 server/gc/pom.xml                               |   5 +
 .../org/apache/accumulo/gc/GCExecutable.java    |  36 +++
 server/master/pom.xml                           |   5 +
 .../accumulo/master/MasterExecutable.java       |  36 +++
 server/monitor/pom.xml                          |   5 +
 .../accumulo/monitor/MonitorExecutable.java     |  36 +++
 server/tracer/pom.xml                           |   5 +
 .../accumulo/tracer/TracerExecutable.java       |  36 +++
 server/tserver/pom.xml                          |   5 +
 .../accumulo/tserver/TServerExecutable.java     |  36 +++
 shell/pom.xml                                   |   5 +
 .../java/org/apache/accumulo/shell/Shell.java   |  28 +-
 .../java/org/apache/accumulo/start/Main.java    | 300 ++++++++++---------
 .../accumulo/start/spi/KeywordExecutable.java   |  54 ++++
 .../org/apache/accumulo/start/MainTest.java     |  12 +-
 34 files changed, 802 insertions(+), 192 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/accumulo/blob/8005e82c/core/pom.xml
----------------------------------------------------------------------
diff --git a/core/pom.xml b/core/pom.xml
index 5fc7a6e..67b486e 100644
--- a/core/pom.xml
+++ b/core/pom.xml
@@ -31,6 +31,11 @@
       <artifactId>jcommander</artifactId>
     </dependency>
     <dependency>
+      <groupId>com.google.auto.service</groupId>
+      <artifactId>auto-service</artifactId>
+      <optional>true</optional>
+    </dependency>
+    <dependency>
       <groupId>com.google.guava</groupId>
       <artifactId>guava</artifactId>
     </dependency>

http://git-wip-us.apache.org/repos/asf/accumulo/blob/8005e82c/core/src/main/java/org/apache/accumulo/core/file/rfile/PrintInfo.java
----------------------------------------------------------------------
diff --git 
a/core/src/main/java/org/apache/accumulo/core/file/rfile/PrintInfo.java 
b/core/src/main/java/org/apache/accumulo/core/file/rfile/PrintInfo.java
index f29efcc..9ff1dd2 100644
--- a/core/src/main/java/org/apache/accumulo/core/file/rfile/PrintInfo.java
+++ b/core/src/main/java/org/apache/accumulo/core/file/rfile/PrintInfo.java
@@ -30,14 +30,18 @@ import org.apache.accumulo.core.data.Value;
 import org.apache.accumulo.core.file.blockfile.impl.CachableBlockFile;
 import org.apache.accumulo.core.file.rfile.RFile.Reader;
 import org.apache.accumulo.core.volume.VolumeConfiguration;
+import org.apache.accumulo.start.spi.KeywordExecutable;
 import org.apache.hadoop.conf.Configuration;
 import org.apache.hadoop.fs.FileSystem;
 import org.apache.hadoop.fs.Path;
 import org.apache.log4j.Logger;
 
 import com.beust.jcommander.Parameter;
+import com.google.auto.service.AutoService;
+
+@AutoService(KeywordExecutable.class)
+public class PrintInfo implements KeywordExecutable {
 
-public class PrintInfo {
   private static final Logger log = Logger.getLogger(PrintInfo.class);
 
   static class Opts extends Help {
@@ -50,6 +54,16 @@ public class PrintInfo {
   }
 
   public static void main(String[] args) throws Exception {
+    new PrintInfo().execute(args);
+  }
+
+  @Override
+  public String keyword() {
+    return "rfile-info";
+  }
+
+  @Override
+  public void execute(final String[] args) throws Exception {
     Configuration conf = new Configuration();
 
     AccumuloConfiguration aconf = 
SiteConfiguration.getInstance(DefaultConfiguration.getInstance());

http://git-wip-us.apache.org/repos/asf/accumulo/blob/8005e82c/core/src/main/java/org/apache/accumulo/core/util/Classpath.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/accumulo/core/util/Classpath.java 
b/core/src/main/java/org/apache/accumulo/core/util/Classpath.java
new file mode 100644
index 0000000..242ca52
--- /dev/null
+++ b/core/src/main/java/org/apache/accumulo/core/util/Classpath.java
@@ -0,0 +1,35 @@
+/*
+ * 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;
+
+import org.apache.accumulo.start.Main;
+import org.apache.accumulo.start.spi.KeywordExecutable;
+
+import com.google.auto.service.AutoService;
+
+@AutoService(KeywordExecutable.class)
+public class Classpath implements KeywordExecutable {
+  @Override
+  public String keyword() {
+    return "classpath";
+  }
+
+  @Override
+  public void execute(final String[] args) throws Exception {
+    Main.getVFSClassLoader().getMethod("printClassPath", new 
Class[0]).invoke(Main.getVFSClassLoader(), new Object[0]);
+  }
+}

http://git-wip-us.apache.org/repos/asf/accumulo/blob/8005e82c/core/src/main/java/org/apache/accumulo/core/util/CreateToken.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/accumulo/core/util/CreateToken.java 
b/core/src/main/java/org/apache/accumulo/core/util/CreateToken.java
index 79b241c..adb7c3d 100644
--- a/core/src/main/java/org/apache/accumulo/core/util/CreateToken.java
+++ b/core/src/main/java/org/apache/accumulo/core/util/CreateToken.java
@@ -33,14 +33,17 @@ import 
org.apache.accumulo.core.client.security.tokens.AuthenticationToken.Authe
 import 
org.apache.accumulo.core.client.security.tokens.AuthenticationToken.Properties;
 import 
org.apache.accumulo.core.client.security.tokens.AuthenticationToken.TokenProperty;
 import org.apache.accumulo.core.client.security.tokens.PasswordToken;
+import org.apache.accumulo.start.spi.KeywordExecutable;
 
 import com.beust.jcommander.Parameter;
+import com.google.auto.service.AutoService;
 
-public class CreateToken {
+@AutoService(KeywordExecutable.class)
+public class CreateToken implements KeywordExecutable {
 
-  private static ConsoleReader reader = null;
+  private ConsoleReader reader = null;
 
-  private static ConsoleReader getConsoleReader() throws IOException {
+  private ConsoleReader getConsoleReader() throws IOException {
     if (reader == null)
       reader = new ConsoleReader();
     return reader;
@@ -65,6 +68,16 @@ public class CreateToken {
   }
 
   public static void main(String[] args) {
+    new CreateToken().execute(args);
+  }
+
+  @Override
+  public String keyword() {
+    return "create-token";
+  }
+
+  @Override
+  public void execute(String[] args) {
     Opts opts = new Opts();
     opts.parseArgs(CreateToken.class.getName(), args);
 

http://git-wip-us.apache.org/repos/asf/accumulo/blob/8005e82c/core/src/main/java/org/apache/accumulo/core/util/Help.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/accumulo/core/util/Help.java 
b/core/src/main/java/org/apache/accumulo/core/util/Help.java
new file mode 100644
index 0000000..fda72fe
--- /dev/null
+++ b/core/src/main/java/org/apache/accumulo/core/util/Help.java
@@ -0,0 +1,35 @@
+/*
+ * 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;
+
+import org.apache.accumulo.start.Main;
+import org.apache.accumulo.start.spi.KeywordExecutable;
+
+import com.google.auto.service.AutoService;
+
+@AutoService(KeywordExecutable.class)
+public class Help implements KeywordExecutable {
+  @Override
+  public String keyword() {
+    return "help";
+  }
+
+  @Override
+  public void execute(final String[] args) throws Exception {
+    Main.printUsage();
+  }
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/accumulo/blob/8005e82c/core/src/main/java/org/apache/accumulo/core/util/Jar.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/accumulo/core/util/Jar.java 
b/core/src/main/java/org/apache/accumulo/core/util/Jar.java
new file mode 100644
index 0000000..2fc6d17
--- /dev/null
+++ b/core/src/main/java/org/apache/accumulo/core/util/Jar.java
@@ -0,0 +1,59 @@
+/*
+ * 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;
+
+import java.io.IOException;
+import java.util.jar.JarFile;
+
+import org.apache.accumulo.start.Main;
+import org.apache.accumulo.start.spi.KeywordExecutable;
+
+import com.google.auto.service.AutoService;
+
+@AutoService(KeywordExecutable.class)
+public class Jar implements KeywordExecutable {
+  @Override
+  public String keyword() {
+    return "jar";
+  }
+
+  @Override
+  public void execute(final String[] args) throws Exception {
+    // need at least one argument for the jar file, two arguments if the jar 
file manifest doesn't specify a main class
+    if (args.length == 0) {
+      Main.printUsage();
+      System.exit(1);
+    }
+    String jarFileName = args[0];
+    String candidateMainClass = args.length > 1 ? args[1] : null;
+    Class<?> mainClass = null;
+    try {
+      JarFile f = new JarFile(jarFileName);
+      mainClass = Main.loadClassFromJar(args, f, Main.getClassLoader());
+    } catch (IOException ioe) {
+      System.out.println("File " + jarFileName + " could not be found or 
read.");
+      System.exit(1);
+    } catch (ClassNotFoundException cnfe) {
+      System.out.println("Classname " + (candidateMainClass != null ? 
candidateMainClass : "in JAR manifest")
+          + " not found.  Please make sure you use the wholly qualified 
package name.");
+      System.exit(1);
+    }
+    // strip the jar file name and, if specified, the main class name from the 
args; then execute
+    String[] newArgs = Main.stripArgs(args, 
mainClass.getName().equals(candidateMainClass) ? 2 : 1);
+    Main.execMainClass(mainClass, newArgs);
+  }
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/accumulo/blob/8005e82c/core/src/main/java/org/apache/accumulo/core/util/Version.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/accumulo/core/util/Version.java 
b/core/src/main/java/org/apache/accumulo/core/util/Version.java
index ee645ff..069e5b1 100644
--- a/core/src/main/java/org/apache/accumulo/core/util/Version.java
+++ b/core/src/main/java/org/apache/accumulo/core/util/Version.java
@@ -19,7 +19,27 @@ package org.apache.accumulo.core.util;
 import java.util.regex.Matcher;
 import java.util.regex.Pattern;
 
-public class Version {
+import org.apache.accumulo.start.Main;
+import org.apache.accumulo.start.spi.KeywordExecutable;
+
+import com.google.auto.service.AutoService;
+
+@AutoService(KeywordExecutable.class)
+public class Version implements KeywordExecutable {
+
+  public Version() {}
+
+  @Override
+  public String keyword() {
+    return "version";
+  }
+
+  @Override
+  public void execute(final String[] args) throws Exception {
+    Class<?> runTMP = 
Main.getClassLoader().loadClass("org.apache.accumulo.core.Constants");
+    System.out.println(runTMP.getField("VERSION").get(null));
+  }
+
   String package_ = null;
   int major = 0;
   int minor = 0;

http://git-wip-us.apache.org/repos/asf/accumulo/blob/8005e82c/minicluster/pom.xml
----------------------------------------------------------------------
diff --git a/minicluster/pom.xml b/minicluster/pom.xml
index ee6cdc8..adec106 100644
--- a/minicluster/pom.xml
+++ b/minicluster/pom.xml
@@ -31,6 +31,11 @@
       <artifactId>jcommander</artifactId>
     </dependency>
     <dependency>
+      <groupId>com.google.auto.service</groupId>
+      <artifactId>auto-service</artifactId>
+      <optional>true</optional>
+    </dependency>
+    <dependency>
       <groupId>com.google.guava</groupId>
       <artifactId>guava</artifactId>
     </dependency>

http://git-wip-us.apache.org/repos/asf/accumulo/blob/8005e82c/minicluster/src/main/java/org/apache/accumulo/minicluster/MiniAccumuloRunner.java
----------------------------------------------------------------------
diff --git 
a/minicluster/src/main/java/org/apache/accumulo/minicluster/MiniAccumuloRunner.java
 
b/minicluster/src/main/java/org/apache/accumulo/minicluster/MiniAccumuloRunner.java
index c45abc0..b727f14 100644
--- 
a/minicluster/src/main/java/org/apache/accumulo/minicluster/MiniAccumuloRunner.java
+++ 
b/minicluster/src/main/java/org/apache/accumulo/minicluster/MiniAccumuloRunner.java
@@ -136,7 +136,7 @@ public class MiniAccumuloRunner {
    * @param args
    *          An optional -p argument can be specified with the path to a 
valid properties file.
    */
-  public static void main(String[] args) throws Exception {
+  public static void main(String[] args) throws IOException, 
InterruptedException {
     Opts opts = new Opts();
     opts.parseArgs(MiniAccumuloRunner.class.getName(), args);
 

http://git-wip-us.apache.org/repos/asf/accumulo/blob/8005e82c/minicluster/src/main/java/org/apache/accumulo/minicluster/impl/MiniClusterExecutable.java
----------------------------------------------------------------------
diff --git 
a/minicluster/src/main/java/org/apache/accumulo/minicluster/impl/MiniClusterExecutable.java
 
b/minicluster/src/main/java/org/apache/accumulo/minicluster/impl/MiniClusterExecutable.java
new file mode 100644
index 0000000..ecd5988
--- /dev/null
+++ 
b/minicluster/src/main/java/org/apache/accumulo/minicluster/impl/MiniClusterExecutable.java
@@ -0,0 +1,37 @@
+/*
+ * 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.minicluster.impl;
+
+import org.apache.accumulo.minicluster.MiniAccumuloRunner;
+import org.apache.accumulo.start.spi.KeywordExecutable;
+
+import com.google.auto.service.AutoService;
+
+@AutoService(KeywordExecutable.class)
+public class MiniClusterExecutable implements KeywordExecutable {
+
+  @Override
+  public String keyword() {
+    return "minicluster";
+  }
+
+  @Override
+  public void execute(final String[] args) throws Exception {
+    MiniAccumuloRunner.main(args);
+  }
+
+}

http://git-wip-us.apache.org/repos/asf/accumulo/blob/8005e82c/pom.xml
----------------------------------------------------------------------
diff --git a/pom.xml b/pom.xml
index dda1cfe..78aeb0c 100644
--- a/pom.xml
+++ b/pom.xml
@@ -150,6 +150,11 @@
         <version>1.32</version>
       </dependency>
       <dependency>
+        <groupId>com.google.auto.service</groupId>
+        <artifactId>auto-service</artifactId>
+        <version>1.0-rc2</version>
+      </dependency>
+      <dependency>
         <groupId>com.google.code.gson</groupId>
         <artifactId>gson</artifactId>
         <version>2.2.2</version>

http://git-wip-us.apache.org/repos/asf/accumulo/blob/8005e82c/proxy/pom.xml
----------------------------------------------------------------------
diff --git a/proxy/pom.xml b/proxy/pom.xml
index 9312d7b..7da1326 100644
--- a/proxy/pom.xml
+++ b/proxy/pom.xml
@@ -31,6 +31,11 @@
       <artifactId>jcommander</artifactId>
     </dependency>
     <dependency>
+      <groupId>com.google.auto.service</groupId>
+      <artifactId>auto-service</artifactId>
+      <optional>true</optional>
+    </dependency>
+    <dependency>
       <groupId>com.google.guava</groupId>
       <artifactId>guava</artifactId>
     </dependency>

http://git-wip-us.apache.org/repos/asf/accumulo/blob/8005e82c/proxy/src/main/java/org/apache/accumulo/proxy/Proxy.java
----------------------------------------------------------------------
diff --git a/proxy/src/main/java/org/apache/accumulo/proxy/Proxy.java 
b/proxy/src/main/java/org/apache/accumulo/proxy/Proxy.java
index 0a4d12e..f9039be 100644
--- a/proxy/src/main/java/org/apache/accumulo/proxy/Proxy.java
+++ b/proxy/src/main/java/org/apache/accumulo/proxy/Proxy.java
@@ -39,6 +39,7 @@ import org.apache.accumulo.server.rpc.TServerUtils;
 import org.apache.accumulo.server.rpc.ThriftServerType;
 import org.apache.accumulo.server.rpc.TimedProcessor;
 import org.apache.accumulo.server.rpc.UGIAssumingProcessor;
+import org.apache.accumulo.start.spi.KeywordExecutable;
 import org.apache.commons.lang.StringUtils;
 import org.apache.hadoop.security.UserGroupInformation;
 import org.apache.log4j.Logger;
@@ -48,10 +49,12 @@ import org.apache.thrift.protocol.TProtocolFactory;
 
 import com.beust.jcommander.IStringConverter;
 import com.beust.jcommander.Parameter;
+import com.google.auto.service.AutoService;
 import com.google.common.io.Files;
 import com.google.common.net.HostAndPort;
 
-public class Proxy {
+@AutoService(KeywordExecutable.class)
+public class Proxy implements KeywordExecutable {
 
   private static final Logger log = Logger.getLogger(Proxy.class);
 
@@ -99,7 +102,13 @@ public class Proxy {
     Properties prop;
   }
 
-  public static void main(String[] args) throws Exception {
+  @Override
+  public String keyword() {
+    return "proxy";
+  }
+
+  @Override
+  public void execute(final String[] args) throws Exception {
     Opts opts = new Opts();
     opts.parseArgs(Proxy.class.getName(), args);
 
@@ -161,6 +170,10 @@ public class Proxy {
     }
   }
 
+  public static void main(String[] args) throws Exception {
+    new Proxy().execute(args);
+  }
+
   public static ServerAddress createProxyServer(HostAndPort address, 
TProtocolFactory protocolFactory, Properties properties) throws Exception {
     final int numThreads = 
Integer.parseInt(properties.getProperty(THRIFT_THREAD_POOL_SIZE_KEY, 
THRIFT_THREAD_POOL_SIZE_DEFAULT));
     final long maxFrameSize = 
AccumuloConfiguration.getMemoryInBytes(properties.getProperty(THRIFT_MAX_FRAME_SIZE_KEY,
 THRIFT_MAX_FRAME_SIZE_DEFAULT));

http://git-wip-us.apache.org/repos/asf/accumulo/blob/8005e82c/server/base/pom.xml
----------------------------------------------------------------------
diff --git a/server/base/pom.xml b/server/base/pom.xml
index c21a168..e01da1c 100644
--- a/server/base/pom.xml
+++ b/server/base/pom.xml
@@ -32,6 +32,11 @@
       <artifactId>jcommander</artifactId>
     </dependency>
     <dependency>
+      <groupId>com.google.auto.service</groupId>
+      <artifactId>auto-service</artifactId>
+      <optional>true</optional>
+    </dependency>
+    <dependency>
       <groupId>com.google.guava</groupId>
       <artifactId>guava</artifactId>
     </dependency>

http://git-wip-us.apache.org/repos/asf/accumulo/blob/8005e82c/server/base/src/main/java/org/apache/accumulo/server/init/Initialize.java
----------------------------------------------------------------------
diff --git 
a/server/base/src/main/java/org/apache/accumulo/server/init/Initialize.java 
b/server/base/src/main/java/org/apache/accumulo/server/init/Initialize.java
index 0a1f411..495e0e0 100644
--- a/server/base/src/main/java/org/apache/accumulo/server/init/Initialize.java
+++ b/server/base/src/main/java/org/apache/accumulo/server/init/Initialize.java
@@ -20,6 +20,7 @@ import static java.nio.charset.StandardCharsets.UTF_8;
 import static 
org.apache.accumulo.core.metadata.schema.MetadataSchema.TabletsSection.ServerColumnFamily.DIRECTORY_COLUMN;
 import static 
org.apache.accumulo.core.metadata.schema.MetadataSchema.TabletsSection.ServerColumnFamily.TIME_COLUMN;
 import static 
org.apache.accumulo.core.metadata.schema.MetadataSchema.TabletsSection.TabletColumnFamily.PREV_ROW_COLUMN;
+
 import java.io.FileNotFoundException;
 import java.io.IOException;
 import java.util.Arrays;
@@ -34,6 +35,7 @@ import java.util.TreeMap;
 import java.util.UUID;
 
 import jline.console.ConsoleReader;
+
 import org.apache.accumulo.core.Constants;
 import org.apache.accumulo.core.cli.Help;
 import org.apache.accumulo.core.client.AccumuloSecurityException;
@@ -94,6 +96,7 @@ import org.apache.accumulo.server.tablets.TabletTime;
 import org.apache.accumulo.server.util.ReplicationTableUtil;
 import org.apache.accumulo.server.util.TablePropUtil;
 import org.apache.accumulo.server.zookeeper.ZooReaderWriter;
+import org.apache.accumulo.start.spi.KeywordExecutable;
 import org.apache.commons.lang.StringUtils;
 import org.apache.hadoop.conf.Configuration;
 import org.apache.hadoop.fs.FileStatus;
@@ -106,6 +109,7 @@ import org.apache.zookeeper.KeeperException;
 import org.apache.zookeeper.ZooDefs.Ids;
 
 import com.beust.jcommander.Parameter;
+import com.google.auto.service.AutoService;
 import com.google.common.base.Joiner;
 import com.google.common.base.Optional;
 
@@ -113,7 +117,8 @@ import com.google.common.base.Optional;
  * This class is used to setup the directory structure and the root tablet to 
get an instance started
  *
  */
-public class Initialize {
+@AutoService(KeywordExecutable.class)
+public class Initialize implements KeywordExecutable {
   private static final Logger log = Logger.getLogger(Initialize.class);
   private static final String DEFAULT_ROOT_USER = "root";
   private static final String TABLE_TABLETS_TABLET_DIR = "/table_info";
@@ -267,7 +272,7 @@ public class Initialize {
     log.fatal("The current value of " + Property.INSTANCE_VOLUMES + " is |" + 
instanceVolumes + "|");
   }
 
-  public static boolean doInit(Opts opts, Configuration conf, VolumeManager 
fs) throws IOException {
+  public boolean doInit(Opts opts, Configuration conf, VolumeManager fs) 
throws IOException {
     if (!checkInit(conf, fs, SiteConfiguration.getInstance())) {
       return false;
     }
@@ -301,7 +306,7 @@ public class Initialize {
     return initialize(opts, instanceNamePath, fs, rootUser);
   }
 
-  private static boolean initialize(Opts opts, String instanceNamePath, 
VolumeManager fs, String rootUser) {
+  private boolean initialize(Opts opts, String instanceNamePath, VolumeManager 
fs, String rootUser) {
 
     UUID uuid = UUID.randomUUID();
     // the actual disk locations of the root table and tablets
@@ -399,7 +404,7 @@ public class Initialize {
     }
   }
 
-  private static void initFileSystem(Opts opts, VolumeManager fs, UUID uuid, 
String rootTabletDir) throws IOException {
+  private void initFileSystem(Opts opts, VolumeManager fs, UUID uuid, String 
rootTabletDir) throws IOException {
     initDirs(fs, uuid, 
VolumeConfiguration.getVolumeUris(SiteConfiguration.getInstance()), false);
 
     // initialize initial system tables config in zookeeper
@@ -539,7 +544,7 @@ public class Initialize {
     zoo.putPersistentData(zkInstanceRoot + ReplicationConstants.ZOO_TSERVERS, 
EMPTY_BYTE_ARRAY, NodeExistsPolicy.FAIL);
   }
 
-  private static String getInstanceNamePath(Opts opts) throws IOException, 
KeeperException, InterruptedException {
+  private String getInstanceNamePath(Opts opts) throws IOException, 
KeeperException, InterruptedException {
     // setup the instance name
     String instanceName, instanceNamePath = null;
     boolean exists = true;
@@ -571,7 +576,7 @@ public class Initialize {
     return instanceNamePath;
   }
 
-  private static String getRootUserName(Opts opts) throws IOException {
+  private String getRootUserName(Opts opts) throws IOException {
     AccumuloConfiguration conf = SiteConfiguration.getInstance();
     final String keytab = conf.get(Property.GENERAL_KERBEROS_KEYTAB);
     if (keytab.equals(Property.GENERAL_KERBEROS_KEYTAB.getDefaultValue()) || 
!conf.getBoolean(Property.INSTANCE_RPC_SASL_ENABLED)) {
@@ -597,7 +602,7 @@ public class Initialize {
     } while (true);
   }
 
-  private static byte[] getRootPassword(Opts opts, String rootUser) throws 
IOException {
+  private byte[] getRootPassword(Opts opts, String rootUser) throws 
IOException {
     if (opts.cliPassword != null) {
       return opts.cliPassword.getBytes(UTF_8);
     }
@@ -693,11 +698,10 @@ public class Initialize {
     UUID uuid = UUID.fromString(ZooUtil.getInstanceIDFromHdfs(iidPath, 
SiteConfiguration.getInstance()));
     for (Pair<Path,Path> replacementVolume : 
ServerConstants.getVolumeReplacements()) {
       if (aBasePath.equals(replacementVolume.getSecond()))
-        log.error(aBasePath + " is set to be replaced in " + 
Property.INSTANCE_VOLUMES_REPLACEMENTS + " and should not appear in " +
-            Property.INSTANCE_VOLUMES + ". It is highly recommended that this 
property be removed as data could still be written to this volume.");
+        log.error(aBasePath + " is set to be replaced in " + 
Property.INSTANCE_VOLUMES_REPLACEMENTS + " and should not appear in " + 
Property.INSTANCE_VOLUMES
+            + ". It is highly recommended that this property be removed as 
data could still be written to this volume.");
     }
 
-
     if (ServerConstants.DATA_VERSION != 
Accumulo.getAccumuloPersistentVersion(versionPath.getFileSystem(CachedConfiguration.getInstance()),
 versionPath)) {
       throw new IOException("Accumulo " + Constants.VERSION + " cannot 
initialize data version " + Accumulo.getAccumuloPersistentVersion(fs));
     }
@@ -722,7 +726,13 @@ public class Initialize {
     byte[] rootpass = null;
   }
 
-  public static void main(String[] args) {
+  @Override
+  public String keyword() {
+    return "init";
+  }
+
+  @Override
+  public void execute(final String[] args) {
     Opts opts = new Opts();
     opts.parseArgs(Initialize.class.getName(), args);
 
@@ -756,4 +766,8 @@ public class Initialize {
       throw new RuntimeException(e);
     }
   }
+
+  public static void main(String[] args) {
+    new Initialize().execute(args);
+  }
 }

http://git-wip-us.apache.org/repos/asf/accumulo/blob/8005e82c/server/base/src/main/java/org/apache/accumulo/server/util/Admin.java
----------------------------------------------------------------------
diff --git 
a/server/base/src/main/java/org/apache/accumulo/server/util/Admin.java 
b/server/base/src/main/java/org/apache/accumulo/server/util/Admin.java
index 77d5ea1..adf3364 100644
--- a/server/base/src/main/java/org/apache/accumulo/server/util/Admin.java
+++ b/server/base/src/main/java/org/apache/accumulo/server/util/Admin.java
@@ -58,16 +58,19 @@ import org.apache.accumulo.server.AccumuloServerContext;
 import org.apache.accumulo.server.cli.ClientOpts;
 import org.apache.accumulo.server.conf.ServerConfigurationFactory;
 import org.apache.accumulo.server.security.SecurityUtil;
+import org.apache.accumulo.start.spi.KeywordExecutable;
 import org.apache.hadoop.conf.Configuration;
 import org.apache.log4j.Logger;
 
 import com.beust.jcommander.JCommander;
 import com.beust.jcommander.Parameter;
 import com.beust.jcommander.Parameters;
+import com.google.auto.service.AutoService;
 import com.google.common.collect.Lists;
 import com.google.common.net.HostAndPort;
 
-public class Admin {
+@AutoService(KeywordExecutable.class)
+public class Admin implements KeywordExecutable {
   private static final Logger log = Logger.getLogger(Admin.class);
 
   static class AdminOpts extends ClientOpts {
@@ -139,6 +142,16 @@ public class Admin {
   }
 
   public static void main(String[] args) {
+    new Admin().execute(args);
+  }
+
+  @Override
+  public String keyword() {
+    return "admin";
+  }
+
+  @Override
+  public void execute(final String[] args) {
     boolean everything;
 
     AdminOpts opts = new AdminOpts();
@@ -368,11 +381,11 @@ public class Admin {
   private static final MessageFormat tablePermFormat = new 
MessageFormat("grant Table.{0} -t {1} -u {2}\n");
   private static final MessageFormat userAuthsFormat = new 
MessageFormat("setauths -u {0} -s {1}\n");
 
-  private static DefaultConfiguration defaultConfig;
-  private static Map<String,String> siteConfig, systemConfig;
-  private static List<String> localUsers;
+  private DefaultConfiguration defaultConfig;
+  private Map<String,String> siteConfig, systemConfig;
+  private List<String> localUsers;
 
-  public static void printConfig(ClientContext context, DumpConfigCommand 
opts) throws Exception {
+  public void printConfig(ClientContext context, DumpConfigCommand opts) 
throws Exception {
 
     File outputDirectory = null;
     if (opts.directory != null) {
@@ -431,7 +444,7 @@ public class Admin {
     }
   }
 
-  private static String getDefaultConfigValue(String key) {
+  private String getDefaultConfigValue(String key) {
     if (null == key)
       return null;
 
@@ -445,7 +458,7 @@ public class Admin {
     return defaultValue;
   }
 
-  private static void printNameSpaceConfiguration(Connector connector, String 
namespace, File outputDirectory) throws IOException, AccumuloException,
+  private void printNameSpaceConfiguration(Connector connector, String 
namespace, File outputDirectory) throws IOException, AccumuloException,
       AccumuloSecurityException, NamespaceNotFoundException {
     File namespaceScript = new File(outputDirectory, namespace + 
NS_FILE_SUFFIX);
     FileWriter nsWriter = new FileWriter(namespaceScript);
@@ -495,7 +508,7 @@ public class Admin {
     userWriter.close();
   }
 
-  private static void printSystemConfiguration(Connector connector, File 
outputDirectory) throws IOException, AccumuloException, 
AccumuloSecurityException {
+  private void printSystemConfiguration(Connector connector, File 
outputDirectory) throws IOException, AccumuloException, 
AccumuloSecurityException {
     Configuration conf = new Configuration(false);
     TreeMap<String,String> site = new TreeMap<String,String>(siteConfig);
     for (Entry<String,String> prop : site.entrySet()) {
@@ -520,7 +533,7 @@ public class Admin {
     }
   }
 
-  private static void printTableConfiguration(Connector connector, String 
tableName, File outputDirectory) throws AccumuloException, 
TableNotFoundException,
+  private void printTableConfiguration(Connector connector, String tableName, 
File outputDirectory) throws AccumuloException, TableNotFoundException,
       IOException, AccumuloSecurityException {
     File tableBackup = new File(outputDirectory, tableName + ".cfg");
     FileWriter writer = new FileWriter(tableBackup);

http://git-wip-us.apache.org/repos/asf/accumulo/blob/8005e82c/server/base/src/main/java/org/apache/accumulo/server/util/Info.java
----------------------------------------------------------------------
diff --git 
a/server/base/src/main/java/org/apache/accumulo/server/util/Info.java 
b/server/base/src/main/java/org/apache/accumulo/server/util/Info.java
index 29fa135..d5440a6 100644
--- a/server/base/src/main/java/org/apache/accumulo/server/util/Info.java
+++ b/server/base/src/main/java/org/apache/accumulo/server/util/Info.java
@@ -19,12 +19,28 @@ package org.apache.accumulo.server.util;
 import org.apache.accumulo.core.client.Instance;
 import org.apache.accumulo.core.util.MonitorUtil;
 import org.apache.accumulo.server.client.HdfsZooInstance;
+import org.apache.accumulo.start.spi.KeywordExecutable;
+import org.apache.zookeeper.KeeperException;
 
-public class Info {
-  public static void main(String[] args) throws Exception {
+import com.google.auto.service.AutoService;
+
+@AutoService(KeywordExecutable.class)
+public class Info implements KeywordExecutable {
+
+  @Override
+  public String keyword() {
+    return "info";
+  }
+
+  @Override
+  public void execute(final String[] args) throws KeeperException, 
InterruptedException {
     Instance instance = HdfsZooInstance.getInstance();
     System.out.println("monitor: " + MonitorUtil.getLocation(instance));
     System.out.println("masters: " + instance.getMasterLocations());
     System.out.println("zookeepers: " + instance.getZooKeepers());
   }
+
+  public static void main(String[] args) throws Exception {
+    new Info().execute(args);
+  }
 }

http://git-wip-us.apache.org/repos/asf/accumulo/blob/8005e82c/server/base/src/main/java/org/apache/accumulo/server/util/LoginProperties.java
----------------------------------------------------------------------
diff --git 
a/server/base/src/main/java/org/apache/accumulo/server/util/LoginProperties.java
 
b/server/base/src/main/java/org/apache/accumulo/server/util/LoginProperties.java
index be5a7c8..8ffe586 100644
--- 
a/server/base/src/main/java/org/apache/accumulo/server/util/LoginProperties.java
+++ 
b/server/base/src/main/java/org/apache/accumulo/server/util/LoginProperties.java
@@ -28,13 +28,20 @@ import org.apache.accumulo.server.client.HdfsZooInstance;
 import org.apache.accumulo.server.conf.ServerConfigurationFactory;
 import org.apache.accumulo.server.security.handler.Authenticator;
 import org.apache.accumulo.start.classloader.vfs.AccumuloVFSClassLoader;
+import org.apache.accumulo.start.spi.KeywordExecutable;
 
-/**
- *
- */
-public class LoginProperties {
+import com.google.auto.service.AutoService;
 
-  public static void main(String[] args) throws Exception {
+@AutoService(KeywordExecutable.class)
+public class LoginProperties implements KeywordExecutable {
+
+  @Override
+  public String keyword() {
+    return "login-info";
+  }
+
+  @Override
+  public void execute(String[] args) throws Exception {
     AccumuloConfiguration config = new 
ServerConfigurationFactory(HdfsZooInstance.getInstance()).getConfiguration();
     Authenticator authenticator = 
AccumuloVFSClassLoader.getClassLoader().loadClass(config.get(Property.INSTANCE_SECURITY_AUTHENTICATOR))
         .asSubclass(Authenticator.class).newInstance();
@@ -56,4 +63,8 @@ public class LoginProperties {
       System.out.println();
     }
   }
+
+  public static void main(String[] args) throws Exception {
+    new LoginProperties().execute(args);
+  }
 }

http://git-wip-us.apache.org/repos/asf/accumulo/blob/8005e82c/server/base/src/main/java/org/apache/accumulo/server/util/ZooKeeperMain.java
----------------------------------------------------------------------
diff --git 
a/server/base/src/main/java/org/apache/accumulo/server/util/ZooKeeperMain.java 
b/server/base/src/main/java/org/apache/accumulo/server/util/ZooKeeperMain.java
index 0edcf71..c8ef6d5 100644
--- 
a/server/base/src/main/java/org/apache/accumulo/server/util/ZooKeeperMain.java
+++ 
b/server/base/src/main/java/org/apache/accumulo/server/util/ZooKeeperMain.java
@@ -21,12 +21,15 @@ import org.apache.accumulo.core.client.Instance;
 import org.apache.accumulo.server.ServerConstants;
 import org.apache.accumulo.server.client.HdfsZooInstance;
 import org.apache.accumulo.server.fs.VolumeManagerImpl;
+import org.apache.accumulo.start.spi.KeywordExecutable;
 import org.apache.hadoop.fs.FileSystem;
 import org.apache.hadoop.fs.Path;
 
 import com.beust.jcommander.Parameter;
+import com.google.auto.service.AutoService;
 
-public class ZooKeeperMain {
+@AutoService(KeywordExecutable.class)
+public class ZooKeeperMain implements KeywordExecutable {
 
   static class Opts extends Help {
 
@@ -38,6 +41,16 @@ public class ZooKeeperMain {
   }
 
   public static void main(String[] args) throws Exception {
+    new ZooKeeperMain().execute(args);
+  }
+
+  @Override
+  public String keyword() {
+    return "zookeeper";
+  }
+
+  @Override
+  public void execute(final String[] args) throws Exception {
     Opts opts = new Opts();
     opts.parseArgs(ZooKeeperMain.class.getName(), args);
     FileSystem fs = VolumeManagerImpl.get().getDefaultVolume().getFileSystem();

http://git-wip-us.apache.org/repos/asf/accumulo/blob/8005e82c/server/gc/pom.xml
----------------------------------------------------------------------
diff --git a/server/gc/pom.xml b/server/gc/pom.xml
index 9602b95..694ffe9 100644
--- a/server/gc/pom.xml
+++ b/server/gc/pom.xml
@@ -32,6 +32,11 @@
       <artifactId>jcommander</artifactId>
     </dependency>
     <dependency>
+      <groupId>com.google.auto.service</groupId>
+      <artifactId>auto-service</artifactId>
+      <optional>true</optional>
+    </dependency>
+    <dependency>
       <groupId>com.google.guava</groupId>
       <artifactId>guava</artifactId>
     </dependency>

http://git-wip-us.apache.org/repos/asf/accumulo/blob/8005e82c/server/gc/src/main/java/org/apache/accumulo/gc/GCExecutable.java
----------------------------------------------------------------------
diff --git a/server/gc/src/main/java/org/apache/accumulo/gc/GCExecutable.java 
b/server/gc/src/main/java/org/apache/accumulo/gc/GCExecutable.java
new file mode 100644
index 0000000..0c74444
--- /dev/null
+++ b/server/gc/src/main/java/org/apache/accumulo/gc/GCExecutable.java
@@ -0,0 +1,36 @@
+/*
+ * 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.gc;
+
+import java.io.IOException;
+
+import org.apache.accumulo.start.spi.KeywordExecutable;
+
+import com.google.auto.service.AutoService;
+
+@AutoService(KeywordExecutable.class)
+public class GCExecutable implements KeywordExecutable {
+  @Override
+  public String keyword() {
+    return "gc";
+  }
+
+  @Override
+  public void execute(final String[] args) throws IOException {
+    SimpleGarbageCollector.main(args);
+  }
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/accumulo/blob/8005e82c/server/master/pom.xml
----------------------------------------------------------------------
diff --git a/server/master/pom.xml b/server/master/pom.xml
index 7e9ab1d..dbeb429 100644
--- a/server/master/pom.xml
+++ b/server/master/pom.xml
@@ -32,6 +32,11 @@
       <artifactId>jcommander</artifactId>
     </dependency>
     <dependency>
+      <groupId>com.google.auto.service</groupId>
+      <artifactId>auto-service</artifactId>
+      <optional>true</optional>
+    </dependency>
+    <dependency>
       <groupId>com.google.guava</groupId>
       <artifactId>guava</artifactId>
     </dependency>

http://git-wip-us.apache.org/repos/asf/accumulo/blob/8005e82c/server/master/src/main/java/org/apache/accumulo/master/MasterExecutable.java
----------------------------------------------------------------------
diff --git 
a/server/master/src/main/java/org/apache/accumulo/master/MasterExecutable.java 
b/server/master/src/main/java/org/apache/accumulo/master/MasterExecutable.java
new file mode 100644
index 0000000..aabfa6d
--- /dev/null
+++ 
b/server/master/src/main/java/org/apache/accumulo/master/MasterExecutable.java
@@ -0,0 +1,36 @@
+/*
+ * 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.master;
+
+import org.apache.accumulo.start.spi.KeywordExecutable;
+
+import com.google.auto.service.AutoService;
+
+@AutoService(KeywordExecutable.class)
+public class MasterExecutable implements KeywordExecutable {
+
+  @Override
+  public String keyword() {
+    return "master";
+  }
+
+  @Override
+  public void execute(final String[] args) throws Exception {
+    Master.main(args);
+  }
+
+}

http://git-wip-us.apache.org/repos/asf/accumulo/blob/8005e82c/server/monitor/pom.xml
----------------------------------------------------------------------
diff --git a/server/monitor/pom.xml b/server/monitor/pom.xml
index ba61aeb..51262c1 100644
--- a/server/monitor/pom.xml
+++ b/server/monitor/pom.xml
@@ -28,6 +28,11 @@
   <description>A web server for monitoring Apache Accumulo.</description>
   <dependencies>
     <dependency>
+      <groupId>com.google.auto.service</groupId>
+      <artifactId>auto-service</artifactId>
+      <optional>true</optional>
+    </dependency>
+    <dependency>
       <groupId>com.google.code.gson</groupId>
       <artifactId>gson</artifactId>
     </dependency>

http://git-wip-us.apache.org/repos/asf/accumulo/blob/8005e82c/server/monitor/src/main/java/org/apache/accumulo/monitor/MonitorExecutable.java
----------------------------------------------------------------------
diff --git 
a/server/monitor/src/main/java/org/apache/accumulo/monitor/MonitorExecutable.java
 
b/server/monitor/src/main/java/org/apache/accumulo/monitor/MonitorExecutable.java
new file mode 100644
index 0000000..9da7519
--- /dev/null
+++ 
b/server/monitor/src/main/java/org/apache/accumulo/monitor/MonitorExecutable.java
@@ -0,0 +1,36 @@
+/*
+ * 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.monitor;
+
+import org.apache.accumulo.start.spi.KeywordExecutable;
+
+import com.google.auto.service.AutoService;
+
+@AutoService(KeywordExecutable.class)
+public class MonitorExecutable implements KeywordExecutable {
+
+  @Override
+  public String keyword() {
+    return "monitor";
+  }
+
+  @Override
+  public void execute(final String[] args) throws Exception {
+    Monitor.main(args);
+  }
+
+}

http://git-wip-us.apache.org/repos/asf/accumulo/blob/8005e82c/server/tracer/pom.xml
----------------------------------------------------------------------
diff --git a/server/tracer/pom.xml b/server/tracer/pom.xml
index ac9f45f..b895d67 100644
--- a/server/tracer/pom.xml
+++ b/server/tracer/pom.xml
@@ -28,6 +28,11 @@
   <description>The tracer server for Apache Accumulo to listen for, and store, 
distributed tracing messages.</description>
   <dependencies>
     <dependency>
+      <groupId>com.google.auto.service</groupId>
+      <artifactId>auto-service</artifactId>
+      <optional>true</optional>
+    </dependency>
+    <dependency>
       <groupId>log4j</groupId>
       <artifactId>log4j</artifactId>
     </dependency>

http://git-wip-us.apache.org/repos/asf/accumulo/blob/8005e82c/server/tracer/src/main/java/org/apache/accumulo/tracer/TracerExecutable.java
----------------------------------------------------------------------
diff --git 
a/server/tracer/src/main/java/org/apache/accumulo/tracer/TracerExecutable.java 
b/server/tracer/src/main/java/org/apache/accumulo/tracer/TracerExecutable.java
new file mode 100644
index 0000000..3995924
--- /dev/null
+++ 
b/server/tracer/src/main/java/org/apache/accumulo/tracer/TracerExecutable.java
@@ -0,0 +1,36 @@
+/*
+ * 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.tracer;
+
+import org.apache.accumulo.start.spi.KeywordExecutable;
+
+import com.google.auto.service.AutoService;
+
+@AutoService(KeywordExecutable.class)
+public class TracerExecutable implements KeywordExecutable {
+
+  @Override
+  public String keyword() {
+    return "tracer";
+  }
+
+  @Override
+  public void execute(final String[] args) throws Exception {
+    TraceServer.main(args);
+  }
+
+}

http://git-wip-us.apache.org/repos/asf/accumulo/blob/8005e82c/server/tserver/pom.xml
----------------------------------------------------------------------
diff --git a/server/tserver/pom.xml b/server/tserver/pom.xml
index cd0f8ef..96eb046 100644
--- a/server/tserver/pom.xml
+++ b/server/tserver/pom.xml
@@ -32,6 +32,11 @@
       <artifactId>jcommander</artifactId>
     </dependency>
     <dependency>
+      <groupId>com.google.auto.service</groupId>
+      <artifactId>auto-service</artifactId>
+      <optional>true</optional>
+    </dependency>
+    <dependency>
       <groupId>com.google.guava</groupId>
       <artifactId>guava</artifactId>
     </dependency>

http://git-wip-us.apache.org/repos/asf/accumulo/blob/8005e82c/server/tserver/src/main/java/org/apache/accumulo/tserver/TServerExecutable.java
----------------------------------------------------------------------
diff --git 
a/server/tserver/src/main/java/org/apache/accumulo/tserver/TServerExecutable.java
 
b/server/tserver/src/main/java/org/apache/accumulo/tserver/TServerExecutable.java
new file mode 100644
index 0000000..4c197ca
--- /dev/null
+++ 
b/server/tserver/src/main/java/org/apache/accumulo/tserver/TServerExecutable.java
@@ -0,0 +1,36 @@
+/*
+ * 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.tserver;
+
+import org.apache.accumulo.start.spi.KeywordExecutable;
+
+import com.google.auto.service.AutoService;
+
+@AutoService(KeywordExecutable.class)
+public class TServerExecutable implements KeywordExecutable {
+
+  @Override
+  public String keyword() {
+    return "tserver";
+  }
+
+  @Override
+  public void execute(final String[] args) throws Exception {
+    TabletServer.main(args);
+  }
+
+}

http://git-wip-us.apache.org/repos/asf/accumulo/blob/8005e82c/shell/pom.xml
----------------------------------------------------------------------
diff --git a/shell/pom.xml b/shell/pom.xml
index db3530f..6ac997f 100644
--- a/shell/pom.xml
+++ b/shell/pom.xml
@@ -31,6 +31,11 @@
       <artifactId>jcommander</artifactId>
     </dependency>
     <dependency>
+      <groupId>com.google.auto.service</groupId>
+      <artifactId>auto-service</artifactId>
+      <optional>true</optional>
+    </dependency>
+    <dependency>
       <groupId>com.google.guava</groupId>
       <artifactId>guava</artifactId>
     </dependency>

http://git-wip-us.apache.org/repos/asf/accumulo/blob/8005e82c/shell/src/main/java/org/apache/accumulo/shell/Shell.java
----------------------------------------------------------------------
diff --git a/shell/src/main/java/org/apache/accumulo/shell/Shell.java 
b/shell/src/main/java/org/apache/accumulo/shell/Shell.java
index a64ff45..b63f291 100644
--- a/shell/src/main/java/org/apache/accumulo/shell/Shell.java
+++ b/shell/src/main/java/org/apache/accumulo/shell/Shell.java
@@ -165,6 +165,7 @@ 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.accumulo.start.spi.KeywordExecutable;
 import org.apache.commons.cli.BasicParser;
 import org.apache.commons.cli.CommandLine;
 import org.apache.commons.cli.HelpFormatter;
@@ -179,11 +180,13 @@ import org.apache.log4j.Logger;
 
 import com.beust.jcommander.JCommander;
 import com.beust.jcommander.ParameterException;
+import com.google.auto.service.AutoService;
 
 /**
  * A convenient console interface to perform basic accumulo functions Includes 
auto-complete, help, and quoted strings with escape sequences
  */
-public class Shell extends ShellOptions {
+@AutoService(KeywordExecutable.class)
+public class Shell extends ShellOptions implements KeywordExecutable {
   public static final Logger log = Logger.getLogger(Shell.class);
   private static final Logger audit = Logger.getLogger(Shell.class.getName() + 
".audit");
 
@@ -535,20 +538,29 @@ public class Shell extends ShellOptions {
     return classloader;
   }
 
-  public static void main(String args[]) throws IOException {
-    Shell shell = new Shell();
-    try{
-      if (!shell.config(args)) {
-        System.exit(shell.getExitCode());
+  @Override
+  public String keyword() {
+    return "shell";
+  }
+
+  @Override
+  public void execute(final String[] args) throws IOException {
+    try {
+      if (!config(args)) {
+        System.exit(getExitCode());
       }
 
-      System.exit(shell.start());
+      System.exit(start());
     } finally {
-      shell.shutdown();
+      shutdown();
       DistributedTrace.disable();
     }
   }
 
+  public static void main(String args[]) throws IOException {
+    new Shell().execute(args);
+  }
+
   public int start() throws IOException {
     String input;
     if (isVerbose())

http://git-wip-us.apache.org/repos/asf/accumulo/blob/8005e82c/start/src/main/java/org/apache/accumulo/start/Main.java
----------------------------------------------------------------------
diff --git a/start/src/main/java/org/apache/accumulo/start/Main.java 
b/start/src/main/java/org/apache/accumulo/start/Main.java
index c820883..fa79196 100644
--- a/start/src/main/java/org/apache/accumulo/start/Main.java
+++ b/start/src/main/java/org/apache/accumulo/start/Main.java
@@ -20,167 +20,139 @@ import java.io.IOException;
 import java.lang.reflect.InvocationTargetException;
 import java.lang.reflect.Method;
 import java.lang.reflect.Modifier;
+import java.util.Collections;
+import java.util.Map;
+import java.util.ServiceLoader;
+import java.util.TreeMap;
+import java.util.TreeSet;
 import java.util.jar.Attributes;
 import java.util.jar.JarFile;
 
 import org.apache.accumulo.start.classloader.AccumuloClassLoader;
+import org.apache.accumulo.start.spi.KeywordExecutable;
 import org.apache.log4j.Logger;
 
 public class Main {
 
   private static final Logger log = Logger.getLogger(Main.class);
+  private static ClassLoader classLoader;
+  private static Class<?> vfsClassLoader;
+  private static Map<String,KeywordExecutable> servicesMap;
 
-  public static void main(String[] args) {
-    Runnable r = null;
-
+  public static void main(final String[] args) {
     try {
       if (args.length == 0) {
         printUsage();
         System.exit(1);
       }
 
-      
Thread.currentThread().setContextClassLoader(AccumuloClassLoader.getClassLoader());
-
-      Class<?> vfsClassLoader = 
AccumuloClassLoader.getClassLoader().loadClass("org.apache.accumulo.start.classloader.vfs.AccumuloVFSClassLoader");
-
-      ClassLoader cl = (ClassLoader) 
vfsClassLoader.getMethod("getClassLoader", new Class[] {}).invoke(null, new 
Object[] {});
-
-      Class<?> runTMP = null;
-
-      Thread.currentThread().setContextClassLoader(cl);
-
-      switch (args[0]) {
-        case "help":
-          printUsage();
-          System.exit(0);
-          break;
-        case "master":
-          runTMP = cl.loadClass("org.apache.accumulo.master.Master");
-          break;
-        case "tserver":
-          runTMP = cl.loadClass("org.apache.accumulo.tserver.TabletServer");
-          break;
-        case "shell":
-          runTMP = cl.loadClass("org.apache.accumulo.shell.Shell");
-          break;
-        case "init":
-          runTMP = cl.loadClass("org.apache.accumulo.server.init.Initialize");
-          break;
-        case "admin":
-          runTMP = cl.loadClass("org.apache.accumulo.server.util.Admin");
-          break;
-        case "gc":
-          runTMP = 
cl.loadClass("org.apache.accumulo.gc.SimpleGarbageCollector");
-          break;
-        case "monitor":
-          runTMP = cl.loadClass("org.apache.accumulo.monitor.Monitor");
-          break;
-        case "tracer":
-          runTMP = cl.loadClass("org.apache.accumulo.tracer.TraceServer");
-          break;
-        case "proxy":
-          runTMP = cl.loadClass("org.apache.accumulo.proxy.Proxy");
-          break;
-        case "minicluster":
-          runTMP = 
cl.loadClass("org.apache.accumulo.minicluster.MiniAccumuloRunner");
-          break;
-        case "classpath":
-          vfsClassLoader.getMethod("printClassPath", new Class[] 
{}).invoke(vfsClassLoader, new Object[] {});
-          return;
-        case "version":
-          runTMP = cl.loadClass("org.apache.accumulo.core.Constants");
-          System.out.println(runTMP.getField("VERSION").get(null));
-          return;
-        case "rfile-info":
-          runTMP = 
cl.loadClass("org.apache.accumulo.core.file.rfile.PrintInfo");
-          break;
-        case "login-info":
-          runTMP = 
cl.loadClass("org.apache.accumulo.server.util.LoginProperties");
-          break;
-        case "zookeeper":
-          runTMP = 
cl.loadClass("org.apache.accumulo.server.util.ZooKeeperMain");
-          break;
-        case "create-token":
-          runTMP = cl.loadClass("org.apache.accumulo.core.util.CreateToken");
-          break;
-        case "info":
-          runTMP = cl.loadClass("org.apache.accumulo.server.util.Info");
-          break;
-        case "jar":
-          if (args.length < 2) {
-            printUsage();
-            System.exit(1);
-          }
-          try {
-            JarFile f = new JarFile(args[1]);
-            runTMP = loadClassFromJar(args, f, cl);
-          } catch (IOException ioe) {
-            System.out.println("File " + args[1] + " could not be found or 
read.");
-            System.exit(1);
-          } catch (ClassNotFoundException cnfe) {
-            System.out.println("Classname " + (args.length > 2 ? args[2] : "in 
JAR manifest")
-                + " not found.  Please make sure you use the wholly qualified 
package name.");
-            System.exit(1);
-          }
-          break;
-        default:
-          try {
-            runTMP = cl.loadClass(args[0]);
-          } catch (ClassNotFoundException cnfe) {
-            System.out.println("Classname " + args[0] + " not found.  Please 
make sure you use the wholly qualified package name.");
-            System.exit(1);
-          }
-          break;
+      // determine whether a keyword was used or a class name, and execute it 
with the remaining args
+      String keywordOrClassName = args[0];
+      KeywordExecutable keywordExec = 
getExecutables(getClassLoader()).get(keywordOrClassName);
+      if (keywordExec != null) {
+        execKeyword(keywordExec, stripArgs(args, 1));
+      } else {
+        execMainClassName(keywordOrClassName, stripArgs(args, 1));
       }
-      Method main = null;
+
+    } catch (Throwable t) {
+      log.error("Uncaught exception", t);
+      System.exit(1);
+    }
+  }
+
+  public static ClassLoader getClassLoader() {
+    if (classLoader == null) {
       try {
-        main = runTMP.getMethod("main", args.getClass());
-      } catch (Throwable t) {
-        log.error("Could not run main method on '" + runTMP.getName() + "'.", 
t);
-      }
-      if (main == null || !Modifier.isPublic(main.getModifiers()) || 
!Modifier.isStatic(main.getModifiers())) {
-        System.out.println(args[0] + " must implement a public static void 
main(String args[]) method");
+        ClassLoader clTmp = (ClassLoader) 
getVFSClassLoader().getMethod("getClassLoader", new Class[0]).invoke(null, new 
Object[0]);
+        classLoader = clTmp;
+      } catch (ClassNotFoundException | IOException | IllegalAccessException | 
IllegalArgumentException | InvocationTargetException | NoSuchMethodException
+          | SecurityException e) {
+        log.error("Problem initializing the class loader", e);
         System.exit(1);
       }
-      int chopArgsCount;
-      if (args[0].equals("jar")) {
-        if (args.length > 2 && runTMP.getName().equals(args[2])) {
-          chopArgsCount = 3;
-        } else {
-          chopArgsCount = 2;
+    }
+    return classLoader;
+  }
+
+  public static Class<?> getVFSClassLoader() throws IOException, 
ClassNotFoundException {
+    if (vfsClassLoader == null) {
+      
Thread.currentThread().setContextClassLoader(AccumuloClassLoader.getClassLoader());
+      Class<?> vfsClassLoaderTmp = 
AccumuloClassLoader.getClassLoader().loadClass("org.apache.accumulo.start.classloader.vfs.AccumuloVFSClassLoader");
+      vfsClassLoader = vfsClassLoaderTmp;
+    }
+    return vfsClassLoader;
+  }
+
+  private static void execKeyword(final KeywordExecutable keywordExec, final 
String[] args) {
+    Runnable r = new Runnable() {
+      @Override
+      public void run() {
+        try {
+          keywordExec.execute(args);
+        } catch (Exception e) {
+          die(e);
         }
-      } else {
-        chopArgsCount = 1;
       }
-      String argsToPass[] = new String[args.length - chopArgsCount];
-      System.arraycopy(args, chopArgsCount, argsToPass, 0, args.length - 
chopArgsCount);
-      final Object thisIsJustOneArgument = argsToPass;
-      final Method finalMain = main;
-      r = new Runnable() {
-        @Override
-        public void run() {
-          try {
-            finalMain.invoke(null, thisIsJustOneArgument);
-          } catch (InvocationTargetException e) {
-            if (e.getCause() != null) {
-              die(e.getCause());
-            } else {
-              // Should never happen, but check anyway.
-              die(e);
-            }
-          } catch (Exception e) {
-            die(e);
-          }
-        }
-      };
+    };
+    startThread(r, keywordExec.keyword());
+  }
 
-      Thread t = new Thread(r, args[0]);
-      t.setContextClassLoader(cl);
-      t.start();
+  private static void execMainClassName(final String className, final String[] 
args) {
+    Class<?> classWithMain = null;
+    try {
+      classWithMain = getClassLoader().loadClass(className);
+    } catch (ClassNotFoundException cnfe) {
+      System.out.println("Classname " + className + " not found.  Please make 
sure you use the wholly qualified package name.");
+      System.exit(1);
+    }
+    execMainClass(classWithMain, args);
+  }
+
+  public static void execMainClass(final Class<?> classWithMain, final 
String[] args) {
+    Method main = null;
+    try {
+      main = classWithMain.getMethod("main", args.getClass());
     } catch (Throwable t) {
-      log.error("Uncaught exception", t);
+      log.error("Could not run main method on '" + classWithMain.getName() + 
"'.", t);
+    }
+    if (main == null || !Modifier.isPublic(main.getModifiers()) || 
!Modifier.isStatic(main.getModifiers())) {
+      System.out.println(classWithMain.getName() + " must implement a public 
static void main(String args[]) method");
       System.exit(1);
     }
+    final Method finalMain = main;
+    Runnable r = new Runnable() {
+      @Override
+      public void run() {
+        try {
+          final Object thisIsJustOneArgument = args;
+          finalMain.invoke(null, thisIsJustOneArgument);
+        } catch (InvocationTargetException e) {
+          if (e.getCause() != null) {
+            die(e.getCause());
+          } else {
+            // Should never happen, but check anyway.
+            die(e);
+          }
+        } catch (Exception e) {
+          die(e);
+        }
+      }
+    };
+    startThread(r, classWithMain.getName());
+  }
+
+  public static String[] stripArgs(final String[] originalArgs, int 
numToStrip) {
+    int newSize = originalArgs.length - numToStrip;
+    String newArgs[] = new String[newSize];
+    System.arraycopy(originalArgs, numToStrip, newArgs, 0, newSize);
+    return newArgs;
+  }
+
+  private static void startThread(final Runnable r, final String name) {
+    Thread t = new Thread(r, name);
+    t.setContextClassLoader(getClassLoader());
+    t.start();
   }
 
   /**
@@ -189,22 +161,65 @@ public class Main {
    * @param t
    *          The {@link Throwable} containing a stack trace to print.
    */
-  private static void die(Throwable t) {
+  private static void die(final Throwable t) {
     log.error("Thread '" + Thread.currentThread().getName() + "' died.", t);
     System.exit(1);
   }
 
-  private static void printUsage() {
-    System.out.println("accumulo init | master | tserver | monitor | shell | 
admin | gc | classpath | rfile-info | login-info "
-        + "| tracer | minicluster | proxy | zookeeper | create-token | info | 
version | help | jar <jar> [<main class>] args | <accumulo class> args");
+  public static void printUsage() {
+    TreeSet<String> keywords = new 
TreeSet<>(getExecutables(getClassLoader()).keySet());
+
+    // jar is a special case, because it has arguments
+    keywords.remove("jar");
+    keywords.add("jar <jar> [<main class>] args");
+
+    String prefix = "";
+    String kwString = "";
+    for (String kw : keywords) {
+      kwString += prefix + kw;
+      prefix = " | ";
+    }
+    System.out.println("accumulo " + kwString + " | <accumulo class> args");
+  }
+
+  static Map<String,KeywordExecutable> getExecutables(final ClassLoader cl) {
+    if (servicesMap == null) {
+      servicesMap = 
checkDuplicates(ServiceLoader.load(KeywordExecutable.class, cl));
+    }
+    return servicesMap;
+  }
+
+  static Map<String,KeywordExecutable> checkDuplicates(final Iterable<? 
extends KeywordExecutable> services) {
+    TreeSet<String> blacklist = new TreeSet<>();
+    TreeMap<String,KeywordExecutable> results = new TreeMap<>();
+    for (KeywordExecutable service : services) {
+      String keyword = service.keyword();
+      if (blacklist.contains(keyword)) {
+        // subsequent times a duplicate is found, just warn and exclude it
+        warnDuplicate(service);
+      } else if (results.containsKey(keyword)) {
+        // the first time a duplicate is found, blacklist it and warn
+        blacklist.add(keyword);
+        warnDuplicate(results.remove(keyword));
+        warnDuplicate(service);
+      } else {
+        // first observance of this keyword, so just add it to the list
+        results.put(service.keyword(), service);
+      }
+    }
+    return Collections.unmodifiableSortedMap(results);
+  }
+
+  private static void warnDuplicate(final KeywordExecutable service) {
+    log.warn("Ambiguous duplicate binding for keyword '" + service.keyword() + 
"' found: " + service.getClass().getName());
   }
 
   // feature: will work even if main class isn't in the JAR
-  static Class<?> loadClassFromJar(String[] args, JarFile f, ClassLoader cl) 
throws IOException, ClassNotFoundException {
+  public static Class<?> loadClassFromJar(final String[] args, final JarFile 
f, final ClassLoader cl) throws ClassNotFoundException, IOException {
     ClassNotFoundException explicitNotFound = null;
-    if (args.length >= 3) {
+    if (args.length >= 2) {
       try {
-        return cl.loadClass(args[2]); // jar jar-file main-class
+        return cl.loadClass(args[1]); // jar-file main-class
       } catch (ClassNotFoundException cnfe) {
         // assume this is the first argument, look for main class in JAR 
manifest
         explicitNotFound = cnfe;
@@ -219,4 +234,5 @@ public class Main {
     }
     return cl.loadClass(mainClass);
   }
+
 }

http://git-wip-us.apache.org/repos/asf/accumulo/blob/8005e82c/start/src/main/java/org/apache/accumulo/start/spi/KeywordExecutable.java
----------------------------------------------------------------------
diff --git 
a/start/src/main/java/org/apache/accumulo/start/spi/KeywordExecutable.java 
b/start/src/main/java/org/apache/accumulo/start/spi/KeywordExecutable.java
new file mode 100644
index 0000000..9c8dbeb
--- /dev/null
+++ b/start/src/main/java/org/apache/accumulo/start/spi/KeywordExecutable.java
@@ -0,0 +1,54 @@
+/*
+ * 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.start.spi;
+
+import java.util.ServiceLoader;
+
+/**
+ * An interface used with the Java {@link ServiceLoader} to auto-discover 
classes executable with a convenient keyword on the command-line.
+ *
+ * <p>
+ * All implementing classes who have an entry in META-INF/services/{@link 
org.apache.accumulo.start.spi.KeywordExecutable} on the classpath will be 
constructed
+ * by the {@link ServiceLoader}, so they should be lightweight and quickly 
constructible with a mandatory no-argument constructor. Because of this, 
implementing
+ * classes could simply be factories which execute a different class, if that 
class is expensive to construct or cannot have a no-argument constructor.
+ *
+ * <p>
+ * One way to easily create META-INF/services files is to use the <a 
href="https://github.com/google/auto/tree/master/service";>AutoService</a> 
annotation.
+ *
+ * <p>
+ * If the implementing class also wishes to have a redundant main method, it 
may be useful to simply implement main as:<br>
+ * {@code new MyImplementingClass().execute(args);}
+ *
+ */
+public interface KeywordExecutable {
+
+  /**
+   * Provides access to the service's keyword.
+   *
+   * @return the keyword which identifies this service
+   */
+  String keyword();
+
+  /**
+   * Execute the item with the given arguments.
+   *
+   * @param args
+   *          command-line arguments to pass to the executed class
+   */
+  void execute(final String[] args) throws Exception;
+
+}

http://git-wip-us.apache.org/repos/asf/accumulo/blob/8005e82c/start/src/test/java/org/apache/accumulo/start/MainTest.java
----------------------------------------------------------------------
diff --git a/start/src/test/java/org/apache/accumulo/start/MainTest.java 
b/start/src/test/java/org/apache/accumulo/start/MainTest.java
index 1ea22fb..0e6102d 100644
--- a/start/src/test/java/org/apache/accumulo/start/MainTest.java
+++ b/start/src/test/java/org/apache/accumulo/start/MainTest.java
@@ -44,7 +44,7 @@ public class MainTest {
 
   @Test
   public void testLoadClassFromJar_ExplicitMainClass() throws Exception {
-    String[] args = {"jar", "the.jar", "main.class", "arg1", "arg2"};
+    String[] args = {"the.jar", "main.class", "arg1", "arg2"};
     EasyMock.<Class<?>> 
expect(cl.loadClass("main.class")).andReturn(MAIN_CLASS);
     replay(cl);
     assertEquals(MAIN_CLASS, Main.loadClassFromJar(args, f, cl));
@@ -52,7 +52,7 @@ public class MainTest {
 
   @Test
   public void testLoadClassFromJar_ManifestMainClass() throws Exception {
-    String[] args = {"jar", "the.jar", "arg1", "arg2"};
+    String[] args = {"the.jar", "arg1", "arg2"};
     expect(cl.loadClass("arg1")).andThrow(new ClassNotFoundException());
     EasyMock.<Class<?>> 
expect(cl.loadClass(MAIN_CLASS_NAME)).andReturn(MAIN_CLASS);
     replay(cl);
@@ -63,7 +63,7 @@ public class MainTest {
 
   @Test(expected = ClassNotFoundException.class)
   public void testLoadClassFromJar_NoMainClass() throws Exception {
-    String[] args = {"jar", "the.jar", "arg1", "arg2"};
+    String[] args = {"the.jar", "arg1", "arg2"};
     expect(cl.loadClass("arg1")).andThrow(new ClassNotFoundException());
     replay(cl);
     mockManifestMainClass(f, null);
@@ -73,7 +73,7 @@ public class MainTest {
 
   @Test(expected = ClassNotFoundException.class)
   public void testLoadClassFromJar_NoMainClassNoArgs() throws Exception {
-    String[] args = {"jar", "the.jar"};
+    String[] args = {"the.jar"};
     mockManifestMainClass(f, null);
     replay(f);
     Main.loadClassFromJar(args, f, cl);
@@ -81,7 +81,7 @@ public class MainTest {
 
   @Test(expected = ClassNotFoundException.class)
   public void testLoadClassFromJar_ExplicitMainClass_Fail() throws Exception {
-    String[] args = {"jar", "the.jar", "main.class", "arg1", "arg2"};
+    String[] args = {"the.jar", "main.class", "arg1", "arg2"};
     expect(cl.loadClass("main.class")).andThrow(new ClassNotFoundException());
     replay(cl);
     mockManifestMainClass(f, null);
@@ -91,7 +91,7 @@ public class MainTest {
 
   @Test(expected = ClassNotFoundException.class)
   public void testLoadClassFromJar_ManifestMainClass_Fail() throws Exception {
-    String[] args = {"jar", "the.jar", "arg1", "arg2"};
+    String[] args = {"the.jar", "arg1", "arg2"};
     expect(cl.loadClass("arg1")).andThrow(new ClassNotFoundException());
     expect(cl.loadClass(MAIN_CLASS_NAME)).andThrow(new 
ClassNotFoundException());
     replay(cl);

Reply via email to