aboutsummaryrefslogtreecommitdiffstats
path: root/utils
diff options
context:
space:
mode:
authoradheli.tavares <adheli.tavares@est.tech>2021-04-09 11:51:05 +0100
committeradheli.tavares <adheli.tavares@est.tech>2021-04-13 13:38:54 +0100
commit1cb818e3cbd6154762f908be8698317fa10fbc49 (patch)
tree3d6b5e5d3d3071d13a29485a63f7c42b8b6edd6e /utils
parent04623fef95f2b164d79ad48de2ef3708d02eda80 (diff)
Add command line handler
Added a Handler for command line classes to share common strucutures. Issue-ID: POLICY-3128 Change-Id: I662911c467faf5c39b8db018bb1a564fba7587a6 Signed-off-by: adheli.tavares <adheli.tavares@est.tech>
Diffstat (limited to 'utils')
-rw-r--r--utils/pom.xml5
-rw-r--r--utils/src/main/java/org/onap/policy/common/utils/cmd/CommandLineArgumentsHandler.java273
-rw-r--r--utils/src/main/java/org/onap/policy/common/utils/cmd/CommandLineException.java54
-rw-r--r--utils/src/test/java/org/onap/policy/common/utils/cmd/TestCommandLineArguments.java181
-rw-r--r--utils/src/test/java/org/onap/policy/common/utils/resources/ResourceUtilsTest.java4
-rw-r--r--utils/src/test/resources/cmdFiles/configuration.json4
-rw-r--r--utils/src/test/resources/cmdFiles/property.json3
-rw-r--r--utils/src/test/resources/version.txt1
8 files changed, 523 insertions, 2 deletions
diff --git a/utils/pom.xml b/utils/pom.xml
index bd9df71a..75173a2a 100644
--- a/utils/pom.xml
+++ b/utils/pom.xml
@@ -3,6 +3,7 @@
ONAP Policy Engine - Common Modules
================================================================================
Copyright (C) 2018-2021 AT&T Intellectual Property. All rights reserved.
+ Modifications Copyright (C) 2021 Nordix Foundation.
================================================================================
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
@@ -117,5 +118,9 @@
<artifactId>assertj-core</artifactId>
<scope>test</scope>
</dependency>
+ <dependency>
+ <groupId>commons-cli</groupId>
+ <artifactId>commons-cli</artifactId>
+ </dependency>
</dependencies>
</project>
diff --git a/utils/src/main/java/org/onap/policy/common/utils/cmd/CommandLineArgumentsHandler.java b/utils/src/main/java/org/onap/policy/common/utils/cmd/CommandLineArgumentsHandler.java
new file mode 100644
index 00000000..37a9047b
--- /dev/null
+++ b/utils/src/main/java/org/onap/policy/common/utils/cmd/CommandLineArgumentsHandler.java
@@ -0,0 +1,273 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * Copyright (C) 2021 Nordix Foundation.
+ * ================================================================================
+ * Licensed 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.
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ * ============LICENSE_END=========================================================
+ */
+
+package org.onap.policy.common.utils.cmd;
+
+import java.io.PrintWriter;
+import java.io.StringWriter;
+import java.net.URISyntaxException;
+import java.net.URL;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.util.Arrays;
+import lombok.Getter;
+import lombok.Setter;
+import org.apache.commons.cli.CommandLine;
+import org.apache.commons.cli.DefaultParser;
+import org.apache.commons.cli.HelpFormatter;
+import org.apache.commons.cli.Option;
+import org.apache.commons.cli.Options;
+import org.apache.commons.cli.ParseException;
+import org.apache.commons.lang3.StringUtils;
+import org.onap.policy.common.utils.resources.ResourceUtils;
+
+/**
+ * Class for command line common processing.
+ *
+ * @author Adheli Tavares (adheli.tavares@est.tech)
+ *
+ */
+public class CommandLineArgumentsHandler {
+ private static final String FILE_MESSAGE_PREAMBLE = " file \"";
+ private static final int HELP_LINE_LENGTH = 120;
+
+ private final Options options;
+
+ private final String helpClassName;
+ private final String component;
+
+ @Getter
+ @Setter
+ private String configurationFilePath = null;
+
+ @Getter
+ @Setter
+ private String propertyFilePath = null;
+
+ @Getter
+ private CommandLine commandLine = null;
+
+ /**
+ * Construct the options with default values for the CLI editor.
+ */
+ protected CommandLineArgumentsHandler(String helpClassName, String component) {
+ this.helpClassName = helpClassName;
+ this.component = component;
+ //@formatter:off
+ options = new Options();
+ options.addOption(Option.builder("h")
+ .longOpt("help")
+ .desc("outputs the usage of this command")
+ .required(false)
+ .type(Boolean.class)
+ .build());
+ options.addOption(Option.builder("v")
+ .longOpt("version")
+ .desc("outputs the version of " + this.component)
+ .required(false)
+ .type(Boolean.class)
+ .build());
+ options.addOption(Option.builder("c")
+ .longOpt("config-file")
+ .desc(String.format("the full path to the configuration file to use, "
+ + "the configuration file must be a Json file containing the %s parameters", this.component))
+ .hasArg().argName("CONFIG_FILE")
+ .required(false)
+ .type(String.class)
+ .build());
+ //@formatter:on
+ }
+
+ /**
+ * Construct the options for the CLI editor with extra options.
+ */
+ public CommandLineArgumentsHandler(String helpClassName, String component, Option... customOptions) {
+ this(helpClassName, component);
+ if (customOptions != null && customOptions.length > 0) {
+ for (Option option : customOptions) {
+ options.addOption(option);
+ }
+ }
+ }
+
+ /**
+ * Construct the options with brand new options for the CLI editor.
+ */
+ public CommandLineArgumentsHandler(String helpClassName, String component, Options options) {
+ this.options = options;
+ this.helpClassName = helpClassName;
+ this.component = component;
+ }
+
+ /**
+ * Parse the command line options.
+ *
+ * @param args The command line arguments
+ * @return a string with a message for help and version, or null if there is no message
+ * @throws CommandLineException on command argument errors
+ */
+ public String parse(final String[] args) throws CommandLineException {
+ // Clear all our arguments
+ setConfigurationFilePath(null);
+ setPropertyFilePath(null);
+
+ try {
+ commandLine = new DefaultParser().parse(options, args);
+ } catch (final ParseException | NullPointerException e) {
+ throw new CommandLineException("invalid command line arguments specified", e);
+ }
+
+ // Arguments left over after Commons CLI does its stuff
+ final String[] remainingArgs = removeEmptyValues(commandLine.getArgs());
+
+ if (remainingArgs.length > 0) {
+ throw new CommandLineException("too many command line arguments specified: " + Arrays.toString(args));
+ }
+
+ if (commandLine.hasOption('h')) {
+ return help();
+ }
+
+ if (commandLine.hasOption('v')) {
+ return version();
+ }
+
+ if (commandLine.hasOption('c')) {
+ setConfigurationFilePath(commandLine.getOptionValue('c'));
+ }
+
+ if (commandLine.hasOption('p')) {
+ setPropertyFilePath(commandLine.getOptionValue('p'));
+ }
+
+ return null;
+ }
+
+ /**
+ * Validate the command line options.
+ *
+ * @throws CommandLineException on command argument validation errors
+ */
+ public void validate() throws CommandLineException {
+ validateReadableFile(this.component + " configuration", configurationFilePath);
+ }
+
+ /**
+ * Print version information for policy distribution.
+ *
+ * @return the version string
+ */
+ public String version() {
+ return ResourceUtils.getResourceAsString("version.txt");
+ }
+
+ /**
+ * Print help information for policy distribution.
+ *
+ * @return the help string
+ */
+ public String help() {
+ final HelpFormatter helpFormatter = new HelpFormatter();
+ final StringWriter stringWriter = new StringWriter();
+ final PrintWriter printWriter = new PrintWriter(stringWriter);
+ final String cmdLineSyntax = this.helpClassName + " [options...]";
+
+ helpFormatter.printHelp(printWriter, HELP_LINE_LENGTH, cmdLineSyntax, "options", options, 0, 0, "");
+
+ return stringWriter.toString();
+ }
+
+ /**
+ * Gets the full expanded configuration file path.
+ *
+ * @return the configuration file path
+ */
+ public String getFullConfigurationFilePath() {
+ return ResourceUtils.getFilePath4Resource(getConfigurationFilePath());
+ }
+
+ /**
+ * Check set configuration file path.
+ *
+ * @return true, if check set configuration file path
+ */
+ public boolean checkSetConfigurationFilePath() {
+ return StringUtils.isNotBlank(getConfigurationFilePath());
+ }
+
+ /**
+ * Gets the full expanded property file path.
+ *
+ * @return the property file path
+ */
+ public String getFullPropertyFilePath() {
+ return ResourceUtils.getFilePath4Resource(getPropertyFilePath());
+ }
+
+ /**
+ * Check set property file path.
+ *
+ * @return true, if check set property file path
+ */
+ public boolean checkSetPropertyFilePath() {
+ return StringUtils.isNotBlank(getPropertyFilePath());
+ }
+
+ /**
+ * Validate readable file.
+ *
+ * @param fileTag the file tag
+ * @param fileName the file name
+ * @throws CommandLineException on the file name passed as a parameter
+ */
+ protected void validateReadableFile(final String fileTag, final String fileName) throws CommandLineException {
+ if (StringUtils.isBlank(fileName)) {
+ throw new CommandLineException(fileTag + " file was not specified as an argument");
+ }
+
+ // The file name refers to a resource on the local file system
+ final URL fileUrl = ResourceUtils.getUrl4Resource(fileName);
+ if (fileUrl == null) {
+ throw new CommandLineException(fileTag + FILE_MESSAGE_PREAMBLE + fileName + "\" does not exist");
+ }
+
+ try {
+ Path path = Path.of(fileUrl.toURI());
+ if (!Files.isRegularFile(path)) {
+ throw new CommandLineException(fileTag + FILE_MESSAGE_PREAMBLE + fileName + "\" is not a normal file");
+ }
+ if (!Files.isReadable(path)) {
+ throw new CommandLineException(fileTag + FILE_MESSAGE_PREAMBLE + fileName + "\" is unreadable");
+ }
+ } catch (URISyntaxException e) {
+ throw new CommandLineException("Error parsing " + fileUrl.toString(), e);
+ }
+
+ }
+
+ /**
+ * Checks if args has any null or empty value after parsing.
+ *
+ * @param args remaining args from CLI parse.
+ */
+ private String[] removeEmptyValues(String[] args) {
+ return Arrays.stream(args).filter(StringUtils::isNotBlank).toArray(String[]::new);
+ }
+}
diff --git a/utils/src/main/java/org/onap/policy/common/utils/cmd/CommandLineException.java b/utils/src/main/java/org/onap/policy/common/utils/cmd/CommandLineException.java
new file mode 100644
index 00000000..95870f75
--- /dev/null
+++ b/utils/src/main/java/org/onap/policy/common/utils/cmd/CommandLineException.java
@@ -0,0 +1,54 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * Copyright (C) 2021 Nordix Foundation.
+ * ================================================================================
+ * Licensed 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.
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ * ============LICENSE_END=========================================================
+ */
+
+package org.onap.policy.common.utils.cmd;
+
+/**
+ * Exception used for CommandLineArguments class.
+ *
+ * @author Adheli Tavares (adheli.tavares@est.tech)
+ *
+ */
+public class CommandLineException extends Exception {
+
+ /**
+ * Generated serialVersionUID.
+ */
+ private static final long serialVersionUID = -1200607308084606425L;
+
+ /**
+ * Instantiates a new exception with a message.
+ *
+ * @param message the message
+ */
+ public CommandLineException(final String message) {
+ super(message);
+ }
+
+ /**
+ * Instantiates a new exception with a message and a caused by exception.
+ *
+ * @param message the message
+ * @param exp the exception that caused this exception to be thrown
+ */
+ public CommandLineException(final String message, final Exception exp) {
+ super(message, exp);
+ }
+}
diff --git a/utils/src/test/java/org/onap/policy/common/utils/cmd/TestCommandLineArguments.java b/utils/src/test/java/org/onap/policy/common/utils/cmd/TestCommandLineArguments.java
new file mode 100644
index 00000000..1501e769
--- /dev/null
+++ b/utils/src/test/java/org/onap/policy/common/utils/cmd/TestCommandLineArguments.java
@@ -0,0 +1,181 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * Copyright (C) 2021 Nordix Foundation.
+ * ================================================================================
+ * Licensed 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.
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ * ============LICENSE_END=========================================================
+ */
+
+package org.onap.policy.common.utils.cmd;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.assertj.core.api.Assertions.assertThatCode;
+import static org.assertj.core.api.Assertions.assertThatThrownBy;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+
+import org.apache.commons.cli.Option;
+import org.apache.commons.cli.Options;
+import org.junit.Test;
+
+public class TestCommandLineArguments {
+ private static final String FAKE_HELP_CLASS = "org.onap.policy.HelpClass";
+ private static final String FAKE_COMPONENT = "fake policy cpm";
+ private static final String TEST_CONFIG_FILE = "cmdFiles/configuration.json";
+ private static final String TEST_PROPERTY_FILE = "cmdFiles/property.json";
+ private static final String ERR_MSG_INVALID_ARGS = "invalid command line arguments specified";
+ private static final String ERR_MSG_POLICY_CONFIG_FILE =
+ "fake policy cpm configuration file was not specified as an argument";
+
+ CommandLineArgumentsHandler testCmd = new CommandLineArgumentsHandler(FAKE_HELP_CLASS, FAKE_COMPONENT);
+
+ @Test
+ public void testVersion() throws CommandLineException {
+ String[] version = {"-v"};
+ assertThat(testCmd.parse(version)).startsWith("ONAP Version test.");
+ }
+
+ @Test
+ public void testHelp() throws CommandLineException {
+ String[] help = {"-h"};
+ assertThat(testCmd.parse(help)).startsWith("usage: org.onap.policy.HelpClass [options...]");
+ }
+
+ @Test
+ public void testParse() throws CommandLineException {
+ String[] args = {"-c", TEST_CONFIG_FILE};
+ testCmd.parse(args);
+
+ assertTrue(testCmd.checkSetConfigurationFilePath());
+ assertThat(testCmd.getFullConfigurationFilePath()).contains(TEST_CONFIG_FILE);
+ }
+
+ @Test
+ public void testParse_ShouldThrowExceptionWithInvalidArguments() {
+ String[] invalidArgs = {"-a"};
+ assertThatThrownBy(() -> testCmd.parse(invalidArgs)).hasMessage(ERR_MSG_INVALID_ARGS)
+ .hasRootCauseMessage("Unrecognized option: -a");
+ }
+
+ @Test
+ public void testParse_ShouldThrowExceptionWithExtraArguments() {
+ String[] remainingArgs = {"-c", TEST_CONFIG_FILE, "extraArgs"};
+ String expectedErrorMsg =
+ "too many command line arguments specified: [-c, cmdFiles/configuration.json, extraArgs]";
+ assertThatThrownBy(() -> testCmd.parse(remainingArgs)).hasMessage(expectedErrorMsg);
+ }
+
+ @Test
+ public void testParse_ShouldThrowExceptionWhenFileNameNull() {
+ String[] nullArgs = {"-c", null};
+ assertThatThrownBy(() -> testCmd.parse(nullArgs)).hasMessage(ERR_MSG_INVALID_ARGS).hasRootCauseMessage(null);
+ }
+
+ @Test
+ public void testValidate() throws CommandLineException {
+ String[] validConfigArgs = {"-c", TEST_CONFIG_FILE};
+ testCmd.parse(validConfigArgs);
+ assertThatCode(() -> testCmd.validate()).doesNotThrowAnyException();
+ }
+
+ @Test
+ public void testValidate_ShouldThrowExceptionWhenConfigFileNotPresent() throws CommandLineException {
+ String[] versionArgs = {"-v"};
+ testCmd.parse(versionArgs);
+ assertValidate(versionArgs, ERR_MSG_POLICY_CONFIG_FILE);
+ }
+
+ @Test
+ public void testValidate_ShouldThrowExceptionWhenFileNameEmpty() {
+ String[] argsOnlyKeyNoValue = {"-c", ""};
+ assertValidate(argsOnlyKeyNoValue, ERR_MSG_POLICY_CONFIG_FILE);
+ assertFalse(testCmd.checkSetConfigurationFilePath());
+ }
+
+ @Test
+ public void testValidate_ShouldThrowExceptionWhenFileNameEmptySpace() {
+ String[] argsOnlyKeyNoValue = {"-c", " "};
+ assertValidate(argsOnlyKeyNoValue, ERR_MSG_POLICY_CONFIG_FILE);
+ assertFalse(testCmd.checkSetConfigurationFilePath());
+ }
+
+ @Test
+ public void testValidate_ShouldThrowExceptionWhenFileNameDoesNotExist() {
+ String[] fileNameNotExistentArgs = {"-c", "someFileName.json"};
+ assertValidate(fileNameNotExistentArgs,
+ "fake policy cpm configuration file \"someFileName.json\" does not exist");
+ }
+
+ @Test
+ public void testValidate_ShouldThrowExceptionWhenFileNameIsNotFile() {
+ String[] folderAsFileNameArgs = {"-c", "src/test/resources"};
+ assertValidate(folderAsFileNameArgs,
+ "fake policy cpm configuration file \"src/test/resources\" is not a normal file");
+ }
+
+ @Test
+ public void testAddExtraOptions() throws CommandLineException {
+ Option extra = Option.builder("p").longOpt("property-file")
+ .desc("the full path to the topic property file to use, the property file contains the "
+ + FAKE_COMPONENT + " properties")
+ .hasArg().argName("PROP_FILE").required(false).type(String.class).build();
+
+ CommandLineArgumentsHandler testCmdExtraOpt =
+ new CommandLineArgumentsHandler(FAKE_HELP_CLASS, FAKE_COMPONENT, extra);
+
+ String[] args = {"-p", TEST_PROPERTY_FILE};
+ testCmdExtraOpt.parse(args);
+
+ assertTrue(testCmdExtraOpt.checkSetPropertyFilePath());
+ assertThat(testCmdExtraOpt.getFullPropertyFilePath()).contains(TEST_PROPERTY_FILE);
+
+ String[] argsNoProperty = {"-p", ""};
+ testCmdExtraOpt.parse(argsNoProperty);
+
+ assertFalse(testCmdExtraOpt.checkSetPropertyFilePath());
+ }
+
+ @Test
+ public void testNewOptions() throws CommandLineException {
+ Options newOptions = new Options();
+ newOptions.addOption(
+ Option.builder("a").longOpt("fake-option").desc("the fake property to check command line parse")
+ .hasArg().argName("FAKE_OPT").required(false).type(String.class).build());
+
+ CommandLineArgumentsHandler testCmdExtraOpt =
+ new CommandLineArgumentsHandler(FAKE_HELP_CLASS, FAKE_COMPONENT, newOptions);
+
+ String[] args = {"-a", "aaaa"};
+ testCmdExtraOpt.parse(args);
+
+ assertTrue(testCmdExtraOpt.getCommandLine().hasOption("a"));
+
+ // should raise exception as -c is not present on options;
+ // default options should've been replaced by constructor parameter.
+ String[] argsError = {"-c", "aaaa.json"};
+ assertThatThrownBy(() -> testCmdExtraOpt.parse(argsError)).hasMessage(ERR_MSG_INVALID_ARGS)
+ .hasRootCauseMessage("Unrecognized option: -c");
+ }
+
+ private void assertValidate(String[] testArgs, String expectedErrorMsg) {
+ try {
+ testCmd.parse(testArgs);
+ } catch (CommandLineException e) {
+ fail(e.getMessage());
+ }
+ assertThatThrownBy(() -> testCmd.validate()).hasMessage(expectedErrorMsg);
+ }
+}
diff --git a/utils/src/test/java/org/onap/policy/common/utils/resources/ResourceUtilsTest.java b/utils/src/test/java/org/onap/policy/common/utils/resources/ResourceUtilsTest.java
index 2e122187..6a2fe1ad 100644
--- a/utils/src/test/java/org/onap/policy/common/utils/resources/ResourceUtilsTest.java
+++ b/utils/src/test/java/org/onap/policy/common/utils/resources/ResourceUtilsTest.java
@@ -2,7 +2,7 @@
* ============LICENSE_START=======================================================
* Copyright (C) 2018 Ericsson. All rights reserved.
* Modifications Copyright (C) 2019-2020 AT&T Intellectual Property. All rights reserved.
- * Modifications Copyright (C) 2020 Nordix Foundation.
+ * Modifications Copyright (C) 2020-2021 Nordix Foundation.
* ================================================================================
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -264,7 +264,7 @@ public class ResourceUtilsTest {
theString = ResourceUtils.getResourceAsString("");
- assertEquals("logback-test.xml\nMETA-INF\norg\ntestdir\n", theString);
+ assertEquals("cmdFiles\nlogback-test.xml\nMETA-INF\norg\ntestdir\nversion.txt\n", theString);
}
diff --git a/utils/src/test/resources/cmdFiles/configuration.json b/utils/src/test/resources/cmdFiles/configuration.json
new file mode 100644
index 00000000..64cd100c
--- /dev/null
+++ b/utils/src/test/resources/cmdFiles/configuration.json
@@ -0,0 +1,4 @@
+{
+ "propertyName" : "test",
+ "propertyType" : "string"
+}
diff --git a/utils/src/test/resources/cmdFiles/property.json b/utils/src/test/resources/cmdFiles/property.json
new file mode 100644
index 00000000..be63ece4
--- /dev/null
+++ b/utils/src/test/resources/cmdFiles/property.json
@@ -0,0 +1,3 @@
+{
+ "configName" : "test"
+}
diff --git a/utils/src/test/resources/version.txt b/utils/src/test/resources/version.txt
new file mode 100644
index 00000000..9970c7b4
--- /dev/null
+++ b/utils/src/test/resources/version.txt
@@ -0,0 +1 @@
+ONAP Version test.