diff options
17 files changed, 1200 insertions, 4 deletions
diff --git a/tosca-controlloop/runtime/pom.xml b/tosca-controlloop/runtime/pom.xml index 93498f5e1..e3811bbe3 100644 --- a/tosca-controlloop/runtime/pom.xml +++ b/tosca-controlloop/runtime/pom.xml @@ -34,10 +34,10 @@ <name>${project.artifactId}</name> <dependencies> - <dependency> - <groupId>com.h2database</groupId> - <artifactId>h2</artifactId> - <scope>test</scope> + <dependency> + <groupId>org.onap.policy.clamp.controlloop</groupId> + <artifactId>controlloop-common</artifactId> + <version>${project.version}</version> </dependency> </dependencies> </project> diff --git a/tosca-controlloop/runtime/src/main/java/org/onap/policy/clamp/controlloop/runtime/main/parameters/ClRuntimeParameterGroup.java b/tosca-controlloop/runtime/src/main/java/org/onap/policy/clamp/controlloop/runtime/main/parameters/ClRuntimeParameterGroup.java new file mode 100644 index 000000000..4c99b8e57 --- /dev/null +++ b/tosca-controlloop/runtime/src/main/java/org/onap/policy/clamp/controlloop/runtime/main/parameters/ClRuntimeParameterGroup.java @@ -0,0 +1,55 @@ +/*- + * ============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.clamp.controlloop.runtime.main.parameters; + +import java.util.List; +import javax.validation.constraints.NotBlank; +import lombok.Getter; +import org.onap.policy.common.endpoints.event.comm.bus.internal.BusTopicParams; +import org.onap.policy.common.endpoints.parameters.RestServerParameters; +import org.onap.policy.common.endpoints.parameters.TopicParameterGroup; +import org.onap.policy.common.parameters.ParameterGroupImpl; +import org.onap.policy.common.parameters.annotations.NotNull; +import org.onap.policy.models.provider.PolicyModelsProviderParameters; + +/** + * Class to hold all parameters needed for the Control Loop runtime component. + * + */ +@NotNull +@NotBlank +@Getter +public class ClRuntimeParameterGroup extends ParameterGroupImpl { + private RestServerParameters restServerParameters; + private PolicyModelsProviderParameters databaseProviderParameters; + private ParticipantParameters participantParameters; + private TopicParameterGroup topicParameterGroup; + private List<BusTopicParams> healthCheckRestClientParameters; + + /** + * Create the Control Loop parameter group. + * + * @param name the parameter group name + */ + public ClRuntimeParameterGroup(final String name) { + super(name); + } +} diff --git a/tosca-controlloop/runtime/src/main/java/org/onap/policy/clamp/controlloop/runtime/main/parameters/ClRuntimeParameterHandler.java b/tosca-controlloop/runtime/src/main/java/org/onap/policy/clamp/controlloop/runtime/main/parameters/ClRuntimeParameterHandler.java new file mode 100644 index 000000000..a7f5ff34d --- /dev/null +++ b/tosca-controlloop/runtime/src/main/java/org/onap/policy/clamp/controlloop/runtime/main/parameters/ClRuntimeParameterHandler.java @@ -0,0 +1,77 @@ +/*- + * ============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.clamp.controlloop.runtime.main.parameters; + +import java.io.File; +import javax.ws.rs.core.Response; +import org.onap.policy.clamp.controlloop.common.exception.ControlLoopException; +import org.onap.policy.clamp.controlloop.runtime.main.startstop.ClRuntimeCommandLineArguments; +import org.onap.policy.common.parameters.GroupValidationResult; +import org.onap.policy.common.utils.coder.Coder; +import org.onap.policy.common.utils.coder.CoderException; +import org.onap.policy.common.utils.coder.StandardCoder; + +/** + * This class handles reading, parsing and validating of control loop runtime parameters from JSON files. + */ +public class ClRuntimeParameterHandler { + + private static final Coder CODER = new StandardCoder(); + + /** + * Read the parameters from the parameter file. + * + * @param arguments the arguments passed to control loop runtime + * @return the parameters read from the configuration file + * @throws ControlLoopException on parameter exceptions + */ + public ClRuntimeParameterGroup getParameters(final ClRuntimeCommandLineArguments arguments) + throws ControlLoopException { + ClRuntimeParameterGroup clRuntimeParameterGroup = null; + + // Read the parameters + try { + // Read the parameters from JSON + File file = new File(arguments.getFullConfigurationFilePath()); + clRuntimeParameterGroup = CODER.decode(file, ClRuntimeParameterGroup.class); + } catch (final CoderException e) { + throw new ControlLoopException( + Response.Status.NOT_ACCEPTABLE, "error reading parameters from \"" + + arguments.getConfigurationFilePath() + "\"\n" + "(" + e.getClass().getSimpleName() + ")", + e); + } + + // The JSON processing returns null if there is an empty file + if (clRuntimeParameterGroup == null) { + throw new ControlLoopException(Response.Status.NOT_ACCEPTABLE, + "no parameters found in \"" + arguments.getConfigurationFilePath() + "\""); + } + + // validate the parameters + final GroupValidationResult validationResult = clRuntimeParameterGroup.validate(); + if (!validationResult.isValid()) { + throw new ControlLoopException(Response.Status.NOT_ACCEPTABLE, "validation error(s) on parameters from \"" + + arguments.getConfigurationFilePath() + "\"\n" + validationResult.getResult()); + } + + return clRuntimeParameterGroup; + } +} diff --git a/tosca-controlloop/runtime/src/main/java/org/onap/policy/clamp/controlloop/runtime/main/parameters/ParticipantParameters.java b/tosca-controlloop/runtime/src/main/java/org/onap/policy/clamp/controlloop/runtime/main/parameters/ParticipantParameters.java new file mode 100644 index 000000000..dfc1b2806 --- /dev/null +++ b/tosca-controlloop/runtime/src/main/java/org/onap/policy/clamp/controlloop/runtime/main/parameters/ParticipantParameters.java @@ -0,0 +1,59 @@ +/*- + * ============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. + * ============LICENSE_END========================================================= + */ + +package org.onap.policy.clamp.controlloop.runtime.main.parameters; + +import java.util.concurrent.TimeUnit; +import lombok.Getter; +import org.onap.policy.common.parameters.ParameterGroupImpl; +import org.onap.policy.common.parameters.annotations.Min; +import org.onap.policy.common.parameters.annotations.NotBlank; +import org.onap.policy.common.parameters.annotations.NotNull; + +/** + * Parameters for communicating with participants. + */ +@NotNull +@NotBlank +@Getter +public class ParticipantParameters extends ParameterGroupImpl { + + /** + * Default maximum message age, in milliseconds, that should be examined. Any message + * older than this is discarded. + */ + public static final long DEFAULT_MAX_AGE_MS = TimeUnit.MILLISECONDS.convert(10, TimeUnit.MINUTES); + + + @Min(1) + private long heartBeatMs; + + @Min(1) + private long maxMessageAgeMs = DEFAULT_MAX_AGE_MS; + + private ParticipantUpdateParameters updateParameters; + private ParticipantStateChangeParameters stateChangeParameters; + + + /** + * Constructs the object. + */ + public ParticipantParameters() { + super(ParticipantParameters.class.getSimpleName()); + } +} diff --git a/tosca-controlloop/runtime/src/main/java/org/onap/policy/clamp/controlloop/runtime/main/parameters/ParticipantStateChangeParameters.java b/tosca-controlloop/runtime/src/main/java/org/onap/policy/clamp/controlloop/runtime/main/parameters/ParticipantStateChangeParameters.java new file mode 100644 index 000000000..2eea4ab51 --- /dev/null +++ b/tosca-controlloop/runtime/src/main/java/org/onap/policy/clamp/controlloop/runtime/main/parameters/ParticipantStateChangeParameters.java @@ -0,0 +1,53 @@ +/* + * ============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. + * ============LICENSE_END========================================================= + */ + +package org.onap.policy.clamp.controlloop.runtime.main.parameters; + +import lombok.Getter; +import org.onap.policy.common.parameters.ParameterGroupImpl; +import org.onap.policy.common.parameters.annotations.Min; +import org.onap.policy.common.parameters.annotations.NotBlank; +import org.onap.policy.common.parameters.annotations.NotNull; + +/** + * Parameters for Participant STATE-CHANGE requests. + */ +@NotNull +@NotBlank +@Getter +public class ParticipantStateChangeParameters extends ParameterGroupImpl { + + /** + * Maximum number of times to re-send a request to a PDP. + */ + @Min(value = 0) + private int maxRetryCount; + + /** + * Maximum time to wait, in milliseconds, for a PDP response. + */ + @Min(value = 0) + private long maxWaitMs; + + /** + * Constructs the object. + */ + public ParticipantStateChangeParameters() { + super(ParticipantStateChangeParameters.class.getSimpleName()); + } +} diff --git a/tosca-controlloop/runtime/src/main/java/org/onap/policy/clamp/controlloop/runtime/main/parameters/ParticipantUpdateParameters.java b/tosca-controlloop/runtime/src/main/java/org/onap/policy/clamp/controlloop/runtime/main/parameters/ParticipantUpdateParameters.java new file mode 100644 index 000000000..f70e7d590 --- /dev/null +++ b/tosca-controlloop/runtime/src/main/java/org/onap/policy/clamp/controlloop/runtime/main/parameters/ParticipantUpdateParameters.java @@ -0,0 +1,53 @@ +/* + * ============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. + * ============LICENSE_END========================================================= + */ + +package org.onap.policy.clamp.controlloop.runtime.main.parameters; + +import lombok.Getter; +import org.onap.policy.common.parameters.ParameterGroupImpl; +import org.onap.policy.common.parameters.annotations.Min; +import org.onap.policy.common.parameters.annotations.NotBlank; +import org.onap.policy.common.parameters.annotations.NotNull; + +/** + * Parameters for Participant UPDATE requests. + */ +@NotNull +@NotBlank +@Getter +public class ParticipantUpdateParameters extends ParameterGroupImpl { + + /** + * Maximum number of times to re-send a request to a PDP. + */ + @Min(value = 0) + private int maxRetryCount; + + /** + * Maximum time to wait, in milliseconds, for a PDP response. + */ + @Min(value = 0) + private long maxWaitMs; + + /** + * Constructs the object. + */ + public ParticipantUpdateParameters() { + super(ParticipantUpdateParameters.class.getSimpleName()); + } +} diff --git a/tosca-controlloop/runtime/src/main/java/org/onap/policy/clamp/controlloop/runtime/main/startstop/ClRuntimeActivator.java b/tosca-controlloop/runtime/src/main/java/org/onap/policy/clamp/controlloop/runtime/main/startstop/ClRuntimeActivator.java new file mode 100644 index 000000000..8ac4d684d --- /dev/null +++ b/tosca-controlloop/runtime/src/main/java/org/onap/policy/clamp/controlloop/runtime/main/startstop/ClRuntimeActivator.java @@ -0,0 +1,115 @@ +/*- + * ============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.clamp.controlloop.runtime.main.startstop; + +import java.util.List; +import javax.ws.rs.core.Response.Status; +import org.onap.policy.clamp.controlloop.common.exception.ControlLoopRuntimeException; +import org.onap.policy.clamp.controlloop.runtime.main.parameters.ClRuntimeParameterGroup; +import org.onap.policy.common.endpoints.event.comm.TopicEndpointManager; +import org.onap.policy.common.endpoints.event.comm.TopicSource; +import org.onap.policy.common.endpoints.listeners.MessageTypeDispatcher; +import org.onap.policy.common.parameters.ParameterService; +import org.onap.policy.common.utils.services.ServiceManagerContainer; + +/** + * This class activates the control loop runtime component as a complete service together with all its controllers, + * listeners & handlers. + */ +public class ClRuntimeActivator extends ServiceManagerContainer { + // Name of the message type for messages on topics + private static final String[] MSG_TYPE_NAMES = {"messageType"}; + + private final ClRuntimeParameterGroup clRuntimeParameterGroup; + + // Topics from which the application receives and to which the application sends messages + private List<TopicSource> topicSources; + + /** + * Listens for messages on the topic, decodes them into a message, and then dispatches them. + */ + private final MessageTypeDispatcher msgDispatcher; + + /** + * Instantiate the activator for the control loop runtime as a complete service. + * + * @param clRuntimeParameterGroup the parameters for the control loop runtime service + */ + public ClRuntimeActivator(final ClRuntimeParameterGroup clRuntimeParameterGroup) { + + if (clRuntimeParameterGroup == null || !clRuntimeParameterGroup.isValid()) { + throw new ControlLoopRuntimeException(Status.INTERNAL_SERVER_ERROR, "ParameterGroup not valid"); + } + + this.clRuntimeParameterGroup = clRuntimeParameterGroup; + + topicSources = TopicEndpointManager.getManager() + .addTopicSources(clRuntimeParameterGroup.getTopicParameterGroup().getTopicSources()); + + try { + msgDispatcher = new MessageTypeDispatcher(MSG_TYPE_NAMES); + } catch (final RuntimeException e) { + throw new ControlLoopRuntimeException(Status.INTERNAL_SERVER_ERROR, + "topic message dispatcher failed to start", e); + } + + // @formatter:off + addAction("Control loop runtime parameters", + () -> ParameterService.register(clRuntimeParameterGroup), + () -> ParameterService.deregister(clRuntimeParameterGroup.getName())); + + addAction("Topic endpoint management", + () -> TopicEndpointManager.getManager().start(), + () -> TopicEndpointManager.getManager().shutdown()); + + addAction("Topic Message Dispatcher", this::registerMsgDispatcher, this::unregisterMsgDispatcher); + + clRuntimeParameterGroup.getRestServerParameters().setName(clRuntimeParameterGroup.getName()); + // @formatter:on + } + + /** + * Registers the dispatcher with the topic source(s). + */ + private void registerMsgDispatcher() { + for (final TopicSource source : topicSources) { + source.register(msgDispatcher); + } + } + + /** + * Unregisters the dispatcher from the topic source(s). + */ + private void unregisterMsgDispatcher() { + for (final TopicSource source : topicSources) { + source.unregister(msgDispatcher); + } + } + + /** + * Get the parameters used by the activator. + * + * @return the parameters of the activator + */ + public ClRuntimeParameterGroup getParameterGroup() { + return clRuntimeParameterGroup; + } +} diff --git a/tosca-controlloop/runtime/src/main/java/org/onap/policy/clamp/controlloop/runtime/main/startstop/ClRuntimeCommandLineArguments.java b/tosca-controlloop/runtime/src/main/java/org/onap/policy/clamp/controlloop/runtime/main/startstop/ClRuntimeCommandLineArguments.java new file mode 100644 index 000000000..fa25b6ddb --- /dev/null +++ b/tosca-controlloop/runtime/src/main/java/org/onap/policy/clamp/controlloop/runtime/main/startstop/ClRuntimeCommandLineArguments.java @@ -0,0 +1,223 @@ +/*- + * ============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.clamp.controlloop.runtime.main.startstop; + +import java.io.File; +import java.io.PrintWriter; +import java.io.StringWriter; +import java.net.URL; +import java.util.Arrays; +import javax.ws.rs.core.Response; +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.onap.policy.clamp.controlloop.common.exception.ControlLoopException; +import org.onap.policy.common.utils.resources.ResourceUtils; + + +/** + * This class reads and handles command line parameters for the control loop runtime service. + * + */ +public class ClRuntimeCommandLineArguments { + private static final String FILE_MESSAGE_PREAMBLE = " file \""; + private static final int HELP_LINE_LENGTH = 120; + + private final Options options; + private String configurationFilePath = null; + + /** + * Construct the options for the control loop runtime component. + */ + public ClRuntimeCommandLineArguments() { + //@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 control loop runtime") + .required(false) + .type(Boolean.class) + .build()); + options.addOption(Option.builder("c") + .longOpt("config-file") + .desc("the full path to the configuration file to use, " + + "the configuration file must be a Json file containing the control loop runtime parameters") + .hasArg() + .argName("CONFIG_FILE") + .required(false) + .type(String.class) + .build()); + //@formatter:on + } + + /** + * 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 ControlLoopException on command argument errors + */ + public String parse(final String[] args) throws ControlLoopException { + // Clear all our arguments + setConfigurationFilePath(null); + + CommandLine commandLine = null; + try { + commandLine = new DefaultParser().parse(options, args); + } catch (final ParseException e) { + throw new ControlLoopException(Response.Status.NOT_ACCEPTABLE, + "invalid command line arguments specified", e); + } + + // Arguments left over after Commons CLI does its stuff + final String[] remainingArgs = commandLine.getArgs(); + + if (remainingArgs.length > 0) { + throw new ControlLoopException(Response.Status.NOT_ACCEPTABLE, + "too many command line arguments specified : " + Arrays.toString(args)); + } + + if (remainingArgs.length == 1) { + configurationFilePath = remainingArgs[0]; + } + + if (commandLine.hasOption('h')) { + return help(Main.class.getName()); + } + + if (commandLine.hasOption('v')) { + return version(); + } + + if (commandLine.hasOption('c')) { + setConfigurationFilePath(commandLine.getOptionValue('c')); + } + + return null; + } + + /** + * Validate the command line options. + * + * @throws ControlLoopException on command argument validation errors + */ + public void validate() throws ControlLoopException { + validateReadableFile("control loop runtime configuration", configurationFilePath); + } + + /** + * Print version information for control loop runtime. + * + * @return the version string + */ + public String version() { + return ResourceUtils.getResourceAsString("version.txt"); + } + + /** + * Print help information for control loop runtime. + * + * @param mainClassName the main class name + * @return the help string + */ + public String help(final String mainClassName) { + final HelpFormatter helpFormatter = new HelpFormatter(); + final StringWriter stringWriter = new StringWriter(); + final PrintWriter printWriter = new PrintWriter(stringWriter); + + helpFormatter.printHelp(printWriter, HELP_LINE_LENGTH, mainClassName + " [options...]", "options", options, 0, + 0, ""); + + return stringWriter.toString(); + } + + /** + * Gets the configuration file path. + * + * @return the configuration file path + */ + public String getConfigurationFilePath() { + return configurationFilePath; + } + + /** + * Gets the full expanded configuration file path. + * + * @return the configuration file path + */ + public String getFullConfigurationFilePath() { + return ResourceUtils.getFilePath4Resource(getConfigurationFilePath()); + } + + /** + * Sets the configuration file path. + * + * @param configurationFilePath the configuration file path + */ + public void setConfigurationFilePath(final String configurationFilePath) { + this.configurationFilePath = configurationFilePath; + + } + + /** + * Validate readable file. + * + * @param fileTag the file tag + * @param fileName the file name + * @throws ControlLoopException on the file name passed as a parameter + */ + private void validateReadableFile(final String fileTag, final String fileName) throws ControlLoopException { + if (fileName == null || fileName.length() == 0) { + throw new ControlLoopException(Response.Status.NOT_ACCEPTABLE, + 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 ControlLoopException(Response.Status.NOT_ACCEPTABLE, + fileTag + FILE_MESSAGE_PREAMBLE + fileName + "\" does not exist"); + } + + final File theFile = new File(fileUrl.getPath()); + if (!theFile.exists()) { + throw new ControlLoopException(Response.Status.NOT_ACCEPTABLE, + fileTag + FILE_MESSAGE_PREAMBLE + fileName + "\" does not exist"); + } + if (!theFile.isFile()) { + throw new ControlLoopException(Response.Status.NOT_ACCEPTABLE, + fileTag + FILE_MESSAGE_PREAMBLE + fileName + "\" is not a normal file"); + } + if (!theFile.canRead()) { + throw new ControlLoopException(Response.Status.NOT_ACCEPTABLE, + fileTag + FILE_MESSAGE_PREAMBLE + fileName + "\" is ureadable"); + } + } +} diff --git a/tosca-controlloop/runtime/src/main/java/org/onap/policy/clamp/controlloop/runtime/main/startstop/Main.java b/tosca-controlloop/runtime/src/main/java/org/onap/policy/clamp/controlloop/runtime/main/startstop/Main.java new file mode 100644 index 000000000..8e60d68cf --- /dev/null +++ b/tosca-controlloop/runtime/src/main/java/org/onap/policy/clamp/controlloop/runtime/main/startstop/Main.java @@ -0,0 +1,156 @@ +/*- + * ============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.clamp.controlloop.runtime.main.startstop; + +import java.util.Arrays; +import javax.ws.rs.core.Response; +import org.onap.policy.clamp.controlloop.common.ControlLoopConstants; +import org.onap.policy.clamp.controlloop.common.exception.ControlLoopException; +import org.onap.policy.clamp.controlloop.common.exception.ControlLoopRuntimeException; +import org.onap.policy.clamp.controlloop.runtime.main.parameters.ClRuntimeParameterGroup; +import org.onap.policy.clamp.controlloop.runtime.main.parameters.ClRuntimeParameterHandler; +import org.onap.policy.common.utils.resources.MessageConstants; +import org.onap.policy.common.utils.services.Registry; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * This class initiates ONAP Policy Framework Control Loop runtime component. + */ +public class Main { + + private static final Logger LOGGER = LoggerFactory.getLogger(Main.class); + + private ClRuntimeActivator activator; + private ClRuntimeParameterGroup parameterGroup; + + /** + * Instantiates the control loop runtime service. + * + * @param args the command line arguments + */ + public Main(final String[] args) { + final String argumentString = Arrays.toString(args); + LOGGER.info("Starting the control loop runtime service with arguments - {}", argumentString); + + // Check the arguments + final ClRuntimeCommandLineArguments arguments = new ClRuntimeCommandLineArguments(); + try { + // The arguments return a string if there is a message to print and we should exit + final String argumentMessage = arguments.parse(args); + if (argumentMessage != null) { + LOGGER.info(argumentMessage); + return; + } + // Validate that the arguments are sane + arguments.validate(); + + // Read the parameters + parameterGroup = new ClRuntimeParameterHandler().getParameters(arguments); + + // Now, create the activator for the service + activator = new ClRuntimeActivator(parameterGroup); + Registry.register(ControlLoopConstants.REG_CLRUNTIME_ACTIVATOR, activator); + + // Start the activator + activator.start(); + } catch (Exception exp) { + if (null != activator) { + Registry.unregister(ControlLoopConstants.REG_CLRUNTIME_ACTIVATOR); + } + throw new ControlLoopRuntimeException(Response.Status.BAD_REQUEST, + String.format(MessageConstants.START_FAILURE_MSG, MessageConstants.POLICY_CLAMP), exp); + } + + // Add a shutdown hook to shut everything down in an orderly manner + Runtime.getRuntime().addShutdownHook(new ClRuntimeShutdownHookClass()); + String successMsg = String.format(MessageConstants.START_SUCCESS_MSG, MessageConstants.POLICY_CLAMP); + LOGGER.info(successMsg); + } + + /** + * Check if main is running. + */ + public boolean isRunning() { + return activator != null && activator.isAlive(); + } + + /** + * Get the parameters specified in JSON. + * + * @return the parameters + */ + public ClRuntimeParameterGroup getParameters() { + return parameterGroup; + } + + /** + * Shut down Execution. + * + * @throws ControlLoopException on shutdown errors + */ + public void shutdown() throws ControlLoopException { + // clear the parameterGroup variable + parameterGroup = null; + + // clear the cl runtime activator + if (activator != null) { + activator.stop(); + } + } + + /** + * The Class ClRuntimeShutdownHookClass terminates the control loop runtime service when its run method is called. + */ + private class ClRuntimeShutdownHookClass extends Thread { + /* + * (non-Javadoc) + * + * @see java.lang.Runnable#run() + */ + @Override + public void run() { + if (!activator.isAlive()) { + return; + } + + try { + // Shutdown the control loop runtime service and wait for everything to stop + activator.stop(); + } catch (final RuntimeException e) { + LOGGER.warn("error occured during shut down of the control loop runtime service", e); + } + } + } + + /** + * The main method. + * + * @param args the arguments + */ + public static void main(final String[] args) { // NOSONAR + /* + * NOTE: arguments are validated by the constructor, thus sonar is disabled. + */ + + new Main(args); + } +} diff --git a/tosca-controlloop/runtime/src/main/resources/version.txt b/tosca-controlloop/runtime/src/main/resources/version.txt new file mode 100644 index 000000000..e11449e5b --- /dev/null +++ b/tosca-controlloop/runtime/src/main/resources/version.txt @@ -0,0 +1,4 @@ +ONAP Tosca defined control loop +Version: ${project.version} +Built (UTC): ${maven.build.timestamp} +ONAP https://wiki.onap.org diff --git a/tosca-controlloop/runtime/src/test/java/org/onap/policy/clamp/controlloop/runtime/main/startstop/ClRuntimeActivatorTest.java b/tosca-controlloop/runtime/src/test/java/org/onap/policy/clamp/controlloop/runtime/main/startstop/ClRuntimeActivatorTest.java new file mode 100644 index 000000000..7928124c8 --- /dev/null +++ b/tosca-controlloop/runtime/src/test/java/org/onap/policy/clamp/controlloop/runtime/main/startstop/ClRuntimeActivatorTest.java @@ -0,0 +1,81 @@ +/*- + * ============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.clamp.controlloop.runtime.main.startstop; + +import static org.assertj.core.api.Assertions.assertThatExceptionOfType; +import static org.assertj.core.api.Assertions.assertThatIllegalStateException; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; + +import org.junit.Test; +import org.onap.policy.clamp.controlloop.common.exception.ControlLoopRuntimeException; +import org.onap.policy.clamp.controlloop.runtime.main.parameters.ClRuntimeParameterGroup; +import org.onap.policy.clamp.controlloop.runtime.main.parameters.ClRuntimeParameterHandler; +import org.onap.policy.common.utils.services.Registry; + +/** + * Class to perform unit test of {@link ClRuntimeActivator}}. + * + */ +public class ClRuntimeActivatorTest { + + @Test + public void testStartAndStop() throws Exception { + Registry.newRegistry(); + final String[] configParameters = {"-c", "src/test/resources/parameters/TestParameters.json"}; + final ClRuntimeCommandLineArguments arguments = new ClRuntimeCommandLineArguments(); + arguments.parse(configParameters); + ClRuntimeParameterGroup parameterGroup = new ClRuntimeParameterHandler().getParameters(arguments); + ClRuntimeActivator activator = new ClRuntimeActivator(parameterGroup); + activator.isAlive(); + + assertFalse(activator.isAlive()); + activator.start(); + assertTrue(activator.isAlive()); + assertTrue(activator.getParameterGroup().isValid()); + assertEquals(activator.getParameterGroup().getName(), + activator.getParameterGroup().getRestServerParameters().getName()); + + // repeat start - should throw an exception + assertThatIllegalStateException().isThrownBy(() -> activator.start()); + assertTrue(activator.isAlive()); + assertTrue(activator.getParameterGroup().isValid()); + + activator.stop(); + assertFalse(activator.isAlive()); + + // repeat stop - should throw an exception + assertThatIllegalStateException().isThrownBy(() -> activator.stop()); + assertFalse(activator.isAlive()); + } + + @Test + public void testNull() { + assertThatExceptionOfType(ControlLoopRuntimeException.class).isThrownBy(() -> new ClRuntimeActivator(null)); + } + + @Test + public void testNotValid() { + assertThatExceptionOfType(ControlLoopRuntimeException.class) + .isThrownBy(() -> new ClRuntimeActivator(new ClRuntimeParameterGroup("name"))); + } +} diff --git a/tosca-controlloop/runtime/src/test/java/org/onap/policy/clamp/controlloop/runtime/main/startstop/MainTest.java b/tosca-controlloop/runtime/src/test/java/org/onap/policy/clamp/controlloop/runtime/main/startstop/MainTest.java new file mode 100644 index 000000000..6dd4031ed --- /dev/null +++ b/tosca-controlloop/runtime/src/test/java/org/onap/policy/clamp/controlloop/runtime/main/startstop/MainTest.java @@ -0,0 +1,157 @@ +/*- + * ============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.clamp.controlloop.runtime.main.startstop; + +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.assertNotNull; +import static org.junit.Assert.assertTrue; + +import org.junit.AfterClass; +import org.junit.BeforeClass; +import org.junit.Test; +import org.onap.policy.clamp.controlloop.common.ControlLoopConstants; +import org.onap.policy.clamp.controlloop.common.exception.ControlLoopRuntimeException; +import org.onap.policy.common.utils.resources.MessageConstants; +import org.onap.policy.common.utils.services.Registry; + +/** + * Class to perform unit test of {@link Main}}. + * + */ +public class MainTest { + + public static final String POLICY_CLAMP_FAILURE_MSG = + String.format(MessageConstants.START_FAILURE_MSG, MessageConstants.POLICY_CLAMP); + + /** + * Set up. + */ + @BeforeClass + public static void setUp() { + Registry.newRegistry(); + } + + /** + * Shuts "main" down. + * + * @throws Exception if an error occurs + */ + @AfterClass + public static void tearDown() throws Exception { + // shut down activator + final ClRuntimeActivator activator = + Registry.getOrDefault(ControlLoopConstants.REG_CLRUNTIME_ACTIVATOR, ClRuntimeActivator.class, null); + if (activator != null && activator.isAlive()) { + activator.shutdown(); + } + } + + @Test + public void testMain_Help() { + final String[] configParameters = {"-h"}; + Main main = new Main(configParameters); + assertFalse(main.isRunning()); + } + + @Test + public void testMain_Version() { + final String[] configParameters = {"-v"}; + Main main = new Main(configParameters); + assertFalse(main.isRunning()); + } + + @Test + public void testMain_Valid() { + final String[] configParameters = {"-c", "src/test/resources/parameters/TestParameters.json"}; + Main main = new Main(configParameters); + assertTrue(main.isRunning()); + + // ensure items were added to the registry + assertNotNull(Registry.get(ControlLoopConstants.REG_CLRUNTIME_ACTIVATOR, ClRuntimeActivator.class)); + + assertThatCode(() -> main.shutdown()).doesNotThrowAnyException(); + + assertFalse(main.isRunning()); + } + + @Test + public void testMain_NoParameter() { + assertThatConfigParameterThrownException(new String[] {}); + } + + @Test + public void testMain_FilePathNotDefined() { + assertThatConfigParameterThrownException(new String[] {"-c"}); + } + + @Test + public void testMain_TooManyCommand() { + assertThatConfigParameterThrownException(new String[] {"-h", "d"}); + } + + @Test + public void testMain_WrongParameter() { + assertThatConfigParameterThrownException(new String[] {"-d"}); + } + + private void assertThatConfigParameterThrownException(final String[] configParameters) { + assertThatThrownBy(() -> Main.main(configParameters)).isInstanceOf(ControlLoopRuntimeException.class) + .hasMessage(POLICY_CLAMP_FAILURE_MSG); + } + + @Test + public void testParticipant_NoFileWithThisName() { + assertThatConfigFileThrownException("src/test/resources/parameters/NoFileWithThisName.json"); + } + + @Test + public void testParticipant_NotValidFile() { + assertThatConfigFileThrownException("src/test/resources/parameters"); + } + + @Test + public void testParticipant_FileEmpty() { + assertThatConfigFileThrownException("src/test/resources/parameters/EmptyParameters.json"); + } + + @Test + public void testParticipant_NoParameters() { + assertThatConfigFileThrownException("src/test/resources/parameters/NoParameters.json"); + } + + @Test + public void testParticipant_InvalidParameters() { + assertThatConfigFileThrownException("src/test/resources/parameters/InvalidParameters.json"); + } + + @Test + public void testParticipant_WrongJsonFormat() { + assertThatConfigFileThrownException("src/test/resources/parameters/Unreadable.json"); + } + + private void assertThatConfigFileThrownException(final String configFilePath) { + final String[] configParameters = new String[] {"-c", configFilePath}; + assertThatThrownBy(() -> new Main(configParameters)).isInstanceOf(ControlLoopRuntimeException.class) + .hasMessage(POLICY_CLAMP_FAILURE_MSG); + } +} diff --git a/tosca-controlloop/runtime/src/test/resources/parameters/EmptyParameters.json b/tosca-controlloop/runtime/src/test/resources/parameters/EmptyParameters.json new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/tosca-controlloop/runtime/src/test/resources/parameters/EmptyParameters.json diff --git a/tosca-controlloop/runtime/src/test/resources/parameters/InvalidParameters.json b/tosca-controlloop/runtime/src/test/resources/parameters/InvalidParameters.json new file mode 100644 index 000000000..976ec2937 --- /dev/null +++ b/tosca-controlloop/runtime/src/test/resources/parameters/InvalidParameters.json @@ -0,0 +1,3 @@ +{ + "name": "" +} diff --git a/tosca-controlloop/runtime/src/test/resources/parameters/NoParameters.json b/tosca-controlloop/runtime/src/test/resources/parameters/NoParameters.json new file mode 100644 index 000000000..2c63c0851 --- /dev/null +++ b/tosca-controlloop/runtime/src/test/resources/parameters/NoParameters.json @@ -0,0 +1,2 @@ +{ +} diff --git a/tosca-controlloop/runtime/src/test/resources/parameters/TestParameters.json b/tosca-controlloop/runtime/src/test/resources/parameters/TestParameters.json new file mode 100644 index 000000000..c3be762d6 --- /dev/null +++ b/tosca-controlloop/runtime/src/test/resources/parameters/TestParameters.json @@ -0,0 +1,79 @@ +{ + "name": "ControlLoopRuntimeGroup", + "restServerParameters": { + "host": "0.0.0.0", + "port": 6969, + "userName": "healthcheck", + "password": "zb!XztG34", + "https": false, + "aaf": false + }, + "participantParameters": { + "heartBeatMs": 120000, + "updateParameters": { + "maxRetryCount": 1, + "maxWaitMs": 30000 + }, + "stateChangeParameters": { + "maxRetryCount": 1, + "maxWaitMs": 30000 + } + }, + "databaseProviderParameters": { + "name": "PolicyProviderParameterGroup", + "implementation": "org.onap.policy.models.provider.impl.DatabasePolicyModelsProviderImpl", + "databaseDriver": "org.h2.Driver", + "databaseUrl": "jdbc:h2:mem:testdb", + "databaseUser": "policy", + "databasePassword": "P01icY", + "persistenceUnit": "ToscaConceptTest" + }, + "topicParameterGroup": { + "topicSources": [ + { + "topic": "POLICY-CLRUNTIME-PARTICIPANT", + "servers": [ + "localhost" + ], + "topicCommInfrastructure": "dmaap", + "fetchTimeout": 15000 + } + ], + "topicSinks": [ + { + "topic": "POLICY-CLRUNTIME-PARTICIPANT", + "servers": [ + "localhost" + ], + "topicCommInfrastructure": "dmaap" + }, + { + "topic": "POLICY-NOTIFICATION", + "servers": [ + "localhost" + ], + "topicCommInfrastructure": "dmaap" + } + ] + }, + "healthCheckRestClientParameters": [ + { + "clientName": "api", + "hostname": "policy-api", + "port": 6969, + "userName": "healthcheck", + "password": "zb!XztG34", + "useHttps": true, + "basePath": "policy/api/v1/healthcheck" + }, + { + "clientName": "distribution", + "hostname": "policy-distribution", + "port": 6969, + "userName": "healthcheck", + "password": "zb!XztG34", + "useHttps": true, + "basePath": "healthcheck" + } + ] +} diff --git a/tosca-controlloop/runtime/src/test/resources/parameters/Unreadable.json b/tosca-controlloop/runtime/src/test/resources/parameters/Unreadable.json new file mode 100644 index 000000000..e69de158b --- /dev/null +++ b/tosca-controlloop/runtime/src/test/resources/parameters/Unreadable.json @@ -0,0 +1,79 @@ +{ + "name": "ControlLoopRuntimeGroup", + "restServerParameters": { + "host": "0.0.0.0", + "port": 6969, + "userName": "healthcheck", + "password": "zb!XztG34", + "https": false, + "aaf": false + }, + "participantParameters": { + "heartBeatMs": 120000, + "updateParameters": { + "maxRetryCount": 1, + "maxWaitMs": 30000 + }, + "stateChangeParameters": { + "maxRetryCount": 1, + "maxWaitMs": 30000 + } + }, + "databaseProviderParameters": { + "name": "PolicyProviderParameterGroup", + "implementation": "org.onap.policy.models.provider.impl.DatabasePolicyModelsProviderImpl", + "databaseDriver": "org.h2.Driver", + "databaseUrl": "jdbc:h2:mem:testdb", + "databaseUser": "policy", + "databasePassword": "P01icY", + "persistenceUnit": "ToscaConceptTest" + }, + "topicParameterGroup": { + "topicSources": [ + { + "topic": "POLICY-CLRUNTIME-PARTICIPANT", + "servers": [ + "localhost" + ], + "topicCommInfrastructure": "dmaap", + "fetchTimeout": 15000 + } + ], + "topicSinks": [ + { + "topic": "POLICY-CLRUNTIME-PARTICIPANT", + "servers": [ + "localhost" + ], + "topicCommInfrastructure": "dmaap" + }, + { + "topic": "POLICY-NOTIFICATION", + "servers": [ + "localhost" + ], + "topicCommInfrastructure": "dmaap" + } + ] + }, + "healthCheckRestClientParameters": [ + { + "clientName": "api", + "hostname": "policy-api", + "port": 6969, + "userName": "healthcheck", + "password": "zb!XztG34", + "useHttps": true, + "basePath": "policy/api/v1/healthcheck" + }, + { + "clientName": "distribution", + "hostname": "policy-distribution", + "port": 6969, + "userName": "healthcheck", + "password": "zb!XztG34", + "useHttps": true, + "basePath": "healthcheck" + } + ] +
\ No newline at end of file |