You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@accumulo.apache.org by dl...@apache.org on 2013/05/23 02:08:38 UTC

svn commit: r1485516 - in /accumulo/trunk: core/src/main/java/org/apache/accumulo/core/util/shell/ core/src/main/java/org/apache/accumulo/core/util/shell/commands/ examples/simple/src/main/java/org/apache/accumulo/examples/simple/shell/ examples/simple...

Author: dlmarion
Date: Thu May 23 00:08:38 2013
New Revision: 1485516

URL: http://svn.apache.org/r1485516
Log:
ACCUMULO-1399: Added support for shell extensions and scripts


Added:
    accumulo/trunk/core/src/main/java/org/apache/accumulo/core/util/shell/ShellExtension.java
    accumulo/trunk/core/src/main/java/org/apache/accumulo/core/util/shell/commands/ExtensionCommand.java
    accumulo/trunk/core/src/main/java/org/apache/accumulo/core/util/shell/commands/ScriptCommand.java
    accumulo/trunk/examples/simple/src/main/java/org/apache/accumulo/examples/simple/shell/
    accumulo/trunk/examples/simple/src/main/java/org/apache/accumulo/examples/simple/shell/DebugCommand.java
    accumulo/trunk/examples/simple/src/main/java/org/apache/accumulo/examples/simple/shell/MyAppShellExtension.java
    accumulo/trunk/examples/simple/src/main/resources/
    accumulo/trunk/examples/simple/src/main/resources/META-INF/
    accumulo/trunk/examples/simple/src/main/resources/META-INF/services/
    accumulo/trunk/examples/simple/src/main/resources/META-INF/services/org.apache.accumulo.core.util.shell.ShellExtension
Modified:
    accumulo/trunk/core/src/main/java/org/apache/accumulo/core/util/shell/Shell.java

Modified: accumulo/trunk/core/src/main/java/org/apache/accumulo/core/util/shell/Shell.java
URL: http://svn.apache.org/viewvc/accumulo/trunk/core/src/main/java/org/apache/accumulo/core/util/shell/Shell.java?rev=1485516&r1=1485515&r2=1485516&view=diff
==============================================================================
--- accumulo/trunk/core/src/main/java/org/apache/accumulo/core/util/shell/Shell.java (original)
+++ accumulo/trunk/core/src/main/java/org/apache/accumulo/core/util/shell/Shell.java Thu May 23 00:08:38 2013
@@ -92,6 +92,7 @@ import org.apache.accumulo.core.util.she
 import org.apache.accumulo.core.util.shell.commands.ExecfileCommand;
 import org.apache.accumulo.core.util.shell.commands.ExitCommand;
 import org.apache.accumulo.core.util.shell.commands.ExportTableCommand;
+import org.apache.accumulo.core.util.shell.commands.ExtensionCommand;
 import org.apache.accumulo.core.util.shell.commands.FateCommand;
 import org.apache.accumulo.core.util.shell.commands.FlushCommand;
 import org.apache.accumulo.core.util.shell.commands.FormatterCommand;
@@ -125,6 +126,7 @@ import org.apache.accumulo.core.util.she
 import org.apache.accumulo.core.util.shell.commands.RenameTableCommand;
 import org.apache.accumulo.core.util.shell.commands.RevokeCommand;
 import org.apache.accumulo.core.util.shell.commands.ScanCommand;
+import org.apache.accumulo.core.util.shell.commands.ScriptCommand;
 import org.apache.accumulo.core.util.shell.commands.SetAuthsCommand;
 import org.apache.accumulo.core.util.shell.commands.SetGroupsCommand;
 import org.apache.accumulo.core.util.shell.commands.SetIterCommand;
@@ -338,7 +340,7 @@ public class Shell extends ShellOptions 
         new InterpreterCommand(), new GrepCommand(), new ImportDirectoryCommand(), new InsertCommand(), new MaxRowCommand(), new ScanCommand()};
     Command[] debuggingCommands = {new ClasspathCommand(), new DebugCommand(), new ListScansCommand(), new ListCompactionsCommand(), new TraceCommand(),
         new PingCommand()};
-    Command[] execCommands = {new ExecfileCommand(), new HistoryCommand()};
+    Command[] execCommands = {new ExecfileCommand(), new HistoryCommand(), new ExtensionCommand(), new ScriptCommand()};
     Command[] exitCommands = {new ByeCommand(), new ExitCommand(), new QuitCommand()};
     Command[] helpCommands = {new AboutCommand(), new HelpCommand(), new InfoCommand(), new QuestionCommand()};
     Command[] iteratorCommands = {new DeleteIterCommand(), new DeleteScanIterCommand(), new ListIterCommand(), new SetIterCommand(), new SetScanIterCommand(),
@@ -1062,4 +1064,9 @@ public class Shell extends ShellOptions 
   public boolean hasExited() {
     return exit;
   }
+
+  public boolean isTabCompletion() {
+    return tabCompletion;
+  }
+  
 }

