From 4a4470eb4155591056663413a33a8e1f79918033 Mon Sep 17 00:00:00 2001 From: Trevor Pering Date: Tue, 12 Nov 2024 12:50:11 -0800 Subject: [PATCH 01/18] CLI starting --- .../daq/mqtt/validator/CommandLineOption.java | 15 +++++ .../mqtt/validator/CommandLineProcessor.java | 62 +++++++++++++++++++ .../google/daq/mqtt/validator/Validator.java | 10 ++- 3 files changed, 85 insertions(+), 2 deletions(-) create mode 100644 validator/src/main/java/com/google/daq/mqtt/validator/CommandLineOption.java create mode 100644 validator/src/main/java/com/google/daq/mqtt/validator/CommandLineProcessor.java diff --git a/validator/src/main/java/com/google/daq/mqtt/validator/CommandLineOption.java b/validator/src/main/java/com/google/daq/mqtt/validator/CommandLineOption.java new file mode 100644 index 000000000..16956e247 --- /dev/null +++ b/validator/src/main/java/com/google/daq/mqtt/validator/CommandLineOption.java @@ -0,0 +1,15 @@ +package com.google.daq.mqtt.validator; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +@Retention(RetentionPolicy.RUNTIME) +@Target({ElementType.METHOD}) +public @interface CommandLineOption { + + String option(); + + String long_form(); +} diff --git a/validator/src/main/java/com/google/daq/mqtt/validator/CommandLineProcessor.java b/validator/src/main/java/com/google/daq/mqtt/validator/CommandLineProcessor.java new file mode 100644 index 000000000..7d80478e1 --- /dev/null +++ b/validator/src/main/java/com/google/daq/mqtt/validator/CommandLineProcessor.java @@ -0,0 +1,62 @@ +package com.google.daq.mqtt.validator; + +import static com.google.udmi.util.GeneralUtils.ifNotNullThen; + +import java.lang.reflect.Method; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Map.Entry; +import java.util.Optional; +import java.util.function.Consumer; + +public class CommandLineProcessor { + + private static final String TERMINATING_ARG = "--"; + private static final String EQUALS_SIGN = "="; + private final Object target + + Map optionMap = new HashMap<>(); + + public CommandLineProcessor(Object target, Consumer usage) { + this.target = target; + + List methods = new ArrayList<>(List.of(target.getClass().getMethods())); + + methods.forEach(method -> ifNotNullThen(method.getAnnotation(CommandLineOption.class), + a -> optionMap.put(a, method))); + } + + public List processArgs(List argList) { + while (!argList.isEmpty()) { + String arg = argList.remove(0); + if (arg.equals(TERMINATING_ARG)) { + return argList; + } + Optional> first = optionMap.entrySet().stream() + .filter(option -> processArgEntry(arg, option.getKey(), option.getValue(), argList)) + .findFirst(); + if (first.isEmpty()) { + throw new IllegalStateException("Unrecognized command line option '" + arg + "'"); + } + } + } + + private boolean processArgEntry(String arg, CommandLineOption option, Method method, + List argList) { + if (arg.equals(option.option())) { + if (requiresArg(method)) { + String arg = argList.remove(0); + method.invoke(target, arg); + } + return true; + } else if (arg.startsWith(option.long_form())) { + + } + return false; + } + + private boolean requiresArg(Method method) { + } +} \ No newline at end of file diff --git a/validator/src/main/java/com/google/daq/mqtt/validator/Validator.java b/validator/src/main/java/com/google/daq/mqtt/validator/Validator.java index a336dbc5d..718698e31 100644 --- a/validator/src/main/java/com/google/daq/mqtt/validator/Validator.java +++ b/validator/src/main/java/com/google/daq/mqtt/validator/Validator.java @@ -238,11 +238,10 @@ public Validator(ExecutionConfiguration validatorConfig, BiConsumer argList) { outputLogger = new LoggingHandler(); List listCopy = new ArrayList<>(argList); - parseArgs(listCopy); + targetDevices = Set.copyOf(parseArgs(listCopy)); if (client == null) { validateReflector(); } - targetDevices = Set.copyOf(listCopy); siteModel = ifNotNullGet(config.site_model, SiteModel::new); ifNotNullThen(siteModel, this::initializeExpectedDevices); } @@ -346,10 +345,13 @@ private List parseArgs(List argList) { private List postProcessArgs(List argList) { try { + CommandLineProcessor commandLineProcessor = new CommandLineProcessor(this); + commandLineProcessor.processArgs(argList); while (!argList.isEmpty()) { String option = removeNextArg(argList); try { switch (option) { + case "-h" -> showHelp(); case "-p" -> setProjectId(removeNextArg(argList)); case "-s" -> setSiteDir(removeNextArg(argList)); case "-a" -> setSchemaSpec(removeNextArg(argList)); @@ -379,6 +381,10 @@ private List postProcessArgs(List argList) { } } + @CommandLineOption(option = "-h", long_form = "--help") + private void showHelp() { + } + private void setReportDelay(String arg) { reportingDelay = Integer.parseInt(arg); } From 58034fc1e2255f8985547d66393d616c1ff53eee Mon Sep 17 00:00:00 2001 From: Trevor Pering Date: Tue, 12 Nov 2024 15:05:17 -0800 Subject: [PATCH 02/18] Basic command line processor --- .../google/daq/mqtt/registrar/Registrar.java | 16 ++- .../daq/mqtt/validator/CommandLineOption.java | 6 +- .../mqtt/validator/CommandLineProcessor.java | 98 +++++++++++++++---- .../google/daq/mqtt/validator/Validator.java | 10 +- 4 files changed, 95 insertions(+), 35 deletions(-) diff --git a/validator/src/main/java/com/google/daq/mqtt/registrar/Registrar.java b/validator/src/main/java/com/google/daq/mqtt/registrar/Registrar.java index d87d9658d..855b64651 100644 --- a/validator/src/main/java/com/google/daq/mqtt/registrar/Registrar.java +++ b/validator/src/main/java/com/google/daq/mqtt/registrar/Registrar.java @@ -53,6 +53,8 @@ import com.google.daq.mqtt.util.ExceptionMap.ErrorTree; import com.google.daq.mqtt.util.PubSubPusher; import com.google.daq.mqtt.util.ValidationError; +import com.google.daq.mqtt.validator.CommandLineOption; +import com.google.daq.mqtt.validator.CommandLineProcessor; import com.google.udmi.util.Common; import com.google.udmi.util.SiteModel; import java.io.File; @@ -83,6 +85,7 @@ import java.util.concurrent.Executors; import java.util.concurrent.Future; import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicInteger; import java.util.function.Consumer; import java.util.stream.Collectors; @@ -150,6 +153,7 @@ public class Registrar { private List> executing = new ArrayList<>(); private SiteModel siteModel; private boolean queryOnly; + private final AtomicBoolean helpShown = new AtomicBoolean(); /** * Main entry point for registrar. @@ -206,6 +210,8 @@ Registrar processArgs(List argListRaw) { } Registrar postProcessArgs(List argList) { + CommandLineProcessor commandLineProcessor = new CommandLineProcessor(this); + commandLineProcessor.processArgs(argList); requireNonNull(siteModel, "siteModel not defined"); while (argList.size() > 0) { String option = argList.remove(0); @@ -219,7 +225,7 @@ Registrar postProcessArgs(List argList) { case "-u" -> setUpdateFlag(true); case "-b" -> setBlockUnknown(true); case "-l" -> setIdleLimit(removeArg(argList, "idle limit")); - case "-t" -> setValidateMetadata(false); + case "-t" -> setValidateMetadata(); case "-q" -> setQueryOnly(true); case "-d" -> setDeleteDevices(true); case "-x" -> setExpungeDevices(true); @@ -364,8 +370,9 @@ private void setUpdateFlag(boolean update) { updateCloudIoT = update; } - private void setValidateMetadata(boolean validateMetadata) { - this.doValidate = validateMetadata; + @CommandLineOption(short_form = "-t") + private void setValidateMetadata() { + this.doValidate = false; } private void setDeviceList(List deviceList) { @@ -1178,7 +1185,8 @@ protected void setProjectId(String projectId) { this.projectId = projectId; } - protected void setToolRoot(String toolRoot) { + @CommandLineOption(short_form = "-r") + public void setToolRoot(String toolRoot) { try { schemaBase = new File(toolRoot, SCHEMA_BASE_PATH); if (!schemaBase.isDirectory()) { diff --git a/validator/src/main/java/com/google/daq/mqtt/validator/CommandLineOption.java b/validator/src/main/java/com/google/daq/mqtt/validator/CommandLineOption.java index 16956e247..dc43ca6b5 100644 --- a/validator/src/main/java/com/google/daq/mqtt/validator/CommandLineOption.java +++ b/validator/src/main/java/com/google/daq/mqtt/validator/CommandLineOption.java @@ -9,7 +9,9 @@ @Target({ElementType.METHOD}) public @interface CommandLineOption { - String option(); + String NO_VALUE = ""; - String long_form(); + String short_form() default NO_VALUE; + + String long_form() default NO_VALUE; } diff --git a/validator/src/main/java/com/google/daq/mqtt/validator/CommandLineProcessor.java b/validator/src/main/java/com/google/daq/mqtt/validator/CommandLineProcessor.java index 7d80478e1..e6ef109b5 100644 --- a/validator/src/main/java/com/google/daq/mqtt/validator/CommandLineProcessor.java +++ b/validator/src/main/java/com/google/daq/mqtt/validator/CommandLineProcessor.java @@ -1,62 +1,118 @@ package com.google.daq.mqtt.validator; +import static com.google.common.base.Preconditions.checkState; import static com.google.udmi.util.GeneralUtils.ifNotNullThen; import java.lang.reflect.Method; +import java.lang.reflect.Type; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Map.Entry; import java.util.Optional; -import java.util.function.Consumer; public class CommandLineProcessor { private static final String TERMINATING_ARG = "--"; private static final String EQUALS_SIGN = "="; - private final Object target + private static final int LINUX_SUCCESS_CODE = 0; + private static final int LINUX_ERROR_CODE = -1; + private final Object target; + private final Method showHelpMethod; Map optionMap = new HashMap<>(); - public CommandLineProcessor(Object target, Consumer usage) { + public CommandLineProcessor(Object target) { this.target = target; List methods = new ArrayList<>(List.of(target.getClass().getMethods())); + showHelpMethod = getShowHelpMethod(); + optionMap.put(getShowHelpOption(), showHelpMethod); methods.forEach(method -> ifNotNullThen(method.getAnnotation(CommandLineOption.class), a -> optionMap.put(a, method))); } + private Method getShowHelpMethod() { + try { + return CommandLineProcessor.class.getMethod("showUsage"); + } catch (Exception e) { + throw new RuntimeException("While getting showHelp method", e); + } + } + + private CommandLineOption getShowHelpOption() { + return showHelpMethod.getAnnotation(CommandLineOption.class); + } + + @CommandLineOption(short_form = "-h") + public void showUsage() { + showUsage(null); + } + + private void showUsage(String message) { + ifNotNullThen(message, m -> System.err.println(m)); + System.err.println("Options supported:"); + optionMap.forEach((option, method) -> + System.err.printf(" %s: %s%n", option.short_form(), option.long_form())); + System.exit(message == null ? LINUX_SUCCESS_CODE : LINUX_ERROR_CODE); + } + public List processArgs(List argList) { - while (!argList.isEmpty()) { - String arg = argList.remove(0); - if (arg.equals(TERMINATING_ARG)) { - return argList; - } - Optional> first = optionMap.entrySet().stream() - .filter(option -> processArgEntry(arg, option.getKey(), option.getValue(), argList)) - .findFirst(); - if (first.isEmpty()) { - throw new IllegalStateException("Unrecognized command line option '" + arg + "'"); + try { + while (!argList.isEmpty()) { + String arg = argList.remove(0); + if (arg.equals(TERMINATING_ARG)) { + return argList; + } + Optional> first = optionMap.entrySet().stream() + .filter(option -> processArgEntry(arg, option.getKey(), option.getValue(), argList)) + .findFirst(); + if (first.isEmpty()) { + throw new IllegalStateException("Unrecognized command line option '" + arg + "'"); + } } + return argList; + } catch (Exception e) { + showUsage(e.getMessage()); + return null; } } private boolean processArgEntry(String arg, CommandLineOption option, Method method, List argList) { - if (arg.equals(option.option())) { - if (requiresArg(method)) { - String arg = argList.remove(0); - method.invoke(target, arg); + try { + if (arg.equals(option.short_form())) { + if (method.equals(showHelpMethod)) { + showUsage(); + } else if (requiresArg(method)) { + String parameter = argList.remove(0); + method.invoke(target, parameter); + } else { + method.invoke(target); + } + return true; + } else if (!option.long_form().isBlank() && arg.startsWith(option.long_form())) { + throw new RuntimeException("Long form command line not yet supported"); + } else if (option.short_form().isBlank() && option.long_form().isBlank()) { + throw new RuntimeException( + "Neither long nor short form not defined for " + method.getName()); } - return true; - } else if (arg.startsWith(option.long_form())) { - + return false; + } catch (Exception e) { + throw new RuntimeException("Processing command line method " + method.getName(), e); } - return false; + } + + private void cleanExit(boolean success) { + System.exit(success ? LINUX_SUCCESS_CODE : LINUX_ERROR_CODE); } private boolean requiresArg(Method method) { + Type[] genericParameterTypes = method.getGenericParameterTypes(); + checkState(genericParameterTypes.length <= 1, + "expected <= 1 parameter for command line method %s", method.getName()); + return genericParameterTypes.length == 1; } } \ No newline at end of file diff --git a/validator/src/main/java/com/google/daq/mqtt/validator/Validator.java b/validator/src/main/java/com/google/daq/mqtt/validator/Validator.java index 718698e31..a336dbc5d 100644 --- a/validator/src/main/java/com/google/daq/mqtt/validator/Validator.java +++ b/validator/src/main/java/com/google/daq/mqtt/validator/Validator.java @@ -238,10 +238,11 @@ public Validator(ExecutionConfiguration validatorConfig, BiConsumer argList) { outputLogger = new LoggingHandler(); List listCopy = new ArrayList<>(argList); - targetDevices = Set.copyOf(parseArgs(listCopy)); + parseArgs(listCopy); if (client == null) { validateReflector(); } + targetDevices = Set.copyOf(listCopy); siteModel = ifNotNullGet(config.site_model, SiteModel::new); ifNotNullThen(siteModel, this::initializeExpectedDevices); } @@ -345,13 +346,10 @@ private List parseArgs(List argList) { private List postProcessArgs(List argList) { try { - CommandLineProcessor commandLineProcessor = new CommandLineProcessor(this); - commandLineProcessor.processArgs(argList); while (!argList.isEmpty()) { String option = removeNextArg(argList); try { switch (option) { - case "-h" -> showHelp(); case "-p" -> setProjectId(removeNextArg(argList)); case "-s" -> setSiteDir(removeNextArg(argList)); case "-a" -> setSchemaSpec(removeNextArg(argList)); @@ -381,10 +379,6 @@ private List postProcessArgs(List argList) { } } - @CommandLineOption(option = "-h", long_form = "--help") - private void showHelp() { - } - private void setReportDelay(String arg) { reportingDelay = Integer.parseInt(arg); } From 4fe8bd620b935a7240c18105f79a250042af6346 Mon Sep 17 00:00:00 2001 From: Trevor Pering Date: Tue, 12 Nov 2024 16:12:08 -0800 Subject: [PATCH 03/18] Add TODO --- .../java/com/google/daq/mqtt/validator/CommandLineProcessor.java | 1 + 1 file changed, 1 insertion(+) diff --git a/validator/src/main/java/com/google/daq/mqtt/validator/CommandLineProcessor.java b/validator/src/main/java/com/google/daq/mqtt/validator/CommandLineProcessor.java index e6ef109b5..015c95056 100644 --- a/validator/src/main/java/com/google/daq/mqtt/validator/CommandLineProcessor.java +++ b/validator/src/main/java/com/google/daq/mqtt/validator/CommandLineProcessor.java @@ -32,6 +32,7 @@ public CommandLineProcessor(Object target) { optionMap.put(getShowHelpOption(), showHelpMethod); methods.forEach(method -> ifNotNullThen(method.getAnnotation(CommandLineOption.class), a -> optionMap.put(a, method))); + // TODO: Make this make target methods accessible. } private Method getShowHelpMethod() { From 9015050fd9796f8198212288ebdb0c1e65709ff3 Mon Sep 17 00:00:00 2001 From: Trevor Pering Date: Tue, 12 Nov 2024 16:28:32 -0800 Subject: [PATCH 04/18] Make methods accessible --- .../com/google/daq/mqtt/validator/CommandLineProcessor.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/validator/src/main/java/com/google/daq/mqtt/validator/CommandLineProcessor.java b/validator/src/main/java/com/google/daq/mqtt/validator/CommandLineProcessor.java index 015c95056..c77815d52 100644 --- a/validator/src/main/java/com/google/daq/mqtt/validator/CommandLineProcessor.java +++ b/validator/src/main/java/com/google/daq/mqtt/validator/CommandLineProcessor.java @@ -26,13 +26,13 @@ public class CommandLineProcessor { public CommandLineProcessor(Object target) { this.target = target; - List methods = new ArrayList<>(List.of(target.getClass().getMethods())); - showHelpMethod = getShowHelpMethod(); optionMap.put(getShowHelpOption(), showHelpMethod); + + List methods = new ArrayList<>(List.of(target.getClass().getDeclaredMethods())); methods.forEach(method -> ifNotNullThen(method.getAnnotation(CommandLineOption.class), a -> optionMap.put(a, method))); - // TODO: Make this make target methods accessible. + optionMap.values().forEach(method -> method.setAccessible(true)); } private Method getShowHelpMethod() { From 170eedb0ccec0de754b2435ebb6f80678aeb4033 Mon Sep 17 00:00:00 2001 From: Trevor Pering Date: Tue, 12 Nov 2024 16:31:42 -0800 Subject: [PATCH 05/18] Make methods private --- .../src/main/java/com/google/daq/mqtt/registrar/Registrar.java | 2 +- .../com/google/daq/mqtt/validator/CommandLineProcessor.java | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/validator/src/main/java/com/google/daq/mqtt/registrar/Registrar.java b/validator/src/main/java/com/google/daq/mqtt/registrar/Registrar.java index 855b64651..f03a0495c 100644 --- a/validator/src/main/java/com/google/daq/mqtt/registrar/Registrar.java +++ b/validator/src/main/java/com/google/daq/mqtt/registrar/Registrar.java @@ -1186,7 +1186,7 @@ protected void setProjectId(String projectId) { } @CommandLineOption(short_form = "-r") - public void setToolRoot(String toolRoot) { + private void setToolRoot(String toolRoot) { try { schemaBase = new File(toolRoot, SCHEMA_BASE_PATH); if (!schemaBase.isDirectory()) { diff --git a/validator/src/main/java/com/google/daq/mqtt/validator/CommandLineProcessor.java b/validator/src/main/java/com/google/daq/mqtt/validator/CommandLineProcessor.java index c77815d52..b093a7c47 100644 --- a/validator/src/main/java/com/google/daq/mqtt/validator/CommandLineProcessor.java +++ b/validator/src/main/java/com/google/daq/mqtt/validator/CommandLineProcessor.java @@ -48,7 +48,7 @@ private CommandLineOption getShowHelpOption() { } @CommandLineOption(short_form = "-h") - public void showUsage() { + private void showUsage() { showUsage(null); } From 7cceb105382fd4e237c31ef19a957223bc203ae4 Mon Sep 17 00:00:00 2001 From: Trevor Pering Date: Tue, 12 Nov 2024 16:40:13 -0800 Subject: [PATCH 06/18] Add a bunch of command line stuff --- .../google/daq/mqtt/registrar/Registrar.java | 19 +++++++++---------- 1 file changed, 9 insertions(+), 10 deletions(-) diff --git a/validator/src/main/java/com/google/daq/mqtt/registrar/Registrar.java b/validator/src/main/java/com/google/daq/mqtt/registrar/Registrar.java index f03a0495c..6bb6e6615 100644 --- a/validator/src/main/java/com/google/daq/mqtt/registrar/Registrar.java +++ b/validator/src/main/java/com/google/daq/mqtt/registrar/Registrar.java @@ -216,12 +216,6 @@ Registrar postProcessArgs(List argList) { while (argList.size() > 0) { String option = argList.remove(0); switch (option) { - case "-r" -> setToolRoot(removeArg(argList, "tool root")); - case "-p" -> setProjectId(removeArg(argList, "project id")); - case "-s" -> setSitePath(removeArg(argList, "site path")); - case "-a" -> setTargetRegistry(removeArg(argList, "alt registry")); - case "-e" -> setRegistrySuffix(removeArg(argList, "registry suffix")); - case "-f" -> setFeedTopic(removeArg(argList, "feed topic")); case "-u" -> setUpdateFlag(true); case "-b" -> setBlockUnknown(true); case "-l" -> setIdleLimit(removeArg(argList, "idle limit")); @@ -284,7 +278,8 @@ private void setExpungeDevices(boolean expungeDevices) { this.updateCloudIoT |= expungeDevices; } - void setRegistrySuffix(String suffix) { + @CommandLineOption(short_form = "-e") + private void setRegistrySuffix(String suffix) { siteModel.getExecutionConfiguration().registry_suffix = suffix; } @@ -380,6 +375,7 @@ private void setDeviceList(List deviceList) { blockUnknown = false; } + @CommandLineOption(short_form = "-f") private void setFeedTopic(String feedTopic) { System.err.println("Sending device feed to topic " + feedTopic); feedPusher = new PubSubPusher(projectId, feedTopic); @@ -447,7 +443,8 @@ private SetupUdmiConfig getCloudVersionInfo() { return ifNotNullGet(cloudIotManager, CloudIotManager::getVersionInformation); } - protected void setSitePath(String sitePath) { + @CommandLineOption(short_form = "-s") + private void setSitePath(String sitePath) { checkNotNull(SCHEMA_NAME, "schemaName not set yet"); siteDir = new File(sitePath); siteModel = ofNullable(siteModel).orElseGet(() -> new SiteModel(sitePath)); @@ -1174,7 +1171,8 @@ private void initializeDevices(Map localDevices) { }); } - protected void setProjectId(String projectId) { + @CommandLineOption(short_form = "-p") + private void setProjectId(String projectId) { if (NO_SITE.equals(projectId) || projectId == null) { this.projectId = null; return; @@ -1266,7 +1264,8 @@ public List getMockActions() { return cloudIotManager.getMockActions(); } - public void setTargetRegistry(String altRegistry) { + @CommandLineOption(short_form = "-a") + private void setTargetRegistry(String altRegistry) { siteModel.getExecutionConfiguration().registry_id = altRegistry; siteModel.getExecutionConfiguration().alt_registry = null; } From 3a968ae7dceef0789f6a3ba2be30eb220a758aaa Mon Sep 17 00:00:00 2001 From: Trevor Pering Date: Tue, 12 Nov 2024 16:43:24 -0800 Subject: [PATCH 07/18] Clean up options --- .../google/daq/mqtt/registrar/Registrar.java | 36 +++++++++---------- 1 file changed, 18 insertions(+), 18 deletions(-) diff --git a/validator/src/main/java/com/google/daq/mqtt/registrar/Registrar.java b/validator/src/main/java/com/google/daq/mqtt/registrar/Registrar.java index 6bb6e6615..a831416ce 100644 --- a/validator/src/main/java/com/google/daq/mqtt/registrar/Registrar.java +++ b/validator/src/main/java/com/google/daq/mqtt/registrar/Registrar.java @@ -216,13 +216,13 @@ Registrar postProcessArgs(List argList) { while (argList.size() > 0) { String option = argList.remove(0); switch (option) { - case "-u" -> setUpdateFlag(true); - case "-b" -> setBlockUnknown(true); + case "-u" -> setUpdateFlag(); + case "-b" -> setBlockUnknown(); case "-l" -> setIdleLimit(removeArg(argList, "idle limit")); case "-t" -> setValidateMetadata(); - case "-q" -> setQueryOnly(true); - case "-d" -> setDeleteDevices(true); - case "-x" -> setExpungeDevices(true); + case "-q" -> setQueryOnly(); + case "-d" -> setDeleteDevices(); + case "-x" -> setExpungeDevices(); case "-n" -> setRunnerThreads(removeArg(argList, "runner threads")); case "-c" -> setCreateRegistries(removeArg(argList, "create registries")); case "--" -> { @@ -243,12 +243,12 @@ Registrar postProcessArgs(List argList) { return this; } - private void setQueryOnly(boolean queryOnly) { - this.queryOnly = queryOnly; + private void setQueryOnly() { + this.queryOnly = true; } - private void setBlockUnknown(boolean block) { - blockUnknown = block; + private void setBlockUnknown() { + blockUnknown = true; } private void setCreateRegistries(String registrySpec) { @@ -266,16 +266,16 @@ private void setRunnerThreads(String argValue) { runnerThreads = Integer.parseInt(argValue); } - private void setDeleteDevices(boolean deleteDevices) { - this.deleteDevices = deleteDevices; + private void setDeleteDevices() { checkNotNull(projectId, "delete devices specified with no target project"); - this.updateCloudIoT |= deleteDevices; + this.deleteDevices = true; + this.updateCloudIoT = true; } - private void setExpungeDevices(boolean expungeDevices) { - this.expungeDevices = expungeDevices; + private void setExpungeDevices() { checkNotNull(projectId, "expunge devices specified with no target project"); - this.updateCloudIoT |= expungeDevices; + this.expungeDevices = true; + this.updateCloudIoT = true; } @CommandLineOption(short_form = "-e") @@ -288,7 +288,7 @@ private void processProfile(ExecutionConfiguration config) { setSitePath(config.site_model); setProjectId(config.project_id); if (config.project_id != null) { - setUpdateFlag(true); + setUpdateFlag(); } } @@ -361,8 +361,8 @@ private void setIdleLimit(String option) { System.err.println("Limiting devices to duration " + idleLimit.toSeconds() + "s"); } - private void setUpdateFlag(boolean update) { - updateCloudIoT = update; + private void setUpdateFlag() { + updateCloudIoT = true; } @CommandLineOption(short_form = "-t") From 740460c88c385151ce4beb1c314b7a1af99cbb7f Mon Sep 17 00:00:00 2001 From: Trevor Pering Date: Tue, 12 Nov 2024 16:45:14 -0800 Subject: [PATCH 08/18] Declared method --- .../com/google/daq/mqtt/validator/CommandLineProcessor.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/validator/src/main/java/com/google/daq/mqtt/validator/CommandLineProcessor.java b/validator/src/main/java/com/google/daq/mqtt/validator/CommandLineProcessor.java index b093a7c47..538177737 100644 --- a/validator/src/main/java/com/google/daq/mqtt/validator/CommandLineProcessor.java +++ b/validator/src/main/java/com/google/daq/mqtt/validator/CommandLineProcessor.java @@ -37,7 +37,7 @@ public CommandLineProcessor(Object target) { private Method getShowHelpMethod() { try { - return CommandLineProcessor.class.getMethod("showUsage"); + return CommandLineProcessor.class.getDeclaredMethod("showUsage"); } catch (Exception e) { throw new RuntimeException("While getting showHelp method", e); } From 9ea5831a659f09eefc60affe8b2d809e1ceb60cf Mon Sep 17 00:00:00 2001 From: Trevor Pering Date: Tue, 12 Nov 2024 16:49:48 -0800 Subject: [PATCH 09/18] Convert all --- .../google/daq/mqtt/registrar/Registrar.java | 36 +++++-------------- 1 file changed, 9 insertions(+), 27 deletions(-) diff --git a/validator/src/main/java/com/google/daq/mqtt/registrar/Registrar.java b/validator/src/main/java/com/google/daq/mqtt/registrar/Registrar.java index a831416ce..7f314c0ea 100644 --- a/validator/src/main/java/com/google/daq/mqtt/registrar/Registrar.java +++ b/validator/src/main/java/com/google/daq/mqtt/registrar/Registrar.java @@ -213,44 +213,21 @@ Registrar postProcessArgs(List argList) { CommandLineProcessor commandLineProcessor = new CommandLineProcessor(this); commandLineProcessor.processArgs(argList); requireNonNull(siteModel, "siteModel not defined"); - while (argList.size() > 0) { - String option = argList.remove(0); - switch (option) { - case "-u" -> setUpdateFlag(); - case "-b" -> setBlockUnknown(); - case "-l" -> setIdleLimit(removeArg(argList, "idle limit")); - case "-t" -> setValidateMetadata(); - case "-q" -> setQueryOnly(); - case "-d" -> setDeleteDevices(); - case "-x" -> setExpungeDevices(); - case "-n" -> setRunnerThreads(removeArg(argList, "runner threads")); - case "-c" -> setCreateRegistries(removeArg(argList, "create registries")); - case "--" -> { - setDeviceList(argList); - return this; - } - default -> { - if (option.startsWith("-")) { - throw new RuntimeException("Unknown cmdline option " + option); - } - // Add the current non-option back into the list and use it as device names list. - argList.add(0, option); - setDeviceList(argList); - return this; - } - } - } + setDeviceList(argList); return this; } + @CommandLineOption(short_form = "-q") private void setQueryOnly() { this.queryOnly = true; } + @CommandLineOption(short_form = "-b") private void setBlockUnknown() { blockUnknown = true; } + @CommandLineOption(short_form = "-c") private void setCreateRegistries(String registrySpec) { try { createRegistries = Integer.parseInt(registrySpec); @@ -262,16 +239,19 @@ private void setCreateRegistries(String registrySpec) { } } + @CommandLineOption(short_form = "-n") private void setRunnerThreads(String argValue) { runnerThreads = Integer.parseInt(argValue); } + @CommandLineOption(short_form = "-d") private void setDeleteDevices() { checkNotNull(projectId, "delete devices specified with no target project"); this.deleteDevices = true; this.updateCloudIoT = true; } + @CommandLineOption(short_form = "-x") private void setExpungeDevices() { checkNotNull(projectId, "expunge devices specified with no target project"); this.expungeDevices = true; @@ -356,11 +336,13 @@ private void createRegistrySuffix(String suffix) { System.err.println("Created registry " + registry); } + @CommandLineOption(short_form = "-l") private void setIdleLimit(String option) { idleLimit = Duration.parse("PT" + option); System.err.println("Limiting devices to duration " + idleLimit.toSeconds() + "s"); } + @CommandLineOption(short_form = "-u") private void setUpdateFlag() { updateCloudIoT = true; } From d550cedae76461c48d8a05316ec51311aef3f1b8 Mon Sep 17 00:00:00 2001 From: Trevor Pering Date: Tue, 12 Nov 2024 16:52:00 -0800 Subject: [PATCH 10/18] Fix remaining args use --- .../main/java/com/google/daq/mqtt/registrar/Registrar.java | 4 ++-- .../com/google/daq/mqtt/validator/CommandLineProcessor.java | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/validator/src/main/java/com/google/daq/mqtt/registrar/Registrar.java b/validator/src/main/java/com/google/daq/mqtt/registrar/Registrar.java index 7f314c0ea..8ebbe4ef1 100644 --- a/validator/src/main/java/com/google/daq/mqtt/registrar/Registrar.java +++ b/validator/src/main/java/com/google/daq/mqtt/registrar/Registrar.java @@ -211,9 +211,9 @@ Registrar processArgs(List argListRaw) { Registrar postProcessArgs(List argList) { CommandLineProcessor commandLineProcessor = new CommandLineProcessor(this); - commandLineProcessor.processArgs(argList); + List remainingArgs = commandLineProcessor.processArgs(argList); + ifNotNullThen(remainingArgs, this::setDeviceList); requireNonNull(siteModel, "siteModel not defined"); - setDeviceList(argList); return this; } diff --git a/validator/src/main/java/com/google/daq/mqtt/validator/CommandLineProcessor.java b/validator/src/main/java/com/google/daq/mqtt/validator/CommandLineProcessor.java index 538177737..183b73a35 100644 --- a/validator/src/main/java/com/google/daq/mqtt/validator/CommandLineProcessor.java +++ b/validator/src/main/java/com/google/daq/mqtt/validator/CommandLineProcessor.java @@ -74,7 +74,7 @@ public List processArgs(List argList) { throw new IllegalStateException("Unrecognized command line option '" + arg + "'"); } } - return argList; + return null; } catch (Exception e) { showUsage(e.getMessage()); return null; From 905d0b9d8611443b0420dca19bb0e8842930d4af Mon Sep 17 00:00:00 2001 From: Trevor Pering Date: Tue, 12 Nov 2024 18:06:08 -0800 Subject: [PATCH 11/18] Fix siteModel arg processing --- .../com/google/udmi/util/GeneralUtils.java | 9 ++++++++- .../java/com/google/udmi/util/SiteModel.java | 5 +++-- .../google/daq/mqtt/registrar/Registrar.java | 8 ++++++-- .../daq/mqtt/validator/CommandLineOption.java | 9 +++++++++ .../mqtt/validator/CommandLineProcessor.java | 19 ++++++++++++++----- 5 files changed, 40 insertions(+), 10 deletions(-) diff --git a/common/src/main/java/com/google/udmi/util/GeneralUtils.java b/common/src/main/java/com/google/udmi/util/GeneralUtils.java index 130de086b..820226745 100644 --- a/common/src/main/java/com/google/udmi/util/GeneralUtils.java +++ b/common/src/main/java/com/google/udmi/util/GeneralUtils.java @@ -660,11 +660,18 @@ public static String prefixedDifference(String prefix, Set a, Set argList, String description) { if (argList.isEmpty()) { - throw new RuntimeException(format("Missing required %s argument", description)); + throw new IllegalArgumentException(format("Missing required %s argument", description)); } return argList.remove(0); } + public static String removeStringArg(List argList, String description) { + if (!argList.isEmpty() && argList.get(0).startsWith("-")) { + throw new IllegalArgumentException(format("Missing required %s string argument", description)); + } + return removeArg(argList, description); + } + public static byte[] getFileBytes(String dataFile) { Path dataPath = Paths.get(dataFile); try { diff --git a/common/src/main/java/com/google/udmi/util/SiteModel.java b/common/src/main/java/com/google/udmi/util/SiteModel.java index 4e7b21745..bdfe0b898 100644 --- a/common/src/main/java/com/google/udmi/util/SiteModel.java +++ b/common/src/main/java/com/google/udmi/util/SiteModel.java @@ -18,6 +18,7 @@ import static com.google.udmi.util.GeneralUtils.ifNotNullThen; import static com.google.udmi.util.GeneralUtils.ifNullThen; import static com.google.udmi.util.GeneralUtils.removeArg; +import static com.google.udmi.util.GeneralUtils.removeStringArg; import static com.google.udmi.util.GeneralUtils.sha256; import static com.google.udmi.util.JsonUtil.asMap; import static com.google.udmi.util.JsonUtil.convertTo; @@ -150,7 +151,7 @@ private static void loadVersionInfo(ExecutionConfiguration exeConfig) { } public SiteModel(String toolName, List argList) { - this(removeArg(argList, "site_model"), projectSpecSupplier(argList), null); + this(removeStringArg(argList, "site_model"), projectSpecSupplier(argList), null); ExecutionConfiguration executionConfiguration = getExecutionConfiguration(); File outFile = new File(CONFIG_OUT_DIR, format("%s_conf.json", toolName)); System.err.println("Writing reconciled configuration file to " + outFile.getAbsolutePath()); @@ -171,7 +172,7 @@ private static Supplier projectSpecSupplier(List argList) { if (nextArg.startsWith("-") && !NO_SITE.equals(nextArg)) { return null; } - return removeArg(argList, "project_spec"); + return removeStringArg(argList, "project_spec"); }; } diff --git a/validator/src/main/java/com/google/daq/mqtt/registrar/Registrar.java b/validator/src/main/java/com/google/daq/mqtt/registrar/Registrar.java index 8ebbe4ef1..f97460ae5 100644 --- a/validator/src/main/java/com/google/daq/mqtt/registrar/Registrar.java +++ b/validator/src/main/java/com/google/daq/mqtt/registrar/Registrar.java @@ -154,6 +154,7 @@ public class Registrar { private SiteModel siteModel; private boolean queryOnly; private final AtomicBoolean helpShown = new AtomicBoolean(); + private CommandLineProcessor commandLineProcessor = new CommandLineProcessor(this); /** * Main entry point for registrar. @@ -204,13 +205,16 @@ Registrar processArgs(List argListRaw) { // Add implicit NO_SITE site spec for local-only site model processing. argList.add(NO_SITE); } - siteModel = new SiteModel(TOOL_NAME, argList); + try { + siteModel = new SiteModel(TOOL_NAME, argList); + } catch (IllegalArgumentException e) { + commandLineProcessor.showUsage(e.getMessage()); + } processProfile(siteModel.getExecutionConfiguration()); return postProcessArgs(argList); } Registrar postProcessArgs(List argList) { - CommandLineProcessor commandLineProcessor = new CommandLineProcessor(this); List remainingArgs = commandLineProcessor.processArgs(argList); ifNotNullThen(remainingArgs, this::setDeviceList); requireNonNull(siteModel, "siteModel not defined"); diff --git a/validator/src/main/java/com/google/daq/mqtt/validator/CommandLineOption.java b/validator/src/main/java/com/google/daq/mqtt/validator/CommandLineOption.java index dc43ca6b5..bc66af035 100644 --- a/validator/src/main/java/com/google/daq/mqtt/validator/CommandLineOption.java +++ b/validator/src/main/java/com/google/daq/mqtt/validator/CommandLineOption.java @@ -5,13 +5,22 @@ import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; +/** + * Annotation to indicate a command-line argument. + */ @Retention(RetentionPolicy.RUNTIME) @Target({ElementType.METHOD}) public @interface CommandLineOption { String NO_VALUE = ""; + /** + * Short form argument. + */ String short_form() default NO_VALUE; + /** + * Long form argument. + */ String long_form() default NO_VALUE; } diff --git a/validator/src/main/java/com/google/daq/mqtt/validator/CommandLineProcessor.java b/validator/src/main/java/com/google/daq/mqtt/validator/CommandLineProcessor.java index 183b73a35..9bc4c0f25 100644 --- a/validator/src/main/java/com/google/daq/mqtt/validator/CommandLineProcessor.java +++ b/validator/src/main/java/com/google/daq/mqtt/validator/CommandLineProcessor.java @@ -12,6 +12,9 @@ import java.util.Map.Entry; import java.util.Optional; +/** + * Simple command line parser class. + */ public class CommandLineProcessor { private static final String TERMINATING_ARG = "--"; @@ -23,6 +26,9 @@ public class CommandLineProcessor { Map optionMap = new HashMap<>(); + /** + * Create a new command line processor for the given target object. + */ public CommandLineProcessor(Object target) { this.target = target; @@ -52,7 +58,7 @@ private void showUsage() { showUsage(null); } - private void showUsage(String message) { + public void showUsage(String message) { ifNotNullThen(message, m -> System.err.println(m)); System.err.println("Options supported:"); optionMap.forEach((option, method) -> @@ -60,6 +66,9 @@ private void showUsage(String message) { System.exit(message == null ? LINUX_SUCCESS_CODE : LINUX_ERROR_CODE); } + /** + * Process the given arg list. Return a list of remaining arguments (if any). + */ public List processArgs(List argList) { try { while (!argList.isEmpty()) { @@ -71,7 +80,7 @@ public List processArgs(List argList) { .filter(option -> processArgEntry(arg, option.getKey(), option.getValue(), argList)) .findFirst(); if (first.isEmpty()) { - throw new IllegalStateException("Unrecognized command line option '" + arg + "'"); + throw new IllegalArgumentException("Unrecognized command line option '" + arg + "'"); } } return null; @@ -95,14 +104,14 @@ private boolean processArgEntry(String arg, CommandLineOption option, Method met } return true; } else if (!option.long_form().isBlank() && arg.startsWith(option.long_form())) { - throw new RuntimeException("Long form command line not yet supported"); + throw new IllegalArgumentException("Long form command line not yet supported"); } else if (option.short_form().isBlank() && option.long_form().isBlank()) { - throw new RuntimeException( + throw new IllegalArgumentException( "Neither long nor short form not defined for " + method.getName()); } return false; } catch (Exception e) { - throw new RuntimeException("Processing command line method " + method.getName(), e); + throw new IllegalArgumentException("Processing command line method " + method.getName(), e); } } From b66a75dad48bd713c83add7c0226dab3ad3a8399 Mon Sep 17 00:00:00 2001 From: Trevor Pering Date: Tue, 12 Nov 2024 18:19:13 -0800 Subject: [PATCH 12/18] Fix NO_SITE usage --- .../src/main/java/com/google/udmi/util/SiteModel.java | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/common/src/main/java/com/google/udmi/util/SiteModel.java b/common/src/main/java/com/google/udmi/util/SiteModel.java index bdfe0b898..66bc44962 100644 --- a/common/src/main/java/com/google/udmi/util/SiteModel.java +++ b/common/src/main/java/com/google/udmi/util/SiteModel.java @@ -168,11 +168,16 @@ public SiteModel(ExecutionConfiguration executionConfiguration) { private static Supplier projectSpecSupplier(List argList) { return () -> { - String nextArg = argList.isEmpty() ? "" : argList.get(0); - if (nextArg.startsWith("-") && !NO_SITE.equals(nextArg)) { + if (argList.isEmpty()) { + throw new IllegalArgumentException("Missing required project spec argument"); + } + String nextArg = argList.get(0); + if (nextArg.equals(NO_SITE)) { + return NO_SITE; + } else if (nextArg.startsWith("-")) { return null; } - return removeStringArg(argList, "project_spec"); + return removeStringArg(argList, "project spec"); }; } From 0315de101055f4e942aefa02c89a684b91c6bf1c Mon Sep 17 00:00:00 2001 From: Trevor Pering Date: Tue, 12 Nov 2024 18:25:49 -0800 Subject: [PATCH 13/18] Proper handling of final no-opt args --- .../com/google/daq/mqtt/validator/CommandLineProcessor.java | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/validator/src/main/java/com/google/daq/mqtt/validator/CommandLineProcessor.java b/validator/src/main/java/com/google/daq/mqtt/validator/CommandLineProcessor.java index 9bc4c0f25..1925f6855 100644 --- a/validator/src/main/java/com/google/daq/mqtt/validator/CommandLineProcessor.java +++ b/validator/src/main/java/com/google/daq/mqtt/validator/CommandLineProcessor.java @@ -82,6 +82,10 @@ public List processArgs(List argList) { if (first.isEmpty()) { throw new IllegalArgumentException("Unrecognized command line option '" + arg + "'"); } + if (!arg.startsWith("-")) { + argList.add(0, arg); + return argList; + } } return null; } catch (Exception e) { @@ -108,6 +112,8 @@ private boolean processArgEntry(String arg, CommandLineOption option, Method met } else if (option.short_form().isBlank() && option.long_form().isBlank()) { throw new IllegalArgumentException( "Neither long nor short form not defined for " + method.getName()); + } else if (!arg.startsWith("-")) { + return true; } return false; } catch (Exception e) { From e484c2dd2df5a0e817707ddd5cff5d1744fe87e3 Mon Sep 17 00:00:00 2001 From: Trevor Pering Date: Tue, 12 Nov 2024 18:43:00 -0800 Subject: [PATCH 14/18] Fix NO_SITE processing (again) --- common/src/main/java/com/google/udmi/util/SiteModel.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/common/src/main/java/com/google/udmi/util/SiteModel.java b/common/src/main/java/com/google/udmi/util/SiteModel.java index 66bc44962..5698b199b 100644 --- a/common/src/main/java/com/google/udmi/util/SiteModel.java +++ b/common/src/main/java/com/google/udmi/util/SiteModel.java @@ -173,7 +173,7 @@ private static Supplier projectSpecSupplier(List argList) { } String nextArg = argList.get(0); if (nextArg.equals(NO_SITE)) { - return NO_SITE; + return argList.remove(0); } else if (nextArg.startsWith("-")) { return null; } From ecc6ee58219e9d92add5325d8867b3ef2e54d60b Mon Sep 17 00:00:00 2001 From: Trevor Pering Date: Tue, 12 Nov 2024 19:08:42 -0800 Subject: [PATCH 15/18] Add argument descriptions --- .../google/daq/mqtt/registrar/Registrar.java | 34 +++++++++---------- .../daq/mqtt/validator/CommandLineOption.java | 10 ++++++ .../mqtt/validator/CommandLineProcessor.java | 22 +++++++++--- 3 files changed, 44 insertions(+), 22 deletions(-) diff --git a/validator/src/main/java/com/google/daq/mqtt/registrar/Registrar.java b/validator/src/main/java/com/google/daq/mqtt/registrar/Registrar.java index f97460ae5..685147b26 100644 --- a/validator/src/main/java/com/google/daq/mqtt/registrar/Registrar.java +++ b/validator/src/main/java/com/google/daq/mqtt/registrar/Registrar.java @@ -19,7 +19,6 @@ import static com.google.udmi.util.GeneralUtils.ifTrueGet; import static com.google.udmi.util.GeneralUtils.ifTrueThen; import static com.google.udmi.util.GeneralUtils.isTrue; -import static com.google.udmi.util.GeneralUtils.removeArg; import static com.google.udmi.util.GeneralUtils.writeString; import static com.google.udmi.util.JsonUtil.JSON_SUFFIX; import static com.google.udmi.util.JsonUtil.OBJECT_MAPPER; @@ -154,7 +153,7 @@ public class Registrar { private SiteModel siteModel; private boolean queryOnly; private final AtomicBoolean helpShown = new AtomicBoolean(); - private CommandLineProcessor commandLineProcessor = new CommandLineProcessor(this); + private final CommandLineProcessor commandLineProcessor = new CommandLineProcessor(this); /** * Main entry point for registrar. @@ -221,17 +220,17 @@ Registrar postProcessArgs(List argList) { return this; } - @CommandLineOption(short_form = "-q") + @CommandLineOption(short_form = "-q", description = "Query only") private void setQueryOnly() { this.queryOnly = true; } - @CommandLineOption(short_form = "-b") + @CommandLineOption(short_form = "-b", description = "Block unknown devices") private void setBlockUnknown() { blockUnknown = true; } - @CommandLineOption(short_form = "-c") + @CommandLineOption(short_form = "-c", description = "Create registries") private void setCreateRegistries(String registrySpec) { try { createRegistries = Integer.parseInt(registrySpec); @@ -243,26 +242,27 @@ private void setCreateRegistries(String registrySpec) { } } - @CommandLineOption(short_form = "-n") + @CommandLineOption(short_form = "-n", arg_type = "n", + description = "Set number of runner threads") private void setRunnerThreads(String argValue) { runnerThreads = Integer.parseInt(argValue); } - @CommandLineOption(short_form = "-d") + @CommandLineOption(short_form = "-d", description = "Delete (known) devices") private void setDeleteDevices() { checkNotNull(projectId, "delete devices specified with no target project"); this.deleteDevices = true; this.updateCloudIoT = true; } - @CommandLineOption(short_form = "-x") + @CommandLineOption(short_form = "-x", description = "Expunge (unknown) devices") private void setExpungeDevices() { checkNotNull(projectId, "expunge devices specified with no target project"); this.expungeDevices = true; this.updateCloudIoT = true; } - @CommandLineOption(short_form = "-e") + @CommandLineOption(short_form = "-e", arg_type = "s", description = "Set registry suffix") private void setRegistrySuffix(String suffix) { siteModel.getExecutionConfiguration().registry_suffix = suffix; } @@ -340,18 +340,18 @@ private void createRegistrySuffix(String suffix) { System.err.println("Created registry " + registry); } - @CommandLineOption(short_form = "-l") + @CommandLineOption(short_form = "-l", arg_type = "t", description = "Set idle limit") private void setIdleLimit(String option) { idleLimit = Duration.parse("PT" + option); System.err.println("Limiting devices to duration " + idleLimit.toSeconds() + "s"); } - @CommandLineOption(short_form = "-u") + @CommandLineOption(short_form = "-u", description = "Update cloud") private void setUpdateFlag() { updateCloudIoT = true; } - @CommandLineOption(short_form = "-t") + @CommandLineOption(short_form = "-t", description = "Do not validate metadata") private void setValidateMetadata() { this.doValidate = false; } @@ -361,7 +361,7 @@ private void setDeviceList(List deviceList) { blockUnknown = false; } - @CommandLineOption(short_form = "-f") + @CommandLineOption(short_form = "-f", arg_type = "s", description = "Set PubSub feed topic") private void setFeedTopic(String feedTopic) { System.err.println("Sending device feed to topic " + feedTopic); feedPusher = new PubSubPusher(projectId, feedTopic); @@ -429,7 +429,7 @@ private SetupUdmiConfig getCloudVersionInfo() { return ifNotNullGet(cloudIotManager, CloudIotManager::getVersionInformation); } - @CommandLineOption(short_form = "-s") + @CommandLineOption(short_form = "-s", arg_type = "s", description = "Set site path") private void setSitePath(String sitePath) { checkNotNull(SCHEMA_NAME, "schemaName not set yet"); siteDir = new File(sitePath); @@ -1157,7 +1157,7 @@ private void initializeDevices(Map localDevices) { }); } - @CommandLineOption(short_form = "-p") + @CommandLineOption(short_form = "-p", arg_type = "s", description = "Set project id") private void setProjectId(String projectId) { if (NO_SITE.equals(projectId) || projectId == null) { this.projectId = null; @@ -1169,7 +1169,7 @@ private void setProjectId(String projectId) { this.projectId = projectId; } - @CommandLineOption(short_form = "-r") + @CommandLineOption(short_form = "-r", arg_type = "s", description = "Set tool root") private void setToolRoot(String toolRoot) { try { schemaBase = new File(toolRoot, SCHEMA_BASE_PATH); @@ -1250,7 +1250,7 @@ public List getMockActions() { return cloudIotManager.getMockActions(); } - @CommandLineOption(short_form = "-a") + @CommandLineOption(short_form = "-a", arg_type = "s", description = "Set alternate registry") private void setTargetRegistry(String altRegistry) { siteModel.getExecutionConfiguration().registry_id = altRegistry; siteModel.getExecutionConfiguration().alt_registry = null; diff --git a/validator/src/main/java/com/google/daq/mqtt/validator/CommandLineOption.java b/validator/src/main/java/com/google/daq/mqtt/validator/CommandLineOption.java index bc66af035..360cba660 100644 --- a/validator/src/main/java/com/google/daq/mqtt/validator/CommandLineOption.java +++ b/validator/src/main/java/com/google/daq/mqtt/validator/CommandLineOption.java @@ -14,6 +14,11 @@ String NO_VALUE = ""; + /** + * Set the text help description. + */ + String description(); + /** * Short form argument. */ @@ -23,4 +28,9 @@ * Long form argument. */ String long_form() default NO_VALUE; + + /** + * Set the argument type description. + */ + String arg_type() default NO_VALUE; } diff --git a/validator/src/main/java/com/google/daq/mqtt/validator/CommandLineProcessor.java b/validator/src/main/java/com/google/daq/mqtt/validator/CommandLineProcessor.java index 1925f6855..71a8625f8 100644 --- a/validator/src/main/java/com/google/daq/mqtt/validator/CommandLineProcessor.java +++ b/validator/src/main/java/com/google/daq/mqtt/validator/CommandLineProcessor.java @@ -2,15 +2,17 @@ import static com.google.common.base.Preconditions.checkState; import static com.google.udmi.util.GeneralUtils.ifNotNullThen; +import static com.google.udmi.util.GeneralUtils.ifNotNullThrow; +import static java.lang.String.CASE_INSENSITIVE_ORDER; import java.lang.reflect.Method; import java.lang.reflect.Type; import java.util.ArrayList; -import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Map.Entry; import java.util.Optional; +import java.util.TreeMap; /** * Simple command line parser class. @@ -24,7 +26,13 @@ public class CommandLineProcessor { private final Object target; private final Method showHelpMethod; - Map optionMap = new HashMap<>(); + Map optionMap = new TreeMap<>( + (a, b) -> CASE_INSENSITIVE_ORDER.compare(getSortArg(a), getSortArg(b))); + + private static String getSortArg(CommandLineOption k) { + String shortForm = k.short_form(); + return shortForm.isBlank() ? k.long_form().substring(1, 2) : shortForm.substring(1, 2); + } /** * Create a new command line processor for the given target object. @@ -53,16 +61,19 @@ private CommandLineOption getShowHelpOption() { return showHelpMethod.getAnnotation(CommandLineOption.class); } - @CommandLineOption(short_form = "-h") + @CommandLineOption(short_form = "-h", description = "Show help and exit") private void showUsage() { showUsage(null); } + /** + * Show program usage. + */ public void showUsage(String message) { ifNotNullThen(message, m -> System.err.println(m)); System.err.println("Options supported:"); - optionMap.forEach((option, method) -> - System.err.printf(" %s: %s%n", option.short_form(), option.long_form())); + optionMap.forEach((option, method) -> System.err.printf(" %s %1s %s%n", + option.short_form(), option.arg_type(), option.description())); System.exit(message == null ? LINUX_SUCCESS_CODE : LINUX_ERROR_CODE); } @@ -101,6 +112,7 @@ private boolean processArgEntry(String arg, CommandLineOption option, Method met if (method.equals(showHelpMethod)) { showUsage(); } else if (requiresArg(method)) { + checkState(!option.arg_type().isBlank(), "Option with argument missing type parameter"); String parameter = argList.remove(0); method.invoke(target, parameter); } else { From a631363a4cd333df21e1cfeac6fd1bbfa4a41026 Mon Sep 17 00:00:00 2001 From: Trevor Pering Date: Tue, 12 Nov 2024 19:18:13 -0800 Subject: [PATCH 16/18] Remove unnecessary arg --- .../src/main/java/com/google/daq/mqtt/registrar/Registrar.java | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/validator/src/main/java/com/google/daq/mqtt/registrar/Registrar.java b/validator/src/main/java/com/google/daq/mqtt/registrar/Registrar.java index 685147b26..b0c54d922 100644 --- a/validator/src/main/java/com/google/daq/mqtt/registrar/Registrar.java +++ b/validator/src/main/java/com/google/daq/mqtt/registrar/Registrar.java @@ -128,6 +128,7 @@ public class Registrar { private final Map schemas = new HashMap<>(); private final String generation = getGenerationString(); private final Set summarizers = new HashSet<>(); + private final CommandLineProcessor commandLineProcessor = new CommandLineProcessor(this); private CloudIotManager cloudIotManager; private File schemaBase; private PubSubPusher updatePusher; @@ -152,8 +153,6 @@ public class Registrar { private List> executing = new ArrayList<>(); private SiteModel siteModel; private boolean queryOnly; - private final AtomicBoolean helpShown = new AtomicBoolean(); - private final CommandLineProcessor commandLineProcessor = new CommandLineProcessor(this); /** * Main entry point for registrar. From fdf064625a26f8f9c15c76f3dc357f7229c0e59d Mon Sep 17 00:00:00 2001 From: Trevor Pering Date: Tue, 12 Nov 2024 19:19:54 -0800 Subject: [PATCH 17/18] move to common --- .../main/java/com/google/udmi/util}/CommandLineOption.java | 2 +- .../java/com/google/udmi/util}/CommandLineProcessor.java | 2 +- .../main/java/com/google/daq/mqtt/registrar/Registrar.java | 5 ++--- 3 files changed, 4 insertions(+), 5 deletions(-) rename {validator/src/main/java/com/google/daq/mqtt/validator => common/src/main/java/com/google/udmi/util}/CommandLineOption.java (94%) rename {validator/src/main/java/com/google/daq/mqtt/validator => common/src/main/java/com/google/udmi/util}/CommandLineProcessor.java (99%) diff --git a/validator/src/main/java/com/google/daq/mqtt/validator/CommandLineOption.java b/common/src/main/java/com/google/udmi/util/CommandLineOption.java similarity index 94% rename from validator/src/main/java/com/google/daq/mqtt/validator/CommandLineOption.java rename to common/src/main/java/com/google/udmi/util/CommandLineOption.java index 360cba660..4753f3ef5 100644 --- a/validator/src/main/java/com/google/daq/mqtt/validator/CommandLineOption.java +++ b/common/src/main/java/com/google/udmi/util/CommandLineOption.java @@ -1,4 +1,4 @@ -package com.google.daq.mqtt.validator; +package com.google.udmi.util; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; diff --git a/validator/src/main/java/com/google/daq/mqtt/validator/CommandLineProcessor.java b/common/src/main/java/com/google/udmi/util/CommandLineProcessor.java similarity index 99% rename from validator/src/main/java/com/google/daq/mqtt/validator/CommandLineProcessor.java rename to common/src/main/java/com/google/udmi/util/CommandLineProcessor.java index 71a8625f8..287468f6d 100644 --- a/validator/src/main/java/com/google/daq/mqtt/validator/CommandLineProcessor.java +++ b/common/src/main/java/com/google/udmi/util/CommandLineProcessor.java @@ -1,4 +1,4 @@ -package com.google.daq.mqtt.validator; +package com.google.udmi.util; import static com.google.common.base.Preconditions.checkState; import static com.google.udmi.util.GeneralUtils.ifNotNullThen; diff --git a/validator/src/main/java/com/google/daq/mqtt/registrar/Registrar.java b/validator/src/main/java/com/google/daq/mqtt/registrar/Registrar.java index b0c54d922..40c172839 100644 --- a/validator/src/main/java/com/google/daq/mqtt/registrar/Registrar.java +++ b/validator/src/main/java/com/google/daq/mqtt/registrar/Registrar.java @@ -52,8 +52,8 @@ import com.google.daq.mqtt.util.ExceptionMap.ErrorTree; import com.google.daq.mqtt.util.PubSubPusher; import com.google.daq.mqtt.util.ValidationError; -import com.google.daq.mqtt.validator.CommandLineOption; -import com.google.daq.mqtt.validator.CommandLineProcessor; +import com.google.udmi.util.CommandLineOption; +import com.google.udmi.util.CommandLineProcessor; import com.google.udmi.util.Common; import com.google.udmi.util.SiteModel; import java.io.File; @@ -84,7 +84,6 @@ import java.util.concurrent.Executors; import java.util.concurrent.Future; import java.util.concurrent.TimeUnit; -import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicInteger; import java.util.function.Consumer; import java.util.stream.Collectors; From c0d82d7afb32a7b560921358b52ff02b6665a7c1 Mon Sep 17 00:00:00 2001 From: Trevor Pering Date: Wed, 13 Nov 2024 06:53:12 -0800 Subject: [PATCH 18/18] Try alternate shadow package --- validator/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/validator/build.gradle b/validator/build.gradle index 1257f0a19..e1d46d2f6 100644 --- a/validator/build.gradle +++ b/validator/build.gradle @@ -6,7 +6,7 @@ buildscript { } dependencies { - classpath 'com.github.johnrengelman.shadow:com.github.johnrengelman.shadow.gradle.plugin:7.1.2' + classpath 'gradle.plugin.com.github.johnrengelman:shadow:7.1.2' } }