This is an automated email from the ASF dual-hosted git repository.

dlmarion pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/accumulo.git


The following commit(s) were added to refs/heads/main by this push:
     new 66f568d90e Modified accumulo script to use group name in command 
syntax (#6109)
66f568d90e is described below

commit 66f568d90e15f2e3fa46a7fe6b658327e0e2e8a6
Author: Dave Marion <[email protected]>
AuthorDate: Wed Feb 4 15:18:52 2026 -0500

    Modified accumulo script to use group name in command syntax (#6109)
    
    Modified command syntax from `accumulo <command>` to
    `accumulo <group> <command>`. This work will be leveraged
    in a future change to decompose the `admin` command into
    seperate KeywordExecutable instances in an `admin` group.
    
    Related to #6086
---
 assemble/bin/accumulo-service                      |   6 +-
 .../miniclusterImpl/MiniAccumuloClusterImpl.java   |   1 -
 .../main/java/org/apache/accumulo/start/Main.java  | 101 +++++++++------
 .../apache/accumulo/test/start/KeywordStartIT.java | 142 +++++++++++++--------
 4 files changed, 153 insertions(+), 97 deletions(-)

diff --git a/assemble/bin/accumulo-service b/assemble/bin/accumulo-service
index b887160ecf..af4aa83dea 100755
--- a/assemble/bin/accumulo-service
+++ b/assemble/bin/accumulo-service
@@ -27,8 +27,8 @@ Services:
   monitor                Accumulo monitor
   manager                Accumulo manager
   tserver                Accumulo tserver
-  compactor              Accumulo compactor (experimental)
-  sserver                Accumulo scan server (experimental)
+  compactor              Accumulo compactor
+  sserver                Accumulo scan server
 
 Commands:
   start                   Starts service(s)
@@ -111,7 +111,7 @@ function start_service() {
     rotate_log "$outfile"
     rotate_log "$errfile"
 
-    nohup "${bin}/accumulo" "$service_type" "$@" >"$outfile" 2>"$errfile" 
</dev/null &
+    nohup "${bin}/accumulo" "process" "$service_type" "$@" >"$outfile" 
2>"$errfile" </dev/null &
     echo "$!" >"${pid_file}"
 
   done
diff --git 
a/minicluster/src/main/java/org/apache/accumulo/miniclusterImpl/MiniAccumuloClusterImpl.java
 
b/minicluster/src/main/java/org/apache/accumulo/miniclusterImpl/MiniAccumuloClusterImpl.java
index d1977799a7..f9703f721b 100644
--- 
a/minicluster/src/main/java/org/apache/accumulo/miniclusterImpl/MiniAccumuloClusterImpl.java
+++ 
b/minicluster/src/main/java/org/apache/accumulo/miniclusterImpl/MiniAccumuloClusterImpl.java
@@ -431,7 +431,6 @@ public class MiniAccumuloClusterImpl implements 
AccumuloCluster {
     File stdErr = logDir.resolve(clazz.getSimpleName() + "_" + hashcode + 
".err").toFile();
 
     Process process = 
builder.redirectError(stdErr).redirectOutput(stdOut).start();
-
     cleanup.add(process);
 
     return new ProcessInfo(process, stdOut);
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 3f6eea7992..d45efd5e57 100644
--- a/start/src/main/java/org/apache/accumulo/start/Main.java
+++ b/start/src/main/java/org/apache/accumulo/start/Main.java
@@ -21,8 +21,10 @@ package org.apache.accumulo.start;
 import java.lang.reflect.InvocationTargetException;
 import java.lang.reflect.Method;
 import java.lang.reflect.Modifier;
+import java.util.Arrays;
 import java.util.Collections;
-import java.util.Comparator;
+import java.util.EnumMap;
+import java.util.List;
 import java.util.Map;
 import java.util.ServiceLoader;
 import java.util.TreeMap;
@@ -37,7 +39,7 @@ public class Main {
 
   private static final Logger log = LoggerFactory.getLogger(Main.class);
   private static ClassLoader classLoader;
-  private static Map<String,KeywordExecutable> servicesMap;
+  private static Map<UsageGroup,Map<String,KeywordExecutable>> servicesMap;
 
   public static void main(final String[] args) throws Exception {
     final ClassLoader loader = getClassLoader();
@@ -51,14 +53,21 @@ public class Main {
       return;
     }
 
-    // determine whether a keyword was used or a class name, and execute it 
with the remaining
-    // args
-    String keywordOrClassName = args[0];
-    KeywordExecutable keywordExec = 
getExecutables(loader).get(keywordOrClassName);
-    if (keywordExec != null) {
-      execKeyword(keywordExec, stripArgs(args, 1));
+    if (args.length == 1) {
+      execMainClassName(args[0], new String[] {});
     } else {
-      execMainClassName(keywordOrClassName, stripArgs(args, 1));
+      String arg1 = args[0];
+      String arg2 = args[1];
+      KeywordExecutable keywordExec = null;
+      try {
+        UsageGroup group = UsageGroup.valueOf(arg1.toUpperCase());
+        keywordExec = getExecutables(loader).get(group).get(arg2);
+      } catch (IllegalArgumentException e) {}
+      if (keywordExec != null) {
+        execKeyword(keywordExec, stripArgs(args, 2));
+      } else {
+        execMainClassName(arg1, stripArgs(args, 1));
+      }
     }
 
   }
@@ -157,61 +166,71 @@ public class Main {
     System.exit(1);
   }
 
-  public static void printCommands(TreeSet<KeywordExecutable> set, UsageGroup 
group) {
-    set.stream().filter(e -> e.usageGroup() == group)
-        .forEach(ke -> System.out.printf("  %-30s %s\n", ke.usage(), 
ke.description()));
-  }
-
   public static void printUsage() {
-    TreeSet<KeywordExecutable> executables =
-        new TreeSet<>(Comparator.comparing(KeywordExecutable::keyword));
-    executables.addAll(getExecutables(getClassLoader()).values());
-
-    System.out.println("\nUsage: accumulo <command> [--help] (<argument> 
...)\n\n"
-        + "  --help   Prints usage for specified command");
-    System.out.println("\nCore Commands:");
-    printCommands(executables, UsageGroup.CORE);
 
-    System.out.println("  jshell                         Runs JShell for 
Accumulo\n"
-        + "  classpath                      Prints Accumulo classpath\n"
-        + "  <main class> args              Runs Java <main class> located on 
Accumulo classpath");
-
-    System.out.println("\nProcess Commands:");
-    printCommands(executables, UsageGroup.PROCESS);
-
-    System.out.println("\nOther Commands:");
-    printCommands(executables, UsageGroup.OTHER);
+    System.out.println("\nUsage one of:");
+    System.out.println("    accumulo --help");
+    System.out.println("    accumulo classpath");
+    System.out.println("    accumulo jshell (<argument> ...)");
+    System.out.println("    accumulo className (<argument> ...)");
+    System.out.println("    accumulo <group> <command> [--help] (<argument> 
...)\n\n");
+
+    Map<UsageGroup,Map<String,KeywordExecutable>> exectuables = 
getExecutables(getClassLoader());
+    List<UsageGroup> groups = Arrays.asList(UsageGroup.values());
+    Collections.sort(groups);
+    groups.forEach(g -> {
+      System.out.println("\n" + g.name() + " Group Commands:");
+      exectuables.get(g).values()
+          .forEach(ke -> System.out.printf("  %-30s %s\n", ke.usage(), 
ke.description()));
+    });
 
     System.out.println();
   }
 
-  public static synchronized Map<String,KeywordExecutable> 
getExecutables(final ClassLoader cl) {
+  public static synchronized Map<UsageGroup,Map<String,KeywordExecutable>>
+      getExecutables(final ClassLoader cl) {
     if (servicesMap == null) {
       servicesMap = 
checkDuplicates(ServiceLoader.load(KeywordExecutable.class, cl));
     }
     return servicesMap;
   }
 
-  public static Map<String,KeywordExecutable>
+  private record BanKey(UsageGroup group, String keyword) implements 
Comparable<BanKey> {
+    @Override
+    public int compareTo(BanKey o) {
+      int result = this.group.compareTo(o.group);
+      if (result == 0) {
+        result = this.keyword.compareTo(o.keyword);
+      }
+      return result;
+    }
+  };
+
+  public static Map<UsageGroup,Map<String,KeywordExecutable>>
       checkDuplicates(final Iterable<? extends KeywordExecutable> services) {
-    TreeSet<String> banList = new TreeSet<>();
-    TreeMap<String,KeywordExecutable> results = new TreeMap<>();
+    TreeSet<BanKey> banList = new TreeSet<>();
+    EnumMap<UsageGroup,Map<String,KeywordExecutable>> results = new 
EnumMap<>(UsageGroup.class);
+    for (UsageGroup ug : UsageGroup.values()) {
+      results.put(ug, new TreeMap<>());
+    }
     for (KeywordExecutable service : services) {
+      UsageGroup group = service.usageGroup();
       String keyword = service.keyword();
-      if (banList.contains(keyword)) {
+      BanKey bk = new BanKey(group, keyword);
+      if (banList.contains(bk)) {
         // subsequent times a duplicate is found, just warn and exclude it
         warnDuplicate(service);
-      } else if (results.containsKey(keyword)) {
+      } else if (results.get(group).containsKey(keyword)) {
         // the first time a duplicate is found, banList it and warn
-        banList.add(keyword);
-        warnDuplicate(results.remove(keyword));
+        banList.add(bk);
+        warnDuplicate(results.get(group).remove(keyword));
         warnDuplicate(service);
       } else {
         // first observance of this keyword, so just add it to the list
-        results.put(service.keyword(), service);
+        results.get(group).put(service.keyword(), service);
       }
     }
-    return Collections.unmodifiableSortedMap(results);
+    return Collections.unmodifiableMap(results);
   }
 
   private static void warnDuplicate(final KeywordExecutable service) {
diff --git 
a/test/src/main/java/org/apache/accumulo/test/start/KeywordStartIT.java 
b/test/src/main/java/org/apache/accumulo/test/start/KeywordStartIT.java
index f3798a2a90..40b7d02e99 100644
--- a/test/src/main/java/org/apache/accumulo/test/start/KeywordStartIT.java
+++ b/test/src/main/java/org/apache/accumulo/test/start/KeywordStartIT.java
@@ -18,7 +18,6 @@
  */
 package org.apache.accumulo.test.start;
 
-import static java.util.stream.Collectors.toSet;
 import static org.apache.accumulo.harness.AccumuloITBase.SUNNY_DAY;
 import static org.junit.jupiter.api.Assertions.assertEquals;
 import static org.junit.jupiter.api.Assertions.assertFalse;
@@ -35,8 +34,10 @@ import java.util.HashSet;
 import java.util.Iterator;
 import java.util.List;
 import java.util.Map;
-import java.util.Map.Entry;
+import java.util.Set;
+import java.util.SortedSet;
 import java.util.TreeMap;
+import java.util.TreeSet;
 
 import org.apache.accumulo.compactor.CompactorExecutable;
 import org.apache.accumulo.core.file.rfile.GenerateSplits;
@@ -68,6 +69,7 @@ import org.apache.accumulo.server.util.ZooZap;
 import org.apache.accumulo.shell.Shell;
 import org.apache.accumulo.start.Main;
 import org.apache.accumulo.start.spi.KeywordExecutable;
+import org.apache.accumulo.start.spi.KeywordExecutable.UsageGroup;
 import org.apache.accumulo.tserver.ScanServerExecutable;
 import org.apache.accumulo.tserver.TServerExecutable;
 import org.apache.accumulo.tserver.TabletServer;
@@ -91,7 +93,7 @@ public class KeywordStartIT {
    * There may be other ways to run annotation processors in your IDE, so this 
may not be necessary,
    * depending on your IDE and its configuration.
    */
-  private Map<String,KeywordExecutable> getKeywordExecutables() {
+  private Map<UsageGroup,Map<String,KeywordExecutable>> 
getKeywordExecutables() {
     var all = Main.getExecutables(ClassLoader.getSystemClassLoader());
     assumeTrue(!all.isEmpty());
     return all;
@@ -99,7 +101,9 @@ public class KeywordStartIT {
 
   @Test
   public void testKeywordsMatch() {
-    getKeywordExecutables().forEach((k, v) -> assertEquals(k, v.keyword()));
+    getKeywordExecutables().forEach((k, v) -> {
+      v.forEach((k2, v2) -> assertEquals(k2, v2.keyword()));
+    });
   }
 
   @Test
@@ -110,14 +114,30 @@ public class KeywordStartIT {
     NoOp three = new NoOp("three");
     List<NoOp> services = Arrays.asList(one, three, two, two, three, three, 
anotherOne);
     assertEquals(7, services.size());
-    Map<String,KeywordExecutable> results = Main.checkDuplicates(services);
-    assertTrue(results.containsKey(one.keyword()));
-    assertTrue(results.containsKey(anotherOne.keyword()));
-    assertFalse(results.containsKey(two.keyword()));
-    assertFalse(results.containsKey(three.keyword()));
-    assertEquals(2, results.size());
+    Map<UsageGroup,Map<String,KeywordExecutable>> results = 
Main.checkDuplicates(services);
+    assertTrue(results.get(UsageGroup.OTHER).containsKey(one.keyword()));
+    
assertTrue(results.get(UsageGroup.OTHER).containsKey(anotherOne.keyword()));
+    assertFalse(results.get(UsageGroup.OTHER).containsKey(two.keyword()));
+    assertFalse(results.get(UsageGroup.OTHER).containsKey(three.keyword()));
+    assertEquals(2, results.get(UsageGroup.OTHER).size());
   }
 
+  private record CommandInfo(UsageGroup group, String keyword,
+      Class<? extends KeywordExecutable> clazz) implements 
Comparable<CommandInfo> {
+
+    @Override
+    public int compareTo(CommandInfo o) {
+      int result = this.group.compareTo(o.group);
+      if (result == 0) {
+        result = this.keyword.compareTo(o.keyword);
+        if (result == 0) {
+          result = this.clazz.getName().compareTo(o.clazz.getName());
+        }
+      }
+      return result;
+    }
+  };
+
   /**
    * This test guards against accidental renaming or incorrect naming of the 
keyword used to
    * identify the service. The keyword is used to access the commands via the 
command line, so
@@ -126,48 +146,59 @@ public class KeywordStartIT {
   @Test
   public void testExpectedClasses() {
     
assumeTrue(Files.exists(Path.of(System.getProperty("user.dir")).resolve("src")));
-    TreeMap<String,Class<? extends KeywordExecutable>> expectSet = new 
TreeMap<>();
-    expectSet.put("admin", Admin.class);
-    expectSet.put("check-compaction-config", CheckCompactionConfig.class);
-    expectSet.put("check-accumulo-properties", CheckAccumuloProperties.class);
-    expectSet.put("compactor", CompactorExecutable.class);
-    expectSet.put("create-empty", CreateEmpty.class);
-    expectSet.put("create-token", CreateToken.class);
-    expectSet.put("dump-zoo", DumpZookeeper.class);
-    expectSet.put("ec-admin", ECAdmin.class);
-    expectSet.put("gc", GCExecutable.class);
-    expectSet.put("generate-splits", GenerateSplits.class);
-    expectSet.put("help", Help.class);
-    expectSet.put("info", Info.class);
-    expectSet.put("init", Initialize.class);
-    expectSet.put("login-info", LoginProperties.class);
-    expectSet.put("manager", ManagerExecutable.class);
-    expectSet.put("minicluster", MiniClusterExecutable.class);
-    expectSet.put("monitor", MonitorExecutable.class);
-    expectSet.put("rfile-info", PrintInfo.class);
-    expectSet.put("shell", Shell.class);
-    expectSet.put("split-large", SplitLarge.class);
-    expectSet.put("sserver", ScanServerExecutable.class);
-    expectSet.put("tserver", TServerExecutable.class);
-    expectSet.put("upgrade", UpgradeUtil.class);
-    expectSet.put("version", Version.class);
-    expectSet.put("wal-info", LogReader.class);
-    expectSet.put("zoo-info-viewer", ZooInfoViewer.class);
-    expectSet.put("zoo-prop-editor", ZooPropEditor.class);
-    expectSet.put("zoo-zap", ZooZap.class);
-    expectSet.put("zookeeper", ZooKeeperMain.class);
+    SortedSet<CommandInfo> expectSet = new TreeSet<>();
+    expectSet.add(new CommandInfo(UsageGroup.CORE, "admin", Admin.class));
+    expectSet.add(
+        new CommandInfo(UsageGroup.OTHER, "check-compaction-config", 
CheckCompactionConfig.class));
+    expectSet.add(new CommandInfo(UsageGroup.OTHER, 
"check-accumulo-properties",
+        CheckAccumuloProperties.class));
+    expectSet.add(new CommandInfo(UsageGroup.PROCESS, "compactor", 
CompactorExecutable.class));
+    expectSet.add(new CommandInfo(UsageGroup.OTHER, "create-empty", 
CreateEmpty.class));
+    expectSet.add(new CommandInfo(UsageGroup.OTHER, "create-token", 
CreateToken.class));
+    expectSet.add(new CommandInfo(UsageGroup.OTHER, "dump-zoo", 
DumpZookeeper.class));
+    expectSet.add(new CommandInfo(UsageGroup.CORE, "ec-admin", ECAdmin.class));
+    expectSet.add(new CommandInfo(UsageGroup.PROCESS, "gc", 
GCExecutable.class));
+    expectSet.add(new CommandInfo(UsageGroup.OTHER, "generate-splits", 
GenerateSplits.class));
+    expectSet.add(new CommandInfo(UsageGroup.CORE, "help", Help.class));
+    expectSet.add(new CommandInfo(UsageGroup.CORE, "info", Info.class));
+    expectSet.add(new CommandInfo(UsageGroup.CORE, "init", Initialize.class));
+    expectSet.add(new CommandInfo(UsageGroup.OTHER, "login-info", 
LoginProperties.class));
+    expectSet.add(new CommandInfo(UsageGroup.PROCESS, "manager", 
ManagerExecutable.class));
+    expectSet.add(new CommandInfo(UsageGroup.PROCESS, "minicluster", 
MiniClusterExecutable.class));
+    expectSet.add(new CommandInfo(UsageGroup.PROCESS, "monitor", 
MonitorExecutable.class));
+    expectSet.add(new CommandInfo(UsageGroup.OTHER, "rfile-info", 
PrintInfo.class));
+    expectSet.add(new CommandInfo(UsageGroup.CORE, "shell", Shell.class));
+    expectSet.add(new CommandInfo(UsageGroup.OTHER, "split-large", 
SplitLarge.class));
+    expectSet.add(new CommandInfo(UsageGroup.PROCESS, "sserver", 
ScanServerExecutable.class));
+    expectSet.add(new CommandInfo(UsageGroup.PROCESS, "tserver", 
TServerExecutable.class));
+    expectSet.add(new CommandInfo(UsageGroup.OTHER, "upgrade", 
UpgradeUtil.class));
+    expectSet.add(new CommandInfo(UsageGroup.CORE, "version", Version.class));
+    expectSet.add(new CommandInfo(UsageGroup.OTHER, "wal-info", 
LogReader.class));
+    expectSet.add(new CommandInfo(UsageGroup.OTHER, "zoo-info-viewer", 
ZooInfoViewer.class));
+    expectSet.add(new CommandInfo(UsageGroup.OTHER, "zoo-prop-editor", 
ZooPropEditor.class));
+    expectSet.add(new CommandInfo(UsageGroup.OTHER, "zoo-zap", ZooZap.class));
+    expectSet.add(new CommandInfo(UsageGroup.PROCESS, "zookeeper", 
ZooKeeperMain.class));
+
+    Map<UsageGroup,Map<String,KeywordExecutable>> actualExecutables =
+        new TreeMap<>(getKeywordExecutables());
+    SortedSet<CommandInfo> actualSet = new TreeSet<>();
+    actualExecutables.entrySet().forEach((e) -> {
+      e.getValue().entrySet().forEach((e2) -> {
+        actualSet.add(new CommandInfo(e.getKey(), e2.getKey(), 
e2.getValue().getClass()));
+      });
+    });
+
+    Iterator<CommandInfo> expectIter = expectSet.iterator();
+    Iterator<CommandInfo> actualIter = actualSet.iterator();
 
-    Iterator<Entry<String,Class<? extends KeywordExecutable>>> expectIter =
-        expectSet.entrySet().iterator();
-    TreeMap<String,KeywordExecutable> actualSet = new 
TreeMap<>(getKeywordExecutables());
-    Iterator<Entry<String,KeywordExecutable>> actualIter = 
actualSet.entrySet().iterator();
-    Entry<String,Class<? extends KeywordExecutable>> expected;
-    Entry<String,KeywordExecutable> actual;
+    CommandInfo expected;
+    CommandInfo actual;
     while (expectIter.hasNext() && actualIter.hasNext()) {
       expected = expectIter.next();
       actual = actualIter.next();
-      assertEquals(expected.getKey(), actual.getKey());
-      assertEquals(expected.getValue(), actual.getValue().getClass());
+      assertEquals(expected.group(), actual.group());
+      assertEquals(expected.keyword(), actual.keyword());
+      assertEquals(expected.clazz(), actual.clazz());
     }
     boolean moreExpected = expectIter.hasNext();
     if (moreExpected) {
@@ -223,16 +254,23 @@ public class KeywordStartIT {
         c -> assertTrue(hasMain(c), "Class " + c.getName() + " is missing a 
main method!"));
 
     // build a list of all classed that implement KeywordExecutable
-    var all = 
getKeywordExecutables().values().stream().map(Object::getClass).collect(toSet());
+    Map<UsageGroup,Map<String,KeywordExecutable>> actualExecutables =
+        new TreeMap<>(getKeywordExecutables());
+    Set<Class<? extends KeywordExecutable>> actualSet = new HashSet<>();
+    actualExecutables.entrySet().forEach((e) -> {
+      e.getValue().entrySet().forEach((e2) -> {
+        actualSet.add(e2.getValue().getClass());
+      });
+    });
 
     // remove the ones we already verified have a main method
-    assertTrue(all.removeAll(expectSet));
+    assertTrue(actualSet.removeAll(expectSet));
 
     // ensure there's still some left (there should be some that don't have a 
main method)
-    assertNotEquals(0, all.size());
+    assertNotEquals(0, actualSet.size());
 
     // for those remaining, make sure they *don't* have an unexpected main 
method
-    all.forEach(
+    actualSet.forEach(
         c -> assertFalse(hasMain(c), "Class " + c.getName() + " has an 
unexpected main method!"));
   }
 

Reply via email to