Added: accumulo/trunk/core/src/main/java/org/apache/accumulo/core/util/shell/ShellExtension.java
URL: http://svn.apache.org/viewvc/accumulo/trunk/core/src/main/java/org/apache/accumulo/core/util/shell/ShellExtension.java?rev=1485516&view=auto
==============================================================================
--- accumulo/trunk/core/src/main/java/org/apache/accumulo/core/util/shell/ShellExtension.java (added)
+++ accumulo/trunk/core/src/main/java/org/apache/accumulo/core/util/shell/ShellExtension.java Thu May 23 00:08:38 2013
@@ -0,0 +1,27 @@
+/*
+ * 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 org.apache.accumulo.core.util.shell.Shell.Command;
+
+public abstract class ShellExtension {
+    
+    public abstract String getExtensionName();
+
+    public abstract Command[] getCommands();
+    
+}

Added: accumulo/trunk/core/src/main/java/org/apache/accumulo/core/util/shell/commands/ExtensionCommand.java
URL: http://svn.apache.org/viewvc/accumulo/trunk/core/src/main/java/org/apache/accumulo/core/util/shell/commands/ExtensionCommand.java?rev=1485516&view=auto
==============================================================================
--- accumulo/trunk/core/src/main/java/org/apache/accumulo/core/util/shell/commands/ExtensionCommand.java (added)
+++ accumulo/trunk/core/src/main/java/org/apache/accumulo/core/util/shell/commands/ExtensionCommand.java Thu May 23 00:08:38 2013
@@ -0,0 +1,102 @@
+/*
+ * 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 java.util.HashSet;
+import java.util.ServiceLoader;
+import java.util.Set;
+import java.util.TreeSet;
+
+import org.apache.accumulo.core.util.shell.Shell;
+import org.apache.accumulo.core.util.shell.Shell.Command;
+import org.apache.accumulo.core.util.shell.ShellExtension;
+import org.apache.commons.cli.CommandLine;
+import org.apache.commons.cli.Option;
+import org.apache.commons.cli.Options;
+
+public class ExtensionCommand extends Command {
+  
+  protected Option enable, disable, list;
+  
+  private static ServiceLoader<ShellExtension> extensions = null;
+  
+  private Set<String> loadedHeaders = new HashSet<String>();
+  private Set<String> loadedCommands = new HashSet<String>();
+  private Set<String> loadedExtensions = new TreeSet<String>();
+  
+  public int execute(String fullCommand, CommandLine cl, Shell shellState) throws Exception {
+    if (cl.hasOption(enable.getOpt())) {
+      extensions = ServiceLoader.load(ShellExtension.class);
+      for (ShellExtension se : extensions) {
+        
+        loadedExtensions.add(se.getExtensionName());
+        String header = "-- " + se.getExtensionName() + " Extension Commands ---------";
+        loadedHeaders.add(header);
+        shellState.commandGrouping.put(header, se.getCommands());
+        
+        for (Command cmd : se.getCommands()) {
+          String name = se.getExtensionName() + "::" + cmd.getName();
+          loadedCommands.add(name);
+          shellState.commandFactory.put(name, cmd);
+        }
+      }
+    } else if (cl.hasOption(disable.getOpt())) {
+      //Remove the headers
+      for (String header : loadedHeaders) {
+        shellState.commandGrouping.remove(header);
+      }
+      //remove the commands
+      for (String name : loadedCommands) {
+        shellState.commandFactory.remove(name);
+      }
+      //Reset state
+      loadedExtensions.clear();
+      extensions.reload();
+    } else if (cl.hasOption(list.getOpt())) {
+      shellState.printLines(loadedExtensions.iterator(), true);
+    } else {
+      printHelp(shellState);
+    }
+    return 0;
+  }
+  
+  public String description() {
+    return "Enable, disable, or list shell extensions";
+  }
+  
+  public int numArgs() {
+    return 0;
+  }
+  
+  @Override
+  public String getName() {
+    return "extensions";
+  }
+
+  @Override
+  public Options getOptions() {
+    final Options o = new Options();
+    enable = new Option("e", "enable", false, "enable shell extensions");
+    disable = new Option("d", "disable", false, "disable shell extensions");
+    list = new Option("l", "list", false, "list shell extensions");
+    o.addOption(enable);
+    o.addOption(disable);
+    o.addOption(list);
+    return o;
+  }
+    
+}

Added: accumulo/trunk/core/src/main/java/org/apache/accumulo/core/util/shell/commands/ScriptCommand.java
URL: http://svn.apache.org/viewvc/accumulo/trunk/core/src/main/java/org/apache/accumulo/core/util/shell/commands/ScriptCommand.java?rev=1485516&view=auto
==============================================================================
--- accumulo/trunk/core/src/main/java/org/apache/accumulo/core/util/shell/commands/ScriptCommand.java (added)
+++ accumulo/trunk/core/src/main/java/org/apache/accumulo/core/util/shell/commands/ScriptCommand.java Thu May 23 00:08:38 2013
@@ -0,0 +1,290 @@
+/*
+ * 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 java.io.File;
+import java.io.FileReader;
+import java.io.FileWriter;
+import java.io.IOException;
+import java.io.Reader;
+import java.io.Writer;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Set;
+import java.util.TreeSet;
+
+import javax.script.Bindings;
+import javax.script.Compilable;
+import javax.script.CompiledScript;
+import javax.script.Invocable;
+import javax.script.ScriptContext;
+import javax.script.ScriptEngine;
+import javax.script.ScriptEngineFactory;
+import javax.script.ScriptEngineManager;
+import javax.script.ScriptException;
+import javax.script.SimpleScriptContext;
+
+import org.apache.accumulo.core.util.shell.Shell;
+import org.apache.accumulo.core.util.shell.Shell.Command;
+import org.apache.commons.cli.CommandLine;
+import org.apache.commons.cli.Option;
+import org.apache.commons.cli.OptionGroup;
+import org.apache.commons.cli.Options;
+
+public class ScriptCommand extends Command {
+  
+  // Command to allow user to run scripts, see JSR-223
+  // http://www.oracle.com/technetwork/articles/javase/scripting-140262.html
+  
+  protected Option list, engine, script, file, args, out, function, object;
+  private static final String DEFAULT_ENGINE = "rhino";
+  
+  public int execute(String fullCommand, CommandLine cl, Shell shellState) throws Exception {
+
+    boolean invoke = false;
+    ScriptEngineManager mgr = new ScriptEngineManager();
+    
+    if (cl.hasOption(list.getOpt())) {
+      listJSREngineInfo(mgr, shellState);
+    } else if (cl.hasOption(file.getOpt()) || cl.hasOption(script.getOpt())) {
+      String engineName = DEFAULT_ENGINE;
+      if (cl.hasOption(engine.getOpt())) {
+        engineName = cl.getOptionValue(engine.getOpt());
+      }
+      ScriptEngine engine = mgr.getEngineByName(engineName);
+      if (null == engine) {
+        shellState.printException(new Exception(engineName + " not found"));
+        return 1;
+      }
+      
+      if (cl.hasOption(object.getOpt()) || cl.hasOption(function.getOpt())) {
+        if (!(engine instanceof Invocable)) {
+          shellState.printException(new Exception(engineName + " does not support invoking functions or methods"));
+          return 1;
+        }
+        invoke = true;
+      }
+      
+      ScriptContext ctx = new SimpleScriptContext();
+      
+      // Put the following objects into the context so that they
+      // are available to the scripts
+      // TODO: What else should go in here?
+      Bindings b = engine.getBindings(ScriptContext.ENGINE_SCOPE);
+      b.put("connection", shellState.getConnector());
+      
+      List<Object> argValues = new ArrayList<Object>();
+      if (cl.hasOption(args.getOpt())) {
+        String[] argList = cl.getOptionValue(args.getOpt()).split(",");
+        for (String arg : argList) {
+          String[] parts = arg.split("=");
+          if (parts.length == 0) {
+            continue;
+          } else if (parts.length == 1) {
+            b.put(parts[0], null);
+            argValues.add(null);
+          } else if (parts.length == 2) {
+            b.put(parts[0], parts[1]);
+            argValues.add(parts[1]);
+          }
+        }
+      }
+      ctx.setBindings(b, ScriptContext.ENGINE_SCOPE);
+      Object[] argArray = argValues.toArray(new Object[argValues.size()]);
+      
+      Writer writer = null;
+      if (cl.hasOption(out.getOpt())) {
+        File f = new File(cl.getOptionValue(out.getOpt()));
+        writer = new FileWriter(f);
+        ctx.setWriter(writer);
+      }
+      
+      if (cl.hasOption(file.getOpt())) {
+        File f = new File(cl.getOptionValue(file.getOpt()));
+        if (!f.exists()) {
+          if (null != writer) {
+            writer.close();
+          }
+          shellState.printException(new Exception(f.getAbsolutePath() + " not found"));
+          return 1;
+        }
+        Reader reader = new FileReader(f);
+        try {
+          engine.eval(reader, ctx);
+          if (invoke) {
+            this.invokeFunctionOrMethod(shellState, engine, cl, argArray);
+          }
+        } catch (ScriptException ex) {
+          shellState.printException(ex);
+          return 1;
+        } finally {
+          reader.close();
+          if (null != writer) {
+            writer.close();
+          }
+        }
+      } else if (cl.hasOption(script.getOpt())) {
+        String inlineScript = cl.getOptionValue(script.getOpt());
+        try {
+          if (engine instanceof Compilable) {
+            Compilable compiledEng = (Compilable) engine;
+            CompiledScript script = compiledEng.compile(inlineScript);
+            script.eval(ctx);
+            if (invoke) {
+              this.invokeFunctionOrMethod(shellState, engine, cl, argArray);
+            }
+          } else {
+            engine.eval(inlineScript, ctx);
+            if (invoke) {
+              this.invokeFunctionOrMethod(shellState, engine, cl, argArray);
+            }
+          }
+        } catch (ScriptException ex) {
+          shellState.printException(ex);
+          return 1;
+        } finally {
+          if (null != writer) {
+            writer.close();
+          }
+        }
+      }
+      if (null != writer) {
+        writer.close();
+      }
+      
+    } else {
+      printHelp(shellState);
+    }
+    return 0;
+  }
+  
+  public String description() {
+    return "execute JSR-223 scripts";
+  }
+  
+  public int numArgs() {
+    return 0;
+  }
+  
+  @Override
+  public String getName() {
+    return "script";
+  }
+  
+  @Override
+  public Options getOptions() {
+    final Options o = new Options();
+    
+    engine = new Option("e", "engine", false, "engine name, defaults to JDK default (Rhino)");
+    engine.setArgName("engineName");
+    engine.setArgs(1);
+    engine.setRequired(false);
+    o.addOption(engine);
+    
+    OptionGroup inputGroup = new OptionGroup();
+    list = new Option("l", "list", false, "list available script engines");
+    inputGroup.addOption(list);
+
+    script = new Option("s", "script", true, "use inline script");
+    script.setArgName("script text");
+    script.setArgs(1);
+    script.setRequired(false);
+    inputGroup.addOption(script);
+    
+    file = new Option("f", "file", true, "use script file");
+    file.setArgName("fileName");
+    file.setArgs(1);
+    file.setRequired(false);
+    
+    inputGroup.addOption(file);
+    inputGroup.setRequired(true);
+    o.addOptionGroup(inputGroup);
+    
+    OptionGroup invokeGroup = new OptionGroup();
+    object = new Option("obj", "object", true, "name of object");
+    object.setArgs(1);
+    object.setArgName("objectName:methodName");
+    object.setRequired(false);
+    invokeGroup.addOption(object);
+    
+    function = new Option("fx", "function", true, "invoke a script function");
+    function.setArgName("functionName");
+    function.setArgs(1);
+    function.setRequired(false);
+    invokeGroup.addOption(function);
+    invokeGroup.setRequired(false);
+    o.addOptionGroup(invokeGroup);
+    
+    args = new Option("a", "args", true, "comma separated list of key=value arguments");
+    args.setArgName("property1=value1,propert2=value2,...");
+    args.setArgs(Option.UNLIMITED_VALUES);
+    args.setRequired(false);
+    o.addOption(args);
+    
+    out = new Option("o", "output", true, "output file");
+    out.setArgName("fileName");
+    out.setArgs(1);
+    out.setRequired(false);
+    o.addOption(out);
+    
+    return o;
+  }
+  
+  private void listJSREngineInfo(ScriptEngineManager mgr, Shell shellState) throws IOException {
+    List<ScriptEngineFactory> factories = mgr.getEngineFactories();
+    Set<String> lines = new TreeSet<String>();
+    for (ScriptEngineFactory factory : factories) {
+      lines.add("ScriptEngineFactory Info");
+      String engName = factory.getEngineName();
+      String engVersion = factory.getEngineVersion();
+      String langName = factory.getLanguageName();
+      String langVersion = factory.getLanguageVersion();
+      lines.add("\tScript Engine: " + engName + " (" + engVersion + ")");
+      List<String> engNames = factory.getNames();
+      for (String name : engNames) {
+        lines.add("\tEngine Alias: " + name);
+      }
+      lines.add("\tLanguage: " + langName + " (" + langVersion + ")");
+    }
+    shellState.printLines(lines.iterator(), true);
+    
+  }
+  
+  private void invokeFunctionOrMethod(Shell shellState, ScriptEngine engine, CommandLine cl, Object[] args) {
+    try {
+      Invocable inv = (Invocable) engine;
+      if (cl.hasOption(function.getOpt())) {
+        inv.invokeFunction(cl.getOptionValue(function.getOpt()), args);
+      } else if (cl.hasOption(object.getOpt())) {
+        String objectMethod = cl.getOptionValue(object.getOpt());
+        String[] parts = objectMethod.split(":");
+        if (!(parts.length == 2)) {
+          shellState.printException(new Exception("Object and Method must be supplied"));
+          return;
+        }
+        String objectName = parts[0];
+        String methodName = parts[1];
+        Object obj = engine.get(objectName);
+        inv.invokeMethod(obj, methodName, args);
+        
+      }
+    } catch (Exception e) {
+      shellState.printException(e);
+    }
+  }
+  
+}

Added: accumulo/trunk/examples/simple/src/main/java/org/apache/accumulo/examples/simple/shell/DebugCommand.java
URL: http://svn.apache.org/viewvc/accumulo/trunk/examples/simple/src/main/java/org/apache/accumulo/examples/simple/shell/DebugCommand.java?rev=1485516&view=auto
==============================================================================
--- accumulo/trunk/examples/simple/src/main/java/org/apache/accumulo/examples/simple/shell/DebugCommand.java (added)
+++ accumulo/trunk/examples/simple/src/main/java/org/apache/accumulo/examples/simple/shell/DebugCommand.java Thu May 23 00:08:38 2013
@@ -0,0 +1,43 @@
+/*
+ * 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.examples.simple.shell;
+
+import java.util.Set;
+import java.util.TreeSet;
+
+import org.apache.accumulo.core.util.shell.Shell;
+import org.apache.accumulo.core.util.shell.Shell.Command;
+import org.apache.commons.cli.CommandLine;
+
+public class DebugCommand extends Command {
+
+  public int execute(String fullCommand, CommandLine cl, Shell shellState) throws Exception {
+    Set<String> lines = new TreeSet<String>();
+    lines.add("This is a test");
+    shellState.printLines(lines.iterator(), true);
+    return 0;
+  }
+
+  public String description() {
+    return "prints a message to test extension feature";
+  }
+
+  public int numArgs() {
+    return 0;
+  }
+  
+}

Added: accumulo/trunk/examples/simple/src/main/java/org/apache/accumulo/examples/simple/shell/MyAppShellExtension.java
URL: http://svn.apache.org/viewvc/accumulo/trunk/examples/simple/src/main/java/org/apache/accumulo/examples/simple/shell/MyAppShellExtension.java?rev=1485516&view=auto
==============================================================================
--- accumulo/trunk/examples/simple/src/main/java/org/apache/accumulo/examples/simple/shell/MyAppShellExtension.java (added)
+++ accumulo/trunk/examples/simple/src/main/java/org/apache/accumulo/examples/simple/shell/MyAppShellExtension.java Thu May 23 00:08:38 2013
@@ -0,0 +1,33 @@
+/*
+ * 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.examples.simple.shell;
+
+import org.apache.accumulo.core.util.shell.Shell.Command;
+import org.apache.accumulo.core.util.shell.ShellExtension;
+
+public class MyAppShellExtension extends ShellExtension {
+  
+  public String getExtensionName() {
+    return "MyApp";
+  }
+  
+  @Override
+  public Command[] getCommands() {
+    return new Command[] { new DebugCommand() };
+  }
+  
+}

Added: accumulo/trunk/examples/simple/src/main/resources/META-INF/services/org.apache.accumulo.core.util.shell.ShellExtension
URL: http://svn.apache.org/viewvc/accumulo/trunk/examples/simple/src/main/resources/META-INF/services/org.apache.accumulo.core.util.shell.ShellExtension?rev=1485516&view=auto
==============================================================================
--- accumulo/trunk/examples/simple/src/main/resources/META-INF/services/org.apache.accumulo.core.util.shell.ShellExtension (added)
+++ accumulo/trunk/examples/simple/src/main/resources/META-INF/services/org.apache.accumulo.core.util.shell.ShellExtension Thu May 23 00:08:38 2013
@@ -0,0 +1 @@
+org.apache.accumulo.examples.simple.shell.MyAppShellExtension
\ No newline at end of file