aboutsummaryrefslogtreecommitdiffstats
path: root/model/src/main/java/org/onap
diff options
context:
space:
mode:
Diffstat (limited to 'model/src/main/java/org/onap')
-rw-r--r--model/src/main/java/org/onap/policy/apex/model/basicmodel/concepts/ApexConceptException.java49
-rw-r--r--model/src/main/java/org/onap/policy/apex/model/basicmodel/concepts/ApexException.java111
-rw-r--r--model/src/main/java/org/onap/policy/apex/model/basicmodel/concepts/ApexRuntimeException.java93
-rw-r--r--model/src/main/java/org/onap/policy/apex/model/basicmodel/concepts/AxArtifactKey.java344
-rw-r--r--model/src/main/java/org/onap/policy/apex/model/basicmodel/concepts/AxConcept.java137
-rw-r--r--model/src/main/java/org/onap/policy/apex/model/basicmodel/concepts/AxConceptGetter.java77
-rw-r--r--model/src/main/java/org/onap/policy/apex/model/basicmodel/concepts/AxConceptGetterImpl.java143
-rw-r--r--model/src/main/java/org/onap/policy/apex/model/basicmodel/concepts/AxKey.java105
-rw-r--r--model/src/main/java/org/onap/policy/apex/model/basicmodel/concepts/AxKeyInfo.java317
-rw-r--r--model/src/main/java/org/onap/policy/apex/model/basicmodel/concepts/AxKeyInformation.java389
-rw-r--r--model/src/main/java/org/onap/policy/apex/model/basicmodel/concepts/AxKeyUse.java221
-rw-r--r--model/src/main/java/org/onap/policy/apex/model/basicmodel/concepts/AxModel.java423
-rw-r--r--model/src/main/java/org/onap/policy/apex/model/basicmodel/concepts/AxReferenceKey.java527
-rw-r--r--model/src/main/java/org/onap/policy/apex/model/basicmodel/concepts/AxToscaPolicyProcessingStatus.java46
-rw-r--r--model/src/main/java/org/onap/policy/apex/model/basicmodel/concepts/AxValidationMessage.java104
-rw-r--r--model/src/main/java/org/onap/policy/apex/model/basicmodel/concepts/AxValidationResult.java150
-rw-r--r--model/src/main/java/org/onap/policy/apex/model/basicmodel/concepts/package-info.java27
-rw-r--r--model/src/main/java/org/onap/policy/apex/model/basicmodel/handling/ApexModelCreator.java41
-rw-r--r--model/src/main/java/org/onap/policy/apex/model/basicmodel/handling/ApexModelCustomGsonMapAdapter.java118
-rw-r--r--model/src/main/java/org/onap/policy/apex/model/basicmodel/handling/ApexModelCustomGsonRefereceKeyAdapter.java50
-rw-r--r--model/src/main/java/org/onap/policy/apex/model/basicmodel/handling/ApexModelException.java51
-rw-r--r--model/src/main/java/org/onap/policy/apex/model/basicmodel/handling/ApexModelFileWriter.java99
-rw-r--r--model/src/main/java/org/onap/policy/apex/model/basicmodel/handling/ApexModelReader.java169
-rw-r--r--model/src/main/java/org/onap/policy/apex/model/basicmodel/handling/ApexModelSaver.java80
-rw-r--r--model/src/main/java/org/onap/policy/apex/model/basicmodel/handling/ApexModelStringWriter.java94
-rw-r--r--model/src/main/java/org/onap/policy/apex/model/basicmodel/handling/ApexModelWriter.java148
-rw-r--r--model/src/main/java/org/onap/policy/apex/model/basicmodel/handling/package-info.java21
-rw-r--r--model/src/main/java/org/onap/policy/apex/model/basicmodel/package-info.java31
-rw-r--r--model/src/main/java/org/onap/policy/apex/model/basicmodel/service/ModelService.java108
-rw-r--r--model/src/main/java/org/onap/policy/apex/model/basicmodel/service/package-info.java26
-rw-r--r--model/src/main/java/org/onap/policy/apex/model/basicmodel/test/TestApexModel.java259
-rw-r--r--model/src/main/java/org/onap/policy/apex/model/basicmodel/test/TestApexModelCreator.java62
-rw-r--r--model/src/main/java/org/onap/policy/apex/model/basicmodel/test/package-info.java26
-rw-r--r--model/src/main/java/org/onap/policy/apex/model/contextmodel/concepts/AxContextAlbum.java268
-rw-r--r--model/src/main/java/org/onap/policy/apex/model/contextmodel/concepts/AxContextAlbums.java310
-rw-r--r--model/src/main/java/org/onap/policy/apex/model/contextmodel/concepts/AxContextModel.java222
-rw-r--r--model/src/main/java/org/onap/policy/apex/model/contextmodel/concepts/AxContextSchema.java299
-rw-r--r--model/src/main/java/org/onap/policy/apex/model/contextmodel/concepts/AxContextSchemas.java304
-rw-r--r--model/src/main/java/org/onap/policy/apex/model/contextmodel/concepts/package-info.java28
-rw-r--r--model/src/main/java/org/onap/policy/apex/model/contextmodel/handling/ContextComparer.java70
-rw-r--r--model/src/main/java/org/onap/policy/apex/model/contextmodel/handling/package-info.java27
-rw-r--r--model/src/main/java/org/onap/policy/apex/model/enginemodel/concepts/AxEngineModel.java338
-rw-r--r--model/src/main/java/org/onap/policy/apex/model/enginemodel/concepts/AxEngineState.java52
-rw-r--r--model/src/main/java/org/onap/policy/apex/model/enginemodel/concepts/AxEngineStats.java533
-rw-r--r--model/src/main/java/org/onap/policy/apex/model/enginemodel/concepts/package-info.java28
-rw-r--r--model/src/main/java/org/onap/policy/apex/model/eventmodel/concepts/AxEvent.java547
-rw-r--r--model/src/main/java/org/onap/policy/apex/model/eventmodel/concepts/AxEventModel.java276
-rw-r--r--model/src/main/java/org/onap/policy/apex/model/eventmodel/concepts/AxEvents.java351
-rw-r--r--model/src/main/java/org/onap/policy/apex/model/eventmodel/concepts/AxField.java347
-rw-r--r--model/src/main/java/org/onap/policy/apex/model/eventmodel/concepts/AxInputField.java88
-rw-r--r--model/src/main/java/org/onap/policy/apex/model/eventmodel/concepts/AxOutputField.java88
-rw-r--r--model/src/main/java/org/onap/policy/apex/model/eventmodel/concepts/package-info.java27
-rw-r--r--model/src/main/java/org/onap/policy/apex/model/modelapi/ApexApiResult.java264
-rw-r--r--model/src/main/java/org/onap/policy/apex/model/modelapi/ApexEditorApi.java905
-rw-r--r--model/src/main/java/org/onap/policy/apex/model/modelapi/ApexModel.java176
-rw-r--r--model/src/main/java/org/onap/policy/apex/model/modelapi/ApexModelFactory.java76
-rw-r--r--model/src/main/java/org/onap/policy/apex/model/modelapi/impl/ApexModelImpl.java866
-rw-r--r--model/src/main/java/org/onap/policy/apex/model/modelapi/impl/ContextAlbum.java39
-rw-r--r--model/src/main/java/org/onap/policy/apex/model/modelapi/impl/ContextAlbumFacade.java254
-rw-r--r--model/src/main/java/org/onap/policy/apex/model/modelapi/impl/ContextSchemaFacade.java238
-rw-r--r--model/src/main/java/org/onap/policy/apex/model/modelapi/impl/CreatePolicyStateTaskRef.java38
-rw-r--r--model/src/main/java/org/onap/policy/apex/model/modelapi/impl/EventFacade.java386
-rw-r--r--model/src/main/java/org/onap/policy/apex/model/modelapi/impl/KeyInformationFacade.java228
-rw-r--r--model/src/main/java/org/onap/policy/apex/model/modelapi/impl/ModelFacade.java202
-rw-r--r--model/src/main/java/org/onap/policy/apex/model/modelapi/impl/ModelHandlerFacade.java498
-rw-r--r--model/src/main/java/org/onap/policy/apex/model/modelapi/impl/PolicyFacade.java1385
-rw-r--r--model/src/main/java/org/onap/policy/apex/model/modelapi/impl/TaskFacade.java597
-rw-r--r--model/src/main/java/org/onap/policy/apex/model/modelapi/impl/package-info.java27
-rw-r--r--model/src/main/java/org/onap/policy/apex/model/modelapi/package-info.java27
-rw-r--r--model/src/main/java/org/onap/policy/apex/model/policymodel/concepts/AxLogic.java355
-rw-r--r--model/src/main/java/org/onap/policy/apex/model/policymodel/concepts/AxLogicReader.java74
-rw-r--r--model/src/main/java/org/onap/policy/apex/model/policymodel/concepts/AxPolicies.java360
-rw-r--r--model/src/main/java/org/onap/policy/apex/model/policymodel/concepts/AxPolicy.java508
-rw-r--r--model/src/main/java/org/onap/policy/apex/model/policymodel/concepts/AxPolicyModel.java661
-rw-r--r--model/src/main/java/org/onap/policy/apex/model/policymodel/concepts/AxState.java870
-rw-r--r--model/src/main/java/org/onap/policy/apex/model/policymodel/concepts/AxStateFinalizerLogic.java121
-rw-r--r--model/src/main/java/org/onap/policy/apex/model/policymodel/concepts/AxStateOutput.java291
-rw-r--r--model/src/main/java/org/onap/policy/apex/model/policymodel/concepts/AxStateParamsBuilder.java162
-rw-r--r--model/src/main/java/org/onap/policy/apex/model/policymodel/concepts/AxStateTaskOutputType.java42
-rw-r--r--model/src/main/java/org/onap/policy/apex/model/policymodel/concepts/AxStateTaskReference.java339
-rw-r--r--model/src/main/java/org/onap/policy/apex/model/policymodel/concepts/AxStateTree.java173
-rw-r--r--model/src/main/java/org/onap/policy/apex/model/policymodel/concepts/AxTask.java405
-rw-r--r--model/src/main/java/org/onap/policy/apex/model/policymodel/concepts/AxTaskLogic.java120
-rw-r--r--model/src/main/java/org/onap/policy/apex/model/policymodel/concepts/AxTaskParameter.java253
-rw-r--r--model/src/main/java/org/onap/policy/apex/model/policymodel/concepts/AxTaskSelectionLogic.java119
-rw-r--r--model/src/main/java/org/onap/policy/apex/model/policymodel/concepts/AxTasks.java351
-rw-r--r--model/src/main/java/org/onap/policy/apex/model/policymodel/concepts/PolicyException.java51
-rw-r--r--model/src/main/java/org/onap/policy/apex/model/policymodel/concepts/PolicyRuntimeException.java51
-rw-r--r--model/src/main/java/org/onap/policy/apex/model/policymodel/concepts/package-info.java28
-rw-r--r--model/src/main/java/org/onap/policy/apex/model/policymodel/handling/PolicyAnalyser.java178
-rw-r--r--model/src/main/java/org/onap/policy/apex/model/policymodel/handling/PolicyAnalysisResult.java266
-rw-r--r--model/src/main/java/org/onap/policy/apex/model/policymodel/handling/PolicyComparer.java46
-rw-r--r--model/src/main/java/org/onap/policy/apex/model/policymodel/handling/PolicyLogicReader.java142
-rw-r--r--model/src/main/java/org/onap/policy/apex/model/policymodel/handling/PolicyModelComparer.java268
-rw-r--r--model/src/main/java/org/onap/policy/apex/model/policymodel/handling/PolicyModelMerger.java183
-rw-r--r--model/src/main/java/org/onap/policy/apex/model/policymodel/handling/PolicyModelSplitter.java165
-rw-r--r--model/src/main/java/org/onap/policy/apex/model/policymodel/handling/package-info.java26
-rw-r--r--model/src/main/java/org/onap/policy/apex/model/utilities/CollectionUtils.java109
-rw-r--r--model/src/main/java/org/onap/policy/apex/model/utilities/DirectoryDeleteShutdownHook.java67
-rw-r--r--model/src/main/java/org/onap/policy/apex/model/utilities/DirectoryUtils.java110
-rw-r--r--model/src/main/java/org/onap/policy/apex/model/utilities/TreeMapUtils.java126
-rw-r--r--model/src/main/java/org/onap/policy/apex/model/utilities/comparison/KeyComparer.java42
-rw-r--r--model/src/main/java/org/onap/policy/apex/model/utilities/comparison/KeyDifference.java86
-rw-r--r--model/src/main/java/org/onap/policy/apex/model/utilities/comparison/KeyedMapComparer.java96
-rw-r--r--model/src/main/java/org/onap/policy/apex/model/utilities/comparison/KeyedMapDifference.java174
-rw-r--r--model/src/main/java/org/onap/policy/apex/model/utilities/comparison/package-info.java26
-rw-r--r--model/src/main/java/org/onap/policy/apex/model/utilities/json/JsonHandler.java50
-rwxr-xr-xmodel/src/main/java/org/onap/policy/apex/model/utilities/json/package-info.java26
-rw-r--r--model/src/main/java/org/onap/policy/apex/model/utilities/package-info.java26
109 files changed, 22601 insertions, 0 deletions
diff --git a/model/src/main/java/org/onap/policy/apex/model/basicmodel/concepts/ApexConceptException.java b/model/src/main/java/org/onap/policy/apex/model/basicmodel/concepts/ApexConceptException.java
new file mode 100644
index 000000000..d62818dc8
--- /dev/null
+++ b/model/src/main/java/org/onap/policy/apex/model/basicmodel/concepts/ApexConceptException.java
@@ -0,0 +1,49 @@
+/*
+ * ============LICENSE_START=======================================================
+ * Copyright (C) 2016-2018 Ericsson. All rights reserved.
+ * ================================================================================
+ * 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.apex.model.basicmodel.concepts;
+
+/**
+ * This class is an exception thrown on Apex Concept errors.
+ *
+ * @author Liam Fallon (liam.fallon@ericsson.com)
+ */
+public class ApexConceptException extends ApexException {
+ private static final long serialVersionUID = -8507246953751956974L;
+
+ /**
+ * Instantiates a new apex concept exception.
+ *
+ * @param message the message on the exception
+ */
+ public ApexConceptException(final String message) {
+ super(message);
+ }
+
+ /**
+ * Instantiates a new apex concept exception.
+ *
+ * @param message the message on the exception
+ * @param exception the exception that caused this Apex exception
+ */
+ public ApexConceptException(final String message, final Exception exception) {
+ super(message, exception);
+ }
+}
diff --git a/model/src/main/java/org/onap/policy/apex/model/basicmodel/concepts/ApexException.java b/model/src/main/java/org/onap/policy/apex/model/basicmodel/concepts/ApexException.java
new file mode 100644
index 000000000..2eca2f783
--- /dev/null
+++ b/model/src/main/java/org/onap/policy/apex/model/basicmodel/concepts/ApexException.java
@@ -0,0 +1,111 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * Copyright (C) 2016-2018 Ericsson. 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.
+ * 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.apex.model.basicmodel.concepts;
+
+/**
+ * This class is a base exception from which all Apex exceptions are sub classes.
+ *
+ * @author Liam Fallon (liam.fallon@ericsson.com)
+ */
+public class ApexException extends Exception {
+ private static final long serialVersionUID = -8507246953751956974L;
+
+ // The object on which the exception was thrown
+ private final transient Object object;
+
+ /**
+ * Instantiates a new apex exception.
+ *
+ * @param message the message on the exception
+ */
+ public ApexException(final String message) {
+ this(message, null);
+ }
+
+ /**
+ * Instantiates a new apex exception.
+ *
+ * @param message the message on the exception
+ * @param object the object that the exception was thrown on
+ */
+ public ApexException(final String message, final Object object) {
+ super(message);
+ this.object = object;
+ }
+
+ /**
+ * Instantiates a new apex exception.
+ *
+ * @param message the message on the exception
+ * @param exception the exception that caused this Apex exception
+ */
+ public ApexException(final String message, final Exception exception) {
+ this(message, exception, null);
+ }
+
+ /**
+ * Instantiates a new apex exception.
+ *
+ * @param message the message on the exception
+ * @param exception the exception that caused this Apex exception
+ * @param object the object that the exception was thrown on
+ */
+ public ApexException(final String message, final Exception exception, final Object object) {
+ super(message, exception);
+ this.object = object;
+ }
+
+ /**
+ * Get the message from this exception and its causes.
+ *
+ * @return the cascaded messages from this exception and the exceptions that caused it
+ */
+ public String getCascadedMessage() {
+ return buildCascadedMessage(this);
+ }
+
+ /**
+ * Build a cascaded message from an exception and all its nested exceptions.
+ * @param throwable the top level exception
+ * @return cascaded message string
+ */
+ public static String buildCascadedMessage(Throwable throwable) {
+ final var builder = new StringBuilder();
+ builder.append(throwable.getMessage());
+
+ for (var t = throwable; t != null; t = t.getCause()) {
+ builder.append("\ncaused by: ");
+ builder.append(t.getMessage());
+ }
+
+ return builder.toString();
+ }
+
+ /**
+ * Get the object on which the exception was thrown.
+ *
+ * @return The object on which the exception was thrown
+ */
+ public Object getObject() {
+ return object;
+ }
+}
diff --git a/model/src/main/java/org/onap/policy/apex/model/basicmodel/concepts/ApexRuntimeException.java b/model/src/main/java/org/onap/policy/apex/model/basicmodel/concepts/ApexRuntimeException.java
new file mode 100644
index 000000000..b4240ad0f
--- /dev/null
+++ b/model/src/main/java/org/onap/policy/apex/model/basicmodel/concepts/ApexRuntimeException.java
@@ -0,0 +1,93 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * Copyright (C) 2016-2018 Ericsson. All rights reserved.
+ * ================================================================================
+ * 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.apex.model.basicmodel.concepts;
+
+/**
+ * This class is a base run time exception from which all Apex run time exceptions are sub classes.
+ *
+ * @author Liam Fallon (liam.fallon@ericsson.com)
+ */
+public class ApexRuntimeException extends RuntimeException {
+ private static final long serialVersionUID = -8507246953751956974L;
+
+ // The object on which the exception was thrown
+ private final transient Object object;
+
+ /**
+ * Instantiates a new apex runtime exception.
+ *
+ * @param message the message on the exception
+ */
+ public ApexRuntimeException(final String message) {
+ this(message, null);
+ }
+
+ /**
+ * Instantiates a new apex runtime exception.
+ *
+ * @param message the message on the exception
+ * @param object the object that the exception was thrown on
+ */
+ public ApexRuntimeException(final String message, final Object object) {
+ super(message);
+ this.object = object;
+ }
+
+ /**
+ * Instantiates a new apex runtime exception.
+ *
+ * @param message the message on the exception
+ * @param exception the exception that caused this Apex exception
+ */
+ public ApexRuntimeException(final String message, final Exception exception) {
+ this(message, exception, null);
+ }
+
+ /**
+ * Instantiates a new apex runtime exception.
+ *
+ * @param message the message on the exception
+ * @param exception the exception that caused this Apex exception
+ * @param object the object that the exception was thrown on
+ */
+ public ApexRuntimeException(final String message, final Exception exception, final Object object) {
+ super(message, exception);
+ this.object = object;
+ }
+
+ /**
+ * Get the message from this exception and its causes.
+ *
+ * @return the message of this exception and all the exceptions that caused this exception
+ */
+ public String getCascadedMessage() {
+ return ApexException.buildCascadedMessage(this);
+ }
+
+ /**
+ * Get the object on which the exception was thrown.
+ *
+ * @return The object
+ */
+ public Object getObject() {
+ return object;
+ }
+}
diff --git a/model/src/main/java/org/onap/policy/apex/model/basicmodel/concepts/AxArtifactKey.java b/model/src/main/java/org/onap/policy/apex/model/basicmodel/concepts/AxArtifactKey.java
new file mode 100644
index 000000000..1efe83db8
--- /dev/null
+++ b/model/src/main/java/org/onap/policy/apex/model/basicmodel/concepts/AxArtifactKey.java
@@ -0,0 +1,344 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * Copyright (C) 2016-2018 Ericsson. All rights reserved.
+ * Modifications Copyright (C) 2019-2022 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.apex.model.basicmodel.concepts;
+
+import java.util.ArrayList;
+import java.util.List;
+import org.onap.policy.apex.model.basicmodel.concepts.AxValidationResult.ValidationResult;
+import org.onap.policy.common.utils.validation.Assertions;
+
+/**
+ * An artifact key uniquely identifies every first order entity in the system. Every first order concept in the system
+ * must have an {@link AxArtifactKey} to identify it. Concepts that are wholly contained in another concept are
+ * identified using a {@link AxReferenceKey} key.
+ *
+ * <p>Key validation checks that the name and version fields match the NAME_REGEXP and VERSION_REGEXP
+ * regular expressions respectively.
+ */
+public class AxArtifactKey extends AxKey {
+ private static final long serialVersionUID = 8932717618579392561L;
+
+ private static final String NAME_TOKEN = "name";
+ private static final String VERSION_TOKEN = "version";
+
+ private String name;
+ private String version;
+
+ /**
+ * The default constructor creates a null artifact key.
+ */
+ public AxArtifactKey() {
+ this(NULL_KEY_NAME, NULL_KEY_VERSION);
+ }
+
+ /**
+ * Copy constructor.
+ *
+ * @param copyConcept the concept to copy from
+ */
+ public AxArtifactKey(final AxArtifactKey copyConcept) {
+ super(copyConcept);
+ }
+
+ /**
+ * Constructor to create a key with the specified name and version.
+ *
+ * @param name the key name
+ * @param version the key version
+ */
+ public AxArtifactKey(final String name, final String version) {
+ super();
+ this.name = Assertions.validateStringParameter(NAME_TOKEN, name, NAME_REGEXP);
+ this.version = Assertions.validateStringParameter(VERSION_TOKEN, version, VERSION_REGEXP);
+ }
+
+ /**
+ * Constructor to create a key using the key and version from the specified key ID.
+ *
+ * @param id the key ID in a format that respects the KEY_ID_REGEXP
+ */
+ public AxArtifactKey(final String id) {
+ Assertions.argumentNotNull(id, "id may not be null");
+
+ // Check the incoming ID is valid
+ Assertions.validateStringParameter("id", id, KEY_ID_REGEXP);
+
+ // Split on colon, if the id passes the regular expression test above
+ // it'll have just one colon separating the name and version
+ // No need for range checks or size checks on the array
+ final String[] nameVersionArray = id.split(":");
+
+ // Return the new key
+ name = Assertions.validateStringParameter(NAME_TOKEN, nameVersionArray[0], NAME_REGEXP);
+ version = Assertions.validateStringParameter(VERSION_TOKEN, nameVersionArray[1], VERSION_REGEXP);
+ }
+
+ /**
+ * Get a null artifact key.
+ *
+ * @return a null artifact key
+ */
+ public static final AxArtifactKey getNullKey() {
+ return new AxArtifactKey(AxKey.NULL_KEY_NAME, AxKey.NULL_KEY_VERSION);
+ }
+
+ /**
+ * {@inheritDoc}.
+ */
+ @Override
+ public AxArtifactKey getKey() {
+ return this;
+ }
+
+ /**
+ * {@inheritDoc}.
+ */
+ @Override
+ public List<AxKey> getKeys() {
+ final List<AxKey> keyList = new ArrayList<>();
+ keyList.add(getKey());
+ return keyList;
+ }
+
+ /**
+ * {@inheritDoc}.
+ */
+ @Override
+ public String getId() {
+ return name + ':' + version;
+ }
+
+ /**
+ * Gets the key name.
+ *
+ * @return the key name
+ */
+ public String getName() {
+ return name;
+ }
+
+ /**
+ * Sets the key name.
+ *
+ * @param name the key name
+ */
+ public void setName(final String name) {
+ this.name = Assertions.validateStringParameter(NAME_TOKEN, name, NAME_REGEXP);
+ }
+
+ /**
+ * Gets the key version.
+ *
+ * @return the key version
+ */
+ public String getVersion() {
+ return version;
+ }
+
+ /**
+ * Sets the key version.
+ *
+ * @param version the key version
+ */
+ public void setVersion(final String version) {
+ this.version = Assertions.validateStringParameter(VERSION_TOKEN, version, VERSION_REGEXP);
+ }
+
+ /**
+ * Check if the key is IDENTICAL to a null key.
+ *
+ * @return true, if the key is IDENTICAL to a null key
+ */
+ public boolean isNullKey() {
+ return this.getCompatibility(AxArtifactKey.getNullKey()).equals(AxKey.Compatibility.IDENTICAL);
+ }
+
+
+ /**
+ * {@inheritDoc}.
+ */
+ @Override
+ public AxKey.Compatibility getCompatibility(final AxKey otherKey) {
+ if (!(otherKey instanceof AxArtifactKey)) {
+ return Compatibility.DIFFERENT;
+ }
+ final AxArtifactKey otherArtifactKey = (AxArtifactKey) otherKey;
+
+ if (this.equals(otherArtifactKey)) {
+ return Compatibility.IDENTICAL;
+ }
+ if (!this.getName().equals(otherArtifactKey.getName())) {
+ return Compatibility.DIFFERENT;
+ }
+
+ final String[] thisVersionArray = getVersion().split("\\.");
+ final String[] otherVersionArray = otherArtifactKey.getVersion().split("\\.");
+
+ // There must always be at least one element in each version
+ if (!thisVersionArray[0].equals(otherVersionArray[0])) {
+ return Compatibility.MAJOR;
+ }
+
+ if (thisVersionArray.length >= 2 && otherVersionArray.length >= 2
+ && !thisVersionArray[1].equals(otherVersionArray[1])) {
+ return Compatibility.MINOR;
+ }
+
+ return Compatibility.PATCH;
+ }
+
+ /**
+ * {@inheritDoc}.
+ */
+ @Override
+ public boolean isCompatible(final AxKey otherKey) {
+ if (!(otherKey instanceof AxArtifactKey)) {
+ return false;
+ }
+ final AxArtifactKey otherArtifactKey = (AxArtifactKey) otherKey;
+
+ final var compatibility = this.getCompatibility(otherArtifactKey);
+
+ return !(compatibility == Compatibility.DIFFERENT || compatibility == Compatibility.MAJOR);
+ }
+
+ /**
+ * {@inheritDoc}.
+ */
+ @Override
+ public AxValidationResult validate(final AxValidationResult result) {
+ final var nameValidationErrorMessage = Assertions.getStringParameterValidationMessage(NAME_TOKEN, name,
+ NAME_REGEXP);
+ if (nameValidationErrorMessage != null) {
+ result.addValidationMessage(new AxValidationMessage(this, this.getClass(), ValidationResult.INVALID,
+ "name invalid-" + nameValidationErrorMessage));
+ }
+
+ final var versionValidationErrorMessage = Assertions.getStringParameterValidationMessage(VERSION_TOKEN,
+ version, VERSION_REGEXP);
+ if (versionValidationErrorMessage != null) {
+ result.addValidationMessage(new AxValidationMessage(this, this.getClass(), ValidationResult.INVALID,
+ "version invalid-" + versionValidationErrorMessage));
+ }
+
+ return result;
+ }
+
+ /**
+ * {@inheritDoc}.
+ */
+ @Override
+ public void clean() {
+ name = Assertions.validateStringParameter(NAME_TOKEN, name, NAME_REGEXP);
+ version = Assertions.validateStringParameter(VERSION_TOKEN, version, VERSION_REGEXP);
+ }
+
+ /**
+ * {@inheritDoc}.
+ */
+ @Override
+ public String toString() {
+ final var builder = new StringBuilder();
+ builder.append(this.getClass().getSimpleName());
+ builder.append(":(");
+ builder.append("name=");
+ builder.append(name);
+ builder.append(",version=");
+ builder.append(version);
+ builder.append(")");
+ return builder.toString();
+ }
+
+ /**
+ * {@inheritDoc}.
+ */
+ @Override
+ public AxConcept copyTo(final AxConcept target) {
+ Assertions.argumentNotNull(target, "target may not be null");
+
+ final AxConcept copyObject = target;
+ Assertions.instanceOf(copyObject, AxArtifactKey.class);
+
+ final AxArtifactKey copy = ((AxArtifactKey) copyObject);
+ copy.setName(name);
+ copy.setVersion(version);
+
+ return copyObject;
+ }
+
+ /**
+ * {@inheritDoc}.
+ */
+ @Override
+ public int hashCode() {
+ final var prime = 31;
+ var result = 1;
+ result = prime * result + name.hashCode();
+ result = prime * result + version.hashCode();
+ return result;
+ }
+
+ /**
+ * {@inheritDoc}.
+ */
+ @Override
+ public boolean equals(final Object obj) {
+ if (obj == null) {
+ return false;
+ }
+ if (this == obj) {
+ return true;
+ }
+ if (getClass() != obj.getClass()) {
+ return false;
+ }
+
+ final AxArtifactKey other = (AxArtifactKey) obj;
+
+ if (!name.equals(other.name)) {
+ return false;
+ }
+ return version.equals(other.version);
+ }
+
+ /**
+ * {@inheritDoc}.
+ */
+ @Override
+ public int compareTo(final AxConcept otherObj) {
+ Assertions.argumentNotNull(otherObj, "comparison object may not be null");
+
+ if (this == otherObj) {
+ return 0;
+ }
+ if (getClass() != otherObj.getClass()) {
+ return this.hashCode() - otherObj.hashCode();
+ }
+
+ final AxArtifactKey other = (AxArtifactKey) otherObj;
+
+ if (!name.equals(other.name)) {
+ return name.compareTo(other.name);
+ }
+ return version.compareTo(other.version);
+ }
+}
diff --git a/model/src/main/java/org/onap/policy/apex/model/basicmodel/concepts/AxConcept.java b/model/src/main/java/org/onap/policy/apex/model/basicmodel/concepts/AxConcept.java
new file mode 100644
index 000000000..37fe30b33
--- /dev/null
+++ b/model/src/main/java/org/onap/policy/apex/model/basicmodel/concepts/AxConcept.java
@@ -0,0 +1,137 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * Copyright (C) 2016-2018 Ericsson. All rights reserved.
+ * Modifications Copyright (C) 2019,2022 Nordix Foundation.
+ * Modifications Copyright (C) 2021 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.apex.model.basicmodel.concepts;
+
+import java.io.Serializable;
+import java.util.List;
+import org.onap.policy.common.utils.validation.Assertions;
+
+/**
+ * This class is the base class for all Apex concept classes. It enforces implementation of abstract methods and
+ * interfaces on all concepts that are sub-classes of this class.
+ *
+ * @author Liam Fallon (liam.fallon@ericsson.com)
+ */
+public abstract class AxConcept implements Serializable, Comparable<AxConcept> {
+ private static final long serialVersionUID = -7434939557282697490L;
+
+ /**
+ * Default constructor.
+ */
+ protected AxConcept() {
+ // Default constructor
+ }
+
+ /**
+ * Copy constructor.
+ *
+ * @param copyConcept the concept to copy from
+ */
+ protected AxConcept(final AxConcept copyConcept) {
+ Assertions.argumentNotNull(copyConcept, "copy concept may not be null");
+ copyConcept.copyTo(this);
+ }
+
+ /**
+ * Gets the key of this concept.
+ *
+ * @return the concept key
+ */
+ public abstract AxKey getKey();
+
+ /**
+ * Gets a list of all keys for this concept and all concepts that are defined or referenced by this concept and its
+ * sub-concepts.
+ *
+ * @return the keys used by this concept and it's contained concepts
+ */
+ public abstract List<AxKey> getKeys();
+
+ /**
+ * Validate that this concept is structurally correct.
+ *
+ * @param result the parameter in which the result of the validation will be returned
+ * @return the validation result that was passed in in the @{link result} field with the result of this validation
+ * added
+ */
+ public abstract AxValidationResult validate(AxValidationResult result);
+
+ /**
+ * Clean this concept, tidy up any superfluous information such as leading and trailing white space.
+ */
+ public abstract void clean();
+
+ /**
+ * Builds references used by a concept.
+ */
+ public void buildReferences() {
+ }
+
+ /**
+ * {@inheritDoc}.
+ */
+ @Override
+ public abstract boolean equals(Object otherObject);
+
+ /**
+ * {@inheritDoc}.
+ */
+ @Override
+ public abstract String toString();
+
+ /**
+ * {@inheritDoc}.
+ */
+ @Override
+ public abstract int hashCode();
+
+ /**
+ * Copy this concept to another object. The target object must have the same class as the source object.
+ *
+ * @param target the target object to which this object is copied
+ * @return the copied object
+ */
+ public abstract AxConcept copyTo(AxConcept target);
+
+ /**
+ * Gets the ID string of this concept.
+ *
+ * @return the ID string of this concept
+ */
+ public String getId() {
+ return getKey().getId();
+ }
+
+ /**
+ * Checks if this key matches the given key ID.
+ *
+ * @param id the key ID to match against
+ * @return true, if this key matches the ID
+ */
+ public final boolean matchesId(final String id) {
+ Assertions.argumentNotNull(id, "id may not be null");
+
+ // Check the ID
+ return getId().equals(id);
+ }
+}
diff --git a/model/src/main/java/org/onap/policy/apex/model/basicmodel/concepts/AxConceptGetter.java b/model/src/main/java/org/onap/policy/apex/model/basicmodel/concepts/AxConceptGetter.java
new file mode 100644
index 000000000..0284de48a
--- /dev/null
+++ b/model/src/main/java/org/onap/policy/apex/model/basicmodel/concepts/AxConceptGetter.java
@@ -0,0 +1,77 @@
+/*
+ * ============LICENSE_START=======================================================
+ * Copyright (C) 2016-2018 Ericsson. All rights reserved.
+ * ================================================================================
+ * 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.apex.model.basicmodel.concepts;
+
+import java.util.Set;
+
+/**
+ * This interface is used to allow get methods to be placed on concepts that have embedded maps.
+ *
+ * <p>It forces those concepts with maps to implement the get methods specified on this interface as convenience methods
+ * to avoid concept users having to use a second level of referencing to access concepts in the the maps.
+ *
+ * @author Liam Fallon (liam.fallon@ericsson.com)
+ * @param <C> the type of concept on which the interface is applied.
+ */
+public interface AxConceptGetter<C> {
+
+ /**
+ * Get the latest version for a concept with the given key.
+ *
+ * @param conceptKey The key of the concept
+ * @return The concept
+ */
+ C get(AxArtifactKey conceptKey);
+
+ /**
+ * Get the latest version for a concept with the given key name.
+ *
+ * @param conceptKeyName The name of the concept
+ * @return The concept
+ */
+ C get(String conceptKeyName);
+
+ /**
+ * Get the latest version for a concept with the given key name and version.
+ *
+ * @param conceptKeyName The name of the concept
+ * @param conceptKeyVersion The version of the concept
+ * @return The concept
+ */
+ C get(String conceptKeyName, String conceptKeyVersion);
+
+ /**
+ * Get the all versions for a concept with the given key name.
+ *
+ * @param conceptKeyName The name of the concept
+ * @return The concepts
+ */
+ Set<C> getAll(String conceptKeyName);
+
+ /**
+ * Get the all versions for a concept with the given key name and starting version.
+ *
+ * @param conceptKeyName The name of the concept
+ * @param conceptKeyVersion The first version version of the concept to get
+ * @return The concepts
+ */
+ Set<C> getAll(String conceptKeyName, String conceptKeyVersion);
+}
diff --git a/model/src/main/java/org/onap/policy/apex/model/basicmodel/concepts/AxConceptGetterImpl.java b/model/src/main/java/org/onap/policy/apex/model/basicmodel/concepts/AxConceptGetterImpl.java
new file mode 100644
index 000000000..9c907da9c
--- /dev/null
+++ b/model/src/main/java/org/onap/policy/apex/model/basicmodel/concepts/AxConceptGetterImpl.java
@@ -0,0 +1,143 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * Copyright (C) 2016-2018 Ericsson. All rights reserved.
+ * Modifications Copyright (C) 2019-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.apex.model.basicmodel.concepts;
+
+import java.util.NavigableMap;
+import java.util.Set;
+import java.util.TreeSet;
+import org.onap.policy.common.utils.validation.Assertions;
+
+/**
+ * Implements concept getting from navigable maps.
+ *
+ * @author Liam Fallon (liam.fallon@ericsson.com)
+ * @param <C> the type of concept on which the interface implementation is applied.
+ */
+public class AxConceptGetterImpl<C> implements AxConceptGetter<C> {
+
+ // The map from which to get concepts
+ private final NavigableMap<AxArtifactKey, C> conceptMap;
+
+ /**
+ * Constructor that sets the concept map on which the getter methods in the interface will operate..
+ *
+ * @param conceptMap the concept map on which the method will operate
+ */
+ public AxConceptGetterImpl(final NavigableMap<AxArtifactKey, C> conceptMap) {
+ this.conceptMap = conceptMap;
+ }
+
+ /**
+ * {@inheritDoc}.
+ */
+ @Override
+ public C get(final AxArtifactKey conceptKey) {
+ return conceptMap.get(conceptKey);
+ }
+
+ /**
+ * {@inheritDoc}.
+ */
+ @Override
+ public C get(final String conceptKeyName) {
+ Assertions.argumentNotNull(conceptKeyName, "conceptKeyName may not be null");
+
+ // The very fist key that could have this name
+ final var lowestArtifactKey = new AxArtifactKey(conceptKeyName, "0.0.1");
+
+ // Check if we found a key for our name
+ AxArtifactKey foundKey = conceptMap.ceilingKey(lowestArtifactKey);
+ if (foundKey == null || !foundKey.getName().equals(conceptKeyName)) {
+ return null;
+ }
+
+ // Look for higher versions of the key
+ do {
+ final AxArtifactKey nextkey = conceptMap.higherKey(foundKey);
+ if (nextkey == null || !nextkey.getName().equals(conceptKeyName)) {
+ break;
+ }
+ foundKey = nextkey;
+ } while (true);
+
+ return conceptMap.get(foundKey);
+ }
+
+ /**
+ * {@inheritDoc}.
+ */
+ @Override
+ public C get(final String conceptKeyName, final String conceptKeyVersion) {
+ Assertions.argumentNotNull(conceptKeyName, "conceptKeyName may not be null");
+
+ if (conceptKeyVersion != null) {
+ return conceptMap.get(new AxArtifactKey(conceptKeyName, conceptKeyVersion));
+ } else {
+ return this.get(conceptKeyName);
+ }
+ }
+
+ /**
+ * {@inheritDoc}.
+ */
+ @Override
+ public Set<C> getAll(final String conceptKeyName) {
+ return getAll(conceptKeyName, null);
+ }
+
+ /**
+ * {@inheritDoc}.
+ */
+ @Override
+ public Set<C> getAll(final String conceptKeyName, final String conceptKeyVersion) {
+ final Set<C> returnSet = new TreeSet<>();
+
+ if (conceptKeyName == null) {
+ returnSet.addAll(conceptMap.values());
+ return returnSet;
+ }
+
+ // The very fist key that could have this name
+ final var lowestArtifactKey = new AxArtifactKey(conceptKeyName, "0.0.1");
+ if (conceptKeyVersion != null) {
+ lowestArtifactKey.setVersion(conceptKeyVersion);
+ }
+
+ // Check if we found a key for our name
+ AxArtifactKey foundKey = conceptMap.ceilingKey(lowestArtifactKey);
+ if (foundKey == null || !foundKey.getName().equals(conceptKeyName)) {
+ return returnSet;
+ }
+ returnSet.add(conceptMap.get(foundKey));
+
+ // Look for higher versions of the key
+ do {
+ foundKey = conceptMap.higherKey(foundKey);
+ if (foundKey == null || !foundKey.getName().equals(conceptKeyName)) {
+ break;
+ }
+ returnSet.add(conceptMap.get(foundKey));
+ } while (true);
+
+ return returnSet;
+ }
+}
diff --git a/model/src/main/java/org/onap/policy/apex/model/basicmodel/concepts/AxKey.java b/model/src/main/java/org/onap/policy/apex/model/basicmodel/concepts/AxKey.java
new file mode 100644
index 000000000..1b6f0148f
--- /dev/null
+++ b/model/src/main/java/org/onap/policy/apex/model/basicmodel/concepts/AxKey.java
@@ -0,0 +1,105 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * Copyright (C) 2016-2018 Ericsson. All rights reserved.
+ * Modifications Copyright (C) 2021 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.apex.model.basicmodel.concepts;
+
+/**
+ * The key uniquely identifies every entity in the system. This class is an abstract class to give a common parent for
+ * all key types in the system.
+ */
+public abstract class AxKey extends AxConcept {
+ private static final long serialVersionUID = 6281159885962014041L;
+
+ /** Regular expression to specify the structure of key names. */
+ public static final String NAME_REGEXP = "[A-Za-z0-9\\-_\\.]+";
+
+ /** Regular expression to specify the structure of key versions. */
+ public static final String VERSION_REGEXP = "[A-Za-z0-9.]+";
+
+ /** Regular expression to specify the structure of key IDs. */
+ public static final String KEY_ID_REGEXP = "[A-Za-z0-9\\-_\\.]+:[0-9].[0-9].[0-9]";
+
+ /** Specifies the value for names in NULL keys. */
+ public static final String NULL_KEY_NAME = "NULL";
+
+ /** Specifies the value for versions in NULL keys. */
+ public static final String NULL_KEY_VERSION = "0.0.0";
+
+ /**
+ * This enumeration is returned on key compatibility checks.
+ */
+ public enum Compatibility {
+ /** The keys have different names. */
+ DIFFERENT,
+ /**
+ * The name of the key matches but the Major version number of the keys is different (x in x.y.z do not match).
+ */
+ MAJOR,
+ /**
+ * The name of the key matches but the Minor version number of the keys is different (y in x.y.z do not match).
+ */
+ MINOR,
+ /**
+ * The name of the key matches but the Patch version number of the keys is different (z in x.y.z do not match).
+ */
+ PATCH,
+ /** The keys match completely. */
+ IDENTICAL
+ }
+
+ /**
+ * Default constructor.
+ */
+ protected AxKey() {
+ super();
+ }
+
+ /**
+ * Copy constructor.
+ *
+ * @param copyConcept the concept to copy from
+ */
+ protected AxKey(final AxKey copyConcept) {
+ super(copyConcept);
+ }
+
+ /**
+ * {@inheritDoc}.
+ */
+ @Override
+ public abstract String getId();
+
+ /**
+ * Return the result of a compatibility check of two keys.
+ *
+ * @param otherKey the key to check compatibility against
+ * @return the compatibility result of the check
+ */
+ public abstract Compatibility getCompatibility(AxKey otherKey);
+
+ /**
+ * Check if two keys are compatible, that is the keys are IDENTICAL or have only MINOR, PATCH differences.
+ *
+ * @param otherKey the key to check compatibility against
+ * @return true, if the keys are compatible
+ */
+ public abstract boolean isCompatible(AxKey otherKey);
+}
diff --git a/model/src/main/java/org/onap/policy/apex/model/basicmodel/concepts/AxKeyInfo.java b/model/src/main/java/org/onap/policy/apex/model/basicmodel/concepts/AxKeyInfo.java
new file mode 100644
index 000000000..bd3b18ec9
--- /dev/null
+++ b/model/src/main/java/org/onap/policy/apex/model/basicmodel/concepts/AxKeyInfo.java
@@ -0,0 +1,317 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * Copyright (C) 2016-2018 Ericsson. All rights reserved.
+ * Modifications Copyright (C) 2019-2022 Nordix Foundation.
+ * Modifications Copyright (C) 2021 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.apex.model.basicmodel.concepts;
+
+import com.google.gson.annotations.SerializedName;
+import java.util.List;
+import java.util.Random;
+import java.util.UUID;
+import org.apache.commons.lang3.StringUtils;
+import org.onap.policy.apex.model.basicmodel.concepts.AxValidationResult.ValidationResult;
+import org.onap.policy.common.utils.validation.Assertions;
+
+/**
+ * The key information on an {@link AxArtifactKey} key in an Apex policy model. Each {@link AxArtifactKey} must have an
+ * {@link AxKeyInfo} object. THe information held is the key's UUID and it's description.
+ *
+ * <p>Validation checks that all fields are defined and that the key is valid. It also observes that descriptions are
+ * blank and warns if the UUID is a zero UUID.
+ */
+public class AxKeyInfo extends AxConcept {
+ private static final long serialVersionUID = -4023935924068914308L;
+
+ private static final int UUID_BYTE_LENGTH_16 = 16;
+
+ /*
+ * This is not used for encryption/security, thus disabling sonar.
+ */
+ private static final Random sharedRandom = new Random(); // NOSONAR
+
+ private AxArtifactKey key;
+
+ @SerializedName("UUID")
+ private UUID uuid;
+
+ private String description;
+
+ /**
+ * The Default Constructor creates this concept with a NULL artifact key.
+ */
+ public AxKeyInfo() {
+ this(new AxArtifactKey());
+ }
+
+ /**
+ * Copy constructor.
+ *
+ * @param copyConcept the concept to copy from
+ */
+ public AxKeyInfo(final AxKeyInfo copyConcept) {
+ super(copyConcept);
+ }
+
+ /**
+ * Constructor to create this concept with the specified key.
+ *
+ * @param key the key of the concept
+ */
+ public AxKeyInfo(final AxArtifactKey key) {
+ this(key, UUID.randomUUID(), "Generated description for concept referred to by key \"" + key.getId() + "\"");
+ }
+
+ /**
+ * Constructor to create this concept and set all its fields.
+ *
+ * @param key the key of the concept
+ * @param uuid the UUID of the concept
+ * @param description the description of the concept
+ */
+ public AxKeyInfo(final AxArtifactKey key, final UUID uuid, final String description) {
+ super();
+ Assertions.argumentNotNull(key, "key may not be null");
+ Assertions.argumentNotNull(uuid, "uuid may not be null");
+ Assertions.argumentNotNull(description, "description may not be null");
+
+ this.key = key;
+ this.uuid = uuid;
+ this.description = description.trim();
+ }
+
+ /**
+ * {@inheritDoc}.
+ */
+ @Override
+ public AxArtifactKey getKey() {
+ return key;
+ }
+
+ /**
+ * {@inheritDoc}.
+ */
+ @Override
+ public List<AxKey> getKeys() {
+ return key.getKeys();
+ }
+
+ /**
+ * Sets the key of the concept.
+ *
+ * @param key the concept key
+ */
+ public void setKey(final AxArtifactKey key) {
+ Assertions.argumentNotNull(key, "key may not be null");
+ this.key = key;
+ }
+
+ /**
+ * Gets the UUID of the concept.
+ *
+ * @return the uuid of the concept
+ */
+ public UUID getUuid() {
+ return uuid;
+ }
+
+ /**
+ * Sets the UUID of the concept.
+ *
+ * @param uuid the uuid of the concept
+ */
+ public void setUuid(final UUID uuid) {
+ Assertions.argumentNotNull(uuid, "uuid may not be null");
+ this.uuid = uuid;
+ }
+
+ /**
+ * Gets the description of the concept.
+ *
+ * @return the description of the concept
+ */
+ public String getDescription() {
+ return description;
+ }
+
+ /**
+ * Sets the description of the concept.
+ *
+ * @param description the description of the concept
+ */
+ public void setDescription(final String description) {
+ Assertions.argumentNotNull(description, "description may not be null");
+ this.description = description.trim();
+ }
+
+ /**
+ * {@inheritDoc}.
+ */
+ @Override
+ public AxValidationResult validate(final AxValidationResult resultIn) {
+ AxValidationResult result = resultIn;
+
+ if (key.equals(AxArtifactKey.getNullKey())) {
+ result.addValidationMessage(
+ new AxValidationMessage(key, this.getClass(), ValidationResult.INVALID, "key is a null key"));
+ }
+
+ result = key.validate(result);
+
+ if (description.trim().length() == 0) {
+ result.addValidationMessage(new AxValidationMessage(key, this.getClass(), ValidationResult.OBSERVATION,
+ "description is blank"));
+ }
+
+ if (uuid.equals(new UUID(0, 0))) {
+ result.addValidationMessage(new AxValidationMessage(key, this.getClass(), ValidationResult.WARNING,
+ "UUID is a zero UUID: " + new UUID(0, 0)));
+ }
+
+ return result;
+ }
+
+ /**
+ * {@inheritDoc}.
+ */
+ @Override
+ public void clean() {
+ key.clean();
+ description = description.trim();
+ }
+
+ /**
+ * {@inheritDoc}.
+ */
+ @Override
+ public String toString() {
+ final var builder = new StringBuilder();
+ builder.append(this.getClass().getSimpleName());
+ builder.append(":(");
+ builder.append("artifactId=");
+ builder.append(key);
+ builder.append(",uuid=");
+ builder.append(uuid);
+ builder.append(",description=");
+ builder.append(description);
+ builder.append(")");
+ return builder.toString();
+ }
+
+ /**
+ * {@inheritDoc}.
+ */
+ @Override
+ public AxConcept copyTo(final AxConcept target) {
+ Assertions.argumentNotNull(target, "target may not be null");
+
+ final Object copyObject = target;
+ Assertions.instanceOf(copyObject, AxKeyInfo.class);
+
+ final AxKeyInfo copy = ((AxKeyInfo) copyObject);
+ copy.setKey(new AxArtifactKey(key));
+ copy.setUuid(UUID.fromString(uuid.toString()));
+ copy.setDescription(description);
+
+ return copy;
+ }
+
+ /**
+ * {@inheritDoc}.
+ */
+ @Override
+ public int hashCode() {
+ final var prime = 31;
+ var result = 1;
+ result = prime * result + key.hashCode();
+ result = prime * result + uuid.hashCode();
+ result = prime * result + description.hashCode();
+ return result;
+ }
+
+ /**
+ * {@inheritDoc}.
+ */
+ @Override
+ public boolean equals(final Object obj) {
+ if (obj == null) {
+ return false;
+ }
+ if (this == obj) {
+ return true;
+ }
+ if (getClass() != obj.getClass()) {
+ return false;
+ }
+
+ final AxKeyInfo other = (AxKeyInfo) obj;
+ if (!key.equals(other.key)) {
+ return false;
+ }
+ if (!uuid.equals(other.uuid)) {
+ return false;
+ }
+ return description.equals(description);
+ }
+
+ /**
+ * {@inheritDoc}.
+ */
+ @Override
+ public int compareTo(final AxConcept otherObj) {
+ if (otherObj == null) {
+ return -1;
+ }
+ if (this == otherObj) {
+ return 0;
+ }
+ if (getClass() != otherObj.getClass()) {
+ return this.hashCode() - otherObj.hashCode();
+ }
+
+ final AxKeyInfo other = (AxKeyInfo) otherObj;
+ if (!key.equals(other.key)) {
+ return key.compareTo(other.key);
+ }
+ if (!uuid.equals(other.uuid)) {
+ return uuid.compareTo(other.uuid);
+ }
+ return description.compareTo(other.description);
+ }
+
+ /**
+ * Generate a reproducible UUID for a given string seed.
+ *
+ * @param seed the seed
+ * @return the uuid
+ */
+ public static UUID generateReproducibleUuid(final String seed) {
+ var random = sharedRandom;
+ if (!StringUtils.isEmpty(seed)) {
+ /*
+ * This is not used for encryption/security, thus disabling sonar.
+ */
+ random = new Random(seed.hashCode()); // NOSONAR
+ }
+ final var array = new byte[UUID_BYTE_LENGTH_16];
+ random.nextBytes(array);
+ return UUID.nameUUIDFromBytes(array);
+ }
+}
diff --git a/model/src/main/java/org/onap/policy/apex/model/basicmodel/concepts/AxKeyInformation.java b/model/src/main/java/org/onap/policy/apex/model/basicmodel/concepts/AxKeyInformation.java
new file mode 100644
index 000000000..439b2960f
--- /dev/null
+++ b/model/src/main/java/org/onap/policy/apex/model/basicmodel/concepts/AxKeyInformation.java
@@ -0,0 +1,389 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * Copyright (C) 2016-2018 Ericsson. All rights reserved.
+ * Modifications Copyright (C) 2019-2022 Nordix Foundation.
+ * Modifications Copyright (C) 2021 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.apex.model.basicmodel.concepts;
+
+import java.util.List;
+import java.util.Map;
+import java.util.Map.Entry;
+import java.util.NavigableMap;
+import java.util.Set;
+import java.util.TreeMap;
+import java.util.TreeSet;
+import java.util.UUID;
+import org.onap.policy.apex.model.basicmodel.concepts.AxValidationResult.ValidationResult;
+import org.onap.policy.common.utils.validation.Assertions;
+
+/**
+ * The Class AxKeyInformation holds a map of the key information for the entire Apex model. All Apex models
+ * {@link AxModel} must have an {@link AxKeyInformation} field. The {@link AxKeyInformation} class implements the helper
+ * methods of the {@link AxConceptGetter} interface to allow {@link AxKeyInfo} instances to be retrieved by calling
+ * methods directly on this class without referencing the contained map.
+ *
+ * <p>Validation checks that the key is not null, that the key information map is not empty, that each key and value in
+ * the map is defined, that the key in each map entry matches the key if each entry value, and that no duplicate UUIDs
+ * exist. Each key information entry is then validated individually.
+ */
+public class AxKeyInformation extends AxConcept implements AxConceptGetter<AxKeyInfo> {
+ private static final long serialVersionUID = -2746380769017043888L;
+
+ private AxArtifactKey key;
+ private Map<AxArtifactKey, AxKeyInfo> keyInfoMap;
+
+ /**
+ * The Default Constructor creates this concept with a null key.
+ */
+ public AxKeyInformation() {
+ this(new AxArtifactKey());
+ }
+
+ /**
+ * Copy constructor.
+ *
+ * @param copyConcept the concept to copy from
+ */
+ public AxKeyInformation(final AxKeyInformation copyConcept) {
+ super(copyConcept);
+ }
+
+ /**
+ * Constructor to create this concept with the specified key.
+ *
+ * @param key the key of the concept
+ */
+ public AxKeyInformation(final AxArtifactKey key) {
+ this(key, new TreeMap<>());
+ }
+
+ /**
+ * Constructor to create this concept and set all its fields.
+ *
+ * @param key the key of the concept
+ * @param keyInfoMap the key info map of the concept
+ */
+ public AxKeyInformation(final AxArtifactKey key, final Map<AxArtifactKey, AxKeyInfo> keyInfoMap) {
+ super();
+ Assertions.argumentNotNull(key, "key may not be null");
+ Assertions.argumentNotNull(keyInfoMap, "keyInfoMap may not be null");
+
+ this.key = key;
+ this.keyInfoMap = new TreeMap<>();
+ this.keyInfoMap.putAll(keyInfoMap);
+ }
+
+ /**
+ * This method generates default key information for all keys found in the concept passed in as a parameter that do
+ * not already have key information.
+ *
+ * @param concept the concept for which to generate key information
+ */
+ public void generateKeyInfo(final AxConcept concept) {
+ for (final AxKey axKey : concept.getKeys()) {
+ if (!(axKey instanceof AxArtifactKey)) {
+ continue;
+ }
+
+ final AxArtifactKey artifactKey = (AxArtifactKey) axKey;
+
+ keyInfoMap.computeIfAbsent(artifactKey, unusedKey -> {
+ final var keyInfo = new AxKeyInfo(artifactKey);
+ // generate a reproducible UUID
+ keyInfo.setUuid(AxKeyInfo.generateReproducibleUuid(keyInfo.getId() + keyInfo.getDescription()));
+ return keyInfo;
+ });
+ }
+ }
+
+ /**
+ * {@inheritDoc}.
+ */
+ @Override
+ public AxArtifactKey getKey() {
+ return key;
+ }
+
+ /**
+ * {@inheritDoc}.
+ */
+ @Override
+ public List<AxKey> getKeys() {
+ final List<AxKey> keyList = key.getKeys();
+ keyList.addAll(keyInfoMap.keySet());
+
+ return keyList;
+ }
+
+ /**
+ * {@inheritDoc}.
+ */
+ @Override
+ public void buildReferences() {
+ keyInfoMap.values().stream().forEach(keyInfo -> keyInfo.buildReferences());
+ }
+
+ /**
+ * Sets the key of this concept.
+ *
+ * @param key the key of this concept
+ */
+ public void setKey(final AxArtifactKey key) {
+ Assertions.argumentNotNull(key, "key may not be null");
+ this.key = key;
+ }
+
+ /**
+ * Gets the key info map of this concept.
+ *
+ * @return the key info map of this concept
+ */
+ public Map<AxArtifactKey, AxKeyInfo> getKeyInfoMap() {
+ return keyInfoMap;
+ }
+
+ /**
+ * Sets the key info map of this concept.
+ *
+ * @param keyInfoMap the key info map of this concept
+ */
+ public void setKeyInfoMap(final Map<AxArtifactKey, AxKeyInfo> keyInfoMap) {
+ Assertions.argumentNotNull(keyInfoMap, "keyInfoMap may not be null");
+ this.keyInfoMap = new TreeMap<>();
+ this.keyInfoMap.putAll(keyInfoMap);
+ }
+
+ /**
+ * {@inheritDoc}.
+ */
+ @Override
+ public AxValidationResult validate(final AxValidationResult resultIn) {
+ AxValidationResult result = resultIn;
+
+ if (key.equals(AxArtifactKey.getNullKey())) {
+ result.addValidationMessage(
+ new AxValidationMessage(key, this.getClass(), ValidationResult.INVALID, "key is a null key"));
+ }
+
+ result = key.validate(result);
+
+ if (keyInfoMap.size() == 0) {
+ result.addValidationMessage(new AxValidationMessage(key, this.getClass(), ValidationResult.INVALID,
+ "keyInfoMap may not be empty"));
+ } else {
+ final Set<UUID> uuidSet = new TreeSet<>();
+
+ for (final Entry<AxArtifactKey, AxKeyInfo> keyInfoEntry : keyInfoMap.entrySet()) {
+ result = validateKeyInfoEntry(keyInfoEntry, uuidSet, result);
+ }
+ }
+
+ return result;
+ }
+
+ /**
+ * Validate a key information entry.
+ *
+ * @param keyInfoEntry the key information entry
+ * @param uuidSet the set of UUIDs encountered in validation so far, the UUID of this entry is added to the set
+ * @param result the validation result to append to
+ * @return The validation result
+ */
+ private AxValidationResult validateKeyInfoEntry(final Entry<AxArtifactKey, AxKeyInfo> keyInfoEntry,
+ final Set<UUID> uuidSet, AxValidationResult result) {
+ if (keyInfoEntry.getKey().equals(AxArtifactKey.getNullKey())) {
+ result.addValidationMessage(new AxValidationMessage(key, this.getClass(), ValidationResult.INVALID,
+ "key on keyInfoMap entry " + keyInfoEntry.getKey() + " may not be the null key"));
+ } else if (keyInfoEntry.getValue() == null) {
+ result.addValidationMessage(new AxValidationMessage(key, this.getClass(), ValidationResult.INVALID,
+ "value on keyInfoMap entry " + keyInfoEntry.getKey() + " may not be null"));
+ } else {
+ if (!keyInfoEntry.getKey().equals(keyInfoEntry.getValue().getKey())) {
+ result.addValidationMessage(new AxValidationMessage(key, this.getClass(), ValidationResult.INVALID,
+ "key on keyInfoMap entry " + keyInfoEntry.getKey() + " does not equal entry key "
+ + keyInfoEntry.getValue().getKey()));
+ }
+
+ result = keyInfoEntry.getValue().validate(result);
+
+ if (uuidSet.contains(keyInfoEntry.getValue().getUuid())) {
+ result.addValidationMessage(new AxValidationMessage(key, this.getClass(), ValidationResult.INVALID,
+ "duplicate UUID found on keyInfoMap entry " + keyInfoEntry.getKey() + ":"
+ + keyInfoEntry.getValue().getUuid()));
+ } else {
+ uuidSet.add(keyInfoEntry.getValue().getUuid());
+ }
+ }
+
+ return result;
+ }
+
+ /**
+ * {@inheritDoc}.
+ */
+ @Override
+ public void clean() {
+ key.clean();
+ for (final Entry<AxArtifactKey, AxKeyInfo> keyInfoEntry : keyInfoMap.entrySet()) {
+ keyInfoEntry.getKey().clean();
+ keyInfoEntry.getValue().clean();
+ }
+ }
+
+ /**
+ * {@inheritDoc}.
+ */
+ @Override
+ public String toString() {
+ final var builder = new StringBuilder();
+ builder.append(this.getClass().getSimpleName());
+ builder.append(":(");
+ builder.append("key=");
+ builder.append(key);
+ builder.append(",keyInfoMap=");
+ builder.append(keyInfoMap);
+ builder.append(")");
+ return builder.toString();
+ }
+
+ /**
+ * {@inheritDoc}.
+ */
+ @Override
+ public AxConcept copyTo(final AxConcept target) {
+ Assertions.argumentNotNull(target, "target may not be null");
+
+ final Object copyObject = target;
+ Assertions.instanceOf(copyObject, AxKeyInformation.class);
+
+ final AxKeyInformation copy = ((AxKeyInformation) copyObject);
+ copy.setKey(new AxArtifactKey(key));
+ final Map<AxArtifactKey, AxKeyInfo> newKeyInfoMap = new TreeMap<>();
+ for (final Entry<AxArtifactKey, AxKeyInfo> keyInfoMapEntry : keyInfoMap.entrySet()) {
+ newKeyInfoMap.put(new AxArtifactKey(keyInfoMapEntry.getKey()), new AxKeyInfo(keyInfoMapEntry.getValue()));
+ }
+ copy.setKeyInfoMap(newKeyInfoMap);
+
+ return copy;
+ }
+
+ /**
+ * {@inheritDoc}.
+ */
+ @Override
+ public int hashCode() {
+ final var prime = 31;
+ var result = 1;
+ result = prime * result + key.hashCode();
+ result = prime * result + keyInfoMap.hashCode();
+ return result;
+ }
+
+ /**
+ * {@inheritDoc}.
+ */
+ @Override
+ public boolean equals(final Object obj) {
+ if (obj == null) {
+ return false;
+ }
+ if (this == obj) {
+ return true;
+ }
+
+ if (getClass() != obj.getClass()) {
+ return false;
+ }
+
+ final AxKeyInformation other = (AxKeyInformation) obj;
+ if (!key.equals(other.key)) {
+ return false;
+ }
+ return keyInfoMap.equals(other.keyInfoMap);
+ }
+
+ /**
+ * {@inheritDoc}.
+ */
+ @Override
+ public int compareTo(final AxConcept otherObj) {
+ if (otherObj == null) {
+ return -1;
+ }
+ if (this == otherObj) {
+ return 0;
+ }
+ if (getClass() != otherObj.getClass()) {
+ return this.hashCode() - otherObj.hashCode();
+ }
+
+ final AxKeyInformation other = (AxKeyInformation) otherObj;
+ if (!key.equals(other.key)) {
+ return key.compareTo(other.key);
+ }
+ if (!keyInfoMap.equals(other.keyInfoMap)) {
+ return (keyInfoMap.hashCode() - other.keyInfoMap.hashCode());
+ }
+
+ return 0;
+ }
+
+ /**
+ * {@inheritDoc}.
+ */
+ @Override
+ public AxKeyInfo get(final AxArtifactKey conceptKey) {
+ return new AxConceptGetterImpl<>((NavigableMap<AxArtifactKey, AxKeyInfo>) keyInfoMap).get(conceptKey);
+ }
+
+ /**
+ * {@inheritDoc}.
+ */
+ @Override
+ public AxKeyInfo get(final String conceptKeyName) {
+ return new AxConceptGetterImpl<>((NavigableMap<AxArtifactKey, AxKeyInfo>) keyInfoMap).get(conceptKeyName);
+ }
+
+ /**
+ * {@inheritDoc}.
+ */
+ @Override
+ public AxKeyInfo get(final String conceptKeyName, final String conceptKeyVersion) {
+ return new AxConceptGetterImpl<>((NavigableMap<AxArtifactKey, AxKeyInfo>) keyInfoMap).get(conceptKeyName,
+ conceptKeyVersion);
+ }
+
+ /**
+ * {@inheritDoc}.
+ */
+ @Override
+ public Set<AxKeyInfo> getAll(final String conceptKeyName) {
+ return new AxConceptGetterImpl<>((NavigableMap<AxArtifactKey, AxKeyInfo>) keyInfoMap).getAll(conceptKeyName);
+ }
+
+ /**
+ * {@inheritDoc}.
+ */
+ @Override
+ public Set<AxKeyInfo> getAll(final String conceptKeyName, final String conceptKeyVersion) {
+ return new AxConceptGetterImpl<>((NavigableMap<AxArtifactKey, AxKeyInfo>) keyInfoMap).getAll(conceptKeyName,
+ conceptKeyVersion);
+ }
+}
diff --git a/model/src/main/java/org/onap/policy/apex/model/basicmodel/concepts/AxKeyUse.java b/model/src/main/java/org/onap/policy/apex/model/basicmodel/concepts/AxKeyUse.java
new file mode 100644
index 000000000..eb701e5b3
--- /dev/null
+++ b/model/src/main/java/org/onap/policy/apex/model/basicmodel/concepts/AxKeyUse.java
@@ -0,0 +1,221 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * Copyright (C) 2016-2018 Ericsson. All rights reserved.
+ * Modifications Copyright (C) 2019-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.apex.model.basicmodel.concepts;
+
+import java.util.List;
+import org.onap.policy.apex.model.basicmodel.concepts.AxValidationResult.ValidationResult;
+import org.onap.policy.common.utils.validation.Assertions;
+
+/**
+ * This class records a usage of a key in the system. When the list of keys being used by a concept is built using the
+ * getKeys() method of the {@link AxConcept} class, an instance of this class is created for every key occurrence. The
+ * list of keys returned by the getKeys() method is a list of {@link AxKeyUse} objects.
+ *
+ * <p>Validation checks that each key is valid.
+ */
+
+public class AxKeyUse extends AxKey {
+ private static final long serialVersionUID = 2007147220109881705L;
+
+ private AxKey usedKey;
+
+ /**
+ * The Default Constructor creates this concept with a null key.
+ */
+ public AxKeyUse() {
+ this(new AxArtifactKey());
+ }
+
+ /**
+ * Copy constructor.
+ *
+ * @param copyConcept the concept to copy from
+ */
+ public AxKeyUse(final AxKeyUse copyConcept) {
+ super(copyConcept);
+ }
+
+ /**
+ * This constructor creates an instance of this class, and holds a reference to a used key.
+ *
+ * @param usedKey a used key
+ */
+ public AxKeyUse(final AxKey usedKey) {
+ Assertions.argumentNotNull(usedKey, "usedKey may not be null");
+ this.usedKey = usedKey;
+ }
+
+ /**
+ * {@inheritDoc}.
+ */
+ @Override
+ public AxKey getKey() {
+ return usedKey;
+ }
+
+ /**
+ * {@inheritDoc}.
+ */
+ @Override
+ public List<AxKey> getKeys() {
+ return usedKey.getKeys();
+ }
+
+ /**
+ * {@inheritDoc}.
+ */
+ @Override
+ public String getId() {
+ return usedKey.getId();
+ }
+
+ /**
+ * Sets the key.
+ *
+ * @param key the key
+ */
+ public void setKey(final AxKey key) {
+ Assertions.argumentNotNull(key, "usedKey may not be null");
+ this.usedKey = key;
+ }
+
+ /**
+ * {@inheritDoc}.
+ */
+ @Override
+ public AxKey.Compatibility getCompatibility(final AxKey otherKey) {
+ return usedKey.getCompatibility(otherKey);
+ }
+
+ /**
+ * {@inheritDoc}.
+ */
+ @Override
+ public boolean isCompatible(final AxKey otherKey) {
+ return usedKey.isCompatible(otherKey);
+ }
+
+ /**
+ * {@inheritDoc}.
+ */
+ @Override
+ public AxValidationResult validate(final AxValidationResult result) {
+ if (usedKey.equals(AxArtifactKey.getNullKey())) {
+ result.addValidationMessage(new AxValidationMessage(usedKey, this.getClass(), ValidationResult.INVALID,
+ "usedKey is a null key"));
+ }
+ return usedKey.validate(result);
+ }
+
+ /**
+ * {@inheritDoc}.
+ */
+ @Override
+ public void clean() {
+ usedKey.clean();
+ }
+
+ /**
+ * {@inheritDoc}.
+ */
+ @Override
+ public String toString() {
+ final var builder = new StringBuilder();
+ builder.append(this.getClass().getSimpleName());
+ builder.append(":(");
+ builder.append("usedKey=");
+ builder.append(usedKey);
+ builder.append(")");
+ return builder.toString();
+ }
+
+ /**
+ * {@inheritDoc}.
+ */
+ @Override
+ public AxConcept copyTo(final AxConcept target) {
+ Assertions.argumentNotNull(target, "target may not be null");
+
+ final Object copyObject = target;
+ Assertions.instanceOf(copyObject, AxKeyUse.class);
+
+ final AxKeyUse copy = ((AxKeyUse) copyObject);
+ try {
+ copy.usedKey = usedKey.getClass().getDeclaredConstructor().newInstance();
+ } catch (final Exception e) {
+ throw new ApexRuntimeException("error copying concept key: " + e.getMessage(), e);
+ }
+ usedKey.copyTo(copy.usedKey);
+
+ return copy;
+ }
+
+ /**
+ * {@inheritDoc}.
+ */
+ @Override
+ public int hashCode() {
+ final var prime = 31;
+ var result = 1;
+ result = prime * result + usedKey.hashCode();
+ return result;
+ }
+
+ /**
+ * {@inheritDoc}.
+ */
+ @Override
+ public boolean equals(final Object obj) {
+ if (obj == null) {
+ throw new IllegalArgumentException("comparison object may not be null");
+ }
+
+ if (this == obj) {
+ return true;
+ }
+ if (getClass() != obj.getClass()) {
+ return false;
+ }
+
+ final AxKeyUse other = (AxKeyUse) obj;
+ return usedKey.equals(other.usedKey);
+ }
+
+ /**
+ * {@inheritDoc}.
+ */
+ @Override
+ public int compareTo(final AxConcept otherObj) {
+ Assertions.argumentNotNull(otherObj, "comparison object may not be null");
+
+ if (this == otherObj) {
+ return 0;
+ }
+ if (getClass() != otherObj.getClass()) {
+ return this.hashCode() - otherObj.hashCode();
+ }
+
+ final AxKeyUse other = (AxKeyUse) otherObj;
+
+ return usedKey.compareTo(other.usedKey);
+ }
+}
diff --git a/model/src/main/java/org/onap/policy/apex/model/basicmodel/concepts/AxModel.java b/model/src/main/java/org/onap/policy/apex/model/basicmodel/concepts/AxModel.java
new file mode 100644
index 000000000..ce52b147f
--- /dev/null
+++ b/model/src/main/java/org/onap/policy/apex/model/basicmodel/concepts/AxModel.java
@@ -0,0 +1,423 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * Copyright (C) 2016-2018 Ericsson. All rights reserved.
+ * Modifications Copyright (C) 2019-2022 Nordix Foundation.
+ * Modifications Copyright (C) 2021 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.apex.model.basicmodel.concepts;
+
+import java.util.List;
+import java.util.Set;
+import java.util.TreeSet;
+import org.onap.policy.apex.model.basicmodel.concepts.AxValidationResult.ValidationResult;
+import org.onap.policy.apex.model.basicmodel.service.ModelService;
+import org.onap.policy.common.utils.validation.Assertions;
+
+/**
+ * This class is the base class for all models in Apex. All model classes inherit from this model so all models must
+ * have a key and have key information.
+ *
+ * <p>Validation checks that the model key is valid. It goes on to check for null keys and checks each key for
+ * uniqueness in the model. A check is carried out to ensure that an {@link AxKeyInfo} instance exists for every
+ * {@link AxArtifactKey} key. For each {@link AxReferenceKey} instance, a check is made that its parent and local name
+ * are nut null and that a {@link AxKeyInfo} entry exists for its parent. Then a check is made that each used
+ * {@link AxArtifactKey} and {@link AxReferenceKey} usage references a key that exists. Finally, a check is made to
+ * ensure that an {@link AxArtifactKey} instance exists for every {@link AxKeyInfo} instance.
+ */
+public class AxModel extends AxConcept {
+ private static final String IS_A_NULL_KEY = " is a null key";
+
+ private static final long serialVersionUID = -771659065637205430L;
+
+ private AxArtifactKey key;
+ private AxKeyInformation keyInformation;
+
+ /**
+ * The Default Constructor creates this concept with a NULL artifact key.
+ */
+ public AxModel() {
+ this(new AxArtifactKey());
+ }
+
+ /**
+ * Copy constructor.
+ *
+ * @param copyConcept the concept to copy from
+ */
+ public AxModel(final AxModel copyConcept) {
+ super(copyConcept);
+ }
+
+ /**
+ * Constructor to create this concept with the specified key.
+ *
+ * @param key the key of this concept
+ */
+ public AxModel(final AxArtifactKey key) {
+ this(key, new AxKeyInformation(new AxArtifactKey(key.getName() + "_KeyInfo", key.getVersion())));
+ }
+
+ /**
+ * Constructor to create this concept and set all its fields.
+ *
+ * @param key the key of this concept
+ * @param keyInformation the key information of this concept
+ */
+ public AxModel(final AxArtifactKey key, final AxKeyInformation keyInformation) {
+ super();
+ Assertions.argumentNotNull(key, "key may not be null");
+ Assertions.argumentNotNull(keyInformation, "keyInformation may not be null");
+
+ this.key = key;
+ this.keyInformation = keyInformation;
+ }
+
+ /**
+ * Registers this model with the {@link ModelService}. All models are registered with the model service so that
+ * models can be references from anywhere in the Apex system without being passed as references through deep call
+ * chains.
+ */
+ public void register() {
+ ModelService.registerModel(AxKeyInformation.class, getKeyInformation());
+ }
+
+ /**
+ * {@inheritDoc}.
+ */
+ @Override
+ public AxArtifactKey getKey() {
+ return key;
+ }
+
+ /**
+ * {@inheritDoc}.
+ */
+ @Override
+ public List<AxKey> getKeys() {
+ final List<AxKey> keyList = key.getKeys();
+
+ // We just add the key for the KeyInformation itself. We don't add the
+ // keys from key information because
+ // that is a list of key information for existing keys
+ keyList.add(keyInformation.getKey());
+
+ return keyList;
+ }
+
+ /**
+ * Sets the key of this concept.
+ *
+ * @param key the key of this concept
+ */
+ public void setKey(final AxArtifactKey key) {
+ Assertions.argumentNotNull(key, "key may not be null");
+ this.key = key;
+ }
+
+ /**
+ * Gets the key information of this concept.
+ *
+ * @return the key information of this concept
+ */
+ public AxKeyInformation getKeyInformation() {
+ return keyInformation;
+ }
+
+ /**
+ * Sets the key information of this concept.
+ *
+ * @param keyInformation the key information of this concept
+ */
+ public void setKeyInformation(final AxKeyInformation keyInformation) {
+ Assertions.argumentNotNull(keyInformation, "keyInformation may not be null");
+ this.keyInformation = keyInformation;
+ }
+
+ /**
+ * {@inheritDoc}.
+ */
+ @Override
+ public AxValidationResult validate(final AxValidationResult resultIn) {
+ AxValidationResult result = resultIn;
+
+ if (key.equals(AxArtifactKey.getNullKey())) {
+ result.addValidationMessage(new AxValidationMessage(key, this.getClass(), ValidationResult.INVALID,
+ "key is a null key"));
+ }
+
+ result = key.validate(result);
+ result = keyInformation.validate(result);
+
+ // Key consistency check
+ final Set<AxArtifactKey> artifactKeySet = new TreeSet<>();
+ final Set<AxReferenceKey> referenceKeySet = new TreeSet<>();
+ final Set<AxKeyUse> usedKeySet = new TreeSet<>();
+
+ for (final AxKey axKey : this.getKeys()) {
+ // Check for the two type of keys we have
+ if (axKey instanceof AxArtifactKey) {
+ result = validateArtifactKeyInModel((AxArtifactKey) axKey, artifactKeySet, result);
+ } else if (axKey instanceof AxReferenceKey) {
+ result = validateReferenceKeyInModel((AxReferenceKey) axKey, referenceKeySet, result);
+ } else {
+ // It must be an AxKeyUse, nothing else is legal
+ usedKeySet.add((AxKeyUse) axKey);
+ }
+ }
+
+ // Check all reference keys have correct parent keys
+ for (final AxReferenceKey referenceKey : referenceKeySet) {
+ if (!artifactKeySet.contains(referenceKey.getParentArtifactKey())) {
+ result.addValidationMessage(new AxValidationMessage(key, this.getClass(), ValidationResult.INVALID,
+ "parent artifact key not found for reference key " + referenceKey));
+ }
+ }
+
+ result = validateKeyUses(usedKeySet, artifactKeySet, referenceKeySet, result);
+
+ // Check key information for unused key information
+ for (final AxArtifactKey keyInfoKey : keyInformation.getKeyInfoMap().keySet()) {
+ if (!artifactKeySet.contains(keyInfoKey)) {
+ result.addValidationMessage(new AxValidationMessage(keyInfoKey, this.getClass(),
+ ValidationResult.WARNING, "key not found for key information entry"));
+ }
+ }
+
+ return result;
+ }
+
+ /**
+ * Check for consistent usage of an artifact key in the model.
+ *
+ * @param artifactKey The artifact key to check
+ * @param artifactKeySet The set of artifact keys encountered so far, this key is appended to the set
+ * @param result The validation result to append to
+ * @return the result of the validation
+ */
+ private AxValidationResult validateArtifactKeyInModel(final AxArtifactKey artifactKey,
+ final Set<AxArtifactKey> artifactKeySet, final AxValidationResult result) {
+ // Null key check
+ if (artifactKey.equals(AxArtifactKey.getNullKey())) {
+ result.addValidationMessage(new AxValidationMessage(key, this.getClass(), ValidationResult.INVALID,
+ "key " + artifactKey + IS_A_NULL_KEY));
+ }
+
+ // Null key name start check
+ if (artifactKey.getName().toUpperCase().startsWith(AxKey.NULL_KEY_NAME)) {
+ result.addValidationMessage(new AxValidationMessage(key, this.getClass(), ValidationResult.INVALID,
+ "key " + artifactKey + " name starts with keyword " + AxKey.NULL_KEY_NAME));
+ }
+
+ // Unique key check
+ if (artifactKeySet.contains(artifactKey)) {
+ result.addValidationMessage(new AxValidationMessage(key, this.getClass(), ValidationResult.INVALID,
+ "duplicate key " + artifactKey + " found"));
+ } else {
+ artifactKeySet.add(artifactKey);
+ }
+
+ // Key information check
+ if (!keyInformation.getKeyInfoMap().containsKey(artifactKey)) {
+ result.addValidationMessage(new AxValidationMessage(key, this.getClass(), ValidationResult.INVALID,
+ "key information not found for key " + artifactKey));
+ }
+
+ return result;
+ }
+
+ /**
+ * Check for consistent usage of a reference key in the model.
+ *
+ * @param referenceKey The reference key to check
+ * @param referenceKeySet The set of reference keys encountered so far, this key is appended to the set
+ * @param result The validation result to append to
+ * @return the result of the validation
+ */
+ private AxValidationResult validateReferenceKeyInModel(final AxReferenceKey referenceKey,
+ final Set<AxReferenceKey> referenceKeySet, final AxValidationResult result) {
+ // Null key check
+ if (referenceKey.equals(AxReferenceKey.getNullKey())) {
+ result.addValidationMessage(new AxValidationMessage(key, this.getClass(), ValidationResult.INVALID,
+ "key " + referenceKey + IS_A_NULL_KEY));
+ }
+
+ // Null parent key check
+ if (referenceKey.getParentArtifactKey().equals(AxArtifactKey.getNullKey())) {
+ result.addValidationMessage(new AxValidationMessage(key, this.getClass(), ValidationResult.INVALID,
+ "parent artifact key of key " + referenceKey + IS_A_NULL_KEY));
+ }
+
+ // Null local name check
+ if (referenceKey.getLocalName().equals(AxKey.NULL_KEY_NAME)) {
+ result.addValidationMessage(new AxValidationMessage(key, this.getClass(), ValidationResult.INVALID,
+ "key " + referenceKey + " has a null local name"));
+ }
+
+ // Null key name start check
+ if (referenceKey.getParentArtifactKey().getName().toUpperCase().startsWith(AxKey.NULL_KEY_NAME)) {
+ result.addValidationMessage(new AxValidationMessage(key, this.getClass(), ValidationResult.INVALID,
+ "key " + referenceKey + " parent name starts with keyword " + AxKey.NULL_KEY_NAME));
+ }
+
+ // Unique key check
+ if (referenceKeySet.contains(referenceKey)) {
+ result.addValidationMessage(new AxValidationMessage(key, this.getClass(), ValidationResult.INVALID,
+ "duplicate key " + referenceKey + " found"));
+ } else {
+ referenceKeySet.add(referenceKey);
+ }
+
+ // Key information check
+ if (!keyInformation.getKeyInfoMap().containsKey(referenceKey.getParentArtifactKey())) {
+ result.addValidationMessage(new AxValidationMessage(key, this.getClass(), ValidationResult.INVALID,
+ "key information not found for parent key of key " + referenceKey));
+ }
+
+ return result;
+ }
+
+ /**
+ * Check for consistent usage of cross-key references in the model.
+ *
+ * @param usedKeySet The set of all keys used in the model
+ * @param artifactKeySet The set of artifact keys encountered so far, this key is appended to the set
+ * @param referenceKeySet The set of reference keys encountered so far, this key is appended to the set
+ * @param result The validation result to append to
+ * @return the result of the validation
+ */
+ private AxValidationResult validateKeyUses(final Set<AxKeyUse> usedKeySet, final Set<AxArtifactKey> artifactKeySet,
+ final Set<AxReferenceKey> referenceKeySet, final AxValidationResult result) {
+ // Check all key uses
+ for (final AxKeyUse usedKey : usedKeySet) {
+ if (usedKey.getKey() instanceof AxArtifactKey) {
+ // AxArtifact key usage, check the key exists
+ if (!artifactKeySet.contains(usedKey.getKey())) {
+ result.addValidationMessage(new AxValidationMessage(usedKey.getKey(), this.getClass(),
+ ValidationResult.INVALID, "an artifact key used in the model is not defined"));
+ }
+ } else {
+ // AxReference key usage, check the key exists
+ if (!referenceKeySet.contains(usedKey.getKey())) {
+ result.addValidationMessage(new AxValidationMessage(usedKey.getKey(), this.getClass(),
+ ValidationResult.INVALID, "a reference key used in the model is not defined"));
+ }
+ }
+ }
+
+ return result;
+ }
+
+ /**
+ * {@inheritDoc}.
+ */
+ @Override
+ public void clean() {
+ key.clean();
+ keyInformation.clean();
+ }
+
+ /**
+ * {@inheritDoc}.
+ */
+ @Override
+ public String toString() {
+ final var builder = new StringBuilder();
+ builder.append(this.getClass().getSimpleName());
+ builder.append(":(");
+ builder.append("key=");
+ builder.append(key);
+ builder.append(",keyInformation=");
+ builder.append(keyInformation);
+ builder.append(")");
+ return builder.toString();
+ }
+
+ /**
+ * {@inheritDoc}.
+ */
+ @Override
+ public AxConcept copyTo(final AxConcept target) {
+ Assertions.argumentNotNull(target, "target may not be null");
+
+ final Object copyObject = target;
+ Assertions.instanceOf(copyObject, AxModel.class);
+
+ final AxModel copy = ((AxModel) copyObject);
+ copy.setKey(new AxArtifactKey(key));
+ copy.setKeyInformation(new AxKeyInformation(keyInformation));
+
+ return copy;
+ }
+
+ /**
+ * {@inheritDoc}.
+ */
+ @Override
+ public int hashCode() {
+ final var prime = 31;
+ var result = 1;
+ result = prime * result + key.hashCode();
+ result = prime * result + keyInformation.hashCode();
+ return result;
+ }
+
+ /**
+ * {@inheritDoc}.
+ */
+ @Override
+ public boolean equals(final Object obj) {
+ if (obj == null) {
+ return false;
+ }
+ if (this == obj) {
+ return true;
+ }
+ if (getClass() != obj.getClass()) {
+ return false;
+ }
+
+ final AxModel other = (AxModel) obj;
+ if (!key.equals(other.key)) {
+ return false;
+ }
+ return keyInformation.equals(other.keyInformation);
+ }
+
+ /**
+ * {@inheritDoc}.
+ */
+ @Override
+ public int compareTo(final AxConcept otherObj) {
+ if (otherObj == null) {
+ return -1;
+ }
+ if (this == otherObj) {
+ return 0;
+ }
+ if (getClass() != otherObj.getClass()) {
+ return this.hashCode() - otherObj.hashCode();
+ }
+
+ final AxModel other = (AxModel) otherObj;
+ if (!key.equals(other.key)) {
+ return key.compareTo(other.key);
+ }
+ return keyInformation.compareTo(other.keyInformation);
+ }
+}
diff --git a/model/src/main/java/org/onap/policy/apex/model/basicmodel/concepts/AxReferenceKey.java b/model/src/main/java/org/onap/policy/apex/model/basicmodel/concepts/AxReferenceKey.java
new file mode 100644
index 000000000..b37bc47e4
--- /dev/null
+++ b/model/src/main/java/org/onap/policy/apex/model/basicmodel/concepts/AxReferenceKey.java
@@ -0,0 +1,527 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * Copyright (C) 2016-2018 Ericsson. All rights reserved.
+ * Modifications Copyright (C) 2019-2022 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.apex.model.basicmodel.concepts;
+
+import java.util.ArrayList;
+import java.util.List;
+import org.onap.policy.apex.model.basicmodel.concepts.AxValidationResult.ValidationResult;
+import org.onap.policy.common.utils.validation.Assertions;
+
+/**
+ * A reference key identifies entities in the system that are contained in other entities. Every contained concept in
+ * the system must have an {@link AxReferenceKey} to identify it. Non-contained first order concepts are identified
+ * using an {@link AxArtifactKey} key.
+ *
+ * <p>An {@link AxReferenceKey} contains an {@link AxArtifactKey} key reference to the first order entity that contains
+ * it. The local name of the reference key must uniquely identify the referenced concept among those concepts contained
+ * in the reference key's parent. In other words, if a parent concept has more than one child, the local name in the key
+ * of all its children must be unique.
+ *
+ * <p>If a reference key's parent is itself a reference key, then the parent's local name must be set in the reference
+ * key. If the parent is a first order concept, then the parent's local name in the key will be set to NULL.
+ *
+ * <p>Key validation checks that the parent name and parent version fields match the NAME_REGEXP and
+ * VERSION_REGEXP regular expressions respectively and that the local name fields match the
+ * LOCAL_NAME_REGEXP regular expression.
+ */
+public class AxReferenceKey extends AxKey {
+ private static final String PARENT_KEY_NAME = "parentKeyName";
+ private static final String PARENT_KEY_VERSION = "parentKeyVersion";
+ private static final String PARENT_LOCAL_NAME = "parentLocalName";
+ private static final String LOCAL_NAME = "localName";
+
+ private static final long serialVersionUID = 8932717618579392561L;
+
+ /**
+ * Regular expression to specify the structure of local names in reference keys.
+ */
+ public static final String LOCAL_NAME_REGEXP = "[A-Za-z0-9\\-_\\.]+|^$";
+
+ /**
+ * Regular expression to specify the structure of IDs in reference keys.
+ */
+ public static final String REFERENCE_KEY_ID_REGEXP =
+ "[A-Za-z0-9\\-_]+:[0-9].[0-9].[0-9]:[A-Za-z0-9\\-_]+:[A-Za-z0-9\\-_]+";
+
+ private static final int PARENT_NAME_FIELD = 0;
+ private static final int PARENT_VERSION_FIELD = 1;
+ private static final int PARENT_LOCAL_NAME_FIELD = 2;
+ private static final int LOCAL_NAME_FIELD = 3;
+
+ private String parentKeyName;
+ private String parentKeyVersion;
+ private String parentLocalName;
+ private String localName;
+
+ /**
+ * The default constructor creates a null reference key.
+ */
+ public AxReferenceKey() {
+ this(NULL_KEY_NAME, NULL_KEY_VERSION, NULL_KEY_NAME, NULL_KEY_NAME);
+ }
+
+ /**
+ * The Copy Constructor creates a key by copying another key.
+ *
+ * @param referenceKey the reference key to copy from
+ */
+ public AxReferenceKey(final AxReferenceKey referenceKey) {
+ this(referenceKey.getParentKeyName(), referenceKey.getParentKeyVersion(), referenceKey.getParentLocalName(),
+ referenceKey.getLocalName());
+ }
+
+ /**
+ * Constructor to create a null reference key for the specified parent artifact key.
+ *
+ * @param axArtifactKey the parent artifact key of this reference key
+ */
+ public AxReferenceKey(final AxArtifactKey axArtifactKey) {
+ this(axArtifactKey.getName(), axArtifactKey.getVersion(), NULL_KEY_NAME, NULL_KEY_NAME);
+ }
+
+ /**
+ * Constructor to create a reference key for the given parent artifact key with the given local name.
+ *
+ * @param axArtifactKey the parent artifact key of this reference key
+ * @param localName the local name of this reference key
+ */
+ public AxReferenceKey(final AxArtifactKey axArtifactKey, final String localName) {
+ this(axArtifactKey, NULL_KEY_NAME, localName);
+ }
+
+ /**
+ * Constructor to create a reference key for the given parent reference key with the given local name.
+ *
+ * @param parentReferenceKey the parent reference key of this reference key
+ * @param localName the local name of this reference key
+ */
+ public AxReferenceKey(final AxReferenceKey parentReferenceKey, final String localName) {
+ this(parentReferenceKey.getParentArtifactKey(), parentReferenceKey.getLocalName(), localName);
+ }
+
+ /**
+ * Constructor to create a reference key for the given parent reference key (specified by the parent reference key's
+ * artifact key and local name) with the given local name.
+ *
+ * @param axArtifactKey the artifact key of the parent reference key of this reference key
+ * @param parentLocalName the local name of the parent reference key of this reference key
+ * @param localName the local name of this reference key
+ */
+ public AxReferenceKey(final AxArtifactKey axArtifactKey, final String parentLocalName, final String localName) {
+ this(axArtifactKey.getName(), axArtifactKey.getVersion(), parentLocalName, localName);
+ }
+
+ /**
+ * Constructor to create a reference key for the given parent artifact key (specified by the parent artifact key's
+ * name and version) with the given local name.
+ *
+ * @param parentKeyName the name of the parent artifact key of this reference key
+ * @param parentKeyVersion the version of the parent artifact key of this reference key
+ * @param localName the local name of this reference key
+ */
+ public AxReferenceKey(final String parentKeyName, final String parentKeyVersion, final String localName) {
+ this(parentKeyName, parentKeyVersion, NULL_KEY_NAME, localName);
+ }
+
+ /**
+ * Constructor to create a reference key for the given parent key (specified by the parent key's name, version nad
+ * local name) with the given local name.
+ *
+ * @param parentKeyName the parent key name of this reference key
+ * @param parentKeyVersion the parent key version of this reference key
+ * @param parentLocalName the parent local name of this reference key
+ * @param localName the local name of this reference key
+ */
+ public AxReferenceKey(final String parentKeyName, final String parentKeyVersion, final String parentLocalName,
+ final String localName) {
+ super();
+ this.parentKeyName = Assertions.validateStringParameter(PARENT_KEY_NAME, parentKeyName, NAME_REGEXP);
+ this.parentKeyVersion = Assertions.validateStringParameter(PARENT_KEY_VERSION, parentKeyVersion,
+ VERSION_REGEXP);
+ this.parentLocalName = Assertions.validateStringParameter(PARENT_LOCAL_NAME, parentLocalName,
+ LOCAL_NAME_REGEXP);
+ this.localName = Assertions.validateStringParameter(LOCAL_NAME, localName, LOCAL_NAME_REGEXP);
+ }
+
+ /**
+ * Constructor to create a key from the specified key ID.
+ *
+ * @param id the key ID in a format that respects the KEY_ID_REGEXP
+ */
+ public AxReferenceKey(final String id) {
+ final var conditionedId = Assertions.validateStringParameter("id", id, REFERENCE_KEY_ID_REGEXP);
+
+ // Split on colon, if the id passes the regular expression test above
+ // it'll have just three colons separating the parent name,
+ // parent version, parent local name, and and local name
+ // No need for range checks or size checks on the array
+ final String[] nameVersionNameArray = conditionedId.split(":");
+
+ // Initiate the new key
+ parentKeyName = Assertions.validateStringParameter(PARENT_KEY_NAME, nameVersionNameArray[PARENT_NAME_FIELD],
+ NAME_REGEXP);
+ parentKeyVersion = Assertions.validateStringParameter(PARENT_KEY_VERSION,
+ nameVersionNameArray[PARENT_VERSION_FIELD], VERSION_REGEXP);
+ parentLocalName = Assertions.validateStringParameter(PARENT_LOCAL_NAME,
+ nameVersionNameArray[PARENT_LOCAL_NAME_FIELD], LOCAL_NAME_REGEXP);
+ localName = Assertions.validateStringParameter(LOCAL_NAME, nameVersionNameArray[LOCAL_NAME_FIELD],
+ LOCAL_NAME_REGEXP);
+ }
+
+ /**
+ * Get a null reference key.
+ *
+ * @return a null reference key
+ */
+ public static AxReferenceKey getNullKey() {
+ return new AxReferenceKey(AxKey.NULL_KEY_NAME, AxKey.NULL_KEY_VERSION, AxKey.NULL_KEY_NAME,
+ AxKey.NULL_KEY_NAME);
+ }
+
+ /**
+ * {@inheritDoc}.
+ */
+ @Override
+ public AxReferenceKey getKey() {
+ return this;
+ }
+
+ /**
+ * {@inheritDoc}.
+ */
+ @Override
+ public List<AxKey> getKeys() {
+ final List<AxKey> keyList = new ArrayList<>();
+ keyList.add(getKey());
+ return keyList;
+ }
+
+ /**
+ * {@inheritDoc}.
+ */
+ @Override
+ public String getId() {
+ return parentKeyName + ':' + parentKeyVersion + ':' + parentLocalName + ':' + localName;
+ }
+
+ /**
+ * Gets the parent artifact key of this reference key.
+ *
+ * @return the parent artifact key of this reference key
+ */
+ public AxArtifactKey getParentArtifactKey() {
+ return new AxArtifactKey(parentKeyName, parentKeyVersion);
+ }
+
+ /**
+ * Gets the parent reference key of this reference key.
+ *
+ * @return the parent reference key of this reference key
+ */
+ public AxReferenceKey getParentReferenceKey() {
+ return new AxReferenceKey(parentKeyName, parentKeyVersion, parentLocalName);
+ }
+
+ /**
+ * Sets the parent artifact key of this reference key.
+ *
+ * @param parentKey the parent artifact key of this reference key
+ */
+ public void setParentArtifactKey(final AxArtifactKey parentKey) {
+ Assertions.argumentNotNull(parentKey, "parentKey may not be null");
+
+ parentKeyName = parentKey.getName();
+ parentKeyVersion = parentKey.getVersion();
+ parentLocalName = NULL_KEY_NAME;
+ }
+
+ /**
+ * Sets the parent reference key of this reference key.
+ *
+ * @param parentKey the parent reference key of this reference key
+ */
+ public void setParentReferenceKey(final AxReferenceKey parentKey) {
+ Assertions.argumentNotNull(parentKey, "parentKey may not be null");
+
+ parentKeyName = parentKey.getParentKeyName();
+ parentKeyVersion = parentKey.getParentKeyVersion();
+ parentLocalName = parentKey.getLocalName();
+ }
+
+ /**
+ * Gets the parent key name of this reference key.
+ *
+ * @return the parent key name of this reference key
+ */
+ public String getParentKeyName() {
+ return parentKeyName;
+ }
+
+ /**
+ * Sets the parent key name of this reference key.
+ *
+ * @param parentKeyName the parent key name of this reference key
+ */
+ public void setParentKeyName(final String parentKeyName) {
+ this.parentKeyName = Assertions.validateStringParameter(PARENT_KEY_NAME, parentKeyName, NAME_REGEXP);
+ }
+
+ /**
+ * Gets the parent key version of this reference key.
+ *
+ * @return the parent key version of this reference key
+ */
+ public String getParentKeyVersion() {
+ return parentKeyVersion;
+ }
+
+ /**
+ * Sets the parent key version of this reference key.
+ *
+ * @param parentKeyVersion the parent key version of this reference key
+ */
+ public void setParentKeyVersion(final String parentKeyVersion) {
+ this.parentKeyVersion = Assertions.validateStringParameter(PARENT_KEY_VERSION, parentKeyVersion,
+ VERSION_REGEXP);
+ }
+
+ /**
+ * Gets the parent local name of this reference key.
+ *
+ * @return the parent local name of this reference key
+ */
+ public String getParentLocalName() {
+ return parentLocalName;
+ }
+
+ /**
+ * Sets the parent local name of this reference key.
+ *
+ * @param parentLocalName the parent local name of this reference key
+ */
+ public void setParentLocalName(final String parentLocalName) {
+ this.parentLocalName = Assertions.validateStringParameter(PARENT_LOCAL_NAME, parentLocalName,
+ LOCAL_NAME_REGEXP);
+ }
+
+ /**
+ * Gets the local name of this reference key.
+ *
+ * @return the local name of this reference key
+ */
+ public String getLocalName() {
+ return localName;
+ }
+
+ /**
+ * Sets the local name of this reference key.
+ *
+ * @param localName the local name of this reference key
+ */
+ public void setLocalName(final String localName) {
+ this.localName = Assertions.validateStringParameter(LOCAL_NAME, localName, LOCAL_NAME_REGEXP);
+ }
+
+ /**
+ * {@inheritDoc}.
+ */
+ @Override
+ public AxKey.Compatibility getCompatibility(final AxKey otherKey) {
+ if (!(otherKey instanceof AxReferenceKey)) {
+ return Compatibility.DIFFERENT;
+ }
+ final AxReferenceKey otherReferenceKey = (AxReferenceKey) otherKey;
+
+ return this.getParentArtifactKey().getCompatibility(otherReferenceKey.getParentArtifactKey());
+ }
+
+ /**
+ * {@inheritDoc}.
+ */
+ @Override
+ public boolean isCompatible(final AxKey otherKey) {
+ if (!(otherKey instanceof AxReferenceKey)) {
+ return false;
+ }
+ final AxReferenceKey otherReferenceKey = (AxReferenceKey) otherKey;
+
+ return this.getParentArtifactKey().isCompatible(otherReferenceKey.getParentArtifactKey());
+ }
+
+ /**
+ * {@inheritDoc}.
+ */
+ @Override
+ public AxValidationResult validate(final AxValidationResult result) {
+ final var parentNameValidationErrorMessage = Assertions.getStringParameterValidationMessage(PARENT_KEY_NAME,
+ parentKeyName, NAME_REGEXP);
+ if (parentNameValidationErrorMessage != null) {
+ result.addValidationMessage(new AxValidationMessage(this, this.getClass(), ValidationResult.INVALID,
+ "parentKeyName invalid-" + parentNameValidationErrorMessage));
+ }
+
+ final var parentKeyVersionValidationErrorMessage = Assertions
+ .getStringParameterValidationMessage(PARENT_KEY_VERSION, parentKeyVersion, VERSION_REGEXP);
+ if (parentKeyVersionValidationErrorMessage != null) {
+ result.addValidationMessage(new AxValidationMessage(this, this.getClass(), ValidationResult.INVALID,
+ "parentKeyVersion invalid-" + parentKeyVersionValidationErrorMessage));
+ }
+
+ final var parentLocalNameValidationErrorMessage = Assertions
+ .getStringParameterValidationMessage(PARENT_LOCAL_NAME, parentLocalName, LOCAL_NAME_REGEXP);
+ if (parentLocalNameValidationErrorMessage != null) {
+ result.addValidationMessage(new AxValidationMessage(this, this.getClass(), ValidationResult.INVALID,
+ "parentLocalName invalid-" + parentLocalNameValidationErrorMessage));
+ }
+
+ final var localNameValidationErrorMessage = Assertions.getStringParameterValidationMessage(LOCAL_NAME,
+ localName, LOCAL_NAME_REGEXP);
+ if (localNameValidationErrorMessage != null) {
+ result.addValidationMessage(new AxValidationMessage(this, this.getClass(), ValidationResult.INVALID,
+ "localName invalid-" + localNameValidationErrorMessage));
+ }
+
+ return result;
+ }
+
+ /**
+ * {@inheritDoc}.
+ */
+ @Override
+ public void clean() {
+ parentKeyName = Assertions.validateStringParameter(PARENT_KEY_NAME, parentKeyName, NAME_REGEXP);
+ parentKeyVersion = Assertions.validateStringParameter(PARENT_KEY_VERSION, parentKeyVersion, VERSION_REGEXP);
+ parentLocalName = Assertions.validateStringParameter(PARENT_LOCAL_NAME, parentLocalName, LOCAL_NAME_REGEXP);
+ localName = Assertions.validateStringParameter(LOCAL_NAME, localName, LOCAL_NAME_REGEXP);
+ }
+
+ /**
+ * {@inheritDoc}.
+ */
+ @Override
+ public String toString() {
+ final var builder = new StringBuilder();
+ builder.append(this.getClass().getSimpleName());
+ builder.append(":(");
+ builder.append("parentKeyName=");
+ builder.append(parentKeyName);
+ builder.append(",parentKeyVersion=");
+ builder.append(parentKeyVersion);
+ builder.append(",parentLocalName=");
+ builder.append(parentLocalName);
+ builder.append(",localName=");
+ builder.append(localName);
+ builder.append(")");
+ return builder.toString();
+ }
+
+ /**
+ * {@inheritDoc}.
+ */
+ @Override
+ public AxConcept copyTo(final AxConcept target) {
+ Assertions.argumentNotNull(target, "target may not be null");
+
+ final Object copyObject = target;
+ Assertions.instanceOf(copyObject, AxReferenceKey.class);
+
+ final AxReferenceKey copy = ((AxReferenceKey) copyObject);
+ copy.setParentKeyName(parentKeyName);
+ copy.setParentKeyVersion(parentKeyVersion);
+ copy.setLocalName(localName);
+ copy.setParentLocalName(parentLocalName);
+
+ return copy;
+ }
+
+ /**
+ * {@inheritDoc}.
+ */
+ @Override
+ public int hashCode() {
+ final var prime = 31;
+ var result = 1;
+ result = prime * result + parentKeyName.hashCode();
+ result = prime * result + parentKeyVersion.hashCode();
+ result = prime * result + parentLocalName.hashCode();
+ result = prime * result + localName.hashCode();
+ return result;
+ }
+
+ /**
+ * {@inheritDoc}.
+ */
+ @Override
+ public boolean equals(final Object obj) {
+ if (obj == null) {
+ throw new IllegalArgumentException("comparison object may not be null");
+ }
+
+ if (this == obj) {
+ return true;
+ }
+
+ if (getClass() != obj.getClass()) {
+ return false;
+ }
+
+ final AxReferenceKey other = (AxReferenceKey) obj;
+
+ if (!parentKeyName.equals(other.parentKeyName)) {
+ return false;
+ }
+ if (!parentKeyVersion.equals(other.parentKeyVersion)) {
+ return false;
+ }
+ if (!parentLocalName.equals(other.parentLocalName)) {
+ return false;
+ }
+ return localName.equals(other.localName);
+ }
+
+ /**
+ * {@inheritDoc}.
+ */
+ @Override
+ public int compareTo(final AxConcept otherObj) {
+ Assertions.argumentNotNull(otherObj, "comparison object may not be null");
+
+ if (this == otherObj) {
+ return 0;
+ }
+ if (getClass() != otherObj.getClass()) {
+ return this.hashCode() - otherObj.hashCode();
+ }
+
+ final AxReferenceKey other = (AxReferenceKey) otherObj;
+ if (!parentKeyName.equals(other.parentKeyName)) {
+ return parentKeyName.compareTo(other.parentKeyName);
+ }
+ if (!parentKeyVersion.equals(other.parentKeyVersion)) {
+ return parentKeyVersion.compareTo(other.parentKeyVersion);
+ }
+ if (!parentLocalName.equals(other.parentLocalName)) {
+ return parentLocalName.compareTo(other.parentLocalName);
+ }
+ return localName.compareTo(other.localName);
+ }
+}
diff --git a/model/src/main/java/org/onap/policy/apex/model/basicmodel/concepts/AxToscaPolicyProcessingStatus.java b/model/src/main/java/org/onap/policy/apex/model/basicmodel/concepts/AxToscaPolicyProcessingStatus.java
new file mode 100644
index 000000000..43c7e7866
--- /dev/null
+++ b/model/src/main/java/org/onap/policy/apex/model/basicmodel/concepts/AxToscaPolicyProcessingStatus.java
@@ -0,0 +1,46 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * Copyright (C) 2022 Bell Canada. All rights reserved.
+ * ================================================================================
+ * 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.apex.model.basicmodel.concepts;
+
+/**
+ * This enumeration indicates the status of TOSCA policy processing on an APEX event.
+ */
+public enum AxToscaPolicyProcessingStatus {
+
+ /** Indicates the entrypoint for the processing of a TOSCA Policy. */
+ ENTRY(0),
+
+ /** Indicates a successful exit point for a TOSCA Policy. */
+ EXIT_SUCCESS(1),
+
+ /** Indicates a failure exit point for a TOSCA Policy. */
+ EXIT_FAILURE(2);
+
+ private final int statusCode;
+
+ AxToscaPolicyProcessingStatus(int statusCode) {
+ this.statusCode = statusCode;
+ }
+
+ public int getStatusCode() {
+ return statusCode;
+ }
+} \ No newline at end of file
diff --git a/model/src/main/java/org/onap/policy/apex/model/basicmodel/concepts/AxValidationMessage.java b/model/src/main/java/org/onap/policy/apex/model/basicmodel/concepts/AxValidationMessage.java
new file mode 100644
index 000000000..9e245c07b
--- /dev/null
+++ b/model/src/main/java/org/onap/policy/apex/model/basicmodel/concepts/AxValidationMessage.java
@@ -0,0 +1,104 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * Copyright (C) 2016-2018 Ericsson. All rights reserved.
+ * Modifications Copyright (C) 2019 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.apex.model.basicmodel.concepts;
+
+import org.onap.policy.apex.model.basicmodel.concepts.AxValidationResult.ValidationResult;
+import org.onap.policy.common.utils.validation.Assertions;
+
+/**
+ * A validation message is created for each validation observation observed during validation of a concept. The message
+ * holds the key and the class of the concept on which the observation was made as well as the type of observation and a
+ * message describing the observation.
+ *
+ * @author Liam Fallon (liam.fallon@ericsson.com)
+ */
+public class AxValidationMessage {
+ private final AxKey observedKey;
+ private ValidationResult validationResult = ValidationResult.VALID;
+ private final String observedClass;
+ private final String message;
+
+ /**
+ * Create an validation observation with the given fields.
+ *
+ * @param observedKey the key of the class on which the validation observation was made
+ * @param observedClass the class on which the validation observation was made
+ * @param validationResult the type of observation made
+ * @param message a message describing the observation
+ */
+ public AxValidationMessage(final AxKey observedKey, final Class<?> observedClass,
+ final ValidationResult validationResult, final String message) {
+ Assertions.argumentNotNull(observedKey, "observedKey may not be null");
+ Assertions.argumentNotNull(observedClass, "observedClass may not be null");
+ Assertions.argumentNotNull(validationResult, "validationResult may not be null");
+ Assertions.argumentNotNull(message, "message may not be null");
+
+ this.observedKey = observedKey;
+ this.observedClass = observedClass.getName();
+ this.validationResult = validationResult;
+ this.message = message;
+ }
+
+ /**
+ * Gets the key of the observation.
+ *
+ * @return the key of the observation
+ */
+ public AxKey getObservedKey() {
+ return observedKey;
+ }
+
+ /**
+ * Gets the observed class.
+ *
+ * @return the observed class
+ */
+ public String getObservedClass() {
+ return observedClass;
+ }
+
+ /**
+ * Gets the type of observation made.
+ *
+ * @return the type of observation made
+ */
+ public ValidationResult getValidationResult() {
+ return validationResult;
+ }
+
+ /**
+ * Get a description of the observation.
+ *
+ * @return the observation description
+ */
+ public String getMessage() {
+ return message;
+ }
+
+ /**
+ * {@inheritDoc}.
+ */
+ @Override
+ public String toString() {
+ return observedKey.toString() + ':' + observedClass + ':' + validationResult.name() + ':' + message;
+ }
+}
diff --git a/model/src/main/java/org/onap/policy/apex/model/basicmodel/concepts/AxValidationResult.java b/model/src/main/java/org/onap/policy/apex/model/basicmodel/concepts/AxValidationResult.java
new file mode 100644
index 000000000..10ccb30aa
--- /dev/null
+++ b/model/src/main/java/org/onap/policy/apex/model/basicmodel/concepts/AxValidationResult.java
@@ -0,0 +1,150 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * Copyright (C) 2016-2018 Ericsson. All rights reserved.
+ * ================================================================================
+ * 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.apex.model.basicmodel.concepts;
+
+import java.util.LinkedList;
+import java.util.List;
+
+/**
+ * This class records the result of a validation and holds all validatino observation messages.
+ *
+ * @author Liam Fallon (liam.fallon@ericsson.com)
+ */
+public class AxValidationResult {
+ /**
+ * The ValidationResult enumeration describes the severity of a validation result.
+ */
+ public enum ValidationResult {
+ /** No problems or observations were detected during validation. */
+ VALID,
+ /**
+ * Observations were made on a concept (such as blank descriptions) of a nature that will not affect the use of
+ * the concept.
+ */
+ OBSERVATION,
+ /**
+ * Warnings were made on a concept (such as defined but unused concepts) of a nature that may affect the use of
+ * the concept.
+ */
+ WARNING,
+ /**
+ * Errors were detected on a concept (such as referenced but undefined concepts) of a nature that will affect
+ * the use of the concept.
+ */
+ INVALID
+ }
+
+ // The actual verification result
+ private ValidationResult validationResult = ValidationResult.VALID;
+
+ // Messages collected during the verification process
+ private final List<AxValidationMessage> messageList = new LinkedList<>();
+
+ /**
+ * Check if a validation reported a valid concept, returns true if the model is usable (that is, even if the model
+ * has warnings or observations).
+ *
+ * @return true, if the concept is reported as valid and can be used
+ */
+ public boolean isValid() {
+ return validationResult != ValidationResult.INVALID;
+ }
+
+ /**
+ * Check if a validation reported a concept with no errors or warnings, returns true if the model is OK to use.
+ *
+ * @return true, if the concept has no warnings or errors
+ */
+ public boolean isOk() {
+ return validationResult == ValidationResult.VALID || validationResult == ValidationResult.OBSERVATION;
+ }
+
+ /**
+ * Gets the validation result.
+ *
+ * @return the validation result on a concept
+ */
+ public ValidationResult getValidationResult() {
+ return validationResult;
+ }
+
+ /**
+ * Gets the list of validation results on the concept.
+ *
+ * @return the list of validaiton results
+ */
+ public List<AxValidationMessage> getMessageList() {
+ return messageList;
+ }
+
+ /**
+ * Adds a validation message to the validation result, used by validate() implementations on {@link AxConcept}
+ * subclasses to report validaiton observations.
+ *
+ * @param validationMessage the validation message
+ */
+ public void addValidationMessage(final AxValidationMessage validationMessage) {
+ messageList.add(validationMessage);
+
+ // Check if the incoming message has a more sever status than the
+ // current one on the overall validation result,
+ // if so, the overall result goes to that level
+ if (validationMessage.getValidationResult().ordinal() > validationResult.ordinal()) {
+ validationResult = validationMessage.getValidationResult();
+ }
+ }
+
+ /**
+ * {@inheritDoc}.
+ */
+ @Override
+ public String toString() {
+ final StringBuilder builder = new StringBuilder();
+
+ switch (validationResult) {
+ case VALID:
+
+ builder.append("***validation of model successful***");
+ return builder.toString();
+ case OBSERVATION:
+
+ builder.append("\n***observations noted during validation of model***\n");
+ break;
+ case WARNING:
+
+ builder.append("\n***warnings issued during validation of model***\n");
+ break;
+ case INVALID:
+ builder.append("\n***validation of model failed***\n");
+ break;
+ default:
+ break;
+ }
+
+ for (final AxValidationMessage message : messageList) {
+ builder.append(message);
+ builder.append("\n");
+ }
+
+ builder.append("********************************");
+ return builder.toString();
+ }
+}
diff --git a/model/src/main/java/org/onap/policy/apex/model/basicmodel/concepts/package-info.java b/model/src/main/java/org/onap/policy/apex/model/basicmodel/concepts/package-info.java
new file mode 100644
index 000000000..347e46a0f
--- /dev/null
+++ b/model/src/main/java/org/onap/policy/apex/model/basicmodel/concepts/package-info.java
@@ -0,0 +1,27 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * Copyright (C) 2016-2018 Ericsson. All rights reserved.
+ * Modifications Copyright (C) 2022 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=========================================================
+ */
+
+/**
+ * This package contains the fundamental concepts for all APEX models.
+ *
+ * @author Liam Fallon (liam.fallon@ericsson.com)
+ */
+package org.onap.policy.apex.model.basicmodel.concepts;
diff --git a/model/src/main/java/org/onap/policy/apex/model/basicmodel/handling/ApexModelCreator.java b/model/src/main/java/org/onap/policy/apex/model/basicmodel/handling/ApexModelCreator.java
new file mode 100644
index 000000000..a33e1bf1a
--- /dev/null
+++ b/model/src/main/java/org/onap/policy/apex/model/basicmodel/handling/ApexModelCreator.java
@@ -0,0 +1,41 @@
+/*
+ * ============LICENSE_START=======================================================
+ * Copyright (C) 2016-2018 Ericsson. All rights reserved.
+ * ================================================================================
+ * 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.apex.model.basicmodel.handling;
+
+import org.onap.policy.apex.model.basicmodel.concepts.AxModel;
+
+/**
+ * This interface is implemented by factories that create Apex models. It is mainly used by unit test classes that
+ * generate Apex models for test purposes.
+ *
+ * @author Liam Fallon (liam.fallon@ericsson.com)
+ * @param <M> the type of Apex model to create, must be a sub class of {@link AxModel}
+ */
+@FunctionalInterface
+public interface ApexModelCreator<M extends AxModel> {
+
+ /**
+ * Gets the model created by the model creator.
+ *
+ * @return the model created by the model creator
+ */
+ M getModel();
+}
diff --git a/model/src/main/java/org/onap/policy/apex/model/basicmodel/handling/ApexModelCustomGsonMapAdapter.java b/model/src/main/java/org/onap/policy/apex/model/basicmodel/handling/ApexModelCustomGsonMapAdapter.java
new file mode 100644
index 000000000..cb67590f7
--- /dev/null
+++ b/model/src/main/java/org/onap/policy/apex/model/basicmodel/handling/ApexModelCustomGsonMapAdapter.java
@@ -0,0 +1,118 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * Copyright (C) 2022 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.apex.model.basicmodel.handling;
+
+import com.google.gson.JsonArray;
+import com.google.gson.JsonDeserializationContext;
+import com.google.gson.JsonDeserializer;
+import com.google.gson.JsonElement;
+import com.google.gson.JsonObject;
+import com.google.gson.JsonParseException;
+import com.google.gson.JsonSerializationContext;
+import com.google.gson.JsonSerializer;
+import java.lang.reflect.ParameterizedType;
+import java.lang.reflect.Type;
+import java.util.AbstractMap;
+import java.util.Map;
+import java.util.Map.Entry;
+import java.util.TreeMap;
+import java.util.stream.Collectors;
+import java.util.stream.StreamSupport;
+
+@SuppressWarnings("rawtypes")
+public class ApexModelCustomGsonMapAdapter implements JsonSerializer<Map>, JsonDeserializer<Map> {
+ private static final String MAP_ENTRY_KEY = "entry";
+
+ @SuppressWarnings("unchecked")
+ @Override
+ public Map deserialize(JsonElement jsonElement, Type typeOfT, JsonDeserializationContext context)
+ throws JsonParseException {
+
+ if (!(jsonElement instanceof JsonObject)) {
+ throw new JsonParseException("could not parse JSON map, map is not a JsonObject");
+ }
+
+ JsonObject jsonObject = (JsonObject) jsonElement;
+
+ if (jsonObject.size() != 1) {
+ throw new JsonParseException("could not parse JSON map, map must be in a JsonObject with a single member");
+ }
+
+ JsonArray mapEntryArray = (JsonArray) jsonObject.get(MAP_ENTRY_KEY);
+ if (mapEntryArray == null) {
+ throw new JsonParseException("could not parse JSON map, map \"entry\" in JsonObject not found");
+ }
+
+ return new TreeMap(
+ StreamSupport
+ .stream(mapEntryArray.spliterator(), true)
+ .map(element -> deserializeMapEntry(element, typeOfT, context))
+ .collect(Collectors.toMap(entry -> entry.getKey(), entry -> entry.getValue()))
+ );
+ }
+
+ @Override
+ public JsonElement serialize(Map sourceMap, Type typeOfSrc, JsonSerializationContext context) {
+
+ // A map is stored in a JsonArray
+ JsonArray mapEntryArray = new JsonArray();
+
+ for (Object mapEntryObject : sourceMap.entrySet()) {
+ Entry mapEntry = (Entry) mapEntryObject;
+ mapEntryArray.add(serializeMapEntry(mapEntry, typeOfSrc, context));
+ }
+
+ JsonObject returnObject = new JsonObject();
+ returnObject.add(MAP_ENTRY_KEY, mapEntryArray);
+
+ return returnObject;
+ }
+
+ @SuppressWarnings("unchecked")
+ private static Entry deserializeMapEntry(JsonElement element, Type typeOfT, JsonDeserializationContext context) {
+ // Get the types of the map
+ ParameterizedType pt = (ParameterizedType) typeOfT;
+
+ // Type of the key and value of the map
+ Type keyType = pt.getActualTypeArguments()[0];
+ Type valueType = pt.getActualTypeArguments()[1];
+
+ // Deserialize the key and value
+ return new AbstractMap.SimpleEntry(
+ context.deserialize(element.getAsJsonObject().get("key"), keyType),
+ context.deserialize(element.getAsJsonObject().get("value"), valueType));
+ }
+
+ private static JsonElement serializeMapEntry(Entry sourceEntry, Type typeOfSrc, JsonSerializationContext context) {
+ // Get the types of the map
+ ParameterizedType pt = (ParameterizedType) typeOfSrc;
+
+ // Type of the key and value of the map
+ Type keyType = pt.getActualTypeArguments()[0];
+ Type valueType = pt.getActualTypeArguments()[1];
+
+ JsonObject entryObject = new JsonObject();
+ entryObject.add("key", context.serialize(sourceEntry.getKey(), keyType));
+ entryObject.add("value", context.serialize(sourceEntry.getValue(), valueType));
+
+ return entryObject;
+ }
+}
diff --git a/model/src/main/java/org/onap/policy/apex/model/basicmodel/handling/ApexModelCustomGsonRefereceKeyAdapter.java b/model/src/main/java/org/onap/policy/apex/model/basicmodel/handling/ApexModelCustomGsonRefereceKeyAdapter.java
new file mode 100644
index 000000000..d731c8689
--- /dev/null
+++ b/model/src/main/java/org/onap/policy/apex/model/basicmodel/handling/ApexModelCustomGsonRefereceKeyAdapter.java
@@ -0,0 +1,50 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * Copyright (C) 2022 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.apex.model.basicmodel.handling;
+
+import com.google.gson.JsonDeserializationContext;
+import com.google.gson.JsonDeserializer;
+import com.google.gson.JsonElement;
+import com.google.gson.JsonObject;
+import com.google.gson.JsonParseException;
+import java.lang.reflect.Type;
+import org.onap.policy.apex.model.basicmodel.concepts.AxReferenceKey;
+
+public class ApexModelCustomGsonRefereceKeyAdapter implements JsonDeserializer<AxReferenceKey> {
+
+ @Override
+ public AxReferenceKey deserialize(JsonElement jsonElement, Type typeOfT, JsonDeserializationContext context)
+ throws JsonParseException {
+
+ if (jsonElement instanceof JsonObject) {
+ return new AxReferenceKey(
+ jsonElement.getAsJsonObject().get("parentKeyName").getAsString(),
+ jsonElement.getAsJsonObject().get("parentKeyVersion").getAsString(),
+ jsonElement.getAsJsonObject().get("parentLocalName").getAsString(),
+ jsonElement.getAsJsonObject().get("localName").getAsString()
+ );
+ } else {
+ AxReferenceKey returnKey = new AxReferenceKey();
+ returnKey.setLocalName(jsonElement.getAsString());
+ return returnKey;
+ }
+ }
+}
diff --git a/model/src/main/java/org/onap/policy/apex/model/basicmodel/handling/ApexModelException.java b/model/src/main/java/org/onap/policy/apex/model/basicmodel/handling/ApexModelException.java
new file mode 100644
index 000000000..17eb2639d
--- /dev/null
+++ b/model/src/main/java/org/onap/policy/apex/model/basicmodel/handling/ApexModelException.java
@@ -0,0 +1,51 @@
+/*
+ * ============LICENSE_START=======================================================
+ * Copyright (C) 2016-2018 Ericsson. All rights reserved.
+ * ================================================================================
+ * 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.apex.model.basicmodel.handling;
+
+import org.onap.policy.apex.model.basicmodel.concepts.ApexException;
+
+/**
+ * This exception is invoked if an exception occurs in model handling.
+ *
+ * @author Liam Fallon (liam.fallon@ericsson.com)
+ */
+public class ApexModelException extends ApexException {
+ private static final long serialVersionUID = -4245694568321686450L;
+
+ /**
+ * Instantiates a new apex model handling exception.
+ *
+ * @param message the message
+ */
+ public ApexModelException(final String message) {
+ super(message);
+ }
+
+ /**
+ * Instantiates a new apex model handling exception.
+ *
+ * @param message the message
+ * @param exception the exception
+ */
+ public ApexModelException(final String message, final Exception exception) {
+ super(message, exception);
+ }
+}
diff --git a/model/src/main/java/org/onap/policy/apex/model/basicmodel/handling/ApexModelFileWriter.java b/model/src/main/java/org/onap/policy/apex/model/basicmodel/handling/ApexModelFileWriter.java
new file mode 100644
index 000000000..54b5651cc
--- /dev/null
+++ b/model/src/main/java/org/onap/policy/apex/model/basicmodel/handling/ApexModelFileWriter.java
@@ -0,0 +1,99 @@
+/*
+ * ============LICENSE_START=======================================================
+ * Copyright (C) 2016-2018 Ericsson. All rights reserved.
+ * Modifications Copyright (C) 2021-2022 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.apex.model.basicmodel.handling;
+
+import java.io.File;
+import java.io.FileOutputStream;
+import lombok.Getter;
+import lombok.Setter;
+import org.onap.policy.apex.model.basicmodel.concepts.ApexException;
+import org.onap.policy.apex.model.basicmodel.concepts.AxModel;
+import org.slf4j.ext.XLogger;
+import org.slf4j.ext.XLoggerFactory;
+
+/**
+ * This class writes an Apex model to a file.
+ *
+ * @author Liam Fallon (liam.fallon@ericsson.com)
+ * @param <M> the type of Apex model to write to file, must be a sub class of {@link AxModel}
+ */
+@Getter
+@Setter
+public class ApexModelFileWriter<M extends AxModel> {
+ private static final XLogger LOGGER = XLoggerFactory.getXLogger(ApexModelFileWriter.class);
+
+ // Should models being written to files be valid
+ private boolean validate;
+
+ /**
+ * Constructor, set the validation flag.
+ *
+ * @param validate indicates if validation be performed prior to output
+ */
+ public ApexModelFileWriter(final boolean validate) {
+ this.validate = validate;
+ }
+
+ /**
+ * Write a model to an JSON file.
+ *
+ * @param model The model to write
+ * @param rootModelClass The concept class
+ * @param modelFileName The name of the file to write to
+ * @throws ApexException thrown on errors
+ */
+ public void apexModelWriteJsonFile(final M model, final Class<M> rootModelClass, final String modelFileName)
+ throws ApexException {
+ LOGGER.debug("running apexModelWriteJSONFile . . .");
+
+ final ApexModelWriter<M> modelWriter = new ApexModelWriter<>(rootModelClass);
+ modelWriter.setValidate(validate);
+
+ writeModelFile(model, modelWriter, modelFileName);
+
+ LOGGER.debug("ran apexModelWriteJSONFile");
+ }
+
+ /**
+ * Write a model to a file using a model writer.
+ *
+ * @param model The model to write
+ * @param modelWriter the model writer to use to write the model to the file
+ * @param modelFileName the file name of the file to write to
+ * @throws ApexException on exceptions writing the model
+ */
+ private void writeModelFile(final M model, final ApexModelWriter<M> modelWriter, final String modelFileName)
+ throws ApexException {
+ final var modelFile = new File(modelFileName);
+ if (!modelFile.getParentFile().exists() && !modelFile.getParentFile().mkdirs()) {
+ LOGGER.warn("could not create directory " + modelFile.getParentFile());
+ throw new ApexException("could not create directory " + modelFile.getParentFile());
+ }
+
+ try (final var fileOutputStream = new FileOutputStream(modelFile)) {
+ modelWriter.write(model, fileOutputStream);
+ } catch (final Exception e) {
+ LOGGER.warn("error processing file " + modelFile.getAbsolutePath(), e);
+ throw new ApexException("error processing file " + modelFile.getAbsolutePath(), e);
+ }
+ }
+}
diff --git a/model/src/main/java/org/onap/policy/apex/model/basicmodel/handling/ApexModelReader.java b/model/src/main/java/org/onap/policy/apex/model/basicmodel/handling/ApexModelReader.java
new file mode 100644
index 000000000..a9bd2fe60
--- /dev/null
+++ b/model/src/main/java/org/onap/policy/apex/model/basicmodel/handling/ApexModelReader.java
@@ -0,0 +1,169 @@
+/*
+ * ============LICENSE_START=======================================================
+ * Copyright (C) 2016-2018 Ericsson. All rights reserved.
+ * Modifications Copyright (C) 2019-2022 Nordix Foundation.
+ * Modifications Copyright (C) 2021 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.apex.model.basicmodel.handling;
+
+import com.google.gson.Gson;
+import com.google.gson.GsonBuilder;
+import java.io.BufferedReader;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.util.Map;
+import lombok.Getter;
+import lombok.Setter;
+import org.onap.policy.apex.model.basicmodel.concepts.AxConcept;
+import org.onap.policy.apex.model.basicmodel.concepts.AxReferenceKey;
+import org.onap.policy.apex.model.basicmodel.concepts.AxValidationResult;
+import org.onap.policy.common.utils.resources.TextFileUtils;
+import org.onap.policy.common.utils.validation.Assertions;
+import org.slf4j.ext.XLogger;
+import org.slf4j.ext.XLoggerFactory;
+
+/**
+ * This class reads an Apex concept from a file into a Java Apex Concept {@link AxConcept}.
+ *
+ * @author Liam Fallon (liam.fallon@ericsson.com)
+ * @param <C> the type of Apex concept to read, must be a sub class of {@link AxConcept}
+ */
+@Getter
+@Setter
+public class ApexModelReader<C extends AxConcept> {
+ // Get a reference to the logger
+ private static final XLogger LOGGER = XLoggerFactory.getXLogger(ApexModelReader.class);
+
+ // Use GSON to deserialize JSON
+ private static Gson gson = new GsonBuilder()
+ .registerTypeAdapter(AxReferenceKey.class, new ApexModelCustomGsonRefereceKeyAdapter())
+ .registerTypeAdapter(Map.class, new ApexModelCustomGsonMapAdapter())
+ .setPrettyPrinting()
+ .create();
+
+ //  The root class of the concept we are reading
+ private final Class<C> rootConceptClass;
+
+ // All read concepts are validated after reading if this flag is set
+ private boolean validate = true;
+
+ /**
+ * Constructor, initiates the reader with validation on.
+ *
+ * @param rootConceptClass the root concept class for concept reading
+ * @throws ApexModelException the apex concept reader exception
+ */
+ public ApexModelReader(final Class<C> rootConceptClass) throws ApexModelException {
+ // Save the root concept class
+ this.rootConceptClass = rootConceptClass;
+ }
+
+ /**
+ * Constructor, initiates the reader.
+ *
+ * @param rootConceptClass the root concept class for concept reading
+ * @param validate whether to perform validation by default
+ * @throws ApexModelException the apex concept reader exception
+ */
+ public ApexModelReader(final Class<C> rootConceptClass, final boolean validate) throws ApexModelException {
+ this(rootConceptClass);
+ this.validate = validate;
+ }
+
+ /**
+ * This method checks the specified Apex concept file and reads it into an Apex concept.
+ *
+ * @param apexConceptStream the apex concept stream
+ * @return the Apex concept
+ * @throws ApexModelException on reading exceptions
+ */
+ public C read(final InputStream apexConceptStream) throws ApexModelException {
+ Assertions.argumentNotNull(apexConceptStream, "concept stream may not be null");
+
+ return read(new BufferedReader(new InputStreamReader(apexConceptStream)));
+ }
+
+ /**
+ * This method reads the specified Apex reader into an Apex concept.
+ *
+ * @param apexConceptReader the apex concept reader
+ * @return the Apex concept
+ * @throws ApexModelException on reading exceptions
+ */
+ public C read(final BufferedReader apexConceptReader) throws ApexModelException {
+ Assertions.argumentNotNull(apexConceptReader, "concept reader may not be null");
+
+ LOGGER.entry("reading Apex concept into a String . . .");
+
+ // Get the Apex concept as a string
+ String apexConceptString = null;
+ try {
+ apexConceptString = TextFileUtils.getReaderAsString(apexConceptReader).trim();
+ } catch (final IOException e) {
+ throw new ApexModelException("Unable to read Apex concept ", e);
+ }
+
+ return read(apexConceptString);
+ }
+
+ /**
+ * This method reads the specified Apex string into an Apex concept.
+ *
+ * @param apexConceptString the apex concept as a string
+ * @return the Apex concept
+ * @throws ApexModelException on reading exceptions
+ */
+ public C read(final String apexConceptString) throws ApexModelException {
+ Assertions.argumentNotNull(apexConceptString, "concept string may not be null");
+
+ LOGGER.entry("reading Apex concept from string . . .");
+
+ C apexConcept = null;
+ try {
+ apexConcept = gson.fromJson(apexConceptString, rootConceptClass);
+ } catch (final Exception je) {
+ throw new ApexModelException("Unable to unmarshal Apex concept ", je);
+ }
+
+ if (apexConcept == null) {
+ throw new ApexModelException("Unable to unmarshal Apex concept, unmarshaled model is null ");
+ }
+
+ LOGGER.debug("reading of Apex concept {} completed");
+
+ apexConcept.buildReferences();
+
+ // Check if the concept should be validated
+ if (validate) {
+ // Validate the configuration file
+ final AxValidationResult validationResult = apexConcept.validate(new AxValidationResult());
+ if (validationResult.isValid()) {
+ return apexConcept;
+ } else {
+ String message = "Apex concept validation failed" + validationResult.toString();
+ LOGGER.error(message);
+ throw new ApexModelException(message);
+ }
+ } else {
+ // No validation check
+ return apexConcept;
+ }
+ }
+}
diff --git a/model/src/main/java/org/onap/policy/apex/model/basicmodel/handling/ApexModelSaver.java b/model/src/main/java/org/onap/policy/apex/model/basicmodel/handling/ApexModelSaver.java
new file mode 100644
index 000000000..79d69bdd5
--- /dev/null
+++ b/model/src/main/java/org/onap/policy/apex/model/basicmodel/handling/ApexModelSaver.java
@@ -0,0 +1,80 @@
+/*
+ * ============LICENSE_START=======================================================
+ * Copyright (C) 2016-2018 Ericsson. All rights reserved.
+ * Modifications Copyright (C) 2019-2022 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.apex.model.basicmodel.handling;
+
+import java.io.File;
+import org.onap.policy.apex.model.basicmodel.concepts.ApexException;
+import org.onap.policy.apex.model.basicmodel.concepts.AxModel;
+import org.onap.policy.common.utils.validation.Assertions;
+import org.slf4j.ext.XLogger;
+import org.slf4j.ext.XLoggerFactory;
+
+/**
+ * This class is used to save Apex models to file in JSON format.
+ *
+ * @author Liam Fallon (liam.fallon@ericsson.com)
+ * @param <M> the type of Apex model to save to file, must be a sub class of {@link AxModel}
+ */
+public class ApexModelSaver<M extends AxModel> {
+ private static final XLogger LOGGER = XLoggerFactory.getXLogger(ApexModelSaver.class);
+
+ // The class of the model and the model to write to disk
+ private final Class<M> rootModelClass;
+ private final M model;
+
+ // The path into which to write the models
+ private final String writePath;
+
+ /**
+ * Constructor, specifies the type of the Apex model (a sub class of {@link AxModel}), the model to write, and the
+ * path of a directory to which to write the model.
+ *
+ * @param rootModelClass the class of the model, a sub class of {@link AxModel}
+ * @param model the model to write, an instance of a sub class of {@link AxModel}
+ * @param writePath the directory to which models will be written. The name of the written model will be the Model
+ * Name for its key with the suffix {@code .json}.
+ */
+ public ApexModelSaver(final Class<M> rootModelClass, final M model, final String writePath) {
+ Assertions.argumentNotNull(rootModelClass, "argument rootModelClass may not be null");
+ Assertions.argumentNotNull(model, "argument model may not be null");
+ Assertions.argumentNotNull(writePath, "writePath rootModelClass may not be null");
+
+ this.rootModelClass = rootModelClass;
+ this.model = model;
+ this.writePath = writePath;
+ }
+
+ /**
+ * Write an Apex model to a file in JSON format. The model will be written to {@code <writePath/modelKeyName.json>}
+ *
+ * @throws ApexException on errors writing the Apex model
+ */
+ public void apexModelWriteJson() throws ApexException {
+ LOGGER.debug("running apexModelWriteJSON . . .");
+
+ // Write the file to disk
+ final var jsonFile = new File(writePath + File.separatorChar + model.getKey().getName() + ".json");
+ new ApexModelFileWriter<M>(true).apexModelWriteJsonFile(model, rootModelClass, jsonFile.getPath());
+
+ LOGGER.debug("ran apexModelWriteJSON");
+ }
+}
diff --git a/model/src/main/java/org/onap/policy/apex/model/basicmodel/handling/ApexModelStringWriter.java b/model/src/main/java/org/onap/policy/apex/model/basicmodel/handling/ApexModelStringWriter.java
new file mode 100644
index 000000000..a9df23afc
--- /dev/null
+++ b/model/src/main/java/org/onap/policy/apex/model/basicmodel/handling/ApexModelStringWriter.java
@@ -0,0 +1,94 @@
+/*
+ * ============LICENSE_START=======================================================
+ * Copyright (C) 2016-2018 Ericsson. All rights reserved.
+ * Modifications Copyright (C) 2019-2022 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.apex.model.basicmodel.handling;
+
+import java.io.ByteArrayOutputStream;
+import lombok.Getter;
+import lombok.Setter;
+import org.onap.policy.apex.model.basicmodel.concepts.ApexException;
+import org.onap.policy.apex.model.basicmodel.concepts.AxConcept;
+import org.onap.policy.common.utils.validation.Assertions;
+import org.slf4j.ext.XLogger;
+import org.slf4j.ext.XLoggerFactory;
+
+/**
+ * This class writes an Apex concept to a string.
+ *
+ * @author Liam Fallon (liam.fallon@ericsson.com)
+ * @param <C> the type of Apex concept to write to a string, must be a sub class of {@link AxConcept}
+ */
+@Getter
+@Setter
+public class ApexModelStringWriter<C extends AxConcept> {
+ private static final XLogger LOGGER = XLoggerFactory.getXLogger(ApexModelStringWriter.class);
+
+ // Should concepts being written to files be valid
+ private boolean validate;
+
+ /**
+ * Constructor, set the validation flag.
+ *
+ * @param validate Should validation be performed prior to output
+ */
+ public ApexModelStringWriter(final boolean validate) {
+ this.validate = validate;
+ }
+
+ /**
+ * Write a concept to a string.
+ *
+ * @param concept The concept to write
+ * @param rootConceptClass The concept class
+ * @return The string with the concept
+ * @throws ApexException thrown on errors
+ */
+ public String writeString(final C concept, final Class<C> rootConceptClass)
+ throws ApexException {
+ Assertions.argumentNotNull(concept, "concept may not be null");
+
+ return writeJsonString(concept, rootConceptClass);
+ }
+
+ /**
+ * Write a concept to a JSON string.
+ *
+ * @param concept The concept to write
+ * @param rootConceptClass The concept class
+ * @return The string with the concept
+ * @throws ApexException thrown on errors
+ */
+ public String writeJsonString(final C concept, final Class<C> rootConceptClass) throws ApexException {
+ LOGGER.debug("running writeJSONString . . .");
+
+ final ApexModelWriter<C> conceptWriter = new ApexModelWriter<>(rootConceptClass);
+ conceptWriter.setValidate(validate);
+
+ try (var baOutputStream = new ByteArrayOutputStream()) {
+ conceptWriter.write(concept, baOutputStream);
+ return baOutputStream.toString();
+ } catch (final Exception e) {
+ LOGGER.warn("error writing JSON string", e);
+ throw new ApexException("error writing JSON string", e);
+ }
+
+ }
+}
diff --git a/model/src/main/java/org/onap/policy/apex/model/basicmodel/handling/ApexModelWriter.java b/model/src/main/java/org/onap/policy/apex/model/basicmodel/handling/ApexModelWriter.java
new file mode 100644
index 000000000..349622697
--- /dev/null
+++ b/model/src/main/java/org/onap/policy/apex/model/basicmodel/handling/ApexModelWriter.java
@@ -0,0 +1,148 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * Copyright (C) 2016-2018 Ericsson. All rights reserved.
+ * Modifications Copyright (C) 2019-2022 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.apex.model.basicmodel.handling;
+
+import com.google.gson.Gson;
+import com.google.gson.GsonBuilder;
+import java.io.IOException;
+import java.io.OutputStream;
+import java.io.OutputStreamWriter;
+import java.io.Writer;
+import java.util.Map;
+import lombok.Getter;
+import lombok.Setter;
+import org.onap.policy.apex.model.basicmodel.concepts.AxConcept;
+import org.onap.policy.apex.model.basicmodel.concepts.AxReferenceKey;
+import org.onap.policy.apex.model.basicmodel.concepts.AxValidationResult;
+import org.onap.policy.common.utils.validation.Assertions;
+import org.slf4j.ext.XLogger;
+import org.slf4j.ext.XLoggerFactory;
+
+/**
+ * This class writes an Apex concept to a file from a Java Apex Concept.
+ *
+ * @param <C> the type of Apex concept to write, must be a sub class of {@link AxConcept}
+ * @author John Keeney (john.keeney@ericsson.com)
+ */
+@Getter
+@Setter
+public class ApexModelWriter<C extends AxConcept> {
+
+ private static final String CONCEPT_MAY_NOT_BE_NULL = "concept may not be null";
+ private static final String CONCEPT_WRITER_MAY_NOT_BE_NULL = "concept writer may not be null";
+ private static final String CONCEPT_STREAM_MAY_NOT_BE_NULL = "concept stream may not be null";
+
+ // Use GSON to serialize JSON
+ private static Gson gson = new GsonBuilder()
+ .registerTypeAdapter(AxReferenceKey.class, new ApexModelCustomGsonRefereceKeyAdapter())
+ .registerTypeAdapter(Map.class, new ApexModelCustomGsonMapAdapter())
+ .setPrettyPrinting()
+ .create();
+
+ // Get a reference to the logger
+ private static final XLogger LOGGER = XLoggerFactory.getXLogger(ApexModelWriter.class);
+
+ //  The root class of the concept we are reading
+ private final Class<C> rootConceptClass;
+
+ // All written concepts are validated before writing if this flag is set
+ private boolean validate = true;
+
+ /**
+ * Constructor, initiates the writer with validation on.
+ *
+ * @param rootConceptClass the root concept class for concept reading
+ * @throws ApexModelException the apex concept reader exception
+ */
+ public ApexModelWriter(final Class<C> rootConceptClass) throws ApexModelException {
+ // Save the root concept class
+ this.rootConceptClass = rootConceptClass;
+ }
+
+ /**
+ * This method validates the Apex concept then writes it into a stream.
+ *
+ * @param concept the concept to write
+ * @param apexConceptStream the stream to write to
+ * @throws ApexModelException on validation or writing exceptions
+ */
+ public void write(final C concept, final OutputStream apexConceptStream) throws ApexModelException {
+ Assertions.argumentNotNull(concept, CONCEPT_MAY_NOT_BE_NULL);
+ Assertions.argumentNotNull(apexConceptStream, CONCEPT_STREAM_MAY_NOT_BE_NULL);
+
+ this.write(concept, new OutputStreamWriter(apexConceptStream));
+ }
+
+ /**
+ * This method validates the Apex concept then writes it into a writer.
+ *
+ * @param concept the concept to write
+ * @param apexConceptWriter the writer to write to
+ * @throws ApexModelException on validation or writing exceptions
+ */
+ public void write(final C concept, final Writer apexConceptWriter) throws ApexModelException {
+ Assertions.argumentNotNull(concept, CONCEPT_MAY_NOT_BE_NULL);
+ Assertions.argumentNotNull(apexConceptWriter, CONCEPT_WRITER_MAY_NOT_BE_NULL);
+
+ // Check if we should validate the concept
+ if (validate) {
+ // Validate the concept first
+ final AxValidationResult validationResult = concept.validate(new AxValidationResult());
+ if (!validationResult.isValid()) {
+ String message =
+ "Apex concept (" + concept.getKey().getId() + ") validation failed: " + validationResult.toString();
+ throw new ApexModelException(message);
+ }
+ }
+
+ writeJson(concept, apexConceptWriter);
+ }
+
+ /**
+ * This method writes the Apex concept into a writer in JSON format.
+ *
+ * @param concept the concept to write
+ * @param apexConceptWriter the writer to write to
+ * @throws ApexModelException on validation or writing exceptions
+ */
+ private void writeJson(final C concept, final Writer apexConceptWriter) throws ApexModelException {
+ Assertions.argumentNotNull(concept, CONCEPT_MAY_NOT_BE_NULL);
+
+ LOGGER.debug("writing Apex concept JSON . . .");
+
+ String modelJsonString = null;
+ try {
+ modelJsonString = gson.toJson(concept, rootConceptClass);
+ } catch (Exception je) {
+ throw new ApexModelException("Unable to marshal Apex concept to JSON", je);
+ }
+
+ try {
+ apexConceptWriter.write(modelJsonString);
+ apexConceptWriter.close();
+ } catch (IOException ioe) {
+ throw new ApexModelException("Unable to write Apex concept as JSON", ioe);
+ }
+
+ LOGGER.debug("wrote Apex concept JSON");
+ }
+}
diff --git a/model/src/main/java/org/onap/policy/apex/model/basicmodel/handling/package-info.java b/model/src/main/java/org/onap/policy/apex/model/basicmodel/handling/package-info.java
new file mode 100644
index 000000000..3d6dab3b2
--- /dev/null
+++ b/model/src/main/java/org/onap/policy/apex/model/basicmodel/handling/package-info.java
@@ -0,0 +1,21 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * Copyright (C) 2016-2018 Ericsson. All rights reserved.
+ * Modifications Copyright (C) 2022 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.apex.model.basicmodel.handling;
diff --git a/model/src/main/java/org/onap/policy/apex/model/basicmodel/package-info.java b/model/src/main/java/org/onap/policy/apex/model/basicmodel/package-info.java
new file mode 100644
index 000000000..8ad405e25
--- /dev/null
+++ b/model/src/main/java/org/onap/policy/apex/model/basicmodel/package-info.java
@@ -0,0 +1,31 @@
+/*
+ * ============LICENSE_START=======================================================
+ * Copyright (C) 2016-2018 Ericsson. All rights reserved.
+ * Modifications Copyright (C) 2019,2022 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=========================================================
+ */
+
+/**
+ * Provides the base definition of an APEX model. It also defines the Model Service, the mechanism that allows access to
+ * the model for APEX concepts anywhere in the system.
+ *
+ * <p>It also provides handling support to models, allowing them to be read and written to file and databases in JSON
+ * format.
+ *
+ * @author Liam Fallon (liam.fallon@ericsson.com)
+ */
+package org.onap.policy.apex.model.basicmodel;
diff --git a/model/src/main/java/org/onap/policy/apex/model/basicmodel/service/ModelService.java b/model/src/main/java/org/onap/policy/apex/model/basicmodel/service/ModelService.java
new file mode 100644
index 000000000..c6eb0e011
--- /dev/null
+++ b/model/src/main/java/org/onap/policy/apex/model/basicmodel/service/ModelService.java
@@ -0,0 +1,108 @@
+/*
+ * ============LICENSE_START=======================================================
+ * Copyright (C) 2016-2018 Ericsson. All rights reserved.
+ * Modifications Copyright (C) 2019 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.apex.model.basicmodel.service;
+
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
+import org.onap.policy.apex.model.basicmodel.concepts.ApexRuntimeException;
+import org.onap.policy.apex.model.basicmodel.concepts.AxConcept;
+
+/**
+ * The model service makes Apex models available to all classes in a JVM.
+ *
+ * <p>The reason for having a model service is to avoid having to pass concept and model definitions down long call
+ * chains in modules such as the Apex engine and editor. The model service makes the model and concept definitions
+ * available statically.
+ *
+ * <p>Note that the use of the model service means that only a single Apex model of a particular type may exist in Apex
+ * (particularly the engine) at any time. Of course the model in a JVM can be changed at any time provided all users of
+ * the model are stopped and restarted in an orderly manner.
+ *
+ * @author Liam Fallon (liam.fallon@ericsson.com)
+ */
+public abstract class ModelService {
+ // The map holding the models
+ private static Map<Class<?>, AxConcept> modelMap = new ConcurrentHashMap<>();
+
+ /**
+ * This class is an abstract static class that cannot be extended.
+ */
+ private ModelService() {
+ }
+
+ /**
+ * Register a model with the model service.
+ *
+ * @param <M> the generic type
+ * @param modelClass the class of the model, used to index the model
+ * @param model The model
+ */
+ public static <M extends AxConcept> void registerModel(final Class<M> modelClass, final M model) {
+ modelMap.put(modelClass, model);
+ }
+
+ /**
+ * Remove a model from the model service.
+ *
+ * @param <M> the generic type
+ * @param modelClass the class of the model, used to index the model
+ */
+ public static <M extends AxConcept> void deregisterModel(final Class<M> modelClass) {
+ modelMap.remove(modelClass);
+ }
+
+ /**
+ * Get a model from the model service.
+ *
+ * @param <M> the generic type
+ * @param modelClass the class of the model, used to index the model
+ * @return The model
+ */
+ @SuppressWarnings("unchecked")
+ public static <M extends AxConcept> M getModel(final Class<M> modelClass) {
+ final M model = (M) modelMap.get(modelClass);
+
+ if (model == null) {
+ throw new ApexRuntimeException("Model for " + modelClass.getName() + " not found in model service");
+ }
+
+ return model;
+ }
+
+ /**
+ * Check if a model is defined on the model service.
+ *
+ * @param <M> the generic type
+ * @param modelClass the class of the model, used to index the model
+ * @return true if the model is defined
+ */
+ public static <M extends AxConcept> boolean existsModel(final Class<M> modelClass) {
+ return modelMap.get(modelClass) != null;
+ }
+
+ /**
+ * Clear all models in the model service.
+ */
+ public static void clear() {
+ modelMap.clear();
+ }
+}
diff --git a/model/src/main/java/org/onap/policy/apex/model/basicmodel/service/package-info.java b/model/src/main/java/org/onap/policy/apex/model/basicmodel/service/package-info.java
new file mode 100644
index 000000000..a27e45d1a
--- /dev/null
+++ b/model/src/main/java/org/onap/policy/apex/model/basicmodel/service/package-info.java
@@ -0,0 +1,26 @@
+/*
+ * ============LICENSE_START=======================================================
+ * Copyright (C) 2016-2018 Ericsson. All rights reserved.
+ * ================================================================================
+ * 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=========================================================
+ */
+
+/**
+ * Contains the static services in Apex that make models and parameters available to all objects in the JVM.
+ *
+ * @author Liam Fallon (liam.fallon@ericsson.com)
+ */
+package org.onap.policy.apex.model.basicmodel.service;
diff --git a/model/src/main/java/org/onap/policy/apex/model/basicmodel/test/TestApexModel.java b/model/src/main/java/org/onap/policy/apex/model/basicmodel/test/TestApexModel.java
new file mode 100644
index 000000000..9dde47d05
--- /dev/null
+++ b/model/src/main/java/org/onap/policy/apex/model/basicmodel/test/TestApexModel.java
@@ -0,0 +1,259 @@
+/*
+ * ============LICENSE_START=======================================================
+ * Copyright (C) 2016-2018 Ericsson. All rights reserved.
+ * Modifications Copyright (C) 2021-2022 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.apex.model.basicmodel.test;
+
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.File;
+import org.onap.policy.apex.model.basicmodel.concepts.ApexException;
+import org.onap.policy.apex.model.basicmodel.concepts.AxModel;
+import org.onap.policy.apex.model.basicmodel.concepts.AxValidationResult;
+import org.onap.policy.apex.model.basicmodel.handling.ApexModelFileWriter;
+import org.onap.policy.apex.model.basicmodel.handling.ApexModelReader;
+import org.onap.policy.apex.model.basicmodel.handling.ApexModelWriter;
+import org.onap.policy.common.utils.resources.ResourceUtils;
+import org.slf4j.ext.XLogger;
+import org.slf4j.ext.XLoggerFactory;
+
+/**
+ * This class tests reading and writing of Apex models to file and to a database using JPA. It also tests validation of
+ * Apex models. This class is designed for use in unit tests in modules that define Apex models.
+ *
+ * @author Liam Fallon (liam.fallon@ericsson.com)
+ * @param <M> the generic type
+ */
+public class TestApexModel<M extends AxModel> {
+ private static final String MODEL_IS_INVALID = "model is invalid ";
+ private static final String ERROR_PROCESSING_FILE = "error processing file ";
+ private static final String TEST_MODEL_UNEQUAL_STR = "test model does not equal model read from file ";
+ private static final String TEMP_FILE_CREATE_ERR_STR = "error creating temporary file for Apex model";
+
+ private static final XLogger LOGGER = XLoggerFactory.getXLogger(TestApexModel.class);
+
+ // The root model class that specifies the root to import and export from
+ private final Class<M> rootModelClass;
+
+ // The class that provides the model
+ private TestApexModelCreator<M> modelCreator = null;
+
+ /**
+ * Constructor, defines the subclass of {@link AxModel} that is being tested and the {@link TestApexModelCreator}
+ * object that is used to generate Apex models.
+ *
+ * @param rootModelClass the Apex model class, a sub class of {@link AxModel}
+ * @param modelCreator the @link TestApexModelCreator} that will generate Apex models of various types for testing
+ */
+ public TestApexModel(final Class<M> rootModelClass, final TestApexModelCreator<M> modelCreator) {
+ this.rootModelClass = rootModelClass;
+ this.modelCreator = modelCreator;
+ }
+
+ /**
+ * Get a test Apex model using the model creator.
+ *
+ * @return the test Apex model
+ */
+ public final M getModel() {
+ return modelCreator.getModel();
+ }
+
+ /**
+ * Test write and read in JSON format.
+ *
+ * @throws ApexException on write/read errors
+ */
+ public final void testApexModelWriteReadJson() throws ApexException {
+ LOGGER.debug("running testApexModelWriteReadJSON . . .");
+
+ final var model = modelCreator.getModel();
+
+ // Write the file to disk
+ File jsonFile;
+ try {
+ jsonFile = File.createTempFile("ApexModel", ".json");
+ jsonFile.deleteOnExit();
+ } catch (final Exception e) {
+ LOGGER.warn(TEMP_FILE_CREATE_ERR_STR, e);
+ throw new ApexException(TEMP_FILE_CREATE_ERR_STR, e);
+ }
+ new ApexModelFileWriter<M>(true).apexModelWriteJsonFile(model, rootModelClass, jsonFile.getPath());
+
+ // Read the file from disk
+ final ApexModelReader<M> modelReader = new ApexModelReader<>(rootModelClass);
+
+ try {
+ final var apexModelUrl = ResourceUtils.getLocalFile(jsonFile.getAbsolutePath());
+ final var fileModel = modelReader.read(apexModelUrl.openStream());
+ checkModelEquality(model, fileModel, TEST_MODEL_UNEQUAL_STR + jsonFile.getAbsolutePath());
+ } catch (final Exception e) {
+ LOGGER.warn(ERROR_PROCESSING_FILE + jsonFile.getAbsolutePath(), e);
+ throw new ApexException(ERROR_PROCESSING_FILE + jsonFile.getAbsolutePath(), e);
+ }
+
+ final ApexModelWriter<M> modelWriter = new ApexModelWriter<>(rootModelClass);
+
+ final var baOutputStream = new ByteArrayOutputStream();
+ modelWriter.write(model, baOutputStream);
+ final var baInputStream = new ByteArrayInputStream(baOutputStream.toByteArray());
+ final var byteArrayModel = modelReader.read(baInputStream);
+
+ checkModelEquality(model, byteArrayModel, "test model does not equal JSON marshalled and unmarshalled model");
+
+ LOGGER.debug("ran testApexModelWriteReadJSON");
+ }
+
+ /**
+ * Test that an Apex model is valid.
+ *
+ * @return the result of the validation
+ * @throws ApexException thrown on errors validating the Apex model
+ */
+ public final AxValidationResult testApexModelValid() throws ApexException {
+ LOGGER.debug("running testApexModelVaid . . .");
+
+ final var model = modelCreator.getModel();
+ final AxValidationResult result = model.validate(new AxValidationResult());
+
+ if (!result.isValid()) {
+ String message = MODEL_IS_INVALID + result.toString();
+ LOGGER.warn(message);
+ throw new ApexException(message);
+ }
+
+ LOGGER.debug("ran testApexModelVaid");
+ return result;
+ }
+
+ /**
+ * Test that an Apex model is structured incorrectly.
+ *
+ * @return the result of the validation
+ * @throws ApexException thrown on errors validating the Apex model
+ */
+ public final AxValidationResult testApexModelVaidateMalstructured() throws ApexException {
+ LOGGER.debug("running testApexModelVaidateMalstructured . . .");
+
+ final var model = modelCreator.getMalstructuredModel();
+ final AxValidationResult result = model.validate(new AxValidationResult());
+
+ if (result.isValid()) {
+ String message = "model should not be valid " + result.toString();
+ LOGGER.warn(message);
+ throw new ApexException(message);
+ }
+
+ LOGGER.debug("ran testApexModelVaidateMalstructured");
+ return result;
+ }
+
+ /**
+ * Test that an Apex model has observations.
+ *
+ * @return the result of the validation
+ * @throws ApexException thrown on errors validating the Apex model
+ */
+ public final AxValidationResult testApexModelVaidateObservation() throws ApexException {
+ LOGGER.debug("running testApexModelVaidateObservation . . .");
+
+ final var model = modelCreator.getObservationModel();
+ final AxValidationResult result = model.validate(new AxValidationResult());
+
+ if (!result.isValid()) {
+ String message = MODEL_IS_INVALID + result.toString();
+ LOGGER.warn(message);
+ throw new ApexException(message);
+ }
+
+ if (!result.getValidationResult().equals(AxValidationResult.ValidationResult.OBSERVATION)) {
+ LOGGER.warn("model should have observations");
+ throw new ApexException("model should have observations");
+ }
+
+ LOGGER.debug("ran testApexModelVaidateObservation");
+ return result;
+ }
+
+ /**
+ * Test that an Apex model has warnings.
+ *
+ * @return the result of the validation
+ * @throws ApexException thrown on errors validating the Apex model
+ */
+ public final AxValidationResult testApexModelVaidateWarning() throws ApexException {
+ LOGGER.debug("running testApexModelVaidateWarning . . .");
+
+ final var model = modelCreator.getWarningModel();
+ final AxValidationResult result = model.validate(new AxValidationResult());
+
+ if (!result.isValid()) {
+ String message = MODEL_IS_INVALID + result.toString();
+ LOGGER.warn(message);
+ throw new ApexException(message);
+ }
+
+ if (!result.getValidationResult().equals(AxValidationResult.ValidationResult.WARNING)) {
+ LOGGER.warn("model should have warnings");
+ throw new ApexException("model should have warnings");
+ }
+
+ LOGGER.debug("ran testApexModelVaidateWarning");
+ return result;
+ }
+
+ /**
+ * Test that an Apex model is invalid.
+ *
+ * @return the result of the validation
+ * @throws ApexException thrown on errors validating the Apex model
+ */
+ public final AxValidationResult testApexModelVaidateInvalidModel() throws ApexException {
+ LOGGER.debug("running testApexModelVaidateInvalidModel . . .");
+
+ final var model = modelCreator.getInvalidModel();
+ final AxValidationResult result = model.validate(new AxValidationResult());
+
+ if (result.isValid()) {
+ String message = "model should not be valid " + result.toString();
+ LOGGER.warn(message);
+ throw new ApexException(message);
+ }
+
+ LOGGER.debug("ran testApexModelVaidateInvalidModel");
+ return result;
+ }
+
+ /**
+ * Check if two models are equal.
+ *
+ * @param leftModel the left model
+ * @param rightModel the right model
+ * @param errorMessage the error message to output on inequality
+ * @throws ApexException the exception to throw on inequality
+ */
+ public void checkModelEquality(final M leftModel, final M rightModel, final String errorMessage)
+ throws ApexException {
+ if (!leftModel.equals(rightModel)) {
+ LOGGER.warn(errorMessage);
+ throw new ApexException(errorMessage);
+ }
+ }
+}
diff --git a/model/src/main/java/org/onap/policy/apex/model/basicmodel/test/TestApexModelCreator.java b/model/src/main/java/org/onap/policy/apex/model/basicmodel/test/TestApexModelCreator.java
new file mode 100644
index 000000000..65441a33c
--- /dev/null
+++ b/model/src/main/java/org/onap/policy/apex/model/basicmodel/test/TestApexModelCreator.java
@@ -0,0 +1,62 @@
+/*
+ * ============LICENSE_START=======================================================
+ * Copyright (C) 2016-2018 Ericsson. All rights reserved.
+ * ================================================================================
+ * 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.apex.model.basicmodel.test;
+
+import org.onap.policy.apex.model.basicmodel.concepts.AxModel;
+import org.onap.policy.apex.model.basicmodel.handling.ApexModelCreator;
+
+/**
+ * The Interface TestApexModelCreator is used to create models for Apex model tests. It is mainly used by unit tests for
+ * Apex domain models so that developers can write test Java programs to create models.
+ *
+ * @author Liam Fallon (liam.fallon@ericsson.com)
+ * @param <M> the generic type
+ */
+public interface TestApexModelCreator<M extends AxModel> extends ApexModelCreator<M> {
+
+ /**
+ * Gets the malstructured model.
+ *
+ * @return the malstructured model
+ */
+ M getMalstructuredModel();
+
+ /**
+ * Gets the observation model.
+ *
+ * @return the observation model
+ */
+ M getObservationModel();
+
+ /**
+ * Gets the warning model.
+ *
+ * @return the warning model
+ */
+ M getWarningModel();
+
+ /**
+ * Gets the invalid model.
+ *
+ * @return the invalid model
+ */
+ M getInvalidModel();
+}
diff --git a/model/src/main/java/org/onap/policy/apex/model/basicmodel/test/package-info.java b/model/src/main/java/org/onap/policy/apex/model/basicmodel/test/package-info.java
new file mode 100644
index 000000000..9bbdd1cce
--- /dev/null
+++ b/model/src/main/java/org/onap/policy/apex/model/basicmodel/test/package-info.java
@@ -0,0 +1,26 @@
+/*
+ * ============LICENSE_START=======================================================
+ * Copyright (C) 2016-2018 Ericsson. All rights reserved.
+ * ================================================================================
+ * 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=========================================================
+ */
+
+/**
+ * Contains utility classes that allow testing of validation and reads and writes on APEX models to files and databases.
+ *
+ * @author Liam Fallon (liam.fallon@ericsson.com)
+ */
+package org.onap.policy.apex.model.basicmodel.test;
diff --git a/model/src/main/java/org/onap/policy/apex/model/contextmodel/concepts/AxContextAlbum.java b/model/src/main/java/org/onap/policy/apex/model/contextmodel/concepts/AxContextAlbum.java
new file mode 100644
index 000000000..db64f8847
--- /dev/null
+++ b/model/src/main/java/org/onap/policy/apex/model/contextmodel/concepts/AxContextAlbum.java
@@ -0,0 +1,268 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * Copyright (C) 2016-2018 Ericsson. All rights reserved.
+ * Modifications Copyright (C) 2019-2022 Nordix Foundation.
+ * Modifications Copyright (C) 2021 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.apex.model.contextmodel.concepts;
+
+import java.util.List;
+import lombok.EqualsAndHashCode;
+import lombok.Getter;
+import lombok.Setter;
+import lombok.ToString;
+import org.onap.policy.apex.model.basicmodel.concepts.AxArtifactKey;
+import org.onap.policy.apex.model.basicmodel.concepts.AxConcept;
+import org.onap.policy.apex.model.basicmodel.concepts.AxKey;
+import org.onap.policy.apex.model.basicmodel.concepts.AxKeyUse;
+import org.onap.policy.apex.model.basicmodel.concepts.AxValidationMessage;
+import org.onap.policy.apex.model.basicmodel.concepts.AxValidationResult;
+import org.onap.policy.apex.model.basicmodel.concepts.AxValidationResult.ValidationResult;
+import org.onap.policy.common.utils.validation.Assertions;
+
+/**
+ * This class is used to define an album of context.
+ *
+ * <p>A context album is a distributed map of context that will be distributed across all process instances that require
+ * access to it. This class defines the schema (structure) of the items in the context album, whether the items on the
+ * context album are writable or not, and what the scope of the context album is.
+ *
+ * <p>The structure of items (objects) the context album is defined as a schema, which is understood by whatever schema
+ * implementation is being used for the context album.
+ *
+ * <p>The scope of a context album is a string field, understood by whatever distribution mechanism is being used for
+ * the context album. The distribution mechanism uses the scope of the context album to decide to which executable
+ * entities a given context album is distributed.
+ *
+ * <p>The writable flag on a context album defines whether users of a context album can write to the context album or
+ * just read objects from the context album.
+ *
+ * <p>Validation checks that the album key and the context schema key are not null and that the scope field is not
+ * undefined and matches the regular expression SCOPE_REGEXP.
+ */
+
+@Getter
+@ToString
+@EqualsAndHashCode(callSuper = false)
+
+public class AxContextAlbum extends AxConcept {
+ private static final String SCOPE_STRING = "scope";
+
+ private static final long serialVersionUID = 4290442590545820316L;
+
+ /**
+ * The legal values for the scope of a context album is constrained by this regular expression.
+ */
+ public static final String SCOPE_REGEXP = "[A-Za-z0-9\\-_]+";
+
+ /** The value of scope for a context album for which a scope has not been specified. */
+ public static final String SCOPE_UNDEFINED = "UNDEFINED";
+
+ private AxArtifactKey key;
+ private String scope;
+
+ @Setter
+ private boolean isWritable;
+
+ private AxArtifactKey itemSchema;
+
+ /**
+ * The default constructor creates a context album with a null artifact key. The scope of the context album is set
+ * as SCOPE_UNDEFINED, the album is writable, and the artifact key of the context schema is set to the null artifact
+ * key.
+ */
+ public AxContextAlbum() {
+ this(new AxArtifactKey());
+ scope = SCOPE_UNDEFINED;
+ isWritable = true;
+ itemSchema = AxArtifactKey.getNullKey();
+ }
+
+ /**
+ * Copy constructor.
+ *
+ * @param copyConcept the concept to copy from
+ */
+ public AxContextAlbum(final AxContextAlbum copyConcept) {
+ super(copyConcept);
+ }
+
+ /**
+ * The keyed constructor creates a context album with the specified artifact key. The scope of the context album is
+ * set as SCOPE_UNDEFINED, the album is writable, and the artifact key of the context schema is set to the null
+ * artifact key.
+ *
+ * @param key the key of the context album
+ */
+ public AxContextAlbum(final AxArtifactKey key) {
+ this(key, SCOPE_UNDEFINED, true, AxArtifactKey.getNullKey());
+ }
+
+ /**
+ * Constructor that sets all the fields of the context album.
+ *
+ * @param key the key of the context album
+ * @param scope the scope field, must match the regular expression SCOPE_REGEXP
+ * @param isWritable specifies whether the context album will be writable or not
+ * @param itemSchema the artifact key of the context schema to use for this context album
+ */
+ public AxContextAlbum(final AxArtifactKey key, final String scope, final boolean isWritable,
+ final AxArtifactKey itemSchema) {
+ super();
+ Assertions.argumentNotNull(key, "key may not be null");
+ Assertions.argumentNotNull(scope, "scope may not be null");
+ Assertions.argumentNotNull(itemSchema, "itemSchema may not be null");
+
+ this.key = key;
+ this.scope = Assertions.validateStringParameter(SCOPE_STRING, scope, SCOPE_REGEXP);
+ this.isWritable = isWritable;
+ this.itemSchema = itemSchema;
+ }
+
+ /**
+ * {@inheritDoc}.
+ */
+ @Override
+ public List<AxKey> getKeys() {
+ final List<AxKey> keyList = key.getKeys();
+ keyList.add(new AxKeyUse(itemSchema.getKey()));
+
+ return keyList;
+ }
+
+ /**
+ * Sets the key of the context album.
+ *
+ * @param key the context album key
+ */
+ public void setKey(final AxArtifactKey key) {
+ Assertions.argumentNotNull(key, "key may not be null");
+ this.key = key;
+ }
+
+ /**
+ * Sets the scope of the context album.
+ *
+ * @param scope the context album scope
+ */
+ public void setScope(final String scope) {
+ Assertions.argumentNotNull(scope, "scope may not be null");
+ this.scope = Assertions.validateStringParameter(SCOPE_STRING, scope, SCOPE_REGEXP);
+ }
+
+ /**
+ * Sets the artifact key of the item schema of this context album.
+ *
+ * @param itemSchema the item schema key
+ */
+ public void setItemSchema(final AxArtifactKey itemSchema) {
+ Assertions.argumentNotNull(itemSchema, "itemSchema key may not be null");
+ this.itemSchema = itemSchema;
+ }
+
+ /**
+ * {@inheritDoc}.
+ */
+ @Override
+ public AxValidationResult validate(final AxValidationResult resultIn) {
+ AxValidationResult result = resultIn;
+
+ if (key.equals(AxArtifactKey.getNullKey())) {
+ result.addValidationMessage(new AxValidationMessage(key, this.getClass(), ValidationResult.INVALID,
+ "key is a null key"));
+ }
+ result = key.validate(result);
+
+ if (scope.replaceAll("\\s+$", "").length() == 0 || scope.equals(SCOPE_UNDEFINED)) {
+ result.addValidationMessage(new AxValidationMessage(key, this.getClass(), ValidationResult.INVALID,
+ "scope is not defined"));
+ }
+
+ var stringCheckResult = Assertions.getStringParameterValidationMessage(SCOPE_STRING, scope, SCOPE_REGEXP);
+ if (stringCheckResult != null) {
+ result.addValidationMessage(new AxValidationMessage(key, this.getClass(), ValidationResult.INVALID,
+ "scope invalid-" + stringCheckResult));
+ }
+
+ if (itemSchema.equals(AxArtifactKey.getNullKey())) {
+ result.addValidationMessage(new AxValidationMessage(key, this.getClass(), ValidationResult.INVALID,
+ "itemSchema reference is a null key, an item schema must be specified"));
+ }
+ result = itemSchema.validate(result);
+
+ return result;
+ }
+
+ /**
+ * {@inheritDoc}.
+ */
+ @Override
+ public void clean() {
+ key.clean();
+ scope = Assertions.validateStringParameter(SCOPE_STRING, scope, SCOPE_REGEXP);
+ itemSchema.clean();
+ }
+
+ /**
+ * {@inheritDoc}.
+ */
+ @Override
+ public AxConcept copyTo(final AxConcept target) {
+ Assertions.argumentNotNull(target, "targetObject may not be null");
+
+ final Object copyObject = target;
+ Assertions.instanceOf(copyObject, AxContextAlbum.class);
+
+ final AxContextAlbum copy = ((AxContextAlbum) copyObject);
+ copy.setKey(new AxArtifactKey(key));
+ copy.setScope(scope);
+ copy.setWritable(isWritable);
+ copy.setItemSchema(new AxArtifactKey(itemSchema));
+
+ return copy;
+ }
+
+ /**
+ * {@inheritDoc}.
+ */
+ @Override
+ public int compareTo(final AxConcept otherObj) {
+ if (otherObj == null) {
+ return -1;
+ }
+ if (this == otherObj) {
+ return 0;
+ }
+ if (getClass() != otherObj.getClass()) {
+ return this.hashCode() - otherObj.hashCode();
+ }
+
+ final AxContextAlbum other = (AxContextAlbum) otherObj;
+ if (!key.equals(other.key)) {
+ return key.compareTo(other.key);
+ }
+ if (!scope.equals(other.scope)) {
+ return scope.compareTo(other.scope);
+ }
+ if (isWritable != other.isWritable) {
+ return (isWritable ? 1 : -1);
+ }
+ return itemSchema.compareTo(other.itemSchema);
+ }
+}
diff --git a/model/src/main/java/org/onap/policy/apex/model/contextmodel/concepts/AxContextAlbums.java b/model/src/main/java/org/onap/policy/apex/model/contextmodel/concepts/AxContextAlbums.java
new file mode 100644
index 000000000..54e3ce44b
--- /dev/null
+++ b/model/src/main/java/org/onap/policy/apex/model/contextmodel/concepts/AxContextAlbums.java
@@ -0,0 +1,310 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * Copyright (C) 2016-2018 Ericsson. All rights reserved.
+ * Modifications Copyright (C) 2019-2022 Nordix Foundation.
+ * Modifications Copyright (C) 2021 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.apex.model.contextmodel.concepts;
+
+import java.util.List;
+import java.util.Map;
+import java.util.Map.Entry;
+import java.util.NavigableMap;
+import java.util.Set;
+import java.util.TreeMap;
+import lombok.AccessLevel;
+import lombok.EqualsAndHashCode;
+import lombok.Getter;
+import lombok.ToString;
+import org.onap.policy.apex.model.basicmodel.concepts.AxArtifactKey;
+import org.onap.policy.apex.model.basicmodel.concepts.AxConcept;
+import org.onap.policy.apex.model.basicmodel.concepts.AxConceptGetter;
+import org.onap.policy.apex.model.basicmodel.concepts.AxConceptGetterImpl;
+import org.onap.policy.apex.model.basicmodel.concepts.AxKey;
+import org.onap.policy.apex.model.basicmodel.concepts.AxValidationMessage;
+import org.onap.policy.apex.model.basicmodel.concepts.AxValidationResult;
+import org.onap.policy.apex.model.basicmodel.concepts.AxValidationResult.ValidationResult;
+import org.onap.policy.common.utils.validation.Assertions;
+
+/**
+ * This class is a context album container and holds a map of the context albums for an entire Apex model. All Apex
+ * models that use context albums must have an {@link AxContextAlbums} field. The {@link AxContextAlbums} class
+ * implements the helper methods of the {@link AxConceptGetter} interface to allow {@link AxContextAlbum} instances to
+ * be retrieved by calling methods directly on this class without referencing the contained map.
+ *
+ * <p>Validation checks that the container key is not null. An observation is issued if no context albums are defined in
+ * the container. If context albums do exist, they are checked to ensure that keys and values are not null and that the
+ * map key matches the key in the map value for all album entries. Each context album entry is then validated
+ * individually.
+ */
+@Getter
+@ToString
+@EqualsAndHashCode(callSuper = false)
+public final class AxContextAlbums extends AxConcept implements AxConceptGetter<AxContextAlbum> {
+ private static final long serialVersionUID = -4844259809024470975L;
+
+ private AxArtifactKey key;
+
+ @Getter(AccessLevel.NONE)
+ private Map<AxArtifactKey, AxContextAlbum> albums;
+
+ /**
+ * The Default Constructor creates a {@link AxContextAlbums} object with a null artifact key and creates an empty
+ * context album map.
+ */
+ public AxContextAlbums() {
+ this(new AxArtifactKey());
+ }
+
+ /**
+ * Copy constructor.
+ *
+ * @param copyConcept the concept to copy from
+ */
+ public AxContextAlbums(final AxContextAlbums copyConcept) {
+ super(copyConcept);
+ }
+
+ /**
+ * The Key Constructor creates a {@link AxContextAlbums} object with the given artifact key and creates an empty
+ * context album map.
+ *
+ * @param key the key of the context album container
+ */
+ public AxContextAlbums(final AxArtifactKey key) {
+ this(key, new TreeMap<>());
+ }
+
+ /**
+ * Constructor that creates the context album map with the given albums and key.
+ *
+ * @param key the key of the context album container
+ * @param albums the context albums to place in this context album container
+ */
+ public AxContextAlbums(final AxArtifactKey key, final Map<AxArtifactKey, AxContextAlbum> albums) {
+ super();
+ Assertions.argumentNotNull(key, "key may not be null");
+ Assertions.argumentNotNull(albums, "albums may not be null");
+
+ this.key = key;
+ this.albums = new TreeMap<>();
+ this.albums.putAll(albums);
+ }
+
+ /**
+ * {@inheritDoc}.
+ */
+ @Override
+ public List<AxKey> getKeys() {
+ final List<AxKey> keyList = key.getKeys();
+
+ for (final AxContextAlbum contextAlbum : albums.values()) {
+ keyList.addAll(contextAlbum.getKeys());
+ }
+
+ return keyList;
+ }
+
+ /**
+ * {@inheritDoc}.
+ */
+ @Override
+ public void buildReferences() {
+ albums.values().stream().forEach(album -> album.buildReferences());
+ }
+
+ /**
+ * Sets the key of the context album container.
+ *
+ * @param key the context album container key
+ */
+ public void setKey(final AxArtifactKey key) {
+ Assertions.argumentNotNull(key, "key may not be null");
+ this.key = key;
+ }
+
+ /**
+ * Gets the map of context albums from the context album container.
+ *
+ * @return the context album map
+ */
+ public Map<AxArtifactKey, AxContextAlbum> getAlbumsMap() {
+ return albums;
+ }
+
+ /**
+ * Sets the map of context albums from the context album container.
+ *
+ * @param albumsMap the map of context albums to place in the container
+ */
+ public void setAlbumsMap(final Map<AxArtifactKey, AxContextAlbum> albumsMap) {
+ Assertions.argumentNotNull(albumsMap, "albums may not be null");
+ this.albums = new TreeMap<>();
+ this.albums.putAll(albumsMap);
+ }
+
+ /**
+ * {@inheritDoc}.
+ */
+ @Override
+ public void clean() {
+ key.clean();
+ for (final Entry<AxArtifactKey, AxContextAlbum> contextAlbumEntry : albums.entrySet()) {
+ contextAlbumEntry.getKey().clean();
+ contextAlbumEntry.getValue().clean();
+ }
+ }
+
+ /**
+ * {@inheritDoc}.
+ */
+ @Override
+ public AxValidationResult validate(final AxValidationResult resultIn) {
+ AxValidationResult result = resultIn;
+
+ if (key.equals(AxArtifactKey.getNullKey())) {
+ result.addValidationMessage(new AxValidationMessage(key, this.getClass(), ValidationResult.INVALID,
+ "key is a null key"));
+ }
+
+ result = key.validate(result);
+
+ if (albums.size() == 0) {
+ result.addValidationMessage(new AxValidationMessage(key, this.getClass(), ValidationResult.OBSERVATION,
+ "albums are empty"));
+ } else {
+ for (final Entry<AxArtifactKey, AxContextAlbum> contextAlbumEntry : albums.entrySet()) {
+ if (contextAlbumEntry.getKey().equals(AxArtifactKey.getNullKey())) {
+ result.addValidationMessage(new AxValidationMessage(key, this.getClass(), ValidationResult.INVALID,
+ "key on context album entry " + contextAlbumEntry.getKey()
+ + " may not be the null key"));
+ } else if (contextAlbumEntry.getValue() == null) {
+ result.addValidationMessage(new AxValidationMessage(key, this.getClass(), ValidationResult.INVALID,
+ "value on context album entry " + contextAlbumEntry.getKey() + " may not be null"));
+ } else {
+ validateContextAlbumKey(result, contextAlbumEntry);
+
+ result = contextAlbumEntry.getValue().validate(result);
+ }
+ }
+ }
+
+ return result;
+ }
+
+ private void validateContextAlbumKey(final AxValidationResult result,
+ final Entry<AxArtifactKey, AxContextAlbum> contextAlbumEntry) {
+ if (!contextAlbumEntry.getKey().equals(contextAlbumEntry.getValue().getKey())) {
+ result.addValidationMessage(new AxValidationMessage(key, this.getClass(), ValidationResult.INVALID,
+ "key on context album entry key " + contextAlbumEntry.getKey()
+ + " does not equal context album value key "
+ + contextAlbumEntry.getValue().getKey()));
+ }
+ }
+
+ /**
+ * {@inheritDoc}.
+ */
+ @Override
+ public AxConcept copyTo(final AxConcept target) {
+ Assertions.argumentNotNull(target, "target may not be null");
+
+ final Object copyObject = target;
+ Assertions.instanceOf(copyObject, AxContextAlbums.class);
+
+ final AxContextAlbums copy = ((AxContextAlbums) copyObject);
+ copy.setKey(key);
+ final Map<AxArtifactKey, AxContextAlbum> newContextAlbum = new TreeMap<>();
+ for (final Entry<AxArtifactKey, AxContextAlbum> contextAlbumEntry : albums.entrySet()) {
+ newContextAlbum.put(new AxArtifactKey(contextAlbumEntry.getKey()),
+ new AxContextAlbum(contextAlbumEntry.getValue()));
+ }
+ copy.setAlbumsMap(newContextAlbum);
+
+ return copy;
+ }
+
+ /**
+ * {@inheritDoc}.
+ */
+ @Override
+ public int compareTo(final AxConcept otherObj) {
+ if (otherObj == null) {
+ return -1;
+ }
+ if (this == otherObj) {
+ return 0;
+ }
+ if (getClass() != otherObj.getClass()) {
+ return this.hashCode() - otherObj.hashCode();
+ }
+
+ final AxContextAlbums other = (AxContextAlbums) otherObj;
+ if (!key.equals(other.key)) {
+ return key.compareTo(other.key);
+ }
+ if (!albums.equals(other.albums)) {
+ return (albums.hashCode() - other.albums.hashCode());
+ }
+
+ return 0;
+ }
+
+ /**
+ * {@inheritDoc}.
+ */
+ @Override
+ public AxContextAlbum get(final AxArtifactKey conceptKey) {
+ return new AxConceptGetterImpl<>((NavigableMap<AxArtifactKey, AxContextAlbum>) albums).get(conceptKey);
+ }
+
+ /**
+ * {@inheritDoc}.
+ */
+ @Override
+ public AxContextAlbum get(final String conceptKeyName) {
+ return new AxConceptGetterImpl<>((NavigableMap<AxArtifactKey, AxContextAlbum>) albums).get(conceptKeyName);
+ }
+
+ /**
+ * {@inheritDoc}.
+ */
+ @Override
+ public AxContextAlbum get(final String conceptKeyName, final String conceptKeyVersion) {
+ return new AxConceptGetterImpl<>((NavigableMap<AxArtifactKey, AxContextAlbum>) albums).get(conceptKeyName,
+ conceptKeyVersion);
+ }
+
+ /**
+ * {@inheritDoc}.
+ */
+ @Override
+ public Set<AxContextAlbum> getAll(final String conceptKeyName) {
+ return new AxConceptGetterImpl<>((NavigableMap<AxArtifactKey, AxContextAlbum>) albums).getAll(conceptKeyName);
+ }
+
+ /**
+ * {@inheritDoc}.
+ */
+ @Override
+ public Set<AxContextAlbum> getAll(final String conceptKeyName, final String conceptKeyVersion) {
+ return new AxConceptGetterImpl<>((NavigableMap<AxArtifactKey, AxContextAlbum>) albums).getAll(conceptKeyName,
+ conceptKeyVersion);
+ }
+}
diff --git a/model/src/main/java/org/onap/policy/apex/model/contextmodel/concepts/AxContextModel.java b/model/src/main/java/org/onap/policy/apex/model/contextmodel/concepts/AxContextModel.java
new file mode 100644
index 000000000..1effe8079
--- /dev/null
+++ b/model/src/main/java/org/onap/policy/apex/model/contextmodel/concepts/AxContextModel.java
@@ -0,0 +1,222 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * Copyright (C) 2016-2018 Ericsson. All rights reserved.
+ * Modifications Copyright (C) 2019-2022 Nordix Foundation.
+ * Modifications Copyright (C) 2021 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.apex.model.contextmodel.concepts;
+
+import java.util.List;
+import lombok.EqualsAndHashCode;
+import lombok.Getter;
+import lombok.ToString;
+import org.onap.policy.apex.model.basicmodel.concepts.AxArtifactKey;
+import org.onap.policy.apex.model.basicmodel.concepts.AxConcept;
+import org.onap.policy.apex.model.basicmodel.concepts.AxKey;
+import org.onap.policy.apex.model.basicmodel.concepts.AxKeyInformation;
+import org.onap.policy.apex.model.basicmodel.concepts.AxModel;
+import org.onap.policy.apex.model.basicmodel.concepts.AxValidationResult;
+import org.onap.policy.apex.model.basicmodel.service.ModelService;
+import org.onap.policy.common.utils.validation.Assertions;
+
+/**
+ * A container class for an Apex context model. This class is a container class that allows an Apex model to be
+ * constructed that just contains context and the key information for that context. The model contains schema
+ * definitions and the definitions of context albums that use those schemas. In the case where Apex context is being
+ * used without policy or independent of policy, an Apex context model is sufficient to get Apex context working.
+ *
+ * <p>Validation runs {@link AxModel} validation on the model. In addition, the {@link AxContextSchemas} and
+ * {@link AxContextAlbums} validation is run on the context schemas and albums in the model.
+ */
+@Getter
+@ToString(callSuper = true)
+@EqualsAndHashCode(callSuper = true)
+public class AxContextModel extends AxModel {
+ private static final long serialVersionUID = 8800599637708309945L;
+
+ private AxContextSchemas schemas;
+ private AxContextAlbums albums;
+
+ /**
+ * The Default Constructor creates a {@link AxContextModel} object with a null artifact key and creates an empty
+ * context model.
+ */
+ public AxContextModel() {
+ this(new AxArtifactKey());
+ }
+
+ /**
+ * The Key Constructor creates a {@link AxContextModel} object with the given artifact key and creates an empty
+ * context model.
+ *
+ * @param key the key of the context model
+ */
+ public AxContextModel(final AxArtifactKey key) {
+ this(key, new AxContextSchemas(new AxArtifactKey(key.getName() + "_Schemas", key.getVersion())),
+ new AxContextAlbums(new AxArtifactKey(key.getName() + "_Albums", key.getVersion())),
+ new AxKeyInformation(new AxArtifactKey(key.getName() + "_KeyInfo", key.getVersion())));
+ }
+
+ /**
+ * Copy constructor.
+ *
+ * @param copyConcept the concept to copy from
+ */
+ public AxContextModel(final AxContextModel copyConcept) {
+ super(copyConcept);
+ }
+
+ /**
+ * Constructor that initiates a {@link AxContextModel} with schemas and keys for those schemas. An empty
+ * {@link AxContextAlbums} container is created.
+ *
+ * @param key the key of the context model
+ * @param schemas the context schema definitions
+ * @param keyInformation the key information for those context schemas
+ */
+ public AxContextModel(final AxArtifactKey key, final AxContextSchemas schemas,
+ final AxKeyInformation keyInformation) {
+ this(key, schemas, new AxContextAlbums(new AxArtifactKey(key.getName() + "_Albums", key.getVersion())),
+ keyInformation);
+ }
+
+ /**
+ * Constructor that initiates a {@link AxContextModel} with all its fields.
+ *
+ * @param key the key of the context model
+ * @param schemas the context schema definitions
+ * @param albums the context album container containing context albums
+ * @param keyInformation the key information for those context schemas
+ */
+ public AxContextModel(final AxArtifactKey key, final AxContextSchemas schemas, final AxContextAlbums albums,
+ final AxKeyInformation keyInformation) {
+ super(key, keyInformation);
+ Assertions.argumentNotNull(schemas, "schemas may not be null");
+ Assertions.argumentNotNull(albums, "albums may not be null");
+ this.schemas = schemas;
+ this.albums = albums;
+ }
+
+ /**
+ * {@inheritDoc}.
+ */
+ @Override
+ public void register() {
+ super.register();
+ ModelService.registerModel(AxContextSchemas.class, getSchemas());
+ ModelService.registerModel(AxContextAlbums.class, getAlbums());
+ }
+
+ /**
+ * {@inheritDoc}.
+ */
+ @Override
+ public List<AxKey> getKeys() {
+ final List<AxKey> keyList = super.getKeys();
+
+ keyList.addAll(schemas.getKeys());
+ keyList.addAll(albums.getKeys());
+
+ return keyList;
+ }
+
+ /**
+ * Sets the context schemas on the model.
+ *
+ * @param schemas the context schemas
+ */
+ public void setSchemas(final AxContextSchemas schemas) {
+ Assertions.argumentNotNull(schemas, "schemas may not be null");
+ this.schemas = schemas;
+ }
+
+ /**
+ * Sets the context albums on the model.
+ *
+ * @param albums the context albums
+ */
+ public void setAlbums(final AxContextAlbums albums) {
+ Assertions.argumentNotNull(albums, "albums may not be null");
+ this.albums = albums;
+ }
+
+ /**
+ * {@inheritDoc}.
+ */
+ @Override
+ public AxValidationResult validate(final AxValidationResult resultIn) {
+ AxValidationResult result = resultIn;
+
+ result = super.validate(result);
+ result = schemas.validate(result);
+ return albums.validate(result);
+ }
+
+ /**
+ * {@inheritDoc}.
+ */
+ @Override
+ public void clean() {
+ super.clean();
+ schemas.clean();
+ albums.clean();
+ }
+
+ /**
+ * {@inheritDoc}.
+ */
+ @Override
+ public AxConcept copyTo(final AxConcept target) {
+ Assertions.argumentNotNull(target, "target may not be null");
+
+ final Object copyObject = target;
+ Assertions.instanceOf(copyObject, AxContextModel.class);
+
+ final AxContextModel copy = ((AxContextModel) copyObject);
+ super.copyTo(target);
+ copy.setSchemas(new AxContextSchemas(schemas));
+ copy.setAlbums(new AxContextAlbums(albums));
+
+ return copy;
+ }
+
+ /**
+ * {@inheritDoc}.
+ */
+ @Override
+ public int compareTo(final AxConcept otherObj) {
+ Assertions.argumentNotNull(otherObj, "comparison object may not be null");
+
+ if (this == otherObj) {
+ return 0;
+ }
+ if (getClass() != otherObj.getClass()) {
+ return this.hashCode() - otherObj.hashCode();
+ }
+
+ final AxContextModel other = (AxContextModel) otherObj;
+ if (!super.equals(other)) {
+ return super.compareTo(other);
+ }
+ if (!schemas.equals(other.schemas)) {
+ return schemas.compareTo(other.schemas);
+ }
+ return albums.compareTo(other.albums);
+ }
+}
diff --git a/model/src/main/java/org/onap/policy/apex/model/contextmodel/concepts/AxContextSchema.java b/model/src/main/java/org/onap/policy/apex/model/contextmodel/concepts/AxContextSchema.java
new file mode 100644
index 000000000..61434ca67
--- /dev/null
+++ b/model/src/main/java/org/onap/policy/apex/model/contextmodel/concepts/AxContextSchema.java
@@ -0,0 +1,299 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * Copyright (C) 2016-2018 Ericsson. All rights reserved.
+ * Modifications Copyright (C) 2019-2022 Nordix Foundation.
+ * Modifications Copyright (C) 2021 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.apex.model.contextmodel.concepts;
+
+import java.util.List;
+import lombok.AccessLevel;
+import lombok.Getter;
+import lombok.ToString;
+import org.onap.policy.apex.model.basicmodel.concepts.AxArtifactKey;
+import org.onap.policy.apex.model.basicmodel.concepts.AxConcept;
+import org.onap.policy.apex.model.basicmodel.concepts.AxKey;
+import org.onap.policy.apex.model.basicmodel.concepts.AxValidationMessage;
+import org.onap.policy.apex.model.basicmodel.concepts.AxValidationResult;
+import org.onap.policy.apex.model.basicmodel.concepts.AxValidationResult.ValidationResult;
+import org.onap.policy.common.utils.validation.Assertions;
+
+/**
+ * This class holds a data schema definition in Apex. A data schema describes the structure of a single atom of data
+ * handled by Apex. This atom of data can be a primitive type such as an integer or a string, or it can be a more
+ * complex data type such as a Java object or an object described using a data definition language such as Avro. The
+ * schema flavour defines the type of schema being defined and the schema itself defines the schema. The schema flavour
+ * is used by Apex to look up and load a plugin class that understands and interprets the schema definition and can
+ * create instances of classes for the schema.
+ *
+ * <p>An {@link AxContextSchema} is used to define each parameter in Apex events, the messages that enter, exit, and are
+ * passed internally in Apex. In addition, an Apex {@link AxContextAlbum} instances hold a map of
+ * {@link AxContextSchema} instances to represent the context being managed as an {@link AxContextAlbum}. For example,
+ * the state of all cells in a mobile network might be represented as an {@link AxContextAlbum} with its
+ * {@link AxContextSchema} being defined as @code cell} objects.
+ *
+ * <p>Validation checks that the schema key is not null. It also checks that the schema flavour is defined and matches
+ * the regular expression SCHEMA_FLAVOUR_REGEXP. Finally, validation checks that the defined schema is not a blank or
+ * empty string.
+ */
+@Getter
+@ToString
+public class AxContextSchema extends AxConcept {
+ private static final String SCHEMA_FLAVOUR = "schemaFlavour";
+ private static final String WHITESPACE_REGEXP = "\\s+$";
+
+ private static final long serialVersionUID = -6443016863162692288L;
+
+ /** Regular expression that constrains what values a schema flavour can have. */
+ public static final String SCHEMA_FLAVOUR_REGEXP = "[A-Za-z0-9\\-_]+";
+
+ /** An undefined schema flavour has this value. */
+ public static final String SCHEMA_FLAVOUR_UNDEFINED = "UNDEFINED";
+
+ /** The maximum permissible size of a schema definition. */
+ public static final int MAX_SCHEMA_SIZE = 32672; // The maximum size supported by Apache Derby
+
+ private AxArtifactKey key;
+ private String schemaFlavour;
+
+ @Getter(AccessLevel.NONE)
+ private String schemaDefinition;
+
+ /**
+ * The default constructor creates a context schema with a null artifact key. The flavour of the context album is
+ * set as SCHEMA_FLAVOUR_UNDEFINED and the schema itself is defined as an empty string.
+ */
+ public AxContextSchema() {
+ this(new AxArtifactKey());
+ schemaFlavour = SCHEMA_FLAVOUR_UNDEFINED;
+ }
+
+ /**
+ * Copy constructor.
+ *
+ * @param copyConcept the concept to copy from
+ */
+ public AxContextSchema(final AxContextSchema copyConcept) {
+ super(copyConcept);
+ }
+
+ /**
+ * The key constructor creates a context schema with the given artifact key. The flavour of the context album is set
+ * as SCHEMA_FLAVOUR_UNDEFINED and the schema itself is defined as an empty string.
+ *
+ * @param key the key
+ */
+ public AxContextSchema(final AxArtifactKey key) {
+ this(key, SCHEMA_FLAVOUR_UNDEFINED, "");
+ }
+
+ /**
+ * This Constructor creates a context schema with all of its fields defined.
+ *
+ * @param key the key
+ * @param schemaFlavour the schema flavour
+ * @param schemaDefinition the schema definition
+ */
+ public AxContextSchema(final AxArtifactKey key, final String schemaFlavour, final String schemaDefinition) {
+ super();
+ Assertions.argumentNotNull(key, "key may not be null");
+ Assertions.argumentNotNull(schemaFlavour, "schemaFlavour may not be null");
+ Assertions.argumentNotNull(schemaDefinition, "schemaDefinition may not be null");
+
+ this.key = key;
+ this.schemaFlavour = Assertions.validateStringParameter(SCHEMA_FLAVOUR, schemaFlavour, SCHEMA_FLAVOUR_REGEXP);
+ this.schemaDefinition = schemaDefinition.replaceAll(WHITESPACE_REGEXP, "");
+ }
+
+ /**
+ * {@inheritDoc}.
+ */
+ @Override
+ public List<AxKey> getKeys() {
+ return key.getKeys();
+ }
+
+ /**
+ * Sets the key of the context schema.
+ *
+ * @param key the key of the context schema
+ */
+ public void setKey(final AxArtifactKey key) {
+ Assertions.argumentNotNull(key, "key may not be null");
+ this.key = key;
+ }
+
+ /**
+ * Sets the schema flavour, which defines the type of schema definition being used.
+ *
+ * @param schemaFlavour the schema flavour
+ */
+ public void setSchemaFlavour(final String schemaFlavour) {
+ this.schemaFlavour = Assertions.validateStringParameter(SCHEMA_FLAVOUR, schemaFlavour, SCHEMA_FLAVOUR_REGEXP);
+ }
+
+ /**
+ * Gets the schema, which defines the structure of this data schema atom.
+ *
+ * @return the schema definition
+ */
+ public String getSchema() {
+ return schemaDefinition;
+ }
+
+ /**
+ * Sets the schema, which defines the structure of this data schema atom.
+ *
+ * @param schema the schema definition
+ */
+ public void setSchema(final String schema) {
+ Assertions.argumentNotNull(schema, "schema may not be null");
+ this.schemaDefinition = schema.replaceAll(WHITESPACE_REGEXP, "");
+ }
+
+ /**
+ * {@inheritDoc}.
+ */
+ @Override
+ public AxValidationResult validate(final AxValidationResult resultIn) {
+ AxValidationResult result = resultIn;
+
+ if (key.equals(AxArtifactKey.getNullKey())) {
+ result.addValidationMessage(new AxValidationMessage(key, this.getClass(), ValidationResult.INVALID,
+ "key is a null key"));
+ }
+
+ result = key.validate(result);
+
+ if (schemaFlavour.replaceAll(WHITESPACE_REGEXP, "").length() == 0
+ || schemaFlavour.equals(SCHEMA_FLAVOUR_UNDEFINED)) {
+ result.addValidationMessage(new AxValidationMessage(key, this.getClass(), ValidationResult.INVALID,
+ "schema flavour is not defined"));
+ }
+
+ var flavourValidationResult = Assertions.getStringParameterValidationMessage(SCHEMA_FLAVOUR, schemaFlavour,
+ SCHEMA_FLAVOUR_REGEXP);
+ if (flavourValidationResult != null) {
+ result.addValidationMessage(new AxValidationMessage(key, this.getClass(), ValidationResult.INVALID,
+ "schema flavour invalid-" + flavourValidationResult));
+ }
+
+ if (schemaDefinition.replaceAll(WHITESPACE_REGEXP, "").length() == 0) {
+ result.addValidationMessage(new AxValidationMessage(key, this.getClass(), ValidationResult.INVALID,
+ "no schemaDefinition specified, schemaDefinition may not be blank"));
+ }
+
+ return result;
+ }
+
+ /**
+ * {@inheritDoc}.
+ */
+ @Override
+ public void clean() {
+ key.clean();
+ schemaFlavour = Assertions.validateStringParameter(SCHEMA_FLAVOUR, schemaFlavour, SCHEMA_FLAVOUR_REGEXP);
+ schemaDefinition = schemaDefinition.replaceAll(WHITESPACE_REGEXP, "");
+ }
+
+ /**
+ * {@inheritDoc}.
+ */
+ @Override
+ public AxConcept copyTo(final AxConcept target) {
+ Assertions.argumentNotNull(target, "target may not be null");
+
+ final Object copyObject = target;
+ Assertions.instanceOf(copyObject, AxContextSchema.class);
+
+ final AxContextSchema copy = ((AxContextSchema) copyObject);
+ copy.setKey(new AxArtifactKey(key));
+ copy.setSchemaFlavour(schemaFlavour);
+ copy.setSchema(schemaDefinition);
+
+ return copy;
+ }
+
+ /**
+ * {@inheritDoc}.
+ */
+ @Override
+ public int hashCode() {
+ final var prime = 31;
+ var result = 1;
+ result = prime * result + key.hashCode();
+ result = prime * result + schemaFlavour.hashCode();
+
+ final String thisSchema = schemaDefinition.replace("\n", "");
+ result = prime * result + thisSchema.hashCode();
+ return result;
+ }
+
+ /**
+ * {@inheritDoc}.
+ */
+ @Override
+ public boolean equals(final Object obj) {
+ if (obj == null) {
+ return false;
+ }
+ if (this == obj) {
+ return true;
+ }
+
+ if (getClass() != obj.getClass()) {
+ return false;
+ }
+
+ final AxContextSchema other = (AxContextSchema) obj;
+
+ if (!key.equals(other.key)) {
+ return false;
+ }
+ if (!schemaFlavour.equals(other.schemaFlavour)) {
+ return false;
+ }
+ return schemaDefinition.equals(other.schemaDefinition);
+ }
+
+ /**
+ * {@inheritDoc}.
+ */
+ @Override
+ public int compareTo(final AxConcept otherObj) {
+ if (otherObj == null) {
+ return -1;
+ }
+ if (this == otherObj) {
+ return 0;
+ }
+ if (getClass() != otherObj.getClass()) {
+ return this.hashCode() - otherObj.hashCode();
+ }
+
+ final AxContextSchema other = (AxContextSchema) otherObj;
+ if (!key.equals(other.key)) {
+ return key.compareTo(other.key);
+ }
+ if (!schemaFlavour.equals(other.schemaFlavour)) {
+ return schemaFlavour.compareTo(other.schemaFlavour);
+ }
+ return schemaDefinition.compareTo(other.schemaDefinition);
+ }
+}
diff --git a/model/src/main/java/org/onap/policy/apex/model/contextmodel/concepts/AxContextSchemas.java b/model/src/main/java/org/onap/policy/apex/model/contextmodel/concepts/AxContextSchemas.java
new file mode 100644
index 000000000..5b79a3dcb
--- /dev/null
+++ b/model/src/main/java/org/onap/policy/apex/model/contextmodel/concepts/AxContextSchemas.java
@@ -0,0 +1,304 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * Copyright (C) 2016-2018 Ericsson. All rights reserved.
+ * Modifications Copyright (C) 2019-2022 Nordix Foundation.
+ * Modifications Copyright (C) 2021 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.apex.model.contextmodel.concepts;
+
+import java.util.List;
+import java.util.Map;
+import java.util.Map.Entry;
+import java.util.NavigableMap;
+import java.util.Set;
+import java.util.TreeMap;
+import lombok.AccessLevel;
+import lombok.EqualsAndHashCode;
+import lombok.Getter;
+import lombok.ToString;
+import org.onap.policy.apex.model.basicmodel.concepts.AxArtifactKey;
+import org.onap.policy.apex.model.basicmodel.concepts.AxConcept;
+import org.onap.policy.apex.model.basicmodel.concepts.AxConceptGetter;
+import org.onap.policy.apex.model.basicmodel.concepts.AxConceptGetterImpl;
+import org.onap.policy.apex.model.basicmodel.concepts.AxKey;
+import org.onap.policy.apex.model.basicmodel.concepts.AxValidationMessage;
+import org.onap.policy.apex.model.basicmodel.concepts.AxValidationResult;
+import org.onap.policy.apex.model.basicmodel.concepts.AxValidationResult.ValidationResult;
+import org.onap.policy.common.utils.validation.Assertions;
+
+/**
+ * This class is a context schema container and holds a map of the context schemas for an entire Apex model. All Apex
+ * models that use context schemas must have an {@link AxContextSchemas} field. The {@link AxContextSchemas} class
+ * implements the helper methods of the {@link AxConceptGetter} interface to allow {@link AxContextSchema} instances to
+ * be retrieved by calling methods directly on this class without referencing the contained map.
+ *
+ * <p>Validation checks that the container key is not null. An error is issued if no context schemas are defined in the
+ * container. Each context schema entry is checked to ensure that its key and value are not null and that the key
+ * matches the key in the map value. Each context schema entry is then validated individually.
+ */
+@Getter
+@ToString
+@EqualsAndHashCode(callSuper = false)
+public class AxContextSchemas extends AxConcept implements AxConceptGetter<AxContextSchema> {
+ private static final long serialVersionUID = -3203734282886453582L;
+
+ private AxArtifactKey key;
+
+ @Getter(AccessLevel.NONE)
+ private Map<AxArtifactKey, AxContextSchema> schemas;
+
+ /**
+ * The Default Constructor creates a {@link AxContextSchemas} object with a null artifact key and creates an empty
+ * context schemas map.
+ */
+ public AxContextSchemas() {
+ this(new AxArtifactKey());
+ }
+
+ /**
+ * Copy constructor.
+ *
+ * @param copyConcept the concept to copy from
+ */
+ public AxContextSchemas(final AxContextSchemas copyConcept) {
+ super(copyConcept);
+ }
+
+ /**
+ * The Key Constructor creates a {@link AxContextSchemas} object with the given artifact key and creates an empty
+ * context schemas map.
+ *
+ * @param key the key of the context album container
+ */
+ public AxContextSchemas(final AxArtifactKey key) {
+ this(key, new TreeMap<>());
+ }
+
+ /**
+ * This Constructor creates a {@link AxContextSchemas} object with all its fields defined.
+ *
+ * @param key the key of the context schema container
+ * @param schemas a map of the schemas to insert in the context schema container
+ */
+ public AxContextSchemas(final AxArtifactKey key, final Map<AxArtifactKey, AxContextSchema> schemas) {
+ super();
+ Assertions.argumentNotNull(key, "key may not be null");
+ Assertions.argumentNotNull(schemas, "schemas may not be null");
+
+ this.key = key;
+ this.schemas = new TreeMap<>();
+ this.schemas.putAll(schemas);
+ }
+
+ /**
+ * {@inheritDoc}.
+ */
+ @Override
+ public List<AxKey> getKeys() {
+ final List<AxKey> keyList = key.getKeys();
+ keyList.addAll(schemas.keySet());
+
+ return keyList;
+ }
+
+ /**
+ * {@inheritDoc}.
+ */
+ @Override
+ public void buildReferences() {
+ schemas.values().stream().forEach(schema -> schema.buildReferences());
+ }
+
+ /**
+ * Sets the key of the context schema container.
+ *
+ * @param key the key of the container
+ */
+ public void setKey(final AxArtifactKey key) {
+ Assertions.argumentNotNull(key, "key may not be null");
+ this.key = key;
+ }
+
+ /**
+ * Gets the map of context schemas in this container.
+ *
+ * @return the map of schemas
+ */
+ public Map<AxArtifactKey, AxContextSchema> getSchemasMap() {
+ return schemas;
+ }
+
+ /**
+ * Sets the map of context schemas in this container.
+ *
+ * @param schemasMap the map of schemas
+ */
+ public void setSchemasMap(final Map<AxArtifactKey, AxContextSchema> schemasMap) {
+ Assertions.argumentNotNull(schemasMap, "schemasMap may not be null");
+
+ this.schemas = new TreeMap<>();
+ this.schemas.putAll(schemasMap);
+ }
+
+ /**
+ * {@inheritDoc}.
+ */
+ @Override
+ public AxValidationResult validate(final AxValidationResult resultIn) {
+ AxValidationResult result = resultIn;
+
+ if (key.equals(AxArtifactKey.getNullKey())) {
+ result.addValidationMessage(new AxValidationMessage(key, this.getClass(), ValidationResult.INVALID,
+ "key is a null key"));
+ }
+
+ result = key.validate(result);
+
+ if (schemas.size() == 0) {
+ result.addValidationMessage(new AxValidationMessage(key, this.getClass(), ValidationResult.INVALID,
+ "contextSchemas may not be empty"));
+ } else {
+ for (final Entry<AxArtifactKey, AxContextSchema> contextSchemaEntry : schemas.entrySet()) {
+ if (contextSchemaEntry.getKey().equals(AxArtifactKey.getNullKey())) {
+ result.addValidationMessage(new AxValidationMessage(key, this.getClass(), ValidationResult.INVALID,
+ "key on schemas entry " + contextSchemaEntry.getKey()
+ + " may not be the null key"));
+ } else if (contextSchemaEntry.getValue() == null) {
+ result.addValidationMessage(new AxValidationMessage(key, this.getClass(), ValidationResult.INVALID,
+ "value on schemas entry " + contextSchemaEntry.getKey() + " may not be null"));
+ } else {
+ if (!contextSchemaEntry.getKey().equals(contextSchemaEntry.getValue().getKey())) {
+ result.addValidationMessage(
+ new AxValidationMessage(key, this.getClass(), ValidationResult.INVALID,
+ "key on schemas entry " + contextSchemaEntry.getKey()
+ + " does not equal entry key "
+ + contextSchemaEntry.getValue().getKey()));
+ }
+
+ result = contextSchemaEntry.getValue().validate(result);
+ }
+ }
+ }
+
+ return result;
+ }
+
+ /**
+ * {@inheritDoc}.
+ */
+ @Override
+ public void clean() {
+ key.clean();
+ for (final Entry<AxArtifactKey, AxContextSchema> contextSchemaEntry : schemas.entrySet()) {
+ contextSchemaEntry.getKey().clean();
+ contextSchemaEntry.getValue().clean();
+ }
+ }
+
+ /**
+ * {@inheritDoc}.
+ */
+ @Override
+ public AxConcept copyTo(final AxConcept target) {
+ Assertions.argumentNotNull(target, "target may not be null");
+
+ final Object copyObject = target;
+ Assertions.instanceOf(copyObject, AxContextSchemas.class);
+
+ final AxContextSchemas copy = ((AxContextSchemas) copyObject);
+ copy.setKey(new AxArtifactKey(key));
+
+ final Map<AxArtifactKey, AxContextSchema> newcontextSchemas = new TreeMap<>();
+ for (final Entry<AxArtifactKey, AxContextSchema> contextSchemasEntry : schemas.entrySet()) {
+ newcontextSchemas.put(new AxArtifactKey(contextSchemasEntry.getKey()),
+ new AxContextSchema(contextSchemasEntry.getValue()));
+ }
+ copy.setSchemasMap(newcontextSchemas);
+
+ return copy;
+ }
+
+ /**
+ * {@inheritDoc}.
+ */
+ @Override
+ public int compareTo(final AxConcept otherObj) {
+ if (otherObj == null) {
+ return -1;
+ }
+ if (this == otherObj) {
+ return 0;
+ }
+ if (getClass() != otherObj.getClass()) {
+ return this.hashCode() - otherObj.hashCode();
+ }
+
+ final AxContextSchemas other = (AxContextSchemas) otherObj;
+ if (!key.equals(other.key)) {
+ return key.compareTo(other.key);
+ }
+ if (!schemas.equals(other.schemas)) {
+ return (schemas.hashCode() - other.schemas.hashCode());
+ }
+
+ return 0;
+ }
+
+ /**
+ * {@inheritDoc}.
+ */
+ @Override
+ public AxContextSchema get(final AxArtifactKey conceptKey) {
+ return new AxConceptGetterImpl<>((NavigableMap<AxArtifactKey, AxContextSchema>) schemas).get(conceptKey);
+ }
+
+ /**
+ * {@inheritDoc}.
+ */
+ @Override
+ public AxContextSchema get(final String conceptKeyName) {
+ return new AxConceptGetterImpl<>((NavigableMap<AxArtifactKey, AxContextSchema>) schemas).get(conceptKeyName);
+ }
+
+ /**
+ * {@inheritDoc}.
+ */
+ @Override
+ public AxContextSchema get(final String conceptKeyName, final String conceptKeyVersion) {
+ return new AxConceptGetterImpl<>((NavigableMap<AxArtifactKey, AxContextSchema>) schemas).get(conceptKeyName,
+ conceptKeyVersion);
+ }
+
+ /**
+ * {@inheritDoc}.
+ */
+ @Override
+ public Set<AxContextSchema> getAll(final String conceptKeyName) {
+ return new AxConceptGetterImpl<>((NavigableMap<AxArtifactKey, AxContextSchema>) schemas).getAll(conceptKeyName);
+ }
+
+ /**
+ * {@inheritDoc}.
+ */
+ @Override
+ public Set<AxContextSchema> getAll(final String conceptKeyName, final String conceptKeyVersion) {
+ return new AxConceptGetterImpl<>((NavigableMap<AxArtifactKey, AxContextSchema>) schemas).getAll(conceptKeyName,
+ conceptKeyVersion);
+ }
+}
diff --git a/model/src/main/java/org/onap/policy/apex/model/contextmodel/concepts/package-info.java b/model/src/main/java/org/onap/policy/apex/model/contextmodel/concepts/package-info.java
new file mode 100644
index 000000000..a7aa3a5ad
--- /dev/null
+++ b/model/src/main/java/org/onap/policy/apex/model/contextmodel/concepts/package-info.java
@@ -0,0 +1,28 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * Copyright (C) 2016-2018 Ericsson. All rights reserved.
+ * ================================================================================
+ * 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=========================================================
+ */
+
+/**
+ * Contains the concepts required to manage context in APEX. It defines the main APEX concepts of Context Schemas and
+ * Context Albums.
+ *
+ * @author Liam Fallon (liam.fallon@ericsson.com)
+ */
+
+package org.onap.policy.apex.model.contextmodel.concepts;
diff --git a/model/src/main/java/org/onap/policy/apex/model/contextmodel/handling/ContextComparer.java b/model/src/main/java/org/onap/policy/apex/model/contextmodel/handling/ContextComparer.java
new file mode 100644
index 000000000..7a5ebfd22
--- /dev/null
+++ b/model/src/main/java/org/onap/policy/apex/model/contextmodel/handling/ContextComparer.java
@@ -0,0 +1,70 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * Copyright (C) 2016-2018 Ericsson. All rights reserved.
+ * ================================================================================
+ * 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.apex.model.contextmodel.handling;
+
+import org.onap.policy.apex.model.basicmodel.concepts.AxArtifactKey;
+import org.onap.policy.apex.model.contextmodel.concepts.AxContextAlbum;
+import org.onap.policy.apex.model.contextmodel.concepts.AxContextAlbums;
+import org.onap.policy.apex.model.contextmodel.concepts.AxContextSchema;
+import org.onap.policy.apex.model.contextmodel.concepts.AxContextSchemas;
+import org.onap.policy.apex.model.utilities.comparison.KeyedMapComparer;
+import org.onap.policy.apex.model.utilities.comparison.KeyedMapDifference;
+
+/**
+ * This class compares the context in two AxContext objects and returns the differences. The
+ * differences are returned in a {@link KeyedMapDifference} object that contains the left, equal,
+ * and right context schemas or albums.
+ *
+ * @author Liam Fallon (liam.fallon@ericsson.com)
+ */
+public class ContextComparer {
+
+ /**
+ * Compare two {@link AxContextAlbums} objects, comparing their context albums one after
+ * another.
+ *
+ * @param left the left context
+ * @param right the right context
+ * @return the difference
+ */
+ public KeyedMapDifference<AxArtifactKey, AxContextAlbum> compare(final AxContextAlbums left,
+ final AxContextAlbums right) {
+ // Find the difference between the AxContext objects
+ return new KeyedMapComparer<AxArtifactKey, AxContextAlbum>().compareMaps(left.getAlbumsMap(),
+ right.getAlbumsMap());
+ }
+
+ /**
+ * Compare two {@link AxContextSchema} objects, comparing their context schemas one after
+ * another.
+ *
+ * @param left the left context
+ * @param right the right context
+ * @return the difference
+ */
+ public KeyedMapDifference<AxArtifactKey, AxContextSchema> compare(final AxContextSchemas left,
+ final AxContextSchemas right) {
+ // Find the difference between the AxContext objects
+ return new KeyedMapComparer<AxArtifactKey, AxContextSchema>().compareMaps(left.getSchemasMap(),
+ right.getSchemasMap());
+ }
+
+}
diff --git a/model/src/main/java/org/onap/policy/apex/model/contextmodel/handling/package-info.java b/model/src/main/java/org/onap/policy/apex/model/contextmodel/handling/package-info.java
new file mode 100644
index 000000000..ec1fa893f
--- /dev/null
+++ b/model/src/main/java/org/onap/policy/apex/model/contextmodel/handling/package-info.java
@@ -0,0 +1,27 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * Copyright (C) 2016-2018 Ericsson. All rights reserved.
+ * ================================================================================
+ * 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=========================================================
+ */
+
+/**
+ * Provides some helper classes for handling context, including a utility class for comparing two
+ * context objects.
+ *
+ * @author Liam Fallon (liam.fallon@ericsson.com)
+ */
+package org.onap.policy.apex.model.contextmodel.handling;
diff --git a/model/src/main/java/org/onap/policy/apex/model/enginemodel/concepts/AxEngineModel.java b/model/src/main/java/org/onap/policy/apex/model/enginemodel/concepts/AxEngineModel.java
new file mode 100644
index 000000000..1a590a411
--- /dev/null
+++ b/model/src/main/java/org/onap/policy/apex/model/enginemodel/concepts/AxEngineModel.java
@@ -0,0 +1,338 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * Copyright (C) 2016-2018 Ericsson. All rights reserved.
+ * Modifications Copyright (C) 2019,2022 Nordix Foundation.
+ * Modifications Copyright (C) 2021 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.apex.model.enginemodel.concepts;
+
+import java.text.SimpleDateFormat;
+import java.util.List;
+import org.onap.policy.apex.model.basicmodel.concepts.AxArtifactKey;
+import org.onap.policy.apex.model.basicmodel.concepts.AxConcept;
+import org.onap.policy.apex.model.basicmodel.concepts.AxKey;
+import org.onap.policy.apex.model.basicmodel.concepts.AxKeyInformation;
+import org.onap.policy.apex.model.basicmodel.concepts.AxReferenceKey;
+import org.onap.policy.apex.model.basicmodel.concepts.AxValidationMessage;
+import org.onap.policy.apex.model.basicmodel.concepts.AxValidationResult;
+import org.onap.policy.apex.model.basicmodel.concepts.AxValidationResult.ValidationResult;
+import org.onap.policy.apex.model.contextmodel.concepts.AxContextAlbums;
+import org.onap.policy.apex.model.contextmodel.concepts.AxContextModel;
+import org.onap.policy.apex.model.contextmodel.concepts.AxContextSchemas;
+import org.onap.policy.common.utils.validation.Assertions;
+
+/**
+ * A container class for an Apex engine model. This class is a container class that allows an Apex
+ * model to be constructed that contains the current context {@link AxContextModel}, current state
+ * {@link AxEngineState} and current statistics {@link AxEngineStats} of an Apex engine. This model
+ * is used by an Apex engine to pass its current execution state to any system that wishes to query
+ * that information. The time stamp of the engine model is the time at which the state and
+ * statistics of the engine were read.
+ *
+ * <p>Validation checks that the current state {@link AxEngineState} is defined and that the time stamp
+ * is set on the engine model.
+ */
+public class AxEngineModel extends AxContextModel {
+ private static final long serialVersionUID = 6381235864606564046L;
+ private static final int HASH_CODE_PRIME = 32;
+
+ private long timestamp;
+ private AxEngineState state;
+ private AxEngineStats stats;
+
+ /**
+ * The Default Constructor creates an engine model with a null key and all its fields undefined.
+ */
+ public AxEngineModel() {
+ this(new AxArtifactKey());
+ timestamp = -1;
+ }
+
+ /**
+ * Copy constructor.
+ *
+ * @param copyConcept the concept to copy from
+ */
+ public AxEngineModel(final AxEngineModel copyConcept) {
+ super(copyConcept);
+ }
+
+ /**
+ * The Keyed Constructor creates an engine model with the given key and all its fields
+ * undefined.
+ *
+ * @param key the engine model key
+ */
+ public AxEngineModel(final AxArtifactKey key) {
+ this(key, new AxContextSchemas(new AxArtifactKey(key.getName() + "_DataTypes", key.getVersion())),
+ new AxKeyInformation(new AxArtifactKey(key.getName() + "_KeyInfo", key.getVersion())),
+ new AxContextAlbums(new AxArtifactKey(key.getName() + "_Context", key.getVersion())));
+ }
+
+ /**
+ * This Constructor creates an engine model with its context model data types all defined, the
+ * state of the engine model is undefined.
+ *
+ * @param key the engine model key
+ * @param contextSchemas the context schemas used by the engine model
+ * @param keyInformation the key information used by the engine model
+ * @param contextAlbums the context albums used by the engine model
+ */
+ public AxEngineModel(final AxArtifactKey key, final AxContextSchemas contextSchemas,
+ final AxKeyInformation keyInformation, final AxContextAlbums contextAlbums) {
+ this(key, contextSchemas, keyInformation, contextAlbums, AxEngineState.UNDEFINED,
+ new AxEngineStats(new AxReferenceKey(key, "_EngineStats", key.getVersion())));
+ }
+
+ /**
+ * This Constructor creates an engine model with all its fields defined.
+ *
+ * @param key the engine model key
+ * @param contextSchemas the context schemas used by the engine model
+ * @param keyInformation the key information used by the engine model
+ * @param contextAlbums the context albums used by the engine model
+ * @param state the state of the engine in the engine model
+ * @param stats the statistics of the engine in the engine model
+ */
+ public AxEngineModel(final AxArtifactKey key, final AxContextSchemas contextSchemas,
+ final AxKeyInformation keyInformation, final AxContextAlbums contextAlbums, final AxEngineState state,
+ final AxEngineStats stats) {
+ super(key, contextSchemas, contextAlbums, keyInformation);
+ Assertions.argumentNotNull(state, "state may not be null");
+ Assertions.argumentNotNull(stats, "stats may not be null");
+
+ this.state = state;
+ this.stats = stats;
+ }
+
+ /**
+ * {@inheritDoc}.
+ */
+ @Override
+ public List<AxKey> getKeys() {
+ final List<AxKey> keyList = super.getKeys();
+ keyList.addAll(stats.getKeys());
+ return keyList;
+ }
+
+ /**
+ * Gets the time stamp at which the engine model measurements were taken.
+ *
+ * @return the time stamp at which the engine model measurements were taken
+ */
+ public long getTimestamp() {
+ return timestamp;
+ }
+
+ /**
+ * Gets the time stamp at which the engine model measurements were taken as a string.
+ *
+ * @return the time stamp string
+ */
+ public String getTimeStampString() {
+ return new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS").format(timestamp);
+ }
+
+ /**
+ * Sets the time stamp at which the engine model measurements were taken.
+ *
+ * @param timestamp the time stamp at which the engine model measurements were taken
+ */
+ public void setTimestamp(final long timestamp) {
+ this.timestamp = timestamp;
+ }
+
+ /**
+ * Gets the state of the engine at the time the measurements were taken.
+ *
+ * @return the state of the engine at the time the measurements were taken
+ */
+ public AxEngineState getState() {
+ return state;
+ }
+
+ /**
+ * Sets the state of the engine.
+ *
+ * @param state the state of the engine
+ */
+ public void setState(final AxEngineState state) {
+ Assertions.argumentNotNull(state, "state may not be null");
+ this.state = state;
+ }
+
+ /**
+ * Gets the statistics of the engine at the time the measurements were taken.
+ *
+ * @return the statistics of the engine at the time the measurements were taken
+ */
+ public AxEngineStats getStats() {
+ return stats;
+ }
+
+ /**
+ * Sets the the statistics of the engine.
+ *
+ * @param stats the the statistics of the engine
+ */
+ public void setStats(final AxEngineStats stats) {
+ Assertions.argumentNotNull(stats, "stats may not be null");
+ this.stats = stats;
+ }
+
+ /**
+ * {@inheritDoc}.
+ */
+ @Override
+ public AxValidationResult validate(final AxValidationResult resultIn) {
+ AxValidationResult result = resultIn;
+
+ result = stats.validate(result);
+
+ if (timestamp == -1) {
+ result.addValidationMessage(new AxValidationMessage(getKey(), this.getClass(), ValidationResult.INVALID,
+ this.getClass().getSimpleName() + " - timestamp is not set"));
+ }
+
+ if (state == AxEngineState.UNDEFINED) {
+ result.addValidationMessage(new AxValidationMessage(getKey(), this.getClass(), ValidationResult.INVALID,
+ this.getClass().getSimpleName() + " - state is UNDEFINED"));
+ }
+
+ return result;
+ }
+
+ /**
+ * {@inheritDoc}.
+ */
+ @Override
+ public void clean() {
+ super.clean();
+ stats.clean();
+ }
+
+ /**
+ * {@inheritDoc}.
+ */
+ @Override
+ public String toString() {
+ final StringBuilder builder = new StringBuilder();
+ builder.append(this.getClass().getSimpleName());
+ builder.append(":(");
+ builder.append(super.toString());
+ builder.append(",timestamp=");
+ builder.append(timestamp);
+ builder.append(",state=");
+ builder.append(state);
+ builder.append(",stats=");
+ builder.append(stats);
+ builder.append(")");
+ return builder.toString();
+ }
+
+ /**
+ * {@inheritDoc}.
+ */
+ @Override
+ public AxConcept copyTo(final AxConcept targetObject) {
+ Assertions.argumentNotNull(targetObject, "target may not be null");
+
+ final Object copyObject = targetObject;
+ Assertions.instanceOf(copyObject, AxEngineModel.class);
+
+ final AxEngineModel copy = ((AxEngineModel) copyObject);
+ super.copyTo(targetObject);
+ copy.timestamp = timestamp;
+ copy.setState(state);
+ copy.setStats(new AxEngineStats(stats));
+
+ return copy;
+ }
+
+ /**
+ * {@inheritDoc}.
+ */
+ @Override
+ public int hashCode() {
+ final int prime = 31;
+ int result = 1;
+ result = prime * result + super.hashCode();
+ result = prime * result + (int) (timestamp ^ (timestamp >>> HASH_CODE_PRIME));
+ result = prime * result + state.hashCode();
+ result = prime * result + stats.hashCode();
+ return result;
+ }
+
+ /**
+ * {@inheritDoc}.
+ */
+ @Override
+ public boolean equals(final Object obj) {
+ if (obj == null) {
+ throw new IllegalArgumentException("comparison object may not be null");
+ }
+
+ if (this == obj) {
+ return true;
+ }
+
+ if (getClass() != obj.getClass()) {
+ return false;
+ }
+
+ final AxEngineModel other = (AxEngineModel) obj;
+ if (!super.equals(other)) {
+ return false;
+ }
+ if (timestamp != other.timestamp) {
+ return false;
+ }
+ if (!state.equals(other.state)) {
+ return false;
+ }
+ return stats.equals(other.stats);
+ }
+
+ /**
+ * {@inheritDoc}.
+ */
+ @Override
+ public int compareTo(final AxConcept otherObj) {
+ Assertions.argumentNotNull(otherObj, "comparison object may not be null");
+
+ if (this == otherObj) {
+ return 0;
+ }
+ if (getClass() != otherObj.getClass()) {
+ return this.hashCode() - otherObj.hashCode();
+ }
+
+ final AxEngineModel other = (AxEngineModel) otherObj;
+ if (!super.equals(other)) {
+ return super.compareTo(other);
+ }
+ if (timestamp != other.timestamp) {
+ return (int) (timestamp - other.timestamp);
+ }
+ if (!state.equals(other.state)) {
+ return state.compareTo(other.state);
+ }
+ return stats.compareTo(other.stats);
+ }
+}
diff --git a/model/src/main/java/org/onap/policy/apex/model/enginemodel/concepts/AxEngineState.java b/model/src/main/java/org/onap/policy/apex/model/enginemodel/concepts/AxEngineState.java
new file mode 100644
index 000000000..24c564b96
--- /dev/null
+++ b/model/src/main/java/org/onap/policy/apex/model/enginemodel/concepts/AxEngineState.java
@@ -0,0 +1,52 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * Copyright (C) 2016-2018 Ericsson. All rights reserved.
+ * Modifications Copyright (C) 2022 Bell Canada. All rights reserved.
+ * Modifications Copyright (C) 2022 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.apex.model.enginemodel.concepts;
+
+/**
+ * This enumeration indicates the execution state of an Apex engine.
+ *
+ * @author Liam Fallon (liam.fallon@ericsson.com)
+ */
+
+public enum AxEngineState {
+ /** The state of the engine is not known. */
+ UNDEFINED(0),
+ /** The engine is stopped. */
+ STOPPED(1),
+ /** The engine is running and is waiting to execute a policy. */
+ READY(2),
+ /** The engine is running and is executing a policy. */
+ EXECUTING(3),
+ /** The engine has been ordered to stop and is stopping. */
+ STOPPING(4);
+
+ private final int stateIdentifier;
+
+ AxEngineState(int stateIdentifier) {
+ this.stateIdentifier = stateIdentifier;
+ }
+
+ public int getStateIdentifier() {
+ return stateIdentifier;
+ }
+}
diff --git a/model/src/main/java/org/onap/policy/apex/model/enginemodel/concepts/AxEngineStats.java b/model/src/main/java/org/onap/policy/apex/model/enginemodel/concepts/AxEngineStats.java
new file mode 100644
index 000000000..1420d1e0d
--- /dev/null
+++ b/model/src/main/java/org/onap/policy/apex/model/enginemodel/concepts/AxEngineStats.java
@@ -0,0 +1,533 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * Copyright (C) 2016-2018 Ericsson. All rights reserved.
+ * Modifications Copyright (C) 2019-2020,2022 Nordix Foundation.
+ * Modifications Copyright (C) 2022 Bell Canada.
+ * ================================================================================
+ * 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.apex.model.enginemodel.concepts;
+
+import io.prometheus.client.Gauge;
+import io.prometheus.client.Histogram;
+import java.text.SimpleDateFormat;
+import java.util.List;
+import lombok.Getter;
+import org.onap.policy.apex.model.basicmodel.concepts.AxArtifactKey;
+import org.onap.policy.apex.model.basicmodel.concepts.AxConcept;
+import org.onap.policy.apex.model.basicmodel.concepts.AxKey;
+import org.onap.policy.apex.model.basicmodel.concepts.AxReferenceKey;
+import org.onap.policy.apex.model.basicmodel.concepts.AxValidationMessage;
+import org.onap.policy.apex.model.basicmodel.concepts.AxValidationResult;
+import org.onap.policy.apex.model.basicmodel.concepts.AxValidationResult.ValidationResult;
+import org.onap.policy.common.utils.resources.PrometheusUtils;
+import org.onap.policy.common.utils.validation.Assertions;
+
+/**
+ * This class is a java bean that is used to record statistics on Apex engines as they execute. Statistics on the number
+ * of events, the amount of time taken to execute the last policy, the average policy execution time, the up time of the
+ * engine, and the time stamp of the last engine start are recorded.
+ */
+
+public class AxEngineStats extends AxConcept {
+ private static final long serialVersionUID = -6981129081962785368L;
+ private static final int HASH_CODE_PRIME = 32;
+ static final String ENGINE_INSTANCE_ID = "engine_instance_id";
+ static final Gauge ENGINE_EVENT_EXECUTIONS = Gauge.build().name("engine_event_executions")
+ .namespace(PrometheusUtils.PdpType.PDPA.getNamespace()).labelNames(ENGINE_INSTANCE_ID)
+ .help("Total number of APEX events processed by the engine.").register();
+ static final Gauge ENGINE_UPTIME = Gauge.build().name("engine_uptime")
+ .namespace(PrometheusUtils.PdpType.PDPA.getNamespace()).labelNames(ENGINE_INSTANCE_ID)
+ .help("Time elapsed since the engine was started.").register();
+ static final Gauge ENGINE_START_TIMESTAMP = Gauge.build().name("engine_last_start_timestamp_epoch")
+ .namespace(PrometheusUtils.PdpType.PDPA.getNamespace()).labelNames(ENGINE_INSTANCE_ID)
+ .help("Epoch timestamp of the instance when engine was last started.").register();
+ static final Gauge ENGINE_AVG_EXECUTION_TIME = Gauge.build().name("engine_average_execution_time_seconds")
+ .namespace(PrometheusUtils.PdpType.PDPA.getNamespace()).labelNames(ENGINE_INSTANCE_ID)
+ .help("Average time taken to execute an APEX policy in seconds.").register();
+ static final Histogram ENGINE_LAST_EXECUTION_TIME = Histogram.build()
+ .namespace(PrometheusUtils.PdpType.PDPA.getNamespace())
+ .name("engine_last_execution_time").labelNames(ENGINE_INSTANCE_ID)
+ .help("Time taken to execute the last APEX policy in seconds.").register();
+
+ private AxReferenceKey key;
+ private long timeStamp;
+ private long eventCount;
+ private long lastExecutionTime;
+ private double averageExecutionTime;
+ private long upTime;
+
+ @Getter
+ private transient long lastEnterTime;
+ private long lastStart;
+
+ /**
+ * The Default Constructor creates an engine statistics instance with a null key and with all values cleared.
+ */
+ public AxEngineStats() {
+ this(new AxReferenceKey());
+ timeStamp = 0;
+ eventCount = 0;
+ lastExecutionTime = 0;
+ averageExecutionTime = 0;
+ upTime = 0;
+ lastEnterTime = 0;
+ lastStart = 0;
+ initEngineMetricsWithPrometheus();
+ }
+
+ /**
+ * Register the APEX engine metrics with Prometheus.
+ */
+ private void initEngineMetricsWithPrometheus() {
+ var engineId = getKey().getParentArtifactKey().getId();
+ if (engineId.startsWith(AxKey.NULL_KEY_NAME)) {
+ return;
+ }
+ ENGINE_UPTIME.labels(engineId).set(upTime / 1000d);
+ ENGINE_EVENT_EXECUTIONS.labels(engineId).set(this.eventCount);
+ ENGINE_START_TIMESTAMP.labels(engineId).set(this.lastStart);
+ ENGINE_AVG_EXECUTION_TIME.labels(engineId).set(this.averageExecutionTime / 1000d);
+ ENGINE_LAST_EXECUTION_TIME.labels(engineId).observe(this.lastExecutionTime / 1000d);
+ }
+
+ /**
+ * Copy constructor.
+ *
+ * @param copyConcept the concept to copy from
+ */
+ public AxEngineStats(final AxEngineStats copyConcept) {
+ super(copyConcept);
+ }
+
+ /**
+ * The Keyed Constructor creates an engine statistics instance with the given key and all values cleared.
+ *
+ * @param key the key
+ */
+ public AxEngineStats(final AxReferenceKey key) {
+ this(key, 0, 0, 0, 0, 0, 0);
+ }
+
+ /**
+ * This Constructor creates an engine statistics instance with all its fields set.
+ *
+ * @param key the engine statistics key
+ * @param timeStamp the time stamp at which the statistics were recorded
+ * @param eventCount the number of events processed by the engine
+ * @param lastExecutionTime the amount of time taken to execute the last policy
+ * @param averageExecutionTime the average amount of time taken to execute a policy
+ * @param upTime the time that has elapsed since the policy engine was started
+ * @param lastStart the time at which the policy engine was last started
+ */
+ public AxEngineStats(final AxReferenceKey key, final long timeStamp, final long eventCount,
+ final long lastExecutionTime, final double averageExecutionTime, final long upTime, final long lastStart) {
+ super();
+ Assertions.argumentNotNull(key, "key may not be null");
+
+ this.key = key;
+ this.timeStamp = timeStamp;
+ this.eventCount = eventCount;
+ this.lastExecutionTime = lastExecutionTime;
+ this.averageExecutionTime = averageExecutionTime;
+ this.upTime = upTime;
+ this.lastStart = lastStart;
+ initEngineMetricsWithPrometheus();
+ }
+
+ /**
+ * {@inheritDoc}.
+ */
+ @Override
+ public AxReferenceKey getKey() {
+ return key;
+ }
+
+ /**
+ * {@inheritDoc}.
+ */
+ @Override
+ public List<AxKey> getKeys() {
+ return key.getKeys();
+ }
+
+ /**
+ * Sets the engine statistics key.
+ *
+ * @param key the engine statistics key
+ */
+ public void setKey(final AxReferenceKey key) {
+ Assertions.argumentNotNull(key, "key may not be null");
+ this.key = key;
+ }
+
+ /**
+ * Gets the time stamp at which the statistics were recorded.
+ *
+ * @return the time stamp at which the statistics were recorded
+ */
+ public long getTimeStamp() {
+ return timeStamp;
+ }
+
+ /**
+ * Gets the time stamp at which the statistics were recorded as a string.
+ *
+ * @return the time stamp at which the statistics were recorded as a string
+ */
+ public String getTimeStampString() {
+ return new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS").format(timeStamp);
+ }
+
+ /**
+ * Sets the time stamp at which the statistics were recorded.
+ *
+ * @param timeStamp the time stamp at which the statistics were recorded
+ */
+ public void setTimeStamp(final long timeStamp) {
+ this.timeStamp = timeStamp;
+ }
+
+ /**
+ * Gets the number of events processed by the engine.
+ *
+ * @return the number of events processed by the engine
+ */
+ public long getEventCount() {
+ return eventCount;
+ }
+
+ /**
+ * Sets the number of events processed by the engine.
+ *
+ * @param eventCount the number of events processed by the engine
+ */
+ public void setEventCount(final long eventCount) {
+ this.eventCount = eventCount;
+ ENGINE_EVENT_EXECUTIONS.labels(getKey().getParentArtifactKey().getId())
+ .set(this.eventCount);
+ }
+
+ /**
+ * Gets the amount of time taken to execute the last policy.
+ *
+ * @return the amount of time taken to execute the last policy
+ */
+ public long getLastExecutionTime() {
+ return lastExecutionTime;
+ }
+
+ /**
+ * Sets the amount of time taken to execute the last policy.
+ *
+ * @param lastExecutionTime the amount of time taken to execute the last policy
+ */
+ public void setLastExecutionTime(final long lastExecutionTime) {
+ this.lastExecutionTime = lastExecutionTime;
+ ENGINE_LAST_EXECUTION_TIME.labels(getKey().getParentArtifactKey().getId())
+ .observe(this.lastExecutionTime / 1000d);
+ }
+
+ /**
+ * Gets the average amount of time taken to execute a policy.
+ *
+ * @return the average amount of time taken to execute a policy
+ */
+ public double getAverageExecutionTime() {
+ return averageExecutionTime;
+ }
+
+ /**
+ * Sets the average amount of time taken to execute a policy.
+ *
+ * @param averageExecutionTime the average amount of time taken to execute a policy
+ */
+ public void setAverageExecutionTime(final double averageExecutionTime) {
+ this.averageExecutionTime = averageExecutionTime;
+ ENGINE_AVG_EXECUTION_TIME.labels(getKey().getParentArtifactKey().getId())
+ .set(this.averageExecutionTime / 1000d);
+ }
+
+ /**
+ * Gets the time that has elapsed since the policy engine was started.
+ *
+ * @return the time that has elapsed since the policy engine was started
+ */
+ public long getUpTime() {
+ if (this.getLastStart() != 0) {
+ return upTime + (timeStamp - this.getLastStart());
+ }
+ return upTime;
+ }
+
+ /**
+ * Sets the time that has elapsed since the policy engine was started.
+ *
+ * @param upTime the time that has elapsed since the policy engine was started
+ */
+ public void setUpTime(final long upTime) {
+ this.upTime = upTime;
+ ENGINE_UPTIME.labels(getKey().getParentArtifactKey().getId()).set(this.upTime / 1000d);
+ }
+
+ /**
+ * Sets the time at which the policy engine was last started.
+ *
+ * @param lastStart the time at which the policy engine was last started
+ */
+ private void setLastStart(final long lastStart) {
+ this.lastStart = lastStart;
+ ENGINE_START_TIMESTAMP.labels(getKey().getParentArtifactKey().getId()).set(this.lastStart);
+ }
+
+ /**
+ * Gets the time at which the policy engine was last started.
+ *
+ * @return the time at which the policy engine was last started
+ */
+ public long getLastStart() {
+ return lastStart;
+ }
+
+ /**
+ * Resets all the statistic values to zero.
+ */
+ public synchronized void reset() {
+ timeStamp = 0;
+ eventCount = 0;
+ lastExecutionTime = 0;
+ averageExecutionTime = 0;
+ upTime = 0;
+ lastEnterTime = 0;
+ lastStart = 0;
+ initEngineMetricsWithPrometheus();
+ }
+
+ /**
+ * Updates the statistics when called, used by the Apex engine when it starts executing a policy.
+ *
+ * @param eventkey the key of the event that is being executed
+ */
+ public synchronized void executionEnter(final AxArtifactKey eventkey) {
+ final long now = System.currentTimeMillis();
+ eventCount++;
+ if (eventCount < 0) {
+ eventCount = 2;
+ }
+ lastEnterTime = now;
+ timeStamp = now;
+ ENGINE_EVENT_EXECUTIONS.labels(getKey().getParentArtifactKey().getId()).set(this.eventCount);
+ }
+
+ /**
+ * Updates the statistics when called, used by the Apex engine when it completes executing a policy.
+ */
+ public synchronized void executionExit() {
+ final long now = System.currentTimeMillis();
+ lastExecutionTime = now - lastEnterTime;
+ ENGINE_LAST_EXECUTION_TIME.labels(getKey().getParentArtifactKey().getId())
+ .observe(this.lastExecutionTime / 1000d);
+
+ averageExecutionTime = ((averageExecutionTime * (eventCount - 1.0)) + lastExecutionTime) / eventCount;
+ lastEnterTime = 0;
+ timeStamp = System.currentTimeMillis();
+ ENGINE_AVG_EXECUTION_TIME.labels(getKey().getParentArtifactKey().getId())
+ .set(this.averageExecutionTime / 1000d);
+ }
+
+ /**
+ * Updates the statistics when called, used by the Apex engine when it is started.
+ */
+ public synchronized void engineStart() {
+ final long now = System.currentTimeMillis();
+ timeStamp = now;
+ this.setLastStart(now);
+ }
+
+ /**
+ * Updates the statistics when called, used by the Apex engine when it is stopped.
+ */
+ public synchronized void engineStop() {
+ final long now = System.currentTimeMillis();
+ timeStamp = now;
+ upTime += (timeStamp - this.getLastStart());
+ this.setLastStart(0);
+ ENGINE_UPTIME.labels(getKey().getParentArtifactKey().getId()).set(this.upTime / 1000d);
+ }
+
+ /**
+ * {@inheritDoc}.
+ */
+ @Override
+ public AxValidationResult validate(final AxValidationResult result) {
+ if (key.equals(AxReferenceKey.getNullKey())) {
+ result.addValidationMessage(
+ new AxValidationMessage(key, this.getClass(), ValidationResult.INVALID, "key is a null key"));
+ }
+
+ return key.validate(result);
+ }
+
+ /**
+ * {@inheritDoc}.
+ */
+ @Override
+ public void clean() {
+ key.clean();
+ }
+
+ /**
+ * {@inheritDoc}.
+ */
+ @Override
+ public String toString() {
+ final StringBuilder builder = new StringBuilder();
+ builder.append(this.getClass().getSimpleName());
+ builder.append(":(");
+ builder.append("engineKey=");
+ builder.append(key);
+ builder.append(",timeStamp=");
+ builder.append(new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS").format(timeStamp));
+ builder.append(",eventCount=");
+ builder.append(eventCount);
+ builder.append(",lastExecutionTime=");
+ builder.append(lastExecutionTime);
+ builder.append(",averageExecutionTime=");
+ builder.append(averageExecutionTime);
+ builder.append(",upTime=");
+ builder.append(getUpTime());
+ builder.append(")");
+ return builder.toString();
+ }
+
+ /**
+ * {@inheritDoc}.
+ */
+ @Override
+ public AxConcept copyTo(final AxConcept targetObject) {
+ Assertions.argumentNotNull(targetObject, "target may not be null");
+
+ final Object copyObject = targetObject;
+ Assertions.instanceOf(copyObject, AxEngineStats.class);
+
+ final AxEngineStats copy = ((AxEngineStats) copyObject);
+ copy.setKey(new AxReferenceKey(key));
+ copy.setTimeStamp(timeStamp);
+ copy.setEventCount(eventCount);
+ copy.setLastExecutionTime(lastExecutionTime);
+ copy.setAverageExecutionTime(averageExecutionTime);
+ copy.setUpTime(upTime);
+ copy.setLastStart(lastStart);
+ initEngineMetricsWithPrometheus();
+
+ return copy;
+ }
+
+ /**
+ * {@inheritDoc}.
+ */
+ @Override
+ public int hashCode() {
+ final int prime = 31;
+ int result = 1;
+ result = prime * result + key.hashCode();
+ result = prime * result + (int) (timeStamp ^ (timeStamp >>> HASH_CODE_PRIME));
+ result = prime * result + (int) (eventCount ^ (eventCount >>> HASH_CODE_PRIME));
+ result = prime * result + (int) (lastExecutionTime ^ (lastExecutionTime >>> HASH_CODE_PRIME));
+ result = prime * result + ((int) averageExecutionTime ^ ((int) averageExecutionTime >>> HASH_CODE_PRIME));
+ result = prime * result + (int) (upTime ^ (upTime >>> HASH_CODE_PRIME));
+ result = prime * result + (int) (getLastStart() ^ (getLastStart() >>> HASH_CODE_PRIME));
+ return result;
+ }
+
+ /**
+ * {@inheritDoc}.
+ */
+ @Override
+ public boolean equals(final Object obj) {
+ if (obj == null) {
+ return false;
+ }
+ if (this == obj) {
+ return true;
+ }
+
+ if (getClass() != obj.getClass()) {
+ return false;
+ }
+
+ final AxEngineStats other = (AxEngineStats) obj;
+ if (!key.equals(other.key)) {
+ return false;
+ }
+ if (timeStamp != other.timeStamp) {
+ return false;
+ }
+ if (eventCount != other.eventCount) {
+ return false;
+ }
+ if (lastExecutionTime != other.lastExecutionTime) {
+ return false;
+ }
+ if (Double.compare(averageExecutionTime, other.averageExecutionTime) != 0) {
+ return false;
+ }
+ if (upTime != other.upTime) {
+ return false;
+ }
+ return getLastStart() == other.getLastStart();
+ }
+
+ /**
+ * {@inheritDoc}.
+ */
+ @Override
+ public int compareTo(final AxConcept otherObj) {
+ if (otherObj == null) {
+ return -1;
+ }
+ if (this == otherObj) {
+ return 0;
+ }
+ if (getClass() != otherObj.getClass()) {
+ return this.hashCode() - otherObj.hashCode();
+ }
+
+ final AxEngineStats other = (AxEngineStats) otherObj;
+ if (!key.equals(other.key)) {
+ return key.compareTo(other.key);
+ }
+ if (timeStamp != other.timeStamp) {
+ return (int) (timeStamp - other.timeStamp);
+ }
+ if (eventCount != other.eventCount) {
+ return (int) (eventCount - other.eventCount);
+ }
+ if (lastExecutionTime != other.lastExecutionTime) {
+ return (int) (lastExecutionTime - other.lastExecutionTime);
+ }
+ final int result = Double.compare(averageExecutionTime, other.averageExecutionTime);
+ if (result != 0) {
+ return result;
+ }
+ if (upTime != other.upTime) {
+ return (int) (upTime - other.upTime);
+ }
+
+ return Long.compare(lastStart, other.lastStart);
+ }
+}
diff --git a/model/src/main/java/org/onap/policy/apex/model/enginemodel/concepts/package-info.java b/model/src/main/java/org/onap/policy/apex/model/enginemodel/concepts/package-info.java
new file mode 100644
index 000000000..4557af7db
--- /dev/null
+++ b/model/src/main/java/org/onap/policy/apex/model/enginemodel/concepts/package-info.java
@@ -0,0 +1,28 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * Copyright (C) 2016-2018 Ericsson. All rights reserved.
+ * Modifications Copyright (C) 2022 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=========================================================
+ */
+
+/**
+ * Contains the concepts required to receive state information and statistcs from running APEX
+ * engines.
+ *
+ * @author Liam Fallon (liam.fallon@ericsson.com)
+ */
+package org.onap.policy.apex.model.enginemodel.concepts;
diff --git a/model/src/main/java/org/onap/policy/apex/model/eventmodel/concepts/AxEvent.java b/model/src/main/java/org/onap/policy/apex/model/eventmodel/concepts/AxEvent.java
new file mode 100644
index 000000000..a4c0e9db1
--- /dev/null
+++ b/model/src/main/java/org/onap/policy/apex/model/eventmodel/concepts/AxEvent.java
@@ -0,0 +1,547 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * Copyright (C) 2016-2018 Ericsson. All rights reserved.
+ * Modifications Copyright (C) 2019-2020,2022 Nordix Foundation.
+ * Modifications Copyright (C) 2022 Bell Canada.
+ * ================================================================================
+ * 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.apex.model.eventmodel.concepts;
+
+import com.google.common.base.Strings;
+import com.google.gson.annotations.SerializedName;
+import java.util.Arrays;
+import java.util.List;
+import java.util.Map;
+import java.util.Map.Entry;
+import java.util.Set;
+import java.util.SortedMap;
+import java.util.TreeMap;
+import java.util.TreeSet;
+import org.apache.commons.lang3.EnumUtils;
+import org.onap.policy.apex.model.basicmodel.concepts.AxArtifactKey;
+import org.onap.policy.apex.model.basicmodel.concepts.AxConcept;
+import org.onap.policy.apex.model.basicmodel.concepts.AxKey;
+import org.onap.policy.apex.model.basicmodel.concepts.AxToscaPolicyProcessingStatus;
+import org.onap.policy.apex.model.basicmodel.concepts.AxValidationMessage;
+import org.onap.policy.apex.model.basicmodel.concepts.AxValidationResult;
+import org.onap.policy.apex.model.basicmodel.concepts.AxValidationResult.ValidationResult;
+import org.onap.policy.common.utils.validation.Assertions;
+
+/**
+ * This class defines an Apex event. An {@link AxEvent} is used to kick off execution of policies in Apex and is emitted
+ * by policies when they complete execution. In addition, Apex uses {@link AxEvent} instances internally to pass
+ * control from one Apex state to the next during execution.
+ *
+ * <p>The {@link AxArtifactKey} of an event uniquely identifies it in an Apex system and the name field in the key is
+ * the name of the event.
+ *
+ * <p>Each {@link AxEvent} has a name space, which is usually set to identify the domain of application of an event. For
+ * example a 4G cell power event might have the name space {@code org.onap.radio.4g} and the name {@code PowerEvent}.
+ * The source and target of the event are reserved to hold an identifier that defines the sender and receiver of an
+ * event respectively. The definition and structure of these fields is reserved for future use and their use by
+ * applications is currently not recommended.
+ *
+ * <p>The parameters that an event has are defined as a map of {@link AxField} instances.
+ *
+ * <p>Validation checks that the event key is valid. If name space is a blank string, a warning is issued. Blank source
+ * or target fields result in observations being issued. An event may not have any parameters. If it has parameters, the
+ * name and value of each parameter entry is checked to ensure they are not null. Then the local name of each parameter
+ * is checked to ensure it matches the event parameter key on the event. Finally, the parent key of each parameter is
+ * checked to ensure it matches the event key.
+ */
+public class AxEvent extends AxConcept {
+ private static final long serialVersionUID = -1460388382582984269L;
+
+ private static final String WHITESPACE_REGEXP = "\\s+$";
+
+ /** The key of the event, unique in the Apex system. */
+ // CHECKSTYLE:OFF: checkstyle:VisibilityMonitor
+ protected AxArtifactKey key;
+ // CHECKSTYLE:ON: checkstyle:VisibilityMonitor
+
+ private String nameSpace;
+ private String source;
+ private String target;
+
+ @SerializedName("parameter")
+ private Map<String, AxField> parameterMap;
+ private String toscaPolicyState;
+
+ /**
+ * The default constructor creates an event with a null artifact key. The event name space, source, and target are
+ * all defined as empty strings and the parameter map is initialized as an empty map.
+ */
+ public AxEvent() {
+ this(new AxArtifactKey());
+ }
+
+ /**
+ * Copy constructor.
+ *
+ * @param copyConcept the concept to copy from
+ */
+ public AxEvent(final AxEvent copyConcept) {
+ super(copyConcept);
+ }
+
+ /**
+ * The default constructor creates an event with the given artifact key. The event name space, source, and target
+ * are all defined as empty strings and the parameter map is initialized as an empty map.
+ *
+ * @param key the key of the event
+ */
+ public AxEvent(final AxArtifactKey key) {
+ this(key, "", "", "", new TreeMap<>(), "");
+ }
+
+ /**
+ * This constructor creates an event with the given artifact key and name space. The event source, and target are
+ * all defined as empty strings and the parameter map is initialized as an empty map.
+ *
+ * @param key the key of the event
+ * @param nameSpace the name space of the event
+ */
+ public AxEvent(final AxArtifactKey key, final String nameSpace) {
+ this(key, nameSpace, "", "", new TreeMap<>(), "");
+ }
+
+ /**
+ * This constructor creates an event with the given artifact key, name space, source and target. The parameter map
+ * is initialized as an empty map.
+ *
+ * @param key the key of the event
+ * @param nameSpace the name space of the event
+ * @param source the source of the event
+ * @param target the target of the event
+ */
+ public AxEvent(final AxArtifactKey key, final String nameSpace, final String source, final String target) {
+ this(key, nameSpace, source, target, new TreeMap<>(), "");
+ }
+
+ /**
+ * This constructor creates an event with all its fields defined.
+ *
+ * @param key the key of the event
+ * @param nameSpace the name space of the event
+ * @param source the source of the event
+ * @param target the target of the event
+ * @param parameterMap the map of parameters that the event has
+ * @param toscaPolicyState the TOSCA policy processing status that event is flagged with
+ */
+ public AxEvent(final AxArtifactKey key, final String nameSpace, final String source, final String target,
+ final SortedMap<String, AxField> parameterMap, final String toscaPolicyState) {
+ super();
+ Assertions.argumentNotNull(key, "key may not be null");
+ Assertions.argumentNotNull(nameSpace, "nameSpace may not be null");
+ Assertions.argumentNotNull(source, "source may not be null");
+ Assertions.argumentNotNull(target, "target may not be null");
+ Assertions.argumentNotNull(parameterMap, "parameterMap may not be null");
+
+ this.key = key;
+ this.nameSpace = nameSpace;
+ this.source = source;
+ this.target = target;
+ this.parameterMap = parameterMap;
+ this.toscaPolicyState = toscaPolicyState;
+ }
+
+ /**
+ * This method checks that an event has all the fields in the {@code otherFieldSet} set defined on it.
+ *
+ * @param otherFieldSet the set of fields to check for existence on this event
+ * @return true, if all the {@code otherFieldSet} fields are defined on this event
+ */
+ public boolean hasFields(final Set<AxField> otherFieldSet) {
+ return parameterMap.values().containsAll(otherFieldSet);
+ }
+
+ /**
+ * {@inheritDoc}.
+ */
+ @Override
+ public void buildReferences() {
+ for (final AxField parameter : parameterMap.values()) {
+ parameter.getKey().setParentArtifactKey(key);
+ }
+ }
+
+ /**
+ * {@inheritDoc}.
+ */
+ @Override
+ public AxArtifactKey getKey() {
+ return key;
+ }
+
+ /**
+ * {@inheritDoc}.
+ */
+ @Override
+ public List<AxKey> getKeys() {
+ final List<AxKey> keyList = key.getKeys();
+
+ for (final AxField field : parameterMap.values()) {
+ keyList.addAll(field.getKeys());
+ }
+ return keyList;
+ }
+
+ /**
+ * Sets the key of the event.
+ *
+ * @param key the key of the event
+ */
+ public void setKey(final AxArtifactKey key) {
+ Assertions.argumentNotNull(key, "key may not be null");
+ this.key = key;
+
+ for (final AxField parameter : parameterMap.values()) {
+ parameter.getKey().setParentArtifactKey(key);
+ }
+ }
+
+ /**
+ * Gets the name space of the event.
+ *
+ * @return the name space of the event
+ */
+ public String getNameSpace() {
+ return nameSpace;
+ }
+
+ /**
+ * Sets the name space of the event.
+ *
+ * @param nameSpace the name space of the event
+ */
+ public void setNameSpace(final String nameSpace) {
+ Assertions.argumentNotNull(nameSpace, "nameSpace may not be null");
+ this.nameSpace = nameSpace.trim();
+ }
+
+ /**
+ * Gets the source of the event.
+ *
+ * @return the source of the event
+ */
+ public String getSource() {
+ return source;
+ }
+
+ /**
+ * Sets the source of the event.
+ *
+ * @param source the source of the event
+ */
+ public void setSource(final String source) {
+ Assertions.argumentNotNull(source, "source may not be null");
+ this.source = source.trim();
+ }
+
+ /**
+ * Gets the target of the event.
+ *
+ * @return the target of the event
+ */
+ public String getTarget() {
+ return target;
+ }
+
+ /**
+ * Sets the target of the event.
+ *
+ * @param target the target of the event
+ */
+ public void setTarget(final String target) {
+ Assertions.argumentNotNull(target, "target may not be null");
+ this.target = target.trim();
+ }
+
+ /**
+ * Gets the event parameter map.
+ *
+ * @return the event parameter map
+ */
+ public Map<String, AxField> getParameterMap() {
+ return parameterMap;
+ }
+
+ /**
+ * Gets the fields defined on the event as a set.
+ *
+ * @return the fields defined on the event as a set
+ */
+ public Set<AxField> getFields() {
+ return new TreeSet<>(parameterMap.values());
+ }
+
+ /**
+ * Sets the event parameter map, containing all the fields of the event.
+ *
+ * @param parameterMap the event parameter map
+ */
+ public void setParameterMap(final Map<String, AxField> parameterMap) {
+ Assertions.argumentNotNull(parameterMap, "parameterMap may not be null");
+ this.parameterMap = parameterMap;
+ }
+
+ /**
+ * Gets the TOSCA policy processing status from the event.
+ *
+ * @return the TOSCA policy processing status
+ */
+ public String getToscaPolicyState() {
+ return toscaPolicyState;
+ }
+
+ /**
+ * Sets the TOSCA policy processing status on the event.
+ *
+ * @param toscaPolicyState the TOSCA policy processing status
+ */
+ public void setToscaPolicyState(String toscaPolicyState) {
+ this.toscaPolicyState = toscaPolicyState;
+ }
+
+ /**
+ * {@inheritDoc}.
+ */
+ @Override
+ public AxValidationResult validate(final AxValidationResult resultIn) {
+ AxValidationResult result = resultIn;
+
+ if (key.equals(AxArtifactKey.getNullKey())) {
+ result.addValidationMessage(new AxValidationMessage(key, this.getClass(), ValidationResult.INVALID,
+ "key is a null key"));
+ }
+
+ result = key.validate(result);
+
+ if (nameSpace.replaceAll(WHITESPACE_REGEXP, "").length() == 0) {
+ result.addValidationMessage(new AxValidationMessage(key, this.getClass(), ValidationResult.WARNING,
+ "nameSpace on event is blank"));
+ }
+
+ if (source.replaceAll(WHITESPACE_REGEXP, "").length() == 0) {
+ result.addValidationMessage(new AxValidationMessage(key, this.getClass(), ValidationResult.OBSERVATION,
+ "source on event is blank"));
+ }
+
+ if (target.replaceAll(WHITESPACE_REGEXP, "").length() == 0) {
+ result.addValidationMessage(new AxValidationMessage(key, this.getClass(), ValidationResult.OBSERVATION,
+ "target on event is blank"));
+ }
+
+ for (final Entry<String, AxField> eventParameterEntry : parameterMap.entrySet()) {
+ if (eventParameterEntry.getKey() == null || eventParameterEntry.getKey().equals(AxKey.NULL_KEY_NAME)) {
+ result.addValidationMessage(new AxValidationMessage(key, this.getClass(), ValidationResult.INVALID,
+ "key on parameter " + eventParameterEntry.getKey() + " may not be the null key"));
+ } else if (eventParameterEntry.getValue() == null) {
+ result.addValidationMessage(new AxValidationMessage(key, this.getClass(), ValidationResult.INVALID,
+ "value on parameter " + eventParameterEntry.getKey() + " may not be null"));
+ } else {
+ result = validateEventParameters(eventParameterEntry, result);
+ }
+ }
+
+ if (!Strings.isNullOrEmpty(toscaPolicyState)
+ && !EnumUtils.isValidEnum(AxToscaPolicyProcessingStatus.class, toscaPolicyState)) {
+ result.addValidationMessage(new AxValidationMessage(key, this.getClass(), ValidationResult.INVALID,
+ "toscaPolicyState on event is not a valid enum. Valid values are: "
+ + Arrays.asList(AxToscaPolicyProcessingStatus.values())));
+ }
+
+ return result;
+ }
+
+ /**
+ * Validate an event parameter entry.
+ *
+ * @param eventParameterEntry the event parameter entry
+ * @param result the validation result to append to
+ * @return The validation result
+ */
+ private AxValidationResult validateEventParameters(final Entry<String, AxField> eventParameterEntry,
+ final AxValidationResult result) {
+ if (!eventParameterEntry.getKey().equals(eventParameterEntry.getValue().getKey().getLocalName())) {
+ result.addValidationMessage(new AxValidationMessage(key, this.getClass(), ValidationResult.INVALID,
+ "key on parameter " + eventParameterEntry.getKey()
+ + " does not equal parameter field local name "
+ + eventParameterEntry.getValue().getKey().getLocalName()));
+ }
+
+ if (!eventParameterEntry.getValue().getKey().getParentArtifactKey().equals(key)) {
+ result.addValidationMessage(new AxValidationMessage(key, this.getClass(), ValidationResult.INVALID,
+ "parent key on parameter field " + eventParameterEntry.getValue().getKey()
+ + " does not equal event key"));
+ }
+
+ return eventParameterEntry.getValue().validate(result);
+ }
+
+ /**
+ * {@inheritDoc}.
+ */
+ @Override
+ public void clean() {
+ key.clean();
+ nameSpace = nameSpace.trim();
+ source = source.trim();
+ target = target.trim();
+ }
+
+ /**
+ * {@inheritDoc}.
+ */
+ @Override
+ public String toString() {
+ final StringBuilder builder = new StringBuilder();
+ builder.append(this.getClass().getSimpleName());
+ builder.append(":(");
+ builder.append("key=");
+ builder.append(key);
+ builder.append(",nameSpace=");
+ builder.append(nameSpace);
+ builder.append(",source=");
+ builder.append(source);
+ builder.append(",target=");
+ builder.append(target);
+ builder.append(",parameter=");
+ builder.append(parameterMap);
+ builder.append(",toscaPolicyState=");
+ builder.append(toscaPolicyState);
+ builder.append(")");
+ return builder.toString();
+ }
+
+ /**
+ * {@inheritDoc}.
+ */
+ @Override
+ public AxConcept copyTo(final AxConcept targetObject) {
+ Assertions.argumentNotNull(targetObject, "targetObject may not be null");
+
+ final Object copyObject = targetObject;
+ Assertions.instanceOf(copyObject, AxEvent.class);
+
+ final AxEvent copy = (AxEvent) copyObject;
+
+ final Map<String, AxField> newParameterMap = new TreeMap<>();
+ for (final Entry<String, AxField> eventParameterMapEntry : parameterMap.entrySet()) {
+ newParameterMap.put(eventParameterMapEntry.getKey(), new AxField(eventParameterMapEntry.getValue()));
+ }
+ copy.setParameterMap(newParameterMap);
+
+ copy.setKey(new AxArtifactKey(key));
+ copy.setNameSpace(nameSpace);
+ copy.setSource(source);
+ copy.setTarget(target);
+ copy.setToscaPolicyState(toscaPolicyState);
+
+ return copy;
+ }
+
+ /**
+ * {@inheritDoc}.
+ */
+ @Override
+ public int hashCode() {
+ final int prime = 31;
+ int result = 1;
+ result = prime * result + key.hashCode();
+ result = prime * result + nameSpace.hashCode();
+ result = prime * result + source.hashCode();
+ result = prime * result + target.hashCode();
+ result = prime * result + parameterMap.hashCode();
+ result = prime * result + toscaPolicyState.hashCode();
+ return result;
+ }
+
+ /**
+ * {@inheritDoc}.
+ */
+ @Override
+ public boolean equals(final Object obj) {
+ if (obj == null) {
+ return false;
+ }
+ if (this == obj) {
+ return true;
+ }
+
+ if (getClass() != obj.getClass()) {
+ return false;
+ }
+
+ final AxEvent other = (AxEvent) obj;
+ if (!key.equals(other.key)) {
+ return false;
+ }
+ if (!nameSpace.equals(other.nameSpace)) {
+ return false;
+ }
+ if (!source.equals(other.source)) {
+ return false;
+ }
+ if (!target.equals(other.target)) {
+ return false;
+ }
+ if (!toscaPolicyState.equals(other.toscaPolicyState)) {
+ return false;
+ }
+ return parameterMap.equals(other.parameterMap);
+ }
+
+ /**
+ * {@inheritDoc}.
+ */
+ @Override
+ public int compareTo(final AxConcept otherObj) {
+ if (otherObj == null) {
+ return -1;
+ }
+ if (this == otherObj) {
+ return 0;
+ }
+ if (getClass() != otherObj.getClass()) {
+ return this.hashCode() - otherObj.hashCode();
+ }
+
+ final AxEvent other = (AxEvent) otherObj;
+ if (!key.equals(other.key)) {
+ return key.compareTo(other.key);
+ }
+ if (!nameSpace.equals(other.nameSpace)) {
+ return nameSpace.compareTo(other.nameSpace);
+ }
+ if (!source.equals(other.source)) {
+ return source.compareTo(other.source);
+ }
+ if (!target.equals(other.target)) {
+ return target.compareTo(other.target);
+ }
+ if (!parameterMap.equals(other.parameterMap)) {
+ return (parameterMap.hashCode() - other.parameterMap.hashCode());
+ }
+ if (!toscaPolicyState.equals(other.toscaPolicyState)) {
+ return toscaPolicyState.compareTo(other.toscaPolicyState);
+ }
+
+ return 0;
+ }
+}
diff --git a/model/src/main/java/org/onap/policy/apex/model/eventmodel/concepts/AxEventModel.java b/model/src/main/java/org/onap/policy/apex/model/eventmodel/concepts/AxEventModel.java
new file mode 100644
index 000000000..b91712c9d
--- /dev/null
+++ b/model/src/main/java/org/onap/policy/apex/model/eventmodel/concepts/AxEventModel.java
@@ -0,0 +1,276 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * Copyright (C) 2016-2018 Ericsson. All rights reserved.
+ * Modifications Copyright (C) 2019,2022 Nordix Foundation.
+ * Modifications Copyright (C) 2021 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.apex.model.eventmodel.concepts;
+
+import java.util.List;
+import org.onap.policy.apex.model.basicmodel.concepts.AxArtifactKey;
+import org.onap.policy.apex.model.basicmodel.concepts.AxConcept;
+import org.onap.policy.apex.model.basicmodel.concepts.AxKey;
+import org.onap.policy.apex.model.basicmodel.concepts.AxKeyInformation;
+import org.onap.policy.apex.model.basicmodel.concepts.AxModel;
+import org.onap.policy.apex.model.basicmodel.concepts.AxValidationResult;
+import org.onap.policy.apex.model.basicmodel.service.ModelService;
+import org.onap.policy.apex.model.contextmodel.concepts.AxContextSchemas;
+import org.onap.policy.common.utils.validation.Assertions;
+
+/**
+ * A container class for an Apex event model. This class is a container class that allows an Apex model to be
+ * constructed that contains events and context and the key information for those events and context. The model contains
+ * schema definitions and the definitions of events that use those schemas.
+ *
+ * <p>Validation runs {@link AxModel} validation on the model. In addition, the {@link AxContextSchemas} and
+ * {@link AxEvents} validation is run on the context schemas and events in the model.
+ */
+public class AxEventModel extends AxModel {
+ private static final long serialVersionUID = 8800599637708309945L;
+
+ private AxContextSchemas schemas;
+ private AxEvents events;
+
+ /**
+ * The Default Constructor creates a {@link AxEventModel} object with a null artifact key and creates an empty event
+ * model.
+ */
+ public AxEventModel() {
+ this(new AxArtifactKey());
+ }
+
+ /**
+ * Copy constructor.
+ *
+ * @param copyConcept the concept to copy from
+ */
+ public AxEventModel(final AxEventModel copyConcept) {
+ super(copyConcept);
+ }
+
+ /**
+ * The Key Constructor creates a {@link AxEventModel} object with the given artifact key and creates an empty event
+ * model.
+ *
+ * @param key the event model key
+ */
+ public AxEventModel(final AxArtifactKey key) {
+ this(key, new AxContextSchemas(new AxArtifactKey(key.getName() + "_Schemas", key.getVersion())),
+ new AxKeyInformation(new AxArtifactKey(key.getName() + "_KeyInfo", key.getVersion())),
+ new AxEvents(new AxArtifactKey(key.getName() + "_Events", key.getVersion())));
+ }
+
+ /**
+ * Constructor that initiates a {@link AxEventModel} with all its fields.
+ *
+ * @param key the event model key
+ * @param schemas the schemas for events in the event model
+ * @param keyInformation the key information for context schemas and events in the event model
+ * @param events the events in the event model
+ */
+ public AxEventModel(final AxArtifactKey key, final AxContextSchemas schemas, final AxKeyInformation keyInformation,
+ final AxEvents events) {
+ super(key, keyInformation);
+ Assertions.argumentNotNull(events, "events may not be null");
+
+ this.schemas = schemas;
+ this.events = events;
+ }
+
+ /**
+ * {@inheritDoc}.
+ */
+ @Override
+ public void register() {
+ super.register();
+ ModelService.registerModel(AxContextSchemas.class, getSchemas());
+ ModelService.registerModel(AxEvents.class, getEvents());
+ }
+
+ /**
+ * {@inheritDoc}.
+ */
+ @Override
+ public List<AxKey> getKeys() {
+ final List<AxKey> keyList = super.getKeys();
+
+ keyList.addAll(schemas.getKeys());
+ keyList.addAll(events.getKeys());
+
+ return keyList;
+ }
+
+ /**
+ * Gets the context schemas.
+ *
+ * @return the context schemas
+ */
+ public AxContextSchemas getSchemas() {
+ return schemas;
+ }
+
+ /**
+ * Sets the context schemas.
+ *
+ * @param schemas the context schemas
+ */
+ public void setSchemas(final AxContextSchemas schemas) {
+ Assertions.argumentNotNull(schemas, "schemas may not be null");
+ this.schemas = schemas;
+ }
+
+ /**
+ * Gets the events from the model.
+ *
+ * @return the events
+ */
+ public AxEvents getEvents() {
+ return events;
+ }
+
+ /**
+ * Sets the events in the model.
+ *
+ * @param events the events
+ */
+ public void setEvents(final AxEvents events) {
+ Assertions.argumentNotNull(events, "events may not be null");
+ this.events = events;
+ }
+
+ /**
+ * {@inheritDoc}.
+ */
+ @Override
+ public AxValidationResult validate(final AxValidationResult resultIn) {
+ AxValidationResult result = resultIn;
+
+ result = super.validate(result);
+ result = schemas.validate(result);
+ return events.validate(result);
+ }
+
+ /**
+ * {@inheritDoc}.
+ */
+ @Override
+ public void clean() {
+ super.clean();
+ schemas.clean();
+ events.clean();
+ }
+
+ /**
+ * {@inheritDoc}.
+ */
+ @Override
+ public String toString() {
+ final StringBuilder builder = new StringBuilder();
+ builder.append(this.getClass().getSimpleName());
+ builder.append(":(");
+ builder.append(super.toString());
+ builder.append(",schemas=");
+ builder.append(schemas);
+ builder.append(",events=");
+ builder.append(events);
+ builder.append(")");
+ return builder.toString();
+ }
+
+ /**
+ * {@inheritDoc}.
+ */
+ @Override
+ public AxConcept copyTo(final AxConcept targetObject) {
+ Assertions.argumentNotNull(targetObject, "target may not be null");
+
+ final Object copyObject = targetObject;
+ Assertions.instanceOf(copyObject, AxEventModel.class);
+
+ final AxEventModel copy = ((AxEventModel) copyObject);
+ super.copyTo(targetObject);
+ copy.setSchemas(new AxContextSchemas(schemas));
+ copy.setEvents(new AxEvents(events));
+
+ return copy;
+ }
+
+ /**
+ * {@inheritDoc}.
+ */
+ @Override
+ public int hashCode() {
+ final int prime = 31;
+ int result = 1;
+ result = prime * result + super.hashCode();
+ result = prime * result + schemas.hashCode();
+ result = prime * result + events.hashCode();
+ return result;
+ }
+
+ /**
+ * {@inheritDoc}.
+ */
+ @Override
+ public boolean equals(final Object obj) {
+ if (obj == null) {
+ throw new IllegalArgumentException("comparison object may not be null");
+ }
+
+ if (this == obj) {
+ return true;
+ }
+ if (getClass() != obj.getClass()) {
+ return false;
+ }
+
+ final AxEventModel other = (AxEventModel) obj;
+ if (!super.equals(other)) {
+ return false;
+ }
+ if (!schemas.equals(other.schemas)) {
+ return false;
+ }
+ return events.equals(other.events);
+ }
+
+ /**
+ * {@inheritDoc}.
+ */
+ @Override
+ public int compareTo(final AxConcept otherObj) {
+ Assertions.argumentNotNull(otherObj, "comparison object may not be null");
+
+ if (this == otherObj) {
+ return 0;
+ }
+ if (getClass() != otherObj.getClass()) {
+ return this.hashCode() - otherObj.hashCode();
+ }
+
+ final AxEventModel other = (AxEventModel) otherObj;
+ if (!super.equals(other)) {
+ return super.compareTo(other);
+ }
+ if (!schemas.equals(other.schemas)) {
+ return schemas.compareTo(other.schemas);
+ }
+ return events.compareTo(other.events);
+ }
+}
diff --git a/model/src/main/java/org/onap/policy/apex/model/eventmodel/concepts/AxEvents.java b/model/src/main/java/org/onap/policy/apex/model/eventmodel/concepts/AxEvents.java
new file mode 100644
index 000000000..aac1562de
--- /dev/null
+++ b/model/src/main/java/org/onap/policy/apex/model/eventmodel/concepts/AxEvents.java
@@ -0,0 +1,351 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * Copyright (C) 2016-2018 Ericsson. All rights reserved.
+ * Modifications Copyright (C) 2019-2020,2022 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.apex.model.eventmodel.concepts;
+
+import java.util.List;
+import java.util.Map;
+import java.util.Map.Entry;
+import java.util.NavigableMap;
+import java.util.Set;
+import java.util.TreeMap;
+import org.onap.policy.apex.model.basicmodel.concepts.AxArtifactKey;
+import org.onap.policy.apex.model.basicmodel.concepts.AxConcept;
+import org.onap.policy.apex.model.basicmodel.concepts.AxConceptGetter;
+import org.onap.policy.apex.model.basicmodel.concepts.AxConceptGetterImpl;
+import org.onap.policy.apex.model.basicmodel.concepts.AxKey;
+import org.onap.policy.apex.model.basicmodel.concepts.AxValidationMessage;
+import org.onap.policy.apex.model.basicmodel.concepts.AxValidationResult;
+import org.onap.policy.apex.model.basicmodel.concepts.AxValidationResult.ValidationResult;
+import org.onap.policy.common.utils.validation.Assertions;
+
+/**
+ * This class is an event container and holds a map of the events for an entire Apex model. All Apex models that use
+ * events must have an {@link AxEvents} field. The {@link AxEvents} class implements the helper methods of the
+ * {@link AxConceptGetter} interface to allow {@link AxEvents} instances to be retrieved by calling methods directly on
+ * this class without referencing the contained map.
+ *
+ * <p>Validation checks that the container key is not null. An error is issued if no events are defined in the
+ * container. Each event entry is checked to ensure that its key and value are not null and that the key matches the key
+ * in the map value. Each event entry is then validated individually.
+ */
+public class AxEvents extends AxConcept implements AxConceptGetter<AxEvent> {
+ private static final long serialVersionUID = 4290442590545820316L;
+
+ private AxArtifactKey key;
+ private Map<AxArtifactKey, AxEvent> eventMap;
+
+ /**
+ * The Default Constructor creates a {@link AxEvents} object with a null artifact key and creates an empty event
+ * map.
+ */
+ public AxEvents() {
+ this(new AxArtifactKey());
+ }
+
+ /**
+ * Copy constructor.
+ *
+ * @param copyConcept the concept to copy from
+ */
+ public AxEvents(final AxEvents copyConcept) {
+ super(copyConcept);
+ }
+
+ /**
+ * The Key Constructor creates a {@link AxEvents} object with the given artifact key and creates an empty event map.
+ *
+ * @param key the event container key
+ */
+ public AxEvents(final AxArtifactKey key) {
+ this(key, new TreeMap<>());
+ }
+
+ /**
+ * This Constructor creates an event container with all of its fields defined.
+ *
+ * @param key the event container key
+ * @param eventMap the events to be stored in the event container
+ */
+ public AxEvents(final AxArtifactKey key, final Map<AxArtifactKey, AxEvent> eventMap) {
+ super();
+ Assertions.argumentNotNull(key, "key may not be null");
+ Assertions.argumentNotNull(eventMap, "eventMap may not be null");
+
+ this.key = key;
+ this.eventMap = new TreeMap<>();
+ this.eventMap.putAll(eventMap);
+ }
+
+ /**
+ * {@inheritDoc}.
+ */
+ @Override
+ public AxArtifactKey getKey() {
+ return key;
+ }
+
+ /**
+ * {@inheritDoc}.
+ */
+ @Override
+ public List<AxKey> getKeys() {
+ final List<AxKey> keyList = key.getKeys();
+
+ for (final AxEvent event : eventMap.values()) {
+ keyList.addAll(event.getKeys());
+ }
+
+ return keyList;
+ }
+
+ /**
+ * {@inheritDoc}.
+ */
+ @Override
+ public void buildReferences() {
+ eventMap.values().stream().forEach(event -> event.buildReferences());
+ }
+
+ /**
+ * Sets the key of the event container.
+ *
+ * @param key the event container key
+ */
+ public void setKey(final AxArtifactKey key) {
+ Assertions.argumentNotNull(key, "key may not be null");
+ this.key = key;
+ }
+
+ /**
+ * Gets the event map containing the events in the event container.
+ *
+ * @return the event map with all the events in the event container
+ */
+ public Map<AxArtifactKey, AxEvent> getEventMap() {
+ return eventMap;
+ }
+
+ /**
+ * Sets the event map containing the events in the event container.
+ *
+ * @param eventMap the event map containing the events in the event container
+ */
+ public void setEventMap(final Map<AxArtifactKey, AxEvent> eventMap) {
+ Assertions.argumentNotNull(eventMap, "eventMap may not be null");
+ this.eventMap = new TreeMap<>();
+ this.eventMap.putAll(eventMap);
+ }
+
+ /**
+ * {@inheritDoc}.
+ */
+ @Override
+ public AxValidationResult validate(final AxValidationResult resultIn) {
+ AxValidationResult result = resultIn;
+
+ if (key.equals(AxArtifactKey.getNullKey())) {
+ result.addValidationMessage(new AxValidationMessage(key, this.getClass(), ValidationResult.INVALID,
+ "key is a null key"));
+ }
+
+ result = key.validate(result);
+
+ if (eventMap.size() == 0) {
+ result.addValidationMessage(new AxValidationMessage(key, this.getClass(), ValidationResult.INVALID,
+ "eventMap may not be empty"));
+ } else {
+ for (final Entry<AxArtifactKey, AxEvent> eventEntry : eventMap.entrySet()) {
+ if (eventEntry.getKey().equals(AxArtifactKey.getNullKey())) {
+ result.addValidationMessage(new AxValidationMessage(key, this.getClass(), ValidationResult.INVALID,
+ "key on event entry " + eventEntry.getKey() + " may not be the null key"));
+ } else if (eventEntry.getValue() == null) {
+ result.addValidationMessage(new AxValidationMessage(key, this.getClass(), ValidationResult.INVALID,
+ "value on event entry " + eventEntry.getKey() + " may not be null"));
+ } else {
+ if (!eventEntry.getKey().equals(eventEntry.getValue().getKey())) {
+ result.addValidationMessage(
+ new AxValidationMessage(key, this.getClass(), ValidationResult.INVALID,
+ "key on event entry key " + eventEntry.getKey()
+ + " does not equal event value key "
+ + eventEntry.getValue().getKey()));
+ }
+
+ result = eventEntry.getValue().validate(result);
+ }
+ }
+ }
+
+ return result;
+ }
+
+ /**
+ * {@inheritDoc}.
+ */
+ @Override
+ public void clean() {
+ key.clean();
+ for (final Entry<AxArtifactKey, AxEvent> eventEntry : eventMap.entrySet()) {
+ eventEntry.getKey().clean();
+ eventEntry.getValue().clean();
+ }
+ }
+
+ /**
+ * {@inheritDoc}.
+ */
+ @Override
+ public String toString() {
+ final StringBuilder builder = new StringBuilder();
+ builder.append(this.getClass().getSimpleName());
+ builder.append(":(");
+ builder.append("key=");
+ builder.append(key);
+ builder.append(",eventMap=");
+ builder.append(eventMap);
+ builder.append(")");
+ return builder.toString();
+ }
+
+ /**
+ * {@inheritDoc}.
+ */
+ @Override
+ public AxConcept copyTo(final AxConcept targetObject) {
+ Assertions.argumentNotNull(targetObject, "target may not be null");
+
+ final Object copyObject = targetObject;
+ Assertions.instanceOf(copyObject, AxEvents.class);
+
+ final AxEvents copy = (AxEvents) copyObject;
+ copy.setKey(new AxArtifactKey(key));
+ final Map<AxArtifactKey, AxEvent> newEventMap = new TreeMap<>();
+ for (final Entry<AxArtifactKey, AxEvent> eventMapEntry : eventMap.entrySet()) {
+ newEventMap.put(new AxArtifactKey(eventMapEntry.getKey()), new AxEvent(eventMapEntry.getValue()));
+ }
+ copy.setEventMap(newEventMap);
+
+ return copy;
+ }
+
+ /**
+ * {@inheritDoc}.
+ */
+ @Override
+ public int hashCode() {
+ final int prime = 31;
+ int result = 1;
+ result = prime * result + key.hashCode();
+ result = prime * result + eventMap.hashCode();
+ return result;
+ }
+
+ /**
+ * {@inheritDoc}.
+ */
+ @Override
+ public boolean equals(final Object obj) {
+ if (obj == null) {
+ return false;
+ }
+ if (this == obj) {
+ return true;
+ }
+
+ if (getClass() != obj.getClass()) {
+ return false;
+ }
+
+ final AxEvents other = (AxEvents) obj;
+ if (!key.equals(other.key)) {
+ return false;
+ }
+ return eventMap.equals(other.eventMap);
+ }
+
+ /**
+ * {@inheritDoc}.
+ */
+ @Override
+ public int compareTo(final AxConcept otherObj) {
+ if (otherObj == null) {
+ return -1;
+ }
+ if (this == otherObj) {
+ return 0;
+ }
+ if (getClass() != otherObj.getClass()) {
+ return this.hashCode() - otherObj.hashCode();
+ }
+
+ final AxEvents other = (AxEvents) otherObj;
+ if (!key.equals(other.key)) {
+ return key.compareTo(other.key);
+ }
+ if (!eventMap.equals(other.eventMap)) {
+ return (eventMap.hashCode() - other.eventMap.hashCode());
+ }
+
+ return 0;
+ }
+
+ /**
+ * {@inheritDoc}.
+ */
+ @Override
+ public AxEvent get(final AxArtifactKey conceptKey) {
+ return new AxConceptGetterImpl<>((NavigableMap<AxArtifactKey, AxEvent>) eventMap).get(conceptKey);
+ }
+
+ /**
+ * {@inheritDoc}.
+ */
+ @Override
+ public AxEvent get(final String conceptKeyName) {
+ return new AxConceptGetterImpl<>((NavigableMap<AxArtifactKey, AxEvent>) eventMap).get(conceptKeyName);
+ }
+
+ /**
+ * {@inheritDoc}.
+ */
+ @Override
+ public AxEvent get(final String conceptKeyName, final String conceptKeyVersion) {
+ return new AxConceptGetterImpl<>((NavigableMap<AxArtifactKey, AxEvent>) eventMap).get(conceptKeyName,
+ conceptKeyVersion);
+ }
+
+ /**
+ * {@inheritDoc}.
+ */
+ @Override
+ public Set<AxEvent> getAll(final String conceptKeyName) {
+ return new AxConceptGetterImpl<>((NavigableMap<AxArtifactKey, AxEvent>) eventMap).getAll(conceptKeyName);
+ }
+
+ /**
+ * {@inheritDoc}.
+ */
+ @Override
+ public Set<AxEvent> getAll(final String conceptKeyName, final String conceptKeyVersion) {
+ return new AxConceptGetterImpl<>((NavigableMap<AxArtifactKey, AxEvent>) eventMap).getAll(conceptKeyName,
+ conceptKeyVersion);
+ }
+}
diff --git a/model/src/main/java/org/onap/policy/apex/model/eventmodel/concepts/AxField.java b/model/src/main/java/org/onap/policy/apex/model/eventmodel/concepts/AxField.java
new file mode 100644
index 000000000..26511c9d1
--- /dev/null
+++ b/model/src/main/java/org/onap/policy/apex/model/eventmodel/concepts/AxField.java
@@ -0,0 +1,347 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * Copyright (C) 2016-2018 Ericsson. All rights reserved.
+ * Modifications Copyright (C) 2019,2022 Nordix Foundation.
+ * Modifications Copyright (C) 2021 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.apex.model.eventmodel.concepts;
+
+import java.util.List;
+import org.onap.policy.apex.model.basicmodel.concepts.AxArtifactKey;
+import org.onap.policy.apex.model.basicmodel.concepts.AxConcept;
+import org.onap.policy.apex.model.basicmodel.concepts.AxKey;
+import org.onap.policy.apex.model.basicmodel.concepts.AxKeyUse;
+import org.onap.policy.apex.model.basicmodel.concepts.AxReferenceKey;
+import org.onap.policy.apex.model.basicmodel.concepts.AxValidationMessage;
+import org.onap.policy.apex.model.basicmodel.concepts.AxValidationResult;
+import org.onap.policy.apex.model.basicmodel.concepts.AxValidationResult.ValidationResult;
+import org.onap.policy.common.utils.validation.Assertions;
+
+/**
+ * In Apex, a field is an input or output parameter to or from a concept. For example, the parameters of an event are
+ * fields and the input and output of a task is defined as a collection of fields.
+ *
+ * <p>A field has an {@link AxReferenceKey} key that defines its name and parent, and a {@link AxArtifactKey} key to a
+ * context schema that defines the structure of the data atom that holds the value of the field. Fields can be specified
+ * as being optional but are mandatory by default.
+ *
+ * <p>Validation checks that the field key and the field schema reference key are not null.
+ */
+public class AxField extends AxConcept {
+ private static final String KEY_MAY_NOT_BE_NULL = "key may not be null";
+ private static final String FIELD_SCHEMA_KEY_MAY_NOT_BE_NULL = "fieldSchemaKey may not be null";
+
+ private static final long serialVersionUID = -6443016863162692288L;
+
+ private static final int HASH_PRIME_0 = 1231;
+ private static final int HASH_PRIME_1 = 1237;
+
+ private AxReferenceKey key;
+ private AxArtifactKey fieldSchemaKey;
+ private boolean optional;
+
+ /**
+ * The default constructor creates a field with a null artifact and schema key.
+ */
+ public AxField() {
+ this(new AxReferenceKey());
+ optional = false;
+ }
+
+ /**
+ * The default constructor creates a field with the given artifact key and a null schema key.
+ *
+ * @param key the field key
+ */
+ public AxField(final AxReferenceKey key) {
+ this(key, new AxArtifactKey());
+ }
+
+ /**
+ * Copy constructor.
+ *
+ * @param copyConcept the concept to copy from
+ */
+ public AxField(final AxField copyConcept) {
+ super(copyConcept);
+ }
+
+ /**
+ * Constructor to create the field with both its keys defined.
+ *
+ * @param key the field key
+ * @param fieldSchemaKey the key of the field schema to use for this field
+ */
+ public AxField(final AxReferenceKey key, final AxArtifactKey fieldSchemaKey) {
+ super();
+ Assertions.argumentNotNull(key, KEY_MAY_NOT_BE_NULL);
+ Assertions.argumentNotNull(fieldSchemaKey, FIELD_SCHEMA_KEY_MAY_NOT_BE_NULL);
+
+ this.key = key;
+ this.fieldSchemaKey = fieldSchemaKey;
+ }
+
+ /**
+ * Constructor to create the field with all its fields defined.
+ *
+ * @param key the field key
+ * @param fieldSchemaKey the key of the field schema to use for this field
+ * @param optional true if this field is optional
+ */
+ public AxField(final AxReferenceKey key, final AxArtifactKey fieldSchemaKey, final boolean optional) {
+ super();
+ Assertions.argumentNotNull(key, KEY_MAY_NOT_BE_NULL);
+ Assertions.argumentNotNull(fieldSchemaKey, FIELD_SCHEMA_KEY_MAY_NOT_BE_NULL);
+
+ this.key = key;
+ this.fieldSchemaKey = fieldSchemaKey;
+ this.optional = optional;
+ }
+
+ /**
+ * Constructor to create the field with the local name of its reference key defined and its schema key defined.
+ *
+ * @param localName the local name of the field reference key
+ * @param fieldSchemaKey the key of the field schema to use for this field
+ */
+ public AxField(final String localName, final AxArtifactKey fieldSchemaKey) {
+ super();
+ Assertions.argumentNotNull(localName, "localName may not be null");
+ Assertions.argumentNotNull(fieldSchemaKey, FIELD_SCHEMA_KEY_MAY_NOT_BE_NULL);
+
+ key = new AxReferenceKey();
+ key.setLocalName(localName);
+ this.fieldSchemaKey = fieldSchemaKey;
+ }
+
+ /**
+ * Constructor to create the field with the local name of its reference key defined, its schema key and optionality
+ * defined.
+ *
+ * @param localName the local name of the field reference key
+ * @param fieldSchemaKey the key of the field schema to use for this field
+ * @param optional true if this field is optional
+ */
+ public AxField(final String localName, final AxArtifactKey fieldSchemaKey, final boolean optional) {
+ super();
+ Assertions.argumentNotNull(localName, "localName may not be null");
+ Assertions.argumentNotNull(fieldSchemaKey, FIELD_SCHEMA_KEY_MAY_NOT_BE_NULL);
+
+ key = new AxReferenceKey();
+ key.setLocalName(localName);
+ this.fieldSchemaKey = fieldSchemaKey;
+ this.optional = optional;
+ }
+
+ /**
+ * {@inheritDoc}.
+ */
+ @Override
+ public AxReferenceKey getKey() {
+ return key;
+ }
+
+ /**
+ * {@inheritDoc}.
+ */
+ @Override
+ public List<AxKey> getKeys() {
+ final List<AxKey> keyList = key.getKeys();
+ keyList.add(new AxKeyUse(fieldSchemaKey));
+ return keyList;
+ }
+
+ /**
+ * Sets the reference key of the field.
+ *
+ * @param key the field reference key
+ */
+ public void setKey(final AxReferenceKey key) {
+ Assertions.argumentNotNull(key, KEY_MAY_NOT_BE_NULL);
+ this.key = key;
+ }
+
+ /**
+ * Gets the key of the field schema.
+ *
+ * @return the field schema key
+ */
+ public AxArtifactKey getSchema() {
+ return fieldSchemaKey;
+ }
+
+ /**
+ * Sets the key of the field schema.
+ *
+ * @param schema the field schema key
+ */
+ public void setSchema(final AxArtifactKey schema) {
+ Assertions.argumentNotNull(schema, "schema may not be null");
+ this.fieldSchemaKey = schema;
+ }
+
+ /**
+ * Gets the optionality of the field.
+ *
+ * @return the field optional flag
+ */
+ public boolean getOptional() {
+ return optional;
+ }
+
+ /**
+ * Sets the optionality of the field.
+ *
+ * @param optional the optionality of the field
+ */
+ public void setOptional(final boolean optional) {
+ this.optional = optional;
+ }
+
+ /**
+ * {@inheritDoc}.
+ */
+ @Override
+ public AxValidationResult validate(final AxValidationResult resultIn) {
+ AxValidationResult result = resultIn;
+
+ if (key.equals(AxReferenceKey.getNullKey())) {
+ result.addValidationMessage(new AxValidationMessage(key, this.getClass(), ValidationResult.INVALID,
+ "key is a null key"));
+ }
+
+ result = key.validate(result);
+
+ if (fieldSchemaKey.equals(AxArtifactKey.getNullKey())) {
+ result.addValidationMessage(new AxValidationMessage(key, this.getClass(), ValidationResult.INVALID,
+ "fieldSchemaKey is a null key: " + fieldSchemaKey));
+ }
+ return fieldSchemaKey.validate(result);
+ }
+
+ /**
+ * {@inheritDoc}.
+ */
+ @Override
+ public void clean() {
+ key.clean();
+ fieldSchemaKey.clean();
+ }
+
+ /**
+ * {@inheritDoc}.
+ */
+ @Override
+ public String toString() {
+ final StringBuilder builder = new StringBuilder();
+ builder.append(this.getClass().getSimpleName());
+ builder.append(":(");
+ builder.append("key=");
+ builder.append(key);
+ builder.append(",fieldSchemaKey=");
+ builder.append(fieldSchemaKey);
+ builder.append(",optional=");
+ builder.append(optional);
+ builder.append(")");
+ return builder.toString();
+ }
+
+ /**
+ * {@inheritDoc}.
+ */
+ @Override
+ public AxConcept copyTo(final AxConcept targetObject) {
+ Assertions.argumentNotNull(targetObject, "target may not be null");
+
+ final Object copyObject = targetObject;
+ Assertions.instanceOf(copyObject, AxField.class);
+
+ final AxField copy = ((AxField) copyObject);
+ copy.setKey(new AxReferenceKey(key));
+ copy.setSchema(new AxArtifactKey(fieldSchemaKey));
+ copy.setOptional(optional);
+ return copy;
+ }
+
+ /**
+ * {@inheritDoc}.
+ */
+ @Override
+ public int hashCode() {
+ final int prime = 31;
+ int result = 1;
+ result = prime * result + key.hashCode();
+ result = prime * result + fieldSchemaKey.hashCode();
+ result = prime * result + (optional ? HASH_PRIME_0 : HASH_PRIME_1);
+ return result;
+ }
+
+ /*
+ * (nonJavadoc)
+ *
+ * @see org.onap.policy.apex.model.basicmodel.concepts.AxConcept#equals(java.lang.Object)
+ */
+ @Override
+ public boolean equals(final Object obj) {
+ if (obj == null) {
+ return false;
+ }
+ if (this == obj) {
+ return true;
+ }
+
+ if (!(obj instanceof AxField)) {
+ return false;
+ }
+
+ final AxField other = (AxField) obj;
+ if (!key.getLocalName().equals(other.key.getLocalName())) {
+ return false;
+ }
+ if (optional != other.optional) {
+ return false;
+ }
+ return fieldSchemaKey.equals(other.fieldSchemaKey);
+ }
+
+ /**
+ * {@inheritDoc}.
+ */
+ @Override
+ public int compareTo(final AxConcept otherObj) {
+ if (otherObj == null) {
+ return 1;
+ }
+ if (this == otherObj) {
+ return 0;
+ }
+ if (!(otherObj instanceof AxField)) {
+ return this.hashCode() - otherObj.hashCode();
+ }
+
+ final AxField other = (AxField) otherObj;
+ if (!key.getLocalName().equals(other.key.getLocalName())) {
+ return key.getLocalName().compareTo(other.key.getLocalName());
+ }
+ if (optional != other.optional) {
+ return (optional ? 1 : -1);
+ }
+ return fieldSchemaKey.compareTo(other.fieldSchemaKey);
+ }
+}
diff --git a/model/src/main/java/org/onap/policy/apex/model/eventmodel/concepts/AxInputField.java b/model/src/main/java/org/onap/policy/apex/model/eventmodel/concepts/AxInputField.java
new file mode 100644
index 000000000..6155c7186
--- /dev/null
+++ b/model/src/main/java/org/onap/policy/apex/model/eventmodel/concepts/AxInputField.java
@@ -0,0 +1,88 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * Copyright (C) 2016-2018 Ericsson. All rights reserved.
+ * Modifications Copyright (C) 2022 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.apex.model.eventmodel.concepts;
+
+import org.onap.policy.apex.model.basicmodel.concepts.AxArtifactKey;
+import org.onap.policy.apex.model.basicmodel.concepts.AxReferenceKey;
+
+/**
+ * This class specializes the {@link AxField} class for use as input fields on events.
+ */
+public class AxInputField extends AxField {
+ private static final long serialVersionUID = 2090324845463750391L;
+
+ /**
+ * The default constructor creates a field with a null artifact and schema key.
+ */
+ public AxInputField() {
+ super();
+ }
+
+ /**
+ * The default constructor creates a field with the given artifact key and a null schema key.
+ *
+ * @param key the field key
+ */
+ public AxInputField(final AxReferenceKey key) {
+ super(key);
+ }
+
+ /**
+ * Constructor to create the field with both its keys defined.
+ *
+ * @param key the field key
+ * @param fieldSchemaKey the key of the field schema to use for this field
+ */
+ public AxInputField(final AxReferenceKey key, final AxArtifactKey fieldSchemaKey) {
+ super(key, fieldSchemaKey);
+ }
+
+ /**
+ * Constructor to create the field with both its keys defined and optional flag specified.
+ *
+ * @param key the field key
+ * @param fieldSchemaKey the key of the field schema to use for this field
+ * @param optional true if the task field is optional, false otherwise
+ */
+ public AxInputField(final AxReferenceKey key, final AxArtifactKey fieldSchemaKey, final boolean optional) {
+ super(key, fieldSchemaKey, optional);
+ }
+
+ /**
+ * Constructor to create the field with the local name of its reference key defined and its schema key defined.
+ *
+ * @param localName the local name of the field reference key
+ * @param fieldSchemaKey the key of the field schema to use for this field
+ */
+ public AxInputField(final String localName, final AxArtifactKey fieldSchemaKey) {
+ super(localName, fieldSchemaKey);
+ }
+
+ /**
+ * Copy constructor, create an input field as a copy of another input field.
+ *
+ * @param field the input field to copy from
+ */
+ public AxInputField(final AxInputField field) {
+ super(new AxReferenceKey(field.getKey()), new AxArtifactKey(field.getSchema()), field.getOptional());
+ }
+}
diff --git a/model/src/main/java/org/onap/policy/apex/model/eventmodel/concepts/AxOutputField.java b/model/src/main/java/org/onap/policy/apex/model/eventmodel/concepts/AxOutputField.java
new file mode 100644
index 000000000..7f3437617
--- /dev/null
+++ b/model/src/main/java/org/onap/policy/apex/model/eventmodel/concepts/AxOutputField.java
@@ -0,0 +1,88 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * Copyright (C) 2016-2018 Ericsson. All rights reserved.
+ * Modifications Copyright (C) 2022 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.apex.model.eventmodel.concepts;
+
+import org.onap.policy.apex.model.basicmodel.concepts.AxArtifactKey;
+import org.onap.policy.apex.model.basicmodel.concepts.AxReferenceKey;
+
+/**
+ * This class specializes the {@link AxField} class for use as output fields on events.
+ */
+public class AxOutputField extends AxField {
+ private static final long serialVersionUID = 2090324845463750391L;
+
+ /**
+ * The default constructor creates a field with a null artifact and schema key.
+ */
+ public AxOutputField() {
+ super();
+ }
+
+ /**
+ * The default constructor creates a field with the given artifact key and a null schema key.
+ *
+ * @param key the field key
+ */
+ public AxOutputField(final AxReferenceKey key) {
+ super(key);
+ }
+
+ /**
+ * Constructor to create the field with both its keys defined.
+ *
+ * @param key the field key
+ * @param fieldSchemaKey the key of the field schema to use for this field
+ */
+ public AxOutputField(final AxReferenceKey key, final AxArtifactKey fieldSchemaKey) {
+ super(key, fieldSchemaKey);
+ }
+
+ /**
+ * Constructor to create the field with both its keys defined and optional flag specified.
+ *
+ * @param key the field key
+ * @param fieldSchemaKey the key of the field schema to use for this field
+ * @param optional true if the task field is optional, false otherwise
+ */
+ public AxOutputField(final AxReferenceKey key, final AxArtifactKey fieldSchemaKey, final boolean optional) {
+ super(key, fieldSchemaKey, optional);
+ }
+
+ /**
+ * Constructor to create the field with the local name of its reference key defined and its schema key defined.
+ *
+ * @param localName the local name of the field reference key
+ * @param fieldSchemaKey the key of the field schema to use for this field
+ */
+ public AxOutputField(final String localName, final AxArtifactKey fieldSchemaKey) {
+ super(localName, fieldSchemaKey);
+ }
+
+ /**
+ * Copy constructor, create an output field as a copy of another output field.
+ *
+ * @param field the output field to copy from
+ */
+ public AxOutputField(final AxOutputField field) {
+ super(new AxReferenceKey(field.getKey()), new AxArtifactKey(field.getSchema()), field.getOptional());
+ }
+}
diff --git a/model/src/main/java/org/onap/policy/apex/model/eventmodel/concepts/package-info.java b/model/src/main/java/org/onap/policy/apex/model/eventmodel/concepts/package-info.java
new file mode 100644
index 000000000..414d22d0e
--- /dev/null
+++ b/model/src/main/java/org/onap/policy/apex/model/eventmodel/concepts/package-info.java
@@ -0,0 +1,27 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * Copyright (C) 2016-2018 Ericsson. All rights reserved.
+ * Modifications Copyright (C) 2022 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=========================================================
+ */
+
+/**
+ * Contains the concepts required to manage events in APEX. It defines the main Apex concepts of events and fields.
+ *
+ * @author Liam Fallon (liam.fallon@ericsson.com)
+ */
+package org.onap.policy.apex.model.eventmodel.concepts;
diff --git a/model/src/main/java/org/onap/policy/apex/model/modelapi/ApexApiResult.java b/model/src/main/java/org/onap/policy/apex/model/modelapi/ApexApiResult.java
new file mode 100644
index 000000000..83e3b5370
--- /dev/null
+++ b/model/src/main/java/org/onap/policy/apex/model/modelapi/ApexApiResult.java
@@ -0,0 +1,264 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * Copyright (C) 2016-2018 Ericsson. All rights reserved.
+ * Modifications Copyright (C) 2020,2022 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.apex.model.modelapi;
+
+import java.io.PrintWriter;
+import java.io.StringWriter;
+import java.util.ArrayList;
+import java.util.List;
+import lombok.Setter;
+
+/**
+ * The Class ApexEditorAPIResult return the result of and messages from all model API method calls on the
+ * {@link ApexModel} API.
+ */
+@Setter
+public class ApexApiResult {
+
+ /**
+ * This enumeration is used to represent the result status of a call on the {@link ApexModel} API.
+ */
+ public enum Result {
+ /** The method call succeeded. */
+ SUCCESS,
+ /** The method call succeeded and all operations are now completed. */
+ FINISHED,
+ /** The method call for a create operation failed because the concept already exists. */
+ CONCEPT_EXISTS,
+ /**
+ * The method call for a create operation failed because multiple concepts already exists.
+ */
+ MULTIPLE_CONCEPTS_EXIST,
+ /** The method call on a concept failed because the referenced concept does not exist. */
+ CONCEPT_DOES_NOT_EXIST,
+ /** The method call failed because no action was specified on the method call. */
+ NO_ACTION_SPECIFIED,
+ /**
+ * The method call failed because of a structural error, a missing reference, or other error on the model.
+ */
+ FAILED,
+ /**
+ * The method call failed for another reason such as the method call is not implemented yet on the concept on
+ * which it was called.
+ */
+ OTHER_ERROR;
+
+ /**
+ * Check if a result is OK.
+ *
+ * @param result the result
+ * @return true if the result is not OK
+ */
+ public static boolean isOk(final Result result) {
+ return result == Result.SUCCESS || result == Result.FINISHED;
+ }
+
+ /**
+ * Check if a result is not OK.
+ *
+ * @param result the result
+ * @return true if the result is not OK
+ */
+ public static boolean isNok(final Result result) {
+ return !isOk(result);
+ }
+ }
+
+ private Result result;
+ private List<String> messages = new ArrayList<>();
+
+ /**
+ * The Default Constructor creates a result for a successful operation with no messages.
+ */
+ public ApexApiResult() {
+ result = Result.SUCCESS;
+ }
+
+ /**
+ * This Constructor creates a result with the given result status with no messages.
+ *
+ * @param result the result status to use on this result
+ */
+ public ApexApiResult(final Result result) {
+ this.result = result;
+ }
+
+ /**
+ * This Constructor creates a result with the given result status and message.
+ *
+ * @param result the result status to use on this result
+ * @param message the message to return with the result
+ */
+ public ApexApiResult(final Result result, final String message) {
+ this.result = result;
+ addMessage(message);
+ }
+
+ /**
+ * This Constructor creates a result with the given result status and {@link Throwable} object such as an exception.
+ * The message and stack trace from the {@link Throwable} object are added to the message list of this message.
+ *
+ * @param result the result status to use on this result
+ * @param throwable the throwable object from which to add the message and stack trace
+ */
+ public ApexApiResult(final Result result, final Throwable throwable) {
+ this.result = result;
+ addThrowable(throwable);
+ }
+
+ /**
+ * This Constructor creates a result with the given result status, message, and {@link Throwable} object such as an
+ * exception. The message and stack trace from the {@link Throwable} object are added to the message list of this
+ * message.
+ *
+ * @param result the result status to use on this result
+ * @param message the message to return with the result
+ * @param throwable the throwable object from which to add the message and stack trace
+ */
+ public ApexApiResult(final Result result, final String message, final Throwable throwable) {
+ this.result = result;
+ addMessage(message);
+ addThrowable(throwable);
+ }
+
+ /**
+ * This message is a utility message that checks if the result of an operation on the API was OK.
+ *
+ * @return true, if the result indicates the API operation succeeded
+ */
+ public boolean isOk() {
+ return Result.isOk(result);
+ }
+
+ /**
+ * This message is a utility message that checks if the result of an operation on the API was not OK.
+ *
+ * @return true, if the result indicates the API operation did not succeed
+ */
+ public boolean isNok() {
+ return Result.isNok(result);
+ }
+
+ /**
+ * Gets the result status of an API operation.
+ *
+ * @return the result status
+ */
+ public Result getResult() {
+ return result;
+ }
+
+ /**
+ * Gets the list of messages returned by an API operation.
+ *
+ * @return the list of messages returned by an API operation
+ */
+ public List<String> getMessages() {
+ return messages;
+ }
+
+ /**
+ * Gets all the messages returned by an API operation as a single string.
+ *
+ * @return the messages returned by an API operation as a single string
+ */
+ public String getMessage() {
+ final StringBuilder builder = new StringBuilder();
+ for (final String message : messages) {
+ builder.append(message);
+ builder.append('\n');
+ }
+
+ return builder.toString();
+ }
+
+ /**
+ * Adds a message from an API operation to the bottom of the list of messages to be returned.
+ *
+ * @param message the message from an API operation to add to the bottom of the list of messages to be returned
+ */
+ public void addMessage(final String message) {
+ if (message != null && message.trim().length() > 0) {
+ messages.add(message);
+ }
+ }
+
+ /**
+ * Adds the message and stack trace from a {@link Throwable} object such as an exception from an API operation to
+ * the bottom of the list of messages to be returned.
+ *
+ * @param throwable the {@link Throwable} object such as an exception from an API operation from which the message
+ * and stack trace are to be extracted and placed at the bottom of the list of messages to be returned
+ */
+ public void addThrowable(final Throwable throwable) {
+ final StringWriter throwableStringWriter = new StringWriter();
+ final PrintWriter throwablePrintWriter = new PrintWriter(throwableStringWriter);
+ throwable.printStackTrace(throwablePrintWriter);
+ messages.add(throwable.getMessage());
+ messages.add(throwableStringWriter.toString());
+ }
+
+ /**
+ * Gets a representation of the {@link ApexApiResult} instance as a JSON string.
+ *
+ * @return the result instance JSON string
+ */
+ public String toJson() {
+ final StringBuilder builder = new StringBuilder();
+ builder.append("{\n");
+
+ builder.append("\"result\": \"");
+ builder.append(result.toString());
+ builder.append("\",\n");
+
+ builder.append("\"messages\": [");
+ boolean first = true;
+ for (final String message : messages) {
+ if (first) {
+ builder.append("\n\"");
+ first = false;
+ } else {
+ builder.append(",\n\"");
+ }
+ builder.append(message.replace("\"", "\\\\\""));
+ builder.append("\"");
+ }
+ builder.append("]\n");
+
+ builder.append("}\n");
+
+ return builder.toString();
+ }
+
+ /**
+ * {@inheritDoc}.
+ */
+ @Override
+ public String toString() {
+ final StringBuilder builder = new StringBuilder();
+ builder.append("result: ");
+ builder.append(result);
+ builder.append('\n');
+ builder.append(getMessage());
+ return builder.toString();
+ }
+}
diff --git a/model/src/main/java/org/onap/policy/apex/model/modelapi/ApexEditorApi.java b/model/src/main/java/org/onap/policy/apex/model/modelapi/ApexEditorApi.java
new file mode 100644
index 000000000..8974d1fa8
--- /dev/null
+++ b/model/src/main/java/org/onap/policy/apex/model/modelapi/ApexEditorApi.java
@@ -0,0 +1,905 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * Copyright (C) 2016-2018 Ericsson. All rights reserved.
+ * Modifications Copyright (C) 2021-2022 Bell Canada. All rights reserved.
+ * ================================================================================
+ * 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.apex.model.modelapi;
+
+/**
+ * The Interface ApexEditorAPI is used to manipulate Apex models.
+ */
+public interface ApexEditorApi {
+ /*
+ * Model API Methods
+ */
+
+ /**
+ * Create model.
+ *
+ * @param name name of the model
+ * @param version version of the model, set to null to use the default version
+ * @param uuid model UUID, set to null to generate a UUID
+ * @param description model description, set to null to generate a description
+ * @return result of the operation
+ */
+ ApexApiResult createModel(final String name, final String version, final String uuid, final String description);
+
+ /**
+ * Update model.
+ *
+ * @param name name of the model
+ * @param version version of the model, set to null to update the latest version
+ * @param uuid key information UUID, set to null to not update
+ * @param description policy description, set to null to not update
+ * @return result of the operation
+ */
+ ApexApiResult updateModel(final String name, final String version, final String uuid, final String description);
+
+ /**
+ * Get the key of an Apex model.
+ *
+ * @return the result of the operation
+ */
+ ApexApiResult getModelKey();
+
+ /**
+ * List an Apex model.
+ *
+ * @return the result of the operation
+ */
+ ApexApiResult listModel();
+
+ /**
+ * Delete an Apex model, clear all the concepts in the model.
+ *
+ * @return the result of the operation
+ */
+ ApexApiResult deleteModel();
+
+ /*
+ * Key Information API Methods
+ */
+
+ /**
+ * Create key information.
+ *
+ * @param name name of the concept for the key information
+ * @param version version of the concept for the key information, set to null to use the default
+ * version
+ * @param uuid key information UUID, set to null to generate a UUID
+ * @param description key information description, set to null to generate a description
+ * @return result of the operation
+ */
+ ApexApiResult createKeyInformation(final String name, final String version, final String uuid,
+ final String description);
+
+ /**
+ * Update key information.
+ *
+ * @param name name of the concept for the key information
+ * @param version version of the concept for the key information, set to null to update the
+ * latest version
+ * @param uuid key information UUID, set to null to not update
+ * @param description key information description, set to null to not update
+ * @return result of the operation
+ */
+ ApexApiResult updateKeyInformation(final String name, final String version, final String uuid,
+ final String description);
+
+ /**
+ * List key information.
+ *
+ * @param name name of the concept for the key information, set to null to list all
+ * @param version starting version of the concept for the key information, set to null to list
+ * all versions
+ * @return result of the operation
+ */
+ ApexApiResult listKeyInformation(final String name, final String version);
+
+ /**
+ * Delete key information.
+ *
+ * @param name name of the concept for the key information
+ * @param version version of the concept for the key information, set to null to delete all
+ * versions
+ * @return result of the operation
+ */
+ ApexApiResult deleteKeyInformation(final String name, final String version);
+
+ /**
+ * Validate key information.
+ *
+ * @param name name of the concept for the key information
+ * @param version version of the concept for the key information, set to null to validate all
+ * versions
+ * @return result of the operation
+ */
+ ApexApiResult validateKeyInformation(final String name, final String version);
+
+ /*
+ * Context Schema API Methods
+ */
+
+ /**
+ * Create a context schema.
+ *
+ * @param name name of the context schema
+ * @param version version of the context schema, set to null to use the default version
+ * @param schemaFlavour a final String identifying the flavour of this context schema
+ * @param schemaDefinition a final String containing the definition of this context schema
+ * @param uuid context schema UUID, set to null to generate a UUID
+ * @param description context schema description, set to null to generate a description
+ * @return result of the operation
+ */
+ ApexApiResult createContextSchema(final String name, final String version, final String schemaFlavour,
+ final String schemaDefinition, final String uuid, final String description);
+
+ /**
+ * Update a context schema.
+ *
+ * @param name name of the context schema
+ * @param version version of the context schema, set to null to update the latest version
+ * @param schemaFlavour a final String identifying the flavour of this context schema
+ * @param schemaDefinition a final String containing the definition of this context schema
+ * @param uuid context schema UUID, set to null to not update
+ * @param description context schema description, set to null to not update
+ * @return result of the operation
+ */
+ ApexApiResult updateContextSchema(final String name, final String version, final String schemaFlavour,
+ final String schemaDefinition, final String uuid, final String description);
+
+ /**
+ * List context schemas.
+ *
+ * @param name name of the context schema, set to null to list all
+ * @param version starting version of the context schema, set to null to list all versions
+ * @return result of the operation
+ */
+ ApexApiResult listContextSchemas(final String name, final String version);
+
+ /**
+ * Delete a context schema.
+ *
+ * @param name name of the context schema
+ * @param version version of the context schema, set to null to delete all versions
+ * @return result of the operation
+ */
+ ApexApiResult deleteContextSchema(final String name, final String version);
+
+ /**
+ * Validate context schemas.
+ *
+ * @param name name of the context schema, set to null to list all
+ * @param version starting version of the context schema, set to null to list all versions
+ * @return result of the operation
+ */
+ ApexApiResult validateContextSchemas(final String name, final String version);
+
+ /*
+ * Event API Methods
+ */
+
+ /**
+ * Create an event.
+ *
+ * @param name name of the event
+ * @param version version of the event, set to null to use the default version
+ * @param nameSpace of the event, set to null to use the default value
+ * @param source of the event, set to null to use the default value
+ * @param target of the event, set to null to use the default value
+ * @param uuid event UUID, set to null to generate a UUID
+ * @param description event description, set to null to generate a description
+ * @param toscaPolicyState specifies TOSCA policy processing status
+ * @return result of the operation
+ */
+ ApexApiResult createEvent(final String name, final String version, final String nameSpace, final String source,
+ final String target, final String uuid, final String description, final String toscaPolicyState);
+
+ /**
+ * Update an event.
+ *
+ * @param name name of the event
+ * @param version version of the event, set to null to use the latest version
+ * @param nameSpace of the event, set to null to not update
+ * @param source of the event, set to null to not update
+ * @param target of the event, set to null to not update
+ * @param uuid event UUID, set to null to not update
+ * @param description event description, set to null to not update
+ * @param toscaPolicyState specifies TOSCA policy processing status
+ * @return result of the operation
+ */
+ ApexApiResult updateEvent(final String name, final String version, final String nameSpace, final String source,
+ final String target, final String uuid, final String description, final String toscaPolicyState);
+
+ /**
+ * List events.
+ *
+ * @param name name of the event, set to null to list all
+ * @param version starting version of the event, set to null to list all versions
+ * @return result of the operation
+ */
+ ApexApiResult listEvent(final String name, final String version);
+
+ /**
+ * Delete an event.
+ *
+ * @param name name of the event
+ * @param version version of the event, set to null to delete all versions
+ * @return result of the operation
+ */
+ ApexApiResult deleteEvent(final String name, final String version);
+
+ /**
+ * Validate events.
+ *
+ * @param name name of the event, set to null to list all
+ * @param version starting version of the event, set to null to list all versions
+ * @return result of the operation
+ */
+ ApexApiResult validateEvent(final String name, final String version);
+
+ /**
+ * Create an event parameter.
+ *
+ * @param name name of the event
+ * @param version version of the event, set to null to use the latest version
+ * @param parName of the parameter
+ * @param contextSchemaName name of the parameter context schema
+ * @param contextSchemaVersion version of the parameter context schema, set to null to use the
+ * latest version
+ * @param optional true if the event parameter is optional, false otherwise
+ * @return result of the operation
+ */
+ ApexApiResult createEventPar(final String name, final String version, final String parName,
+ final String contextSchemaName, final String contextSchemaVersion, boolean optional);
+
+ /**
+ * List event parameters.
+ *
+ * @param name name of the event
+ * @param version version of the event, set to null to list latest version
+ * @param parName name of the parameter, set to null to list all parameters of the event
+ * @return result of the operation
+ */
+ ApexApiResult listEventPar(final String name, final String version, final String parName);
+
+ /**
+ * Delete an event parameter.
+ *
+ * @param name name of the event
+ * @param version version of the event, set to null to use the latest version
+ * @param parName of the parameter, set to null to delete all parameters
+ * @return result of the operation
+ */
+ ApexApiResult deleteEventPar(final String name, final String version, final String parName);
+
+ /*
+ * Context Album API Methods
+ */
+
+ /**
+ * Create a context album.
+ *
+ * @param name name of the context album
+ * @param version version of the context album, set to null to use the default version
+ * @param scope of the context album
+ * @param writable "true" or "t" if the context album is writable, set to null or any other
+ * value for a read-only album
+ * @param contextSchemaName name of the parameter context schema
+ * @param contextSchemaVersion version of the parameter context schema, set to null to use the
+ * latest version
+ * @param uuid context album UUID, set to null to generate a UUID
+ * @param description context album description, set to null to generate a description
+ * @return result of the operation
+ */
+ // CHECKSTYLE:OFF: checkstyle:parameterNumber
+ ApexApiResult createContextAlbum(final String name, final String version, final String scope, final String writable,
+ final String contextSchemaName, final String contextSchemaVersion, final String uuid,
+ final String description);
+ // CHECKSTYLE:ON: checkstyle:parameterNumber
+
+ /**
+ * Update a context album.
+ *
+ * @param name name of the context album
+ * @param version version of the context album, set to null to use the default version
+ * @param scope of the context album
+ * @param writable "true" or "t" if the context album is writable, set to null or any other
+ * value for a read-only album
+ * @param contextSchemaName name of the parameter context schema
+ * @param contextSchemaVersion version of the parameter context schema, set to null to use the
+ * latest version
+ * @param uuid context album UUID, set to null to generate a UUID
+ * @param description context album description, set to null to generate a description
+ * @return result of the operation
+ */
+ // CHECKSTYLE:OFF: checkstyle:parameterNumber
+ ApexApiResult updateContextAlbum(final String name, final String version, final String scope, final String writable,
+ final String contextSchemaName, final String contextSchemaVersion, final String uuid,
+ final String description);
+ // CHECKSTYLE:ON: checkstyle:parameterNumber
+
+ /**
+ * List context albums.
+ *
+ * @param name name of the context album, set to null to list all
+ * @param version starting version of the context album, set to null to list all versions
+ * @return result of the operation
+ */
+ ApexApiResult listContextAlbum(final String name, final String version);
+
+ /**
+ * Delete a context album.
+ *
+ * @param name name of the context album
+ * @param version version of the context album, set to null to delete versions
+ * @return result of the operation
+ */
+ ApexApiResult deleteContextAlbum(final String name, final String version);
+
+ /**
+ * Validate context albums.
+ *
+ * @param name name of the context album, set to null to list all
+ * @param version starting version of the context album, set to null to list all versions
+ * @return result of the operation
+ */
+ ApexApiResult validateContextAlbum(final String name, final String version);
+
+ /*
+ * Task API Methods
+ */
+
+ /**
+ * Create a task.
+ *
+ * @param name name of the task
+ * @param version version of the task, set to null to use the default version
+ * @param uuid task UUID, set to null to generate a UUID
+ * @param description task description, set to null to generate a description
+ * @return result of the operation
+ */
+ ApexApiResult createTask(final String name, final String version, final String uuid, final String description);
+
+ /**
+ * Update a task.
+ *
+ * @param name name of the task
+ * @param version version of the task, set to null to use the latest version
+ * @param uuid task UUID, set to null to not update
+ * @param description task description, set to null to not update
+ * @return result of the operation
+ */
+ ApexApiResult updateTask(final String name, final String version, final String uuid, final String description);
+
+ /**
+ * List tasks.
+ *
+ * @param name name of the task, set to null to list all
+ * @param version starting version of the task, set to null to list all versions
+ * @return result of the operation
+ */
+ ApexApiResult listTask(final String name, final String version);
+
+ /**
+ * Delete a task.
+ *
+ * @param name name of the task
+ * @param version version of the task, set to null to use the latest version
+ * @return result of the operation
+ */
+ ApexApiResult deleteTask(final String name, final String version);
+
+ /**
+ * Validate tasks.
+ *
+ * @param name name of the task, set to null to list all
+ * @param version starting version of the task, set to null to list all versions
+ * @return result of the operation
+ */
+ ApexApiResult validateTask(final String name, final String version);
+
+ /**
+ * Create logic for a task.
+ *
+ * @param name name of the task
+ * @param version version of the task, set to null to use the latest version
+ * @param logicFlavour the task logic flavour for the task, set to null to use the default task
+ * logic flavour
+ * @param logic the source code for the logic of the task
+ * @return result of the operation
+ */
+ ApexApiResult createTaskLogic(final String name, final String version, final String logicFlavour,
+ final String logic);
+
+ /**
+ * Update logic for a task.
+ *
+ * @param name name of the task
+ * @param version version of the task, set to null to use the latest version
+ * @param logicFlavour the task logic flavour for the task, set to null to not update
+ * @param logic the source code for the logic of the task, set to null to not update
+ * @return result of the operation
+ */
+ ApexApiResult updateTaskLogic(final String name, final String version, final String logicFlavour,
+ final String logic);
+
+ /**
+ * List task logic.
+ *
+ * @param name name of the task
+ * @param version version of the task, set to null to list the latest version
+ * @return result of the operation
+ */
+ ApexApiResult listTaskLogic(final String name, final String version);
+
+ /**
+ * Delete logic for a task.
+ *
+ * @param name name of the task
+ * @param version version of the task, set to null to use the latest version
+ * @return result of the operation
+ */
+ ApexApiResult deleteTaskLogic(final String name, final String version);
+
+ /**
+ * Create a task field.
+ *
+ * @param name name of the task
+ * @param version version of the task, set to null to use the latest version
+ * @param fieldName of the input field
+ * @param contextSchemaName name of the input field context schema
+ * @param contextSchemaVersion version of the input field context schema, set to null to use the
+ * latest version
+ * @param optional true if the task field is optional, false otherwise
+ * @return result of the operation
+ */
+ ApexApiResult createTaskField(final String name, final String version, final String fieldName,
+ final String contextSchemaName, final String contextSchemaVersion, boolean optional);
+
+ /**
+ * Handle a task field.
+ *
+ * @param name name of the task
+ * @param version version of the task, set to null to use the latest version
+ * @param fieldName field name of the input field, set to null to list all input fields of the
+ * task
+ * @return result of the operation
+ */
+ ApexApiResult handleTaskField(final String name, final String version, final String fieldName);
+
+ /**
+ * Create a task parameter.
+ *
+ * @param name name of the task
+ * @param version version of the task, set to null to use the latest version
+ * @param parName of the parameter
+ * @param defaultValue of the parameter
+ * @return result of the operation
+ */
+ ApexApiResult createTaskParameter(final String name, final String version, final String parName,
+ final String defaultValue);
+
+ /**
+ * List task parameters.
+ *
+ * @param name name of the task
+ * @param version version of the task, set to null to use the latest version
+ * @param parName name of the parameter, set to null to list all parameters of the task
+ * @return result of the operation
+ */
+ ApexApiResult listTaskParameter(final String name, final String version, final String parName);
+
+ /**
+ * Delete a task parameter.
+ *
+ * @param name name of the task
+ * @param version version of the task, set to null to use the latest version
+ * @param parName of the parameter, set to null to delete all task parameters
+ * @return result of the operation
+ */
+ ApexApiResult deleteTaskParameter(final String name, final String version, final String parName);
+
+ /**
+ * Create a task context album reference.
+ *
+ * @param name name of the task
+ * @param version version of the task, set to null to use the latest version
+ * @param contextAlbumName name of the context album for the context album reference
+ * @param contextAlbumVersion version of the context album for the context album reference, set
+ * to null to use the latest version
+ * @return result of the operation
+ */
+ ApexApiResult createTaskContextRef(final String name, final String version, final String contextAlbumName,
+ final String contextAlbumVersion);
+
+ /**
+ * List task context album references.
+ *
+ * @param name name of the task
+ * @param version version of the task, set to null to use the latest version
+ * @param contextAlbumName name of the context album for the context album reference, set to
+ * null to list all task context album references
+ * @param contextAlbumVersion version of the context album for the context album reference, set
+ * to null to use the latest version
+ * @return result of the operation
+ */
+ ApexApiResult listTaskContextRef(final String name, final String version, final String contextAlbumName,
+ final String contextAlbumVersion);
+
+ /**
+ * Delete a task context album reference.
+ *
+ * @param name name of the task
+ * @param version version of the task, set to null to use the latest version
+ * @param contextAlbumName name of the context album for the context album reference, set to
+ * null to delete all task context album references
+ * @param contextAlbumVersion version of the context album for the context album reference, set
+ * to null to use the latest version
+ * @return result of the operation
+ */
+ ApexApiResult deleteTaskContextRef(final String name, final String version, final String contextAlbumName,
+ final String contextAlbumVersion);
+
+ /*
+ * Policy API Methods
+ */
+
+ /**
+ * Create a policy.
+ *
+ * @param name name of the policy
+ * @param version version of the policy, set to null to use the default version
+ * @param template template used to create the policy, set to null to use the default template
+ * @param firstState the first state of the policy
+ * @param uuid policy UUID, set to null to generate a UUID
+ * @param description policy description, set to null to generate a description
+ * @return result of the operation
+ */
+ ApexApiResult createPolicy(final String name, final String version, final String template, final String firstState,
+ final String uuid, final String description);
+
+ /**
+ * Update a policy.
+ *
+ * @param name name of the policy
+ * @param version version of the policy, set to null to use the latest version
+ * @param template template used to create the policy, set to null to not update
+ * @param firstState the first state of the policy
+ * @param uuid policy UUID, set to null to not update
+ * @param description policy description, set to null to not update
+ * @return result of the operation
+ */
+ ApexApiResult updatePolicy(final String name, final String version, final String template, final String firstState,
+ final String uuid, final String description);
+
+ /**
+ * List policies.
+ *
+ * @param name name of the policy, set to null to list all
+ * @param version starting version of the policy, set to null to list all versions
+ * @return result of the operation
+ */
+ ApexApiResult listPolicy(final String name, final String version);
+
+ /**
+ * Delete a policy.
+ *
+ * @param name name of the policy
+ * @param version version of the policy, set to null to use the latest version
+ * @return result of the operation
+ */
+ ApexApiResult deletePolicy(final String name, final String version);
+
+ /**
+ * Validate policies.
+ *
+ * @param name name of the policy, set to null to list all
+ * @param version starting version of the policy, set to null to list all versions
+ * @return result of the operation
+ */
+ ApexApiResult validatePolicy(final String name, final String version);
+
+ /**
+ * Create a policy state.
+ *
+ * @param name name of the policy
+ * @param version version of the policy, set to null to use the latest version
+ * @param stateName of the state
+ * @param triggerName name of the trigger event for this state
+ * @param triggerVersion version of the trigger event for this state, set to null to use the
+ * latest version
+ * @param defaultTaskName the default task name
+ * @param defaltTaskVersion the default task version, set to null to use the latest version
+ * @return result of the operation
+ */
+ ApexApiResult createPolicyState(final String name, final String version, final String stateName,
+ final String triggerName, final String triggerVersion, final String defaultTaskName,
+ final String defaltTaskVersion);
+
+ /**
+ * Update a policy state.
+ *
+ * @param name name of the policy
+ * @param version version of the policy, set to null to use the latest version
+ * @param stateName of the state
+ * @param triggerName name of the trigger event for this state, set to null to not update
+ * @param triggerVersion version of the trigger event for this state, set to use latest version
+ * of trigger event
+ * @param defaultTaskName the default task name, set to null to not update
+ * @param defaltTaskVersion the default task version, set to use latest version of default task
+ * @return result of the operation
+ */
+ ApexApiResult updatePolicyState(final String name, final String version, final String stateName,
+ final String triggerName, final String triggerVersion, final String defaultTaskName,
+ final String defaltTaskVersion);
+
+ /**
+ * List policy states.
+ *
+ * @param name name of the policy
+ * @param version version of the policy, set to null to use the latest version
+ * @param stateName of the state, set to null to list all states of the policy
+ * @return result of the operation
+ */
+ ApexApiResult listPolicyState(final String name, final String version, final String stateName);
+
+ /**
+ * Delete a policy state.
+ *
+ * @param name name of the policy
+ * @param version version of the policy, set to null to use the latest version
+ * @param stateName of the state, set to null to delete all states
+ * @return result of the operation
+ */
+ ApexApiResult deletePolicyState(final String name, final String version, final String stateName);
+
+ /**
+ * Create task selection logic for a state.
+ *
+ * @param name name of the policy
+ * @param version version of the policy, set to null to use the latest version
+ * @param stateName of the state
+ * @param logicFlavour the task selection logic flavour for the state, set to null to use the
+ * default task logic flavour
+ * @param logic the source code for the logic of the state
+ * @return result of the operation
+ */
+ ApexApiResult createPolicyStateTaskSelectionLogic(final String name, final String version, final String stateName,
+ final String logicFlavour, final String logic);
+
+ /**
+ * Update task selection logic for a state.
+ *
+ * @param name name of the policy
+ * @param version version of the policy, set to null to use the latest version
+ * @param stateName of the state
+ * @param logicFlavour the task selection logic flavour for the state, set to null to not update
+ * @param logic the source code for the logic of the state, set to null to not update
+ * @return result of the operation
+ */
+ ApexApiResult updatePolicyStateTaskSelectionLogic(final String name, final String version, final String stateName,
+ final String logicFlavour, final String logic);
+
+ /**
+ * List task selection logic for a state.
+ *
+ * @param name name of the policy
+ * @param version version of the policy, set to null to use the latest version
+ * @param stateName of the state
+ * @return result of the operation
+ */
+ ApexApiResult listPolicyStateTaskSelectionLogic(final String name, final String version, final String stateName);
+
+ /**
+ * Delete task selection logic for a state.
+ *
+ * @param name name of the policy
+ * @param version version of the policy, set to null to use the latest version
+ * @param stateName of the state
+ * @return result of the operation
+ */
+ ApexApiResult deletePolicyStateTaskSelectionLogic(final String name, final String version, final String stateName);
+
+ /**
+ * Create a policy state output.
+ *
+ * @param name name of the policy
+ * @param version version of the policy, set to null to use the latest version
+ * @param stateName of the state
+ * @param outputName of the state output
+ * @param eventName name of the output event for this state output
+ * @param eventVersion version of the output event for this state output, set to null to use the
+ * latest version
+ * @param nextState for this state to transition to, set to null if this is the last state that
+ * the policy transitions to on this branch
+ * @return result of the operation
+ */
+ ApexApiResult createPolicyStateOutput(final String name, final String version, final String stateName,
+ final String outputName, final String eventName, final String eventVersion, final String nextState);
+
+ /**
+ * List policy state outputs.
+ *
+ * @param name name of the policy
+ * @param version version of the policy, set to null to use the latest version
+ * @param stateName of the state
+ * @param outputName of the state output, set to null to list all outputs of the state
+ * @return result of the operation
+ */
+ ApexApiResult listPolicyStateOutput(final String name, final String version, final String stateName,
+ final String outputName);
+
+ /**
+ * Delete a policy state output.
+ *
+ * @param name name of the policy
+ * @param version version of the policy, set to null to use the latest version
+ * @param stateName of the state
+ * @param outputName of the state output, set to null to delete all state outputs
+ * @return result of the operation
+ */
+ ApexApiResult deletePolicyStateOutput(final String name, final String version, final String stateName,
+ final String outputName);
+
+ /**
+ * Create policy finalizer logic for a state.
+ *
+ * @param name name of the policy
+ * @param version version of the policy, set to null to use the latest version
+ * @param stateName of the state
+ * @param finalizerLogicName name of the state finalizer logic
+ * @param logicFlavour the policy finalizer logic flavour for the state, set to null to use the
+ * default task logic flavour
+ * @param logic the source code for the logic of the state
+ * @return result of the operation
+ */
+ ApexApiResult createPolicyStateFinalizerLogic(final String name, final String version, final String stateName,
+ final String finalizerLogicName, final String logicFlavour, final String logic);
+
+ /**
+ * Update policy finalizer logic for a state.
+ *
+ * @param name name of the policy
+ * @param version version of the policy, set to null to use the latest version
+ * @param stateName of the state
+ * @param finalizerLogicName name of the state finalizer logic
+ * @param logicFlavour the policy finalizer logic flavour for the state, set to null to not
+ * update
+ * @param logic the source code for the logic of the state, set to null to not update
+ * @return result of the operation
+ */
+ ApexApiResult updatePolicyStateFinalizerLogic(final String name, final String version, final String stateName,
+ final String finalizerLogicName, final String logicFlavour, final String logic);
+
+ /**
+ * List policy finalizer logic for a state.
+ *
+ * @param name name of the policy
+ * @param version version of the policy, set to null to use the latest version
+ * @param stateName of the state
+ * @param finalizerLogicName name of the state finalizer logic
+ * @return result of the operation
+ */
+ ApexApiResult listPolicyStateFinalizerLogic(final String name, final String version, final String stateName,
+ final String finalizerLogicName);
+
+ /**
+ * Delete policy finalizer logic for a state.
+ *
+ * @param name name of the policy
+ * @param version version of the policy, set to null to use the latest version
+ * @param stateName of the state
+ * @param finalizerLogicName name of the state finalizer logic
+ * @return result of the operation
+ */
+ ApexApiResult deletePolicyStateFinalizerLogic(final String name, final String version, final String stateName,
+ final String finalizerLogicName);
+
+ /**
+ * Create a policy state task reference.
+ *
+ * @param name name of the policy
+ * @param version version of the policy, set to null to use the latest version
+ * @param stateName of the state
+ * @param taskLocalName the task local name
+ * @param taskName name of the task
+ * @param taskVersion version of the task, set to null to use the latest version
+ * @param outputType Type of output for the task, must be DIRECT for direct output to a state
+ * output or LOGIC for output to state finalizer logic
+ * @param outputName the name of the state output or state state finalizer logic to handle the
+ * task output
+ * @return result of the operation
+ */
+ // CHECKSTYLE:OFF: checkstyle:parameterNumber
+ ApexApiResult createPolicyStateTaskRef(final String name, final String version, final String stateName,
+ final String taskLocalName, final String taskName, final String taskVersion, final String outputType,
+ final String outputName);
+ // CHECKSTYLE:ON: checkstyle:parameterNumber
+
+ /**
+ * List policy state task references.
+ *
+ * @param name name of the policy
+ * @param version version of the policy, set to null to use the latest version
+ * @param stateName of the state
+ * @param taskName name of the task, set to null to list all task references
+ * @param taskVersion version of the task, set to null to use the latest version
+ * @return result of the operation
+ */
+ ApexApiResult listPolicyStateTaskRef(final String name, final String version, final String stateName,
+ final String taskName, final String taskVersion);
+
+ /**
+ * Delete a policy state task reference.
+ *
+ * @param name name of the policy
+ * @param version version of the policy, set to null to use the latest version
+ * @param stateName of the state
+ * @param taskName name of the task, set to null to delete all task references
+ * @param taskVersion version of the task, set to null to use the latest version
+ * @return result of the operation
+ */
+ ApexApiResult deletePolicyStateTaskRef(final String name, final String version, final String stateName,
+ final String taskName, final String taskVersion);
+
+ /**
+ * Create a policy state context album reference.
+ *
+ * @param name name of the policy
+ * @param version version of the policy, set to null to use the latest version
+ * @param stateName of the state
+ * @param contextAlbumName name of the context album for the context album reference
+ * @param contextAlbumVersion version of the context album for the context album reference, set
+ * to null to use the latest version
+ * @return result of the operation
+ */
+ ApexApiResult createPolicyStateContextRef(final String name, final String version, final String stateName,
+ final String contextAlbumName, final String contextAlbumVersion);
+
+ /**
+ * List policy state context album references.
+ *
+ * @param name name of the policy
+ * @param version version of the policy, set to null to use the latest version
+ * @param stateName of the state
+ * @param contextAlbumName name of the context album for the context album reference, set to
+ * null to list all task context album references
+ * @param contextAlbumVersion version of the context album for the context album reference, set
+ * to null to use the latest version
+ * @return result of the operation
+ */
+ ApexApiResult listPolicyStateContextRef(final String name, final String version, final String stateName,
+ final String contextAlbumName, final String contextAlbumVersion);
+
+ /**
+ * Delete a policy state context album reference.
+ *
+ * @param name name of the policy
+ * @param version version of the policy, set to null to use the default version
+ * @param stateName of the state
+ * @param contextAlbumName name of the context album for the context album reference, set to
+ * null to delete all task context album references
+ * @param contextAlbumVersion version of the context album for the context album reference, set
+ * to null to use the latest version
+ * @return result of the operation
+ */
+ ApexApiResult deletePolicyStateContextRef(final String name, final String version, final String stateName,
+ final String contextAlbumName, final String contextAlbumVersion);
+} \ No newline at end of file
diff --git a/model/src/main/java/org/onap/policy/apex/model/modelapi/ApexModel.java b/model/src/main/java/org/onap/policy/apex/model/modelapi/ApexModel.java
new file mode 100644
index 000000000..3c6e4c063
--- /dev/null
+++ b/model/src/main/java/org/onap/policy/apex/model/modelapi/ApexModel.java
@@ -0,0 +1,176 @@
+/*
+ * ============LICENSE_START=======================================================
+ * Copyright (C) 2016-2018 Ericsson. All rights reserved.
+ * Modifications Copyright (C) 2022 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.apex.model.modelapi;
+
+import org.onap.policy.apex.model.policymodel.concepts.AxPolicyModel;
+
+/**
+ * The Interface ApexModelAPI provides functional methods that allow Apex models to be managed.
+ *
+ * @author Liam Fallon (liam.fallon@ericsson.com)
+ */
+public interface ApexModel extends ApexEditorApi {
+ /**
+ * Make a deep copy of the Model.
+ *
+ * @return the result of the operation
+ */
+ ApexModel getCopy();
+
+ /**
+ * Load an Apex model from a string.
+ *
+ * @param modelString the string with the model
+ * @return the result of the operation
+ */
+ ApexApiResult loadFromString(String modelString);
+
+ /**
+ * Load an Apex model from a file.
+ *
+ * @param fileName the file name of the file with the model
+ * @return the result of the operation
+ */
+ ApexApiResult loadFromFile(String fileName);
+
+ /**
+ * Save an Apex model to a file.
+ *
+ * @param fileName the file name
+ * @return the result of the operation
+ */
+ ApexApiResult saveToFile(String fileName);
+
+ /**
+ * Read an APEX model from a location identified by a URL.
+ *
+ * @param urlString the url string
+ * @return the result of the operation
+ */
+ ApexApiResult readFromUrl(String urlString);
+
+ /**
+ * Write an APEX model to a location identified by a URL.
+ *
+ * @param urlString the URL to read the model from
+ * @return the result of the operation
+ */
+ ApexApiResult writeToUrl(String urlString);
+
+ /**
+ * Analyse an Apex model that shows the concept usage references of a policy model.
+ *
+ * @return the result of the operation
+ */
+ ApexApiResult analyse();
+
+ /**
+ * Validate an Apex model, checking all concepts and references in the model.
+ *
+ * @return the result of the operation
+ */
+ ApexApiResult validate();
+
+ /**
+ * Compare to Apex models, returning the differences between the models.
+ *
+ * @param otherModelFileName the file name of the other model
+ * @param diffsOnly only returns differences between the model when set
+ * @param keysOnly only returns the keys that are different when set, when not set values are also returned
+ * @return the result of the operation
+ */
+ ApexApiResult compare(String otherModelFileName, boolean diffsOnly, boolean keysOnly);
+
+ /**
+ * Compare two Apex models, returning the differences between the models.
+ *
+ * @param otherModelString the other model as a string
+ * @param diffsOnly only returns differences between the model when set
+ * @param keysOnly only returns the keys that are different when set, when not set values are also returned
+ * @return the result of the operation
+ */
+ ApexApiResult compareWithString(String otherModelString, boolean diffsOnly, boolean keysOnly);
+
+ /**
+ * Split out a sub model from an Apex model that contains a given subset of the policies in the original model.
+ *
+ * @param targetModelName the file name of the target model in which to store the model split out from the original
+ * model
+ * @param splitOutPolicies the policies form the original model to include in the split out model, specified as a
+ * comma delimited list of policy names
+ * @return the result of the operation
+ */
+ ApexApiResult split(String targetModelName, String splitOutPolicies);
+
+ /**
+ * Split out a sub model from an Apex model that contains a given subset of the policies in the original model,
+ * return the split model in the result as a string.
+ *
+ * @param splitOutPolicies the policies form the original model to include in the split out model, specified as a
+ * comma delimited list of policy names
+ * @return the result of the operation
+ */
+ ApexApiResult split(String splitOutPolicies);
+
+ /**
+ * Merge two Apex models together.
+ *
+ * @param mergeInModelName the file name of the model to merge into the current model
+ * @param keepOriginal if this flag is set to true, if a concept exists in both models, the original model copy of
+ * that concept is kept, if the flag is set to false, then the copy of the concept from the mergeInModel
+ * overwrites the concept in the original model
+ * @return the result of the operation
+ */
+ ApexApiResult merge(String mergeInModelName, boolean keepOriginal);
+
+ /**
+ * Merge two Apex models together.
+ *
+ * @param otherModelString the model to merge as a string
+ * @param keepOriginal if this flag is set to true, if a concept exists in both models, the original model copy of
+ * that concept is kept, if the flag is set to false, then the copy of the concept from the mergeInModel
+ * overwrites the concept in the original model
+ * @return the result of the operation
+ */
+ ApexApiResult mergeWithString(String otherModelString, boolean keepOriginal);
+
+ /**
+ * Get the raw policy model being used by this model.
+ *
+ * @return the policy model
+ */
+ AxPolicyModel getPolicyModel();
+
+ /**
+ * Set the raw policy model being used by this model.
+ *
+ * @param policyModel the policy model
+ */
+ void setPolicyModel(AxPolicyModel policyModel);
+
+ /**
+ * Builds the raw policy model being used by this model.
+ *
+ * @return the policy model
+ */
+ AxPolicyModel build();
+}
diff --git a/model/src/main/java/org/onap/policy/apex/model/modelapi/ApexModelFactory.java b/model/src/main/java/org/onap/policy/apex/model/modelapi/ApexModelFactory.java
new file mode 100644
index 000000000..908f562d2
--- /dev/null
+++ b/model/src/main/java/org/onap/policy/apex/model/modelapi/ApexModelFactory.java
@@ -0,0 +1,76 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * Copyright (C) 2016-2018 Ericsson. All rights reserved.
+ * Modifications Copyright (C) 2022 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.apex.model.modelapi;
+
+import java.util.Properties;
+import org.onap.policy.apex.model.modelapi.impl.ApexModelImpl;
+
+/**
+ * A factory for creating ApexModel objects using the Apex Model implementation.
+ *
+ * @author Liam Fallon (liam.fallon@ericsson.com)
+ */
+public class ApexModelFactory {
+
+ /**
+ * Creates a new ApexModel object from its implementation.
+ *
+ * @param apexProperties default values and other configuration information for the apex model
+ * @return the apex model
+ */
+ public ApexModel createApexModel(final Properties apexProperties) {
+ return new ApexModelImpl(setDefaultPropertyValues(apexProperties));
+ }
+
+ /**
+ * Sets default property values for Apex properties that must be set for the Apex model
+ * implementation if those properties are not already set.
+ *
+ * @param apexPropertiesIn the default property values
+ * @return the properties
+ */
+ private Properties setDefaultPropertyValues(final Properties apexPropertiesIn) {
+ Properties apexProperties = apexPropertiesIn;
+
+ if (apexProperties == null) {
+ apexProperties = new Properties();
+ }
+
+ if (apexProperties.getProperty("DEFAULT_CONCEPT_VERSION") == null) {
+ apexProperties.setProperty("DEFAULT_CONCEPT_VERSION", "0.0.1");
+ }
+ if (apexProperties.getProperty("DEFAULT_EVENT_NAMESPACE") == null) {
+ apexProperties.setProperty("DEFAULT_EVENT_NAMESPACE", "org.onap.policy.apex");
+ }
+ if (apexProperties.getProperty("DEFAULT_EVENT_SOURCE") == null) {
+ apexProperties.setProperty("DEFAULT_EVENT_SOURCE", "source");
+ }
+ if (apexProperties.getProperty("DEFAULT_EVENT_TARGET") == null) {
+ apexProperties.setProperty("DEFAULT_EVENT_TARGET", "target");
+ }
+ if (apexProperties.getProperty("DEFAULT_POLICY_TEMPLATE") == null) {
+ apexProperties.setProperty("DEFAULT_POLICY_TEMPLATE", "FREEFORM");
+ }
+
+ return apexProperties;
+ }
+}
diff --git a/model/src/main/java/org/onap/policy/apex/model/modelapi/impl/ApexModelImpl.java b/model/src/main/java/org/onap/policy/apex/model/modelapi/impl/ApexModelImpl.java
new file mode 100644
index 000000000..54b94d0e7
--- /dev/null
+++ b/model/src/main/java/org/onap/policy/apex/model/modelapi/impl/ApexModelImpl.java
@@ -0,0 +1,866 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * Copyright (C) 2016-2018 Ericsson. All rights reserved.
+ * Modifications Copyright (C) 2019 Samsung Electronics Co., Ltd.
+ * Modifications Copyright (C) 2019,2022 Nordix Foundation.
+ * Modifications Copyright (C) 2022 Bell Canada. All rights reserved.
+ * ================================================================================
+ * 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.apex.model.modelapi.impl;
+
+import java.util.Properties;
+import lombok.AccessLevel;
+import lombok.Getter;
+import lombok.NoArgsConstructor;
+import lombok.Setter;
+import org.onap.policy.apex.model.modelapi.ApexApiResult;
+import org.onap.policy.apex.model.modelapi.ApexApiResult.Result;
+import org.onap.policy.apex.model.modelapi.ApexModel;
+import org.onap.policy.apex.model.policymodel.concepts.AxPolicyModel;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * This class is an implementation of a facade on an Apex model for editors of Apex models.
+ *
+ * @author Liam Fallon (liam.fallon@ericsson.com)
+ */
+@NoArgsConstructor(access = AccessLevel.PRIVATE)
+public final class ApexModelImpl implements ApexModel {
+
+ public static final String FIELDS_DEPRECATED_WARN_MSG =
+ "inputFields and outputFields are deprecated from Task definition and will be removed. "
+ + "Instead, inputEvent and outputEvents are automatically populated to Tasks based on State definition";
+
+ private static final Logger LOGGER = LoggerFactory.getLogger(ApexModelImpl.class);
+
+ // The policy model being acted upon
+ @Getter
+ @Setter
+ private AxPolicyModel policyModel = new AxPolicyModel();
+
+ // @formatter:off
+ private ModelFacade modelFacade;
+ private KeyInformationFacade keyInformationFacade;
+ private ContextSchemaFacade contextSchemaFacade;
+ private EventFacade eventFacade;
+ private ContextAlbumFacade contextAlbumFacade;
+ private TaskFacade taskFacade;
+ private PolicyFacade policyFacade;
+ private ModelHandlerFacade modelHandlerFacade;
+ // @formatter:on
+
+ private Properties apexProperties;
+
+ /**
+ * Create an implementation of the Apex editor and model APIs.
+ *
+ * @param apexProperties The properties to use for the model
+ */
+ public ApexModelImpl(final Properties apexProperties) {
+ this.apexProperties = apexProperties;
+
+ // @formatter:off
+ this.modelFacade = new ModelFacade(this, apexProperties);
+ this.keyInformationFacade = new KeyInformationFacade(this, apexProperties);
+ this.contextSchemaFacade = new ContextSchemaFacade(this, apexProperties);
+ this.eventFacade = new EventFacade(this, apexProperties);
+ this.contextAlbumFacade = new ContextAlbumFacade(this, apexProperties);
+ this.taskFacade = new TaskFacade(this, apexProperties);
+ this.policyFacade = new PolicyFacade(this, apexProperties);
+ this.modelHandlerFacade = new ModelHandlerFacade(this, apexProperties);
+ // @formatter:on
+ }
+
+ /**
+ * {@inheritDoc}.
+ */
+ @Override
+ public ApexModel getCopy() {
+ ApexModelImpl ret = new ApexModelImpl();
+ // @formatter:off
+ ret.policyModel = new AxPolicyModel(policyModel);
+ ret.apexProperties = this.apexProperties;
+ ret.modelFacade = new ModelFacade(ret, this.apexProperties);
+ ret.keyInformationFacade = new KeyInformationFacade(ret, this.apexProperties);
+ ret.contextSchemaFacade = new ContextSchemaFacade(ret, this.apexProperties);
+ ret.eventFacade = new EventFacade(ret, this.apexProperties);
+ ret.contextAlbumFacade = new ContextAlbumFacade(ret, this.apexProperties);
+ ret.taskFacade = new TaskFacade(ret, this.apexProperties);
+ ret.policyFacade = new PolicyFacade(ret, this.apexProperties);
+ ret.modelHandlerFacade = new ModelHandlerFacade(ret, this.apexProperties);
+ // @formatter:on
+
+ return ret;
+ }
+
+ /**
+ * {@inheritDoc}.
+ */
+ @Override
+ public ApexApiResult createModel(final String name, final String version, final String uuid,
+ final String description) {
+ return modelFacade.createModel(name, version, uuid, description);
+ }
+
+ /**
+ * {@inheritDoc}.
+ */
+ @Override
+ public ApexApiResult updateModel(final String name, final String version, final String uuid,
+ final String description) {
+ return modelFacade.updateModel(name, version, uuid, description);
+ }
+
+ /**
+ * {@inheritDoc}.
+ */
+ @Override
+ public ApexApiResult getModelKey() {
+ return modelFacade.getModelKey();
+ }
+
+ /**
+ * {@inheritDoc}.
+ */
+ @Override
+ public ApexApiResult listModel() {
+ return modelFacade.listModel();
+ }
+
+ /**
+ * {@inheritDoc}.
+ */
+ @Override
+ public ApexApiResult deleteModel() {
+ return modelFacade.deleteModel();
+ }
+
+ /**
+ * {@inheritDoc}.
+ */
+ @Override
+ public ApexApiResult createKeyInformation(final String name, final String version, final String uuid,
+ final String description) {
+ return keyInformationFacade.createKeyInformation(name, version, uuid, description);
+ }
+
+ /**
+ * {@inheritDoc}.
+ */
+ @Override
+ public ApexApiResult updateKeyInformation(final String name, final String version, final String uuid,
+ final String description) {
+ return keyInformationFacade.updateKeyInformation(name, version, uuid, description);
+ }
+
+ /**
+ * {@inheritDoc}.
+ */
+ @Override
+ public ApexApiResult listKeyInformation(final String name, final String version) {
+ return keyInformationFacade.listKeyInformation(name, version);
+ }
+
+ /**
+ * {@inheritDoc}.
+ */
+ @Override
+ public ApexApiResult deleteKeyInformation(final String name, final String version) {
+ return keyInformationFacade.deleteKeyInformation(name, version);
+ }
+
+ /**
+ * {@inheritDoc}.
+ */
+ @Override
+ public ApexApiResult validateKeyInformation(final String name, final String version) {
+ return keyInformationFacade.validateKeyInformation(name, version);
+ }
+
+ /**
+ * {@inheritDoc}.
+ */
+ @Override
+ public ApexApiResult createContextSchema(final String name, final String version, final String schemaFlavour,
+ final String schemaDefinition, final String uuid, final String description) {
+ return contextSchemaFacade.createContextSchema(name, version, schemaFlavour, schemaDefinition, uuid,
+ description);
+ }
+
+ /**
+ * {@inheritDoc}.
+ */
+ @Override
+ public ApexApiResult updateContextSchema(final String name, final String version, final String schemaFlavour,
+ final String schemaDefinition, final String uuid, final String description) {
+ return contextSchemaFacade.updateContextSchema(name, version, schemaFlavour, schemaDefinition, uuid,
+ description);
+ }
+
+ /**
+ * {@inheritDoc}.
+ */
+ @Override
+ public ApexApiResult listContextSchemas(final String name, final String version) {
+ return contextSchemaFacade.listContextSchemas(name, version);
+ }
+
+ /**
+ * {@inheritDoc}.
+ */
+ @Override
+ public ApexApiResult deleteContextSchema(final String name, final String version) {
+ return contextSchemaFacade.deleteContextSchema(name, version);
+ }
+
+ /**
+ * {@inheritDoc}.
+ */
+ @Override
+ public ApexApiResult validateContextSchemas(final String name, final String version) {
+ return contextSchemaFacade.validateContextSchemas(name, version);
+ }
+
+ /**
+ * {@inheritDoc}.
+ */
+ @Override
+ public ApexApiResult createEvent(final String name, final String version, final String nameSpace,
+ final String source, final String target, final String uuid, final String description,
+ final String toscaPolicyState) {
+ return eventFacade.createEvent(name, version, nameSpace, source, target, uuid, description, toscaPolicyState);
+ }
+
+ /**
+ * {@inheritDoc}.
+ */
+ @Override
+ public ApexApiResult updateEvent(final String name, final String version, final String nameSpace,
+ final String source, final String target, final String uuid, final String description,
+ final String toscaPolicyState) {
+ return eventFacade.updateEvent(name, version, nameSpace, source, target, uuid, description, toscaPolicyState);
+ }
+
+ /**
+ * {@inheritDoc}.
+ */
+ @Override
+ public ApexApiResult listEvent(final String name, final String version) {
+ return eventFacade.listEvent(name, version);
+ }
+
+ /**
+ * {@inheritDoc}.
+ */
+ @Override
+ public ApexApiResult deleteEvent(final String name, final String version) {
+ return eventFacade.deleteEvent(name, version);
+ }
+
+ /**
+ * {@inheritDoc}.
+ */
+ @Override
+ public ApexApiResult validateEvent(final String name, final String version) {
+ return eventFacade.validateEvent(name, version);
+ }
+
+ /**
+ * {@inheritDoc}.
+ */
+ @Override
+ public ApexApiResult createEventPar(final String name, final String version, final String parName,
+ final String contextSchemaName, final String contextSchemaVersion, final boolean optional) {
+ return eventFacade.createEventPar(name, version, parName, contextSchemaName, contextSchemaVersion, optional);
+ }
+
+ /**
+ * {@inheritDoc}.
+ */
+ @Override
+ public ApexApiResult listEventPar(final String name, final String version, final String parName) {
+ return eventFacade.listEventPar(name, version, parName);
+ }
+
+ /**
+ * {@inheritDoc}.
+ */
+ @Override
+ public ApexApiResult deleteEventPar(final String name, final String version, final String parName) {
+ return eventFacade.deleteEventPar(name, version, parName);
+ }
+
+ /**
+ * {@inheritDoc}.
+ */
+ @Override
+ // CHECKSTYLE:OFF: checkstyle:parameterNumber
+ public ApexApiResult createContextAlbum(final String name, final String version, final String scope,
+ final String writable, final String contextSchemaName, final String contextSchemaVersion, final String uuid,
+ final String description) {
+ return contextAlbumFacade.createContextAlbum(ContextAlbum.builder().name(name).version(version)
+ .scope(scope).writable(writable).contextSchemaName(contextSchemaName)
+ .contextSchemaVersion(contextSchemaVersion).uuid(uuid).description(description).build());
+ }
+ // CHECKSTYLE:ON: checkstyle:parameterNumber
+
+ /**
+ * {@inheritDoc}.
+ */
+ // CHECKSTYLE:OFF: checkstyle:parameterNumber
+ @Override
+ public ApexApiResult updateContextAlbum(final String name, final String version, final String scope,
+ final String writable, final String contextSchemaName, final String contextSchemaVersion, final String uuid,
+ final String description) {
+ return contextAlbumFacade.updateContextAlbum(ContextAlbum.builder().name(name).version(version)
+ .scope(scope).writable(writable).contextSchemaName(contextSchemaName)
+ .contextSchemaVersion(contextSchemaVersion).uuid(uuid).description(description).build());
+ }
+ // CHECKSTYLE:ON: checkstyle:parameterNumber
+
+ /**
+ * {@inheritDoc}.
+ */
+ @Override
+ public ApexApiResult listContextAlbum(final String name, final String version) {
+ return contextAlbumFacade.listContextAlbum(name, version);
+ }
+
+ /**
+ * {@inheritDoc}.
+ */
+ @Override
+ public ApexApiResult deleteContextAlbum(final String name, final String version) {
+ return contextAlbumFacade.deleteContextAlbum(name, version);
+ }
+
+ /**
+ * {@inheritDoc}.
+ */
+ @Override
+ public ApexApiResult validateContextAlbum(final String name, final String version) {
+ return contextAlbumFacade.validateContextAlbum(name, version);
+ }
+
+ /**
+ * {@inheritDoc}.
+ */
+ @Override
+ public ApexApiResult createTask(final String name, final String version, final String uuid,
+ final String description) {
+ return taskFacade.createTask(name, version, uuid, description);
+ }
+
+ /**
+ * {@inheritDoc}.
+ */
+ @Override
+ public ApexApiResult updateTask(final String name, final String version, final String uuid,
+ final String description) {
+ return taskFacade.updateTask(name, version, uuid, description);
+ }
+
+ /**
+ * {@inheritDoc}.
+ */
+ @Override
+ public ApexApiResult listTask(final String name, final String version) {
+ return taskFacade.listTask(name, version);
+ }
+
+ /**
+ * {@inheritDoc}.
+ */
+ @Override
+ public ApexApiResult deleteTask(final String name, final String version) {
+ return taskFacade.deleteTask(name, version);
+ }
+
+ /**
+ * {@inheritDoc}.
+ */
+ @Override
+ public ApexApiResult validateTask(final String name, final String version) {
+ return taskFacade.validateTask(name, version);
+ }
+
+ /**
+ * {@inheritDoc}.
+ */
+ @Override
+ public ApexApiResult createTaskLogic(final String name, final String version, final String logicFlavour,
+ final String logic) {
+ return taskFacade.createTaskLogic(name, version, logicFlavour, logic);
+ }
+
+ /**
+ * {@inheritDoc}.
+ */
+ @Override
+ public ApexApiResult updateTaskLogic(final String name, final String version, final String logicFlavour,
+ final String logic) {
+ return taskFacade.updateTaskLogic(name, version, logicFlavour, logic);
+ }
+
+ /**
+ * {@inheritDoc}.
+ */
+ @Override
+ public ApexApiResult listTaskLogic(final String name, final String version) {
+ return taskFacade.listTaskLogic(name, version);
+ }
+
+ /**
+ * {@inheritDoc}.
+ */
+ @Override
+ public ApexApiResult deleteTaskLogic(final String name, final String version) {
+ return taskFacade.deleteTaskLogic(name, version);
+ }
+
+ /**
+ * {@inheritDoc}.
+ */
+ @Override
+ public ApexApiResult createTaskField(final String name, final String version, final String fieldName,
+ final String dataTypeName, final String dataTypeVersion, final boolean optional) {
+ LOGGER.warn(FIELDS_DEPRECATED_WARN_MSG);
+ return new ApexApiResult(Result.SUCCESS, FIELDS_DEPRECATED_WARN_MSG);
+ }
+
+ /**
+ * {@inheritDoc}.
+ */
+ @Override
+ public ApexApiResult handleTaskField(final String name, final String version, final String fieldName) {
+ LOGGER.warn(FIELDS_DEPRECATED_WARN_MSG);
+ return new ApexApiResult(Result.SUCCESS, FIELDS_DEPRECATED_WARN_MSG);
+ }
+
+ /**
+ * {@inheritDoc}.
+ */
+ @Override
+ public ApexApiResult createTaskParameter(final String name, final String version, final String parName,
+ final String defaultValue) {
+ return taskFacade.createTaskParameter(name, version, parName, defaultValue);
+ }
+
+ /**
+ * {@inheritDoc}.
+ */
+ @Override
+ public ApexApiResult listTaskParameter(final String name, final String version, final String parName) {
+ return taskFacade.listTaskParameter(name, version, parName);
+ }
+
+ /**
+ * {@inheritDoc}.
+ */
+ @Override
+ public ApexApiResult deleteTaskParameter(final String name, final String version, final String parName) {
+ return taskFacade.deleteTaskParameter(name, version, parName);
+ }
+
+ /**
+ * {@inheritDoc}.
+ */
+ @Override
+ public ApexApiResult createTaskContextRef(final String name, final String version, final String contextAlbumName,
+ final String contextAlbumVersion) {
+ return taskFacade.createTaskContextRef(name, version, contextAlbumName, contextAlbumVersion);
+ }
+
+ /**
+ * {@inheritDoc}.
+ */
+ @Override
+ public ApexApiResult listTaskContextRef(final String name, final String version, final String contextAlbumName,
+ final String contextAlbumVersion) {
+ return taskFacade.listTaskContextRef(name, version, contextAlbumName, contextAlbumVersion);
+ }
+
+ /**
+ * {@inheritDoc}.
+ */
+ @Override
+ public ApexApiResult deleteTaskContextRef(final String name, final String version, final String contextAlbumName,
+ final String contextAlbumVersion) {
+ return taskFacade.deleteTaskContextRef(name, version, contextAlbumName, contextAlbumVersion);
+ }
+
+ /**
+ * {@inheritDoc}.
+ */
+ @Override
+ public ApexApiResult createPolicy(final String name, final String version, final String template,
+ final String firstState, final String uuid, final String description) {
+ return policyFacade.createPolicy(name, version, template, firstState, uuid, description);
+ }
+
+ /**
+ * {@inheritDoc}.
+ */
+ @Override
+ public ApexApiResult updatePolicy(final String name, final String version, final String template,
+ final String firstState, final String uuid, final String description) {
+ return policyFacade.updatePolicy(name, version, template, firstState, uuid, description);
+ }
+
+ /**
+ * {@inheritDoc}.
+ */
+ @Override
+ public ApexApiResult listPolicy(final String name, final String version) {
+ return policyFacade.listPolicy(name, version);
+ }
+
+ /**
+ * {@inheritDoc}.
+ */
+ @Override
+ public ApexApiResult deletePolicy(final String name, final String version) {
+ return policyFacade.deletePolicy(name, version);
+ }
+
+ /**
+ * {@inheritDoc}.
+ */
+ @Override
+ public ApexApiResult validatePolicy(final String name, final String version) {
+ return policyFacade.validatePolicy(name, version);
+ }
+
+ /**
+ * {@inheritDoc}.
+ */
+ @Override
+ public ApexApiResult createPolicyState(final String name, final String version, final String stateName,
+ final String triggerName, final String triggerVersion, final String defaultTaskName,
+ final String defaltTaskVersion) {
+ return policyFacade.createPolicyState(name, version, stateName, triggerName, triggerVersion, defaultTaskName,
+ defaltTaskVersion);
+ }
+
+ /**
+ * {@inheritDoc}.
+ */
+ @Override
+ public ApexApiResult updatePolicyState(final String name, final String version, final String stateName,
+ final String triggerName, final String triggerVersion, final String defaultTaskName,
+ final String defaltTaskVersion) {
+ return policyFacade.updatePolicyState(name, version, stateName, triggerName, triggerVersion, defaultTaskName,
+ defaltTaskVersion);
+ }
+
+ /**
+ * {@inheritDoc}.
+ */
+ @Override
+ public ApexApiResult listPolicyState(final String name, final String version, final String stateName) {
+ return policyFacade.listPolicyState(name, version, stateName);
+ }
+
+ /**
+ * {@inheritDoc}.
+ */
+ @Override
+ public ApexApiResult deletePolicyState(final String name, final String version, final String stateName) {
+ return policyFacade.deletePolicyState(name, version, stateName);
+ }
+
+ /**
+ * {@inheritDoc}.
+ */
+ @Override
+ public ApexApiResult createPolicyStateTaskSelectionLogic(final String name, final String version,
+ final String stateName, final String logicFlavour, final String logic) {
+ return policyFacade.createPolicyStateTaskSelectionLogic(name, version, stateName, logicFlavour, logic);
+ }
+
+ /**
+ * {@inheritDoc}.
+ */
+ @Override
+ public ApexApiResult updatePolicyStateTaskSelectionLogic(final String name, final String version,
+ final String stateName, final String logicFlavour, final String logic) {
+ return policyFacade.updatePolicyStateTaskSelectionLogic(name, version, stateName, logicFlavour, logic);
+ }
+
+ /**
+ * {@inheritDoc}.
+ */
+ @Override
+ public ApexApiResult listPolicyStateTaskSelectionLogic(final String name, final String version,
+ final String stateName) {
+ return policyFacade.listPolicyStateTaskSelectionLogic(name, version, stateName);
+ }
+
+ /**
+ * {@inheritDoc}.
+ */
+ @Override
+ public ApexApiResult deletePolicyStateTaskSelectionLogic(final String name, final String version,
+ final String stateName) {
+ return policyFacade.deletePolicyStateTaskSelectionLogic(name, version, stateName);
+ }
+
+ /**
+ * {@inheritDoc}.
+ */
+ @Override
+ public ApexApiResult createPolicyStateOutput(final String name, final String version, final String stateName,
+ final String outputName, final String eventName, final String eventVersion, final String nextState) {
+ return policyFacade.createPolicyStateOutput(name, version, stateName, outputName, eventName, eventVersion,
+ nextState);
+ }
+
+ /**
+ * {@inheritDoc}.
+ */
+ @Override
+ public ApexApiResult listPolicyStateOutput(final String name, final String version, final String stateName,
+ final String outputName) {
+ return policyFacade.listPolicyStateOutput(name, version, stateName, outputName);
+ }
+
+ /**
+ * {@inheritDoc}.
+ */
+ @Override
+ public ApexApiResult deletePolicyStateOutput(final String name, final String version, final String stateName,
+ final String outputName) {
+ return policyFacade.deletePolicyStateOutput(name, version, stateName, outputName);
+ }
+
+ /**
+ * {@inheritDoc}.
+ */
+ @Override
+ public ApexApiResult createPolicyStateFinalizerLogic(final String name, final String version,
+ final String stateName, final String finalizerLogicName, final String logicFlavour, final String logic) {
+ return policyFacade.createPolicyStateFinalizerLogic(name, version, stateName, finalizerLogicName, logicFlavour,
+ logic);
+ }
+
+ /**
+ * {@inheritDoc}.
+ */
+ @Override
+ public ApexApiResult updatePolicyStateFinalizerLogic(final String name, final String version,
+ final String stateName, final String finalizerLogicName, final String logicFlavour, final String logic) {
+ return policyFacade.updatePolicyStateFinalizerLogic(name, version, stateName, finalizerLogicName, logicFlavour,
+ logic);
+ }
+
+ /**
+ * {@inheritDoc}.
+ */
+ @Override
+ public ApexApiResult listPolicyStateFinalizerLogic(final String name, final String version, final String stateName,
+ final String finalizerLogicName) {
+ return policyFacade.listPolicyStateFinalizerLogic(name, version, stateName, finalizerLogicName);
+ }
+
+ /**
+ * {@inheritDoc}.
+ */
+ @Override
+ public ApexApiResult deletePolicyStateFinalizerLogic(final String name, final String version,
+ final String stateName, final String finalizerLogicName) {
+ return policyFacade.deletePolicyStateFinalizerLogic(name, version, stateName, finalizerLogicName);
+ }
+
+ /**
+ * {@inheritDoc}.
+ */
+ @Override
+ // CHECKSTYLE:OFF: checkstyle:parameterNumber
+ public ApexApiResult createPolicyStateTaskRef(final String name, final String version, final String stateName,
+ final String taskLocalName, final String taskName, final String taskVersion, final String outputType,
+ final String outputName) {
+ return policyFacade.createPolicyStateTaskRef(CreatePolicyStateTaskRef.builder().name(name)
+ .version(version).stateName(stateName).taskLocalName(taskLocalName).taskName(taskName)
+ .taskVersion(taskVersion).outputType(outputType).outputName(outputName).build());
+ }
+ // CHECKSTYLE:ON: checkstyle:parameterNumber
+
+ /**
+ * {@inheritDoc}.
+ */
+ @Override
+ public ApexApiResult listPolicyStateTaskRef(final String name, final String version, final String stateName,
+ final String taskName, final String taskVersion) {
+ return policyFacade.listPolicyStateTaskRef(name, version, stateName, taskName, taskVersion);
+ }
+
+ /**
+ * {@inheritDoc}.
+ */
+ @Override
+ public ApexApiResult deletePolicyStateTaskRef(final String name, final String version, final String stateName,
+ final String taskName, final String taskVersion) {
+ return policyFacade.deletePolicyStateTaskRef(name, version, stateName, taskName, taskVersion);
+ }
+
+ /**
+ * {@inheritDoc}.
+ */
+ @Override
+ public ApexApiResult createPolicyStateContextRef(final String name, final String version, final String stateName,
+ final String contextAlbumName, final String contextAlbumVersion) {
+ return policyFacade.createPolicyStateContextRef(name, version, stateName, contextAlbumName,
+ contextAlbumVersion);
+ }
+
+ /**
+ * {@inheritDoc}.
+ */
+ @Override
+ public ApexApiResult listPolicyStateContextRef(final String name, final String version, final String stateName,
+ final String contextAlbumName, final String contextAlbumVersion) {
+ return policyFacade.listPolicyStateContextRef(name, version, stateName, contextAlbumName, contextAlbumVersion);
+ }
+
+ /**
+ * {@inheritDoc}.
+ */
+ @Override
+ public ApexApiResult deletePolicyStateContextRef(final String name, final String version, final String stateName,
+ final String contextAlbumName, final String contextAlbumVersion) {
+ return policyFacade.deletePolicyStateContextRef(name, version, stateName, contextAlbumName,
+ contextAlbumVersion);
+ }
+
+ /**
+ * {@inheritDoc}.
+ */
+ @Override
+ public ApexApiResult loadFromString(final String modelString) {
+ return modelHandlerFacade.loadFromString(modelString);
+ }
+
+ /**
+ * {@inheritDoc}.
+ */
+ @Override
+ // CHECKSTYLE:OFF: checkstyle:HiddenField
+ public ApexApiResult loadFromFile(final String fileName) {
+ return modelHandlerFacade.loadFromFile(fileName);
+ }
+ // CHECKSTYLE:ON: checkstyle:HiddenField
+
+ /**
+ * {@inheritDoc}.
+ */
+ @Override
+ public ApexApiResult saveToFile(final String fileName) {
+ return modelHandlerFacade.saveToFile(fileName);
+ }
+
+ /**
+ * {@inheritDoc}.
+ */
+ @Override
+ public ApexApiResult readFromUrl(final String urlString) {
+ return modelHandlerFacade.readFromUrl(urlString);
+ }
+
+ /**
+ * {@inheritDoc}.
+ */
+ @Override
+ public ApexApiResult writeToUrl(final String urlString) {
+ return modelHandlerFacade.writeToUrl(urlString);
+ }
+
+ /**
+ * {@inheritDoc}.
+ */
+ @Override
+ public ApexApiResult analyse() {
+ return modelHandlerFacade.analyse();
+ }
+
+ /**
+ * {@inheritDoc}.
+ */
+ @Override
+ public ApexApiResult validate() {
+ return modelHandlerFacade.validate();
+ }
+
+ /**
+ * {@inheritDoc}.
+ */
+ @Override
+ public ApexApiResult compare(final String otherModelFileName, final boolean diffsOnly, final boolean keysOnly) {
+ return modelHandlerFacade.compare(otherModelFileName, diffsOnly, keysOnly);
+ }
+
+ /**
+ * {@inheritDoc}.
+ */
+ @Override
+ public ApexApiResult compareWithString(final String otherModelString, final boolean diffsOnly,
+ final boolean keysOnly) {
+ return modelHandlerFacade.compareWithString(otherModelString, diffsOnly, keysOnly);
+ }
+
+ /**
+ * {@inheritDoc}.
+ */
+ @Override
+ public ApexApiResult split(final String targetModelFileName, final String splitOutPolicies) {
+ return modelHandlerFacade.split(targetModelFileName, splitOutPolicies);
+ }
+
+ /**
+ * {@inheritDoc}.
+ */
+ @Override
+ public ApexApiResult split(final String splitOutPolicies) {
+ return modelHandlerFacade.split(splitOutPolicies);
+ }
+
+ /**
+ * {@inheritDoc}.
+ */
+ @Override
+ public ApexApiResult merge(final String mergeInModelFileName, final boolean keepOriginal) {
+ return modelHandlerFacade.merge(mergeInModelFileName, keepOriginal);
+ }
+
+ /**
+ * {@inheritDoc}.
+ */
+ @Override
+ public ApexApiResult mergeWithString(final String otherModelString, final boolean keepOriginal) {
+ return modelHandlerFacade.mergeWithString(otherModelString, keepOriginal);
+ }
+
+ /**
+ * {@inheritDoc}.
+ */
+ @Override
+ public AxPolicyModel build() {
+ return policyModel;
+ }
+
+}
diff --git a/model/src/main/java/org/onap/policy/apex/model/modelapi/impl/ContextAlbum.java b/model/src/main/java/org/onap/policy/apex/model/modelapi/impl/ContextAlbum.java
new file mode 100644
index 000000000..5a42ed14e
--- /dev/null
+++ b/model/src/main/java/org/onap/policy/apex/model/modelapi/impl/ContextAlbum.java
@@ -0,0 +1,39 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * Copyright (C) 2019 Samsung Electronics Co., Ltd. All rights reserved.
+ * Modifications Copyright (C) 2019 Nordix Foundation.
+ * Modifications Copyright (C) 2021 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.apex.model.modelapi.impl;
+
+import lombok.Builder;
+import lombok.Getter;
+
+@Getter
+@Builder
+public class ContextAlbum {
+ private String name;
+ private String version;
+ private String scope;
+ private String writable;
+ private String contextSchemaName;
+ private String contextSchemaVersion;
+ private String uuid;
+ private String description;
+}
diff --git a/model/src/main/java/org/onap/policy/apex/model/modelapi/impl/ContextAlbumFacade.java b/model/src/main/java/org/onap/policy/apex/model/modelapi/impl/ContextAlbumFacade.java
new file mode 100644
index 000000000..d29f32ea0
--- /dev/null
+++ b/model/src/main/java/org/onap/policy/apex/model/modelapi/impl/ContextAlbumFacade.java
@@ -0,0 +1,254 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * Copyright (C) 2016-2018 Ericsson. All rights reserved.
+ * Modifications Copyright (C) 2019 Samsung Electronics Co., Ltd.
+ * Modifications Copyright (C) 2019,2022 Nordix Foundation.
+ * Modifications Copyright (C) 2021 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.apex.model.modelapi.impl;
+
+import java.util.Properties;
+import java.util.Set;
+import org.onap.policy.apex.model.basicmodel.concepts.AxArtifactKey;
+import org.onap.policy.apex.model.basicmodel.concepts.AxValidationResult;
+import org.onap.policy.apex.model.basicmodel.handling.ApexModelStringWriter;
+import org.onap.policy.apex.model.contextmodel.concepts.AxContextAlbum;
+import org.onap.policy.apex.model.contextmodel.concepts.AxContextSchema;
+import org.onap.policy.apex.model.modelapi.ApexApiResult;
+import org.onap.policy.apex.model.modelapi.ApexModel;
+
+/**
+ * This class acts as a facade for operations towards a policy model for context album operations.
+ *
+ * @author Liam Fallon (liam.fallon@ericsson.com)
+ */
+public class ContextAlbumFacade {
+ private static final String CONCEPT = "concept ";
+ private static final String CONCEPT_S = "concept(s) ";
+ private static final String DOES_NOT_EXIST = " does not exist";
+ private static final String DO_ES_NOT_EXIST = " do(es) not exist";
+
+ // Apex model we're working towards
+ private final ApexModel apexModel;
+
+ // Properties to use for the model
+ private final Properties apexProperties;
+
+ // Facade classes for working towards the real Apex model
+ private final KeyInformationFacade keyInformationFacade;
+
+ /**
+ * Constructor that creates a context album facade for the Apex Model API.
+ *
+ * @param apexModel the apex model
+ * @param apexProperties Properties for the model
+ */
+ public ContextAlbumFacade(final ApexModel apexModel, final Properties apexProperties) {
+ this.apexModel = apexModel;
+ this.apexProperties = apexProperties;
+
+ keyInformationFacade = new KeyInformationFacade(apexModel, apexProperties);
+ }
+
+ /**
+ * Create a context album.
+ *
+ * @param builder the builder for the context album parameters
+ * @return result of the operation
+ */
+ // CHECKSTYLE:OFF: checkstyle:parameterNumber
+ public ApexApiResult createContextAlbum(ContextAlbum builder) {
+ try {
+ final AxArtifactKey key = new AxArtifactKey();
+ key.setName(builder.getName());
+ if (builder.getVersion() != null) {
+ key.setVersion(builder.getVersion());
+ } else {
+ key.setVersion(apexProperties.getProperty("DEFAULT_CONCEPT_VERSION"));
+ }
+
+ if (apexModel.getPolicyModel().getAlbums().getAlbumsMap().containsKey(key)) {
+ return new ApexApiResult(ApexApiResult.Result.CONCEPT_EXISTS,
+ CONCEPT + key.getId() + " already exists");
+ }
+
+ final AxContextSchema schema = apexModel.getPolicyModel().getSchemas().get(builder.getContextSchemaName(),
+ builder.getContextSchemaVersion());
+ if (schema == null) {
+ return new ApexApiResult(ApexApiResult.Result.CONCEPT_DOES_NOT_EXIST, CONCEPT
+ + builder.getContextSchemaName() + ':' + builder.getContextSchemaVersion() + DOES_NOT_EXIST);
+ }
+
+ final AxContextAlbum contextAlbum = new AxContextAlbum(key);
+ contextAlbum.setScope(builder.getScope());
+ contextAlbum.setItemSchema(schema.getKey());
+
+ contextAlbum
+ .setWritable(builder.getWritable() != null && ("true".equalsIgnoreCase(builder.getWritable().trim())
+ || "t".equalsIgnoreCase(builder.getWritable().trim())));
+
+ apexModel.getPolicyModel().getAlbums().getAlbumsMap().put(key, contextAlbum);
+
+ if (apexModel.getPolicyModel().getKeyInformation().getKeyInfoMap().containsKey(key)) {
+ return keyInformationFacade.updateKeyInformation(builder.getName(), builder.getVersion(),
+ builder.getUuid(), builder.getDescription());
+ } else {
+ return keyInformationFacade.createKeyInformation(builder.getName(), builder.getVersion(),
+ builder.getUuid(), builder.getDescription());
+ }
+ } catch (final Exception e) {
+ return new ApexApiResult(ApexApiResult.Result.FAILED, e);
+ }
+ }
+ // CHECKSTYLE:ON: checkstyle:parameterNumber
+
+ /**
+ * Update a context album.
+ *
+ * @param builder the builder for the context album parameters
+ * @return result of the operation
+ */
+ // CHECKSTYLE:OFF: checkstyle:parameterNumber
+ public ApexApiResult updateContextAlbum(ContextAlbum builder) {
+ try {
+ final AxContextAlbum contextAlbum =
+ apexModel.getPolicyModel().getAlbums().get(builder.getName(), builder.getVersion());
+ if (contextAlbum == null) {
+ return new ApexApiResult(ApexApiResult.Result.CONCEPT_DOES_NOT_EXIST,
+ CONCEPT + builder.getName() + ':' + builder.getVersion() + DOES_NOT_EXIST);
+ }
+
+ if (builder.getScope() != null) {
+ contextAlbum.setScope(builder.getScope());
+ }
+
+ contextAlbum
+ .setWritable(builder.getWritable() != null && ("true".equalsIgnoreCase(builder.getWritable().trim())
+ || "t".equalsIgnoreCase(builder.getWritable().trim())));
+
+ if (builder.getContextSchemaName() != null) {
+ final AxContextSchema schema = apexModel.getPolicyModel().getSchemas()
+ .get(builder.getContextSchemaName(), builder.getContextSchemaVersion());
+ if (schema == null) {
+ return new ApexApiResult(ApexApiResult.Result.CONCEPT_DOES_NOT_EXIST, CONCEPT
+ + builder.getContextSchemaName() + ':' + builder.getContextSchemaVersion() + DOES_NOT_EXIST);
+ }
+ contextAlbum.setItemSchema(schema.getKey());
+ }
+
+ return keyInformationFacade.updateKeyInformation(builder.getName(), builder.getVersion(), builder.getUuid(),
+ builder.getDescription());
+ } catch (final Exception e) {
+ return new ApexApiResult(ApexApiResult.Result.FAILED, e);
+ }
+ }
+ // CHECKSTYLE:ON: checkstyle:parameterNumber
+
+ /**
+ * List context albums.
+ *
+ * @param name name of the context album, set to null to list all
+ * @param version starting version of the context album, set to null to list all versions
+ * @return result of the operation
+ */
+ public ApexApiResult listContextAlbum(final String name, final String version) {
+ try {
+ final Set<AxContextAlbum> contextAlbumSet = apexModel.getPolicyModel().getAlbums().getAll(name, version);
+ if (name != null && contextAlbumSet.isEmpty()) {
+ return new ApexApiResult(ApexApiResult.Result.CONCEPT_DOES_NOT_EXIST,
+ CONCEPT_S + name + ':' + version + DO_ES_NOT_EXIST);
+ }
+
+ final ApexApiResult result = new ApexApiResult();
+ for (final AxContextAlbum contextAlbum : contextAlbumSet) {
+ result.addMessage(
+ new ApexModelStringWriter<AxContextAlbum>(false).writeString(contextAlbum, AxContextAlbum.class));
+ }
+ return result;
+ } catch (final Exception e) {
+ return new ApexApiResult(ApexApiResult.Result.FAILED, e);
+ }
+ }
+
+ /**
+ * Delete a context album.
+ *
+ * @param name name of the context album
+ * @param version version of the context album, set to null to delete versions
+ * @return result of the operation
+ */
+ public ApexApiResult deleteContextAlbum(final String name, final String version) {
+ try {
+ if (version != null) {
+ final AxArtifactKey key = new AxArtifactKey(name, version);
+ if (apexModel.getPolicyModel().getAlbums().getAlbumsMap().remove(key) != null) {
+ return new ApexApiResult();
+ } else {
+ return new ApexApiResult(ApexApiResult.Result.CONCEPT_DOES_NOT_EXIST,
+ CONCEPT + key.getId() + DOES_NOT_EXIST);
+ }
+ }
+
+ final Set<AxContextAlbum> contextAlbumSet = apexModel.getPolicyModel().getAlbums().getAll(name, version);
+ if (contextAlbumSet.isEmpty()) {
+ return new ApexApiResult(ApexApiResult.Result.CONCEPT_DOES_NOT_EXIST,
+ CONCEPT_S + name + ':' + version + DO_ES_NOT_EXIST);
+ }
+
+ final ApexApiResult result = new ApexApiResult();
+ for (final AxContextAlbum contextAlbum : contextAlbumSet) {
+ result.addMessage(
+ new ApexModelStringWriter<AxContextAlbum>(false).writeString(contextAlbum, AxContextAlbum.class));
+ apexModel.getPolicyModel().getAlbums().getAlbumsMap().remove(contextAlbum.getKey());
+ keyInformationFacade.deleteKeyInformation(name, version);
+ }
+ return result;
+ } catch (final Exception e) {
+ return new ApexApiResult(ApexApiResult.Result.FAILED, e);
+ }
+ }
+
+ /**
+ * Validate context albums.
+ *
+ * @param name name of the context album, set to null to list all
+ * @param version starting version of the context album, set to null to list all versions
+ * @return result of the operation
+ */
+ public ApexApiResult validateContextAlbum(final String name, final String version) {
+ try {
+ final Set<AxContextAlbum> contextAlbumSet = apexModel.getPolicyModel().getAlbums().getAll(name, version);
+ if (contextAlbumSet.isEmpty()) {
+ return new ApexApiResult(ApexApiResult.Result.CONCEPT_DOES_NOT_EXIST,
+ CONCEPT_S + name + ':' + version + DO_ES_NOT_EXIST);
+ }
+
+ final ApexApiResult result = new ApexApiResult();
+ for (final AxContextAlbum contextAlbum : contextAlbumSet) {
+ final AxValidationResult validationResult = contextAlbum.validate(new AxValidationResult());
+ result.addMessage(new ApexModelStringWriter<AxArtifactKey>(false).writeString(contextAlbum.getKey(),
+ AxArtifactKey.class));
+ result.addMessage(validationResult.toString());
+ }
+ return result;
+ } catch (final Exception e) {
+ return new ApexApiResult(ApexApiResult.Result.FAILED, e);
+ }
+ }
+}
diff --git a/model/src/main/java/org/onap/policy/apex/model/modelapi/impl/ContextSchemaFacade.java b/model/src/main/java/org/onap/policy/apex/model/modelapi/impl/ContextSchemaFacade.java
new file mode 100644
index 000000000..83bca5a01
--- /dev/null
+++ b/model/src/main/java/org/onap/policy/apex/model/modelapi/impl/ContextSchemaFacade.java
@@ -0,0 +1,238 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * Copyright (C) 2016-2018 Ericsson. All rights reserved.
+ * Modifications Copyright (C) 2019,2022 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.apex.model.modelapi.impl;
+
+import java.util.Properties;
+import java.util.Set;
+import org.onap.policy.apex.model.basicmodel.concepts.AxArtifactKey;
+import org.onap.policy.apex.model.basicmodel.concepts.AxValidationResult;
+import org.onap.policy.apex.model.basicmodel.handling.ApexModelStringWriter;
+import org.onap.policy.apex.model.contextmodel.concepts.AxContextSchema;
+import org.onap.policy.apex.model.modelapi.ApexApiResult;
+import org.onap.policy.apex.model.modelapi.ApexModel;
+import org.onap.policy.common.utils.validation.Assertions;
+
+/**
+ * This class acts as a facade for operations towards a policy model for context schema operations.
+ *
+ * @author Liam Fallon (liam.fallon@ericsson.com)
+ */
+public class ContextSchemaFacade {
+ private static final String CONCEPT = "concept ";
+ private static final String CONCEPT_S = "concept(s) ";
+ private static final String DOES_NOT_EXIST = " does not exist";
+ private static final String DO_ES_NOT_EXIST = " do(es) not exist";
+ private static final String ALREADY_EXISTS = " already exists";
+
+ // Apex model we're working towards
+ private final ApexModel apexModel;
+
+ // Properties to use for the model
+ private final Properties apexProperties;
+
+ // Facade classes for working towards the real Apex model
+ private final KeyInformationFacade keyInformationFacade;
+
+ /**
+ * Constructor to create the context schema facade for the Model API.
+ *
+ * @param apexModel the apex model
+ * @param apexProperties Properties for the model
+ */
+ public ContextSchemaFacade(final ApexModel apexModel, final Properties apexProperties) {
+ this.apexModel = apexModel;
+ this.apexProperties = apexProperties;
+
+ keyInformationFacade = new KeyInformationFacade(apexModel, apexProperties);
+ }
+
+ /**
+ * Create a context schema.
+ *
+ * @param name name of the context schema
+ * @param version version of the context schema, set to null to use the default version
+ * @param schemaFlavour a string identifying the flavour of this context schema
+ * @param schemaDefinition a string containing the definition of this context schema
+ * @param uuid context schema UUID, set to null to generate a UUID
+ * @param description context schema description, set to null to generate a description
+ * @return result of the operation
+ */
+ public ApexApiResult createContextSchema(final String name, final String version, final String schemaFlavour,
+ final String schemaDefinition, final String uuid, final String description) {
+ try {
+ Assertions.argumentNotNull(schemaFlavour, "schemaFlavour may not be null");
+
+ final AxArtifactKey key = new AxArtifactKey();
+ key.setName(name);
+ if (version != null) {
+ key.setVersion(version);
+ } else {
+ key.setVersion(apexProperties.getProperty("DEFAULT_CONCEPT_VERSION"));
+ }
+
+ if (apexModel.getPolicyModel().getSchemas().getSchemasMap().containsKey(key)) {
+ return new ApexApiResult(ApexApiResult.Result.CONCEPT_EXISTS, CONCEPT + key.getId() + ALREADY_EXISTS);
+ }
+
+ apexModel.getPolicyModel().getSchemas().getSchemasMap().put(key,
+ new AxContextSchema(key, schemaFlavour, schemaDefinition));
+
+ if (apexModel.getPolicyModel().getKeyInformation().getKeyInfoMap().containsKey(key)) {
+ return keyInformationFacade.updateKeyInformation(name, version, uuid, description);
+ } else {
+ return keyInformationFacade.createKeyInformation(name, version, uuid, description);
+ }
+ } catch (final Exception e) {
+ return new ApexApiResult(ApexApiResult.Result.FAILED, e);
+ }
+ }
+
+ /**
+ * Update a context schema.
+ *
+ * @param name name of the context schema
+ * @param version version of the context schema, set to null to update the latest version
+ * @param schemaFlavour a string identifying the flavour of this context schema
+ * @param schemaDefinition a string containing the definition of this context schema
+ * @param uuid context schema UUID, set to null to not update
+ * @param description context schema description, set to null to not update
+ * @return result of the operation
+ */
+ public ApexApiResult updateContextSchema(final String name, final String version, final String schemaFlavour,
+ final String schemaDefinition, final String uuid, final String description) {
+ try {
+ final AxContextSchema schema = apexModel.getPolicyModel().getSchemas().get(name, version);
+ if (schema == null) {
+ return new ApexApiResult(ApexApiResult.Result.CONCEPT_DOES_NOT_EXIST,
+ CONCEPT + name + ':' + version + DOES_NOT_EXIST);
+ }
+
+ if (schemaFlavour != null) {
+ schema.setSchemaFlavour(schemaFlavour);
+ }
+
+ if (schemaDefinition != null) {
+ schema.setSchema(schemaDefinition);
+ }
+
+ return keyInformationFacade.updateKeyInformation(name, version, uuid, description);
+ } catch (final Exception e) {
+ return new ApexApiResult(ApexApiResult.Result.FAILED, e);
+ }
+ }
+
+ /**
+ * List context schemas.
+ *
+ * @param name name of the context schema, set to null to list all
+ * @param version starting version of the context schema, set to null to list all versions
+ * @return result of the operation
+ */
+ public ApexApiResult listContextSchemas(final String name, final String version) {
+ try {
+ final Set<AxContextSchema> schemaSet = apexModel.getPolicyModel().getSchemas().getAll(name, version);
+ if (name != null && schemaSet.isEmpty()) {
+ return new ApexApiResult(ApexApiResult.Result.CONCEPT_DOES_NOT_EXIST,
+ CONCEPT_S + name + ':' + version + DO_ES_NOT_EXIST);
+ }
+
+ final ApexApiResult result = new ApexApiResult();
+ for (final AxContextSchema schema : schemaSet) {
+ result.addMessage(
+ new ApexModelStringWriter<AxContextSchema>(false).writeString(schema, AxContextSchema.class));
+ }
+ return result;
+ } catch (final Exception e) {
+ return new ApexApiResult(ApexApiResult.Result.FAILED, e);
+ }
+ }
+
+ /**
+ * Delete a context schema.
+ *
+ * @param name name of the context schema
+ * @param version version of the context schema, set to null to delete all versions
+ * @return result of the operation
+ */
+ public ApexApiResult deleteContextSchema(final String name, final String version) {
+ try {
+ if (version != null) {
+ final AxArtifactKey key = new AxArtifactKey(name, version);
+ final AxContextSchema removedSchema =
+ apexModel.getPolicyModel().getSchemas().getSchemasMap().remove(key);
+ if (removedSchema != null) {
+ return new ApexApiResult(ApexApiResult.Result.SUCCESS,
+ new ApexModelStringWriter<AxContextSchema>(false).writeString(removedSchema,
+ AxContextSchema.class));
+ } else {
+ return new ApexApiResult(ApexApiResult.Result.CONCEPT_DOES_NOT_EXIST,
+ CONCEPT + key.getId() + DOES_NOT_EXIST);
+ }
+ }
+
+ final Set<AxContextSchema> schemaSet = apexModel.getPolicyModel().getSchemas().getAll(name, version);
+ if (schemaSet.isEmpty()) {
+ return new ApexApiResult(ApexApiResult.Result.CONCEPT_DOES_NOT_EXIST,
+ CONCEPT_S + name + ':' + version + DO_ES_NOT_EXIST);
+ }
+
+ final ApexApiResult result = new ApexApiResult();
+ for (final AxContextSchema schema : schemaSet) {
+ result.addMessage(
+ new ApexModelStringWriter<AxContextSchema>(false).writeString(schema, AxContextSchema.class));
+ apexModel.getPolicyModel().getSchemas().getSchemasMap().remove(schema.getKey());
+ keyInformationFacade.deleteKeyInformation(name, version);
+ }
+ return result;
+ } catch (final Exception e) {
+ return new ApexApiResult(ApexApiResult.Result.FAILED, e);
+ }
+ }
+
+ /**
+ * Validate context schemas.
+ *
+ * @param name name of the context schema, set to null to list all
+ * @param version starting version of the context schema, set to null to list all versions
+ * @return result of the operation
+ */
+ public ApexApiResult validateContextSchemas(final String name, final String version) {
+ try {
+ final Set<AxContextSchema> schemaSet = apexModel.getPolicyModel().getSchemas().getAll(name, version);
+ if (schemaSet.isEmpty()) {
+ return new ApexApiResult(ApexApiResult.Result.CONCEPT_DOES_NOT_EXIST,
+ CONCEPT_S + name + ':' + version + DO_ES_NOT_EXIST);
+ }
+
+ final ApexApiResult result = new ApexApiResult();
+ for (final AxContextSchema schema : schemaSet) {
+ final AxValidationResult validationResult = schema.validate(new AxValidationResult());
+ result.addMessage(
+ new ApexModelStringWriter<AxArtifactKey>(false).writeString(schema.getKey(), AxArtifactKey.class));
+ result.addMessage(validationResult.toString());
+ }
+ return result;
+ } catch (final Exception e) {
+ return new ApexApiResult(ApexApiResult.Result.FAILED, e);
+ }
+ }
+}
diff --git a/model/src/main/java/org/onap/policy/apex/model/modelapi/impl/CreatePolicyStateTaskRef.java b/model/src/main/java/org/onap/policy/apex/model/modelapi/impl/CreatePolicyStateTaskRef.java
new file mode 100644
index 000000000..a6e4c334e
--- /dev/null
+++ b/model/src/main/java/org/onap/policy/apex/model/modelapi/impl/CreatePolicyStateTaskRef.java
@@ -0,0 +1,38 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * Copyright (C) 2019 Samsung Electronics Co., Ltd. All rights reserved.
+ * Modifications Copyright (C) 2021 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.apex.model.modelapi.impl;
+
+import lombok.Builder;
+import lombok.Getter;
+
+@Getter
+@Builder
+public class CreatePolicyStateTaskRef {
+ private String name;
+ private String version;
+ private String stateName;
+ private String taskLocalName;
+ private String taskName;
+ private String taskVersion;
+ private String outputType;
+ private String outputName;
+}
diff --git a/model/src/main/java/org/onap/policy/apex/model/modelapi/impl/EventFacade.java b/model/src/main/java/org/onap/policy/apex/model/modelapi/impl/EventFacade.java
new file mode 100644
index 000000000..336f27b5d
--- /dev/null
+++ b/model/src/main/java/org/onap/policy/apex/model/modelapi/impl/EventFacade.java
@@ -0,0 +1,386 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * Copyright (C) 2016-2018 Ericsson. All rights reserved.
+ * Modifications Copyright (C) 2019,2022 Nordix Foundation.
+ * Modifications Copyright (C) 2022 Bell Canada.
+ * ================================================================================
+ * 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.apex.model.modelapi.impl;
+
+import java.util.Properties;
+import java.util.Set;
+import org.onap.policy.apex.model.basicmodel.concepts.AxArtifactKey;
+import org.onap.policy.apex.model.basicmodel.concepts.AxReferenceKey;
+import org.onap.policy.apex.model.basicmodel.concepts.AxValidationResult;
+import org.onap.policy.apex.model.basicmodel.handling.ApexModelStringWriter;
+import org.onap.policy.apex.model.contextmodel.concepts.AxContextSchema;
+import org.onap.policy.apex.model.eventmodel.concepts.AxEvent;
+import org.onap.policy.apex.model.eventmodel.concepts.AxField;
+import org.onap.policy.apex.model.modelapi.ApexApiResult;
+import org.onap.policy.apex.model.modelapi.ApexModel;
+import org.onap.policy.common.utils.validation.Assertions;
+
+/**
+ * This class acts as a facade for operations towards a policy model for event operations
+ * operations.
+ *
+ * @author Liam Fallon (liam.fallon@ericsson.com)
+ */
+public class EventFacade {
+ private static final String CONCEPT = "concept ";
+ private static final String CONCEPT_S = "concept(s) ";
+ private static final String DOES_NOT_EXIST = " does not exist";
+ private static final String DO_ES_NOT_EXIST = " do(es) not exist";
+ private static final String ALREADY_EXISTS = " already exists";
+
+ // Apex model we're working towards
+ private final ApexModel apexModel;
+
+ // Properties to use for the model
+ private final Properties apexProperties;
+
+ // Facade classes for working towards the real Apex model
+ private final KeyInformationFacade keyInformationFacade;
+
+ /**
+ * Constructor to create an event facade for the Model API.
+ *
+ * @param apexModel the apex model
+ * @param apexProperties Properties for the model
+ */
+ public EventFacade(final ApexModel apexModel, final Properties apexProperties) {
+ this.apexModel = apexModel;
+ this.apexProperties = apexProperties;
+
+ keyInformationFacade = new KeyInformationFacade(apexModel, apexProperties);
+ }
+
+ /**
+ * Create an event.
+ *
+ * @param name name of the event
+ * @param version version of the event, set to null to use the default version
+ * @param nameSpace of the event, set to null to use the default value
+ * @param source of the event, set to null to use the default value
+ * @param target of the event, set to null to use the default value
+ * @param uuid event UUID, set to null to generate a UUID
+ * @param description event description, set to null to generate a description
+ * @param toscaPolicyState specifies TOSCA policy processing status
+ * @return result of the operation
+ */
+ public ApexApiResult createEvent(final String name, final String version, final String nameSpace,
+ final String source, final String target, final String uuid, final String description,
+ final String toscaPolicyState) {
+ try {
+ final AxArtifactKey key = new AxArtifactKey();
+ key.setName(name);
+ if (version != null) {
+ key.setVersion(version);
+ } else {
+ key.setVersion(apexProperties.getProperty("DEFAULT_CONCEPT_VERSION"));
+ }
+
+ if (apexModel.getPolicyModel().getEvents().getEventMap().containsKey(key)) {
+ return new ApexApiResult(ApexApiResult.Result.CONCEPT_EXISTS, CONCEPT + key.getId() + ALREADY_EXISTS);
+ }
+
+ final AxEvent event = new AxEvent(key);
+
+ event.setNameSpace((nameSpace != null ? nameSpace : apexProperties.getProperty("DEFAULT_EVENT_NAMESPACE")));
+ event.setSource((source != null ? source : apexProperties.getProperty("DEFAULT_EVENT_SOURCE")));
+ event.setTarget((target != null ? target : apexProperties.getProperty("DEFAULT_EVENT_TARGET")));
+ if (toscaPolicyState != null) {
+ event.setToscaPolicyState(toscaPolicyState);
+ }
+
+ apexModel.getPolicyModel().getEvents().getEventMap().put(key, event);
+
+ if (apexModel.getPolicyModel().getKeyInformation().getKeyInfoMap().containsKey(key)) {
+ return keyInformationFacade.updateKeyInformation(name, version, uuid, description);
+ } else {
+ return keyInformationFacade.createKeyInformation(name, version, uuid, description);
+ }
+ } catch (final Exception e) {
+ return new ApexApiResult(ApexApiResult.Result.FAILED, e);
+ }
+ }
+
+ /**
+ * Update an event.
+ *
+ * @param name name of the event
+ * @param version version of the event, set to null to use the latest version
+ * @param nameSpace of the event, set to null to not update
+ * @param source of the event, set to null to not update
+ * @param target of the event, set to null to not update
+ * @param uuid event UUID, set to null to not update
+ * @param description event description, set to null to not update
+ * @param toscaPolicyState specifies TOSCA policy processing status
+ * @return result of the operation
+ */
+ public ApexApiResult updateEvent(final String name, final String version, final String nameSpace,
+ final String source, final String target, final String uuid, final String description,
+ final String toscaPolicyState) {
+ try {
+ final AxEvent event = apexModel.getPolicyModel().getEvents().get(name, version);
+ if (event == null) {
+ return new ApexApiResult(ApexApiResult.Result.CONCEPT_DOES_NOT_EXIST,
+ CONCEPT + name + ':' + version + DOES_NOT_EXIST);
+ }
+
+ if (nameSpace != null) {
+ event.setNameSpace(nameSpace);
+ }
+ if (source != null) {
+ event.setSource(source);
+ }
+ if (target != null) {
+ event.setTarget(target);
+ }
+ if (toscaPolicyState != null) {
+ event.setToscaPolicyState(toscaPolicyState);
+ }
+
+ return keyInformationFacade.updateKeyInformation(name, version, uuid, description);
+ } catch (final Exception e) {
+ return new ApexApiResult(ApexApiResult.Result.FAILED, e);
+ }
+ }
+
+ /**
+ * List events.
+ *
+ * @param name name of the event, set to null to list all
+ * @param version starting version of the event, set to null to list all versions
+ * @return result of the operation
+ */
+ public ApexApiResult listEvent(final String name, final String version) {
+ try {
+ final Set<AxEvent> eventSet = apexModel.getPolicyModel().getEvents().getAll(name, version);
+ if (name != null && eventSet.isEmpty()) {
+ return new ApexApiResult(ApexApiResult.Result.CONCEPT_DOES_NOT_EXIST,
+ CONCEPT_S + name + ':' + version + DO_ES_NOT_EXIST);
+ }
+
+ final ApexApiResult result = new ApexApiResult();
+ for (final AxEvent event : eventSet) {
+ result.addMessage(new ApexModelStringWriter<AxEvent>(false).writeString(event, AxEvent.class));
+ }
+ return result;
+ } catch (final Exception e) {
+ return new ApexApiResult(ApexApiResult.Result.FAILED, e);
+ }
+ }
+
+ /**
+ * Delete an event.
+ *
+ * @param name name of the event
+ * @param version version of the event, set to null to delete all versions
+ * @return result of the operation
+ */
+ public ApexApiResult deleteEvent(final String name, final String version) {
+ try {
+ if (version != null) {
+ final AxArtifactKey key = new AxArtifactKey(name, version);
+ final AxEvent removedEvent = apexModel.getPolicyModel().getEvents().getEventMap().remove(key);
+ if (removedEvent != null) {
+ return new ApexApiResult(ApexApiResult.Result.SUCCESS,
+ new ApexModelStringWriter<AxEvent>(false).writeString(removedEvent, AxEvent.class));
+ } else {
+ return new ApexApiResult(ApexApiResult.Result.CONCEPT_DOES_NOT_EXIST,
+ CONCEPT + key.getId() + DOES_NOT_EXIST);
+ }
+ }
+
+ final Set<AxEvent> eventSet = apexModel.getPolicyModel().getEvents().getAll(name, version);
+ if (eventSet.isEmpty()) {
+ return new ApexApiResult(ApexApiResult.Result.CONCEPT_DOES_NOT_EXIST,
+ CONCEPT_S + name + ':' + version + DO_ES_NOT_EXIST);
+ }
+
+ final ApexApiResult result = new ApexApiResult();
+ for (final AxEvent event : eventSet) {
+ result.addMessage(new ApexModelStringWriter<AxEvent>(false).writeString(event, AxEvent.class));
+ apexModel.getPolicyModel().getEvents().getEventMap().remove(event.getKey());
+ keyInformationFacade.deleteKeyInformation(name, version);
+ }
+ return result;
+ } catch (final Exception e) {
+ return new ApexApiResult(ApexApiResult.Result.FAILED, e);
+ }
+ }
+
+ /**
+ * Validate events.
+ *
+ * @param name name of the event, set to null to list all
+ * @param version starting version of the event, set to null to list all versions
+ * @return result of the operation
+ */
+ public ApexApiResult validateEvent(final String name, final String version) {
+ try {
+ final Set<AxEvent> eventSet = apexModel.getPolicyModel().getEvents().getAll(name, version);
+ if (eventSet.isEmpty()) {
+ return new ApexApiResult(ApexApiResult.Result.CONCEPT_DOES_NOT_EXIST,
+ CONCEPT_S + name + ':' + version + DO_ES_NOT_EXIST);
+ }
+
+ final ApexApiResult result = new ApexApiResult();
+ for (final AxEvent event : eventSet) {
+ final AxValidationResult validationResult = event.validate(new AxValidationResult());
+ result.addMessage(
+ new ApexModelStringWriter<AxArtifactKey>(false).writeString(event.getKey(), AxArtifactKey.class));
+ result.addMessage(validationResult.toString());
+ }
+ return result;
+ } catch (final Exception e) {
+ return new ApexApiResult(ApexApiResult.Result.FAILED, e);
+ }
+ }
+
+ /**
+ * Create an event parameter.
+ *
+ * @param name name of the event
+ * @param version version of the event, set to null to use the latest version
+ * @param parName of the parameter
+ * @param contextSchemaName name of the parameter context schema
+ * @param contextSchemaVersion version of the parameter context schema, set to null to use the
+ * latest version
+ * @param optional true if the event parameter is optional, false otherwise
+ * @return result of the operation
+ */
+ public ApexApiResult createEventPar(final String name, final String version, final String parName,
+ final String contextSchemaName, final String contextSchemaVersion, final boolean optional) {
+ try {
+ Assertions.argumentNotNull(parName, "parName may not be null");
+
+ final AxEvent event = apexModel.getPolicyModel().getEvents().get(name, version);
+ if (event == null) {
+ return new ApexApiResult(ApexApiResult.Result.CONCEPT_DOES_NOT_EXIST,
+ CONCEPT + name + ':' + version + DOES_NOT_EXIST);
+ }
+
+ final AxReferenceKey refKey = new AxReferenceKey(event.getKey(), parName);
+
+ if (event.getParameterMap().containsKey(refKey.getLocalName())) {
+ return new ApexApiResult(ApexApiResult.Result.CONCEPT_EXISTS,
+ CONCEPT + refKey.getId() + ALREADY_EXISTS);
+ }
+
+ final AxContextSchema schema =
+ apexModel.getPolicyModel().getSchemas().get(contextSchemaName, contextSchemaVersion);
+ if (schema == null) {
+ return new ApexApiResult(ApexApiResult.Result.CONCEPT_DOES_NOT_EXIST,
+ CONCEPT + contextSchemaName + ':' + contextSchemaVersion + DOES_NOT_EXIST);
+ }
+
+ event.getParameterMap().put(refKey.getLocalName(), new AxField(refKey, schema.getKey(), optional));
+ return new ApexApiResult();
+ } catch (final Exception e) {
+ return new ApexApiResult(ApexApiResult.Result.FAILED, e);
+ }
+ }
+
+ /**
+ * List event parameters.
+ *
+ * @param name name of the event
+ * @param version version of the event, set to null to list latest version
+ * @param parName name of the parameter, set to null to list all parameters of the event
+ * @return result of the operation
+ */
+ public ApexApiResult listEventPar(final String name, final String version, final String parName) {
+ try {
+ final AxEvent event = apexModel.getPolicyModel().getEvents().get(name, version);
+ if (event == null) {
+ return new ApexApiResult(ApexApiResult.Result.CONCEPT_DOES_NOT_EXIST,
+ CONCEPT + name + ':' + version + DOES_NOT_EXIST);
+ }
+
+ if (parName != null) {
+ final AxField eventField = event.getParameterMap().get(parName);
+ if (eventField != null) {
+ return new ApexApiResult(ApexApiResult.Result.SUCCESS,
+ new ApexModelStringWriter<AxField>(false).writeString(eventField, AxField.class));
+ } else {
+ return new ApexApiResult(ApexApiResult.Result.CONCEPT_DOES_NOT_EXIST,
+ CONCEPT + name + ':' + version + ':' + parName + DOES_NOT_EXIST);
+ }
+ } else {
+ if (event.getParameterMap().size() == 0) {
+ return new ApexApiResult(ApexApiResult.Result.CONCEPT_DOES_NOT_EXIST,
+ "no parameters defined on event " + event.getKey().getId());
+ }
+
+ final ApexApiResult result = new ApexApiResult();
+ for (final AxField eventPar : event.getParameterMap().values()) {
+ result.addMessage(new ApexModelStringWriter<AxField>(false).writeString(eventPar, AxField.class));
+ }
+ return result;
+ }
+ } catch (final Exception e) {
+ return new ApexApiResult(ApexApiResult.Result.FAILED, e);
+ }
+ }
+
+ /**
+ * Delete an event parameter.
+ *
+ * @param name name of the event
+ * @param version version of the event, set to null to use the latest version
+ * @param parName of the parameter, set to null to delete all parameters
+ * @return result of the operation
+ */
+ public ApexApiResult deleteEventPar(final String name, final String version, final String parName) {
+ try {
+ final AxEvent event = apexModel.getPolicyModel().getEvents().get(name, version);
+ if (event == null) {
+ return new ApexApiResult(ApexApiResult.Result.CONCEPT_DOES_NOT_EXIST,
+ CONCEPT + name + ':' + version + DOES_NOT_EXIST);
+ }
+
+ final ApexApiResult result = new ApexApiResult();
+ if (parName != null) {
+ if (event.getParameterMap().containsKey(parName)) {
+ result.addMessage(new ApexModelStringWriter<AxField>(false)
+ .writeString(event.getParameterMap().get(parName), AxField.class));
+ event.getParameterMap().remove(parName);
+ return result;
+ } else {
+ return new ApexApiResult(ApexApiResult.Result.CONCEPT_DOES_NOT_EXIST,
+ CONCEPT + name + ':' + version + ':' + parName + DOES_NOT_EXIST);
+ }
+ } else {
+ if (event.getParameterMap().size() == 0) {
+ return new ApexApiResult(ApexApiResult.Result.CONCEPT_DOES_NOT_EXIST,
+ "no parameters defined on event " + event.getKey().getId());
+ }
+
+ for (final AxField eventPar : event.getParameterMap().values()) {
+ result.addMessage(new ApexModelStringWriter<AxField>(false).writeString(eventPar, AxField.class));
+ }
+ event.getParameterMap().clear();
+ return result;
+ }
+ } catch (final Exception e) {
+ return new ApexApiResult(ApexApiResult.Result.FAILED, e);
+ }
+ }
+}
diff --git a/model/src/main/java/org/onap/policy/apex/model/modelapi/impl/KeyInformationFacade.java b/model/src/main/java/org/onap/policy/apex/model/modelapi/impl/KeyInformationFacade.java
new file mode 100644
index 000000000..6875a4c20
--- /dev/null
+++ b/model/src/main/java/org/onap/policy/apex/model/modelapi/impl/KeyInformationFacade.java
@@ -0,0 +1,228 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * Copyright (C) 2016-2018 Ericsson. All rights reserved.
+ * Modifications Copyright (C) 2021 AT&T Intellectual Property. All rights reserved.
+ * Modifications Copyright (C) 2022 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.apex.model.modelapi.impl;
+
+import java.util.Properties;
+import java.util.Set;
+import java.util.UUID;
+import lombok.AllArgsConstructor;
+import org.onap.policy.apex.model.basicmodel.concepts.AxArtifactKey;
+import org.onap.policy.apex.model.basicmodel.concepts.AxKeyInfo;
+import org.onap.policy.apex.model.basicmodel.concepts.AxValidationResult;
+import org.onap.policy.apex.model.basicmodel.handling.ApexModelStringWriter;
+import org.onap.policy.apex.model.modelapi.ApexApiResult;
+import org.onap.policy.apex.model.modelapi.ApexModel;
+
+/**
+ * This class acts as a facade for operations towards a policy model for key information operations.
+ *
+ * @author Liam Fallon (liam.fallon@ericsson.com)
+ */
+@AllArgsConstructor
+public class KeyInformationFacade {
+ private static final String CONCEPT = "concept ";
+ private static final String CONCEPT_S = "concept(s) ";
+ private static final String DOES_NOT_EXIST = " does not exist";
+ private static final String DO_ES_NOT_EXIST = " do(es) not exist";
+ private static final String ALREADY_EXISTS = " already exists";
+
+ // Apex model we're working towards
+ private final ApexModel apexModel;
+
+ // Properties to use for the model
+ private final Properties apexProperties;
+
+ /**
+ * Create key information.
+ *
+ * @param name name of the concept for the key information
+ * @param version version of the concept for the key information, set to null to use the default
+ * version
+ * @param uuid key information UUID, set to null to generate a UUID
+ * @param description key information description, set to null to generate a description
+ * @return result of the operation
+ */
+ public ApexApiResult createKeyInformation(final String name, final String version, final String uuid,
+ final String description) {
+ try {
+ final AxArtifactKey key = new AxArtifactKey();
+ key.setName(name);
+ if (version != null) {
+ key.setVersion(version);
+ } else {
+ key.setVersion(apexProperties.getProperty("DEFAULT_CONCEPT_VERSION"));
+ }
+
+ if (apexModel.getPolicyModel().getKeyInformation().getKeyInfoMap().containsKey(key)) {
+ return new ApexApiResult(ApexApiResult.Result.CONCEPT_EXISTS, CONCEPT + key.getId() + ALREADY_EXISTS);
+ }
+
+ final AxKeyInfo keyInfo = new AxKeyInfo(key);
+ if (description != null) {
+ keyInfo.setDescription(description);
+ }
+ if (uuid != null) {
+ keyInfo.setUuid(UUID.fromString(uuid));
+ } else {
+ // generate a reproducible UUID
+ keyInfo.setUuid(AxKeyInfo.generateReproducibleUuid(keyInfo.getId() + keyInfo.getDescription()));
+ }
+ apexModel.getPolicyModel().getKeyInformation().getKeyInfoMap().put(key, keyInfo);
+ return new ApexApiResult();
+ } catch (final Exception e) {
+ return new ApexApiResult(ApexApiResult.Result.FAILED, e);
+ }
+ }
+
+ /**
+ * Update key information.
+ *
+ * @param name name of the concept for the key information
+ * @param version version of the concept for the key information, set to null to update the
+ * latest version
+ * @param uuid key information UUID, set to null to not update
+ * @param description key information description, set to null to not update
+ * @return result of the operation
+ */
+ public ApexApiResult updateKeyInformation(final String name, final String version, final String uuid,
+ final String description) {
+ try {
+ final AxKeyInfo keyInfo = apexModel.getPolicyModel().getKeyInformation().get(name, version);
+ if (keyInfo == null) {
+ return new ApexApiResult(ApexApiResult.Result.CONCEPT_DOES_NOT_EXIST,
+ CONCEPT + name + ":" + version + DOES_NOT_EXIST);
+ }
+
+ if (description != null) {
+ keyInfo.setDescription(description);
+ }
+
+ if (uuid != null) {
+ keyInfo.setUuid(UUID.fromString(uuid));
+ } else {
+ // generate a reproducible UUID
+ keyInfo.setUuid(AxKeyInfo.generateReproducibleUuid(keyInfo.getId() + keyInfo.getDescription()));
+ }
+
+ return new ApexApiResult();
+ } catch (final Exception e) {
+ return new ApexApiResult(ApexApiResult.Result.FAILED, e);
+ }
+ }
+
+ /**
+ * List key information.
+ *
+ * @param name name of the concept for the key information, set to null to list all
+ * @param version starting version of the concept for the key information, set to null to list
+ * all versions
+ * @return result of the operation
+ */
+ public ApexApiResult listKeyInformation(final String name, final String version) {
+ try {
+ final Set<AxKeyInfo> keyInfoSet = apexModel.getPolicyModel().getKeyInformation().getAll(name, version);
+ if (name != null && keyInfoSet.isEmpty()) {
+ return new ApexApiResult(ApexApiResult.Result.CONCEPT_DOES_NOT_EXIST,
+ CONCEPT_S + name + ':' + version + DO_ES_NOT_EXIST);
+ }
+
+ final ApexApiResult result = new ApexApiResult();
+ for (final AxKeyInfo keyInfo : keyInfoSet) {
+ result.addMessage(new ApexModelStringWriter<AxKeyInfo>(false).writeString(keyInfo, AxKeyInfo.class));
+ }
+ return result;
+ } catch (final Exception e) {
+ return new ApexApiResult(ApexApiResult.Result.FAILED, e);
+ }
+ }
+
+ /**
+ * Delete key information.
+ *
+ * @param name name of the concept for the key information
+ * @param version version of the concept for the key information, set to null to delete all
+ * versions
+ * @return result of the operation
+ */
+ public ApexApiResult deleteKeyInformation(final String name, final String version) {
+ try {
+ if (version != null) {
+ final AxArtifactKey key = new AxArtifactKey(name, version);
+ final AxKeyInfo removedKeyInfo =
+ apexModel.getPolicyModel().getKeyInformation().getKeyInfoMap().remove(key);
+ if (removedKeyInfo != null) {
+ return new ApexApiResult(ApexApiResult.Result.SUCCESS,
+ new ApexModelStringWriter<AxKeyInfo>(false).writeString(removedKeyInfo, AxKeyInfo.class));
+ } else {
+ return new ApexApiResult(ApexApiResult.Result.CONCEPT_DOES_NOT_EXIST,
+ CONCEPT + key.getId() + DOES_NOT_EXIST);
+ }
+ }
+
+ final Set<AxKeyInfo> keyInfoSet = apexModel.getPolicyModel().getKeyInformation().getAll(name, version);
+ if (keyInfoSet.isEmpty()) {
+ return new ApexApiResult(ApexApiResult.Result.CONCEPT_DOES_NOT_EXIST,
+ CONCEPT_S + name + ':' + version + DO_ES_NOT_EXIST);
+ }
+
+ final ApexApiResult result = new ApexApiResult();
+ for (final AxKeyInfo keyInfo : keyInfoSet) {
+ result.addMessage(new ApexModelStringWriter<AxKeyInfo>(false).writeString(keyInfo, AxKeyInfo.class));
+ apexModel.getPolicyModel().getKeyInformation().getKeyInfoMap().remove(keyInfo.getKey());
+ }
+ return result;
+ } catch (final Exception e) {
+ return new ApexApiResult(ApexApiResult.Result.FAILED, e);
+ }
+ }
+
+ /**
+ * Validate key information.
+ *
+ * @param name name of the concept for the key information
+ * @param version version of the concept for the key information, set to null to validate all
+ * versions
+ * @return result of the operation
+ */
+ public ApexApiResult validateKeyInformation(final String name, final String version) {
+ try {
+ final Set<AxKeyInfo> keyInfoSet = apexModel.getPolicyModel().getKeyInformation().getAll(name, version);
+ if (keyInfoSet.isEmpty()) {
+ return new ApexApiResult(ApexApiResult.Result.CONCEPT_DOES_NOT_EXIST,
+ CONCEPT_S + name + ':' + version + DO_ES_NOT_EXIST);
+ }
+
+ final ApexApiResult result = new ApexApiResult();
+ for (final AxKeyInfo keyInfo : keyInfoSet) {
+ final AxValidationResult validationResult = keyInfo.validate(new AxValidationResult());
+ result.addMessage(
+ new ApexModelStringWriter<AxArtifactKey>(false).writeString(keyInfo.getKey(), AxArtifactKey.class));
+ result.addMessage(validationResult.toString());
+ }
+ return result;
+ } catch (final Exception e) {
+ return new ApexApiResult(ApexApiResult.Result.FAILED, e);
+ }
+ }
+
+}
diff --git a/model/src/main/java/org/onap/policy/apex/model/modelapi/impl/ModelFacade.java b/model/src/main/java/org/onap/policy/apex/model/modelapi/impl/ModelFacade.java
new file mode 100644
index 000000000..1030a5bf5
--- /dev/null
+++ b/model/src/main/java/org/onap/policy/apex/model/modelapi/impl/ModelFacade.java
@@ -0,0 +1,202 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * Copyright (C) 2016-2018 Ericsson. All rights reserved.
+ * Modifications Copyright (C) 2019,2022 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.apex.model.modelapi.impl;
+
+import java.util.Properties;
+import org.onap.policy.apex.model.basicmodel.concepts.AxArtifactKey;
+import org.onap.policy.apex.model.basicmodel.handling.ApexModelStringWriter;
+import org.onap.policy.apex.model.modelapi.ApexApiResult;
+import org.onap.policy.apex.model.modelapi.ApexModel;
+import org.onap.policy.apex.model.policymodel.concepts.AxPolicyModel;
+import org.onap.policy.common.utils.validation.Assertions;
+
+/**
+ * This class acts as a facade for operations towards a policy model.
+ *
+ * @author Liam Fallon (liam.fallon@ericsson.com)
+ */
+public class ModelFacade {
+ private static final String CONCEPT = "concept ";
+ private static final String DOES_NOT_EXIST = " does not exist";
+ private static final String ALREADY_CREATED = " already created";
+ private static final String NO_VERSION_SPECIFIED = ", no version specified";
+
+ // Apex model we're working towards
+ private final ApexModel apexModel;
+
+ // Properties to use for the model
+ private final Properties apexProperties;
+
+ // Facade classes for working towards the real Apex model
+ private final KeyInformationFacade keyInformationFacade;
+
+ /**
+ * Constructor to create a model facade for the Apex model.
+ *
+ * @param apexModel the apex model
+ * @param apexProperties Properties for the model
+ */
+ public ModelFacade(final ApexModel apexModel, final Properties apexProperties) {
+ Assertions.argumentNotNull(apexModel, "apexModel may not be null");
+ Assertions.argumentNotNull(apexProperties, "apexProperties may not be null");
+
+ this.apexModel = apexModel;
+ this.apexProperties = apexProperties;
+
+ keyInformationFacade = new KeyInformationFacade(apexModel, apexProperties);
+ }
+
+ /**
+ * Create model.
+ *
+ * @param name name of the model
+ * @param version version of the model, set to null to use the default version
+ * @param uuid model UUID, set to null to generate a UUID
+ * @param description model description, set to null to generate a description
+ * @return result of the operation
+ */
+ public ApexApiResult createModel(final String name, final String version, final String uuid,
+ final String description) {
+ try {
+ final AxArtifactKey key = new AxArtifactKey();
+ key.setName(name);
+ if (version != null) {
+ key.setVersion(version);
+ } else {
+ final String defaultVersion = apexProperties.getProperty("DEFAULT_CONCEPT_VERSION");
+ if (defaultVersion != null) {
+ key.setVersion(defaultVersion);
+ } else {
+ return new ApexApiResult(ApexApiResult.Result.FAILED, CONCEPT + name + NO_VERSION_SPECIFIED);
+ }
+ }
+
+ if (!apexModel.getPolicyModel().getKey().equals(AxArtifactKey.getNullKey())) {
+ return new ApexApiResult(ApexApiResult.Result.CONCEPT_EXISTS,
+ CONCEPT + apexModel.getPolicyModel().getKey().getId() + ALREADY_CREATED);
+ }
+
+ apexModel.setPolicyModel(new AxPolicyModel(key));
+
+ ApexApiResult result;
+
+ result = keyInformationFacade.createKeyInformation(name, version, uuid, description);
+ if (result.getResult().equals(ApexApiResult.Result.SUCCESS)) {
+ apexModel.getPolicyModel().buildReferences();
+ apexModel.getPolicyModel().getKeyInformation().generateKeyInfo(apexModel.getPolicyModel());
+ }
+ return result;
+ } catch (final Exception e) {
+ return new ApexApiResult(ApexApiResult.Result.FAILED, e);
+ }
+ }
+
+ /**
+ * Update model.
+ *
+ * @param name name of the model
+ * @param version version of the model, set to null to update the latest version
+ * @param uuid key information UUID, set to null to not update
+ * @param description policy description, set to null to not update
+ * @return result of the operation
+ */
+ public ApexApiResult updateModel(final String name, final String version, final String uuid,
+ final String description) {
+ try {
+ final AxArtifactKey key = new AxArtifactKey();
+ key.setName(name);
+ if (version != null) {
+ key.setVersion(version);
+ } else {
+ final String defaultVersion = apexProperties.getProperty("DEFAULT_CONCEPT_VERSION");
+ if (defaultVersion != null) {
+ key.setVersion(defaultVersion);
+ } else {
+ return new ApexApiResult(ApexApiResult.Result.FAILED,
+ CONCEPT + apexModel.getPolicyModel().getKey().getId() + NO_VERSION_SPECIFIED);
+ }
+ }
+
+ if (apexModel.getPolicyModel().getKey().equals(AxArtifactKey.getNullKey())) {
+ return new ApexApiResult(ApexApiResult.Result.CONCEPT_DOES_NOT_EXIST,
+ CONCEPT + apexModel.getPolicyModel().getKey().getId() + DOES_NOT_EXIST);
+ }
+
+ return keyInformationFacade.updateKeyInformation(name, version, uuid, description);
+ } catch (final Exception e) {
+ return new ApexApiResult(ApexApiResult.Result.FAILED, e);
+ }
+ }
+
+ /**
+ * Get the key of an Apex model.
+ *
+ * @return the result of the operation
+ */
+ public ApexApiResult getModelKey() {
+ try {
+ final ApexApiResult result = new ApexApiResult();
+ final AxArtifactKey modelkey = apexModel.getPolicyModel().getKey();
+ result
+ .addMessage(new ApexModelStringWriter<AxArtifactKey>(false).writeString(modelkey, AxArtifactKey.class));
+ return result;
+ } catch (final Exception e) {
+ return new ApexApiResult(ApexApiResult.Result.FAILED, e);
+ }
+ }
+
+ /**
+ * List an Apex model.
+ *
+ * @return the result of the operation
+ */
+ public ApexApiResult listModel() {
+ try {
+ final ApexApiResult result = new ApexApiResult();
+ result.addMessage(new ApexModelStringWriter<AxPolicyModel>(false).writeString(apexModel.getPolicyModel(),
+ AxPolicyModel.class));
+ return result;
+ } catch (final Exception e) {
+ return new ApexApiResult(ApexApiResult.Result.FAILED, e);
+ }
+ }
+
+ /**
+ * Delete an Apex model, clear all the concepts in the model.
+ *
+ * @return the result of the operation
+ */
+ public ApexApiResult deleteModel() {
+ // @formatter:off
+ apexModel.getPolicyModel().getSchemas().getSchemasMap().clear();
+ apexModel.getPolicyModel().getEvents().getEventMap().clear();
+ apexModel.getPolicyModel().getAlbums().getAlbumsMap().clear();
+ apexModel.getPolicyModel().getTasks().getTaskMap().clear();
+ apexModel.getPolicyModel().getPolicies().getPolicyMap().clear();
+ apexModel.getPolicyModel().getKeyInformation().getKeyInfoMap().clear();
+ // @formatter:on
+
+ apexModel.setPolicyModel(new AxPolicyModel());
+
+ return new ApexApiResult();
+ }
+}
diff --git a/model/src/main/java/org/onap/policy/apex/model/modelapi/impl/ModelHandlerFacade.java b/model/src/main/java/org/onap/policy/apex/model/modelapi/impl/ModelHandlerFacade.java
new file mode 100644
index 000000000..fecf92e1f
--- /dev/null
+++ b/model/src/main/java/org/onap/policy/apex/model/modelapi/impl/ModelHandlerFacade.java
@@ -0,0 +1,498 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * Copyright (C) 2016-2018 Ericsson. All rights reserved.
+ * Modifications Copyright (C) 2019-2020,2022 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.apex.model.modelapi.impl;
+
+import java.io.ByteArrayInputStream;
+import java.io.File;
+import java.io.IOException;
+import java.io.InputStream;
+import java.net.MalformedURLException;
+import java.net.URL;
+import java.net.URLConnection;
+import java.nio.file.Files;
+import java.util.LinkedHashSet;
+import java.util.Properties;
+import java.util.Set;
+import org.onap.policy.apex.model.basicmodel.concepts.ApexException;
+import org.onap.policy.apex.model.basicmodel.concepts.AxArtifactKey;
+import org.onap.policy.apex.model.basicmodel.concepts.AxValidationResult;
+import org.onap.policy.apex.model.basicmodel.handling.ApexModelException;
+import org.onap.policy.apex.model.basicmodel.handling.ApexModelFileWriter;
+import org.onap.policy.apex.model.basicmodel.handling.ApexModelReader;
+import org.onap.policy.apex.model.basicmodel.handling.ApexModelStringWriter;
+import org.onap.policy.apex.model.basicmodel.handling.ApexModelWriter;
+import org.onap.policy.apex.model.modelapi.ApexApiResult;
+import org.onap.policy.apex.model.modelapi.ApexModel;
+import org.onap.policy.apex.model.policymodel.concepts.AxPolicy;
+import org.onap.policy.apex.model.policymodel.concepts.AxPolicyModel;
+import org.onap.policy.apex.model.policymodel.handling.PolicyAnalyser;
+import org.onap.policy.apex.model.policymodel.handling.PolicyAnalysisResult;
+import org.onap.policy.apex.model.policymodel.handling.PolicyModelComparer;
+import org.onap.policy.apex.model.policymodel.handling.PolicyModelMerger;
+import org.onap.policy.apex.model.policymodel.handling.PolicyModelSplitter;
+import org.onap.policy.common.utils.resources.ResourceUtils;
+import org.onap.policy.common.utils.resources.TextFileUtils;
+import org.onap.policy.common.utils.validation.Assertions;
+import org.slf4j.ext.XLogger;
+import org.slf4j.ext.XLoggerFactory;
+
+/**
+ * This class acts as a facade for model handling for the Apex Model API.
+ *
+ * @author Liam Fallon (liam.fallon@ericsson.com)
+ */
+public class ModelHandlerFacade {
+ private static final String FILE_NAME_MAY_NOT_BE_NULL = "fileName may not be null";
+ private static final String MODEL = "model ";
+ private static final String ALREADY_LOADED = " already loaded";
+
+ private static final XLogger LOGGER = XLoggerFactory.getXLogger(ModelHandlerFacade.class);
+
+ // Apex model we're working towards
+ private final ApexModel apexModel;
+
+ /**
+ * This Constructor creates a model handling facade for the given {@link ApexModel}.
+ *
+ * @param apexModel the apex model to manipulate
+ * @param apexProperties properties for the model
+ */
+ public ModelHandlerFacade(final ApexModel apexModel, final Properties apexProperties) {
+ Assertions.argumentNotNull(apexModel, "apexModel may not be null");
+ Assertions.argumentNotNull(apexProperties, "apexProperties may not be null");
+
+ this.apexModel = apexModel;
+ }
+
+ /**
+ * Load an Apex model from a string.
+ *
+ * @param modelString the string with the model
+ * @return the result of the operation
+ */
+ public ApexApiResult loadFromString(final String modelString) {
+ Assertions.argumentNotNull(modelString, "modelString may not be null");
+
+ if (!apexModel.getPolicyModel().getKey().equals(AxArtifactKey.getNullKey())) {
+ return new ApexApiResult(ApexApiResult.Result.CONCEPT_EXISTS,
+ MODEL + apexModel.getPolicyModel().getKey().getId() + ALREADY_LOADED);
+ }
+
+ ApexApiResult result = new ApexApiResult();
+ AxPolicyModel newPolicyModel = loadModelFromString(modelString, result);
+ apexModel.setPolicyModel(newPolicyModel != null ? newPolicyModel : new AxPolicyModel());
+
+ return result;
+ }
+
+ /**
+ * Load an Apex model from a file.
+ *
+ * @param fileName the file name of the file with the model
+ * @return the result of the operation
+ */
+ public ApexApiResult loadFromFile(final String fileName) {
+ Assertions.argumentNotNull(fileName, FILE_NAME_MAY_NOT_BE_NULL);
+
+ if (!apexModel.getPolicyModel().getKey().equals(AxArtifactKey.getNullKey())) {
+ return new ApexApiResult(ApexApiResult.Result.CONCEPT_EXISTS,
+ MODEL + apexModel.getPolicyModel().getKey().getId() + ALREADY_LOADED);
+ }
+
+ ApexApiResult result = new ApexApiResult();
+ AxPolicyModel newPolicyModel = loadModelFromFile(fileName, result);
+ apexModel.setPolicyModel(newPolicyModel != null ? newPolicyModel : new AxPolicyModel());
+
+ return result;
+ }
+
+ /**
+ * Save an Apex model to a file.
+ *
+ * @param fileName the file name
+ * @return the result of the operation
+ */
+ public ApexApiResult saveToFile(final String fileName) {
+ Assertions.argumentNotNull(fileName, FILE_NAME_MAY_NOT_BE_NULL);
+
+ ApexModelFileWriter<AxPolicyModel> apexModelFileWriter = new ApexModelFileWriter<>(false);
+
+ try {
+ apexModelFileWriter.apexModelWriteJsonFile(apexModel.getPolicyModel(), AxPolicyModel.class, fileName);
+ return new ApexApiResult();
+ } catch (ApexException e) {
+ return new ApexApiResult(ApexApiResult.Result.FAILED, e);
+ }
+ }
+
+ /**
+ * Read an APEX model from a location identified by a URL.
+ *
+ * @param urlString the url string
+ * @return the result of the operation
+ */
+ public ApexApiResult readFromUrl(final String urlString) {
+ Assertions.argumentNotNull(urlString, "urlString may not be null");
+
+ if (!apexModel.getPolicyModel().getKey().equals(AxArtifactKey.getNullKey())) {
+ return new ApexApiResult(ApexApiResult.Result.CONCEPT_EXISTS,
+ MODEL + apexModel.getPolicyModel().getKey().getId() + ALREADY_LOADED);
+ }
+
+ URL apexModelUrl;
+ try {
+ apexModelUrl = new URL(urlString);
+ } catch (MalformedURLException e) {
+ ApexApiResult result = new ApexApiResult(ApexApiResult.Result.FAILED);
+ result.addMessage("URL string " + urlString + " is not a valid URL");
+ result.addThrowable(e);
+ return result;
+ }
+
+ try {
+ ApexModelReader<AxPolicyModel> apexModelReader = new ApexModelReader<>(AxPolicyModel.class);
+ apexModelReader.setValidate(false);
+ AxPolicyModel newPolicyModel = apexModelReader.read(apexModelUrl.openStream());
+ apexModel.setPolicyModel(newPolicyModel != null ? newPolicyModel : new AxPolicyModel());
+ return new ApexApiResult();
+ } catch (ApexModelException | IOException e) {
+ apexModel.setPolicyModel(new AxPolicyModel());
+ return new ApexApiResult(ApexApiResult.Result.FAILED, e);
+ }
+ }
+
+ /**
+ * Write an APEX model to a location identified by a URL.
+ *
+ * @param urlString the URL to read the model from
+ * @return the result of the operation
+ */
+ public ApexApiResult writeToUrl(final String urlString) {
+ Assertions.argumentNotNull(urlString, "urlString may not be null");
+
+ URL apexModelUrl;
+ try {
+ apexModelUrl = new URL(urlString);
+ } catch (MalformedURLException e) {
+ ApexApiResult result = new ApexApiResult(ApexApiResult.Result.FAILED);
+ result.addMessage("URL string " + urlString + " is not a valid URL");
+ result.addThrowable(e);
+ return result;
+ }
+
+ try {
+ ApexModelWriter<AxPolicyModel> apexModelWriter = new ApexModelWriter<>(AxPolicyModel.class);
+ apexModelWriter.setValidate(false);
+
+ // Open the URL for output and write the model
+ URLConnection urlConnection = apexModelUrl.openConnection();
+ urlConnection.setDoOutput(true);
+
+ apexModelWriter.write(apexModel.getPolicyModel(), urlConnection.getOutputStream());
+ return new ApexApiResult();
+ } catch (ApexModelException | IOException e) {
+ return new ApexApiResult(ApexApiResult.Result.FAILED, e);
+ }
+ }
+
+ /**
+ * Analyse an Apex model that shows the concept usage references of a policy model.
+ *
+ * @return the result of the operation
+ */
+ public ApexApiResult analyse() {
+ PolicyAnalysisResult analysisResult = new PolicyAnalyser().analyse(apexModel.getPolicyModel());
+ return new ApexApiResult(ApexApiResult.Result.SUCCESS, analysisResult.toString());
+ }
+
+ /**
+ * Validate an Apex model, checking all concepts and references in the model.
+ *
+ * @return the result of the operation
+ */
+ public ApexApiResult validate() {
+ ApexApiResult result = new ApexApiResult();
+ try {
+ AxValidationResult validationResult = apexModel.getPolicyModel().validate(new AxValidationResult());
+
+ if (!validationResult.isValid()) {
+ result.setResult(ApexApiResult.Result.FAILED);
+ }
+ result.addMessage(new ApexModelStringWriter<AxArtifactKey>(false)
+ .writeString(apexModel.getPolicyModel().getKey(), AxArtifactKey.class));
+ result.addMessage(validationResult.toString());
+ return result;
+ } catch (Exception e) {
+ return new ApexApiResult(ApexApiResult.Result.FAILED, e);
+ }
+ }
+
+ /**
+ * Compare to Apex models, returning the differences between the models.
+ *
+ * @param otherModelFileName the file name of the other model
+ * @param diffsOnly only returns differences between the model when set
+ * @param keysOnly only returns the keys that are different when set, when not set values are also returned
+ * @return the result of the operation
+ */
+ public ApexApiResult compare(final String otherModelFileName, final boolean diffsOnly, final boolean keysOnly) {
+ ApexApiResult result = new ApexApiResult();
+ try {
+ AxPolicyModel otherPolicyModel = loadModelFromFile(otherModelFileName, result);
+ if (!result.getResult().equals(ApexApiResult.Result.SUCCESS)) {
+ return result;
+ }
+
+ PolicyModelComparer policyModelComparer =
+ new PolicyModelComparer(apexModel.getPolicyModel(), otherPolicyModel);
+ result.addMessage(new ApexModelStringWriter<AxArtifactKey>(false)
+ .writeString(apexModel.getPolicyModel().getKey(), AxArtifactKey.class));
+ result.addMessage(policyModelComparer.toString());
+
+ return result;
+ } catch (Exception e) {
+ return new ApexApiResult(ApexApiResult.Result.FAILED, e);
+ }
+ }
+
+ /**
+ * Compare two Apex models, returning the differences between the models.
+ *
+ * @param otherModelString the other model as a string
+ * @param diffsOnly only returns differences between the model when set
+ * @param keysOnly only returns the keys that are different when set, when not set values are also returned
+ * @return the result of the operation
+ */
+ public ApexApiResult compareWithString(final String otherModelString, final boolean diffsOnly,
+ final boolean keysOnly) {
+ ApexApiResult result = new ApexApiResult();
+ try {
+ AxPolicyModel otherPolicyModel = loadModelFromString(otherModelString, result);
+ if (!result.getResult().equals(ApexApiResult.Result.SUCCESS)) {
+ return result;
+ }
+
+ PolicyModelComparer policyModelComparer =
+ new PolicyModelComparer(apexModel.getPolicyModel(), otherPolicyModel);
+ result.addMessage(new ApexModelStringWriter<AxArtifactKey>(false)
+ .writeString(apexModel.getPolicyModel().getKey(), AxArtifactKey.class));
+ result.addMessage(policyModelComparer.toString());
+
+ return result;
+ } catch (Exception e) {
+ return new ApexApiResult(ApexApiResult.Result.FAILED, e);
+ }
+ }
+
+ /**
+ * Split out a sub model from an Apex model that contains a given subset of the policies in the original model.
+ *
+ * @param targetModelName the file name of the target model in which to store the model split out from the original
+ * model
+ * @param splitOutPolicies the policies form the original model to include in the split out model, specified as a
+ * comma delimited list of policy names
+ * @return the result of the operation
+ */
+ public ApexApiResult split(final String targetModelName, final String splitOutPolicies) {
+ Set<AxArtifactKey> requiredPolicySet = new LinkedHashSet<>();
+
+ // Split the policy names on comma
+ String[] policyNames = splitOutPolicies.split(",");
+
+ // Iterate over the policy names
+ for (String policyName : policyNames) {
+ // Split out this specific policy
+ AxPolicy requiredPolicy = apexModel.getPolicyModel().getPolicies().get(policyName);
+
+ if (requiredPolicy != null) {
+ requiredPolicySet.add(requiredPolicy.getKey());
+ } else {
+ return new ApexApiResult(ApexApiResult.Result.FAILED,
+ "policy for policy name " + policyName + " not found in model");
+ }
+ }
+
+ try {
+ AxPolicyModel splitPolicyModel =
+ PolicyModelSplitter.getSubPolicyModel(apexModel.getPolicyModel(), requiredPolicySet, false);
+
+ ApexModelFileWriter<AxPolicyModel> apexModelFileWriter = new ApexModelFileWriter<>(false);
+ apexModelFileWriter.apexModelWriteJsonFile(splitPolicyModel, AxPolicyModel.class, targetModelName);
+ return new ApexApiResult();
+ } catch (ApexException e) {
+ return new ApexApiResult(ApexApiResult.Result.FAILED, e);
+ }
+ }
+
+ /**
+ * Split out a sub model from an Apex model that contains a given subset of the policies in the original model,
+ * return the split model in the result as a string.
+ *
+ * @param splitOutPolicies the policies form the original model to include in the split out model, specified as a
+ * comma delimited list of policy names
+ * @return the result of the operation
+ */
+ public ApexApiResult split(final String splitOutPolicies) {
+ ApexApiResult splitResult = new ApexApiResult();
+ File tempSplitPolicyFile = null;
+ try {
+ tempSplitPolicyFile = TextFileUtils.createTempFile("ApexTempPolicy", null);
+
+ // Split the policy into a temporary file
+ splitResult = split(tempSplitPolicyFile.getCanonicalPath(), splitOutPolicies);
+ if (splitResult.isNok()) {
+ return splitResult;
+ }
+
+ // Get the policy model into a string
+ String splitPolicyModelString = TextFileUtils.getTextFileAsString(tempSplitPolicyFile.getCanonicalPath());
+
+ // Return the policy model
+ splitResult.addMessage(splitPolicyModelString);
+ return splitResult;
+ } catch (Exception e) {
+ return new ApexApiResult(ApexApiResult.Result.FAILED,
+ "split of policy model " + apexModel.getPolicyModel().getId() + " failed", e);
+ } finally {
+ if (tempSplitPolicyFile != null) {
+ try {
+ Files.delete(tempSplitPolicyFile.toPath());
+ } catch (IOException e) {
+ LOGGER.debug("delete of temporary file failed", e);
+ }
+ }
+ }
+ }
+
+ /**
+ * Merge two Apex models together.
+ *
+ * @param mergeInModelName the file name of the model to merge into the current model
+ * @param keepOriginal if this flag is set to true, if a concept exists in both models, the original model copy of
+ * that concept is kept, if the flag is set to false, then the copy of the concept from the mergeInModel
+ * overwrites the concept in the original model
+ * @return the result of the operation
+ */
+ public ApexApiResult merge(final String mergeInModelName, final boolean keepOriginal) {
+ ApexApiResult result = new ApexApiResult();
+ AxPolicyModel mergeInPolicyModel = loadModelFromFile(mergeInModelName, result);
+ if (!result.getResult().equals(ApexApiResult.Result.SUCCESS)) {
+ return result;
+ }
+
+ try {
+ AxPolicyModel mergedPolicyModel = PolicyModelMerger.getMergedPolicyModel(apexModel.getPolicyModel(),
+ mergeInPolicyModel, keepOriginal, false, false);
+ apexModel.setPolicyModel(mergedPolicyModel != null ? mergedPolicyModel : new AxPolicyModel());
+ return new ApexApiResult();
+ } catch (ApexModelException e) {
+ apexModel.setPolicyModel(new AxPolicyModel());
+ return new ApexApiResult(ApexApiResult.Result.FAILED, e);
+ }
+ }
+
+ /**
+ * Merge two Apex models together.
+ *
+ * @param otherModelString the model to merge as a string
+ * @param keepOriginal if this flag is set to true, if a concept exists in both models, the original model copy of
+ * that concept is kept, if the flag is set to false, then the copy of the concept from the mergeInModel
+ * overwrites the concept in the original model
+ * @return the result of the operation
+ */
+ public ApexApiResult mergeWithString(final String otherModelString, final boolean keepOriginal) {
+ ApexApiResult result = new ApexApiResult();
+ AxPolicyModel mergeInPolicyModel = loadModelFromString(otherModelString, result);
+ if (!result.getResult().equals(ApexApiResult.Result.SUCCESS)) {
+ return result;
+ }
+
+ try {
+ AxPolicyModel mergedPolicyModel = PolicyModelMerger.getMergedPolicyModel(apexModel.getPolicyModel(),
+ mergeInPolicyModel, keepOriginal, false, false);
+ apexModel.setPolicyModel(mergedPolicyModel != null ? mergedPolicyModel : new AxPolicyModel());
+ return new ApexApiResult();
+ } catch (ApexModelException e) {
+ apexModel.setPolicyModel(new AxPolicyModel());
+ return new ApexApiResult(ApexApiResult.Result.FAILED, e);
+ }
+ }
+
+ /**
+ * Load a policy model from a file.
+ *
+ * @param fileName the name of the file containing the model
+ * @param result the result of the operation
+ * @return the model
+ */
+ private AxPolicyModel loadModelFromFile(final String fileName, final ApexApiResult result) {
+ Assertions.argumentNotNull(fileName, FILE_NAME_MAY_NOT_BE_NULL);
+
+ AxPolicyModel readModel = null;
+
+ final URL apexModelUrl = ResourceUtils.getLocalFile(fileName);
+ if (apexModelUrl == null) {
+ result.setResult(ApexApiResult.Result.FAILED);
+ result.addMessage("file " + fileName + " not found");
+ return null;
+ }
+
+ try {
+ ApexModelReader<AxPolicyModel> apexModelReader = new ApexModelReader<>(AxPolicyModel.class);
+ apexModelReader.setValidate(false);
+ readModel = apexModelReader.read(apexModelUrl.openStream());
+ result.setResult(ApexApiResult.Result.SUCCESS);
+ return readModel;
+ } catch (Exception e) {
+ result.setResult(ApexApiResult.Result.FAILED);
+ result.addThrowable(e);
+ return null;
+ }
+ }
+
+ /**
+ * Load a policy model from a string.
+ *
+ * @param modelString the string containing the model
+ * @param result the result of the operation
+ * @return the model
+ */
+ private AxPolicyModel loadModelFromString(final String modelString, final ApexApiResult result) {
+ Assertions.argumentNotNull(modelString, "modelString may not be null");
+
+ AxPolicyModel readModel = null;
+
+ InputStream modelStringStream = new ByteArrayInputStream(modelString.getBytes());
+
+ try {
+ ApexModelReader<AxPolicyModel> apexModelReader = new ApexModelReader<>(AxPolicyModel.class);
+ apexModelReader.setValidate(false);
+ readModel = apexModelReader.read(modelStringStream);
+ result.setResult(ApexApiResult.Result.SUCCESS);
+ return readModel;
+ } catch (Exception e) {
+ result.setResult(ApexApiResult.Result.FAILED);
+ result.addThrowable(e);
+ return null;
+ }
+ }
+}
diff --git a/model/src/main/java/org/onap/policy/apex/model/modelapi/impl/PolicyFacade.java b/model/src/main/java/org/onap/policy/apex/model/modelapi/impl/PolicyFacade.java
new file mode 100644
index 000000000..408f0913f
--- /dev/null
+++ b/model/src/main/java/org/onap/policy/apex/model/modelapi/impl/PolicyFacade.java
@@ -0,0 +1,1385 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * Copyright (C) 2016-2018 Ericsson. All rights reserved.
+ * Modifications Copyright (C) 2019,2022 Nordix Foundation.
+ * Modifications Copyright (C) 2021 Bell Canada. All rights reserved.
+ * Modifications Copyright (C) 2021 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.apex.model.modelapi.impl;
+
+import java.util.Map;
+import java.util.Map.Entry;
+import java.util.Properties;
+import java.util.Set;
+import java.util.TreeMap;
+import java.util.TreeSet;
+import org.onap.policy.apex.model.basicmodel.concepts.AxArtifactKey;
+import org.onap.policy.apex.model.basicmodel.concepts.AxKey;
+import org.onap.policy.apex.model.basicmodel.concepts.AxReferenceKey;
+import org.onap.policy.apex.model.basicmodel.concepts.AxValidationResult;
+import org.onap.policy.apex.model.basicmodel.handling.ApexModelStringWriter;
+import org.onap.policy.apex.model.contextmodel.concepts.AxContextAlbum;
+import org.onap.policy.apex.model.eventmodel.concepts.AxEvent;
+import org.onap.policy.apex.model.modelapi.ApexApiResult;
+import org.onap.policy.apex.model.modelapi.ApexModel;
+import org.onap.policy.apex.model.policymodel.concepts.AxPolicy;
+import org.onap.policy.apex.model.policymodel.concepts.AxState;
+import org.onap.policy.apex.model.policymodel.concepts.AxStateFinalizerLogic;
+import org.onap.policy.apex.model.policymodel.concepts.AxStateOutput;
+import org.onap.policy.apex.model.policymodel.concepts.AxStateTaskOutputType;
+import org.onap.policy.apex.model.policymodel.concepts.AxStateTaskReference;
+import org.onap.policy.apex.model.policymodel.concepts.AxTask;
+import org.onap.policy.apex.model.policymodel.concepts.AxTaskSelectionLogic;
+import org.onap.policy.common.utils.validation.Assertions;
+
+/**
+ * This class acts as a facade for operations towards a policy model for policy operations.
+ *
+ * @author Liam Fallon (liam.fallon@ericsson.com)
+ */
+public class PolicyFacade {
+ private static final String STATE_NAME_MAY_NOT_BE_NULL = "stateName may not be null";
+ private static final String DOES_NOT_EXIST_ON_STATE = " does not exist on state ";
+ private static final String STATE_FINALIZER_LOGIC = "state finalizer logic ";
+ private static final String DO_ES_NOT_EXIST = " do(es) not exist";
+ private static final String CONCEPT_S = "concept(s) ";
+ private static final String DOES_NOT_EXIST = " does not exist";
+ private static final String CONCEPT = "concept ";
+ private static final String ALREADY_EXISTS = " already exists";
+
+ // Apex model we're working towards
+ private final ApexModel apexModel;
+
+ // Properties to use for the model
+ private final Properties apexProperties;
+
+ // Facade classes for working towards the real Apex model
+ private final KeyInformationFacade keyInformationFacade;
+
+ /**
+ * Constructor that creates a policy facade for the Apex Model API.
+ *
+ * @param apexModel the apex model
+ * @param apexProperties Properties for the model
+ */
+ public PolicyFacade(final ApexModel apexModel, final Properties apexProperties) {
+ this.apexModel = apexModel;
+ this.apexProperties = apexProperties;
+
+ keyInformationFacade = new KeyInformationFacade(apexModel, apexProperties);
+ }
+
+ /**
+ * Create a policy.
+ *
+ * @param name name of the policy
+ * @param version version of the policy, set to null to use the default version
+ * @param template template used to create the policy, set to null to use the default template
+ * @param firstState the first state of the policy
+ * @param uuid policy UUID, set to null to generate a UUID
+ * @param description policy description, set to null to generate a description
+ * @return result of the operation
+ */
+ public ApexApiResult createPolicy(final String name, final String version, final String template,
+ final String firstState, final String uuid, final String description) {
+ try {
+ final AxArtifactKey key = new AxArtifactKey();
+ key.setName(name);
+ if (version != null) {
+ key.setVersion(version);
+ } else {
+ key.setVersion(apexProperties.getProperty("DEFAULT_CONCEPT_VERSION"));
+ }
+
+ String templateString = template;
+ if (templateString == null) {
+ templateString = apexProperties.getProperty("DEFAULT_POLICY_TEMPLATE");
+ }
+
+ if (apexModel.getPolicyModel().getPolicies().getPolicyMap().containsKey(key)) {
+ return new ApexApiResult(ApexApiResult.Result.CONCEPT_EXISTS, CONCEPT + key.getId() + ALREADY_EXISTS);
+ }
+
+ final AxPolicy policy = new AxPolicy(key);
+ policy.setTemplate(templateString);
+ policy.setFirstState(firstState);
+
+ apexModel.getPolicyModel().getPolicies().getPolicyMap().put(key, policy);
+
+ if (apexModel.getPolicyModel().getKeyInformation().getKeyInfoMap().containsKey(key)) {
+ return keyInformationFacade.updateKeyInformation(name, version, uuid, description);
+ } else {
+ return keyInformationFacade.createKeyInformation(name, version, uuid, description);
+ }
+ } catch (final Exception e) {
+ return new ApexApiResult(ApexApiResult.Result.FAILED, e);
+ }
+ }
+
+ /**
+ * Update a policy.
+ *
+ * @param name name of the policy
+ * @param version version of the policy, set to null to use the latest version
+ * @param template template used to create the policy, set to null to not update
+ * @param firstState the first state of the policy
+ * @param uuid policy UUID, set to null to not update
+ * @param description policy description, set to null to not update
+ * @return result of the operation
+ */
+ public ApexApiResult updatePolicy(final String name, final String version, final String template,
+ final String firstState, final String uuid, final String description) {
+ try {
+ final AxPolicy policy = apexModel.getPolicyModel().getPolicies().get(name, version);
+ if (policy == null) {
+ return new ApexApiResult(ApexApiResult.Result.CONCEPT_DOES_NOT_EXIST,
+ CONCEPT + name + ':' + version + DOES_NOT_EXIST);
+ }
+
+ if (template != null) {
+ policy.setTemplate(template);
+ }
+ if (firstState != null) {
+ policy.setFirstState(firstState);
+ }
+
+ return keyInformationFacade.updateKeyInformation(name, version, uuid, description);
+ } catch (final Exception e) {
+ return new ApexApiResult(ApexApiResult.Result.FAILED, e);
+ }
+ }
+
+ /**
+ * List policies.
+ *
+ * @param name name of the policy, set to null to list all
+ * @param version starting version of the policy, set to null to list all versions
+ * @return result of the operation
+ */
+ public ApexApiResult listPolicy(final String name, final String version) {
+ try {
+ final Set<AxPolicy> policySet = apexModel.getPolicyModel().getPolicies().getAll(name, version);
+ if (name != null && policySet.isEmpty()) {
+ return new ApexApiResult(ApexApiResult.Result.CONCEPT_DOES_NOT_EXIST,
+ CONCEPT_S + name + ':' + version + DO_ES_NOT_EXIST);
+ }
+
+ final ApexApiResult result = new ApexApiResult();
+ for (final AxPolicy policy : policySet) {
+ result.addMessage(new ApexModelStringWriter<AxPolicy>(false).writeString(policy, AxPolicy.class));
+ }
+ return result;
+ } catch (final Exception e) {
+ return new ApexApiResult(ApexApiResult.Result.FAILED, e);
+ }
+ }
+
+ /**
+ * Delete a policy.
+ *
+ * @param name name of the policy
+ * @param version version of the policy, set to null to use the latest version
+ * @return result of the operation
+ */
+ public ApexApiResult deletePolicy(final String name, final String version) {
+ try {
+ if (version != null) {
+ final AxArtifactKey key = new AxArtifactKey(name, version);
+ final AxPolicy removedPolicy = apexModel.getPolicyModel().getPolicies().getPolicyMap().remove(key);
+ if (removedPolicy != null) {
+ return new ApexApiResult(ApexApiResult.Result.SUCCESS,
+ new ApexModelStringWriter<AxPolicy>(false).writeString(removedPolicy, AxPolicy.class));
+ } else {
+ return new ApexApiResult(ApexApiResult.Result.CONCEPT_DOES_NOT_EXIST,
+ CONCEPT + key.getId() + DOES_NOT_EXIST);
+ }
+ }
+
+ final Set<AxPolicy> policySet = apexModel.getPolicyModel().getPolicies().getAll(name, version);
+ if (policySet.isEmpty()) {
+ return new ApexApiResult(ApexApiResult.Result.CONCEPT_DOES_NOT_EXIST,
+ CONCEPT_S + name + ':' + version + DO_ES_NOT_EXIST);
+ }
+
+ final ApexApiResult result = new ApexApiResult();
+ for (final AxPolicy policy : policySet) {
+ result.addMessage(new ApexModelStringWriter<AxPolicy>(false).writeString(policy, AxPolicy.class));
+ apexModel.getPolicyModel().getPolicies().getPolicyMap().remove(policy.getKey());
+ keyInformationFacade.deleteKeyInformation(name, version);
+ }
+ return result;
+ } catch (final Exception e) {
+ return new ApexApiResult(ApexApiResult.Result.FAILED, e);
+ }
+ }
+
+ /**
+ * Validate policies.
+ *
+ * @param name name of the policy, set to null to list all
+ * @param version starting version of the policy, set to null to list all versions
+ * @return result of the operation
+ */
+ public ApexApiResult validatePolicy(final String name, final String version) {
+ try {
+ final Set<AxPolicy> policySet = apexModel.getPolicyModel().getPolicies().getAll(name, version);
+ if (policySet.isEmpty()) {
+ return new ApexApiResult(ApexApiResult.Result.CONCEPT_DOES_NOT_EXIST,
+ CONCEPT_S + name + ':' + version + DO_ES_NOT_EXIST);
+ }
+
+ final ApexApiResult result = new ApexApiResult();
+ for (final AxPolicy policy : policySet) {
+ final AxValidationResult validationResult = policy.validate(new AxValidationResult());
+ result.addMessage(
+ new ApexModelStringWriter<AxArtifactKey>(false).writeString(policy.getKey(), AxArtifactKey.class));
+ result.addMessage(validationResult.toString());
+ }
+ return result;
+ } catch (final Exception e) {
+ return new ApexApiResult(ApexApiResult.Result.FAILED, e);
+ }
+ }
+
+ /**
+ * Create a policy state.
+ *
+ * @param name name of the policy
+ * @param version version of the policy, set to null to use the latest version
+ * @param stateName of the state
+ * @param triggerName name of the trigger event for this state
+ * @param triggerVersion version of the trigger event for this state, set to null to use the
+ * latest version
+ * @param defaultTaskName the default task name
+ * @param defaltTaskVersion the default task version, set to null to use the latest version
+ * @return result of the operation
+ */
+ public ApexApiResult createPolicyState(final String name, final String version, final String stateName,
+ final String triggerName, final String triggerVersion, final String defaultTaskName,
+ final String defaltTaskVersion) {
+ try {
+ Assertions.argumentNotNull(stateName, STATE_NAME_MAY_NOT_BE_NULL);
+
+ final AxPolicy policy = apexModel.getPolicyModel().getPolicies().get(name, version);
+ if (policy == null) {
+ return new ApexApiResult(ApexApiResult.Result.CONCEPT_DOES_NOT_EXIST,
+ CONCEPT + name + ':' + version + DOES_NOT_EXIST);
+ }
+
+ final AxReferenceKey refKey = new AxReferenceKey(policy.getKey(), stateName);
+
+ if (policy.getStateMap().containsKey(refKey.getLocalName())) {
+ return new ApexApiResult(ApexApiResult.Result.CONCEPT_EXISTS,
+ CONCEPT + refKey.getId() + ALREADY_EXISTS);
+ }
+
+ final AxEvent triggerEvent = apexModel.getPolicyModel().getEvents().get(triggerName, triggerVersion);
+ if (triggerEvent == null) {
+ return new ApexApiResult(ApexApiResult.Result.CONCEPT_DOES_NOT_EXIST,
+ CONCEPT + triggerName + ':' + triggerVersion + DOES_NOT_EXIST);
+ }
+
+ final AxTask defaultTask = apexModel.getPolicyModel().getTasks().get(defaultTaskName, defaltTaskVersion);
+ if (defaultTask == null) {
+ return new ApexApiResult(ApexApiResult.Result.CONCEPT_DOES_NOT_EXIST,
+ CONCEPT + defaultTaskName + ':' + defaltTaskVersion + DOES_NOT_EXIST);
+ }
+
+ final AxState state = new AxState(refKey);
+ state.setTrigger(triggerEvent.getKey());
+ state.setDefaultTask(defaultTask.getKey());
+
+ policy.getStateMap().put(state.getKey().getLocalName(), state);
+ return new ApexApiResult();
+ } catch (final Exception e) {
+ return new ApexApiResult(ApexApiResult.Result.FAILED, e);
+ }
+ }
+
+ /**
+ * Update a policy state.
+ *
+ * @param name name of the policy
+ * @param version version of the policy, set to null to use the latest version
+ * @param stateName of the state
+ * @param triggerName name of the trigger event for this state, set to null to not update
+ * @param triggerVersion version of the trigger event for this state, set to use latest version
+ * of trigger event
+ * @param defaultTaskName the default task name, set to null to not update
+ * @param defaltTaskVersion the default task version, set to use latest version of default task
+ * @return result of the operation
+ */
+ public ApexApiResult updatePolicyState(final String name, final String version, final String stateName,
+ final String triggerName, final String triggerVersion, final String defaultTaskName,
+ final String defaltTaskVersion) {
+ try {
+ Assertions.argumentNotNull(stateName, STATE_NAME_MAY_NOT_BE_NULL);
+
+ final AxPolicy policy = apexModel.getPolicyModel().getPolicies().get(name, version);
+ if (policy == null) {
+ return new ApexApiResult(ApexApiResult.Result.CONCEPT_DOES_NOT_EXIST,
+ CONCEPT + name + ':' + version + DOES_NOT_EXIST);
+ }
+
+ final AxState state = policy.getStateMap().get(stateName);
+ if (state == null) {
+ return new ApexApiResult(ApexApiResult.Result.CONCEPT_DOES_NOT_EXIST,
+ CONCEPT + policy.getKey().getId() + ':' + stateName + DOES_NOT_EXIST);
+ }
+
+ if (triggerName != null) {
+ final AxEvent triggerEvent = apexModel.getPolicyModel().getEvents().get(triggerName, triggerVersion);
+ if (triggerEvent == null) {
+ return new ApexApiResult(ApexApiResult.Result.CONCEPT_DOES_NOT_EXIST,
+ CONCEPT + triggerName + ':' + triggerVersion + DOES_NOT_EXIST);
+ }
+ state.setTrigger(triggerEvent.getKey());
+ }
+
+ if (defaultTaskName != null) {
+ final AxTask defaultTask =
+ apexModel.getPolicyModel().getTasks().get(defaultTaskName, defaltTaskVersion);
+ if (defaultTask == null) {
+ return new ApexApiResult(ApexApiResult.Result.CONCEPT_DOES_NOT_EXIST,
+ CONCEPT + defaultTaskName + ':' + defaltTaskVersion + DOES_NOT_EXIST);
+ }
+ state.setDefaultTask(defaultTask.getKey());
+ }
+
+ return new ApexApiResult();
+ } catch (final Exception e) {
+ return new ApexApiResult(ApexApiResult.Result.FAILED, e);
+ }
+ }
+
+ /**
+ * List policy states.
+ *
+ * @param name name of the policy
+ * @param version version of the policy, set to null to use the latest version
+ * @param stateName of the state, set to null to list all states of the policy
+ * @return result of the operation
+ */
+ public ApexApiResult listPolicyState(final String name, final String version, final String stateName) {
+ try {
+ final AxPolicy policy = apexModel.getPolicyModel().getPolicies().get(name, version);
+ if (policy == null) {
+ return new ApexApiResult(ApexApiResult.Result.CONCEPT_DOES_NOT_EXIST,
+ CONCEPT + name + ':' + version + DOES_NOT_EXIST);
+ }
+
+ if (stateName != null) {
+ final AxState state = policy.getStateMap().get(stateName);
+ if (state != null) {
+ return new ApexApiResult(ApexApiResult.Result.SUCCESS,
+ new ApexModelStringWriter<AxState>(false).writeString(state, AxState.class));
+ } else {
+ return new ApexApiResult(ApexApiResult.Result.CONCEPT_DOES_NOT_EXIST,
+ CONCEPT + name + ':' + version + ':' + state + DOES_NOT_EXIST);
+ }
+ } else {
+ if (policy.getStateMap().size() == 0) {
+ return new ApexApiResult(ApexApiResult.Result.CONCEPT_DOES_NOT_EXIST,
+ "no states defined on policy " + policy.getKey().getId());
+ }
+ final ApexApiResult result = new ApexApiResult();
+ for (final AxState state : policy.getStateMap().values()) {
+ result.addMessage(new ApexModelStringWriter<AxState>(false).writeString(state, AxState.class));
+ }
+ return result;
+ }
+ } catch (final Exception e) {
+ return new ApexApiResult(ApexApiResult.Result.FAILED, e);
+ }
+ }
+
+ /**
+ * Delete a policy state.
+ *
+ * @param name name of the policy
+ * @param version version of the policy, set to null to use the latest version
+ * @param stateName of the state, set to null to delete all states
+ * @return result of the operation
+ */
+ public ApexApiResult deletePolicyState(final String name, final String version, final String stateName) {
+ try {
+ final AxPolicy policy = apexModel.getPolicyModel().getPolicies().get(name, version);
+ if (policy == null) {
+ return new ApexApiResult(ApexApiResult.Result.CONCEPT_DOES_NOT_EXIST,
+ CONCEPT + name + ':' + version + DOES_NOT_EXIST);
+ }
+
+ final ApexApiResult result = new ApexApiResult();
+ if (stateName != null) {
+ if (policy.getStateMap().containsKey(stateName)) {
+ result.addMessage(new ApexModelStringWriter<AxState>(false)
+ .writeString(policy.getStateMap().get(stateName), AxState.class));
+ policy.getStateMap().remove(stateName);
+ return result;
+ } else {
+ return new ApexApiResult(ApexApiResult.Result.CONCEPT_DOES_NOT_EXIST,
+ CONCEPT + name + ':' + version + ':' + stateName + DOES_NOT_EXIST);
+ }
+ } else {
+ if (policy.getStateMap().size() == 0) {
+ return new ApexApiResult(ApexApiResult.Result.CONCEPT_DOES_NOT_EXIST,
+ "no states defined on policy " + policy.getKey().getId());
+ }
+ for (final AxState state : policy.getStateMap().values()) {
+ result.addMessage(new ApexModelStringWriter<AxState>(false).writeString(state, AxState.class));
+ }
+ policy.getStateMap().clear();
+ return result;
+ }
+ } catch (final Exception e) {
+ return new ApexApiResult(ApexApiResult.Result.FAILED, e);
+ }
+ }
+
+ /**
+ * Create task selection logic for a state.
+ *
+ * @param name name of the policy
+ * @param version version of the policy, set to null to use the latest version
+ * @param stateName of the state
+ * @param logicFlavour the task selection logic flavour for the state, set to null to use the
+ * default task logic flavour
+ * @param logic the source code for the logic of the state
+ * @return result of the operation
+ */
+ public ApexApiResult createPolicyStateTaskSelectionLogic(final String name, final String version,
+ final String stateName, final String logicFlavour, final String logic) {
+ try {
+ Assertions.argumentNotNull(stateName, STATE_NAME_MAY_NOT_BE_NULL);
+
+ final AxPolicy policy = apexModel.getPolicyModel().getPolicies().get(name, version);
+ if (policy == null) {
+ return new ApexApiResult(ApexApiResult.Result.CONCEPT_DOES_NOT_EXIST,
+ CONCEPT + name + ':' + version + DOES_NOT_EXIST);
+ }
+
+ final AxState state = policy.getStateMap().get(stateName);
+ if (state == null) {
+ return new ApexApiResult(ApexApiResult.Result.CONCEPT_DOES_NOT_EXIST,
+ CONCEPT + policy.getKey().getId() + ':' + stateName + DOES_NOT_EXIST);
+ }
+
+ // There is only one logic item associated with a state so we use a hard coded logic
+ // name
+ final AxReferenceKey refKey = new AxReferenceKey(state.getKey(), "TaskSelectionLogic");
+
+ if (!state.getTaskSelectionLogic().getKey().getLocalName().equals(AxKey.NULL_KEY_NAME)) {
+ return new ApexApiResult(ApexApiResult.Result.CONCEPT_EXISTS,
+ CONCEPT + refKey.getId() + ALREADY_EXISTS);
+ }
+
+ state.setTaskSelectionLogic(new AxTaskSelectionLogic(refKey, logicFlavour, logic));
+ return new ApexApiResult();
+ } catch (final Exception e) {
+ return new ApexApiResult(ApexApiResult.Result.FAILED, e);
+ }
+ }
+
+ /**
+ * Update task selection logic for a state.
+ *
+ * @param name name of the policy
+ * @param version version of the policy, set to null to use the latest version
+ * @param stateName of the state
+ * @param logicFlavour the task selection logic flavour for the state, set to null to not update
+ * @param logic the source code for the logic of the state, set to null to not update
+ * @return result of the operation
+ */
+ public ApexApiResult updatePolicyStateTaskSelectionLogic(final String name, final String version,
+ final String stateName, final String logicFlavour, final String logic) {
+ try {
+ Assertions.argumentNotNull(stateName, STATE_NAME_MAY_NOT_BE_NULL);
+
+ final AxPolicy policy = apexModel.getPolicyModel().getPolicies().get(name, version);
+ if (policy == null) {
+ return new ApexApiResult(ApexApiResult.Result.CONCEPT_DOES_NOT_EXIST,
+ CONCEPT + name + ':' + version + DOES_NOT_EXIST);
+ }
+
+ final AxState state = policy.getStateMap().get(stateName);
+ if (state == null) {
+ return new ApexApiResult(ApexApiResult.Result.CONCEPT_DOES_NOT_EXIST,
+ CONCEPT + policy.getKey().getId() + ':' + stateName + DOES_NOT_EXIST);
+ }
+
+ if (state.getTaskSelectionLogic().getKey().getLocalName().equals(AxKey.NULL_KEY_NAME)) {
+ return new ApexApiResult(ApexApiResult.Result.CONCEPT_DOES_NOT_EXIST,
+ CONCEPT + state.getTaskSelectionLogic().getKey().getId() + DOES_NOT_EXIST);
+ }
+
+ final AxTaskSelectionLogic taskSelectionLogic = state.getTaskSelectionLogic();
+ if (logicFlavour != null) {
+ taskSelectionLogic.setLogicFlavour(logicFlavour);
+ }
+ if (logic != null) {
+ taskSelectionLogic.setLogic(logic);
+ }
+
+ return new ApexApiResult();
+ } catch (final Exception e) {
+ return new ApexApiResult(ApexApiResult.Result.FAILED, e);
+ }
+ }
+
+ /**
+ * List task selection logic for a state.
+ *
+ * @param name name of the policy
+ * @param version version of the policy, set to null to use the latest version
+ * @param stateName of the state
+ * @return result of the operation
+ */
+ public ApexApiResult listPolicyStateTaskSelectionLogic(final String name, final String version,
+ final String stateName) {
+ try {
+ Assertions.argumentNotNull(stateName, STATE_NAME_MAY_NOT_BE_NULL);
+
+ final AxPolicy policy = apexModel.getPolicyModel().getPolicies().get(name, version);
+ if (policy == null) {
+ return new ApexApiResult(ApexApiResult.Result.CONCEPT_DOES_NOT_EXIST,
+ CONCEPT + name + ':' + version + DOES_NOT_EXIST);
+ }
+
+ final AxState state = policy.getStateMap().get(stateName);
+ if (state == null) {
+ return new ApexApiResult(ApexApiResult.Result.CONCEPT_DOES_NOT_EXIST,
+ CONCEPT + policy.getKey().getId() + ':' + stateName + DOES_NOT_EXIST);
+ }
+
+ return new ApexApiResult(ApexApiResult.Result.SUCCESS,
+ new ApexModelStringWriter<AxTaskSelectionLogic>(false).writeString(state.getTaskSelectionLogic(),
+ AxTaskSelectionLogic.class));
+ } catch (final Exception e) {
+ return new ApexApiResult(ApexApiResult.Result.FAILED, e);
+ }
+ }
+
+ /**
+ * Delete task selection logic for a state.
+ *
+ * @param name name of the policy
+ * @param version version of the policy, set to null to use the latest version
+ * @param stateName of the state
+ * @return result of the operation
+ */
+ public ApexApiResult deletePolicyStateTaskSelectionLogic(final String name, final String version,
+ final String stateName) {
+ try {
+ Assertions.argumentNotNull(stateName, STATE_NAME_MAY_NOT_BE_NULL);
+
+ final AxPolicy policy = apexModel.getPolicyModel().getPolicies().get(name, version);
+ if (policy == null) {
+ return new ApexApiResult(ApexApiResult.Result.CONCEPT_DOES_NOT_EXIST,
+ CONCEPT + name + ':' + version + DOES_NOT_EXIST);
+ }
+
+ final AxState state = policy.getStateMap().get(stateName);
+ if (state == null) {
+ return new ApexApiResult(ApexApiResult.Result.CONCEPT_DOES_NOT_EXIST,
+ CONCEPT + policy.getKey().getId() + ':' + stateName + DOES_NOT_EXIST);
+ }
+
+ if (state.getTaskSelectionLogic().getKey().getLocalName().equals(AxKey.NULL_KEY_NAME)) {
+ return new ApexApiResult(ApexApiResult.Result.CONCEPT_DOES_NOT_EXIST,
+ CONCEPT + state.getTaskSelectionLogic().getKey().getId() + DOES_NOT_EXIST);
+ }
+
+ final ApexApiResult result = new ApexApiResult();
+ result.addMessage(new ApexModelStringWriter<AxTaskSelectionLogic>(false)
+ .writeString(state.getTaskSelectionLogic(), AxTaskSelectionLogic.class));
+ state.setTaskSelectionLogic(new AxTaskSelectionLogic());
+ return result;
+ } catch (final Exception e) {
+ return new ApexApiResult(ApexApiResult.Result.FAILED, e);
+ }
+ }
+
+ /**
+ * Create a policy state output.
+ *
+ * @param name name of the policy
+ * @param version version of the policy, set to null to use the latest version
+ * @param stateName of the state
+ * @param outputName of the state output
+ * @param eventName name of the output event for this state output
+ * @param eventVersion version of the output event for this state output, set to null to use the
+ * latest version
+ * @param nextState for this state to transition to, set to null if this is the last state that
+ * the policy transitions to on this branch
+ * @return result of the operation
+ */
+ public ApexApiResult createPolicyStateOutput(final String name, final String version, final String stateName,
+ final String outputName, final String eventName, final String eventVersion, final String nextState) {
+ try {
+ Assertions.argumentNotNull(stateName, STATE_NAME_MAY_NOT_BE_NULL);
+ Assertions.argumentNotNull(outputName, "outputName may not be null");
+
+ final AxPolicy policy = apexModel.getPolicyModel().getPolicies().get(name, version);
+ if (policy == null) {
+ return new ApexApiResult(ApexApiResult.Result.CONCEPT_DOES_NOT_EXIST,
+ "Policy concept " + name + ':' + version + DOES_NOT_EXIST);
+ }
+
+ final AxState state = policy.getStateMap().get(stateName);
+ if (state == null) {
+ return new ApexApiResult(ApexApiResult.Result.CONCEPT_DOES_NOT_EXIST,
+ "State concept " + policy.getKey().getId() + ':' + stateName + DOES_NOT_EXIST);
+ }
+
+ final AxReferenceKey refKey = new AxReferenceKey(state.getKey(), outputName);
+ // There can be multipe state outputs only when the current state is the final state
+ if (nextState != null && !AxReferenceKey.getNullKey().getLocalName().equals(nextState)
+ && state.getStateOutputs().containsKey(refKey.getLocalName())) {
+ return new ApexApiResult(ApexApiResult.Result.CONCEPT_EXISTS,
+ "Output concept " + refKey.getId() + ALREADY_EXISTS);
+ }
+
+ final AxEvent event = apexModel.getPolicyModel().getEvents().get(eventName, eventVersion);
+ if (event == null) {
+ return new ApexApiResult(ApexApiResult.Result.CONCEPT_DOES_NOT_EXIST,
+ "Event concept " + eventName + ':' + eventVersion + DOES_NOT_EXIST);
+ }
+
+ AxReferenceKey nextStateKey = AxReferenceKey.getNullKey();
+ if (nextState != null && !(AxReferenceKey.getNullKey().getLocalName().equals(nextState))) {
+ if (state.getKey().getLocalName().equals(nextState)) {
+ return new ApexApiResult(ApexApiResult.Result.FAILED,
+ "next state " + nextState + " of a state cannot be the state itself");
+ }
+ nextStateKey = new AxReferenceKey(state.getKey().getParentArtifactKey(), nextState);
+
+ if (!policy.getStateMap().containsKey(nextState)) {
+ return new ApexApiResult(ApexApiResult.Result.CONCEPT_DOES_NOT_EXIST,
+ "Next state concept " + nextStateKey.getId() + DOES_NOT_EXIST);
+ }
+ }
+
+ populateStateOuputInfo(nextState, state, refKey, event, nextStateKey);
+ return new ApexApiResult();
+ } catch (final Exception e) {
+ return new ApexApiResult(ApexApiResult.Result.FAILED, e);
+ }
+ }
+
+ private void populateStateOuputInfo(final String nextState, final AxState state, final AxReferenceKey refKey,
+ final AxEvent event, AxReferenceKey nextStateKey) {
+ // nextState is null. There could be multiple events coming out of the state
+ if ((nextState == null || AxReferenceKey.getNullKey().getLocalName().equals(nextState))
+ && state.getStateOutputs().containsKey(refKey.getLocalName())) {
+ AxStateOutput existingStateOutput = state.getStateOutputs().get(refKey.getLocalName());
+ if (null == existingStateOutput.getOutgoingEventSet()
+ || existingStateOutput.getOutgoingEventSet().isEmpty()) {
+ Set<AxArtifactKey> eventSet = new TreeSet<>();
+ eventSet.add(existingStateOutput.getOutgoingEvent());
+ existingStateOutput.setOutgoingEventSet(eventSet);
+ }
+ existingStateOutput.getOutgoingEventSet().add(event.getKey());
+ } else {
+ AxStateOutput axStateOutput = new AxStateOutput(refKey, event.getKey(), nextStateKey);
+ Set<AxArtifactKey> eventSet = new TreeSet<>();
+ eventSet.add(axStateOutput.getOutgoingEvent());
+ axStateOutput.setOutgoingEventSet(eventSet);
+ state.getStateOutputs().put(refKey.getLocalName(), axStateOutput);
+ }
+ }
+
+ /**
+ * List policy state outputs.
+ *
+ * @param name name of the policy
+ * @param version version of the policy, set to null to use the latest version
+ * @param stateName of the state
+ * @param outputName of the state output, set to null to list all outputs of the state
+ * @return result of the operation
+ */
+ public ApexApiResult listPolicyStateOutput(final String name, final String version, final String stateName,
+ final String outputName) {
+ try {
+ Assertions.argumentNotNull(stateName, STATE_NAME_MAY_NOT_BE_NULL);
+
+ final AxPolicy policy = apexModel.getPolicyModel().getPolicies().get(name, version);
+ if (policy == null) {
+ return new ApexApiResult(ApexApiResult.Result.CONCEPT_DOES_NOT_EXIST,
+ CONCEPT + name + ':' + version + DOES_NOT_EXIST);
+ }
+
+ final AxState state = policy.getStateMap().get(stateName);
+ if (state == null) {
+ return new ApexApiResult(ApexApiResult.Result.CONCEPT_DOES_NOT_EXIST,
+ CONCEPT + policy.getKey().getId() + ':' + stateName + DOES_NOT_EXIST);
+ }
+
+ if (outputName != null) {
+ final AxStateOutput stateOutput = state.getStateOutputs().get(outputName);
+ if (stateOutput != null) {
+ return new ApexApiResult(ApexApiResult.Result.SUCCESS,
+ new ApexModelStringWriter<AxStateOutput>(false).writeString(stateOutput, AxStateOutput.class));
+ } else {
+ return new ApexApiResult(ApexApiResult.Result.CONCEPT_DOES_NOT_EXIST,
+ CONCEPT + state.getKey().getId() + ':' + outputName + DOES_NOT_EXIST);
+ }
+ } else {
+ if (state.getStateOutputs().size() == 0) {
+ return new ApexApiResult(ApexApiResult.Result.CONCEPT_DOES_NOT_EXIST,
+ "no state output concepts exist for state " + state.getKey().getId());
+ }
+
+ final ApexApiResult result = new ApexApiResult();
+
+ for (final AxStateOutput stateOutput : state.getStateOutputs().values()) {
+ result.addMessage(
+ new ApexModelStringWriter<AxStateOutput>(false).writeString(stateOutput, AxStateOutput.class));
+ }
+ return result;
+ }
+ } catch (final Exception e) {
+ return new ApexApiResult(ApexApiResult.Result.FAILED, e);
+ }
+ }
+
+ /**
+ * Delete a policy state output.
+ *
+ * @param name name of the policy
+ * @param version version of the policy, set to null to use the latest version
+ * @param stateName of the state
+ * @param outputName of the state output, set to null to delete all state outputs
+ * @return result of the operation
+ */
+ public ApexApiResult deletePolicyStateOutput(final String name, final String version, final String stateName,
+ final String outputName) {
+ try {
+ Assertions.argumentNotNull(stateName, STATE_NAME_MAY_NOT_BE_NULL);
+
+ final AxPolicy policy = apexModel.getPolicyModel().getPolicies().get(name, version);
+ if (policy == null) {
+ return new ApexApiResult(ApexApiResult.Result.CONCEPT_DOES_NOT_EXIST,
+ CONCEPT + name + ':' + version + DOES_NOT_EXIST);
+ }
+
+ final AxState state = policy.getStateMap().get(stateName);
+ if (state == null) {
+ return new ApexApiResult(ApexApiResult.Result.CONCEPT_DOES_NOT_EXIST,
+ CONCEPT + policy.getKey().getId() + ':' + stateName + DOES_NOT_EXIST);
+ }
+
+ if (outputName != null) {
+ final AxStateOutput stateOutput = state.getStateOutputs().get(outputName);
+ if (stateOutput != null) {
+ final ApexApiResult result = new ApexApiResult(ApexApiResult.Result.SUCCESS,
+ new ApexModelStringWriter<AxStateOutput>(false).writeString(stateOutput, AxStateOutput.class));
+ state.getStateOutputs().remove(outputName);
+ return result;
+ } else {
+ return new ApexApiResult(ApexApiResult.Result.CONCEPT_DOES_NOT_EXIST,
+ CONCEPT + state.getKey().getId() + ':' + outputName + DOES_NOT_EXIST);
+ }
+ } else {
+ if (state.getStateOutputs().size() == 0) {
+ return new ApexApiResult(ApexApiResult.Result.CONCEPT_DOES_NOT_EXIST,
+ "no state output concepts exist for state " + state.getKey().getId());
+ }
+
+ final ApexApiResult result = new ApexApiResult();
+
+ for (final Entry<String, AxStateOutput> stateOutputEntry : state.getStateOutputs().entrySet()) {
+ result.addMessage(new ApexModelStringWriter<AxStateOutput>(false)
+ .writeString(stateOutputEntry.getValue(), AxStateOutput.class));
+ }
+ state.getStateOutputs().clear();
+ return result;
+ }
+ } catch (final Exception e) {
+ return new ApexApiResult(ApexApiResult.Result.FAILED, e);
+ }
+ }
+
+ /**
+ * Create policy finalizer logic for a state.
+ *
+ * @param name name of the policy
+ * @param version version of the policy, set to null to use the latest version
+ * @param stateName of the state
+ * @param finalizerLogicName name of the state finalizer logic
+ * @param logicFlavour the policy finalizer logic flavour for the state, set to null to use the
+ * default task logic flavour
+ * @param logic the source code for the logic of the state
+ * @return result of the operation
+ */
+ public ApexApiResult createPolicyStateFinalizerLogic(final String name, final String version,
+ final String stateName, final String finalizerLogicName, final String logicFlavour, final String logic) {
+ try {
+ Assertions.argumentNotNull(stateName, STATE_NAME_MAY_NOT_BE_NULL);
+ Assertions.argumentNotNull(finalizerLogicName, "finalizerlogicName may not be null");
+
+ final AxPolicy policy = apexModel.getPolicyModel().getPolicies().get(name, version);
+ if (policy == null) {
+ return new ApexApiResult(ApexApiResult.Result.CONCEPT_DOES_NOT_EXIST,
+ CONCEPT + name + ':' + version + DOES_NOT_EXIST);
+ }
+
+ final AxState state = policy.getStateMap().get(stateName);
+ if (state == null) {
+ return new ApexApiResult(ApexApiResult.Result.CONCEPT_DOES_NOT_EXIST,
+ CONCEPT + policy.getKey().getId() + ':' + stateName + DOES_NOT_EXIST);
+ }
+
+ final AxReferenceKey refKey = new AxReferenceKey(state.getKey(), finalizerLogicName);
+
+ if (state.getStateFinalizerLogicMap().containsKey(refKey.getLocalName())) {
+ return new ApexApiResult(ApexApiResult.Result.CONCEPT_EXISTS,
+ CONCEPT + refKey.getId() + ALREADY_EXISTS);
+ }
+
+ state.getStateFinalizerLogicMap().put(finalizerLogicName,
+ new AxStateFinalizerLogic(refKey, logicFlavour, logic));
+ return new ApexApiResult();
+ } catch (final Exception e) {
+ return new ApexApiResult(ApexApiResult.Result.FAILED, e);
+ }
+ }
+
+ /**
+ * Update policy finalizer logic for a state.
+ *
+ * @param name name of the policy
+ * @param version version of the policy, set to null to use the latest version
+ * @param stateName of the state
+ * @param finalizerLogicName name of the state finalizer logic
+ * @param logicFlavour the policy finalizer logic flavour for the state, set to null to not
+ * update
+ * @param logic the source code for the logic of the state, set to null to not update
+ * @return result of the operation
+ */
+ public ApexApiResult updatePolicyStateFinalizerLogic(final String name, final String version,
+ final String stateName, final String finalizerLogicName, final String logicFlavour, final String logic) {
+ try {
+ Assertions.argumentNotNull(stateName, STATE_NAME_MAY_NOT_BE_NULL);
+ Assertions.argumentNotNull(finalizerLogicName, "finalizerLogicName may not be null");
+
+ final AxPolicy policy = apexModel.getPolicyModel().getPolicies().get(name, version);
+ if (policy == null) {
+ return new ApexApiResult(ApexApiResult.Result.CONCEPT_DOES_NOT_EXIST,
+ CONCEPT + name + ':' + version + DOES_NOT_EXIST);
+ }
+
+ final AxState state = policy.getStateMap().get(stateName);
+ if (state == null) {
+ return new ApexApiResult(ApexApiResult.Result.CONCEPT_DOES_NOT_EXIST,
+ CONCEPT + policy.getKey().getId() + ':' + stateName + DOES_NOT_EXIST);
+ }
+
+ final AxReferenceKey refKey = new AxReferenceKey(state.getKey(), finalizerLogicName);
+ final AxStateFinalizerLogic stateFinalizerLogic =
+ state.getStateFinalizerLogicMap().get(refKey.getKey().getLocalName());
+ if (stateFinalizerLogic == null) {
+ return new ApexApiResult(ApexApiResult.Result.CONCEPT_DOES_NOT_EXIST,
+ STATE_FINALIZER_LOGIC + refKey.getId() + DOES_NOT_EXIST);
+ }
+
+ if (logicFlavour != null) {
+ stateFinalizerLogic.setLogicFlavour(logicFlavour);
+ }
+ if (logic != null) {
+ stateFinalizerLogic.setLogic(logic);
+ }
+
+ return new ApexApiResult();
+ } catch (final Exception e) {
+ return new ApexApiResult(ApexApiResult.Result.FAILED, e);
+ }
+ }
+
+ /**
+ * List policy finalizer logic for a state.
+ *
+ * @param name name of the policy
+ * @param version version of the policy, set to null to use the latest version
+ * @param stateName of the state
+ * @param finalizerLogicName name of the state finalizer logic
+ * @return result of the operation
+ */
+ public ApexApiResult listPolicyStateFinalizerLogic(final String name, final String version, final String stateName,
+ final String finalizerLogicName) {
+ try {
+ Assertions.argumentNotNull(stateName, STATE_NAME_MAY_NOT_BE_NULL);
+
+ final AxPolicy policy = apexModel.getPolicyModel().getPolicies().get(name, version);
+ if (policy == null) {
+ return new ApexApiResult(ApexApiResult.Result.CONCEPT_DOES_NOT_EXIST,
+ CONCEPT + name + ':' + version + DOES_NOT_EXIST);
+ }
+
+ final AxState state = policy.getStateMap().get(stateName);
+ if (state == null) {
+ return new ApexApiResult(ApexApiResult.Result.CONCEPT_DOES_NOT_EXIST,
+ CONCEPT + policy.getKey().getId() + ':' + stateName + DOES_NOT_EXIST);
+ }
+
+ if (finalizerLogicName != null) {
+ final AxReferenceKey refKey = new AxReferenceKey(state.getKey(), finalizerLogicName);
+ final AxStateFinalizerLogic stateFinalizerLogic =
+ state.getStateFinalizerLogicMap().get(refKey.getKey().getLocalName());
+ if (stateFinalizerLogic == null) {
+ return new ApexApiResult(ApexApiResult.Result.CONCEPT_DOES_NOT_EXIST,
+ STATE_FINALIZER_LOGIC + refKey.getId() + DOES_NOT_EXIST);
+ }
+
+ return new ApexApiResult(ApexApiResult.Result.SUCCESS,
+ new ApexModelStringWriter<AxStateFinalizerLogic>(false).writeString(stateFinalizerLogic,
+ AxStateFinalizerLogic.class));
+ } else {
+ if (state.getStateFinalizerLogicMap().size() == 0) {
+ return new ApexApiResult(ApexApiResult.Result.CONCEPT_DOES_NOT_EXIST,
+ "no state finalizer logic defined on state " + state.getKey().getId());
+ }
+ final ApexApiResult result = new ApexApiResult();
+ for (final AxStateFinalizerLogic stateFinalizerLogic : state.getStateFinalizerLogicMap().values()) {
+ result.addMessage(new ApexModelStringWriter<AxStateFinalizerLogic>(false)
+ .writeString(stateFinalizerLogic, AxStateFinalizerLogic.class));
+ }
+ return result;
+ }
+ } catch (final Exception e) {
+ return new ApexApiResult(ApexApiResult.Result.FAILED, e);
+ }
+ }
+
+ /**
+ * Delete policy finalizer logic for a state.
+ *
+ * @param name name of the policy
+ * @param version version of the policy, set to null to use the latest version
+ * @param stateName of the state
+ * @param finalizerLogicName name of the state finalizer logic
+ * @return result of the operation
+ */
+ public ApexApiResult deletePolicyStateFinalizerLogic(final String name, final String version,
+ final String stateName, final String finalizerLogicName) {
+ try {
+ Assertions.argumentNotNull(stateName, STATE_NAME_MAY_NOT_BE_NULL);
+
+ final AxPolicy policy = apexModel.getPolicyModel().getPolicies().get(name, version);
+ if (policy == null) {
+ return new ApexApiResult(ApexApiResult.Result.CONCEPT_DOES_NOT_EXIST,
+ CONCEPT + name + ':' + version + DOES_NOT_EXIST);
+ }
+
+ final AxState state = policy.getStateMap().get(stateName);
+ if (state == null) {
+ return new ApexApiResult(ApexApiResult.Result.CONCEPT_DOES_NOT_EXIST,
+ CONCEPT + policy.getKey().getId() + ':' + stateName + DOES_NOT_EXIST);
+ }
+
+ if (finalizerLogicName != null) {
+ final AxReferenceKey refKey = new AxReferenceKey(state.getKey(), finalizerLogicName);
+ final AxStateFinalizerLogic stateFinalizerLogic =
+ state.getStateFinalizerLogicMap().get(refKey.getKey().getLocalName());
+ if (stateFinalizerLogic == null) {
+ return new ApexApiResult(ApexApiResult.Result.CONCEPT_DOES_NOT_EXIST,
+ STATE_FINALIZER_LOGIC + refKey.getId() + DOES_NOT_EXIST);
+ }
+
+ final ApexApiResult result = new ApexApiResult();
+ result.addMessage(new ApexModelStringWriter<AxStateFinalizerLogic>(false)
+ .writeString(stateFinalizerLogic, AxStateFinalizerLogic.class));
+ state.getStateFinalizerLogicMap().remove(refKey.getLocalName());
+ return result;
+ } else {
+ if (state.getStateFinalizerLogicMap().size() == 0) {
+ return new ApexApiResult(ApexApiResult.Result.CONCEPT_DOES_NOT_EXIST,
+ "no state finalizer logic defined on state " + state.getKey().getId());
+ }
+
+ final ApexApiResult result = new ApexApiResult();
+ for (final AxStateFinalizerLogic stateFinalizerLogic : state.getStateFinalizerLogicMap().values()) {
+ result.addMessage(new ApexModelStringWriter<AxStateFinalizerLogic>(false)
+ .writeString(stateFinalizerLogic, AxStateFinalizerLogic.class));
+ }
+ state.getStateFinalizerLogicMap().clear();
+ return result;
+ }
+ } catch (final Exception e) {
+ return new ApexApiResult(ApexApiResult.Result.FAILED, e);
+ }
+ }
+
+ /**
+ * Create a policy state task reference.
+ *
+ * @param builder builder for the state task reference
+ * @return result of the operation
+ */
+ public ApexApiResult createPolicyStateTaskRef(CreatePolicyStateTaskRef builder) {
+ try {
+ Assertions.argumentNotNull(builder.getStateName(), STATE_NAME_MAY_NOT_BE_NULL);
+ Assertions.argumentNotNull(builder.getOutputName(), "outputName may not be null");
+
+ final AxPolicy policy =
+ apexModel.getPolicyModel().getPolicies().get(builder.getName(), builder.getVersion());
+ if (policy == null) {
+ return new ApexApiResult(ApexApiResult.Result.CONCEPT_DOES_NOT_EXIST,
+ CONCEPT + builder.getName() + ':' + builder.getVersion() + DOES_NOT_EXIST);
+ }
+
+ final AxState state = policy.getStateMap().get(builder.getStateName());
+ if (state == null) {
+ return new ApexApiResult(ApexApiResult.Result.CONCEPT_DOES_NOT_EXIST,
+ CONCEPT + policy.getKey().getId() + ':' + builder.getStateName() + DOES_NOT_EXIST);
+ }
+
+ final AxTask task =
+ apexModel.getPolicyModel().getTasks().get(builder.getTaskName(), builder.getTaskVersion());
+ if (task == null) {
+ return new ApexApiResult(ApexApiResult.Result.CONCEPT_DOES_NOT_EXIST,
+ CONCEPT + builder.getTaskName() + ':' + builder.getTaskVersion() + DOES_NOT_EXIST);
+ }
+
+ if (state.getTaskReferences().containsKey(task.getKey())) {
+ return new ApexApiResult(ApexApiResult.Result.CONCEPT_EXISTS, "task " + task.getKey().getId()
+ + " already has reference with output " + state.getTaskReferences().get(task.getKey()));
+ }
+
+ AxReferenceKey refKey;
+ if (builder.getTaskLocalName() == null) {
+ refKey = new AxReferenceKey(state.getKey(), state.getKey().getParentKeyName());
+ } else {
+ refKey = new AxReferenceKey(state.getKey(), builder.getTaskLocalName());
+ }
+
+ // The reference to the output we're using here
+ final AxReferenceKey outputRefKey = new AxReferenceKey(state.getKey(), builder.getOutputName());
+
+ final AxStateTaskOutputType stateTaskOutputType = AxStateTaskOutputType.valueOf(builder.getOutputType());
+ if (stateTaskOutputType.equals(AxStateTaskOutputType.DIRECT)) {
+ if (!state.getStateOutputs().containsKey(outputRefKey.getLocalName())) {
+ return new ApexApiResult(ApexApiResult.Result.CONCEPT_DOES_NOT_EXIST,
+ "state output concept " + outputRefKey.getId() + DOES_NOT_EXIST);
+ }
+ } else if (stateTaskOutputType.equals(AxStateTaskOutputType.LOGIC)) {
+ if (!state.getStateFinalizerLogicMap().containsKey(outputRefKey.getLocalName())) {
+ return new ApexApiResult(ApexApiResult.Result.CONCEPT_DOES_NOT_EXIST,
+ "state finalizer logic concept " + outputRefKey.getId() + DOES_NOT_EXIST);
+ }
+ } else {
+ return new ApexApiResult(ApexApiResult.Result.FAILED,
+ "output type " + builder.getOutputType() + " invalid");
+ }
+
+ String outputRefName = outputRefKey.getLocalName();
+ // in case of SFL, outgoing event will be same for all state outputs that are part of SFL.So, take any entry
+ if (AxStateTaskOutputType.LOGIC.equals(stateTaskOutputType)) {
+ outputRefName = state.getStateOutputs().keySet().iterator().next();
+ }
+
+ // add input and output events to the task based on state definition
+ if (state.getStateOutputs().containsKey(outputRefName)) {
+ populateIoEventsToTask(state, task, outputRefName);
+ }
+
+ state.getTaskReferences().put(task.getKey(),
+ new AxStateTaskReference(refKey, stateTaskOutputType, outputRefKey));
+ return new ApexApiResult();
+ } catch (final Exception e) {
+ return new ApexApiResult(ApexApiResult.Result.FAILED, e);
+ }
+ }
+
+ private void populateIoEventsToTask(final AxState state, final AxTask task, final String outputRefName) {
+ AxEvent triggerEvent = apexModel.getPolicyModel().getEvents().get(state.getTrigger());
+ task.setInputEvent(triggerEvent);
+ Map<String, AxEvent> outputEvents = new TreeMap<>();
+ if (state.getNextStateSet().isEmpty()
+ || state.getNextStateSet().contains(AxReferenceKey.getNullKey().getLocalName())) {
+ state.getStateOutputs().get(outputRefName).getOutgoingEventSet().forEach(outgoingEventKey -> outputEvents
+ .put(outgoingEventKey.getName(), apexModel.getPolicyModel().getEvents().get(outgoingEventKey)));
+ } else {
+ AxArtifactKey outgoingEventKey = state.getStateOutputs().get(outputRefName).getOutgoingEvent();
+ outputEvents.put(outgoingEventKey.getName(), apexModel.getPolicyModel().getEvents().get(outgoingEventKey));
+ }
+ task.setOutputEvents(outputEvents);
+ }
+
+ /**
+ * List policy state task references.
+ *
+ * @param name name of the policy
+ * @param version version of the policy, set to null to use the latest version
+ * @param stateName of the state
+ * @param taskName name of the task, set to null to list all task references
+ * @param taskVersion version of the task, set to null to use the latest version
+ * @return result of the operation
+ */
+ public ApexApiResult listPolicyStateTaskRef(final String name, final String version, final String stateName,
+ final String taskName, final String taskVersion) {
+ try {
+ Assertions.argumentNotNull(stateName, STATE_NAME_MAY_NOT_BE_NULL);
+
+ final AxPolicy policy = apexModel.getPolicyModel().getPolicies().get(name, version);
+ if (policy == null) {
+ return new ApexApiResult(ApexApiResult.Result.CONCEPT_DOES_NOT_EXIST,
+ CONCEPT + name + ':' + version + DOES_NOT_EXIST);
+ }
+
+ final AxState state = policy.getStateMap().get(stateName);
+ if (state == null) {
+ return new ApexApiResult(ApexApiResult.Result.CONCEPT_DOES_NOT_EXIST,
+ CONCEPT + policy.getKey().getId() + ':' + stateName + DOES_NOT_EXIST);
+ }
+
+ final ApexApiResult result = new ApexApiResult();
+ boolean found = false;
+ final Map<AxArtifactKey, AxStateTaskReference> taskReferences = state.getTaskReferences();
+ for (final Entry<AxArtifactKey, AxStateTaskReference> taskReferenceEntry : taskReferences.entrySet()) {
+ final AxArtifactKey key = taskReferenceEntry.getKey();
+ final AxStateTaskReference value = taskReferenceEntry.getValue();
+ if ((taskName != null && !key.getName().equals(taskName))
+ || (taskVersion != null && !key.getVersion().equals(taskVersion))) {
+ continue;
+ }
+
+ found = true;
+ result
+ .addMessage(new ApexModelStringWriter<AxArtifactKey>(false).writeString(key, AxArtifactKey.class));
+ result.addMessage(new ApexModelStringWriter<AxStateTaskReference>(false).writeString(value,
+ AxStateTaskReference.class));
+ }
+ if (found) {
+ return result;
+ } else {
+ return new ApexApiResult(ApexApiResult.Result.CONCEPT_DOES_NOT_EXIST,
+ "no task references found for state " + state.getKey().getId());
+ }
+ } catch (final Exception e) {
+ return new ApexApiResult(ApexApiResult.Result.FAILED, e);
+ }
+ }
+
+ /**
+ * Delete a policy state task reference.
+ *
+ * @param name name of the policy
+ * @param version version of the policy, set to null to use the latest version
+ * @param stateName of the state
+ * @param taskName name of the task, set to null to delete all task references
+ * @param taskVersion version of the task, set to null to use the latest version
+ * @return result of the operation
+ */
+ public ApexApiResult deletePolicyStateTaskRef(final String name, final String version, final String stateName,
+ final String taskName, final String taskVersion) {
+ try {
+ Assertions.argumentNotNull(stateName, STATE_NAME_MAY_NOT_BE_NULL);
+
+ final AxPolicy policy = apexModel.getPolicyModel().getPolicies().get(name, version);
+ if (policy == null) {
+ return new ApexApiResult(ApexApiResult.Result.CONCEPT_DOES_NOT_EXIST,
+ CONCEPT + name + ':' + version + DOES_NOT_EXIST);
+ }
+
+ final AxState state = policy.getStateMap().get(stateName);
+ if (state == null) {
+ return new ApexApiResult(ApexApiResult.Result.CONCEPT_DOES_NOT_EXIST,
+ CONCEPT + policy.getKey().getId() + ':' + stateName + DOES_NOT_EXIST);
+ }
+
+ final Set<AxArtifactKey> deleteSet = new TreeSet<>();
+
+ for (final AxArtifactKey taskReferenceKey : state.getTaskReferences().keySet()) {
+ if ((taskName != null && !taskReferenceKey.getName().equals(taskName))
+ || (taskVersion != null && !taskReferenceKey.getVersion().equals(taskVersion))) {
+ continue;
+ }
+ deleteSet.add(taskReferenceKey);
+ }
+ if (deleteSet.isEmpty()) {
+ return new ApexApiResult(ApexApiResult.Result.CONCEPT_DOES_NOT_EXIST,
+ CONCEPT + taskName + ':' + taskVersion + DOES_NOT_EXIST_ON_STATE + state.getKey().getId());
+ }
+
+ final ApexApiResult result = new ApexApiResult();
+ for (final AxArtifactKey keyToDelete : deleteSet) {
+ state.getTaskReferences().remove(keyToDelete);
+ result.addMessage(
+ new ApexModelStringWriter<AxArtifactKey>(false).writeString(keyToDelete, AxArtifactKey.class));
+ }
+ return result;
+ } catch (final Exception e) {
+ return new ApexApiResult(ApexApiResult.Result.FAILED, e);
+ }
+ }
+
+ /**
+ * Create a policy state context album reference.
+ *
+ * @param name name of the policy
+ * @param version version of the policy, set to null to use the latest version
+ * @param stateName of the state
+ * @param contextAlbumName name of the context album for the context album reference
+ * @param contextAlbumVersion version of the context album for the context album reference, set
+ * to null to use the latest version
+ * @return result of the operation
+ */
+ public ApexApiResult createPolicyStateContextRef(final String name, final String version, final String stateName,
+ final String contextAlbumName, final String contextAlbumVersion) {
+ try {
+ Assertions.argumentNotNull(stateName, STATE_NAME_MAY_NOT_BE_NULL);
+
+ final AxPolicy policy = apexModel.getPolicyModel().getPolicies().get(name, version);
+ if (policy == null) {
+ return new ApexApiResult(ApexApiResult.Result.CONCEPT_DOES_NOT_EXIST,
+ CONCEPT + name + ':' + version + DOES_NOT_EXIST);
+ }
+
+ final AxState state = policy.getStateMap().get(stateName);
+ if (state == null) {
+ return new ApexApiResult(ApexApiResult.Result.CONCEPT_DOES_NOT_EXIST,
+ CONCEPT + policy.getKey().getId() + ':' + stateName + DOES_NOT_EXIST);
+ }
+
+ final AxContextAlbum contextAlbum =
+ apexModel.getPolicyModel().getAlbums().get(contextAlbumName, contextAlbumVersion);
+ if (contextAlbum == null) {
+ return new ApexApiResult(ApexApiResult.Result.CONCEPT_DOES_NOT_EXIST,
+ CONCEPT + contextAlbumName + ':' + contextAlbumVersion + DOES_NOT_EXIST);
+ }
+
+ if (state.getContextAlbumReferences().contains(contextAlbum.getKey())) {
+ return new ApexApiResult(ApexApiResult.Result.CONCEPT_EXISTS, "concept album reference for concept "
+ + contextAlbum.getKey().getId() + " already exists in state");
+ }
+
+ state.getContextAlbumReferences().add(contextAlbum.getKey());
+ return new ApexApiResult();
+ } catch (final Exception e) {
+ return new ApexApiResult(ApexApiResult.Result.FAILED, e);
+ }
+ }
+
+ /**
+ * List policy state context album references.
+ *
+ * @param name name of the policy
+ * @param version version of the policy, set to null to use the latest version
+ * @param stateName of the state
+ * @param contextAlbumName name of the context album for the context album reference, set to
+ * null to list all task context album references
+ * @param contextAlbumVersion version of the context album for the context album reference, set
+ * to null to use the latest version
+ * @return result of the operation
+ */
+ public ApexApiResult listPolicyStateContextRef(final String name, final String version, final String stateName,
+ final String contextAlbumName, final String contextAlbumVersion) {
+ try {
+ Assertions.argumentNotNull(stateName, STATE_NAME_MAY_NOT_BE_NULL);
+
+ final AxPolicy policy = apexModel.getPolicyModel().getPolicies().get(name, version);
+ if (policy == null) {
+ return new ApexApiResult(ApexApiResult.Result.CONCEPT_DOES_NOT_EXIST,
+ CONCEPT + name + ':' + version + DOES_NOT_EXIST);
+ }
+
+ final AxState state = policy.getStateMap().get(stateName);
+ if (state == null) {
+ return new ApexApiResult(ApexApiResult.Result.CONCEPT_DOES_NOT_EXIST,
+ CONCEPT + policy.getKey().getId() + ':' + stateName + DOES_NOT_EXIST);
+ }
+
+ final ApexApiResult result = new ApexApiResult();
+ boolean found = false;
+ for (final AxArtifactKey albumKey : state.getContextAlbumReferences()) {
+ if ((contextAlbumName != null && !albumKey.getName().equals(contextAlbumName))
+ || (contextAlbumVersion != null && !albumKey.getVersion().equals(contextAlbumVersion))) {
+ continue;
+ }
+ result.addMessage(
+ new ApexModelStringWriter<AxArtifactKey>(false).writeString(albumKey, AxArtifactKey.class));
+ found = true;
+ }
+ if (!found) {
+ return new ApexApiResult(ApexApiResult.Result.CONCEPT_DOES_NOT_EXIST, CONCEPT + contextAlbumName + ':'
+ + contextAlbumVersion + DOES_NOT_EXIST_ON_STATE + state.getKey().getId());
+ }
+ return result;
+ } catch (final Exception e) {
+ return new ApexApiResult(ApexApiResult.Result.FAILED, e);
+ }
+ }
+
+ /**
+ * Delete a policy state context album reference.
+ *
+ * @param name name of the policy
+ * @param version version of the policy, set to null to use the default version
+ * @param stateName of the state
+ * @param contextAlbumName name of the context album for the context album reference, set to
+ * null to delete all task context album references
+ * @param contextAlbumVersion version of the context album for the context album reference, set
+ * to null to use the latest version
+ * @return result of the operation
+ */
+ public ApexApiResult deletePolicyStateContextRef(final String name, final String version, final String stateName,
+ final String contextAlbumName, final String contextAlbumVersion) {
+ try {
+ Assertions.argumentNotNull(stateName, STATE_NAME_MAY_NOT_BE_NULL);
+
+ final AxPolicy policy = apexModel.getPolicyModel().getPolicies().get(name, version);
+ if (policy == null) {
+ return new ApexApiResult(ApexApiResult.Result.CONCEPT_DOES_NOT_EXIST,
+ CONCEPT + name + ':' + version + DOES_NOT_EXIST);
+ }
+
+ final AxState state = policy.getStateMap().get(stateName);
+ if (state == null) {
+ return new ApexApiResult(ApexApiResult.Result.CONCEPT_DOES_NOT_EXIST,
+ CONCEPT + policy.getKey().getId() + ':' + stateName + DOES_NOT_EXIST);
+ }
+
+ final Set<AxArtifactKey> deleteSet = new TreeSet<>();
+
+ for (final AxArtifactKey albumKey : state.getContextAlbumReferences()) {
+
+ if ((contextAlbumName != null && !albumKey.getName().equals(contextAlbumName))
+ || (contextAlbumVersion != null && !albumKey.getVersion().equals(contextAlbumVersion))) {
+
+ continue;
+ }
+ deleteSet.add(albumKey);
+ }
+ if (deleteSet.isEmpty()) {
+ return new ApexApiResult(ApexApiResult.Result.CONCEPT_DOES_NOT_EXIST, CONCEPT + contextAlbumName + ':'
+ + contextAlbumVersion + DOES_NOT_EXIST_ON_STATE + state.getKey().getId());
+ }
+
+ final ApexApiResult result = new ApexApiResult();
+ for (final AxArtifactKey keyToDelete : deleteSet) {
+ state.getContextAlbumReferences().remove(keyToDelete);
+ result.addMessage(
+ new ApexModelStringWriter<AxArtifactKey>(false).writeString(keyToDelete, AxArtifactKey.class));
+ }
+ return result;
+ } catch (final Exception e) {
+ return new ApexApiResult(ApexApiResult.Result.FAILED, e);
+ }
+ }
+}
diff --git a/model/src/main/java/org/onap/policy/apex/model/modelapi/impl/TaskFacade.java b/model/src/main/java/org/onap/policy/apex/model/modelapi/impl/TaskFacade.java
new file mode 100644
index 000000000..6a2ded3e2
--- /dev/null
+++ b/model/src/main/java/org/onap/policy/apex/model/modelapi/impl/TaskFacade.java
@@ -0,0 +1,597 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * Copyright (C) 2016-2018 Ericsson. All rights reserved.
+ * Modifications Copyright (C) 2019,2022 Nordix Foundation.
+ * Modifications Copyright (C) 2021 Bell Canada. All rights reserved.
+ * ================================================================================
+ * 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.apex.model.modelapi.impl;
+
+import java.util.Properties;
+import java.util.Set;
+import java.util.TreeSet;
+import org.onap.policy.apex.model.basicmodel.concepts.AxArtifactKey;
+import org.onap.policy.apex.model.basicmodel.concepts.AxKey;
+import org.onap.policy.apex.model.basicmodel.concepts.AxReferenceKey;
+import org.onap.policy.apex.model.basicmodel.concepts.AxValidationResult;
+import org.onap.policy.apex.model.basicmodel.handling.ApexModelStringWriter;
+import org.onap.policy.apex.model.contextmodel.concepts.AxContextAlbum;
+import org.onap.policy.apex.model.modelapi.ApexApiResult;
+import org.onap.policy.apex.model.modelapi.ApexModel;
+import org.onap.policy.apex.model.policymodel.concepts.AxTask;
+import org.onap.policy.apex.model.policymodel.concepts.AxTaskLogic;
+import org.onap.policy.apex.model.policymodel.concepts.AxTaskParameter;
+import org.onap.policy.common.utils.validation.Assertions;
+
+/**
+ * This class acts as a facade for operations towards a policy model for task operations.
+ *
+ * @author Liam Fallon (liam.fallon@ericsson.com)
+ */
+public class TaskFacade {
+ private static final String CONCEPT = "concept ";
+ private static final String CONCEPT_S = "concept(s) ";
+ private static final String DOES_NOT_EXIST = " does not exist";
+ private static final String DO_ES_NOT_EXIST = " do(es) not exist";
+ private static final String ALREADY_EXISTS = " already exists";
+
+ // Apex model we're working towards
+ private final ApexModel apexModel;
+
+ // Properties to use for the model
+ private final Properties apexProperties;
+
+ // Facade classes for working towards the real Apex model
+ private final KeyInformationFacade keyInformationFacade;
+
+ /**
+ * Constructor that creates a task facade for the Apex Model API.
+ *
+ * @param apexModel the apex model
+ * @param apexProperties Properties for the model
+ */
+ public TaskFacade(final ApexModel apexModel, final Properties apexProperties) {
+ this.apexModel = apexModel;
+ this.apexProperties = apexProperties;
+
+ keyInformationFacade = new KeyInformationFacade(apexModel, apexProperties);
+ }
+
+ /**
+ * Create a task.
+ *
+ * @param name name of the task
+ * @param version version of the task, set to null to use the default version
+ * @param uuid task UUID, set to null to generate a UUID
+ * @param description task description, set to null to generate a description
+ * @return result of the operation
+ */
+ public ApexApiResult createTask(final String name, final String version, final String uuid,
+ final String description) {
+ try {
+ final AxArtifactKey key = new AxArtifactKey();
+ key.setName(name);
+ if (version != null) {
+ key.setVersion(version);
+ } else {
+ key.setVersion(apexProperties.getProperty("DEFAULT_CONCEPT_VERSION"));
+ }
+
+ if (apexModel.getPolicyModel().getTasks().getTaskMap().containsKey(key)) {
+ return new ApexApiResult(ApexApiResult.Result.CONCEPT_EXISTS, CONCEPT + key.getId() + ALREADY_EXISTS);
+ }
+
+ apexModel.getPolicyModel().getTasks().getTaskMap().put(key, new AxTask(key));
+
+ if (apexModel.getPolicyModel().getKeyInformation().getKeyInfoMap().containsKey(key)) {
+ return keyInformationFacade.updateKeyInformation(name, version, uuid, description);
+ } else {
+ return keyInformationFacade.createKeyInformation(name, version, uuid, description);
+ }
+ } catch (final Exception e) {
+ return new ApexApiResult(ApexApiResult.Result.FAILED, e);
+ }
+ }
+
+ /**
+ * Update a task.
+ *
+ * @param name name of the task
+ * @param version version of the task, set to null to use the latest version
+ * @param uuid task UUID, set to null to not update
+ * @param description task description, set to null to not update
+ * @return result of the operation
+ */
+ public ApexApiResult updateTask(final String name, final String version, final String uuid,
+ final String description) {
+ try {
+ final AxTask task = apexModel.getPolicyModel().getTasks().get(name, version);
+ if (task == null) {
+ return new ApexApiResult(ApexApiResult.Result.CONCEPT_DOES_NOT_EXIST,
+ CONCEPT + name + ':' + version + DOES_NOT_EXIST);
+ }
+
+ return keyInformationFacade.updateKeyInformation(name, version, uuid, description);
+ } catch (final Exception e) {
+ return new ApexApiResult(ApexApiResult.Result.FAILED, e);
+ }
+ }
+
+ /**
+ * List tasks.
+ *
+ * @param name name of the task, set to null to list all
+ * @param version starting version of the task, set to null to list all versions
+ * @return result of the operation
+ */
+ public ApexApiResult listTask(final String name, final String version) {
+ try {
+ final Set<AxTask> taskSet = apexModel.getPolicyModel().getTasks().getAll(name, version);
+ if (name != null && taskSet.isEmpty()) {
+ return new ApexApiResult(ApexApiResult.Result.CONCEPT_DOES_NOT_EXIST,
+ CONCEPT_S + name + ':' + version + DO_ES_NOT_EXIST);
+ }
+
+ final ApexApiResult result = new ApexApiResult();
+ for (final AxTask task : taskSet) {
+ result.addMessage(new ApexModelStringWriter<AxTask>(false).writeString(task, AxTask.class));
+ }
+ return result;
+ } catch (final Exception e) {
+ return new ApexApiResult(ApexApiResult.Result.FAILED, e);
+ }
+ }
+
+ /**
+ * Delete a task.
+ *
+ * @param name name of the task
+ * @param version version of the task, set to null to use the latest version
+ * @return result of the operation
+ */
+ public ApexApiResult deleteTask(final String name, final String version) {
+ try {
+ if (version != null) {
+ final AxArtifactKey key = new AxArtifactKey(name, version);
+ final AxTask removedTask = apexModel.getPolicyModel().getTasks().getTaskMap().remove(key);
+ if (removedTask != null) {
+ return new ApexApiResult(ApexApiResult.Result.SUCCESS,
+ new ApexModelStringWriter<AxTask>(false).writeString(removedTask, AxTask.class));
+ } else {
+ return new ApexApiResult(ApexApiResult.Result.CONCEPT_DOES_NOT_EXIST,
+ CONCEPT + key.getId() + DOES_NOT_EXIST);
+ }
+ }
+
+ final Set<AxTask> taskSet = apexModel.getPolicyModel().getTasks().getAll(name, version);
+ if (taskSet.isEmpty()) {
+ return new ApexApiResult(ApexApiResult.Result.CONCEPT_DOES_NOT_EXIST,
+ CONCEPT_S + name + ':' + version + DO_ES_NOT_EXIST);
+ }
+
+ final ApexApiResult result = new ApexApiResult();
+ for (final AxTask task : taskSet) {
+ result.addMessage(new ApexModelStringWriter<AxTask>(false).writeString(task, AxTask.class));
+ apexModel.getPolicyModel().getTasks().getTaskMap().remove(task.getKey());
+ keyInformationFacade.deleteKeyInformation(name, version);
+ }
+ return result;
+ } catch (final Exception e) {
+ return new ApexApiResult(ApexApiResult.Result.FAILED, e);
+ }
+ }
+
+ /**
+ * Validate tasks.
+ *
+ * @param name name of the task, set to null to list all
+ * @param version starting version of the task, set to null to list all versions
+ * @return result of the operation
+ */
+ public ApexApiResult validateTask(final String name, final String version) {
+ try {
+ final Set<AxTask> taskSet = apexModel.getPolicyModel().getTasks().getAll(name, version);
+ if (taskSet.isEmpty()) {
+ return new ApexApiResult(ApexApiResult.Result.CONCEPT_DOES_NOT_EXIST,
+ CONCEPT_S + name + ':' + version + DO_ES_NOT_EXIST);
+ }
+
+ final ApexApiResult result = new ApexApiResult();
+ for (final AxTask task : taskSet) {
+ final AxValidationResult validationResult = task.validate(new AxValidationResult());
+ result.addMessage(
+ new ApexModelStringWriter<AxArtifactKey>(false).writeString(task.getKey(), AxArtifactKey.class));
+ result.addMessage(validationResult.toString());
+ }
+ return result;
+ } catch (final Exception e) {
+ return new ApexApiResult(ApexApiResult.Result.FAILED, e);
+ }
+ }
+
+ /**
+ * Create logic for a task.
+ *
+ * @param name name of the task
+ * @param version version of the task, set to null to use the latest version
+ * @param logicFlavour the task logic flavour for the task, set to null to use the default task
+ * logic flavour
+ * @param logic the source code for the logic of the task
+ * @return result of the operation
+ */
+ public ApexApiResult createTaskLogic(final String name, final String version, final String logicFlavour,
+ final String logic) {
+ try {
+ final AxTask task = apexModel.getPolicyModel().getTasks().get(name, version);
+ if (task == null) {
+ return new ApexApiResult(ApexApiResult.Result.CONCEPT_DOES_NOT_EXIST,
+ CONCEPT + name + ':' + version + DOES_NOT_EXIST);
+ }
+
+ // There is only one logic item associated with a task so we use a hard coded logic name
+ final AxReferenceKey refKey = new AxReferenceKey(task.getKey(), "TaskLogic");
+
+ if (!task.getTaskLogic().getKey().getLocalName().equals(AxKey.NULL_KEY_NAME)) {
+ return new ApexApiResult(ApexApiResult.Result.CONCEPT_EXISTS,
+ CONCEPT + refKey.getId() + ALREADY_EXISTS);
+ }
+
+ task.setTaskLogic(new AxTaskLogic(refKey, logicFlavour, logic));
+ return new ApexApiResult();
+ } catch (final Exception e) {
+ return new ApexApiResult(ApexApiResult.Result.FAILED, e);
+ }
+ }
+
+ /**
+ * Update logic for a task.
+ *
+ * @param name name of the task
+ * @param version version of the task, set to null to use the latest version
+ * @param logicFlavour the task logic flavour for the task, set to null to not update
+ * @param logic the source code for the logic of the task, set to null to not update
+ * @return result of the operation
+ */
+ public ApexApiResult updateTaskLogic(final String name, final String version, final String logicFlavour,
+ final String logic) {
+ try {
+ final AxTask task = apexModel.getPolicyModel().getTasks().get(name, version);
+ if (task == null) {
+ return new ApexApiResult(ApexApiResult.Result.CONCEPT_DOES_NOT_EXIST,
+ CONCEPT + name + ':' + version + DOES_NOT_EXIST);
+ }
+
+ if (task.getTaskLogic().getKey().getLocalName().equals(AxKey.NULL_KEY_NAME)) {
+ return new ApexApiResult(ApexApiResult.Result.CONCEPT_DOES_NOT_EXIST,
+ CONCEPT + task.getTaskLogic().getKey().getId() + DOES_NOT_EXIST);
+ }
+
+ final AxTaskLogic taskLogic = task.getTaskLogic();
+ if (logicFlavour != null) {
+ taskLogic.setLogicFlavour(logicFlavour);
+ }
+ if (logic != null) {
+ taskLogic.setLogic(logic);
+ }
+
+ return new ApexApiResult();
+ } catch (final Exception e) {
+ return new ApexApiResult(ApexApiResult.Result.FAILED, e);
+ }
+ }
+
+ /**
+ * List task logic.
+ *
+ * @param name name of the task
+ * @param version version of the task, set to null to list the latest version
+ * @return result of the operation
+ */
+ public ApexApiResult listTaskLogic(final String name, final String version) {
+ try {
+ final AxTask task = apexModel.getPolicyModel().getTasks().get(name, version);
+ if (task == null) {
+ return new ApexApiResult(ApexApiResult.Result.CONCEPT_DOES_NOT_EXIST,
+ CONCEPT + name + ':' + version + DOES_NOT_EXIST);
+ }
+
+ return new ApexApiResult(ApexApiResult.Result.SUCCESS,
+ new ApexModelStringWriter<AxTaskLogic>(false).writeString(task.getTaskLogic(), AxTaskLogic.class));
+ } catch (final Exception e) {
+ return new ApexApiResult(ApexApiResult.Result.FAILED, e);
+ }
+ }
+
+ /**
+ * Delete logic for a task.
+ *
+ * @param name name of the task
+ * @param version version of the task, set to null to use the latest version
+ * @return result of the operation
+ */
+ public ApexApiResult deleteTaskLogic(final String name, final String version) {
+ try {
+ final AxTask task = apexModel.getPolicyModel().getTasks().get(name, version);
+ if (task == null) {
+ return new ApexApiResult(ApexApiResult.Result.CONCEPT_DOES_NOT_EXIST,
+ CONCEPT + name + ':' + version + DOES_NOT_EXIST);
+ }
+
+ if (task.getTaskLogic().getKey().getLocalName().equals(AxKey.NULL_KEY_NAME)) {
+ return new ApexApiResult(ApexApiResult.Result.CONCEPT_DOES_NOT_EXIST,
+ CONCEPT + task.getTaskLogic().getKey().getId() + DOES_NOT_EXIST);
+ }
+
+ final ApexApiResult result = new ApexApiResult();
+ result.addMessage(
+ new ApexModelStringWriter<AxTaskLogic>(false).writeString(task.getTaskLogic(), AxTaskLogic.class));
+ task.setTaskLogic(new AxTaskLogic());
+ return result;
+ } catch (final Exception e) {
+ return new ApexApiResult(ApexApiResult.Result.FAILED, e);
+ }
+ }
+
+ /**
+ * Create a task parameter.
+ *
+ * @param name name of the task
+ * @param version version of the task, set to null to use the latest version
+ * @param parName of the parameter
+ * @param defaultValue of the parameter
+ * @return result of the operation
+ */
+ public ApexApiResult createTaskParameter(final String name, final String version, final String parName,
+ final String defaultValue) {
+ try {
+ Assertions.argumentNotNull(parName, "parName may not be null");
+
+ final AxTask task = apexModel.getPolicyModel().getTasks().get(name, version);
+ if (task == null) {
+ return new ApexApiResult(ApexApiResult.Result.CONCEPT_DOES_NOT_EXIST,
+ CONCEPT + name + ':' + version + DOES_NOT_EXIST);
+ }
+
+ final AxReferenceKey refKey = new AxReferenceKey(task.getKey(), parName);
+
+ if (task.getTaskParameters().containsKey(refKey.getLocalName())) {
+ return new ApexApiResult(ApexApiResult.Result.CONCEPT_EXISTS,
+ CONCEPT + refKey.getId() + ALREADY_EXISTS);
+ }
+
+ task.getTaskParameters().put(refKey.getLocalName(), new AxTaskParameter(refKey, defaultValue));
+ return new ApexApiResult();
+ } catch (final Exception e) {
+ return new ApexApiResult(ApexApiResult.Result.FAILED, e);
+ }
+ }
+
+ /**
+ * List task parameters.
+ *
+ * @param name name of the task
+ * @param version version of the task, set to null to use the latest version
+ * @param parName name of the parameter, set to null to list all parameters of the task
+ * @return result of the operation
+ */
+ public ApexApiResult listTaskParameter(final String name, final String version, final String parName) {
+ try {
+ final AxTask task = apexModel.getPolicyModel().getTasks().get(name, version);
+ if (task == null) {
+ return new ApexApiResult(ApexApiResult.Result.CONCEPT_DOES_NOT_EXIST,
+ CONCEPT + name + ':' + version + DOES_NOT_EXIST);
+ }
+
+ if (parName != null) {
+ final AxTaskParameter taskParameter = task.getTaskParameters().get(parName);
+ if (taskParameter != null) {
+ return new ApexApiResult(ApexApiResult.Result.SUCCESS,
+ new ApexModelStringWriter<AxTaskParameter>(false).writeString(taskParameter,
+ AxTaskParameter.class));
+ } else {
+ return new ApexApiResult(ApexApiResult.Result.CONCEPT_DOES_NOT_EXIST,
+ CONCEPT + name + ':' + version + ':' + taskParameter + DOES_NOT_EXIST);
+ }
+ } else {
+ if (task.getTaskParameters().size() == 0) {
+ return new ApexApiResult(ApexApiResult.Result.CONCEPT_DOES_NOT_EXIST,
+ "no task parameters defined on task " + task.getKey().getId());
+ }
+
+ final ApexApiResult result = new ApexApiResult();
+ for (final AxTaskParameter parameter : task.getTaskParameters().values()) {
+ result.addMessage(new ApexModelStringWriter<AxTaskParameter>(false).writeString(parameter,
+ AxTaskParameter.class));
+ }
+ return result;
+ }
+ } catch (final Exception e) {
+ return new ApexApiResult(ApexApiResult.Result.FAILED, e);
+ }
+ }
+
+ /**
+ * Delete a task parameter.
+ *
+ * @param name name of the task
+ * @param version version of the task, set to null to use the latest version
+ * @param parName of the parameter, set to null to delete all task parameters
+ * @return result of the operation
+ */
+ public ApexApiResult deleteTaskParameter(final String name, final String version, final String parName) {
+ try {
+ final AxTask task = apexModel.getPolicyModel().getTasks().get(name, version);
+ if (task == null) {
+ return new ApexApiResult(ApexApiResult.Result.CONCEPT_DOES_NOT_EXIST,
+ CONCEPT + name + ':' + version + DOES_NOT_EXIST);
+ }
+
+ final ApexApiResult result = new ApexApiResult();
+ if (parName != null) {
+ if (task.getTaskParameters().containsKey(parName)) {
+ result.addMessage(new ApexModelStringWriter<AxTaskParameter>(false)
+ .writeString(task.getTaskParameters().get(parName), AxTaskParameter.class));
+ task.getTaskParameters().remove(parName);
+ return result;
+ } else {
+ return new ApexApiResult(ApexApiResult.Result.CONCEPT_DOES_NOT_EXIST,
+ CONCEPT + name + ':' + version + ':' + parName + DOES_NOT_EXIST);
+ }
+ } else {
+ if (task.getTaskParameters().size() == 0) {
+ return new ApexApiResult(ApexApiResult.Result.CONCEPT_DOES_NOT_EXIST,
+ "no task parameters defined on task " + task.getKey().getId());
+ }
+
+ for (final AxTaskParameter parameter : task.getTaskParameters().values()) {
+ result.addMessage(new ApexModelStringWriter<AxTaskParameter>(false).writeString(parameter,
+ AxTaskParameter.class));
+ }
+ task.getTaskParameters().clear();
+ return result;
+ }
+ } catch (final Exception e) {
+ return new ApexApiResult(ApexApiResult.Result.FAILED, e);
+ }
+ }
+
+ /**
+ * Create a task context album reference.
+ *
+ * @param name name of the task
+ * @param version version of the task, set to null to use the latest version
+ * @param contextAlbumName name of the context album for the context album reference
+ * @param contextAlbumVersion version of the context album for the context album reference, set
+ * to null to use the latest version
+ * @return result of the operation
+ */
+ public ApexApiResult createTaskContextRef(final String name, final String version, final String contextAlbumName,
+ final String contextAlbumVersion) {
+ try {
+ final AxTask task = apexModel.getPolicyModel().getTasks().get(name, version);
+ if (task == null) {
+ return new ApexApiResult(ApexApiResult.Result.CONCEPT_DOES_NOT_EXIST,
+ CONCEPT + name + ':' + version + DOES_NOT_EXIST);
+ }
+
+ final AxContextAlbum contextAlbum =
+ apexModel.getPolicyModel().getAlbums().get(contextAlbumName, contextAlbumVersion);
+ if (contextAlbum == null) {
+ return new ApexApiResult(ApexApiResult.Result.CONCEPT_DOES_NOT_EXIST,
+ CONCEPT + contextAlbumName + ':' + contextAlbumVersion + DOES_NOT_EXIST);
+ }
+
+ if (task.getContextAlbumReferences().contains(contextAlbum.getKey())) {
+ return new ApexApiResult(ApexApiResult.Result.CONCEPT_EXISTS,
+ "context album reference for concept " + contextAlbum.getKey().getId() + " already exists in task");
+ }
+
+ task.getContextAlbumReferences().add(contextAlbum.getKey());
+ return new ApexApiResult();
+ } catch (final Exception e) {
+ return new ApexApiResult(ApexApiResult.Result.FAILED, e);
+ }
+ }
+
+ /**
+ * List task context album references.
+ *
+ * @param name name of the task
+ * @param version version of the task, set to null to use the latest version
+ * @param contextAlbumName name of the context album for the context album reference, set to
+ * null to list all task context album references
+ * @param contextAlbumVersion version of the context album for the context album reference, set
+ * to null to use the latest version
+ * @return result of the operation
+ */
+ public ApexApiResult listTaskContextRef(final String name, final String version, final String contextAlbumName,
+ final String contextAlbumVersion) {
+ try {
+ final AxTask task = apexModel.getPolicyModel().getTasks().get(name, version);
+ if (task == null) {
+ return new ApexApiResult(ApexApiResult.Result.CONCEPT_DOES_NOT_EXIST,
+ CONCEPT + name + ':' + version + DOES_NOT_EXIST);
+ }
+
+ final ApexApiResult result = new ApexApiResult();
+ boolean found = false;
+ for (final AxArtifactKey albumKey : task.getContextAlbumReferences()) {
+ if ((contextAlbumName != null && !albumKey.getName().equals(contextAlbumName))
+ || (contextAlbumVersion != null && !albumKey.getVersion().equals(contextAlbumVersion))) {
+ continue;
+ }
+ result.addMessage(
+ new ApexModelStringWriter<AxArtifactKey>(false).writeString(albumKey, AxArtifactKey.class));
+ found = true;
+ }
+ if (!found) {
+ return new ApexApiResult(ApexApiResult.Result.CONCEPT_DOES_NOT_EXIST,
+ CONCEPT + contextAlbumName + ':' + contextAlbumVersion + DOES_NOT_EXIST);
+ }
+ return result;
+ } catch (final Exception e) {
+ return new ApexApiResult(ApexApiResult.Result.FAILED, e);
+ }
+ }
+
+ /**
+ * Delete a task context album reference.
+ *
+ * @param name name of the task
+ * @param version version of the task, set to null to use the latest version
+ * @param contextAlbumName name of the context album for the context album reference, set to
+ * null to delete all task context album references
+ * @param contextAlbumVersion version of the context album for the context album reference, set
+ * to null to use the latest version
+ * @return result of the operation
+ */
+ public ApexApiResult deleteTaskContextRef(final String name, final String version, final String contextAlbumName,
+ final String contextAlbumVersion) {
+ try {
+ final AxTask task = apexModel.getPolicyModel().getTasks().get(name, version);
+ if (task == null) {
+ return new ApexApiResult(ApexApiResult.Result.CONCEPT_DOES_NOT_EXIST,
+ CONCEPT + name + ':' + version + DOES_NOT_EXIST);
+ }
+
+ final Set<AxArtifactKey> deleteSet = new TreeSet<>();
+
+ for (final AxArtifactKey albumKey : task.getContextAlbumReferences()) {
+ if ((contextAlbumName != null && !albumKey.getName().equals(contextAlbumName))
+ || (contextAlbumVersion != null && !albumKey.getVersion().equals(contextAlbumVersion))) {
+ continue;
+ }
+ deleteSet.add(albumKey);
+ }
+
+ if (deleteSet.isEmpty()) {
+ return new ApexApiResult(ApexApiResult.Result.CONCEPT_DOES_NOT_EXIST,
+ CONCEPT + contextAlbumName + ':' + contextAlbumVersion + DOES_NOT_EXIST);
+ }
+ final ApexApiResult result = new ApexApiResult();
+ for (final AxArtifactKey keyToDelete : deleteSet) {
+ task.getContextAlbumReferences().remove(keyToDelete);
+ result.addMessage(
+ new ApexModelStringWriter<AxArtifactKey>(false).writeString(keyToDelete, AxArtifactKey.class));
+ }
+ return result;
+ } catch (final Exception e) {
+ return new ApexApiResult(ApexApiResult.Result.FAILED, e);
+ }
+ }
+}
diff --git a/model/src/main/java/org/onap/policy/apex/model/modelapi/impl/package-info.java b/model/src/main/java/org/onap/policy/apex/model/modelapi/impl/package-info.java
new file mode 100644
index 000000000..f31542044
--- /dev/null
+++ b/model/src/main/java/org/onap/policy/apex/model/modelapi/impl/package-info.java
@@ -0,0 +1,27 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * Copyright (C) 2016-2018 Ericsson. All rights reserved.
+ * ================================================================================
+ * 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=========================================================
+ */
+
+/**
+ * Provides an implementation of the APEX Model API that holds an APEX model and manipulates its
+ * various parts in response to operations called over the Model API.
+ *
+ * @author Liam Fallon (liam.fallon@ericsson.com)
+ */
+package org.onap.policy.apex.model.modelapi.impl;
diff --git a/model/src/main/java/org/onap/policy/apex/model/modelapi/package-info.java b/model/src/main/java/org/onap/policy/apex/model/modelapi/package-info.java
new file mode 100644
index 000000000..97e8db4ea
--- /dev/null
+++ b/model/src/main/java/org/onap/policy/apex/model/modelapi/package-info.java
@@ -0,0 +1,27 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * Copyright (C) 2016-2018 Ericsson. All rights reserved.
+ * ================================================================================
+ * 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=========================================================
+ */
+
+/**
+ * Provides an API to APEX models so that they can be manipulated over a common interface by CLI and
+ * GUI based editors and other development tools.
+ *
+ * @author Liam Fallon (liam.fallon@ericsson.com)
+ */
+package org.onap.policy.apex.model.modelapi;
diff --git a/model/src/main/java/org/onap/policy/apex/model/policymodel/concepts/AxLogic.java b/model/src/main/java/org/onap/policy/apex/model/policymodel/concepts/AxLogic.java
new file mode 100644
index 000000000..0564d5854
--- /dev/null
+++ b/model/src/main/java/org/onap/policy/apex/model/policymodel/concepts/AxLogic.java
@@ -0,0 +1,355 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * Copyright (C) 2016-2018 Ericsson. All rights reserved.
+ * Modifications Copyright (C) 2019-2020,2022 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.apex.model.policymodel.concepts;
+
+import java.util.List;
+import org.onap.policy.apex.model.basicmodel.concepts.AxConcept;
+import org.onap.policy.apex.model.basicmodel.concepts.AxKey;
+import org.onap.policy.apex.model.basicmodel.concepts.AxReferenceKey;
+import org.onap.policy.apex.model.basicmodel.concepts.AxValidationMessage;
+import org.onap.policy.apex.model.basicmodel.concepts.AxValidationResult;
+import org.onap.policy.apex.model.basicmodel.concepts.AxValidationResult.ValidationResult;
+import org.onap.policy.common.utils.validation.Assertions;
+
+/**
+ * This class holds Logic for executing a task or task selection on an Apex policy state. The flavour of the logic
+ * describes the type of logic being used and it may be a language identifier such as "Javascript" or "Jython". The
+ * logic itself is held as a string. The {@link AxLogic} instance is used by the Apex engine to start an executor with
+ * the required flavour. Once the executor is started, the Apex engine passes the logic to the executor and the executor
+ * executes it. In the Apex engine, executors are deployed as plugins. Apex also provides the executor with run-time
+ * context, which makes context such as input fields, output fields, and context albums available to the task at
+ * runtime.
+ *
+ * <p>Validation checks that the logic key is valid, that the logic flavour is defined and is valid when checked against
+ * the {@code LOGIC_FLAVOUR_REGEXP} regular expression, and that the specified logic string is not null or blank.
+ */
+public class AxLogic extends AxConcept {
+ private static final long serialVersionUID = -4260562004005697328L;
+
+ private static final String WHITESPACE_REGEXP = "\\s+$";
+
+ private static final String LOGIC_FLAVOUR_TOKEN = "logicFlavour";
+ private static final String KEY_NULL_MESSAGE = "key may not be null";
+ private static final String LOGIC_FLAVOUR_NULL_MESSAGE = "logicFlavour may not be null";
+ private static final String LOGIC_NULL_MESSAGE = "logic may not be null";
+
+ /** Regular expression that specifies the allowed characters in logic flavour tokens. */
+ public static final String LOGIC_FLAVOUR_REGEXP = "[A-Za-z0-9\\-_]+";
+
+ /** When logic flavour is undefined, it has this value. */
+ public static final String LOGIC_FLAVOUR_UNDEFINED = "UNDEFINED";
+
+ /** The maximum permissible size of a logic definition. */
+ public static final int MAX_LOGIC_SIZE = 32672; // The maximum size supported by Apache Derby
+
+ private AxReferenceKey key;
+ private String logicFlavour;
+ private String logic;
+
+ /**
+ * The Default Constructor creates a logic instance with a null key, undefined logic flavour and a null logic
+ * string.
+ */
+ public AxLogic() {
+ this(new AxReferenceKey());
+ logicFlavour = LOGIC_FLAVOUR_UNDEFINED;
+ }
+
+ /**
+ * Copy constructor.
+ *
+ * @param copyConcept the concept to copy from
+ */
+ public AxLogic(final AxLogic copyConcept) {
+ super(copyConcept);
+ }
+
+ /**
+ * The Key Constructor creates a logic instance with the given reference key, undefined logic flavour and a null
+ * logic string.
+ *
+ * @param key the reference key of the logic
+ */
+ public AxLogic(final AxReferenceKey key) {
+ this(key, LOGIC_FLAVOUR_UNDEFINED, "");
+ }
+
+ /**
+ * This Constructor creates a logic instance with a reference key constructed from the parents key and the logic
+ * local name and all of its fields defined.
+ *
+ * @param parentKey the reference key of the parent of this logic
+ * @param logicName the logic name, held as the local name of the reference key of this logic
+ * @param logicFlavour the flavour of this logic
+ * @param logic the actual logic as a string
+ */
+ public AxLogic(final AxReferenceKey parentKey, final String logicName, final String logicFlavour,
+ final String logic) {
+ this(new AxReferenceKey(parentKey, logicName), logicFlavour, logic);
+ }
+
+ /**
+ * This Constructor creates a logic instance with the given reference key and all of its fields defined.
+ *
+ * @param key the reference key of this logic
+ * @param logicFlavour the flavour of this logic
+ * @param logic the actual logic as a string
+ */
+ public AxLogic(final AxReferenceKey key, final String logicFlavour, final String logic) {
+ super();
+ Assertions.argumentNotNull(key, KEY_NULL_MESSAGE);
+ Assertions.argumentNotNull(logicFlavour, LOGIC_FLAVOUR_NULL_MESSAGE);
+ Assertions.argumentNotNull(logic, LOGIC_NULL_MESSAGE);
+
+ this.key = key;
+ this.logicFlavour = Assertions.validateStringParameter(LOGIC_FLAVOUR_TOKEN, logicFlavour, LOGIC_FLAVOUR_REGEXP);
+ this.logic = logic.replaceAll(WHITESPACE_REGEXP, "");
+ }
+
+ /**
+ * This Constructor creates a logic instance with the given reference key and logic flavour, the logic is provided
+ * by the given logic reader instance.
+ *
+ * @param key the reference key of this logic
+ * @param logicFlavour the flavour of this logic
+ * @param logicReader the logic reader to use to read the logic for this logic instance
+ */
+ public AxLogic(final AxReferenceKey key, final String logicFlavour, final AxLogicReader logicReader) {
+ super();
+ Assertions.argumentNotNull(key, KEY_NULL_MESSAGE);
+ Assertions.argumentNotNull(logicFlavour, LOGIC_FLAVOUR_NULL_MESSAGE);
+ Assertions.argumentNotNull(logicReader, "logicReader may not be null");
+
+ this.key = key;
+ this.logicFlavour = Assertions.validateStringParameter(LOGIC_FLAVOUR_TOKEN, logicFlavour, LOGIC_FLAVOUR_REGEXP);
+ logic = logicReader.readLogic(this);
+ }
+
+ /**
+ * {@inheritDoc}.
+ */
+ @Override
+ public AxReferenceKey getKey() {
+ return key;
+ }
+
+ /**
+ * {@inheritDoc}.
+ */
+ @Override
+ public List<AxKey> getKeys() {
+ return key.getKeys();
+ }
+
+ /**
+ * Sets the key.
+ *
+ * @param key the key
+ */
+ public void setKey(final AxReferenceKey key) {
+ Assertions.argumentNotNull(key, KEY_NULL_MESSAGE);
+ this.key = key;
+ }
+
+ /**
+ * Gets the logic flavour.
+ *
+ * @return the logic flavour
+ */
+ public String getLogicFlavour() {
+ return logicFlavour;
+ }
+
+ /**
+ * Sets the logic flavour.
+ *
+ * @param logicFlavour the logic flavour
+ */
+ public void setLogicFlavour(final String logicFlavour) {
+ this.logicFlavour = Assertions.validateStringParameter(LOGIC_FLAVOUR_TOKEN, logicFlavour, LOGIC_FLAVOUR_REGEXP);
+ }
+
+ /**
+ * Gets the logic.
+ *
+ * @return the logic
+ */
+ public String getLogic() {
+ return logic;
+ }
+
+ /**
+ * Sets the logic.
+ *
+ * @param logic the logic
+ */
+ public void setLogic(final String logic) {
+ Assertions.argumentNotNull(logic, LOGIC_NULL_MESSAGE);
+ this.logic = logic.replaceAll(WHITESPACE_REGEXP, "");
+ }
+
+ /**
+ * {@inheritDoc}.
+ */
+ @Override
+ public AxValidationResult validate(final AxValidationResult resultIn) {
+ AxValidationResult result = resultIn;
+
+ if (key.equals(AxReferenceKey.getNullKey())) {
+ result.addValidationMessage(new AxValidationMessage(key, this.getClass(), ValidationResult.INVALID,
+ "key is a null key"));
+ }
+
+ result = key.validate(result);
+
+ if (logicFlavour.replaceAll(WHITESPACE_REGEXP, "").length() == 0
+ || logicFlavour.equals(LOGIC_FLAVOUR_UNDEFINED)) {
+ result.addValidationMessage(new AxValidationMessage(key, this.getClass(), ValidationResult.INVALID,
+ "logic flavour is not defined"));
+ }
+
+ String flavourValidationString = Assertions.getStringParameterValidationMessage(LOGIC_FLAVOUR_TOKEN,
+ logicFlavour, LOGIC_FLAVOUR_REGEXP);
+ if (flavourValidationString != null) {
+ result.addValidationMessage(new AxValidationMessage(key, this.getClass(), ValidationResult.INVALID,
+ "logic flavour invalid-" + flavourValidationString));
+ }
+
+ if (logic.replaceAll(WHITESPACE_REGEXP, "").length() == 0) {
+ result.addValidationMessage(new AxValidationMessage(key, this.getClass(), ValidationResult.INVALID,
+ "no logic specified, logic may not be blank"));
+ }
+
+ return result;
+ }
+
+ /**
+ * {@inheritDoc}.
+ */
+ @Override
+ public void clean() {
+ if (key != null) {
+ key.clean();
+ }
+ logicFlavour = Assertions.validateStringParameter(LOGIC_FLAVOUR_TOKEN, logicFlavour, LOGIC_FLAVOUR_REGEXP);
+ logic = logic.replaceAll(WHITESPACE_REGEXP, "");
+ }
+
+ /**
+ * {@inheritDoc}.
+ */
+ @Override
+ public String toString() {
+ final StringBuilder builder = new StringBuilder();
+ builder.append(this.getClass().getSimpleName());
+ builder.append(":(");
+ builder.append("key=");
+ builder.append(key);
+ builder.append(",logicFlavour=");
+ builder.append(logicFlavour);
+ builder.append(",logic=");
+ builder.append(logic);
+ builder.append(")");
+ return builder.toString();
+ }
+
+ /**
+ * {@inheritDoc}.
+ */
+ @Override
+ public AxConcept copyTo(final AxConcept targetObject) {
+ Assertions.argumentNotNull(targetObject, "target may not be null");
+
+ final Object copyObject = targetObject;
+ Assertions.instanceOf(copyObject, AxLogic.class);
+
+ final AxLogic copy = ((AxLogic) copyObject);
+ copy.setKey(new AxReferenceKey(key));
+ copy.setLogicFlavour(logicFlavour);
+ copy.setLogic(logic);
+
+ return copy;
+ }
+
+ /**
+ * {@inheritDoc}.
+ */
+ @Override
+ public int hashCode() {
+ final int prime = 31;
+ int result = 1;
+ result = prime * result + key.hashCode();
+ result = prime * result + logicFlavour.hashCode();
+ result = prime * result + logic.hashCode();
+ return result;
+ }
+
+ /**
+ * {@inheritDoc}.
+ */
+ @Override
+ public boolean equals(final Object obj) {
+ if (obj == null) {
+ return false;
+ }
+ if (this == obj) {
+ return true;
+ }
+ if (getClass() != obj.getClass()) {
+ return false;
+ }
+
+ final AxLogic other = (AxLogic) obj;
+ if (!key.equals(other.key)) {
+ return false;
+ }
+ if (!logicFlavour.equals(other.logicFlavour)) {
+ return false;
+ }
+ return logic.equals(other.logic);
+ }
+
+ /**
+ * {@inheritDoc}.
+ */
+ @Override
+ public int compareTo(final AxConcept otherObj) {
+ if (otherObj == null) {
+ return -1;
+ }
+ if (this == otherObj) {
+ return 0;
+ }
+ if (getClass() != otherObj.getClass()) {
+ return this.hashCode() - otherObj.hashCode();
+ }
+
+ final AxLogic other = (AxLogic) otherObj;
+ if (!key.equals(other.key)) {
+ return key.compareTo(other.key);
+ }
+ if (!logicFlavour.equals(other.logicFlavour)) {
+ return logicFlavour.compareTo(other.logicFlavour);
+ }
+ return logic.compareTo(other.logic);
+ }
+}
diff --git a/model/src/main/java/org/onap/policy/apex/model/policymodel/concepts/AxLogicReader.java b/model/src/main/java/org/onap/policy/apex/model/policymodel/concepts/AxLogicReader.java
new file mode 100644
index 000000000..7532f9f34
--- /dev/null
+++ b/model/src/main/java/org/onap/policy/apex/model/policymodel/concepts/AxLogicReader.java
@@ -0,0 +1,74 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * Copyright (C) 2016-2018 Ericsson. All rights reserved.
+ * ================================================================================
+ * 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.apex.model.policymodel.concepts;
+
+/**
+ * This interface is used to provide logic to a {@link AxLogic} instance. Implementations usually
+ * store logic on disk in a structure similar to Java package naming conventions. The logic package
+ * gives the package path, a directory where a set of logic is defined. Default logic is logic that
+ * can be used as dummy logic in tasks or states that are filler tasks or states. The actual logic
+ * is returned by the {@code readLogic()} method. The interface is used mainly by unit test classes
+ * that generate test logic.
+ *
+ * @author Liam Fallon (liam.fallon@ericsson.com)
+ */
+public interface AxLogicReader {
+
+ /**
+ * Get the logic package path.
+ *
+ * @return the logic package path
+ */
+ String getLogicPackage();
+
+ /**
+ * Set the logic package path.
+ *
+ * @param logicPackage the name of the package that contains the logic for this logic reader
+ * @return the logic reader on which this method was called, used for daisy chaining
+ * configuration
+ */
+ AxLogicReader setLogicPackage(final String logicPackage);
+
+ /**
+ * Get the default logic name.
+ *
+ * @return the default logic name
+ */
+ String getDefaultLogic();
+
+ /**
+ * Set the default logic name.
+ *
+ * @param defaultLogic the default logic name
+ * @return the logic reader on which this method was called, used for daisy chaining
+ * configuration
+ */
+ AxLogicReader setDefaultLogic(final String defaultLogic);
+
+ /**
+ * Read the logic for an AxLogic object.
+ *
+ * @param axLogic the AxLogic object
+ * @return the logic as a string
+ */
+ String readLogic(final AxLogic axLogic);
+}
diff --git a/model/src/main/java/org/onap/policy/apex/model/policymodel/concepts/AxPolicies.java b/model/src/main/java/org/onap/policy/apex/model/policymodel/concepts/AxPolicies.java
new file mode 100644
index 000000000..bb30f46cf
--- /dev/null
+++ b/model/src/main/java/org/onap/policy/apex/model/policymodel/concepts/AxPolicies.java
@@ -0,0 +1,360 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * Copyright (C) 2016-2018 Ericsson. All rights reserved.
+ * Modifications Copyright (C) 2019-2020,2022 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.apex.model.policymodel.concepts;
+
+import java.util.List;
+import java.util.Map;
+import java.util.Map.Entry;
+import java.util.NavigableMap;
+import java.util.Set;
+import java.util.TreeMap;
+import org.onap.policy.apex.model.basicmodel.concepts.AxArtifactKey;
+import org.onap.policy.apex.model.basicmodel.concepts.AxConcept;
+import org.onap.policy.apex.model.basicmodel.concepts.AxConceptGetter;
+import org.onap.policy.apex.model.basicmodel.concepts.AxConceptGetterImpl;
+import org.onap.policy.apex.model.basicmodel.concepts.AxKey;
+import org.onap.policy.apex.model.basicmodel.concepts.AxValidationMessage;
+import org.onap.policy.apex.model.basicmodel.concepts.AxValidationResult;
+import org.onap.policy.apex.model.basicmodel.concepts.AxValidationResult.ValidationResult;
+import org.onap.policy.common.utils.validation.Assertions;
+
+/**
+ * This class is a policy container and holds a map of the policies for an entire Apex model. All Apex models that use
+ * policies must have an {@link AxPolicies} field. The {@link AxPolicies} class implements the helper methods of the
+ * {@link AxConceptGetter} interface to allow {@link AxPolicy} instances to be retrieved by calling methods directly on
+ * this class without referencing the contained map.
+ *
+ * <p>Validation checks that the container key is not null. An error is issued if no policies are defined in the
+ * container. Each policy entry is checked to ensure that its key and value are not null and that the key matches the
+ * key in the map value. Each policy entry is then validated individually.
+ */
+public class AxPolicies extends AxConcept implements AxConceptGetter<AxPolicy> {
+ private static final long serialVersionUID = 4290442590545820316L;
+
+ private AxArtifactKey key;
+ private Map<AxArtifactKey, AxPolicy> policyMap;
+
+ /**
+ * The Default Constructor creates a {@link AxPolicies} object with a null artifact key and creates an empty event
+ * map.
+ */
+ public AxPolicies() {
+ this(new AxArtifactKey());
+ }
+
+ /**
+ * The Key Constructor creates a {@link AxPolicies} object with the given artifact key and creates an empty event
+ * map.
+ *
+ * @param key the key
+ */
+ public AxPolicies(final AxArtifactKey key) {
+ this(key, new TreeMap<>());
+ }
+
+ /**
+ * Copy constructor.
+ *
+ * @param copyConcept the concept to copy from
+ */
+ public AxPolicies(final AxPolicies copyConcept) {
+ super(copyConcept);
+ }
+
+ /**
+ * This Constructor creates a policy container with all of its fields defined.
+ *
+ * @param key the policy container key
+ * @param policyMap the policies to be stored in the policy container
+ */
+ public AxPolicies(final AxArtifactKey key, final Map<AxArtifactKey, AxPolicy> policyMap) {
+ super();
+ Assertions.argumentNotNull(key, "key may not be null");
+ Assertions.argumentNotNull(policyMap, "policyMap may not be null");
+
+ this.key = key;
+ this.policyMap = new TreeMap<>();
+ this.policyMap.putAll(policyMap);
+ }
+
+ /**
+ * {@inheritDoc}.
+ */
+ @Override
+ public AxArtifactKey getKey() {
+ return key;
+ }
+
+ /**
+ * {@inheritDoc}.
+ */
+ @Override
+ public List<AxKey> getKeys() {
+ final List<AxKey> keyList = key.getKeys();
+
+ for (final AxPolicy policy : policyMap.values()) {
+ keyList.addAll(policy.getKeys());
+ }
+
+ return keyList;
+ }
+
+ /**
+ * {@inheritDoc}.
+ */
+ @Override
+ public void buildReferences() {
+ policyMap.entrySet().stream().forEach(policyEntry -> {
+ policyEntry.getValue().setKey(policyEntry.getKey());
+ policyEntry.getValue().buildReferences();
+ }
+ );
+ }
+
+ /**
+ * Sets the key of the policy container.
+ *
+ * @param key the policy container key
+ */
+ public void setKey(final AxArtifactKey key) {
+ Assertions.argumentNotNull(key, "key may not be null");
+ this.key = key;
+ }
+
+ /**
+ * Gets the policy map containing all policies in the policy container.
+ *
+ * @return the policy map with all the policies in the container
+ */
+ public Map<AxArtifactKey, AxPolicy> getPolicyMap() {
+ return policyMap;
+ }
+
+ /**
+ * Sets the policy map containing all policies in the policy container.
+ *
+ * @param policyMap the policy map with all the policies to be put in the container
+ */
+ public void setPolicyMap(final Map<AxArtifactKey, AxPolicy> policyMap) {
+ Assertions.argumentNotNull(policyMap, "policyMap may not be null");
+ this.policyMap = new TreeMap<>();
+ this.policyMap.putAll(policyMap);
+ }
+
+ /**
+ * {@inheritDoc}.
+ */
+ @Override
+ public AxValidationResult validate(final AxValidationResult resultIn) {
+ AxValidationResult result = resultIn;
+
+ if (key.equals(AxArtifactKey.getNullKey())) {
+ result.addValidationMessage(new AxValidationMessage(key, this.getClass(), ValidationResult.INVALID,
+ "key is a null key"));
+ }
+
+ result = key.validate(result);
+
+ if (policyMap.size() == 0) {
+ result.addValidationMessage(new AxValidationMessage(key, this.getClass(), ValidationResult.INVALID,
+ "policyMap may not be empty"));
+ } else {
+ for (final Entry<AxArtifactKey, AxPolicy> policyEntry : policyMap.entrySet()) {
+ final AxArtifactKey entryKey = policyEntry.getKey();
+ if (entryKey.equals(AxArtifactKey.getNullKey())) {
+ result.addValidationMessage(new AxValidationMessage(key, this.getClass(), ValidationResult.INVALID,
+ "key on policy entry " + entryKey + " may not be the null key"));
+ } else if (policyEntry.getValue() == null) {
+ result.addValidationMessage(new AxValidationMessage(key, this.getClass(), ValidationResult.INVALID,
+ "value on policy entry " + entryKey + " may not be null"));
+ } else {
+ validate(result, policyEntry, entryKey);
+ result = policyEntry.getValue().validate(result);
+ }
+ }
+ }
+
+ return result;
+ }
+
+ private void validate(final AxValidationResult result, final Entry<AxArtifactKey, AxPolicy> policyEntry,
+ final AxArtifactKey entryKey) {
+ if (!entryKey.equals(policyEntry.getValue().getKey())) {
+ result.addValidationMessage(new AxValidationMessage(key, this.getClass(), ValidationResult.INVALID,
+ "key on policy entry key " + entryKey + " does not equal policy value key "
+ + policyEntry.getValue().getKey()));
+ }
+ }
+
+ /**
+ * {@inheritDoc}.
+ */
+ @Override
+ public void clean() {
+ key.clean();
+ for (final Entry<AxArtifactKey, AxPolicy> policyEntry : policyMap.entrySet()) {
+ policyEntry.getKey().clean();
+ policyEntry.getValue().clean();
+ }
+ }
+
+ /**
+ * {@inheritDoc}.
+ */
+ @Override
+ public String toString() {
+ final StringBuilder builder = new StringBuilder();
+ builder.append(this.getClass().getSimpleName());
+ builder.append(":(");
+ builder.append("key=");
+ builder.append(key);
+ builder.append(",policyMap=");
+ builder.append(policyMap);
+ builder.append(")");
+ return builder.toString();
+ }
+
+ /**
+ * {@inheritDoc}.
+ */
+ @Override
+ public AxConcept copyTo(final AxConcept targetObject) {
+ Assertions.argumentNotNull(targetObject, "target may not be null");
+
+ final Object copyObject = targetObject;
+ Assertions.instanceOf(copyObject, AxPolicies.class);
+
+ final AxPolicies copy = ((AxPolicies) copyObject);
+ copy.setKey(new AxArtifactKey(key));
+
+ final Map<AxArtifactKey, AxPolicy> newPolicyMap = new TreeMap<>();
+ for (final Entry<AxArtifactKey, AxPolicy> policyMapEntry : policyMap.entrySet()) {
+ newPolicyMap.put(new AxArtifactKey(policyMapEntry.getKey()), new AxPolicy(policyMapEntry.getValue()));
+ }
+ copy.setPolicyMap(newPolicyMap);
+
+ return copy;
+ }
+
+ /**
+ * {@inheritDoc}.
+ */
+ @Override
+ public int hashCode() {
+ final int prime = 31;
+ int result = 1;
+ result = prime * result + key.hashCode();
+ result = prime * result + policyMap.hashCode();
+ return result;
+ }
+
+ /**
+ * {@inheritDoc}.
+ */
+ @Override
+ public boolean equals(final Object obj) {
+ if (obj == null) {
+ return false;
+ }
+ if (this == obj) {
+ return true;
+ }
+
+ if (getClass() != obj.getClass()) {
+ return false;
+ }
+
+ final AxPolicies other = (AxPolicies) obj;
+ if (!key.equals(other.key)) {
+ return false;
+ }
+ return policyMap.equals(other.policyMap);
+ }
+
+ /**
+ * {@inheritDoc}.
+ */
+ @Override
+ public int compareTo(final AxConcept otherObj) {
+ if (otherObj == null) {
+ return -1;
+ }
+ if (this == otherObj) {
+ return 0;
+ }
+ if (getClass() != otherObj.getClass()) {
+ return this.hashCode() - otherObj.hashCode();
+ }
+
+ final AxPolicies other = (AxPolicies) otherObj;
+ if (!key.equals(other.key)) {
+ return key.compareTo(other.key);
+ }
+ if (!policyMap.equals(other.policyMap)) {
+ return (policyMap.hashCode() - other.policyMap.hashCode());
+ }
+
+ return 0;
+ }
+
+ /**
+ * {@inheritDoc}.
+ */
+ @Override
+ public AxPolicy get(final AxArtifactKey conceptKey) {
+ return new AxConceptGetterImpl<>((NavigableMap<AxArtifactKey, AxPolicy>) policyMap).get(conceptKey);
+ }
+
+ /**
+ * {@inheritDoc}.
+ */
+ @Override
+ public AxPolicy get(final String conceptKeyName) {
+ return new AxConceptGetterImpl<>((NavigableMap<AxArtifactKey, AxPolicy>) policyMap).get(conceptKeyName);
+ }
+
+ /**
+ * {@inheritDoc}.
+ */
+ @Override
+ public AxPolicy get(final String conceptKeyName, final String conceptKeyVersion) {
+ return new AxConceptGetterImpl<>((NavigableMap<AxArtifactKey, AxPolicy>) policyMap).get(conceptKeyName,
+ conceptKeyVersion);
+ }
+
+ /**
+ * {@inheritDoc}.
+ */
+ @Override
+ public Set<AxPolicy> getAll(final String conceptKeyName) {
+ return new AxConceptGetterImpl<>((NavigableMap<AxArtifactKey, AxPolicy>) policyMap).getAll(conceptKeyName);
+ }
+
+ /**
+ * {@inheritDoc}.
+ */
+ @Override
+ public Set<AxPolicy> getAll(final String conceptKeyName, final String conceptKeyVersion) {
+ return new AxConceptGetterImpl<>((NavigableMap<AxArtifactKey, AxPolicy>) policyMap).getAll(conceptKeyName,
+ conceptKeyVersion);
+ }
+}
diff --git a/model/src/main/java/org/onap/policy/apex/model/policymodel/concepts/AxPolicy.java b/model/src/main/java/org/onap/policy/apex/model/policymodel/concepts/AxPolicy.java
new file mode 100644
index 000000000..a47afe4d6
--- /dev/null
+++ b/model/src/main/java/org/onap/policy/apex/model/policymodel/concepts/AxPolicy.java
@@ -0,0 +1,508 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * Copyright (C) 2016-2018 Ericsson. All rights reserved.
+ * Modifications Copyright (C) 2019-2020,2022 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.apex.model.policymodel.concepts;
+
+import com.google.gson.annotations.SerializedName;
+import java.util.List;
+import java.util.Map;
+import java.util.Map.Entry;
+import java.util.Set;
+import java.util.TreeMap;
+import java.util.TreeSet;
+import org.onap.policy.apex.model.basicmodel.concepts.AxArtifactKey;
+import org.onap.policy.apex.model.basicmodel.concepts.AxConcept;
+import org.onap.policy.apex.model.basicmodel.concepts.AxKey;
+import org.onap.policy.apex.model.basicmodel.concepts.AxReferenceKey;
+import org.onap.policy.apex.model.basicmodel.concepts.AxValidationMessage;
+import org.onap.policy.apex.model.basicmodel.concepts.AxValidationResult;
+import org.onap.policy.apex.model.basicmodel.concepts.AxValidationResult.ValidationResult;
+import org.onap.policy.common.utils.validation.Assertions;
+import org.slf4j.ext.XLogger;
+import org.slf4j.ext.XLoggerFactory;
+
+/**
+ * This class holds the definition of an Apex policy. A policy is made up of a tree of states, each represented by an
+ * {@link AxState} instance. The states of a policy are held in a map in the policy. The state tree is built up at
+ * policy design time by a policy editor and each state is connected to its next state by an {@link AxStateOutput}
+ * instance.
+ *
+ * <p>Execution of a policy is triggered by an event. A policy starts execution from its first state so the trigger
+ * event for the first sate is the trigger event for the entire policy. Execution from that first state can continue to
+ * one or more subsequent states and so on down branches of states. The state output of the final state in a branch has
+ * no next state, indicating the end of execution of that branch. Therefore, the set of output events from final states
+ * in the policy are the possible set of output events on the policy. A state may only be used once in the state tree of
+ * a policy and recursive execution of states in the same execution branch is not allowed, so the same state may not
+ * execute more than once on a single execution of a policy.
+ *
+ * <p>The template of a policy is a string that can be used by policy editors to store meta information on the policy
+ * that can be used at design time. The policy template string is not used during policy execution.
+ *
+ * <p>During validation of a policy, the validation checks listed below are executed:
+ * <ol>
+ * <li>The policy key must not
+ * be a null key
+ * <li>The policy key must be valid
+ * <li>If the policy template is not set, an observation is issued
+ * <li>At
+ * least one state must be defined
+ * <li>Keys and values must all be defined, that is not null
+ * <li>The key on each entry
+ * in the state map must match the key in the entry's value
+ * <li>The parent key of each state in the state map of a
+ * policy must be the key of that policy
+ * <li>Each state must itself be valid, see validation in {@link AxState}
+ * <li>The
+ * next state of the state output of each state must be defined as a state in the policy
+ * <li>The first state of a policy
+ * must be set
+ * <li>The first state of a policy must be defined in the policy
+ * <li>If a state is defined but is not used
+ * in a policy,a warning is issued
+ * <li>The state tree of the policy must be valid, see validation in {@link AxStateTree}
+ * </ol>
+ */
+public class AxPolicy extends AxConcept {
+ private static final long serialVersionUID = -1775614096390365941L;
+
+ // Logger for this class
+ private static final XLogger LOGGER = XLoggerFactory.getXLogger(AxPolicy.class);
+
+ @SerializedName("policyKey")
+ private AxArtifactKey key;
+ private String template;
+
+ @SerializedName("state")
+ private Map<String, AxState> stateMap;
+ private String firstState;
+
+ /**
+ * The Default Constructor creates a policy instance with a null key, a blank template and undefined first state.
+ */
+ public AxPolicy() {
+ this(new AxArtifactKey());
+ }
+
+ /**
+ * Copy constructor.
+ *
+ * @param copyConcept the concept to copy from
+ */
+ public AxPolicy(final AxPolicy copyConcept) {
+ super(copyConcept);
+ }
+
+ /**
+ * The Key Constructor creates a policy instance with the given key, a blank template and undefined first state.
+ *
+ * @param key the key of the policy
+ */
+ public AxPolicy(final AxArtifactKey key) {
+ this(key,
+ "",
+ new TreeMap<>(),
+ "");
+ }
+
+ /**
+ * This Constructor creates a policy with the given key and all its fields defined.
+ *
+ * @param key the key of the policy
+ * @param template the policy template for policy editor metadata
+ * @param stateMap the state map containing the states of the policy
+ * @param firstState the first state that will execute on this policy
+ */
+ public AxPolicy(final AxArtifactKey key, final String template, final Map<String, AxState> stateMap,
+ final String firstState) {
+ super();
+ Assertions.argumentNotNull(key, "key may not be null");
+ Assertions.argumentNotNull(template, "template may not be null");
+ Assertions.argumentNotNull(stateMap, "stateMap may not be null");
+ Assertions.argumentNotNull(firstState, "firstState may not be null");
+
+ this.key = key;
+ this.template = template;
+ this.stateMap = stateMap;
+ this.firstState = firstState;
+ }
+
+ /**
+ * Gets a tree that holds all the possible execution paths for this policy. This method may be used for verification
+ * of policies, to find the branches of policy execution and the final states of policies.
+ *
+ * @return the state tree of the policy, a tree representing the execution branches of the policy
+ */
+ public AxStateTree getStateTree() {
+ return new AxStateTree(this, stateMap.get(firstState), null);
+ }
+
+ /**
+ * {@inheritDoc}.
+ */
+ @Override
+ public AxArtifactKey getKey() {
+ return key;
+ }
+
+ /**
+ * {@inheritDoc}.
+ */
+ @Override
+ public List<AxKey> getKeys() {
+ final List<AxKey> keyList = key.getKeys();
+ for (final AxState state : stateMap.values()) {
+ keyList.addAll(state.getKeys());
+ }
+ return keyList;
+ }
+
+ /**
+ * {@inheritDoc}.
+ */
+ @Override
+ public void buildReferences() {
+ stateMap.entrySet().stream().forEach(stateEntry -> {
+ if (!stateEntry.getValue().getKey().getLocalName().equals(AxKey.NULL_KEY_NAME)) {
+ stateEntry.getValue().getKey().setParentArtifactKey(key);
+ }
+ stateEntry.getValue().buildReferences();
+ }
+ );
+ }
+
+ /**
+ * Sets the key of the policy.
+ *
+ * @param key the key of the policy
+ */
+ public void setKey(final AxArtifactKey key) {
+ Assertions.argumentNotNull(key, "key may not be null");
+ this.key = key;
+ }
+
+ /**
+ * Gets the policy template for policy editor metadata.
+ *
+ * @return the policy template for policy editor metadata
+ */
+ public String getTemplate() {
+ return template;
+ }
+
+ /**
+ * Sets the policy template for policy editor metadata.
+ *
+ * @param template the policy template for policy editor metadata
+ */
+ public void setTemplate(final String template) {
+ Assertions.argumentNotNull(template, "template may not be null");
+ this.template = template;
+ }
+
+ /**
+ * Gets a map containing the states of the policy.
+ *
+ * @return the map of states in the policy
+ */
+ public Map<String, AxState> getStateMap() {
+ return stateMap;
+ }
+
+ /**
+ * Sets a map containing the states of the policy.
+ *
+ * @param stateMap a map of states in the policy
+ */
+ public void setStateMap(final Map<String, AxState> stateMap) {
+ Assertions.argumentNotNull(stateMap, "stateMap may not be null");
+ this.stateMap = stateMap;
+ }
+
+ /**
+ * Gets the first state of the policy.
+ *
+ * @return the first state of the policy
+ */
+ public String getFirstState() {
+ return firstState;
+ }
+
+ /**
+ * Sets the first state of the policy.
+ *
+ * @param firstState the first state of the policy
+ */
+ public void setFirstState(final String firstState) {
+ Assertions.argumentNotNull(firstState, "firstState may not be null");
+ this.firstState = firstState;
+ }
+
+ /**
+ * {@inheritDoc}.
+ */
+ @Override
+ public AxValidationResult validate(final AxValidationResult resultIn) {
+ AxValidationResult result = resultIn;
+
+ if (key.equals(AxArtifactKey.getNullKey())) {
+ result.addValidationMessage(
+ new AxValidationMessage(key, this.getClass(), ValidationResult.INVALID, "key is a null key"));
+ }
+
+ result = key.validate(result);
+
+ if (template.trim().length() == 0) {
+ result.addValidationMessage(new AxValidationMessage(key, this.getClass(), ValidationResult.OBSERVATION,
+ "a policy template has not been specified"));
+ }
+
+ if (stateMap.size() == 0) {
+ result.addValidationMessage(
+ new AxValidationMessage(key, this.getClass(), ValidationResult.INVALID,
+ "stateMap may not be empty"));
+ } else {
+ for (final Entry<String, AxState> stateEntry : stateMap.entrySet()) {
+ result = validateStateEntry(stateEntry, result);
+ }
+ }
+
+ // Validation continues from this point only if all validation checks this far have been
+ // passed
+ if (!result.isOk()) {
+ return result;
+ }
+
+ // We only check the unused states on models validated this far
+ if (firstState.trim().length() == 0) {
+ result.addValidationMessage(new AxValidationMessage(key, this.getClass(), ValidationResult.INVALID,
+ "no first state specified, first state may not be blank"));
+ } else {
+ if (!stateMap.containsKey(firstState)) {
+ result.addValidationMessage(new AxValidationMessage(key, this.getClass(), ValidationResult.INVALID,
+ "first state not found in stateMap"));
+ } else {
+ validateStateTree(result);
+ }
+ }
+
+ return result;
+ }
+
+ /**
+ * Validate a state entry.
+ *
+ * @param stateEntry the state entry to validate
+ * @param result The validation result to append to
+ * @return The result of the validation
+ */
+ private AxValidationResult validateStateEntry(final Entry<String, AxState> stateEntry, AxValidationResult
+ result) {
+ if (stateEntry.getKey() == null || stateEntry.getKey().equals(AxKey.NULL_KEY_NAME)) {
+ result.addValidationMessage(new AxValidationMessage(key, this.getClass(), ValidationResult.INVALID,
+ "key on state entry key " + stateEntry.getKey() + " may not be the null key"));
+ return result;
+ }
+
+ if (stateEntry.getValue() == null) {
+ result.addValidationMessage(new AxValidationMessage(key, this.getClass(), ValidationResult.INVALID,
+ "value on state entry value " + stateEntry.getKey() + " may not be null"));
+ return result;
+ }
+
+ if (!stateEntry.getKey().equals(stateEntry.getValue().getKey().getLocalName())) {
+ result.addValidationMessage(new AxValidationMessage(key, this.getClass(), ValidationResult.INVALID,
+ "key on state entry key " + stateEntry.getKey() + " does not equal state entry value local name "
+ + stateEntry.getValue().getKey().getLocalName()));
+ }
+
+ if (!stateEntry.getValue().getKey().getParentArtifactKey().equals(key)) {
+ result.addValidationMessage(new AxValidationMessage(key, this.getClass(), ValidationResult.INVALID,
+ "parent key on state entry key " + stateEntry.getValue().getKey() + " does not equal policy key"));
+ }
+
+ result = stateEntry.getValue().validate(result);
+
+ for (final AxStateOutput stateOutput : stateEntry.getValue().getStateOutputs().values()) {
+ if (!stateOutput.getNextState().equals(AxReferenceKey.getNullKey())
+ && !stateMap.containsKey(stateOutput.getNextState().getLocalName())) {
+ result.addValidationMessage(
+ new AxValidationMessage(key, this.getClass(), ValidationResult.INVALID, " nextState of state "
+ + stateEntry.getKey() + " not found in StateMap: " + stateOutput.getNextState().getId()));
+ }
+ }
+
+ return result;
+ }
+
+ /**
+ * Validate a state tree to ensure there are no circular references in it.
+ *
+ * @param result The validation result to append to
+ * @return The result of the validation
+ */
+ private AxValidationResult validateStateTree(AxValidationResult result) {
+ try {
+ // Constructor validates policy state tree
+ AxStateTree policyStateTree = getStateTree();
+
+ // Check for unused states
+ final Set<AxState> referencedStateSet = policyStateTree.getReferencedStateSet();
+ final Set<AxState> unreferencedStateSet = new TreeSet<>(stateMap.values());
+ unreferencedStateSet.removeAll(referencedStateSet);
+
+ for (final AxState unreferencedState : unreferencedStateSet) {
+ result.addValidationMessage(new AxValidationMessage(key, this.getClass(), ValidationResult.WARNING,
+ "state " + unreferencedState.getKey() + " is not referenced in the policy execution tree"));
+ }
+ } catch (PolicyRuntimeException pre) {
+ AxValidationMessage validationMessage = new AxValidationMessage(key, this.getClass(),
+ ValidationResult.WARNING, "state tree in policy is invalid");
+ LOGGER.trace(validationMessage.getMessage(), pre);
+ result.addValidationMessage(validationMessage);
+ }
+
+ return result;
+ }
+
+ /**
+ * {@inheritDoc}.
+ */
+ @Override
+ public void clean() {
+ key.clean();
+ firstState = firstState.trim();
+ }
+
+ /**
+ * {@inheritDoc}.
+ */
+ @Override
+ public String toString() {
+ final StringBuilder builder = new StringBuilder();
+ builder.append(this.getClass().getSimpleName());
+ builder.append(":(");
+ builder.append("key=");
+ builder.append(key);
+ builder.append(",template=");
+ builder.append(template);
+ builder.append(",stateMap=");
+ builder.append(stateMap);
+ builder.append(",firstState=");
+ builder.append(firstState);
+ builder.append(")");
+ return builder.toString();
+ }
+
+ /**
+ * {@inheritDoc}.
+ */
+ @Override
+ public AxConcept copyTo(final AxConcept targetObject) {
+ Assertions.argumentNotNull(targetObject, "target may not be null");
+
+ final Object copyObject = targetObject;
+ Assertions.instanceOf(copyObject, AxPolicy.class);
+
+ final AxPolicy copy = ((AxPolicy) copyObject);
+ copy.setKey(new AxArtifactKey(key));
+ copy.setTemplate(template);
+
+ final Map<String, AxState> newStateMap = new TreeMap<>();
+ for (final Entry<String, AxState> stateMapEntry : stateMap.entrySet()) {
+ newStateMap.put(stateMapEntry.getKey(), new AxState(stateMapEntry.getValue()));
+ }
+ copy.setStateMap(newStateMap);
+
+ copy.setFirstState(firstState);
+
+ return copy;
+ }
+
+ /**
+ * {@inheritDoc}.
+ */
+ @Override
+ public int hashCode() {
+ final int prime = 31;
+ int result = 1;
+ result = prime * result + key.hashCode();
+ result = prime * result + template.hashCode();
+ result = prime * result + stateMap.hashCode();
+ result = prime * result + firstState.hashCode();
+ return result;
+ }
+
+ /**
+ * {@inheritDoc}.
+ */
+ @Override
+ public boolean equals(final Object obj) {
+ if (obj == null) {
+ return false;
+ }
+ if (this == obj) {
+ return true;
+ }
+ if (getClass() != obj.getClass()) {
+ return false;
+ }
+
+ final AxPolicy other = (AxPolicy) obj;
+ if (!key.equals(other.key)) {
+ return false;
+ }
+ if (!template.equals(other.template)) {
+ return false;
+ }
+ if (!stateMap.equals(other.stateMap)) {
+ return false;
+ }
+ return firstState.equals(other.firstState);
+ }
+
+ /**
+ * {@inheritDoc}.
+ */
+ @Override
+ public int compareTo(final AxConcept otherObj) {
+ if (otherObj == null) {
+ return -1;
+ }
+ if (this == otherObj) {
+ return 0;
+ }
+ if (getClass() != otherObj.getClass()) {
+ return this.hashCode() - otherObj.hashCode();
+ }
+
+ final AxPolicy other = (AxPolicy) otherObj;
+ if (!key.equals(other.key)) {
+ return key.compareTo(other.key);
+ }
+ if (!template.equals(other.template)) {
+ return template.compareTo(other.template);
+ }
+ if (!stateMap.equals(other.stateMap)) {
+ return (stateMap.hashCode() - other.stateMap.hashCode());
+ }
+ return firstState.compareTo(other.firstState);
+ }
+}
diff --git a/model/src/main/java/org/onap/policy/apex/model/policymodel/concepts/AxPolicyModel.java b/model/src/main/java/org/onap/policy/apex/model/policymodel/concepts/AxPolicyModel.java
new file mode 100644
index 000000000..e119536fb
--- /dev/null
+++ b/model/src/main/java/org/onap/policy/apex/model/policymodel/concepts/AxPolicyModel.java
@@ -0,0 +1,661 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * Copyright (C) 2016-2018 Ericsson. All rights reserved.
+ * Modifications Copyright (C) 2019,2022 Nordix Foundation.
+ * Modifications Copyright (C) 2021 AT&T Intellectual Property. All rights reserved.
+ * Modifications Copyright (C) 2021 Bell Canada. All rights reserved.
+ * ================================================================================
+ * 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.apex.model.policymodel.concepts;
+
+import java.util.List;
+import java.util.Map.Entry;
+import org.onap.policy.apex.model.basicmodel.concepts.AxArtifactKey;
+import org.onap.policy.apex.model.basicmodel.concepts.AxConcept;
+import org.onap.policy.apex.model.basicmodel.concepts.AxKey;
+import org.onap.policy.apex.model.basicmodel.concepts.AxKeyInformation;
+import org.onap.policy.apex.model.basicmodel.concepts.AxModel;
+import org.onap.policy.apex.model.basicmodel.concepts.AxValidationMessage;
+import org.onap.policy.apex.model.basicmodel.concepts.AxValidationResult;
+import org.onap.policy.apex.model.basicmodel.concepts.AxValidationResult.ValidationResult;
+import org.onap.policy.apex.model.basicmodel.service.ModelService;
+import org.onap.policy.apex.model.contextmodel.concepts.AxContextAlbum;
+import org.onap.policy.apex.model.contextmodel.concepts.AxContextAlbums;
+import org.onap.policy.apex.model.contextmodel.concepts.AxContextModel;
+import org.onap.policy.apex.model.contextmodel.concepts.AxContextSchemas;
+import org.onap.policy.apex.model.eventmodel.concepts.AxEvent;
+import org.onap.policy.apex.model.eventmodel.concepts.AxEvents;
+import org.onap.policy.apex.model.eventmodel.concepts.AxField;
+import org.onap.policy.common.utils.validation.Assertions;
+
+/**
+ * A container class for an Apex policy model. This class is a container class that allows an Apex
+ * model to be constructed that contains definitions of all the context, event, and policy concepts
+ * required to run policies in Apex. The model contains schema definitions, definitions of events
+ * and context albums that use those schemas, definitions of tasks for policies and definitions of
+ * the policies themselves.
+ *
+ * <p>An Apex policy model is an important artifact in Apex. At editing time, an Apex editor creates
+ * and edits a policy model and a policy model is loaded into and is executed by an Apex engine.
+ * Therefore, an Apex model and the set of policies that it holds is the way that the policy domain
+ * that an Apex engine or a group of Apex engines executes across is expressed, both at design time
+ * and run time. The Apex deployment system is responsible for deploying Apex models to and the
+ * context they need the appropriate engines for execution.
+ *
+ * <p>Model registration is carried out by calling the {@code register()} method, which registers the
+ * policy model and all its constituent containers with the model service. The containers for
+ * context schemas, events, context albums, tasks, policies, and key information are all registered.
+ *
+ * <p>Once a policy model is composed, the overall structure of the policy model and all its references
+ * can be validated. During validation of a policy model, the validation checks listed below are
+ * executed:
+ * <ol>
+ * <li>The policy model is validated as an Apex model, which validates the existence, correctness,
+ * and duplication of all keys in the model as well as validating the key information of the keys,
+ * see validation in {@link AxModel}
+ * <li>The schemas in the model must be valid, see validation in {@link AxContextSchemas}
+ * <li>The context albums in the model must be valid, see validation in {@link AxContextAlbums}
+ * <li>The tasks in the model must be valid, see validation in {@link AxTasks}
+ * <li>The policies in the model must be valid, see validation in {@link AxPolicies}
+ * <li>The events in the model must be valid, see validation in {@link AxEvents}
+ * <li>The context schemas referred to in each field in every event must exist
+ * <li>The context schemas referred to in every context album must exist
+ * <li>The context schemas referred to in every task input field and output field must exist
+ * <li>The context albums referred to in every task must exist
+ * <li>The context albums referred to in every state must exist
+ * <li>The trigger event referred to in every state must exist
+ * <li>The default task referred to in every state must exist
+ * <li>In a state, an event that triggers a task must contain all the input fields required by that
+ * task
+ * <li>In a state, an event that is emitted by a task must contain all the output fields produced by
+ * that task
+ * <li>All tasks referred to by a state must exist
+ * <li>All events referred to on direct state outputs must exist
+ * </ol>
+ */
+public class AxPolicyModel extends AxModel {
+ // @formatter:off
+ private static final String SCHEMAS_TOKEN = "_Schemas";
+ private static final String KEY_INFO_TOKEN = "_KeyInfo";
+ private static final String EVENTS_TOKEN = "_Events";
+ private static final String ALBUMS_TOKEN = "_Albums";
+ private static final String TASKS_TOKEN = "_Tasks";
+ private static final String POLICIESS_TOKEN = "_Policies";
+
+ private static final String DOES_NOT_EXIST = " does not exist";
+
+ private static final long serialVersionUID = 8800599637708309945L;
+
+ private AxPolicies policies;
+ private AxTasks tasks;
+ private AxEvents events;
+ private AxContextAlbums albums;
+ private AxContextSchemas schemas;
+
+ /**
+ * The Default Constructor creates a policy model with a null key and empty containers for
+ * schemas, key information, events, context albums, tasks and policies.
+ */
+ public AxPolicyModel() {
+ this(new AxArtifactKey());
+ }
+
+ /**
+ * Copy constructor.
+ *
+ * @param copyConcept the concept to copy from
+ */
+ public AxPolicyModel(final AxPolicyModel copyConcept) {
+ super(copyConcept);
+ }
+
+ /**
+ * The Keyed Constructor creates a policy model with the given key and empty containers for
+ * schemas, key information, events, context albums, tasks and policies.
+ *
+ * @param key the key
+ */
+ public AxPolicyModel(final AxArtifactKey key) {
+ this(key,
+ new AxContextSchemas(new AxArtifactKey(key.getName() + SCHEMAS_TOKEN, key.getVersion())),
+ new AxKeyInformation(new AxArtifactKey(key.getName() + KEY_INFO_TOKEN, key.getVersion())),
+ new AxEvents(new AxArtifactKey(key.getName() + EVENTS_TOKEN, key.getVersion())),
+ new AxContextAlbums(new AxArtifactKey(key.getName() + ALBUMS_TOKEN, key.getVersion())),
+ new AxTasks(new AxArtifactKey(key.getName() + TASKS_TOKEN, key.getVersion())),
+ new AxPolicies(new AxArtifactKey(key.getName() + POLICIESS_TOKEN, key.getVersion())));
+ }
+
+ /**
+ * This Constructor creates a policy model with all of its fields specified.
+ *
+ * @param key the key of the policy model
+ * @param schemas the context schema container for the policy model
+ * @param keyInformation the key information container for the policy model
+ * @param events the event container for the policy model
+ * @param albums the context album container for the policy model
+ * @param tasks the task container for the policy model
+ * @param policies the policy container for the policy model
+ */
+ public AxPolicyModel(final AxArtifactKey key, final AxContextSchemas schemas, final AxKeyInformation keyInformation,
+ final AxEvents events, final AxContextAlbums albums, final AxTasks tasks, final AxPolicies policies) {
+ super(key, keyInformation);
+ Assertions.argumentNotNull(schemas, "schemas may not be null");
+ Assertions.argumentNotNull(events, "events may not be null");
+ Assertions.argumentNotNull(albums, "albums may not be null");
+ Assertions.argumentNotNull(tasks, "tasks may not be null");
+ Assertions.argumentNotNull(policies, "policies may not be null");
+
+ this.schemas = schemas;
+ this.events = events;
+ this.albums = albums;
+ this.tasks = tasks;
+ this.policies = policies;
+ }
+
+ /**
+ * {@inheritDoc}.
+ */
+ @Override
+ public void register() {
+ super.register();
+ ModelService.registerModel(AxContextSchemas.class, getSchemas());
+ ModelService.registerModel(AxEvents.class, getEvents());
+ ModelService.registerModel(AxContextAlbums.class, getAlbums());
+ ModelService.registerModel(AxTasks.class, getTasks());
+ ModelService.registerModel(AxPolicies.class, getPolicies());
+ ModelService.registerModel(AxPolicyModel.class, this);
+ }
+
+ /**
+ * {@inheritDoc}.
+ */
+ @Override
+ public List<AxKey> getKeys() {
+ final List<AxKey> keyList = super.getKeys();
+
+ keyList.addAll(schemas.getKeys());
+ keyList.addAll(events.getKeys());
+ keyList.addAll(albums.getKeys());
+ keyList.addAll(tasks.getKeys());
+ keyList.addAll(policies.getKeys());
+
+ return keyList;
+ }
+
+ /**
+ * Gets a context model from the policy model.
+ *
+ * @return the context model
+ */
+ public AxContextModel getContextModel() {
+ return new AxContextModel(new AxArtifactKey(albums.getKey().getName() + "_Model", albums.getKey().getVersion()),
+ getSchemas(), getAlbums(), getKeyInformation());
+ }
+
+ /**
+ * Gets the policy container from the policy model.
+ *
+ * @return the policy container with all the policies in the model
+ */
+ public AxPolicies getPolicies() {
+ return policies;
+ }
+
+ /**
+ * Sets the policy container for the policy model.
+ *
+ * @param policies the policy container with all the policies in the model
+ */
+ public void setPolicies(final AxPolicies policies) {
+ Assertions.argumentNotNull(policies, "policies may not be null");
+ this.policies = policies;
+ }
+
+ /**
+ * Gets the task container from the policy model.
+ *
+ * @return the task container with all the tasks in the model
+ */
+ public AxTasks getTasks() {
+ return tasks;
+ }
+
+ /**
+ * Sets the task container from the policy model.
+ *
+ * @param tasks the task container with all the tasks in the model
+ */
+ public void setTasks(final AxTasks tasks) {
+ Assertions.argumentNotNull(tasks, "tasks may not be null");
+ this.tasks = tasks;
+ }
+
+ /**
+ * Gets the event container from the policy model.
+ *
+ * @return the event container with all the events in the model
+ */
+ public AxEvents getEvents() {
+ return events;
+ }
+
+ /**
+ * Sets the event container from the policy model.
+ *
+ * @param events the event container with all the events in the model
+ */
+ public void setEvents(final AxEvents events) {
+ Assertions.argumentNotNull(events, "events may not be null");
+ this.events = events;
+ }
+
+ /**
+ * Gets the context album container from the policy model.
+ *
+ * @return the context album container with all the context albums in the model
+ */
+ public AxContextAlbums getAlbums() {
+ return albums;
+ }
+
+ /**
+ * Sets the context album container from the policy model.
+ *
+ * @param albums the context album container with all the context albums in the model
+ */
+ public void setAlbums(final AxContextAlbums albums) {
+ Assertions.argumentNotNull(albums, "albums may not be null");
+ this.albums = albums;
+ }
+
+ /**
+ * Gets the context schema container from the policy model.
+ *
+ * @return the context schema container with all the context schemas in the model
+ */
+ public AxContextSchemas getSchemas() {
+ return schemas;
+ }
+
+ /**
+ * Sets the context schema container from the policy model.
+ *
+ * @param schemas the context schema container with all the context schemas in the model
+ */
+ public void setSchemas(final AxContextSchemas schemas) {
+ Assertions.argumentNotNull(schemas, "schemas may not be null");
+ this.schemas = schemas;
+ }
+
+ /**
+ * {@inheritDoc}.
+ */
+ @Override
+ public AxValidationResult validate(final AxValidationResult resultIn) {
+ AxValidationResult result = resultIn;
+
+ result = super.validate(result);
+ result = schemas.validate(result);
+ result = events.validate(result);
+ result = albums.validate(result);
+ result = tasks.validate(result);
+ result = policies.validate(result);
+
+ validateEventKeys(result);
+ validateContextAlbumKeys(result);
+ validateAllTaskKeys(result);
+ validatePolicyKeys(result);
+
+ return result;
+ }
+
+ /**
+ * Validate all fundamental concepts keyed in events exist.
+ *
+ * @param result the validation result to return
+ * @return the result
+ */
+ private AxValidationResult validateEventKeys(final AxValidationResult result) {
+ for (final AxEvent event : events.getAll(null)) {
+ for (final AxField field : event.getFields()) {
+ if (getSchemas().get(field.getSchema()) == null) {
+ result.addValidationMessage(
+ new AxValidationMessage(event.getKey(), this.getClass(), ValidationResult.INVALID,
+ "event field data type " + field.getSchema().getId() + DOES_NOT_EXIST));
+ }
+ }
+ }
+ return result;
+ }
+
+ /**
+ * Validate all fundamental concepts keyed in concept maps exist.
+ *
+ * @param result the validation result to return
+ * @return the result
+ */
+ private AxValidationResult validateContextAlbumKeys(final AxValidationResult result) {
+ for (final AxContextAlbum contextAlbum : albums.getAll(null)) {
+ if (getSchemas().get(contextAlbum.getItemSchema()) == null) {
+ result.addValidationMessage(
+ new AxValidationMessage(contextAlbum.getKey(), this.getClass(), ValidationResult.INVALID,
+ "context album schema " + contextAlbum.getItemSchema().getId() + DOES_NOT_EXIST));
+ }
+ }
+ return result;
+ }
+
+ /**
+ * Validate all fundamental concepts keyed in tasks exist.
+ *
+ * @param result the validation result to return
+ * @return the result
+ */
+ private AxValidationResult validateAllTaskKeys(AxValidationResult result) {
+ for (final AxTask task : tasks.getAll(null)) {
+ validateTaskKeys(task, result);
+ }
+ return result;
+ }
+
+ /**
+ * Validate all fundamental concepts keyed in tasks exist.
+ *
+ * @param task The task to validate the keys of
+ * @param result the validation result to return
+ * @return the result
+ */
+ private AxValidationResult validateTaskKeys(final AxTask task, AxValidationResult result) {
+ for (final AxArtifactKey contextAlbumKey : task.getContextAlbumReferences()) {
+ if (albums.get(contextAlbumKey) == null) {
+ result.addValidationMessage(new AxValidationMessage(task.getKey(), this.getClass(),
+ ValidationResult.INVALID, "task context album " + contextAlbumKey.getId() + DOES_NOT_EXIST));
+ }
+ }
+ return result;
+ }
+
+ /**
+ * Validate all fundamental concepts keyed in policies exist.
+ *
+ * @param result the validation result to return
+ * @return the result
+ */
+ private AxValidationResult validatePolicyKeys(final AxValidationResult result) {
+ for (final AxPolicy policy : policies.getAll(null)) {
+ for (final AxState state : policy.getStateMap().values()) {
+ validateStateReferences(state, result);
+ }
+ }
+
+ return result;
+ }
+
+ /**
+ * Validate that the references used on a state are valid.
+ *
+ * @param state The state to check
+ * @param result the validation result to append to
+ */
+ private void validateStateReferences(AxState state, AxValidationResult result) {
+ for (final AxArtifactKey contextAlbumKey : state.getContextAlbumReferences()) {
+ if (albums.get(contextAlbumKey) == null) {
+ result.addValidationMessage(new AxValidationMessage(state.getKey(), this.getClass(),
+ ValidationResult.INVALID, "state context album " + contextAlbumKey.getId() + DOES_NOT_EXIST));
+ }
+ }
+
+ final AxEvent triggerEvent = events.getEventMap().get(state.getTrigger());
+ if (triggerEvent == null) {
+ result.addValidationMessage(new AxValidationMessage(state.getKey(), this.getClass(),
+ ValidationResult.INVALID, "state trigger event " + state.getTrigger().getId() + DOES_NOT_EXIST));
+ }
+
+ final AxTask defaultTask = tasks.getTaskMap().get(state.getDefaultTask());
+ if (defaultTask == null) {
+ result.addValidationMessage(new AxValidationMessage(state.getKey(), this.getClass(),
+ ValidationResult.INVALID, "state default task " + state.getDefaultTask().getId() + DOES_NOT_EXIST));
+ }
+
+ for (final AxStateOutput stateOutput : state.getStateOutputs().values()) {
+ if (events.getEventMap().get(stateOutput.getOutgoingEvent()) == null) {
+ result.addValidationMessage(new AxValidationMessage(stateOutput.getKey(), this.getClass(),
+ ValidationResult.INVALID, "output event " + stateOutput.getOutgoingEvent().getId()
+ + " for state output " + stateOutput.getId() + DOES_NOT_EXIST));
+ }
+ }
+
+ validateEventTaskFieldCompatibilityOnState(state, result);
+ }
+
+ /**
+ * Validate that the fields on tasks and events that trigger them and are output by them are
+ * compatible for all tasks used on a state.
+ *
+ * @param state The state to check
+ * @param result the validation result to append to
+ */
+ private void validateEventTaskFieldCompatibilityOnState(AxState state, AxValidationResult result) {
+ // Check task output fields and event fields are compatible for tasks that directly
+ // reference state outputs
+ for (final Entry<AxArtifactKey, AxStateTaskReference> taskRefEntry : state.getTaskReferences().entrySet()) {
+ if (!taskRefEntry.getValue().getStateTaskOutputType().equals(AxStateTaskOutputType.DIRECT)) {
+ continue;
+ }
+
+ final AxTask usedTask = tasks.getTaskMap().get(taskRefEntry.getKey());
+ if (usedTask == null) {
+ result.addValidationMessage(new AxValidationMessage(state.getKey(), this.getClass(),
+ ValidationResult.INVALID, "state task " + taskRefEntry.getKey().getId() + DOES_NOT_EXIST));
+ } else {
+ AxStateOutput stateOutput =
+ state.getStateOutputs().get(taskRefEntry.getValue().getOutput().getKey().getLocalName());
+ validateEventTaskFieldCompatibilityOnStateOutput(state, usedTask, stateOutput, result);
+ }
+ }
+ }
+
+ /**
+ * Validate that the fields on a task of a state output and the events that trigger it are
+ * compatible.
+ *
+ * @param state The state to check
+ * @param task The task to check
+ * @param stateOutput The state output to check
+ * @param result the validation result to append to
+ */
+ private void validateEventTaskFieldCompatibilityOnStateOutput(final AxState state, final AxTask task,
+ final AxStateOutput stateOutput, AxValidationResult result) {
+ if (stateOutput == null) {
+ result.addValidationMessage(new AxValidationMessage(state.getKey(), this.getClass(),
+ ValidationResult.INVALID, "state output on task reference for task " + task.getId() + " is null"));
+
+ } else {
+ final AxEvent usedEvent = events.getEventMap().get(stateOutput.getOutgoingEvent());
+ if (usedEvent == null) {
+ result.addValidationMessage(new AxValidationMessage(stateOutput.getKey(), this.getClass(),
+ ValidationResult.INVALID, "output event " + stateOutput.getOutgoingEvent().getId()
+ + " for state output " + stateOutput.getId() + DOES_NOT_EXIST));
+ }
+ }
+ }
+
+ /**
+ * When a model is deserialized, if the albums field was missing a blank
+ * with a null key was added. This method is called by JAXB after deserializing and is
+ * used to insert an appropriate key
+ */
+ @Override
+ public void buildReferences() {
+ getSchemas().buildReferences();
+ getEvents().buildReferences();
+ getAlbums().buildReferences();
+ getTasks().buildReferences();
+ getPolicies().buildReferences();
+ getKeyInformation().buildReferences();
+
+ AxArtifactKey nullAlbumskey = new AxArtifactKey(AxKey.NULL_KEY_NAME + ALBUMS_TOKEN, AxKey.NULL_KEY_VERSION);
+
+ if (AxArtifactKey.getNullKey().equals(getAlbums().getKey())
+ || nullAlbumskey.equals(getAlbums().getKey())) {
+ getAlbums().setKey(new AxArtifactKey(getKey().getName() + ALBUMS_TOKEN, getKey().getVersion()));
+ getKeyInformation().generateKeyInfo(getAlbums());
+ }
+ }
+
+ /**
+ * {@inheritDoc}.
+ */
+ @Override
+ public void clean() {
+ super.clean();
+ policies.clean();
+ tasks.clean();
+ events.clean();
+ albums.clean();
+ schemas.clean();
+ }
+
+ /**
+ * {@inheritDoc}.
+ */
+ @Override
+ public String toString() {
+ final StringBuilder builder = new StringBuilder();
+ builder.append(this.getClass().getSimpleName());
+ builder.append(":(");
+ builder.append(super.toString());
+ builder.append(",policies=");
+ builder.append(policies);
+ builder.append(",tasks=");
+ builder.append(tasks);
+ builder.append(",events=");
+ builder.append(events);
+ builder.append(",albums=");
+ builder.append(albums);
+ builder.append(",schemas=");
+ builder.append(schemas);
+ builder.append(")");
+ return builder.toString();
+ }
+
+ /**
+ * {@inheritDoc}.
+ */
+ @Override
+ public AxConcept copyTo(final AxConcept targetObject) {
+ Assertions.argumentNotNull(targetObject, "target may not be null");
+
+ final Object copyObject = targetObject;
+ Assertions.instanceOf(copyObject, AxPolicyModel.class);
+
+ final AxPolicyModel copy = ((AxPolicyModel) copyObject);
+ super.copyTo(targetObject);
+ copy.setPolicies(new AxPolicies(policies));
+ copy.setTasks(new AxTasks(tasks));
+ copy.setEvents(new AxEvents(events));
+ copy.setAlbums(new AxContextAlbums(albums));
+ copy.setSchemas(new AxContextSchemas(schemas));
+
+ return copy;
+ }
+
+ /**
+ * {@inheritDoc}.
+ */
+ @Override
+ public int hashCode() {
+ final int prime = 31;
+ int result = 1;
+ result = prime * result + super.hashCode();
+ result = prime * result + policies.hashCode();
+ result = prime * result + tasks.hashCode();
+ result = prime * result + events.hashCode();
+ result = prime * result + albums.hashCode();
+ result = prime * result + schemas.hashCode();
+ return result;
+ }
+
+ /**
+ * {@inheritDoc}.
+ */
+ @Override
+ public boolean equals(final Object obj) {
+ if (obj == null) {
+ throw new IllegalArgumentException("comparison object may not be null");
+ }
+
+ if (this == obj) {
+ return true;
+ }
+ if (getClass() != obj.getClass()) {
+ return false;
+ }
+
+ final AxPolicyModel other = (AxPolicyModel) obj;
+ if (!super.equals(other)) {
+ return false;
+ }
+ if (!policies.equals(other.policies)) {
+ return false;
+ }
+ if (!tasks.equals(other.tasks)) {
+ return false;
+ }
+ if (!events.equals(other.events)) {
+ return false;
+ }
+ if (!albums.equals(other.albums)) {
+ return false;
+ }
+ return schemas.equals(other.schemas);
+ }
+
+ /**
+ * {@inheritDoc}.
+ */
+ @Override
+ public int compareTo(final AxConcept otherObj) {
+ Assertions.argumentNotNull(otherObj, "comparison object may not be null");
+
+ if (this == otherObj) {
+ return 0;
+ }
+ if (getClass() != otherObj.getClass()) {
+ return this.hashCode() - otherObj.hashCode();
+ }
+
+ final AxPolicyModel other = (AxPolicyModel) otherObj;
+ if (!super.equals(other)) {
+ return super.compareTo(other);
+ }
+ if (!policies.equals(other.policies)) {
+ return policies.compareTo(other.policies);
+ }
+ if (!tasks.equals(other.tasks)) {
+ return tasks.compareTo(other.tasks);
+ }
+ if (!events.equals(other.events)) {
+ return events.compareTo(other.events);
+ }
+ if (!albums.equals(other.albums)) {
+ return albums.compareTo(other.albums);
+ }
+ return schemas.compareTo(other.schemas);
+ }
+}
diff --git a/model/src/main/java/org/onap/policy/apex/model/policymodel/concepts/AxState.java b/model/src/main/java/org/onap/policy/apex/model/policymodel/concepts/AxState.java
new file mode 100644
index 000000000..ae8efbff4
--- /dev/null
+++ b/model/src/main/java/org/onap/policy/apex/model/policymodel/concepts/AxState.java
@@ -0,0 +1,870 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * Copyright (C) 2016-2018 Ericsson. All rights reserved.
+ * Modifications Copyright (C) 2018 Samsung Electronics Co., Ltd.
+ * Modifications Copyright (C) 2019-2022 Nordix Foundation.
+ * Modifications Copyright (C) 2021 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.apex.model.policymodel.concepts;
+
+import com.google.gson.annotations.SerializedName;
+import java.util.List;
+import java.util.Map;
+import java.util.Map.Entry;
+import java.util.Set;
+import java.util.TreeMap;
+import java.util.TreeSet;
+import org.onap.policy.apex.model.basicmodel.concepts.AxArtifactKey;
+import org.onap.policy.apex.model.basicmodel.concepts.AxConcept;
+import org.onap.policy.apex.model.basicmodel.concepts.AxKey;
+import org.onap.policy.apex.model.basicmodel.concepts.AxKeyUse;
+import org.onap.policy.apex.model.basicmodel.concepts.AxReferenceKey;
+import org.onap.policy.apex.model.basicmodel.concepts.AxValidationMessage;
+import org.onap.policy.apex.model.basicmodel.concepts.AxValidationResult;
+import org.onap.policy.apex.model.basicmodel.concepts.AxValidationResult.ValidationResult;
+import org.onap.policy.common.utils.validation.Assertions;
+
+/**
+ * This class holds the definition of a single state in a policy. A state is a single stage in a policy. A state has a
+ * single input event, its trigger. A state can output many events, but can only output one event on a single execution.
+ * After it executes, a state can pass control to another state or can simply emit its event to an external system. In
+ * the case where a state passes control to another state, the output event of the state becomes the input event of the
+ * next state. The outputs of a state {@link AxStateOutput} are held as a map in the state. Each state output contains
+ * the outgoing event of the state and optionally the next state to pass control to.
+ *
+ * <p>A state uses tasks {@link AxTask} to execute its logic. A state holds its tasks in a map and must have at least
+ * one task. A state uses Task Selection Logic {@link AxTaskSelectionLogic} to select which task should be executed in a
+ * given execution cycle. Optional Task Selection Logic can use fields on the incoming event and information from the
+ * context albums available on the state to decide what task to execute in a given context. The default task of a state
+ * is the task that is executed when task Selection Logic is not specified. In cases where only a single task is
+ * specified on a state, the default task must be that task and the state always executes that task.
+ *
+ * <p>What happens when a state completes its execution cycle depends on the task that is selected for execution by the
+ * state. Therefore, the action to be performed a state on execution of each task must be defined in the state as a
+ * {@link AxStateTaskReference} instance for each task defined in the state. The {@link AxStateTaskReference} instance
+ * defines the action to be performed as either a {@link AxStateTaskOutputType} of {@link AxStateTaskOutputType#DIRECT}
+ * or {@link AxStateTaskOutputType#LOGIC} and contains an {@link AxReferenceKey} reference to the instance that will
+ * complete the state output.
+ *
+ * <p>In the case of direct output, the {@link AxReferenceKey} reference in the {@link AxStateTaskReference} instance is
+ * a reference to an {@link AxStateOutput} instance. The state output defines the event to be emitted by the state and
+ * the next state to pass control to if any. All fields of the executed task are marshaled onto the outgoing event
+ * automatically by Apex.
+ *
+ * <p>In the case of logic output, the {@link AxReferenceKey} reference in the {@link AxStateTaskReference} instance is
+ * a reference to State Finalizer Logic in an {@link AxStateFinalizerLogic} instance, which selects the
+ * {@link AxStateOutput} that the state will use. The state finalizer logic uses fields emitted by the executed task and
+ * information from the context albums available on the state to decide what {@link AxStateOutput} to select in a given
+ * context. The state output defines the event to be emitted by the state and the next state to pass control to if any.
+ * The State Finalizer Logic instances for the state are held in a map in the state. State Finalizer Logic must marshal
+ * the fields of the output event in whatever manner it wishes; Apex does not automatically transfer the output fields
+ * from the task directly to the output event.
+ *
+ * <p>The Task Selection Logic instance or State Finalizer Logic instances in a state may use information in context
+ * albums to arrive at their task or state output selections. The context albums that the state uses and that should be
+ * made available to the state by Apex policy distribution are held as a set of references to context albums in the
+ * state.
+ *
+ * <p>During validation of a state, the validation checks listed below are executed: <ol> <li>The policy key must not be
+ * a null key and must be valid, see validation in {@link AxReferenceKey} <li>The trigger event key must not be a null
+ * key and must be valid, see validation in {@link AxArtifactKey} <li>At least one state output must be defined <li>Each
+ * state output in a state must have that state as its parent <li>Each state output must be valid, see validation in
+ * {@link AxStateOutput} <li>The next state defined in a state output must be unique in a state <li>The default task key
+ * must not be a null key and must be valid, see validation in {@link AxArtifactKey} <li>The default task must appear in
+ * the task map of the state <li>At least one task must be defined on the state <li>Each task key on the task map for
+ * the state must not be a null key and must be valid, see validation in {@link AxArtifactKey} <li>All state task
+ * references for each task in the state must exist and must be valid, see validation in {@link AxStateTaskReference}
+ * <li>Each state task reference in a state must have that state as its parent <li>For direct state outputs from tasks,
+ * the state output must be defined on the state <li>For logic state outputs from tasks, the State Finalizer Logic must
+ * be defined on the state <li>An observation is issued for each state output defined on the state that is not used as a
+ * direct output on a task <li>An observation is issued for each state finalizer logic instance defined on the state
+ * that is not used as an output on a task <li>Each context album key on the context album set for the state must not be
+ * a null key and must be valid, see validation in {@link AxArtifactKey} <li>Task Selection logic in a state must have
+ * that state as its parent <li>Task Selection logic in a state must be valid, see validation in
+ * {@link AxTaskSelectionLogic} <li>Each State Finalizer logic instance in a state must have that state as its parent
+ * <li>Each State Finalizer logic instance in a state must be valid, see validation in {@link AxStateFinalizerLogic}
+ * </ol>
+ */
+public class AxState extends AxConcept {
+ private static final String DOES_NOT_EQUAL_STATE_KEY = " does not equal state key";
+
+ private static final long serialVersionUID = 8041771382337655535L;
+
+
+
+ @SerializedName("stateKey")
+ private AxReferenceKey key;
+
+ private AxArtifactKey trigger;
+ private Map<String, AxStateOutput> stateOutputs;
+
+ @SerializedName("contextAlbumReference")
+ private Set<AxArtifactKey> contextAlbumReferenceSet;
+
+ private AxTaskSelectionLogic taskSelectionLogic;
+ private Map<String, AxStateFinalizerLogic> stateFinalizerLogicMap;
+ private AxArtifactKey defaultTask;
+
+ @SerializedName("taskReferences")
+ private Map<AxArtifactKey, AxStateTaskReference> taskReferenceMap;
+
+ /**
+ * The Default Constructor creates a state with a null reference key and with default values for all other fields.
+ */
+ public AxState() {
+ this(new AxReferenceKey());
+ contextAlbumReferenceSet = new TreeSet<>();
+ taskReferenceMap = new TreeMap<>();
+ }
+
+ /**
+ * Copy constructor.
+ *
+ * @param copyConcept the concept to copy from
+ */
+ public AxState(final AxState copyConcept) {
+ super(copyConcept);
+ }
+
+ /**
+ * The Keyed Constructor creates a state with the given reference key and with default values for all other fields.
+ *
+ * @param key the reference key of the state
+ */
+ public AxState(final AxReferenceKey key) {
+ // @formatter:off
+ this(new AxStateParamsBuilder()
+ .key(key) // Key
+ .trigger(AxArtifactKey.getNullKey()) // Trigger Reference
+ .stateOutputs(new TreeMap<>()) // State Outputs
+ .contextAlbumReferenceSet(new TreeSet<>()) // Context Album Refs
+ .taskSelectionLogic(new AxTaskSelectionLogic()) // Task Selection Logic
+ .stateFinalizerLogicMap(new TreeMap<>()) // State Finalizer Logics
+ .defaultTask(AxArtifactKey.getNullKey()) // Default Task
+ .taskReferenceMap(new TreeMap<>()) // Task References
+ );
+ // @formatter:on
+ }
+
+ /**
+ * This Constructor creates a state with all its fields defined.
+ *
+ * @param axStateParams parameters for state creation
+ */
+ // CHECKSTYLE:OFF: checkstyle:parameterNumber
+ public AxState(AxStateParamsBuilder axStateParams) {
+ super();
+ Assertions.argumentNotNull(axStateParams.getKey(), "key may not be null");
+ Assertions.argumentNotNull(axStateParams.getTrigger(), "trigger may not be null");
+ Assertions.argumentNotNull(axStateParams.getStateOutputs(), "stateOutputs may not be null");
+ Assertions.argumentNotNull(axStateParams.getContextAlbumReferenceSet(),
+ "contextAlbumReferenceSet may not be null");
+ Assertions.argumentNotNull(axStateParams.getTaskSelectionLogic(), "taskSelectionLogic may not be null");
+ Assertions.argumentNotNull(axStateParams.getStateFinalizerLogicMap(), "stateFinalizerLogicMap may not be null");
+ Assertions.argumentNotNull(axStateParams.getDefaultTask(), "defaultTask may not be null");
+ Assertions.argumentNotNull(axStateParams.getTaskReferenceMap(), "taskReferenceMap may not be null");
+
+ this.key = axStateParams.getKey();
+ this.trigger = axStateParams.getTrigger();
+ this.stateOutputs = axStateParams.getStateOutputs();
+ this.contextAlbumReferenceSet = axStateParams.getContextAlbumReferenceSet();
+ this.taskSelectionLogic = axStateParams.getTaskSelectionLogic();
+ this.stateFinalizerLogicMap = axStateParams.getStateFinalizerLogicMap();
+ this.defaultTask = axStateParams.getDefaultTask();
+ this.taskReferenceMap = axStateParams.getTaskReferenceMap();
+ }
+ // CHECKSTYLE:ON: checkstyle:parameterNumber
+
+ /**
+ * When a state is deserialized from disk or from the database, the parent of contained objects is not defined. This
+ * method is called by JAXB after deserialized and is used to set the parent keys of all
+ * {@link AxTaskSelectionLogic}, {@link AxStateOutput}, and {@link AxStateFinalizerLogic} instance in the state.
+ */
+ @Override
+ public void buildReferences() {
+ if (!taskSelectionLogic.getKey().getLocalName().equals(AxKey.NULL_KEY_NAME)) {
+ taskSelectionLogic.getKey().setParentReferenceKey(key);
+ }
+
+ stateOutputs.values().stream().forEach(output -> output.getKey().setParentReferenceKey(key));
+ stateFinalizerLogicMap.values().stream().forEach(output -> output.getKey().setParentReferenceKey(key));
+ taskReferenceMap.values().stream().forEach(output -> output.getKey().setParentReferenceKey(key));
+ }
+
+ /**
+ * Gets the names of all the states that this state may pass control to.
+ *
+ * @return the list of possible states that may receive control when this state completes execution
+ */
+ public Set<String> getNextStateSet() {
+ final Set<String> nextStateSet = new TreeSet<>();
+
+ for (final AxStateOutput stateOutput : stateOutputs.values()) {
+ nextStateSet.add(stateOutput.getNextState().getLocalName());
+ }
+ return nextStateSet;
+ }
+
+ /**
+ * {@inheritDoc}.
+ */
+ @Override
+ public AxReferenceKey getKey() {
+ return key;
+ }
+
+ /**
+ * {@inheritDoc}.
+ */
+ @Override
+ public List<AxKey> getKeys() {
+ final List<AxKey> keyList = key.getKeys();
+ keyList.add(new AxKeyUse(trigger.getKey()));
+ for (final AxStateOutput stateOutput : stateOutputs.values()) {
+ keyList.addAll(stateOutput.getKeys());
+ }
+ for (final AxArtifactKey contextAlbumReferenceKey : contextAlbumReferenceSet) {
+ keyList.add(new AxKeyUse(contextAlbumReferenceKey));
+ }
+ if (!taskSelectionLogic.getKey().equals(AxReferenceKey.getNullKey())) {
+ keyList.addAll(taskSelectionLogic.getKeys());
+ }
+ for (final Entry<String, AxStateFinalizerLogic> stateFinalizerLogicEntry : stateFinalizerLogicMap.entrySet()) {
+ keyList.addAll(stateFinalizerLogicEntry.getValue().getKeys());
+ }
+ keyList.add(new AxKeyUse(defaultTask.getKey()));
+ for (final Entry<AxArtifactKey, AxStateTaskReference> taskReferenceEntry : taskReferenceMap.entrySet()) {
+ keyList.add(new AxKeyUse(taskReferenceEntry.getKey()));
+
+ // A state output is allowed to be used more than once but we only return one usage as a
+ // key
+ for (AxKey referencedKey : taskReferenceEntry.getValue().getKeys()) {
+ if (keyList.contains(referencedKey)) {
+ keyList.add(referencedKey);
+ }
+ }
+ }
+ return keyList;
+ }
+
+ /**
+ * Sets the reference key of the state.
+ *
+ * @param key the state reference key
+ */
+ public void setKey(final AxReferenceKey key) {
+ Assertions.argumentNotNull(key, "key may not be null");
+ this.key = key;
+ }
+
+ /**
+ * Gets the event that triggers the state.
+ *
+ * @return the event that triggers the state
+ */
+ public AxArtifactKey getTrigger() {
+ return trigger;
+ }
+
+ /**
+ * Sets the event that triggers the state.
+ *
+ * @param trigger the event that triggers the state
+ */
+ public void setTrigger(final AxArtifactKey trigger) {
+ Assertions.argumentNotNull(trigger, "trigger may not be null");
+ this.trigger = trigger;
+ }
+
+ /**
+ * Gets the possible state outputs for the state.
+ *
+ * @return the the possible state outputs for the state
+ */
+ public Map<String, AxStateOutput> getStateOutputs() {
+ return stateOutputs;
+ }
+
+ /**
+ * Sets the the possible state outputs for the state.
+ *
+ * @param stateOutputs the the possible state outputs for the state
+ */
+ public void setStateOutputs(final Map<String, AxStateOutput> stateOutputs) {
+ Assertions.argumentNotNull(stateOutputs, "stateOutputs may not be null");
+ this.stateOutputs = stateOutputs;
+ }
+
+ /**
+ * Gets the context album reference set defines the context that may be used by Task Selection Logic and State
+ * Finalizer Logic in the state.
+ *
+ * @return the context album reference set defines the context that may be used by Task Selection Logic and State
+ * Finalizer Logic in the state
+ */
+ public Set<AxArtifactKey> getContextAlbumReferences() {
+ return contextAlbumReferenceSet;
+ }
+
+ /**
+ * Sets the context album reference set defines the context that may be used by Task Selection Logic and State
+ * Finalizer Logic in the state.
+ *
+ * @param contextAlbumReferences the context album reference set defines the context that may be used by Task
+ * Selection Logic and State Finalizer Logic in the state
+ */
+ public void setContextAlbumReferences(final Set<AxArtifactKey> contextAlbumReferences) {
+ Assertions.argumentNotNull(contextAlbumReferences, "contextAlbumReferenceSet may not be null");
+ this.contextAlbumReferenceSet = contextAlbumReferences;
+ }
+
+ /**
+ * Gets the task selection logic that selects the task a state executes in an execution cycle.
+ *
+ * @return the task selection logic that selects the task a state executes in an execution cycle
+ */
+ public AxTaskSelectionLogic getTaskSelectionLogic() {
+ return taskSelectionLogic;
+ }
+
+ /**
+ * Sets the task selection logic that selects the task a state executes in an execution cycle.
+ *
+ * @param taskSelectionLogic the task selection logic that selects the task a state executes in an execution cycle
+ */
+ public void setTaskSelectionLogic(final AxTaskSelectionLogic taskSelectionLogic) {
+ Assertions.argumentNotNull(taskSelectionLogic, "taskSelectionLogic may not be null");
+ this.taskSelectionLogic = taskSelectionLogic;
+ }
+
+ /**
+ * Check if task selection logic has been specified the state.
+ *
+ * @return true, if task selection logic has been specified
+ */
+ public boolean checkSetTaskSelectionLogic() {
+ return !taskSelectionLogic.getKey().equals(AxReferenceKey.getNullKey());
+ }
+
+ /**
+ * Gets the state finalizer logic instances that selects the state output to use after a task executes in a state
+ * execution cycle.
+ *
+ * @return the state finalizer logic instances that selects the state output to use after a task executes in a state
+ * execution cycle
+ */
+ public Map<String, AxStateFinalizerLogic> getStateFinalizerLogicMap() {
+ return stateFinalizerLogicMap;
+ }
+
+ /**
+ * Sets the state finalizer logic instances that selects the state output to use after a task executes in a state
+ * execution cycle.
+ *
+ * @param stateFinalizerLogicMap the state finalizer logic instances that selects the state output to use after a
+ * task executes in a state execution cycle
+ */
+ public void setStateFinalizerLogicMap(final Map<String, AxStateFinalizerLogic> stateFinalizerLogicMap) {
+ Assertions.argumentNotNull(stateFinalizerLogicMap, "stateFinalizerLogic may not be null");
+ this.stateFinalizerLogicMap = stateFinalizerLogicMap;
+ }
+
+ /**
+ * Gets the default task that will execute in a state if Task Selection Logic is not specified.
+ *
+ * @return the default task that will execute in a state if Task Selection Logic is not specified
+ */
+ public AxArtifactKey getDefaultTask() {
+ return defaultTask;
+ }
+
+ /**
+ * Sets the default task that will execute in a state if Task Selection Logic is not specified.
+ *
+ * @param defaultTask the default task that will execute in a state if Task Selection Logic is not specified
+ */
+ public void setDefaultTask(final AxArtifactKey defaultTask) {
+ Assertions.argumentNotNull(defaultTask, "defaultTask may not be null");
+ this.defaultTask = defaultTask;
+ }
+
+ /**
+ * Gets the task reference map that defines the tasks for the state and how the task outputs are handled.
+ *
+ * @return the task reference map that defines the tasks for the state and how the task outputs are handled
+ */
+ public Map<AxArtifactKey, AxStateTaskReference> getTaskReferences() {
+ return taskReferenceMap;
+ }
+
+ /**
+ * Sets the task reference map that defines the tasks for the state and how the task outputs are handled.
+ *
+ * @param taskReferences the task reference map that defines the tasks for the state and how the task outputs are
+ * handled
+ */
+ public void setTaskReferences(final Map<AxArtifactKey, AxStateTaskReference> taskReferences) {
+ Assertions.argumentNotNull(taskReferences, "taskReferenceMap may not be null");
+ this.taskReferenceMap = taskReferences;
+ }
+
+ /**
+ * {@inheritDoc}.
+ */
+ @Override
+ public AxValidationResult validate(final AxValidationResult resultIn) {
+ AxValidationResult result = resultIn;
+
+ if (key.equals(AxReferenceKey.getNullKey())) {
+ result.addValidationMessage(new AxValidationMessage(key, this.getClass(), ValidationResult.INVALID,
+ "key is a null key"));
+ }
+
+ result = key.validate(result);
+
+ if (trigger.equals(AxArtifactKey.getNullKey())) {
+ result.addValidationMessage(new AxValidationMessage(key, this.getClass(), ValidationResult.INVALID,
+ "trigger is a null key: " + trigger));
+ }
+ result = trigger.validate(result);
+
+ if (stateOutputs.size() == 0) {
+ result.addValidationMessage(new AxValidationMessage(key, this.getClass(), ValidationResult.INVALID,
+ "stateOutputs may not be empty"));
+ } else {
+ validateStateOutputs(result);
+ }
+
+ validateContextAlbumReferences(result);
+ result = validateTaskSelectionLogic(result);
+ validateStateFinalizerLogics(result);
+
+ if (defaultTask.equals(AxArtifactKey.getNullKey())) {
+ result.addValidationMessage(new AxValidationMessage(key, this.getClass(), ValidationResult.INVALID,
+ "default task has a null key: " + defaultTask));
+ }
+ result = defaultTask.validate(result);
+
+ if (taskReferenceMap.size() == 0) {
+ result.addValidationMessage(new AxValidationMessage(key, this.getClass(), ValidationResult.INVALID,
+ "taskReferenceMap may not be empty"));
+ } else {
+ validateStateTaskReferences(result);
+ }
+
+ return result;
+ }
+
+ /**
+ * Validate the state outputs of the state.
+ *
+ * @param result the validation result to append to
+ */
+ private void validateStateOutputs(AxValidationResult result) {
+ final Set<String> nextStateNameSet = new TreeSet<>();
+ for (final Entry<String, AxStateOutput> stateOutputEntry : stateOutputs.entrySet()) {
+ if (stateOutputEntry.getValue() == null) {
+ result.addValidationMessage(new AxValidationMessage(key, this.getClass(), ValidationResult.INVALID,
+ "null state output value found on state output " + stateOutputEntry.getKey()));
+ } else {
+ if (!stateOutputEntry.getValue().getKey().getParentReferenceKey().equals(key)) {
+ result.addValidationMessage(new AxValidationMessage(key, this.getClass(), ValidationResult.INVALID,
+ "parent key on state output " + stateOutputEntry.getKey()
+ + DOES_NOT_EQUAL_STATE_KEY));
+ }
+
+ if (stateOutputEntry.getValue().getNextState().getLocalName().equals(key.getLocalName())) {
+ result.addValidationMessage(new AxValidationMessage(key, this.getClass(), ValidationResult.INVALID,
+ "state output next state "
+ + stateOutputEntry.getValue().getNextState().getLocalName()
+ + " may not be this state"));
+
+ }
+
+ if (nextStateNameSet.contains(stateOutputEntry.getValue().getNextState().getLocalName())) {
+ result.addValidationMessage(new AxValidationMessage(key, this.getClass(), ValidationResult.INVALID,
+ "duplicate state output next state name "
+ + stateOutputEntry.getValue().getNextState().getLocalName()
+ + " found"));
+ } else {
+ nextStateNameSet.add(stateOutputEntry.getValue().getNextState().getLocalName());
+ }
+ result = stateOutputEntry.getValue().validate(result);
+ }
+ }
+ }
+
+ /**
+ * Validate the context album references of the state.
+ *
+ * @param result the validation result to append to
+ */
+ private void validateContextAlbumReferences(AxValidationResult result) {
+ for (final AxArtifactKey contextAlbumReference : contextAlbumReferenceSet) {
+ if (contextAlbumReference.equals(AxArtifactKey.getNullKey())) {
+ result.addValidationMessage(new AxValidationMessage(key, this.getClass(), ValidationResult.INVALID,
+ "key on context album reference entry " + contextAlbumReference.getKey()
+ + " may not be the null key"));
+ }
+
+ result = contextAlbumReference.validate(result);
+ }
+ }
+
+ /**
+ * Validate the task selection logic of the state.
+ *
+ * @param result the validation result to append to
+ * @return the result of the validation
+ */
+ private AxValidationResult validateTaskSelectionLogic(AxValidationResult result) {
+ if (!taskSelectionLogic.getKey().equals(AxReferenceKey.getNullKey())) {
+ if (!taskSelectionLogic.getKey().getParentReferenceKey().equals(key)) {
+ result.addValidationMessage(new AxValidationMessage(key, this.getClass(), ValidationResult.INVALID,
+ "taskSelectionLogic key " + taskSelectionLogic.getKey().getId()
+ + DOES_NOT_EQUAL_STATE_KEY));
+ }
+ result = taskSelectionLogic.validate(result);
+ }
+
+ return result;
+ }
+
+ /**
+ * Validate all the state finalizer logic of the state.
+ *
+ * @param result the validation result to append to
+ */
+ private void validateStateFinalizerLogics(AxValidationResult result) {
+ for (final Entry<String, AxStateFinalizerLogic> stateFinalizerLogicEntry : stateFinalizerLogicMap.entrySet()) {
+ if (stateFinalizerLogicEntry.getValue() == null) {
+ result.addValidationMessage(new AxValidationMessage(key, this.getClass(), ValidationResult.INVALID,
+ "null state finalizer logic value found on state finalizer entry "
+ + stateFinalizerLogicEntry.getKey()));
+ } else {
+ if (!stateFinalizerLogicEntry.getValue().getKey().getParentReferenceKey().equals(key)) {
+ result.addValidationMessage(new AxValidationMessage(key, this.getClass(), ValidationResult.INVALID,
+ "stateFinalizerLogic parent key "
+ + stateFinalizerLogicEntry.getValue().getKey().getId()
+ + DOES_NOT_EQUAL_STATE_KEY));
+ }
+
+ result = stateFinalizerLogicEntry.getValue().validate(result);
+ }
+ }
+ }
+
+ /**
+ * Validate the tasks used the state.
+ *
+ * @param result the validation result to append to
+ */
+ private void validateStateTaskReferences(AxValidationResult result) {
+ final Set<String> usedStateOutputNameSet = new TreeSet<>();
+ final Set<String> usedStateFinalizerLogicNameSet = new TreeSet<>();
+
+ for (final Entry<AxArtifactKey, AxStateTaskReference> taskRefEntry : taskReferenceMap.entrySet()) {
+ if (taskRefEntry.getKey().equals(AxArtifactKey.getNullKey())) {
+ result.addValidationMessage(new AxValidationMessage(key, this.getClass(), ValidationResult.INVALID,
+ "task has a null key: " + taskRefEntry.getKey()));
+ }
+ result = taskRefEntry.getKey().validate(result);
+
+ if (taskRefEntry.getValue() == null) {
+ result.addValidationMessage(new AxValidationMessage(key, this.getClass(), ValidationResult.INVALID,
+ "null task reference value found on task reference " + taskRefEntry.getKey()));
+ } else {
+ result = validateStateTaskReference(taskRefEntry.getKey(), taskRefEntry.getValue(),
+ usedStateOutputNameSet, usedStateFinalizerLogicNameSet, result);
+ }
+ }
+
+ final Set<String> unUsedStateOutputNameSet = new TreeSet<>(stateOutputs.keySet());
+ unUsedStateOutputNameSet.removeAll(usedStateOutputNameSet);
+ for (final String unUsedStateOutputName : unUsedStateOutputNameSet) {
+ result.addValidationMessage(new AxValidationMessage(key, this.getClass(), ValidationResult.OBSERVATION,
+ "state output " + unUsedStateOutputName + " is not used directly by any task"));
+ }
+
+ final Set<String> usnUedStateFinalizerLogicNameSet = new TreeSet<>(stateFinalizerLogicMap.keySet());
+ usnUedStateFinalizerLogicNameSet.removeAll(usedStateFinalizerLogicNameSet);
+ for (final String unusedStateFinalizerLogicName : usnUedStateFinalizerLogicNameSet) {
+ result.addValidationMessage(new AxValidationMessage(key, this.getClass(), ValidationResult.OBSERVATION,
+ "state finalizer logic " + unusedStateFinalizerLogicName + " is not used by any task"));
+ }
+
+ if (!taskReferenceMap.containsKey(defaultTask)) {
+ result.addValidationMessage(new AxValidationMessage(key, this.getClass(), ValidationResult.INVALID,
+ "defaultTask " + defaultTask + " not found in taskReferenceMap"));
+ }
+ }
+
+ /**
+ * Validate the references of a task used in a state.
+ *
+ * @param taskKey The key of the task
+ * @param taskReference the task reference of the task
+ * @param stateOutputNameSet State outputs that have been used so far, will be appended for this task reference
+ * @param stateFinalizerLogicNameSet State finalizers that have been used so far, may be appended if this task
+ * reference uses a finalzier
+ * @param result the validation result to append to
+ * @return the result of the validation
+ */
+ private AxValidationResult validateStateTaskReference(final AxArtifactKey taskKey,
+ final AxStateTaskReference taskReference, Set<String> stateOutputNameSet,
+ Set<String> stateFinalizerLogicNameSet, AxValidationResult result) {
+ if (!taskReference.getKey().getParentReferenceKey().equals(key)) {
+ result.addValidationMessage(new AxValidationMessage(key, this.getClass(), ValidationResult.INVALID,
+ "stateTaskReference parent key " + taskReference.getKey().getId()
+ + DOES_NOT_EQUAL_STATE_KEY));
+ }
+
+ if (taskReference.getStateTaskOutputType().equals(AxStateTaskOutputType.DIRECT)) {
+ if (stateOutputs.containsKey(taskReference.getOutput().getLocalName())) {
+ stateOutputNameSet.add(taskReference.getOutput().getLocalName());
+ } else {
+ result.addValidationMessage(new AxValidationMessage(key, this.getClass(), ValidationResult.INVALID,
+ "state output for task " + taskKey + " not found in stateOutputs"));
+ }
+ } else if (taskReference.getStateTaskOutputType().equals(AxStateTaskOutputType.LOGIC)) {
+ if (stateFinalizerLogicMap.containsKey(taskReference.getOutput().getLocalName())) {
+ stateFinalizerLogicNameSet.add(taskReference.getOutput().getLocalName());
+ } else {
+ result.addValidationMessage(new AxValidationMessage(key, this.getClass(), ValidationResult.INVALID,
+ "state finalizer logic for task " + taskKey + " not found in stateFinalizerLogicMap"));
+ }
+ } else {
+ result.addValidationMessage(new AxValidationMessage(key, this.getClass(), ValidationResult.INVALID,
+ "stateTaskReference task output type " + taskReference.getStateTaskOutputType()
+ + " is invalid"));
+ }
+
+ return taskReference.validate(result);
+ }
+
+ /**
+ * {@inheritDoc}.
+ */
+ @Override
+ public void clean() {
+ key.clean();
+ trigger.clean();
+ for (final AxStateOutput stateOutput : stateOutputs.values()) {
+ stateOutput.clean();
+ }
+ for (final AxArtifactKey contextAlbumReference : contextAlbumReferenceSet) {
+ contextAlbumReference.clean();
+ }
+ taskSelectionLogic.clean();
+ for (final AxStateFinalizerLogic stateFinalizerLogic : stateFinalizerLogicMap.values()) {
+ stateFinalizerLogic.clean();
+ }
+ defaultTask.clean();
+ for (final AxStateTaskReference taskReference : taskReferenceMap.values()) {
+ taskReference.clean();
+ }
+ }
+
+ /**
+ * {@inheritDoc}.
+ */
+ @Override
+ public String toString() {
+ final StringBuilder builder = new StringBuilder();
+ builder.append(this.getClass().getSimpleName());
+ builder.append(":(");
+ builder.append("stateKey=");
+ builder.append(key);
+ builder.append(",trigger=");
+ builder.append(trigger);
+ builder.append(",stateOutputs=");
+ builder.append(stateOutputs);
+ builder.append(",contextAlbumReferenceSet=");
+ builder.append(contextAlbumReferenceSet);
+ builder.append(",taskSelectionLogic=");
+ builder.append(taskSelectionLogic);
+ builder.append(",stateFinalizerLogicSet=");
+ builder.append(stateFinalizerLogicMap);
+ builder.append(",defaultTask=");
+ builder.append(defaultTask);
+ builder.append(",taskReferenceMap=");
+ builder.append(taskReferenceMap);
+ builder.append(")");
+ return builder.toString();
+ }
+
+ /**
+ * {@inheritDoc}.
+ */
+ @Override
+ public AxConcept copyTo(final AxConcept targetObject) {
+ Assertions.argumentNotNull(targetObject, "target may not be null");
+
+ final Object copyObject = targetObject;
+ Assertions.instanceOf(copyObject, AxState.class);
+
+ final AxState copy = ((AxState) copyObject);
+ copy.setKey(new AxReferenceKey(key));
+ copy.setTrigger(new AxArtifactKey(trigger));
+
+ final Map<String, AxStateOutput> newStateOutputs = new TreeMap<>();
+ for (final Entry<String, AxStateOutput> stateOutputEntry : stateOutputs.entrySet()) {
+ newStateOutputs.put(stateOutputEntry.getKey(), new AxStateOutput(stateOutputEntry.getValue()));
+ }
+ copy.setStateOutputs(newStateOutputs);
+
+ final Set<AxArtifactKey> newContextUsage = new TreeSet<>();
+ for (final AxArtifactKey contextAlbumReferenceItem : contextAlbumReferenceSet) {
+ newContextUsage.add(new AxArtifactKey(contextAlbumReferenceItem));
+ }
+ copy.setContextAlbumReferences(newContextUsage);
+
+ copy.setTaskSelectionLogic(new AxTaskSelectionLogic(taskSelectionLogic));
+
+ final Map<String, AxStateFinalizerLogic> newStateFinalizerLogicMap = new TreeMap<>();
+ for (final Entry<String, AxStateFinalizerLogic> stateFinalizerLogicEntry : stateFinalizerLogicMap.entrySet()) {
+ newStateFinalizerLogicMap.put(stateFinalizerLogicEntry.getKey(),
+ new AxStateFinalizerLogic(stateFinalizerLogicEntry.getValue()));
+ }
+ copy.setStateFinalizerLogicMap(newStateFinalizerLogicMap);
+
+ copy.setDefaultTask(new AxArtifactKey(defaultTask));
+
+ final Map<AxArtifactKey, AxStateTaskReference> newTaskReferenceMap = new TreeMap<>();
+ for (final Entry<AxArtifactKey, AxStateTaskReference> taskReferenceEntry : taskReferenceMap.entrySet()) {
+ newTaskReferenceMap.put(new AxArtifactKey(taskReferenceEntry.getKey()),
+ new AxStateTaskReference(taskReferenceEntry.getValue()));
+ }
+ copy.setTaskReferences(newTaskReferenceMap);
+
+ return copy;
+ }
+
+ /**
+ * {@inheritDoc}.
+ */
+ @Override
+ public int hashCode() {
+ final int prime = 31;
+ int result = 1;
+ result = prime * result + key.hashCode();
+ result = prime * result + trigger.hashCode();
+ result = prime * result + stateOutputs.hashCode();
+ result = prime * result + contextAlbumReferenceSet.hashCode();
+ result = prime * result + taskSelectionLogic.hashCode();
+ result = prime * result + stateFinalizerLogicMap.hashCode();
+ result = prime * result + defaultTask.hashCode();
+ result = prime * result + taskReferenceMap.hashCode();
+ return result;
+ }
+
+ /**
+ * {@inheritDoc}.
+ */
+ @Override
+ public boolean equals(final Object obj) {
+ if (obj == null) {
+ return false;
+ }
+ if (this == obj) {
+ return true;
+ }
+
+ if (getClass() != obj.getClass()) {
+ return false;
+ }
+
+ final AxState other = (AxState) obj;
+ if (!key.equals(other.key)) {
+ return false;
+ }
+ if (!trigger.equals(other.trigger)) {
+ return false;
+ }
+ if (!stateOutputs.equals(other.stateOutputs)) {
+ return false;
+ }
+ if (!contextAlbumReferenceSet.equals(other.contextAlbumReferenceSet)) {
+ return false;
+ }
+ if (!taskSelectionLogic.equals(other.taskSelectionLogic)) {
+ return false;
+ }
+ if (!stateFinalizerLogicMap.equals(other.stateFinalizerLogicMap)) {
+ return false;
+ }
+ if (!defaultTask.equals(other.defaultTask)) {
+ return false;
+ }
+ return taskReferenceMap.equals(other.taskReferenceMap);
+ }
+
+ /**
+ * {@inheritDoc}.
+ */
+ @Override
+ public int compareTo(final AxConcept otherObj) {
+ if (otherObj == null) {
+ return -1;
+ }
+ if (this == otherObj) {
+ return 0;
+ }
+ if (getClass() != otherObj.getClass()) {
+ return this.hashCode() - otherObj.hashCode();
+ }
+
+ return compareObjectFields((AxState) otherObj);
+ }
+
+ /**
+ * Compare the object fields on this state to another state.
+ *
+ * @param other the other state to compare with
+ * @return the result of the comparison
+ */
+ private int compareObjectFields(final AxState other) {
+ if (!key.equals(other.key)) {
+ return key.compareTo(other.key);
+ }
+ if (!trigger.equals(other.trigger)) {
+ return trigger.compareTo(other.trigger);
+ }
+ if (!stateOutputs.equals(other.stateOutputs)) {
+ return stateOutputs.hashCode() - other.stateOutputs.hashCode();
+ }
+ if (!contextAlbumReferenceSet.equals(other.contextAlbumReferenceSet)) {
+ return (contextAlbumReferenceSet.hashCode() - other.contextAlbumReferenceSet.hashCode());
+ }
+ if (!taskSelectionLogic.equals(other.taskSelectionLogic)) {
+ return taskSelectionLogic.compareTo(other.taskSelectionLogic);
+ }
+ if (!stateFinalizerLogicMap.equals(other.stateFinalizerLogicMap)) {
+ return stateFinalizerLogicMap.hashCode() - other.stateFinalizerLogicMap.hashCode();
+ }
+ if (!defaultTask.equals(other.defaultTask)) {
+ return defaultTask.compareTo(other.defaultTask);
+ }
+ if (!taskReferenceMap.equals(other.taskReferenceMap)) {
+ return (taskReferenceMap.hashCode() - other.taskReferenceMap.hashCode());
+ }
+
+ return 0;
+ }
+}
diff --git a/model/src/main/java/org/onap/policy/apex/model/policymodel/concepts/AxStateFinalizerLogic.java b/model/src/main/java/org/onap/policy/apex/model/policymodel/concepts/AxStateFinalizerLogic.java
new file mode 100644
index 000000000..31ffedea8
--- /dev/null
+++ b/model/src/main/java/org/onap/policy/apex/model/policymodel/concepts/AxStateFinalizerLogic.java
@@ -0,0 +1,121 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * Copyright (C) 2016-2018 Ericsson. All rights reserved.
+ * Modifications Copyright (C) 2022 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.apex.model.policymodel.concepts;
+
+import org.onap.policy.apex.model.basicmodel.concepts.AxReferenceKey;
+
+/**
+ * This class holds State Finalizer Logic for {@link AxState} states in Apex. It is a specialization
+ * of the {@link AxLogic} class, so that State Finalizer Logic in Apex states can be strongly typed.
+ *
+ * <p>State Finalizer Logic is used to select the output {@link AxStateOutput} that a state will use.
+ * The logic uses fields emitted by the executed {@link AxTask} task and information from the
+ * context albums available on a state to decide what state output {@link AxStateOutput} to select
+ * in a given context. State Finalizer Logic must marshal the output fields from the task onto the
+ * output event in whatever manner is appropriate for the domain being handled.
+ *
+ * <p>Validation uses standard Apex Logic validation, see validation in {@link AxLogic}.
+ */
+public class AxStateFinalizerLogic extends AxLogic {
+ private static final long serialVersionUID = 2090324845463750391L;
+
+ /**
+ * The Default Constructor creates a logic instance with a null key, undefined logic flavour and
+ * a null logic string.
+ */
+ public AxStateFinalizerLogic() {
+ super();
+ }
+
+ /**
+ * The Key Constructor creates a logic instance with the given reference key, undefined logic
+ * flavour and a null logic string.
+ *
+ * @param key the reference key of the logic
+ */
+ public AxStateFinalizerLogic(final AxReferenceKey key) {
+ super(key, LOGIC_FLAVOUR_UNDEFINED, "");
+ }
+
+ /**
+ * This Constructor creates a logic instance with a reference key constructed from the parents
+ * key and the logic local name and all of its fields defined.
+ *
+ * @param parentKey the reference key of the parent of this logic
+ * @param logicName the logic name, held as the local name of the reference key of this logic
+ * @param logicFlavour the flavour of this logic
+ * @param logic the actual logic as a string
+ */
+ public AxStateFinalizerLogic(final AxReferenceKey parentKey, final String logicName, final String logicFlavour,
+ final String logic) {
+ super(parentKey, logicName, logicFlavour, logic);
+ }
+
+ /**
+ * This Constructor creates a logic instance with the given reference key and all of its fields
+ * defined.
+ *
+ * @param key the reference key of this logic
+ * @param logicFlavour the flavour of this logic
+ * @param logic the actual logic as a string
+ */
+ public AxStateFinalizerLogic(final AxReferenceKey key, final String logicFlavour, final String logic) {
+ super(key, logicFlavour, logic);
+ }
+
+ /**
+ * This Constructor creates a logic instance by cloning the fields from another logic instance
+ * into this logic instance.
+ *
+ * @param logic the logic instance to clone from
+ */
+ public AxStateFinalizerLogic(final AxLogic logic) {
+ super(new AxReferenceKey(logic.getKey()), logic.getLogicFlavour(), logic.getLogic());
+ }
+
+ /**
+ * This Constructor creates a logic instance with a reference key constructed from the parents
+ * key and the logic local name, the given logic flavour, with the logic being provided by the
+ * given logic reader instance.
+ *
+ * @param parentKey the reference key of the parent of this logic
+ * @param logicName the logic name, held as the local name of the reference key of this logic
+ * @param logicFlavour the flavour of this logic
+ * @param logicReader the logic reader to use to read the logic for this logic instance
+ */
+ public AxStateFinalizerLogic(final AxReferenceKey parentKey, final String logicName, final String logicFlavour,
+ final AxLogicReader logicReader) {
+ super(new AxReferenceKey(parentKey, logicName), logicFlavour, logicReader);
+ }
+
+ /**
+ * This Constructor creates a logic instance with the given reference key and logic flavour, the
+ * logic is provided by the given logic reader instance.
+ *
+ * @param key the reference key of this logic
+ * @param logicFlavour the flavour of this logic
+ * @param logicReader the logic reader to use to read the logic for this logic instance
+ */
+ public AxStateFinalizerLogic(final AxReferenceKey key, final String logicFlavour, final AxLogicReader logicReader) {
+ super(key, logicFlavour, logicReader);
+ }
+}
diff --git a/model/src/main/java/org/onap/policy/apex/model/policymodel/concepts/AxStateOutput.java b/model/src/main/java/org/onap/policy/apex/model/policymodel/concepts/AxStateOutput.java
new file mode 100644
index 000000000..7d4695290
--- /dev/null
+++ b/model/src/main/java/org/onap/policy/apex/model/policymodel/concepts/AxStateOutput.java
@@ -0,0 +1,291 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * Copyright (C) 2016-2018 Ericsson. All rights reserved.
+ * Modifications Copyright (C) 2019,2022 Nordix Foundation.
+ * Modifications Copyright (C) 2021 AT&T Intellectual Property. All rights reserved.
+ * Modifications Copyright (C) 2021 Bell Canada. All rights reserved.
+ * ================================================================================
+ * 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.apex.model.policymodel.concepts;
+
+import com.google.gson.annotations.SerializedName;
+import java.util.List;
+import java.util.Set;
+import lombok.Getter;
+import lombok.NonNull;
+import lombok.Setter;
+import org.onap.policy.apex.model.basicmodel.concepts.AxArtifactKey;
+import org.onap.policy.apex.model.basicmodel.concepts.AxConcept;
+import org.onap.policy.apex.model.basicmodel.concepts.AxKey;
+import org.onap.policy.apex.model.basicmodel.concepts.AxKeyUse;
+import org.onap.policy.apex.model.basicmodel.concepts.AxReferenceKey;
+import org.onap.policy.apex.model.basicmodel.concepts.AxValidationMessage;
+import org.onap.policy.apex.model.basicmodel.concepts.AxValidationResult;
+import org.onap.policy.apex.model.basicmodel.concepts.AxValidationResult.ValidationResult;
+import org.onap.policy.common.utils.validation.Assertions;
+
+/**
+ * This class defines a single output that a state can have. A state can have many outputs with each
+ * output defined as an instance of this class. Each state output defines the output event that will
+ * be emitted when this output is selected and optionally the next state that is executed when this
+ * state output is selected. If no next state is defined (the next state is a null
+ * {@link AxReferenceKey} key), then this state output outputs its event to an external system and
+ * is an output state for the full policy.
+ *
+ * <p>During validation of a state output, the validation checks listed below are executed:
+ * <ol>
+ * <li>The state output key must not be a null key and must be valid, see validation in
+ * {@link AxReferenceKey}
+ * <li>The outgoing event key must not be a null key and must be valid, see validation in
+ * {@link AxArtifactKey}
+ * <li>The next state key must be valid, see validation in {@link AxReferenceKey}
+ * </ol>
+ */
+@Getter
+@Setter
+public class AxStateOutput extends AxConcept {
+ private static final long serialVersionUID = 8041771382337655535L;
+
+ @NonNull
+ private AxReferenceKey key;
+
+ @NonNull
+ private AxArtifactKey outgoingEvent;
+
+ @SerializedName("outgoingEventReference")
+ private Set<AxArtifactKey> outgoingEventSet;
+
+ @NonNull
+ private AxReferenceKey nextState;
+ // @formatter:on
+
+ /**
+ * The Default Constructor creates a state output instance with a null reference key, outgoing
+ * event key and next state reference key.
+ */
+ public AxStateOutput() {
+ this(new AxReferenceKey());
+ }
+
+ /**
+ * Copy constructor.
+ *
+ * @param copyConcept the concept to copy from
+ */
+ public AxStateOutput(final AxStateOutput copyConcept) {
+ super(copyConcept);
+ }
+
+ /**
+ * The Keyed Constructor creates a state output instance with the given reference key, outgoing
+ * event key and next state reference key.
+ *
+ * @param key the reference key for the state output
+ */
+ public AxStateOutput(final AxReferenceKey key) {
+ this(key, // Key
+ AxArtifactKey.getNullKey(), // Outgoing Event
+ AxReferenceKey.getNullKey() // Next State
+ );
+ }
+
+ /**
+ * This Constructor creates a state output with a reference key composed of the given parent key
+ * and with a local name composed from the parent key local name concatenated with the next
+ * state's local name. The next state and outgoing event of the state output are set as
+ * specified.
+ *
+ * @param parentKey the parent key of the state output
+ * @param nextState the next state to which execution will pass on use of this state output
+ * @param outgoingEvent the outgoing event emitted on use of this state output
+ */
+ public AxStateOutput(final AxReferenceKey parentKey, final AxReferenceKey nextState,
+ final AxArtifactKey outgoingEvent) {
+ this(new AxReferenceKey(parentKey, parentKey.getLocalName() + '_' + nextState.getLocalName()), outgoingEvent,
+ nextState);
+ }
+
+ /**
+ * This Constructor creates a state output with the specified reference key. The next state and
+ * outgoing event of the state output are set as specified.
+ *
+ * @param key the key
+ * @param nextState the next state to which execution will pass on use of this state output
+ * @param outgoingEvent the outgoing event emitted on use of this state output
+ */
+ public AxStateOutput(final AxReferenceKey key, final AxArtifactKey outgoingEvent, final AxReferenceKey nextState) {
+ super();
+ Assertions.argumentNotNull(key, "key may not be null");
+ Assertions.argumentNotNull(outgoingEvent, "outgoingEvent may not be null");
+ Assertions.argumentNotNull(nextState, "nextState may not be null");
+
+ this.key = key;
+ this.outgoingEvent = outgoingEvent;
+ this.nextState = nextState;
+ }
+
+ /**
+ * {@inheritDoc}.
+ */
+ @Override
+ public List<AxKey> getKeys() {
+ final List<AxKey> keyList = key.getKeys();
+ keyList.add(new AxKeyUse(outgoingEvent));
+
+ if (!nextState.equals(AxReferenceKey.getNullKey())) {
+ keyList.add(new AxKeyUse(nextState));
+ }
+
+ return keyList;
+ }
+
+ /**
+ * {@inheritDoc}.
+ */
+ @Override
+ public AxValidationResult validate(final AxValidationResult resultIn) {
+ AxValidationResult result = resultIn;
+
+ if (key.equals(AxReferenceKey.getNullKey())) {
+ result.addValidationMessage(
+ new AxValidationMessage(key, this.getClass(), ValidationResult.INVALID, "key is a null key"));
+ }
+
+ result = key.validate(result);
+
+ if (outgoingEvent.equals(AxArtifactKey.getNullKey())) {
+ result.addValidationMessage(new AxValidationMessage(key, this.getClass(), ValidationResult.INVALID,
+ "outgoingEvent reference is a null key, an outgoing event must be specified"));
+ }
+ result = outgoingEvent.validate(result);
+
+ // Note: Null keys are allowed on nextState as there may not be a next state
+ result = nextState.validate(result);
+
+ return result;
+ }
+
+ /**
+ * {@inheritDoc}.
+ */
+ @Override
+ public void clean() {
+ key.clean();
+ outgoingEvent.clean();
+ nextState.clean();
+ }
+
+ /**
+ * {@inheritDoc}.
+ */
+ @Override
+ public String toString() {
+ final StringBuilder builder = new StringBuilder();
+ builder.append(this.getClass().getSimpleName());
+ builder.append(":(");
+ builder.append("stateKey=");
+ builder.append(key);
+ builder.append(",outgoingEvent=");
+ builder.append(outgoingEvent);
+ builder.append(",nextState=");
+ builder.append(nextState);
+ builder.append(")");
+ return builder.toString();
+ }
+
+ /**
+ * {@inheritDoc}.
+ */
+ @Override
+ public AxConcept copyTo(final AxConcept targetObject) {
+ Assertions.argumentNotNull(targetObject, "target may not be null");
+
+ final Object copyObject = targetObject;
+ Assertions.instanceOf(copyObject, AxStateOutput.class);
+
+ final AxStateOutput copy = ((AxStateOutput) copyObject);
+ copy.setKey(new AxReferenceKey(key));
+ copy.setOutgoingEvent(new AxArtifactKey(outgoingEvent));
+ copy.setNextState(nextState);
+
+ return copy;
+ }
+
+ /**
+ * {@inheritDoc}.
+ */
+ @Override
+ public int hashCode() {
+ final int prime = 31;
+ int result = 1;
+ result = prime * result + key.hashCode();
+ result = prime * result + outgoingEvent.hashCode();
+ result = prime * result + nextState.hashCode();
+ return result;
+ }
+
+ /**
+ * {@inheritDoc}.
+ */
+ @Override
+ public boolean equals(final Object obj) {
+ if (obj == null) {
+ return false;
+ }
+ if (this == obj) {
+ return true;
+ }
+ if (getClass() != obj.getClass()) {
+ return false;
+ }
+
+ final AxStateOutput other = (AxStateOutput) obj;
+ if (!key.equals(other.key)) {
+ return false;
+ }
+ if (!outgoingEvent.equals(other.outgoingEvent)) {
+ return false;
+ }
+ return nextState.equals(other.nextState);
+ }
+
+ /**
+ * {@inheritDoc}.
+ */
+ @Override
+ public int compareTo(final AxConcept otherObj) {
+ if (otherObj == null) {
+ return -1;
+ }
+ if (this == otherObj) {
+ return 0;
+ }
+ if (getClass() != otherObj.getClass()) {
+ return this.hashCode() - otherObj.hashCode();
+ }
+
+ final AxStateOutput other = (AxStateOutput) otherObj;
+ if (!key.equals(other.key)) {
+ return key.compareTo(other.key);
+ }
+ if (!outgoingEvent.equals(other.outgoingEvent)) {
+ return outgoingEvent.compareTo(other.outgoingEvent);
+ }
+ return nextState.compareTo(other.nextState);
+ }
+}
diff --git a/model/src/main/java/org/onap/policy/apex/model/policymodel/concepts/AxStateParamsBuilder.java b/model/src/main/java/org/onap/policy/apex/model/policymodel/concepts/AxStateParamsBuilder.java
new file mode 100644
index 000000000..6ef6e0b0a
--- /dev/null
+++ b/model/src/main/java/org/onap/policy/apex/model/policymodel/concepts/AxStateParamsBuilder.java
@@ -0,0 +1,162 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * Copyright (C) 2018 Samsung Electronics Co., Ltd. All rights reserved.
+ * ================================================================================
+ * 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.apex.model.policymodel.concepts;
+
+import java.util.Map;
+import java.util.Set;
+import org.onap.policy.apex.model.basicmodel.concepts.AxArtifactKey;
+import org.onap.policy.apex.model.basicmodel.concepts.AxReferenceKey;
+
+public class AxStateParamsBuilder {
+ private AxReferenceKey key;
+ private AxArtifactKey trigger;
+ private Map<String, AxStateOutput> stateOutputs;
+ private Set<AxArtifactKey> contextAlbumReferenceSet;
+ private AxTaskSelectionLogic taskSelectionLogic;
+ private Map<String, AxStateFinalizerLogic> stateFinalizerLogicMap;
+ private AxArtifactKey defaultTask;
+ private Map<AxArtifactKey, AxStateTaskReference> taskReferenceMap;
+
+ public AxReferenceKey getKey() {
+ return key;
+ }
+
+ public AxArtifactKey getTrigger() {
+ return trigger;
+ }
+
+ public Map<String, AxStateOutput> getStateOutputs() {
+ return stateOutputs;
+ }
+
+ public Set<AxArtifactKey> getContextAlbumReferenceSet() {
+ return contextAlbumReferenceSet;
+ }
+
+ public AxTaskSelectionLogic getTaskSelectionLogic() {
+ return taskSelectionLogic;
+ }
+
+ public Map<String, AxStateFinalizerLogic> getStateFinalizerLogicMap() {
+ return stateFinalizerLogicMap;
+ }
+
+ public AxArtifactKey getDefaultTask() {
+ return defaultTask;
+ }
+
+ public Map<AxArtifactKey, AxStateTaskReference> getTaskReferenceMap() {
+ return taskReferenceMap;
+ }
+
+ /**
+ * Setter method.
+ *
+ * @param key the reference key of the state
+ * @return builder object
+ */
+ public AxStateParamsBuilder key(AxReferenceKey key) {
+ this.key = key;
+ return this;
+ }
+
+ /**
+ * Setter method.
+ *
+ * @param trigger the event that triggers the state
+ * @return builder object
+ */
+ public AxStateParamsBuilder trigger(AxArtifactKey trigger) {
+ this.trigger = trigger;
+ return this;
+ }
+
+ /**
+ * Setter method.
+ *
+ * @param stateOutputs the possible state outputs for the state
+ * @return builder object
+ */
+ public AxStateParamsBuilder stateOutputs(Map<String, AxStateOutput> stateOutputs) {
+ this.stateOutputs = stateOutputs;
+ return this;
+ }
+
+ /**
+ * Setter method.
+ *
+ * @param contextAlbumReferenceSet the context album reference set defines the context that may
+ * be used by Task Selection Logic and State Finalizer Logic in the state
+ * @return builder object
+ */
+ public AxStateParamsBuilder contextAlbumReferenceSet(Set<AxArtifactKey> contextAlbumReferenceSet) {
+ this.contextAlbumReferenceSet = contextAlbumReferenceSet;
+ return this;
+ }
+
+ /**
+ * Setter method.
+ *
+ * @param taskSelectionLogic the task selection logic that selects the task a state executes in
+ * an execution cycle
+ * @return builder object
+ */
+ public AxStateParamsBuilder taskSelectionLogic(AxTaskSelectionLogic taskSelectionLogic) {
+ this.taskSelectionLogic = taskSelectionLogic;
+ return this;
+ }
+
+ /**
+ * Setter method.
+ *
+ * @param stateFinalizerLogicMap the state finalizer logic instances that selects the state
+ * output to use after a task executes in a state execution cycle
+ * @return builder object
+ */
+ public AxStateParamsBuilder stateFinalizerLogicMap(
+ Map<String, AxStateFinalizerLogic> stateFinalizerLogicMap) {
+ this.stateFinalizerLogicMap = stateFinalizerLogicMap;
+ return this;
+ }
+
+ /**
+ * Setter method.
+ *
+ * @param defaultTask the default task that will execute in a state if Task Selection Logic is
+ * not specified
+ * @return builder object
+ */
+ public AxStateParamsBuilder defaultTask(AxArtifactKey defaultTask) {
+ this.defaultTask = defaultTask;
+ return this;
+ }
+
+ /**
+ * Setter method.
+ *
+ * @param taskReferenceMap the task reference map that defines the tasks for the state and how
+ * @return builder object
+ */
+ public AxStateParamsBuilder taskReferenceMap(Map<AxArtifactKey, AxStateTaskReference> taskReferenceMap) {
+ this.taskReferenceMap = taskReferenceMap;
+ return this;
+ }
+}
diff --git a/model/src/main/java/org/onap/policy/apex/model/policymodel/concepts/AxStateTaskOutputType.java b/model/src/main/java/org/onap/policy/apex/model/policymodel/concepts/AxStateTaskOutputType.java
new file mode 100644
index 000000000..171747c0c
--- /dev/null
+++ b/model/src/main/java/org/onap/policy/apex/model/policymodel/concepts/AxStateTaskOutputType.java
@@ -0,0 +1,42 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * Copyright (C) 2016-2018 Ericsson. All rights reserved.
+ * Modifications Copyright (C) 2022 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.apex.model.policymodel.concepts;
+
+/**
+ * This enumeration defines the type of state output selection that is defined for a task in a
+ * state. The {@link AxStateTaskReference} instance for each task uses this enumeration to decide
+ * what type of output selection to use when a task has completed execution.
+ */
+public enum AxStateTaskOutputType {
+ /** The state output selection for the task has not been defined. */
+ UNDEFINED,
+ /**
+ * Direct state output selection has been selected, the task will select a {@link AxStateOutput}
+ * directly.
+ */
+ DIRECT,
+ /**
+ * Logic state output selection has been selected, the task will select a {@link AxStateOutput}
+ * using logic defined in a {@link AxStateFinalizerLogic} instance.
+ */
+ LOGIC
+}
diff --git a/model/src/main/java/org/onap/policy/apex/model/policymodel/concepts/AxStateTaskReference.java b/model/src/main/java/org/onap/policy/apex/model/policymodel/concepts/AxStateTaskReference.java
new file mode 100644
index 000000000..748eca92d
--- /dev/null
+++ b/model/src/main/java/org/onap/policy/apex/model/policymodel/concepts/AxStateTaskReference.java
@@ -0,0 +1,339 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * Copyright (C) 2016-2018 Ericsson. All rights reserved.
+ * Modifications Copyright (C) 2019,2022 Nordix Foundation.
+ * Modifications Copyright (C) 2021 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.apex.model.policymodel.concepts;
+
+import java.util.List;
+import org.onap.policy.apex.model.basicmodel.concepts.AxArtifactKey;
+import org.onap.policy.apex.model.basicmodel.concepts.AxConcept;
+import org.onap.policy.apex.model.basicmodel.concepts.AxKey;
+import org.onap.policy.apex.model.basicmodel.concepts.AxKeyUse;
+import org.onap.policy.apex.model.basicmodel.concepts.AxReferenceKey;
+import org.onap.policy.apex.model.basicmodel.concepts.AxValidationMessage;
+import org.onap.policy.apex.model.basicmodel.concepts.AxValidationResult;
+import org.onap.policy.apex.model.basicmodel.concepts.AxValidationResult.ValidationResult;
+import org.onap.policy.common.utils.validation.Assertions;
+
+/**
+ * This class defines the type of output handling that will be used when a task in a state completes
+ * execution. Each task {@link AxTask} in a state {@link AxState} must select a state output
+ * {@link AxStateOutput} in order to pass its fields to an output event. Therefore, each task has an
+ * associated instance of this class that defines how the state output of the state is selected and
+ * how the output fields of the task are marshaled onto the fields of the output event. A
+ * {@link AxStateTaskReference} instance defines the task output handling as either
+ * {@link AxStateTaskOutputType#DIRECT} or {@link AxStateTaskOutputType#LOGIC}. In the case of
+ * {@link AxStateTaskOutputType#DIRECT} output selection, the output reference key held in this
+ * {@link AxStateTaskReference} instance to an instance of an {@link AxStateOutput} class. In the
+ * case of {@link AxStateTaskOutputType#LOGIC} output selection, the output reference key held in
+ * this {@link AxStateTaskReference} instance to an instance of an {@link AxStateFinalizerLogic}
+ * class. See the explanation in the {@link AxState} class for a full description of this handling.
+ *
+ * <p>During validation of a state task reference, the validation checks listed below are executed:
+ * <ol>
+ * <li>The state task reference key must not be a null key and must be valid, see validation in
+ * {@link AxReferenceKey}
+ * <li>The output type must be defined, that is not equal to {@link AxStateTaskOutputType#UNDEFINED}
+ * <li>The output key must not be a null key and must be valid, see validation in
+ * {@link AxReferenceKey}
+ * </ol>
+ */
+public class AxStateTaskReference extends AxConcept {
+ private static final long serialVersionUID = 8041771382337655535L;
+
+ private AxReferenceKey key;
+ private AxStateTaskOutputType outputType;
+ private AxReferenceKey output;
+
+ /**
+ * The Default Constructor creates a state task reference with a null reference key, an
+ * undefined output type and a null output reference key.
+ */
+ public AxStateTaskReference() {
+ this(new AxReferenceKey());
+ }
+
+ /**
+ * Copy constructor.
+ *
+ * @param copyConcept the concept to copy from
+ */
+ public AxStateTaskReference(final AxStateTaskReference copyConcept) {
+ super(copyConcept);
+ }
+
+ /**
+ * The Keyed Constructor creates a state task reference with the given reference key, an
+ * undefined output type and a null output reference key.
+ *
+ * @param key the key
+ */
+ public AxStateTaskReference(final AxReferenceKey key) {
+ this(key, // Key
+ AxStateTaskOutputType.UNDEFINED, // Output type
+ AxReferenceKey.getNullKey()); // Output
+ }
+
+ /**
+ * This Constructor creates a state task reference instance with a reference key composed from
+ * the given parent key with a local name composed by concatenating the name of the task key
+ * with the local name of the output. The output type and output are set to the given values.
+ *
+ * @param parentKey the parent key to use for the key of the state task reference
+ * @param taskKey the task key to use for the first part of the state task reference local name
+ * @param outputType the type of output to perform when this state task reference instance is
+ * used
+ * @param output the output to perform when this state task reference instance is used
+ */
+ public AxStateTaskReference(final AxReferenceKey parentKey, final AxArtifactKey taskKey,
+ final AxStateTaskOutputType outputType, final AxReferenceKey output) {
+ this(new AxReferenceKey(parentKey, taskKey.getName() + '_' + outputType.name() + '_' + output.getLocalName()),
+ outputType, output);
+ }
+
+ /**
+ * This Constructor creates a state task reference instance with the given reference key and all
+ * its fields defined.
+ *
+ * @param key the key of the state task reference
+ * @param outputType the type of output to perform when this state task reference instance is
+ * used
+ * @param output the output to perform when this state task reference instance is used
+ */
+ public AxStateTaskReference(final AxReferenceKey key, final AxStateTaskOutputType outputType,
+ final AxReferenceKey output) {
+ super();
+ Assertions.argumentNotNull(key, "key may not be null");
+ Assertions.argumentNotNull(outputType, "outputType may not be null");
+ Assertions.argumentNotNull(output, "output may not be null");
+
+ this.key = key;
+ this.outputType = outputType;
+ this.output = output;
+ }
+
+ /**
+ * {@inheritDoc}.
+ */
+ @Override
+ public AxReferenceKey getKey() {
+ return key;
+ }
+
+ /**
+ * {@inheritDoc}.
+ */
+ @Override
+ public List<AxKey> getKeys() {
+ final List<AxKey> keyList = key.getKeys();
+
+ if (!output.equals(AxReferenceKey.getNullKey())) {
+ keyList.add(new AxKeyUse(output));
+ }
+
+ return keyList;
+ }
+
+ /**
+ * Sets the key of the state task reference.
+ *
+ * @param key the key of the state task reference
+ */
+ public void setKey(final AxReferenceKey key) {
+ Assertions.argumentNotNull(key, "key may not be null");
+ this.key = key;
+ }
+
+ /**
+ * Gets the type of output to perform when this state task reference instance is used.
+ *
+ * @return the the type of output to perform when this state task reference instance is used
+ */
+ public AxStateTaskOutputType getStateTaskOutputType() {
+ return outputType;
+ }
+
+ /**
+ * Sets the type of output to perform when this state task reference instance is used.
+ *
+ * @param stateTaskOutputType the type of output to perform when this state task reference
+ * instance is used
+ */
+ public void setStateTaskOutputType(final AxStateTaskOutputType stateTaskOutputType) {
+ Assertions.argumentNotNull(stateTaskOutputType, "outputType may not be null");
+ this.outputType = stateTaskOutputType;
+ }
+
+ /**
+ * Gets the output to perform when this state task reference instance is used.
+ *
+ * @return the output to perform when this state task reference instance is used
+ */
+ public AxReferenceKey getOutput() {
+ return output;
+ }
+
+ /**
+ * Sets the output to perform when this state task reference instance is used.
+ *
+ * @param output the output to perform when this state task reference instance is used
+ */
+ public void setOutput(final AxReferenceKey output) {
+ Assertions.argumentNotNull(output, "output may not be null");
+ this.output = output;
+ }
+
+ /**
+ * {@inheritDoc}.
+ */
+ @Override
+ public AxValidationResult validate(final AxValidationResult resultIn) {
+ AxValidationResult result = resultIn;
+
+ if (key.equals(AxReferenceKey.getNullKey())) {
+ result.addValidationMessage(
+ new AxValidationMessage(key, this.getClass(), ValidationResult.INVALID, "key is a null key"));
+ }
+
+ result = key.validate(result);
+
+ if (outputType == AxStateTaskOutputType.UNDEFINED) {
+ result.addValidationMessage(new AxValidationMessage(key, this.getClass(), ValidationResult.INVALID,
+ "outputType may not be UNDEFINED"));
+ }
+
+ if (output.equals(AxReferenceKey.getNullKey())) {
+ result.addValidationMessage(new AxValidationMessage(key, this.getClass(), ValidationResult.INVALID,
+ "output key " + output.getId() + " is a null key"));
+ }
+ result = output.validate(result);
+
+ return result;
+ }
+
+ /**
+ * {@inheritDoc}.
+ */
+ @Override
+ public void clean() {
+ key.clean();
+ output.clean();
+ }
+
+ /**
+ * {@inheritDoc}.
+ */
+ @Override
+ public String toString() {
+ final StringBuilder builder = new StringBuilder();
+ builder.append(this.getClass().getSimpleName());
+ builder.append(":(");
+ builder.append("stateKey=");
+ builder.append(key);
+ builder.append(",outputType=");
+ builder.append(outputType);
+ builder.append(",output=");
+ builder.append(output);
+ builder.append(")");
+ return builder.toString();
+ }
+
+ /**
+ * {@inheritDoc}.
+ */
+ @Override
+ public AxConcept copyTo(final AxConcept targetObject) {
+ Assertions.argumentNotNull(targetObject, "target may not be null");
+
+ final Object copyObject = targetObject;
+ Assertions.instanceOf(copyObject, AxStateTaskReference.class);
+
+ final AxStateTaskReference copy = ((AxStateTaskReference) copyObject);
+ copy.setKey(new AxReferenceKey(key));
+ copy.setStateTaskOutputType(AxStateTaskOutputType.valueOf(outputType.name()));
+ copy.setOutput(output);
+
+ return copy;
+ }
+
+ /**
+ * {@inheritDoc}.
+ */
+ @Override
+ public int hashCode() {
+ final int prime = 31;
+ int result = 1;
+ result = prime * result + key.hashCode();
+ result = prime * result + outputType.hashCode();
+ result = prime * result + output.hashCode();
+ return result;
+ }
+
+ /**
+ * {@inheritDoc}.
+ */
+ @Override
+ public boolean equals(final Object obj) {
+ if (obj == null) {
+ return false;
+ }
+ if (this == obj) {
+ return true;
+ }
+ if (getClass() != obj.getClass()) {
+ return false;
+ }
+
+ final AxStateTaskReference other = (AxStateTaskReference) obj;
+ if (!key.equals(other.key)) {
+ return false;
+ }
+ if (outputType != other.outputType) {
+ return false;
+ }
+ return output.equals(other.output);
+ }
+
+ /**
+ * {@inheritDoc}.
+ */
+ @Override
+ public int compareTo(final AxConcept otherObj) {
+ if (otherObj == null) {
+ return -1;
+ }
+ if (this == otherObj) {
+ return 0;
+ }
+ if (getClass() != otherObj.getClass()) {
+ return this.hashCode() - otherObj.hashCode();
+ }
+
+ final AxStateTaskReference other = (AxStateTaskReference) otherObj;
+ if (!key.equals(other.key)) {
+ return key.compareTo(other.key);
+ }
+ if (!outputType.equals(other.outputType)) {
+ return outputType.compareTo(other.outputType);
+ }
+ return output.compareTo(other.output);
+ }
+}
diff --git a/model/src/main/java/org/onap/policy/apex/model/policymodel/concepts/AxStateTree.java b/model/src/main/java/org/onap/policy/apex/model/policymodel/concepts/AxStateTree.java
new file mode 100644
index 000000000..a27e1af1e
--- /dev/null
+++ b/model/src/main/java/org/onap/policy/apex/model/policymodel/concepts/AxStateTree.java
@@ -0,0 +1,173 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * Copyright (C) 2016-2018 Ericsson. All rights reserved.
+ * Modifications Copyright (C) 2019 Nordix Foundation.
+ * Modifications Copyright (C) 2021 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.apex.model.policymodel.concepts;
+
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.LinkedHashSet;
+import java.util.List;
+import java.util.Set;
+import java.util.TreeSet;
+import lombok.EqualsAndHashCode;
+import org.onap.policy.apex.model.basicmodel.concepts.AxReferenceKey;
+import org.onap.policy.common.utils.validation.Assertions;
+
+/**
+ * The Class is used to return the tree that represents the state branches or chains in a policy. It
+ * creates a tree that holds the state fan out branches in a policy that starts from the given top
+ * state of the tree. Each branch from a state is held in a set of next states for the top state and
+ * each branch in the state tree is itself a {@link AxStateTree} instance.
+ *
+ * <p>Validation checks for recursive state use, in other words validation forbids the use of a given
+ * state more than once in a state tree.
+ */
+@EqualsAndHashCode
+public class AxStateTree implements Comparable<AxStateTree> {
+ private final AxState thisState;
+ private final Set<AxStateTree> nextStates;
+
+ /**
+ * This constructor recursively creates a state tree for the given policy starting at the given
+ * state.
+ *
+ * @param policy the policy from which to read states
+ * @param thisState the state to start state tree construction at
+ * @param referencedStateNameSet a set of state names already referenced in the tree, null for
+ * the first recursive call
+ */
+ public AxStateTree(final AxPolicy policy, final AxState thisState, Set<AxReferenceKey> referencedStateNameSet) {
+ Assertions.argumentNotNull(policy, "policy may not be null");
+ Assertions.argumentNotNull(thisState, "thisState may not be null");
+
+ this.thisState = thisState;
+ nextStates = new TreeSet<>();
+
+ for (final AxStateOutput stateOutput : thisState.getStateOutputs().values()) {
+ final AxState nextState = policy.getStateMap().get(stateOutput.getNextState().getLocalName());
+
+ // Check for end of state branch
+ if (stateOutput.getNextState().equals(AxReferenceKey.getNullKey())) {
+ continue;
+ }
+
+ if (referencedStateNameSet == null) {
+ referencedStateNameSet = new LinkedHashSet<>();
+ referencedStateNameSet.add(thisState.getKey());
+ }
+
+ // Check for state tree loops
+ if (referencedStateNameSet.contains(nextState.getKey())) {
+ throw new PolicyRuntimeException("loop detected in state tree for policy " + policy.getId() + " state "
+ + thisState.getKey().getLocalName() + ", next state " + nextState.getKey().getLocalName()
+ + " referenced more than once");
+ }
+ referencedStateNameSet.add(stateOutput.getNextState());
+ nextStates.add(new AxStateTree(policy, nextState, referencedStateNameSet));
+ }
+ }
+
+ /**
+ * Gets the state for this state tree node.
+ *
+ * @return the state
+ */
+ public AxState getState() {
+ return thisState;
+ }
+
+ /**
+ * Gets the next states for this state tree node.
+ *
+ * @return the next states
+ */
+ public Set<AxStateTree> getNextStates() {
+ return nextStates;
+ }
+
+ /**
+ * Gets the list of states referenced by this state tree as a list.
+ *
+ * @return the list of states referenced
+ */
+ public List<AxState> getReferencedStateList() {
+ final List<AxState> referencedStateList = new ArrayList<>();
+
+ referencedStateList.add(thisState);
+ for (final AxStateTree nextStateTree : nextStates) {
+ referencedStateList.addAll(nextStateTree.getReferencedStateList());
+ }
+
+ return referencedStateList;
+ }
+
+ /**
+ * Gets the list of states referenced by this state tree as a set.
+ *
+ * @return the set of states referenced
+ */
+ public Set<AxState> getReferencedStateSet() {
+ final Set<AxState> referencedStateSet = new TreeSet<>();
+
+ referencedStateSet.add(thisState);
+ for (final AxStateTree nextStateTree : nextStates) {
+ referencedStateSet.addAll(nextStateTree.getReferencedStateSet());
+ }
+
+ return referencedStateSet;
+ }
+
+ /**
+ * {@inheritDoc}.
+ */
+ @Override
+ public int compareTo(final AxStateTree otherObj) {
+ Assertions.argumentNotNull(otherObj, "comparison object may not be null");
+
+ if (this == otherObj) {
+ return 0;
+ }
+
+ final AxStateTree other = otherObj;
+ int result = thisState.compareTo(other.thisState);
+ if (result != 0) {
+ return result;
+ }
+
+ result = Integer.compare(nextStates.size(), other.nextStates.size());
+ if (result != 0) {
+ return result;
+ }
+
+ Iterator<AxStateTree> iter1 = nextStates.iterator();
+ Iterator<AxStateTree> iter2 = other.nextStates.iterator();
+
+ while (iter1.hasNext()) {
+ result = iter1.next().compareTo(iter2.next());
+ if (result != 0) {
+ return result;
+ }
+ }
+
+ return 0;
+ }
+}
diff --git a/model/src/main/java/org/onap/policy/apex/model/policymodel/concepts/AxTask.java b/model/src/main/java/org/onap/policy/apex/model/policymodel/concepts/AxTask.java
new file mode 100644
index 000000000..c8b6baf4b
--- /dev/null
+++ b/model/src/main/java/org/onap/policy/apex/model/policymodel/concepts/AxTask.java
@@ -0,0 +1,405 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * Copyright (C) 2016-2018 Ericsson. All rights reserved.
+ * Modifications Copyright (C) 2019-2022 Nordix Foundation.
+ * Modifications Copyright (C) 2021 Bell Canada. All rights reserved.
+ * ================================================================================
+ * 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.apex.model.policymodel.concepts;
+
+import com.google.gson.annotations.SerializedName;
+import java.util.List;
+import java.util.Map;
+import java.util.Map.Entry;
+import java.util.Set;
+import java.util.TreeMap;
+import java.util.TreeSet;
+import lombok.Getter;
+import lombok.NonNull;
+import lombok.Setter;
+import org.onap.policy.apex.model.basicmodel.concepts.AxArtifactKey;
+import org.onap.policy.apex.model.basicmodel.concepts.AxConcept;
+import org.onap.policy.apex.model.basicmodel.concepts.AxKey;
+import org.onap.policy.apex.model.basicmodel.concepts.AxKeyUse;
+import org.onap.policy.apex.model.basicmodel.concepts.AxReferenceKey;
+import org.onap.policy.apex.model.basicmodel.concepts.AxValidationMessage;
+import org.onap.policy.apex.model.basicmodel.concepts.AxValidationResult;
+import org.onap.policy.apex.model.basicmodel.concepts.AxValidationResult.ValidationResult;
+import org.onap.policy.apex.model.eventmodel.concepts.AxEvent;
+import org.onap.policy.common.utils.validation.Assertions;
+
+/**
+ * This class holds the definition of a task in Apex. A task is executed by a state and performs
+ * some domain specific logic to carry out work required to be done by a policy. The Task Logic that
+ * is executed by a task is held in a {@link AxTaskLogic} instance.
+ *
+ * <p>A task has a set of input fields and output fields, which are passed to and are emitted from the
+ * task during a task execution cycle. A task may have task parameters {@link AxTaskParameter},
+ * which are configuration values passed to a task at initialization time.
+ *
+ * <p>The Task Logic in a task may use information in context albums to perform their domain specific
+ * work. The context albums that the task uses and that should be made available to the task by Apex
+ * policy distribution are held as a set of references to context albums in the task.
+ *
+ * <p>During validation of a task, the validation checks listed below are executed:
+ * <ol>
+ * <li>The task key must not be a null key and must be valid, see validation in
+ * {@link AxArtifactKey}
+ * <li>The parent of each task parameter of a task must be that task
+ * <li>Each task parameter must be valid, see validation in {@link AxTaskParameter}
+ * <li>The parent of the task logic in a task must be that task
+ * <li>The task logic must be valid, see validation in {@link AxTaskLogic}
+ * </ol>
+ */
+@Getter
+@Setter
+public class AxTask extends AxConcept {
+ private static final String DOES_NOT_EQUAL_TASK_KEY = " does not equal task key";
+
+ private static final long serialVersionUID = 5374237330697362762L;
+
+ @NonNull
+ private AxArtifactKey key;
+
+ private AxEvent inputEvent;
+ private Map<String, AxEvent> outputEvents;
+ private Map<String, AxTaskParameter> taskParameters;
+
+ @SerializedName("contextAlbumReference")
+ @NonNull
+ private Set<AxArtifactKey> contextAlbumReferenceSet;
+
+ @NonNull
+ private AxTaskLogic taskLogic;
+
+ /**
+ * The Default Constructor creates a task with a null key no input or output fields, no task
+ * parameters, no context album references and no logic.
+ */
+ public AxTask() {
+ this(new AxArtifactKey());
+ contextAlbumReferenceSet = new TreeSet<>();
+ }
+
+ /**
+ * Copy constructor.
+ *
+ * @param copyConcept the concept to copy from
+ */
+ public AxTask(final AxTask copyConcept) {
+ super(copyConcept);
+ }
+
+ /**
+ * The Keyed Constructor creates a task with the given key no input or output fields, no task
+ * parameters, no context album references and no logic.
+ *
+ * @param key the key of the task
+ */
+ public AxTask(final AxArtifactKey key) {
+ this(key, // Task Key
+ new TreeMap<>(), // Task Parameters
+ new TreeSet<>(), // Context Album References
+ new AxTaskLogic(new AxReferenceKey(key)) // Task Logic
+ );
+ }
+
+ /**
+ * This Constructor defines all the fields of the task.
+ *
+ * @param key the key of the task
+ * @param taskParameters the task parameters that are used to initialize tasks of this type
+ * @param contextAlbumReferenceSet the context album reference set defines the context that may
+ * be used by Task Logic in the state
+ * @param taskLogic the task logic that performs the domain specific work of the task
+ */
+ public AxTask(final AxArtifactKey key, final Map<String, AxTaskParameter> taskParameters,
+ final Set<AxArtifactKey> contextAlbumReferenceSet, final AxTaskLogic taskLogic) {
+ super();
+ Assertions.argumentNotNull(key, "key may not be null");
+ Assertions.argumentNotNull(taskParameters, "taskParameters may not be null");
+ Assertions.argumentNotNull(contextAlbumReferenceSet, "contextAlbumReferenceSet may not be null");
+ Assertions.argumentNotNull(taskLogic, "taskLogic may not be null");
+
+ this.key = key;
+ this.taskParameters = taskParameters;
+ this.contextAlbumReferenceSet = contextAlbumReferenceSet;
+ this.taskLogic = taskLogic;
+ }
+
+ /**
+ * When a task is deserialized from disk or from the database, the parent of contained objects
+ * is not defined. This method is called by JAXB after deserialization and is used to set the
+ * parent keys of all {@link AxTaskParameter} instance in the task.
+ */
+ @Override
+ public void buildReferences() {
+ if (!taskLogic.getKey().getLocalName().equals(AxKey.NULL_KEY_NAME)) {
+ taskLogic.getKey().setParentArtifactKey(key);
+ }
+
+ taskParameters.values().stream().forEach(parameter -> parameter.getKey().setParentArtifactKey(key));
+ }
+
+ /**
+ * {@inheritDoc}.
+ */
+ @Override
+ public List<AxKey> getKeys() {
+ final List<AxKey> keyList = key.getKeys();
+ for (final AxTaskParameter taskParameter : taskParameters.values()) {
+ keyList.addAll(taskParameter.getKeys());
+ }
+ for (final AxArtifactKey contextAlbumKey : contextAlbumReferenceSet) {
+ keyList.add(new AxKeyUse(contextAlbumKey));
+ }
+ keyList.addAll(taskLogic.getKeys());
+ return keyList;
+ }
+
+ /**
+ * Gets the context album reference set defines the context that may be used by Task Logic in
+ * the state.
+ *
+ * @return the context album reference set defines the context that may be used by Task Logic in
+ * the state
+ */
+ public Set<AxArtifactKey> getContextAlbumReferences() {
+ return contextAlbumReferenceSet;
+ }
+
+ /**
+ * Sets the context album reference set defines the context that may be used by Task Logic in
+ * the state.
+ *
+ * @param contextAlbumReferences the context album reference set defines the context that may be
+ * used by Task Logic in the state
+ */
+ public void setContextAlbumReferences(final Set<AxArtifactKey> contextAlbumReferences) {
+ Assertions.argumentNotNull(contextAlbumReferences, "contextAlbumReferences may not be null");
+ this.contextAlbumReferenceSet = contextAlbumReferences;
+ }
+
+ /**
+ * {@inheritDoc}.
+ */
+ @Override
+ public AxValidationResult validate(final AxValidationResult resultIn) {
+ AxValidationResult result = resultIn;
+
+ if (key.equals(AxArtifactKey.getNullKey())) {
+ result.addValidationMessage(
+ new AxValidationMessage(key, this.getClass(), ValidationResult.INVALID, "key is a null key"));
+ }
+
+ result = key.validate(result);
+
+ for (final Entry<String, AxTaskParameter> taskParameterEntry : taskParameters.entrySet()) {
+ result = validateTaskParameterEntry(taskParameterEntry, result);
+ }
+
+ for (final AxArtifactKey contextAlbumReference : contextAlbumReferenceSet) {
+ result = validateContextAlbumReference(contextAlbumReference, result);
+ }
+
+ if (!taskLogic.getKey().getParentArtifactKey().equals(key)) {
+ result.addValidationMessage(new AxValidationMessage(key, this.getClass(), ValidationResult.INVALID,
+ "taskLogic parent key " + taskLogic.getKey().getId() + DOES_NOT_EQUAL_TASK_KEY));
+ }
+
+ return taskLogic.validate(result);
+ }
+
+ /**
+ * Validate a task parameter entry.
+ *
+ * @param taskParameterEntry the task parameter entry to validate
+ * @param result The validation result to append to
+ * @return The result of the validation
+ */
+ private AxValidationResult validateTaskParameterEntry(final Entry<String, AxTaskParameter> taskParameterEntry,
+ AxValidationResult result) {
+ if (taskParameterEntry.getValue() == null) {
+ result.addValidationMessage(new AxValidationMessage(key, this.getClass(), ValidationResult.INVALID,
+ "null input task parameter value found on task parameter " + taskParameterEntry.getKey()));
+ } else {
+ if (!taskParameterEntry.getValue().getKey().getParentArtifactKey().equals(key)) {
+ result.addValidationMessage(new AxValidationMessage(key, this.getClass(), ValidationResult.INVALID,
+ "parent key on task parameter " + taskParameterEntry.getKey() + DOES_NOT_EQUAL_TASK_KEY));
+ }
+
+ result = taskParameterEntry.getValue().validate(result);
+ }
+
+ return result;
+ }
+
+ /**
+ * Validate a context album reference entry.
+ *
+ * @param contextAlbumReference the context album reference entry to validate
+ * @param result The validation result to append to
+ * @return The result of the validation
+ */
+ private AxValidationResult validateContextAlbumReference(final AxArtifactKey contextAlbumReference,
+ AxValidationResult result) {
+ if (contextAlbumReference.equals(AxArtifactKey.getNullKey())) {
+ result.addValidationMessage(new AxValidationMessage(key, this.getClass(), ValidationResult.INVALID,
+ "key on context item reference entry " + contextAlbumReference.getKey()
+ + " may not be the null key"));
+ }
+
+ return contextAlbumReference.validate(result);
+ }
+
+ /**
+ * {@inheritDoc}.
+ */
+ @Override
+ public void clean() {
+ key.clean();
+ for (final AxTaskParameter parameter : taskParameters.values()) {
+ parameter.clean();
+ }
+ for (final AxArtifactKey contextAlbumReference : contextAlbumReferenceSet) {
+ contextAlbumReference.clean();
+ }
+ taskLogic.clean();
+ }
+
+ /**
+ * {@inheritDoc}.
+ */
+ @Override
+ public String toString() {
+ final StringBuilder builder = new StringBuilder();
+ builder.append(this.getClass().getSimpleName());
+ builder.append(":(");
+ builder.append("key=");
+ builder.append(key);
+ builder.append(",taskParameters=");
+ builder.append(taskParameters);
+ builder.append(",contextAlbumReferenceSet=");
+ builder.append(contextAlbumReferenceSet);
+ builder.append(",taskLogic=");
+ builder.append(taskLogic);
+ builder.append(")");
+ return builder.toString();
+ }
+
+ /**
+ * {@inheritDoc}.
+ */
+ @Override
+ public AxConcept copyTo(final AxConcept targetObject) {
+ Assertions.argumentNotNull(targetObject, "target may not be null");
+
+ final Object copyObject = targetObject;
+ Assertions.instanceOf(copyObject, AxTask.class);
+
+ final AxTask copy = ((AxTask) copyObject);
+ copy.setKey(key);
+
+ final Map<String, AxTaskParameter> newTaskParameter = new TreeMap<>();
+ for (final Entry<String, AxTaskParameter> taskParameterEntry : taskParameters.entrySet()) {
+ newTaskParameter.put(taskParameterEntry.getKey(), new AxTaskParameter(taskParameterEntry.getValue()));
+ }
+ copy.setTaskParameters(newTaskParameter);
+
+ final Set<AxArtifactKey> newContextUsage = new TreeSet<>();
+ for (final AxArtifactKey contextAlbumReference : contextAlbumReferenceSet) {
+ newContextUsage.add(new AxArtifactKey(contextAlbumReference));
+ }
+ copy.setContextAlbumReferences(newContextUsage);
+
+ copy.setTaskLogic(new AxTaskLogic(taskLogic));
+
+ return copy;
+ }
+
+ /**
+ * {@inheritDoc}.
+ */
+ @Override
+ public int hashCode() {
+ final int prime = 31;
+ int result = 1;
+ result = prime * result + key.hashCode();
+ result = prime * result + taskParameters.hashCode();
+ result = prime * result + contextAlbumReferenceSet.hashCode();
+ result = prime * result + taskLogic.hashCode();
+ return result;
+ }
+
+ /**
+ * {@inheritDoc}.
+ */
+ @Override
+ public boolean equals(final Object obj) {
+ if (obj == null) {
+ return false;
+ }
+ if (this == obj) {
+ return true;
+ }
+
+ if (getClass() != obj.getClass()) {
+ return false;
+ }
+
+ final AxTask other = (AxTask) obj;
+ if (!key.equals(other.key)) {
+ return false;
+ }
+ if (!taskParameters.equals(other.taskParameters)) {
+ return false;
+ }
+ if (!contextAlbumReferenceSet.equals(other.contextAlbumReferenceSet)) {
+ return false;
+ }
+ return taskLogic.equals(other.taskLogic);
+ }
+
+ /**
+ * {@inheritDoc}.
+ */
+ @Override
+ public int compareTo(final AxConcept otherObj) {
+ if (otherObj == null) {
+ return -1;
+ }
+ if (this == otherObj) {
+ return 0;
+ }
+ if (getClass() != otherObj.getClass()) {
+ return this.hashCode() - otherObj.hashCode();
+ }
+
+ final AxTask other = (AxTask) otherObj;
+ if (!key.equals(other.key)) {
+ return key.compareTo(other.key);
+ }
+ if (!taskParameters.equals(other.taskParameters)) {
+ return (taskParameters.hashCode() - other.taskParameters.hashCode());
+ }
+ if (!contextAlbumReferenceSet.equals(other.contextAlbumReferenceSet)) {
+ return (contextAlbumReferenceSet.hashCode() - other.contextAlbumReferenceSet.hashCode());
+ }
+ return taskLogic.compareTo(other.taskLogic);
+ }
+}
diff --git a/model/src/main/java/org/onap/policy/apex/model/policymodel/concepts/AxTaskLogic.java b/model/src/main/java/org/onap/policy/apex/model/policymodel/concepts/AxTaskLogic.java
new file mode 100644
index 000000000..8665d9e3e
--- /dev/null
+++ b/model/src/main/java/org/onap/policy/apex/model/policymodel/concepts/AxTaskLogic.java
@@ -0,0 +1,120 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * Copyright (C) 2016-2018 Ericsson. All rights reserved.
+ * Modifications Copyright (C) 2022 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.apex.model.policymodel.concepts;
+
+import org.onap.policy.apex.model.basicmodel.concepts.AxArtifactKey;
+import org.onap.policy.apex.model.basicmodel.concepts.AxReferenceKey;
+
+/**
+ * This class holds Task Logic for {@link AxTask} tasks in Apex. It is a specialization of the
+ * {@link AxLogic} class, so that Task Logic in Apex states can be strongly typed.
+ *
+ * <p>Task Logic is used to execute tasks {@link AxTask} in Apex. The logic uses fields on the incoming
+ * trigger event and information from the context albums available on a task to get context during
+ * execution. The task logic populates the output fields of the task.
+ *
+ * <p>Validation uses standard Apex Logic validation, see validation in {@link AxLogic}.
+ */
+public class AxTaskLogic extends AxLogic {
+ private static final long serialVersionUID = 2090324845463750391L;
+
+ /**
+ * The Default Constructor creates a logic instance with a null key, undefined logic flavour and
+ * a null logic string.
+ */
+ public AxTaskLogic() {
+ super();
+ }
+
+ /**
+ * The Key Constructor creates a logic instance with the given reference key, undefined logic
+ * flavour and a null logic string.
+ *
+ * @param key the reference key of the logic
+ */
+ public AxTaskLogic(final AxReferenceKey key) {
+ super(key, LOGIC_FLAVOUR_UNDEFINED, "");
+ }
+
+ /**
+ * This Constructor creates a logic instance with a reference key constructed from the parents
+ * key and the logic local name and all of its fields defined.
+ *
+ * @param parentKey the reference key of the parent of this logic
+ * @param logicName the logic name, held as the local name of the reference key of this logic
+ * @param logicFlavour the flavour of this logic
+ * @param logic the actual logic as a string
+ */
+ public AxTaskLogic(final AxArtifactKey parentKey, final String logicName, final String logicFlavour,
+ final String logic) {
+ super(new AxReferenceKey(parentKey, logicName), logicFlavour, logic);
+ }
+
+ /**
+ * This Constructor creates a logic instance with the given reference key and all of its fields
+ * defined.
+ *
+ * @param key the reference key of this logic
+ * @param logicFlavour the flavour of this logic
+ * @param logic the actual logic as a string
+ */
+ public AxTaskLogic(final AxReferenceKey key, final String logicFlavour, final String logic) {
+ super(key, logicFlavour, logic);
+ }
+
+ /**
+ * This Constructor creates a logic instance by cloning the fields from another logic instance
+ * into this logic instance.
+ *
+ * @param logic the logic instance to clone from
+ */
+ public AxTaskLogic(final AxLogic logic) {
+ super(new AxReferenceKey(logic.getKey()), logic.getLogicFlavour(), logic.getLogic());
+ }
+
+ /**
+ * This Constructor creates a logic instance with a reference key constructed from the parents
+ * key and the logic local name, the given logic flavour, with the logic being provided by the
+ * given logic reader instance.
+ *
+ * @param parentKey the reference key of the parent of this logic
+ * @param logicName the logic name, held as the local name of the reference key of this logic
+ * @param logicFlavour the flavour of this logic
+ * @param logicReader the logic reader to use to read the logic for this logic instance
+ */
+ public AxTaskLogic(final AxArtifactKey parentKey, final String logicName, final String logicFlavour,
+ final AxLogicReader logicReader) {
+ super(new AxReferenceKey(parentKey, logicName), logicFlavour, logicReader);
+ }
+
+ /**
+ * This Constructor creates a logic instance with the given reference key and logic flavour, the
+ * logic is provided by the given logic reader instance.
+ *
+ * @param key the reference key of this logic
+ * @param logicFlavour the flavour of this logic
+ * @param logicReader the logic reader to use to read the logic for this logic instance
+ */
+ public AxTaskLogic(final AxReferenceKey key, final String logicFlavour, final AxLogicReader logicReader) {
+ super(key, logicFlavour, logicReader);
+ }
+}
diff --git a/model/src/main/java/org/onap/policy/apex/model/policymodel/concepts/AxTaskParameter.java b/model/src/main/java/org/onap/policy/apex/model/policymodel/concepts/AxTaskParameter.java
new file mode 100644
index 000000000..a468ec499
--- /dev/null
+++ b/model/src/main/java/org/onap/policy/apex/model/policymodel/concepts/AxTaskParameter.java
@@ -0,0 +1,253 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * Copyright (C) 2016-2018 Ericsson. All rights reserved.
+ * Modifications Copyright (C) 2019,2022 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.apex.model.policymodel.concepts;
+
+import java.util.List;
+import org.onap.policy.apex.model.basicmodel.concepts.AxConcept;
+import org.onap.policy.apex.model.basicmodel.concepts.AxKey;
+import org.onap.policy.apex.model.basicmodel.concepts.AxReferenceKey;
+import org.onap.policy.apex.model.basicmodel.concepts.AxValidationMessage;
+import org.onap.policy.apex.model.basicmodel.concepts.AxValidationResult;
+import org.onap.policy.apex.model.basicmodel.concepts.AxValidationResult.ValidationResult;
+import org.onap.policy.common.utils.validation.Assertions;
+
+/**
+ * This class is used to specify the configuration parameters that may be passed to a task
+ * {@link AxTask}. Task parameters are read from a configuration file when Apex starts and are
+ * passed to the task by the Apex engine when a task is executed. Each task parameter has a key and
+ * a default value. If the task parameter is not set in a configuration file, the task uses its
+ * default value.
+ */
+public class AxTaskParameter extends AxConcept {
+ private static final long serialVersionUID = 7351688156934099977L;
+
+ private AxReferenceKey key;
+ private String defaultValue;
+
+ /**
+ * The Default Constructor creates a task parameter with a null reference key and a null default
+ * value.
+ */
+ public AxTaskParameter() {
+ this(new AxReferenceKey());
+ }
+
+ /**
+ * Copy constructor.
+ *
+ * @param copyConcept the concept to copy from
+ */
+ public AxTaskParameter(final AxTaskParameter copyConcept) {
+ super(copyConcept);
+ }
+
+ /**
+ * The Keyed Constructor creates a task parameter with the given reference key and a null
+ * default value.
+ *
+ * @param taskParameterKey the task parameter key
+ */
+ public AxTaskParameter(final AxReferenceKey taskParameterKey) {
+ this(taskParameterKey, "");
+ }
+
+ /**
+ * The Default Constructor creates a task parameter with the given reference key and default
+ * value.
+ *
+ * @param key the reference key of the task parameter
+ * @param defaultValue the default value of the task parameter
+ */
+ public AxTaskParameter(final AxReferenceKey key, final String defaultValue) {
+ super();
+ Assertions.argumentNotNull(key, "key may not be null");
+ Assertions.argumentNotNull(defaultValue, "defaultValue may not be null");
+
+ this.key = key;
+ this.defaultValue = defaultValue.trim();
+ }
+
+ /**
+ * {@inheritDoc}.
+ */
+ @Override
+ public AxReferenceKey getKey() {
+ return key;
+ }
+
+ /**
+ * {@inheritDoc}.
+ */
+ @Override
+ public List<AxKey> getKeys() {
+ return key.getKeys();
+ }
+
+ /**
+ * Sets the reference key of the task parameter.
+ *
+ * @param key the reference key of the task parameter
+ */
+ public void setKey(final AxReferenceKey key) {
+ Assertions.argumentNotNull(key, "key may not be null");
+ this.key = key;
+ }
+
+ /**
+ * Gets the default value of the task parameter.
+ *
+ * @return the default value of the task parameter
+ */
+ public String getTaskParameterValue() {
+ return defaultValue;
+ }
+
+ /**
+ * Sets the default value of the task parameter.
+ *
+ * @param defaultValue the default value of the task parameter
+ */
+ public void setDefaultValue(final String defaultValue) {
+ Assertions.argumentNotNull(defaultValue, "defaultValue may not be null");
+ this.defaultValue = defaultValue.trim();
+ }
+
+ /**
+ * {@inheritDoc}.
+ */
+ @Override
+ public AxValidationResult validate(final AxValidationResult resultIn) {
+ AxValidationResult result = resultIn;
+
+ if (key.equals(AxReferenceKey.getNullKey())) {
+ result.addValidationMessage(
+ new AxValidationMessage(key, this.getClass(), ValidationResult.INVALID, "key is a null key"));
+ }
+
+ result = key.validate(result);
+
+ if (defaultValue.trim().length() == 0) {
+ result.addValidationMessage(new AxValidationMessage(key, this.getClass(), ValidationResult.WARNING,
+ "no defaultValue specified, defaultValue is blank"));
+ }
+
+ return result;
+ }
+
+ /**
+ * {@inheritDoc}.
+ */
+ @Override
+ public void clean() {
+ key.clean();
+ defaultValue = defaultValue.trim();
+ }
+
+ /**
+ * {@inheritDoc}.
+ */
+ @Override
+ public String toString() {
+ final StringBuilder builder = new StringBuilder();
+ builder.append(this.getClass().getSimpleName());
+ builder.append(":(");
+ builder.append("key=");
+ builder.append(key);
+ builder.append(",defaultValue=");
+ builder.append(defaultValue);
+ builder.append(")");
+ return builder.toString();
+ }
+
+ /**
+ * {@inheritDoc}.
+ */
+ @Override
+ public AxConcept copyTo(final AxConcept targetObject) {
+ Assertions.argumentNotNull(targetObject, "target may not be null");
+
+ final Object copyObject = targetObject;
+ Assertions.instanceOf(copyObject, AxTaskParameter.class);
+
+ final AxTaskParameter copy = ((AxTaskParameter) copyObject);
+ copy.setKey(new AxReferenceKey(key));
+ copy.setDefaultValue(defaultValue);
+
+ return copy;
+ }
+
+ /**
+ * {@inheritDoc}.
+ */
+ @Override
+ public int hashCode() {
+ final int prime = 31;
+ int result = 1;
+ result = prime * result + key.hashCode();
+ result = prime * result + defaultValue.hashCode();
+ return result;
+ }
+
+ /**
+ * {@inheritDoc}.
+ */
+ @Override
+ public boolean equals(final Object obj) {
+ if (obj == null) {
+ return false;
+ }
+ if (this == obj) {
+ return true;
+ }
+ if (getClass() != obj.getClass()) {
+ return false;
+ }
+
+ final AxTaskParameter other = (AxTaskParameter) obj;
+ if (!key.equals(other.key)) {
+ return false;
+ }
+ return defaultValue.equals(other.defaultValue);
+ }
+
+ /**
+ * {@inheritDoc}.
+ */
+ @Override
+ public int compareTo(final AxConcept otherObj) {
+ if (otherObj == null) {
+ return -1;
+ }
+ if (this == otherObj) {
+ return 0;
+ }
+ if (getClass() != otherObj.getClass()) {
+ return this.hashCode() - otherObj.hashCode();
+ }
+
+ final AxTaskParameter other = (AxTaskParameter) otherObj;
+ if (!key.equals(other.key)) {
+ return key.compareTo(other.key);
+ }
+ return defaultValue.compareTo(other.defaultValue);
+ }
+}
diff --git a/model/src/main/java/org/onap/policy/apex/model/policymodel/concepts/AxTaskSelectionLogic.java b/model/src/main/java/org/onap/policy/apex/model/policymodel/concepts/AxTaskSelectionLogic.java
new file mode 100644
index 000000000..b9cfd802d
--- /dev/null
+++ b/model/src/main/java/org/onap/policy/apex/model/policymodel/concepts/AxTaskSelectionLogic.java
@@ -0,0 +1,119 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * Copyright (C) 2016-2018 Ericsson. All rights reserved.
+ * Modifications Copyright (C) 2022 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.apex.model.policymodel.concepts;
+
+import org.onap.policy.apex.model.basicmodel.concepts.AxReferenceKey;
+
+/**
+ * This class holds Task Selection Logic for {@link AxState} states in Apex. It is a specialization
+ * of the {@link AxLogic} class, so that Task Selection Logic in Apex states can be strongly typed.
+ *
+ * <p>Task Selection Logic is used to select the task {@link AxTask} that a state will execute. The
+ * logic uses fields on the incoming trigger event and information from the context albums available
+ * on a state to decide what task {@link AxTask} to select for execution in a given context.
+ *
+ * <p>Validation uses standard Apex Logic validation, see validation in {@link AxLogic}.
+ */
+public class AxTaskSelectionLogic extends AxLogic {
+ private static final long serialVersionUID = 2090324845463750391L;
+
+ /**
+ * The Default Constructor creates a logic instance with a null key, undefined logic flavour and
+ * a null logic string.
+ */
+ public AxTaskSelectionLogic() {
+ super();
+ }
+
+ /**
+ * The Key Constructor creates a logic instance with the given reference key, undefined logic
+ * flavour and a null logic string.
+ *
+ * @param key the reference key of the logic
+ */
+ public AxTaskSelectionLogic(final AxReferenceKey key) {
+ super(key, LOGIC_FLAVOUR_UNDEFINED, "");
+ }
+
+ /**
+ * This Constructor creates a logic instance with a reference key constructed from the parents
+ * key and the logic local name and all of its fields defined.
+ *
+ * @param parentKey the reference key of the parent of this logic
+ * @param logicName the logic name, held as the local name of the reference key of this logic
+ * @param logicFlavour the flavour of this logic
+ * @param logic the actual logic as a string
+ */
+ public AxTaskSelectionLogic(final AxReferenceKey parentKey, final String logicName, final String logicFlavour,
+ final String logic) {
+ super(parentKey, logicName, logicFlavour, logic);
+ }
+
+ /**
+ * This Constructor creates a logic instance with the given reference key and all of its fields
+ * defined.
+ *
+ * @param key the reference key of this logic
+ * @param logicFlavour the flavour of this logic
+ * @param logic the actual logic as a string
+ */
+ public AxTaskSelectionLogic(final AxReferenceKey key, final String logicFlavour, final String logic) {
+ super(key, logicFlavour, logic);
+ }
+
+ /**
+ * This Constructor creates a logic instance by cloning the fields from another logic instance
+ * into this logic instance.
+ *
+ * @param logic the logic instance to clone from
+ */
+ public AxTaskSelectionLogic(final AxLogic logic) {
+ super(new AxReferenceKey(logic.getKey()), logic.getLogicFlavour(), logic.getLogic());
+ }
+
+ /**
+ * This Constructor creates a logic instance with a reference key constructed from the parents
+ * key and the logic local name, the given logic flavour, with the logic being provided by the
+ * given logic reader instance.
+ *
+ * @param parentKey the reference key of the parent of this logic
+ * @param logicName the logic name, held as the local name of the reference key of this logic
+ * @param logicFlavour the flavour of this logic
+ * @param logicReader the logic reader to use to read the logic for this logic instance
+ */
+ public AxTaskSelectionLogic(final AxReferenceKey parentKey, final String logicName, final String logicFlavour,
+ final AxLogicReader logicReader) {
+ super(new AxReferenceKey(parentKey, logicName), logicFlavour, logicReader);
+ }
+
+ /**
+ * This Constructor creates a logic instance with the given reference key and logic flavour, the
+ * logic is provided by the given logic reader instance.
+ *
+ * @param key the reference key of this logic
+ * @param logicFlavour the flavour of this logic
+ * @param logicReader the logic reader to use to read the logic for this logic instance
+ */
+ public AxTaskSelectionLogic(final AxReferenceKey key, final String logicFlavour, final AxLogicReader logicReader) {
+ super(key, logicFlavour, logicReader);
+ }
+}
diff --git a/model/src/main/java/org/onap/policy/apex/model/policymodel/concepts/AxTasks.java b/model/src/main/java/org/onap/policy/apex/model/policymodel/concepts/AxTasks.java
new file mode 100644
index 000000000..f9ccafd54
--- /dev/null
+++ b/model/src/main/java/org/onap/policy/apex/model/policymodel/concepts/AxTasks.java
@@ -0,0 +1,351 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * Copyright (C) 2016-2018 Ericsson. All rights reserved.
+ * Modifications Copyright (C) 2019-2020,2022 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.apex.model.policymodel.concepts;
+
+import java.util.List;
+import java.util.Map;
+import java.util.Map.Entry;
+import java.util.NavigableMap;
+import java.util.Set;
+import java.util.TreeMap;
+import org.onap.policy.apex.model.basicmodel.concepts.AxArtifactKey;
+import org.onap.policy.apex.model.basicmodel.concepts.AxConcept;
+import org.onap.policy.apex.model.basicmodel.concepts.AxConceptGetter;
+import org.onap.policy.apex.model.basicmodel.concepts.AxConceptGetterImpl;
+import org.onap.policy.apex.model.basicmodel.concepts.AxKey;
+import org.onap.policy.apex.model.basicmodel.concepts.AxValidationMessage;
+import org.onap.policy.apex.model.basicmodel.concepts.AxValidationResult;
+import org.onap.policy.apex.model.basicmodel.concepts.AxValidationResult.ValidationResult;
+import org.onap.policy.common.utils.validation.Assertions;
+
+/**
+ * This class is a task container and holds a map of the tasks for an entire Apex model. All Apex
+ * models that use tasks must have an {@link AxTasks} field. The {@link AxTasks} class implements
+ * the helper methods of the {@link AxConceptGetter} interface to allow {@link AxTask} instances to
+ * be retrieved by calling methods directly on this class without referencing the contained map.
+ *
+ * <p>Validation checks that the container key is not null. An error is issued if no tasks are defined
+ * in the container. Each task entry is checked to ensure that its key and value are not null and
+ * that the key matches the key in the map value. Each task entry is then validated individually.
+ */
+public class AxTasks extends AxConcept implements AxConceptGetter<AxTask> {
+ private static final long serialVersionUID = 4290442590545820316L;
+
+ private AxArtifactKey key;
+ private Map<AxArtifactKey, AxTask> taskMap;
+
+ /**
+ * The Default Constructor creates a {@link AxTasks} object with a null artifact key and creates
+ * an empty event map.
+ */
+ public AxTasks() {
+ this(new AxArtifactKey());
+ }
+
+ /**
+ * Copy constructor.
+ *
+ * @param copyConcept the concept to copy from
+ */
+ public AxTasks(final AxTasks copyConcept) {
+ super(copyConcept);
+ }
+
+ /**
+ * The Keyed Constructor creates a {@link AxTasks} object with the given artifact key and
+ * creates an empty event map.
+ *
+ * @param key the key
+ */
+ public AxTasks(final AxArtifactKey key) {
+ this(key, new TreeMap<>());
+ }
+
+ /**
+ * This Constructor creates a task container with all of its fields defined.
+ *
+ * @param key the task container key
+ * @param taskMap the tasks to be stored in the task container
+ */
+ public AxTasks(final AxArtifactKey key, final Map<AxArtifactKey, AxTask> taskMap) {
+ super();
+ Assertions.argumentNotNull(key, "key may not be null");
+ Assertions.argumentNotNull(taskMap, "taskMap may not be null");
+
+ this.key = key;
+ this.taskMap = new TreeMap<>();
+ this.taskMap.putAll(taskMap);
+ }
+
+ /**
+ * {@inheritDoc}.
+ */
+ @Override
+ public AxArtifactKey getKey() {
+ return key;
+ }
+
+ /**
+ * {@inheritDoc}.
+ */
+ @Override
+ public List<AxKey> getKeys() {
+ final List<AxKey> keyList = key.getKeys();
+
+ for (final AxTask task : taskMap.values()) {
+ keyList.addAll(task.getKeys());
+ }
+
+ return keyList;
+ }
+
+ /**
+ * {@inheritDoc}.
+ */
+ @Override
+ public void buildReferences() {
+ taskMap.values().stream().forEach(task -> task.buildReferences());
+ }
+
+ /**
+ * Sets the task container key.
+ *
+ * @param key the task container key
+ */
+ public void setKey(final AxArtifactKey key) {
+ Assertions.argumentNotNull(key, "key may not be null");
+ this.key = key;
+ }
+
+ /**
+ * Gets the tasks stored in the task container.
+ *
+ * @return the tasks stored in the task container
+ */
+ public Map<AxArtifactKey, AxTask> getTaskMap() {
+ return taskMap;
+ }
+
+ /**
+ * Sets the tasks to be stored in the task container.
+ *
+ * @param taskMap the tasks to be stored in the task container
+ */
+ public void setTaskMap(final Map<AxArtifactKey, AxTask> taskMap) {
+ Assertions.argumentNotNull(taskMap, "taskMap may not be null");
+ this.taskMap = new TreeMap<>();
+ this.taskMap.putAll(taskMap);
+ }
+
+ /**
+ * {@inheritDoc}.
+ */
+ @Override
+ public AxValidationResult validate(final AxValidationResult resultIn) {
+ AxValidationResult result = resultIn;
+
+ if (key.equals(AxArtifactKey.getNullKey())) {
+ result.addValidationMessage(
+ new AxValidationMessage(key, this.getClass(), ValidationResult.INVALID, "key is a null key"));
+ }
+
+ result = key.validate(result);
+
+ if (taskMap.size() == 0) {
+ result.addValidationMessage(new AxValidationMessage(key, this.getClass(), ValidationResult.INVALID,
+ "taskMap may not be empty"));
+ } else {
+ for (final Entry<AxArtifactKey, AxTask> taskEntry : taskMap.entrySet()) {
+ if (taskEntry.getKey().equals(AxArtifactKey.getNullKey())) {
+ result.addValidationMessage(new AxValidationMessage(key, this.getClass(), ValidationResult.INVALID,
+ "key on task entry " + taskEntry.getKey() + " may not be the null key"));
+ } else if (taskEntry.getValue() == null) {
+ result.addValidationMessage(new AxValidationMessage(key, this.getClass(), ValidationResult.INVALID,
+ "value on task entry " + taskEntry.getKey() + " may not be null"));
+ } else {
+ if (!taskEntry.getKey().equals(taskEntry.getValue().getKey())) {
+ result.addValidationMessage(new AxValidationMessage(key, this.getClass(),
+ ValidationResult.INVALID, "key on task entry key " + taskEntry.getKey()
+ + " does not equal task value key " + taskEntry.getValue().getKey()));
+ }
+
+ result = taskEntry.getValue().validate(result);
+ }
+ }
+ }
+
+ return result;
+ }
+
+ /**
+ * {@inheritDoc}.
+ */
+ @Override
+ public void clean() {
+ key.clean();
+ for (final Entry<AxArtifactKey, AxTask> taskEntry : taskMap.entrySet()) {
+ taskEntry.getKey().clean();
+ taskEntry.getValue().clean();
+ }
+ }
+
+ /**
+ * {@inheritDoc}.
+ */
+ @Override
+ public String toString() {
+ final StringBuilder builder = new StringBuilder();
+ builder.append(this.getClass().getSimpleName());
+ builder.append(":(");
+ builder.append("key=");
+ builder.append(key);
+ builder.append(",taskMap=");
+ builder.append(taskMap);
+ builder.append(")");
+ return builder.toString();
+ }
+
+ /**
+ * {@inheritDoc}.
+ */
+ @Override
+ public AxConcept copyTo(final AxConcept targetObject) {
+ Assertions.argumentNotNull(targetObject, "target may not be null");
+
+ final Object copyObject = targetObject;
+ Assertions.instanceOf(copyObject, AxTasks.class);
+
+ final AxTasks copy = ((AxTasks) copyObject);
+ copy.setKey(new AxArtifactKey(key));
+
+ final Map<AxArtifactKey, AxTask> newTaskMap = new TreeMap<>();
+ for (final Entry<AxArtifactKey, AxTask> taskMapEntry : taskMap.entrySet()) {
+ newTaskMap.put(new AxArtifactKey(taskMapEntry.getKey()), new AxTask(taskMapEntry.getValue()));
+ }
+ copy.setTaskMap(newTaskMap);
+
+ return copy;
+ }
+
+ /**
+ * {@inheritDoc}.
+ */
+ @Override
+ public int hashCode() {
+ final int prime = 31;
+ int result = 1;
+ result = prime * result + key.hashCode();
+ result = prime * result + taskMap.hashCode();
+ return result;
+ }
+
+ /**
+ * {@inheritDoc}.
+ */
+ @Override
+ public boolean equals(final Object obj) {
+ if (obj == null) {
+ return false;
+ }
+ if (this == obj) {
+ return true;
+ }
+
+ if (getClass() != obj.getClass()) {
+ return false;
+ }
+
+ final AxTasks other = (AxTasks) obj;
+ if (!key.equals(other.key)) {
+ return false;
+ }
+ return taskMap.equals(other.taskMap);
+ }
+
+ /**
+ * {@inheritDoc}.
+ */
+ @Override
+ public int compareTo(final AxConcept otherObj) {
+ if (otherObj == null) {
+ return -1;
+ }
+ if (this == otherObj) {
+ return 0;
+ }
+ if (getClass() != otherObj.getClass()) {
+ return this.hashCode() - otherObj.hashCode();
+ }
+
+ final AxTasks other = (AxTasks) otherObj;
+ if (!key.equals(other.key)) {
+ return key.compareTo(other.key);
+ }
+ if (!taskMap.equals(other.taskMap)) {
+ return (taskMap.hashCode() - other.taskMap.hashCode());
+ }
+
+ return 0;
+ }
+
+ /**
+ * {@inheritDoc}.
+ */
+ @Override
+ public AxTask get(final AxArtifactKey conceptKey) {
+ return new AxConceptGetterImpl<>((NavigableMap<AxArtifactKey, AxTask>) taskMap).get(conceptKey);
+ }
+
+ /**
+ * {@inheritDoc}.
+ */
+ @Override
+ public AxTask get(final String conceptKeyName) {
+ return new AxConceptGetterImpl<>((NavigableMap<AxArtifactKey, AxTask>) taskMap).get(conceptKeyName);
+ }
+
+ /**
+ * {@inheritDoc}.
+ */
+ @Override
+ public AxTask get(final String conceptKeyName, final String conceptKeyVersion) {
+ return new AxConceptGetterImpl<>((NavigableMap<AxArtifactKey, AxTask>) taskMap).get(conceptKeyName,
+ conceptKeyVersion);
+ }
+
+ /**
+ * {@inheritDoc}.
+ */
+ @Override
+ public Set<AxTask> getAll(final String conceptKeyName) {
+ return new AxConceptGetterImpl<>((NavigableMap<AxArtifactKey, AxTask>) taskMap).getAll(conceptKeyName);
+ }
+
+ /**
+ * {@inheritDoc}.
+ */
+ @Override
+ public Set<AxTask> getAll(final String conceptKeyName, final String conceptKeyVersion) {
+ return new AxConceptGetterImpl<>((NavigableMap<AxArtifactKey, AxTask>) taskMap).getAll(conceptKeyName,
+ conceptKeyVersion);
+ }
+}
diff --git a/model/src/main/java/org/onap/policy/apex/model/policymodel/concepts/PolicyException.java b/model/src/main/java/org/onap/policy/apex/model/policymodel/concepts/PolicyException.java
new file mode 100644
index 000000000..271e0c2af
--- /dev/null
+++ b/model/src/main/java/org/onap/policy/apex/model/policymodel/concepts/PolicyException.java
@@ -0,0 +1,51 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * Copyright (C) 2016-2018 Ericsson. All rights reserved.
+ * ================================================================================
+ * 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.apex.model.policymodel.concepts;
+
+import org.onap.policy.apex.model.basicmodel.concepts.ApexException;
+
+/**
+ * This exception is raised if an error occurs in an Apex policy or in Apex policy handling.
+ *
+ * @author Liam Fallon
+ */
+public class PolicyException extends ApexException {
+ private static final long serialVersionUID = -8507246953751956974L;
+
+ /**
+ * Instantiates a new apex policy exception with a message.
+ *
+ * @param message the message
+ */
+ public PolicyException(final String message) {
+ super(message);
+ }
+
+ /**
+ * Instantiates a new apex policy exception with a message and a caused by exception.
+ *
+ * @param message the message
+ * @param exception the exception that caused this exception to be thrown
+ */
+ public PolicyException(final String message, final Exception exception) {
+ super(message, exception);
+ }
+}
diff --git a/model/src/main/java/org/onap/policy/apex/model/policymodel/concepts/PolicyRuntimeException.java b/model/src/main/java/org/onap/policy/apex/model/policymodel/concepts/PolicyRuntimeException.java
new file mode 100644
index 000000000..7ad293187
--- /dev/null
+++ b/model/src/main/java/org/onap/policy/apex/model/policymodel/concepts/PolicyRuntimeException.java
@@ -0,0 +1,51 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * Copyright (C) 2016-2018 Ericsson. All rights reserved.
+ * ================================================================================
+ * 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.apex.model.policymodel.concepts;
+
+import org.onap.policy.apex.model.basicmodel.concepts.ApexRuntimeException;
+
+/**
+ * This exception is raised if a runtime error occurs in an Apex policy or in Apex policy handling.
+ *
+ * @author Liam Fallon
+ */
+public class PolicyRuntimeException extends ApexRuntimeException {
+ private static final long serialVersionUID = -8507246953751956974L;
+
+ /**
+ * Instantiates a new apex policy runtime exception with a message.
+ *
+ * @param message the message
+ */
+ public PolicyRuntimeException(final String message) {
+ super(message);
+ }
+
+ /**
+ * Instantiates a new apex policy runtime exception with a message and a caused by exception.
+ *
+ * @param message the message
+ * @param exception the exception that caused this exception to be thrown
+ */
+ public PolicyRuntimeException(final String message, final Exception exception) {
+ super(message, exception);
+ }
+}
diff --git a/model/src/main/java/org/onap/policy/apex/model/policymodel/concepts/package-info.java b/model/src/main/java/org/onap/policy/apex/model/policymodel/concepts/package-info.java
new file mode 100644
index 000000000..acf0e8221
--- /dev/null
+++ b/model/src/main/java/org/onap/policy/apex/model/policymodel/concepts/package-info.java
@@ -0,0 +1,28 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * Copyright (C) 2016-2018 Ericsson. All rights reserved.
+ * Modifications Copyright (C) 2022 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=========================================================
+ */
+
+/**
+ * Contains the concepts required to specify policies and tasks in APEX. It defines the main Apex
+ * concepts of policies and tasks.
+ *
+ * @author Liam Fallon (liam.fallon@ericsson.com)
+ */
+package org.onap.policy.apex.model.policymodel.concepts;
diff --git a/model/src/main/java/org/onap/policy/apex/model/policymodel/handling/PolicyAnalyser.java b/model/src/main/java/org/onap/policy/apex/model/policymodel/handling/PolicyAnalyser.java
new file mode 100644
index 000000000..6731caf30
--- /dev/null
+++ b/model/src/main/java/org/onap/policy/apex/model/policymodel/handling/PolicyAnalyser.java
@@ -0,0 +1,178 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * Copyright (C) 2016-2018 Ericsson. All rights reserved.
+ * Modifications Copyright (C) 2019 Nordix Foundation.
+ * Modifications Copyright (C) 2021 Bell Canada. All rights reserved.
+ * ================================================================================
+ * 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.apex.model.policymodel.handling;
+
+import java.util.Map.Entry;
+import java.util.Set;
+import org.onap.policy.apex.model.basicmodel.concepts.AxArtifactKey;
+import org.onap.policy.apex.model.basicmodel.concepts.AxKey;
+import org.onap.policy.apex.model.contextmodel.concepts.AxContextAlbum;
+import org.onap.policy.apex.model.eventmodel.concepts.AxEvent;
+import org.onap.policy.apex.model.eventmodel.concepts.AxField;
+import org.onap.policy.apex.model.policymodel.concepts.AxPolicy;
+import org.onap.policy.apex.model.policymodel.concepts.AxPolicyModel;
+import org.onap.policy.apex.model.policymodel.concepts.AxState;
+import org.onap.policy.apex.model.policymodel.concepts.AxStateOutput;
+import org.onap.policy.apex.model.policymodel.concepts.AxTask;
+import org.onap.policy.common.utils.validation.Assertions;
+
+/**
+ * This class analyses a policy model and shows what the usage of each context album, context item, data type, and event
+ * is.
+ *
+ * @author Liam Fallon (liam.fallon@ericsson.com)
+ */
+public class PolicyAnalyser {
+ /**
+ * Perform an analysis on a policy model.
+ *
+ * @param policyModel The policy model
+ * @return the analysis result of the policy model
+ */
+ public PolicyAnalysisResult analyse(final AxPolicyModel policyModel) {
+ Assertions.argumentNotNull(policyModel, "policyModel may not be null");
+
+ final PolicyAnalysisResult result = new PolicyAnalysisResult(policyModel);
+
+ for (final AxPolicy policy : policyModel.getPolicies().getPolicyMap().values()) {
+ for (final AxState state : policy.getStateMap().values()) {
+ analyseState(state, result);
+ }
+ }
+
+ for (final AxTask task : policyModel.getTasks().getTaskMap().values()) {
+ analyseTask(task, result);
+ }
+
+ for (final AxEvent event : policyModel.getEvents().getEventMap().values()) {
+ analyseEvent(event, result);
+ }
+
+ for (final AxContextAlbum contextAlbum : policyModel.getAlbums().getAll(null)) {
+ result.getContextSchemaUsage().get(contextAlbum.getItemSchema()).add(contextAlbum.getKey());
+ }
+
+ return result;
+ }
+
+ /**
+ * Perform an analysis on a single policy in a policy model.
+ *
+ * @param policyModel The policy model
+ * @param policy The policy
+ * @return the analysis result of the policy model
+ */
+ public PolicyAnalysisResult analyse(final AxPolicyModel policyModel, final AxPolicy policy) {
+ Assertions.argumentNotNull(policyModel, "policyModel may not be null");
+ Assertions.argumentNotNull(policy, "policy may not be null");
+
+ final PolicyAnalysisResult result = new PolicyAnalysisResult(policyModel);
+
+ for (final AxState state : policy.getStateMap().values()) {
+ analyseState(state, result);
+ }
+
+ // Only analyse tasks used by this policy
+ for (final Entry<AxArtifactKey, Set<AxKey>> taskUsageEntry : result.getTaskUsage().entrySet()) {
+ // If the usage set is empty, then we skip the task as its not used in the policy
+ if (!taskUsageEntry.getValue().isEmpty()) {
+ analyseTask(policyModel.getTasks().getTaskMap().get(taskUsageEntry.getKey()), result);
+ }
+ }
+
+ // Only analyse events used by this policy, same approach as for tasks
+ for (final Entry<AxArtifactKey, Set<AxKey>> eventUsageEntry : result.getEventUsage().entrySet()) {
+ if (!eventUsageEntry.getValue().isEmpty()) {
+ analyseEvent(policyModel.getEvents().getEventMap().get(eventUsageEntry.getKey()), result);
+ }
+ }
+
+ // Only analyse context albums used by this policy, same approach as for tasks
+ for (final Entry<AxArtifactKey, Set<AxKey>> contextAlbumUsageEntry : result.getContextAlbumUsage().entrySet()) {
+ if (!contextAlbumUsageEntry.getValue().isEmpty()) {
+ final AxContextAlbum contextAlbum = policyModel.getAlbums().get(contextAlbumUsageEntry.getKey());
+ result.getContextSchemaUsage().get(contextAlbum.getItemSchema()).add(contextAlbum.getKey());
+ }
+ }
+
+ for (final AxEvent event : policyModel.getEvents().getEventMap().values()) {
+ analyseEvent(event, result);
+ }
+
+ for (final AxContextAlbum contextAlbum : policyModel.getAlbums().getAll(null)) {
+ result.getContextSchemaUsage().get(contextAlbum.getItemSchema()).add(contextAlbum.getKey());
+ }
+
+ return result;
+ }
+
+ /**
+ * Analyse the usage of concepts by a state.
+ *
+ * @param state the state to analyse
+ * @param result the result
+ */
+ private void analyseState(final AxState state, final PolicyAnalysisResult result) {
+ // Event usage by state
+ result.getEventUsage().get(state.getTrigger()).add(state.getKey());
+ for (final AxStateOutput stateOutput : state.getStateOutputs().values()) {
+ result.getEventUsage().get(stateOutput.getOutgoingEvent()).add(state.getKey());
+ }
+
+ // State Context Usage
+ for (final AxArtifactKey contextAlbumKey : state.getContextAlbumReferences()) {
+ result.getContextAlbumUsage().get(contextAlbumKey).add(state.getKey());
+ }
+
+ // Task usage by state
+ for (final AxArtifactKey task : state.getTaskReferences().keySet()) {
+ result.getTaskUsage().get(task).add(state.getKey());
+ }
+ }
+
+ /**
+ * Analyse the usage of concepts by a task.
+ *
+ * @param task the task to analyse
+ * @param result the result
+ */
+ private void analyseTask(final AxTask task, final PolicyAnalysisResult result) {
+ // Task Context Usage
+ for (final AxArtifactKey contextAlbumKey : task.getContextAlbumReferences()) {
+ result.getContextAlbumUsage().get(contextAlbumKey).add(task.getKey());
+ }
+ }
+
+ /**
+ * Analyse the usage of concepts by an event.
+ *
+ * @param event the event to analyse
+ * @param result the result of the analysis
+ */
+ private void analyseEvent(final AxEvent event, final PolicyAnalysisResult result) {
+ // Event data type usage
+ for (final AxField eventField : event.getFields()) {
+ result.getContextSchemaUsage().get(eventField.getSchema()).add(event.getKey());
+ }
+ }
+}
diff --git a/model/src/main/java/org/onap/policy/apex/model/policymodel/handling/PolicyAnalysisResult.java b/model/src/main/java/org/onap/policy/apex/model/policymodel/handling/PolicyAnalysisResult.java
new file mode 100644
index 000000000..96bdc57d0
--- /dev/null
+++ b/model/src/main/java/org/onap/policy/apex/model/policymodel/handling/PolicyAnalysisResult.java
@@ -0,0 +1,266 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * Copyright (C) 2016-2018 Ericsson. All rights reserved.
+ * Modifications Copyright (C) 2020 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.apex.model.policymodel.handling;
+
+import java.util.Map;
+import java.util.Map.Entry;
+import java.util.Set;
+import java.util.TreeMap;
+import java.util.TreeSet;
+import org.onap.policy.apex.model.basicmodel.concepts.AxArtifactKey;
+import org.onap.policy.apex.model.basicmodel.concepts.AxKey;
+import org.onap.policy.apex.model.contextmodel.concepts.AxContextAlbum;
+import org.onap.policy.apex.model.policymodel.concepts.AxPolicyModel;
+
+/**
+ * This class finds and holds the usage of context schemas, context albums, events, and tasks by the policies in a
+ * policy model.
+ *
+ * @author Liam Fallon (liam.fallon@ericsson.com)
+ */
+public class PolicyAnalysisResult {
+ // Usage of context schemas
+ private final Map<AxArtifactKey, Set<AxKey>> contextSchemaUsage = new TreeMap<>();
+
+ // Usage of context maps
+ private final Map<AxArtifactKey, Set<AxKey>> contextAlbumUsage = new TreeMap<>();
+
+ // Usage of events
+ private final Map<AxArtifactKey, Set<AxKey>> eventUsage = new TreeMap<>();
+
+ // Usage of tasks
+ private final Map<AxArtifactKey, Set<AxKey>> taskUsage = new TreeMap<>();
+
+ /**
+ * This constructor creates a {@link PolicyAnalysisResult} instance that holds maps that contain the usage of
+ * context schemas, contxt albums, events, and tasks by all policies in a policy model.
+ *
+ * @param policyModel the policy model to analyse
+ */
+ public PolicyAnalysisResult(final AxPolicyModel policyModel) {
+ for (final AxArtifactKey contextSchemaKey : policyModel.getSchemas().getSchemasMap().keySet()) {
+ contextSchemaUsage.put(contextSchemaKey, new TreeSet<>());
+ }
+
+ for (final Entry<AxArtifactKey, AxContextAlbum> contextAlbumEntry : policyModel.getAlbums().getAlbumsMap()
+ .entrySet()) {
+ contextAlbumUsage.put(contextAlbumEntry.getKey(), new TreeSet<>());
+ }
+
+ for (final AxArtifactKey eventKey : policyModel.getEvents().getEventMap().keySet()) {
+ eventUsage.put(eventKey, new TreeSet<>());
+ }
+
+ for (final AxArtifactKey taskKey : policyModel.getTasks().getTaskMap().keySet()) {
+ taskUsage.put(taskKey, new TreeSet<>());
+ }
+ }
+
+ /**
+ * Gets the context schemas used by policies in the policy model.
+ *
+ * @return the context schemas used by policies in the policy model
+ */
+ public Map<AxArtifactKey, Set<AxKey>> getContextSchemaUsage() {
+ return contextSchemaUsage;
+ }
+
+ /**
+ * Gets the context albums used by policies in the policy model.
+ *
+ * @return the context albums used by policies in the policy model
+ */
+ public Map<AxArtifactKey, Set<AxKey>> getContextAlbumUsage() {
+ return contextAlbumUsage;
+ }
+
+ /**
+ * Gets the events used by policies in the policy model.
+ *
+ * @return the events used by policies in the policy model
+ */
+ public Map<AxArtifactKey, Set<AxKey>> getEventUsage() {
+ return eventUsage;
+ }
+
+ /**
+ * Gets the tasks used by policies in the policy model.
+ *
+ * @return the tasks used by policies in the policy model
+ */
+ public Map<AxArtifactKey, Set<AxKey>> getTaskUsage() {
+ return taskUsage;
+ }
+
+ /**
+ * Gets the context schemas used by policies in the policy model.
+ *
+ * @return the context schemas used by policies in the policy model
+ */
+ public Set<AxArtifactKey> getUsedContextSchemas() {
+ return getUsedKeySet(contextSchemaUsage);
+ }
+
+ /**
+ * Gets the context albums used by policies in the policy model.
+ *
+ * @return the context albums used by policies in the policy model
+ */
+ public Set<AxArtifactKey> getUsedContextAlbums() {
+ return getUsedKeySet(contextAlbumUsage);
+ }
+
+ /**
+ * Gets the events used by policies in the policy model.
+ *
+ * @return the events used by policies in the policy model
+ */
+ public Set<AxArtifactKey> getUsedEvents() {
+ return getUsedKeySet(eventUsage);
+ }
+
+ /**
+ * Gets the tasks used by policies in the policy model.
+ *
+ * @return the tasks used by policies in the policy model
+ */
+ public Set<AxArtifactKey> getUsedTasks() {
+ return getUsedKeySet(taskUsage);
+ }
+
+ /**
+ * Gets the context schemas in the policy model that were not used by any policies in the policy model.
+ *
+ * @return the unused context schemas
+ */
+ public Set<AxArtifactKey> getUnusedContextSchemas() {
+ return getUnusedKeySet(contextSchemaUsage);
+ }
+
+ /**
+ * Gets the context albums in the policy model that were not used by any policies in the policy model.
+ *
+ * @return the unused context albums
+ */
+ public Set<AxArtifactKey> getUnusedContextAlbums() {
+ return getUnusedKeySet(contextAlbumUsage);
+ }
+
+ /**
+ * Gets the events in the policy model that were not used by any policies in the policy model.
+ *
+ * @return the unused events
+ */
+ public Set<AxArtifactKey> getUnusedEvents() {
+ return getUnusedKeySet(eventUsage);
+ }
+
+ /**
+ * Gets the tasks in the policy model that were not used by any policies in the policy model.
+ *
+ * @return the unused tasks
+ */
+ public Set<AxArtifactKey> getUnusedTasks() {
+ return getUnusedKeySet(taskUsage);
+ }
+
+ /**
+ * {@inheritDoc}.
+ */
+ @Override
+ public String toString() {
+ final StringBuilder builder = new StringBuilder();
+
+ builder.append(getUsageMapString("Context Schema usage", contextSchemaUsage));
+ builder.append(getUsageMapString("Context Album usage", contextAlbumUsage));
+ builder.append(getUsageMapString("Event usage", eventUsage));
+ builder.append(getUsageMapString("Task usage", taskUsage));
+
+ return builder.toString();
+ }
+
+ /**
+ * Gets the usage map string.
+ *
+ * @param header the header
+ * @param usageMap the usage map
+ * @return the usage map string
+ */
+ private String getUsageMapString(final String header, final Map<? extends AxKey, Set<AxKey>> usageMap) {
+ final StringBuilder builder = new StringBuilder();
+
+ builder.append(header);
+ builder.append('\n');
+ for (final Entry<? extends AxKey, Set<AxKey>> usageEntry : usageMap.entrySet()) {
+ builder.append(" ");
+ builder.append(usageEntry.getKey().getId());
+ if (usageEntry.getValue().isEmpty()) {
+ builder.append(" (unused)\n");
+ continue;
+ }
+
+ builder.append('\n');
+ for (final AxKey usageKey : usageEntry.getValue()) {
+ builder.append(" ");
+ builder.append(usageKey.getId());
+ builder.append("\n");
+ }
+ }
+ return builder.toString();
+ }
+
+ /**
+ * Gets the used key set.
+ *
+ * @param usageMap the usage map
+ * @return the used key set
+ */
+ private Set<AxArtifactKey> getUsedKeySet(final Map<AxArtifactKey, Set<AxKey>> usageMap) {
+ final Set<AxArtifactKey> usedKeySet = new TreeSet<>();
+
+ for (final Entry<AxArtifactKey, Set<AxKey>> usageEntry : usageMap.entrySet()) {
+ if (!usageEntry.getValue().isEmpty()) {
+ usedKeySet.add(usageEntry.getKey());
+ }
+ }
+
+ return usedKeySet;
+ }
+
+ /**
+ * Gets the unused key set.
+ *
+ * @param usageMap the usage map
+ * @return the unused key set
+ */
+ private Set<AxArtifactKey> getUnusedKeySet(final Map<AxArtifactKey, Set<AxKey>> usageMap) {
+ final Set<AxArtifactKey> usedKeySet = new TreeSet<>();
+
+ for (final Entry<AxArtifactKey, Set<AxKey>> usageEntry : usageMap.entrySet()) {
+ if (usageEntry.getValue().isEmpty()) {
+ usedKeySet.add(usageEntry.getKey());
+ }
+ }
+
+ return usedKeySet;
+ }
+}
diff --git a/model/src/main/java/org/onap/policy/apex/model/policymodel/handling/PolicyComparer.java b/model/src/main/java/org/onap/policy/apex/model/policymodel/handling/PolicyComparer.java
new file mode 100644
index 000000000..5a947c596
--- /dev/null
+++ b/model/src/main/java/org/onap/policy/apex/model/policymodel/handling/PolicyComparer.java
@@ -0,0 +1,46 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * Copyright (C) 2016-2018 Ericsson. All rights reserved.
+ * ================================================================================
+ * 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.apex.model.policymodel.handling;
+
+import org.onap.policy.apex.model.basicmodel.concepts.AxArtifactKey;
+import org.onap.policy.apex.model.policymodel.concepts.AxPolicies;
+import org.onap.policy.apex.model.policymodel.concepts.AxPolicy;
+import org.onap.policy.apex.model.utilities.comparison.KeyedMapComparer;
+import org.onap.policy.apex.model.utilities.comparison.KeyedMapDifference;
+
+/**
+ * This class compares the policies in two {@link AxPolicies} objects and returns the differences.
+ *
+ * @author Liam Fallon (liam.fallon@ericsson.com)
+ */
+public class PolicyComparer {
+ /**
+ * Compare two {@link AxPolicies} objects, comparing their policy maps one after another.
+ *
+ * @param left the left policies
+ * @param right the right policies
+ * @return the difference
+ */
+ public KeyedMapDifference<AxArtifactKey, AxPolicy> compare(final AxPolicies left, final AxPolicies right) {
+ // Find the difference between the AxPolicy objects
+ return new KeyedMapComparer<AxArtifactKey, AxPolicy>().compareMaps(left.getPolicyMap(), right.getPolicyMap());
+ }
+}
diff --git a/model/src/main/java/org/onap/policy/apex/model/policymodel/handling/PolicyLogicReader.java b/model/src/main/java/org/onap/policy/apex/model/policymodel/handling/PolicyLogicReader.java
new file mode 100644
index 000000000..f70068454
--- /dev/null
+++ b/model/src/main/java/org/onap/policy/apex/model/policymodel/handling/PolicyLogicReader.java
@@ -0,0 +1,142 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * Copyright (C) 2016-2018 Ericsson. All rights reserved.
+ * Modifications Copyright (C) 2020 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.apex.model.policymodel.handling;
+
+import org.onap.policy.apex.model.basicmodel.concepts.AxKey;
+import org.onap.policy.apex.model.policymodel.concepts.AxLogic;
+import org.onap.policy.apex.model.policymodel.concepts.AxLogicReader;
+import org.onap.policy.apex.model.policymodel.concepts.PolicyRuntimeException;
+import org.onap.policy.common.utils.resources.ResourceUtils;
+import org.slf4j.ext.XLogger;
+import org.slf4j.ext.XLoggerFactory;
+
+/**
+ * This class is used to read Task Logic and Task Selection Logic from files into a string. A
+ * {@link PolicyLogicReader} can then be used to provide the logic on a {@link AxLogic} class
+ * constructor.
+ *
+ * @author Liam Fallon (liam.fallon@ericsson.com)
+ */
+public class PolicyLogicReader implements AxLogicReader {
+ private static final String DOT_JAVA = ".java.";
+
+ private static final XLogger LOGGER = XLoggerFactory.getXLogger(PolicyModelSplitter.class);
+
+ // The path of the logic package
+ private String logicPackage = "";
+
+ // Flag indicating if default logic should be returned
+ private String defaultLogic;
+
+ /**
+ * {@inheritDoc}.
+ */
+ @Override
+ public String getLogicPackage() {
+ return logicPackage;
+ }
+
+ /**
+ * {@inheritDoc}.
+ */
+ @Override
+ public AxLogicReader setLogicPackage(final String incomingLogicPackage) {
+ this.logicPackage = incomingLogicPackage;
+ return this;
+ }
+
+ /**
+ * {@inheritDoc}.
+ */
+ @Override
+ public String getDefaultLogic() {
+ return defaultLogic;
+ }
+
+ /**
+ * {@inheritDoc}.
+ */
+ @Override
+ public AxLogicReader setDefaultLogic(final String incomingDefaultLogic) {
+ this.defaultLogic = incomingDefaultLogic;
+ return this;
+ }
+
+ /**
+ * {@inheritDoc}.
+ */
+ @Override
+ public String readLogic(final AxLogic axLogic) {
+ // Java uses compiled logic, other executor types run scripts
+ if ("JAVA".equals(axLogic.getLogicFlavour())) {
+ // Check if we're using the default logic
+ if (defaultLogic != null) {
+ // Return the java class name for the default logic
+ return logicPackage + DOT_JAVA + defaultLogic;
+ } else {
+ // Return the java class name for the logic
+ if (axLogic.getKey().getParentLocalName().equals(AxKey.NULL_KEY_NAME)) {
+ return logicPackage + DOT_JAVA + axLogic.getKey().getParentKeyName()
+ + axLogic.getKey().getLocalName();
+ } else {
+ return logicPackage + DOT_JAVA + axLogic.getKey().getParentKeyName()
+ + axLogic.getKey().getParentLocalName() + axLogic.getKey().getLocalName();
+ }
+ }
+ }
+ // Now, we read in the script
+
+ // Get the package name of the current package and convert dots to slashes for the file path
+ String fullLogicFilePath = logicPackage.replace(".", "/");
+
+ // Now, the logic should be in a sub directory for the logic executor type
+ fullLogicFilePath += "/" + axLogic.getLogicFlavour().toLowerCase();
+
+ // Check if we're using the default logic
+ if (defaultLogic != null) {
+ // Default logic
+ fullLogicFilePath += "/" + defaultLogic;
+ } else {
+ if (axLogic.getKey().getParentLocalName().equals(AxKey.NULL_KEY_NAME)) {
+ fullLogicFilePath += "/" + axLogic.getKey().getParentKeyName() + axLogic.getKey().getLocalName();
+ } else {
+ fullLogicFilePath += "/" + axLogic.getKey().getParentKeyName()
+ + axLogic.getKey().getParentLocalName() + axLogic.getKey().getLocalName();
+ }
+ }
+
+ // Now get the type of executor to find the extension of the file
+ fullLogicFilePath += "." + axLogic.getLogicFlavour().toLowerCase();
+
+ final String logicString = ResourceUtils.getResourceAsString(fullLogicFilePath);
+
+ // Check if the logic was found
+ if (logicString == null || logicString.length() == 0) {
+ String errorMessage = "logic not found for logic \"" + fullLogicFilePath + "\"";
+ LOGGER.warn(errorMessage);
+ throw new PolicyRuntimeException(errorMessage);
+ }
+
+ // Return the right trimmed logic string
+ return logicString.replaceAll("\\s+$", "");
+ }
+}
diff --git a/model/src/main/java/org/onap/policy/apex/model/policymodel/handling/PolicyModelComparer.java b/model/src/main/java/org/onap/policy/apex/model/policymodel/handling/PolicyModelComparer.java
new file mode 100644
index 000000000..f3bbdcbcf
--- /dev/null
+++ b/model/src/main/java/org/onap/policy/apex/model/policymodel/handling/PolicyModelComparer.java
@@ -0,0 +1,268 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * Copyright (C) 2016-2018 Ericsson. All rights reserved.
+ * ================================================================================
+ * 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.apex.model.policymodel.handling;
+
+import org.onap.policy.apex.model.basicmodel.concepts.AxArtifactKey;
+import org.onap.policy.apex.model.basicmodel.concepts.AxKeyInfo;
+import org.onap.policy.apex.model.contextmodel.concepts.AxContextAlbum;
+import org.onap.policy.apex.model.contextmodel.concepts.AxContextSchema;
+import org.onap.policy.apex.model.eventmodel.concepts.AxEvent;
+import org.onap.policy.apex.model.policymodel.concepts.AxPolicy;
+import org.onap.policy.apex.model.policymodel.concepts.AxPolicyModel;
+import org.onap.policy.apex.model.policymodel.concepts.AxTask;
+import org.onap.policy.apex.model.utilities.comparison.KeyComparer;
+import org.onap.policy.apex.model.utilities.comparison.KeyDifference;
+import org.onap.policy.apex.model.utilities.comparison.KeyedMapComparer;
+import org.onap.policy.apex.model.utilities.comparison.KeyedMapDifference;
+
+/**
+ * This class compares two policy models {@link AxPolicyModel} and holds the result of that comparison. It compares
+ * policy models on their keys, their context schema differences, their event differences, their context album
+ * differences, their task differences, their policy differences, and their key information differences.
+ *
+ * @author Liam Fallon (liam.fallon@ericsson.com)
+ */
+public class PolicyModelComparer {
+ // Comparison of policy model keys
+ private final KeyDifference<AxArtifactKey> policyModelsKeyDifference;
+
+ // Comparison of context schemas
+ private final KeyDifference<AxArtifactKey> contextSchemasKeyDifference;
+ private KeyedMapDifference<AxArtifactKey, AxContextSchema> contextSchemaCompareResult = new KeyedMapDifference<>();
+
+ // Comparison of events
+ private final KeyDifference<AxArtifactKey> eventsKeyDifference;
+ private KeyedMapDifference<AxArtifactKey, AxEvent> eventComparisonResult = new KeyedMapDifference<>();
+
+ // Comparison of context albums
+ private final KeyDifference<AxArtifactKey> contextAlbumKeyDifference;
+ private KeyedMapDifference<AxArtifactKey, AxContextAlbum> contextAlbumComparisonResult = new KeyedMapDifference<>();
+
+ // Comparison of tasks
+ private final KeyDifference<AxArtifactKey> tasksKeyDifference;
+ private KeyedMapDifference<AxArtifactKey, AxTask> taskComparisonResult = new KeyedMapDifference<>();
+
+ // Comparison of policies
+ private final KeyDifference<AxArtifactKey> policiesKeyDifference;
+ private KeyedMapDifference<AxArtifactKey, AxPolicy> policyComparisonResult = new KeyedMapDifference<>();
+
+ // Comparison of key information
+ private final KeyDifference<AxArtifactKey> keyInformationKeyDifference;
+ private KeyedMapDifference<AxArtifactKey, AxKeyInfo> keyInfoComparisonResult = new KeyedMapDifference<>();
+
+ /**
+ * The Constructor.
+ *
+ * @param left the left
+ * @param right the right
+ */
+ public PolicyModelComparer(final AxPolicyModel left, final AxPolicyModel right) {
+ // @formatter:off
+ policyModelsKeyDifference = new KeyComparer<AxArtifactKey>().compareKeys(left.getKey(), right.getKey());
+ contextSchemasKeyDifference = new KeyComparer<AxArtifactKey>().compareKeys(left.getKey(), right.getKey());
+ eventsKeyDifference = new KeyComparer<AxArtifactKey>().compareKeys(left.getKey(), right.getKey());
+ contextAlbumKeyDifference = new KeyComparer<AxArtifactKey>().compareKeys(left.getKey(), right.getKey());
+ tasksKeyDifference = new KeyComparer<AxArtifactKey>().compareKeys(left.getKey(), right.getKey());
+ policiesKeyDifference = new KeyComparer<AxArtifactKey>().compareKeys(left.getKey(), right.getKey());
+ keyInformationKeyDifference = new KeyComparer<AxArtifactKey>().compareKeys(left.getKey(), right.getKey());
+
+ contextSchemaCompareResult = new KeyedMapComparer<AxArtifactKey, AxContextSchema>().compareMaps(
+ left.getSchemas().getSchemasMap(), right.getSchemas().getSchemasMap());
+ eventComparisonResult = new KeyedMapComparer<AxArtifactKey, AxEvent>().compareMaps(
+ left.getEvents().getEventMap(), right.getEvents().getEventMap());
+ contextAlbumComparisonResult = new KeyedMapComparer<AxArtifactKey, AxContextAlbum>().compareMaps(
+ left.getAlbums().getAlbumsMap(), right.getAlbums().getAlbumsMap());
+ taskComparisonResult = new KeyedMapComparer<AxArtifactKey, AxTask>()
+ .compareMaps(left.getTasks().getTaskMap(), right.getTasks().getTaskMap());
+ policyComparisonResult = new KeyedMapComparer<AxArtifactKey, AxPolicy>().compareMaps(
+ left.getPolicies().getPolicyMap(), right.getPolicies().getPolicyMap());
+ keyInfoComparisonResult = new KeyedMapComparer<AxArtifactKey, AxKeyInfo>().compareMaps(
+ left.getKeyInformation().getKeyInfoMap(), right.getKeyInformation().getKeyInfoMap());
+ // @formatter:on
+ }
+
+ /**
+ * Gets the difference between policy model keys on the two models.
+ *
+ * @return the difference between policy model keys
+ */
+ public KeyDifference<AxArtifactKey> getPolicyModelsKeyDifference() {
+ return policyModelsKeyDifference;
+ }
+
+ /**
+ * Gets the difference between context schema keys on the two models.
+ *
+ * @return the difference between context schema keys
+ */
+ public KeyDifference<AxArtifactKey> getContextSchemaKeyDifference() {
+ return contextSchemasKeyDifference;
+ }
+
+ /**
+ * Gets the difference between context schemas on the two models.
+ *
+ * @return the difference between context schemas
+ */
+ public KeyedMapDifference<AxArtifactKey, AxContextSchema> getContextSchemaComparisonResult() {
+ return contextSchemaCompareResult;
+ }
+
+ /**
+ * Gets the difference between event keys on the two models.
+ *
+ * @return the difference between event keys
+ */
+ public KeyDifference<AxArtifactKey> getEventKeyDifference() {
+ return eventsKeyDifference;
+ }
+
+ /**
+ * Gets the difference between the events on the two models.
+ *
+ * @return the difference between the events
+ */
+ public KeyedMapDifference<AxArtifactKey, AxEvent> getEventComparisonResult() {
+ return eventComparisonResult;
+ }
+
+ /**
+ * Gets the difference between context album keys on the two models.
+ *
+ * @return the difference between context album keys
+ */
+ public KeyDifference<AxArtifactKey> getContextAlbumKeyDifference() {
+ return contextAlbumKeyDifference;
+ }
+
+ /**
+ * Gets the difference between the context albums on the two models.
+ *
+ * @return the difference between the context albums
+ */
+ public KeyedMapDifference<AxArtifactKey, AxContextAlbum> getContextAlbumComparisonResult() {
+ return contextAlbumComparisonResult;
+ }
+
+ /**
+ * Gets the difference between task keys on the two models.
+ *
+ * @return the difference between task keys
+ */
+ public KeyDifference<AxArtifactKey> getTaskKeyDifference() {
+ return tasksKeyDifference;
+ }
+
+ /**
+ * Gets the difference between the tasks on the two models.
+ *
+ * @return the difference between the tasks
+ */
+ public KeyedMapDifference<AxArtifactKey, AxTask> getTaskComparisonResult() {
+ return taskComparisonResult;
+ }
+
+ /**
+ * Gets the difference between policy keys on the two models.
+ *
+ * @return the difference between policy keys
+ */
+ public KeyDifference<AxArtifactKey> getPolicykeyDifference() {
+ return policiesKeyDifference;
+ }
+
+ /**
+ * Gets the difference between the policies on the two models.
+ *
+ * @return the difference between the policies
+ */
+ public KeyedMapDifference<AxArtifactKey, AxPolicy> getPolicyComparisonResult() {
+ return policyComparisonResult;
+ }
+
+ /**
+ * Gets the difference between key information keys on the two models.
+ *
+ * @return the difference between key information keys
+ */
+ public KeyDifference<AxArtifactKey> getKeyInformationKeyDifference() {
+ return keyInformationKeyDifference;
+ }
+
+ /**
+ * Gets the difference between the key information on the two models.
+ *
+ * @return the difference between the key information
+ */
+ public KeyedMapDifference<AxArtifactKey, AxKeyInfo> getKeyInfoComparisonResult() {
+ return keyInfoComparisonResult;
+ }
+
+ /**
+ * {@inheritDoc}.
+ */
+ @Override
+ public String toString() {
+ return asString(true, true);
+ }
+
+ /**
+ * As string.
+ *
+ * @param diffsOnly the diffs only
+ * @param keysOnly the keys only
+ * @return the string
+ */
+ public String asString(final boolean diffsOnly, final boolean keysOnly) {
+ final StringBuilder builder = new StringBuilder();
+
+ builder.append("****** policy map differences ******\n");
+ builder.append(policyModelsKeyDifference.asString(diffsOnly));
+
+ builder.append("*** context schema differences ***\n");
+ builder.append(contextSchemasKeyDifference.asString(diffsOnly));
+ builder.append(contextSchemaCompareResult.asString(diffsOnly, keysOnly));
+
+ builder.append("*** event differences ***\n");
+ builder.append(eventsKeyDifference.asString(diffsOnly));
+ builder.append(eventComparisonResult.asString(diffsOnly, keysOnly));
+
+ builder.append("*** context album differences ***\n");
+ builder.append(contextAlbumKeyDifference.asString(diffsOnly));
+ builder.append(contextAlbumComparisonResult.asString(diffsOnly, keysOnly));
+
+ builder.append("*** task differences ***\n");
+ builder.append(tasksKeyDifference.asString(diffsOnly));
+ builder.append(taskComparisonResult.asString(diffsOnly, keysOnly));
+
+ builder.append("*** policy differences ***\n");
+ builder.append(policiesKeyDifference.asString(diffsOnly));
+ builder.append(policyComparisonResult.asString(diffsOnly, keysOnly));
+
+ builder.append("*** key information differences ***\n");
+ builder.append(keyInformationKeyDifference.asString(diffsOnly));
+ builder.append(keyInfoComparisonResult.asString(diffsOnly, keysOnly));
+
+ builder.append("***********************************\n");
+
+ return builder.toString();
+ }
+}
diff --git a/model/src/main/java/org/onap/policy/apex/model/policymodel/handling/PolicyModelMerger.java b/model/src/main/java/org/onap/policy/apex/model/policymodel/handling/PolicyModelMerger.java
new file mode 100644
index 000000000..ae0dda814
--- /dev/null
+++ b/model/src/main/java/org/onap/policy/apex/model/policymodel/handling/PolicyModelMerger.java
@@ -0,0 +1,183 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * Copyright (C) 2016-2018 Ericsson. All rights reserved.
+ * Modifications Copyright (C) 2019 Nordix Foundation.
+ * Modifications Copyright (C) 2020-2021 Bell Canada. All rights reserved.
+ * ================================================================================
+ * 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.apex.model.policymodel.handling;
+
+import java.util.Map;
+import java.util.Map.Entry;
+import org.onap.policy.apex.model.basicmodel.concepts.AxArtifactKey;
+import org.onap.policy.apex.model.basicmodel.concepts.AxKeyInfo;
+import org.onap.policy.apex.model.basicmodel.concepts.AxValidationResult;
+import org.onap.policy.apex.model.basicmodel.handling.ApexModelException;
+import org.onap.policy.apex.model.contextmodel.concepts.AxContextAlbum;
+import org.onap.policy.apex.model.contextmodel.concepts.AxContextSchema;
+import org.onap.policy.apex.model.eventmodel.concepts.AxEvent;
+import org.onap.policy.apex.model.policymodel.concepts.AxPolicy;
+import org.onap.policy.apex.model.policymodel.concepts.AxPolicyModel;
+import org.onap.policy.apex.model.policymodel.concepts.AxTask;
+import org.slf4j.ext.XLogger;
+import org.slf4j.ext.XLoggerFactory;
+
+/**
+ * Helper class used to merge information from two policy models together into a single policy
+ * model.
+ *
+ * @author Liam Fallon (liam.fallon@ericsson.com)
+ */
+public final class PolicyModelMerger {
+ private static final XLogger LOGGER = XLoggerFactory.getXLogger(PolicyModelMerger.class);
+
+ /**
+ * Private constructor used to prevent sub class instantiation.
+ */
+ private PolicyModelMerger() {
+ }
+
+ /**
+ * Get a merged policy model with information from two policy models merged into a larger policy
+ * model.
+ *
+ * @param leftPolicyModel the source Apex Model
+ * @param rightPolicyModel the policies to include in sub policy model
+ * @param useLeftOnMatches if true, uses concepts from the left model if concepts with common
+ * keys are found, if false it uses the concepts from the right model
+ * @param failOnDuplicateKeys whether to fail or not on the occurence of duplicate concept keys
+ * @return the new Destination Model
+ * @throws ApexModelException on model transfer errors
+ */
+ public static AxPolicyModel getMergedPolicyModel(final AxPolicyModel leftPolicyModel,
+ final AxPolicyModel rightPolicyModel, final boolean useLeftOnMatches, final boolean failOnDuplicateKeys)
+ throws ApexModelException {
+ return getMergedPolicyModel(leftPolicyModel, rightPolicyModel, useLeftOnMatches, false, failOnDuplicateKeys);
+ }
+
+ /**
+ * Get a merged policy model with information from two policy models merged into a larger policy
+ * model.
+ *
+ * @param leftPolicyModel the source Apex Model
+ * @param rightPolicyModel the policies to include in sub policy model
+ * @param useLeftOnMatches if true, uses concepts from the left model if concepts with common
+ * keys are found, if false it uses the concepts from the right model
+ * @param ignoreInvalidSource Ignore errors on the source model, do the best you can
+ * @param failOnDuplicateKeys whether to fail or not on the occurence of duplicate concept keys
+ * @return the new Destination Model
+ * @throws ApexModelException on model transfer errors
+ */
+ public static AxPolicyModel getMergedPolicyModel(final AxPolicyModel leftPolicyModel,
+ final AxPolicyModel rightPolicyModel, final boolean useLeftOnMatches, final boolean ignoreInvalidSource,
+ final boolean failOnDuplicateKeys) throws ApexModelException {
+
+ if (!ignoreInvalidSource) {
+ validateModels(leftPolicyModel, "left");
+ validateModels(rightPolicyModel, "right");
+ }
+
+ // The new policy model uses the favoured copy side as its base
+ final AxPolicyModel mergedPolicyModel =
+ (useLeftOnMatches ? new AxPolicyModel(leftPolicyModel) : new AxPolicyModel(rightPolicyModel));
+
+ // The Compared to policy model is the unfavoured side
+ final AxPolicyModel copyFromPolicyModel =
+ (useLeftOnMatches ? new AxPolicyModel(rightPolicyModel) : new AxPolicyModel(leftPolicyModel));
+
+ Map<AxArtifactKey, AxKeyInfo> mergedKeyInfoMap = mergedPolicyModel.getKeyInformation().getKeyInfoMap();
+ Map<AxArtifactKey, AxContextSchema> mergedSchemasMap = mergedPolicyModel.getSchemas().getSchemasMap();
+ Map<AxArtifactKey, AxEvent> mergedEventMap = mergedPolicyModel.getEvents().getEventMap();
+ Map<AxArtifactKey, AxContextAlbum> mergedAlbumsMap = mergedPolicyModel.getAlbums().getAlbumsMap();
+ Map<AxArtifactKey, AxTask> mergedTaskMap = mergedPolicyModel.getTasks().getTaskMap();
+ Map<AxArtifactKey, AxPolicy> mergedPolicyMap = mergedPolicyModel.getPolicies().getPolicyMap();
+
+ Map<AxArtifactKey, AxKeyInfo> copyOverKeyInfoMap = copyFromPolicyModel.getKeyInformation().getKeyInfoMap();
+ Map<AxArtifactKey, AxContextSchema> copyOverSchemasMap = copyFromPolicyModel.getSchemas().getSchemasMap();
+ Map<AxArtifactKey, AxEvent> copyOverEventMap = copyFromPolicyModel.getEvents().getEventMap();
+ Map<AxArtifactKey, AxContextAlbum> copyOverAlbumsMap = copyFromPolicyModel.getAlbums().getAlbumsMap();
+ Map<AxArtifactKey, AxTask> copyOverTaskMap = copyFromPolicyModel.getTasks().getTaskMap();
+ Map<AxArtifactKey, AxPolicy> copyOverPolicyMap = copyFromPolicyModel.getPolicies().getPolicyMap();
+
+ if (failOnDuplicateKeys) {
+ StringBuilder errorMessage = new StringBuilder();
+ checkForDuplicateItem(mergedSchemasMap, copyOverSchemasMap, errorMessage, "schema");
+ checkForDuplicateItem(mergedEventMap, copyOverEventMap, errorMessage, "event");
+ checkForDuplicateItem(mergedAlbumsMap, copyOverAlbumsMap, errorMessage, "album");
+ checkForDuplicateItem(mergedTaskMap, copyOverTaskMap, errorMessage, "task");
+ checkForDuplicateItem(mergedPolicyMap, copyOverPolicyMap, errorMessage, "policy");
+ if (errorMessage.length() > 0) {
+ throw new ApexModelException(errorMessage.toString());
+ }
+ } else {
+ //  Remove entries that already exist
+ copyOverKeyInfoMap.keySet().removeIf(mergedKeyInfoMap::containsKey);
+ copyOverSchemasMap.keySet().removeIf(mergedSchemasMap::containsKey);
+ copyOverEventMap.keySet().removeIf(mergedEventMap::containsKey);
+ copyOverAlbumsMap.keySet().removeIf(mergedAlbumsMap::containsKey);
+ copyOverTaskMap.keySet().removeIf(mergedTaskMap::containsKey);
+ copyOverPolicyMap.keySet().removeIf(mergedPolicyMap::containsKey);
+ }
+ // Now add all the concepts that must be copied over
+ mergedKeyInfoMap.putAll(copyOverKeyInfoMap);
+ mergedSchemasMap.putAll(copyOverSchemasMap);
+ mergedEventMap.putAll(copyOverEventMap);
+ mergedAlbumsMap.putAll(copyOverAlbumsMap);
+ mergedTaskMap.putAll(copyOverTaskMap);
+ mergedPolicyMap.putAll(copyOverPolicyMap);
+
+ // That's it, return the model
+ return mergedPolicyModel;
+ }
+
+ /**
+ * Method to check for duplicate items.
+ *
+ * @param <V> the concept type
+ * @param mergedItemsMap the map to which items are copied
+ * @param copyOverItemsMap the map from where items are copied
+ * @param errorMessage error message in case of any duplicate concepts
+ * @param itemType the type of concept to specify distinguished error messages
+ */
+ public static <V> void checkForDuplicateItem(Map<AxArtifactKey, V> mergedItemsMap,
+ Map<AxArtifactKey, V> copyOverItemsMap, StringBuilder errorMessage, String itemType) {
+ for (Entry<AxArtifactKey, V> entry : copyOverItemsMap.entrySet()) {
+ V item = mergedItemsMap.get(entry.getKey());
+ // same item with different definitions cannot occur in multiple policies
+ if (null != item) {
+ if (item.equals(entry.getValue())) {
+ LOGGER.info("Same {} - {} is used by multiple policies.", itemType, entry.getKey().getId());
+ } else {
+ errorMessage.append("\n Same " + itemType + " - ").append(entry.getKey().getId())
+ .append(" with different definitions used in different policies");
+ }
+ }
+ }
+ }
+
+ private static void validateModels(AxPolicyModel policyModel, String position) throws ApexModelException {
+ // Validate the model
+ final AxValidationResult validationResult = new AxValidationResult();
+ policyModel.validate(validationResult);
+ if (!validationResult.isValid()) {
+ String message = position + " model is invalid: " + validationResult.toString();
+ LOGGER.warn(message);
+ throw new ApexModelException(message);
+ }
+ }
+}
diff --git a/model/src/main/java/org/onap/policy/apex/model/policymodel/handling/PolicyModelSplitter.java b/model/src/main/java/org/onap/policy/apex/model/policymodel/handling/PolicyModelSplitter.java
new file mode 100644
index 000000000..1f1c27a83
--- /dev/null
+++ b/model/src/main/java/org/onap/policy/apex/model/policymodel/handling/PolicyModelSplitter.java
@@ -0,0 +1,165 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * Copyright (C) 2016-2018 Ericsson. All rights reserved.
+ * ================================================================================
+ * 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.apex.model.policymodel.handling;
+
+import java.util.Collection;
+import java.util.Set;
+import java.util.TreeSet;
+import org.onap.policy.apex.model.basicmodel.concepts.AxArtifactKey;
+import org.onap.policy.apex.model.basicmodel.concepts.AxValidationResult;
+import org.onap.policy.apex.model.basicmodel.handling.ApexModelException;
+import org.onap.policy.apex.model.policymodel.concepts.AxPolicy;
+import org.onap.policy.apex.model.policymodel.concepts.AxPolicyModel;
+import org.slf4j.ext.XLogger;
+import org.slf4j.ext.XLoggerFactory;
+
+/**
+ * Helper class used to extract information from a policy model into a policy model that is a subset of the original
+ * policy model.
+ *
+ * @author Liam Fallon (liam.fallon@ericsson.com)
+ */
+public final class PolicyModelSplitter {
+ private static final XLogger LOGGER = XLoggerFactory.getXLogger(PolicyModelSplitter.class);
+
+ /**
+ * Private constructor used to prevent sub class instantiation.
+ */
+ private PolicyModelSplitter() {
+ // Private constructor to block subclassing
+ }
+
+ /**
+ * Get a sub policy model with only the information required for the specified policies from a larger policy model.
+ *
+ * @param sourcePolicyModel the source Apex Model
+ * @param subPolicies the policies to include in sub policy model
+ * @return the new Destination Model
+ * @throws ApexModelException on model transfer errors
+ */
+ public static AxPolicyModel getSubPolicyModel(final AxPolicyModel sourcePolicyModel,
+ final Collection<AxArtifactKey> subPolicies) throws ApexModelException {
+ return getSubPolicyModel(sourcePolicyModel, subPolicies, false);
+ }
+
+ /**
+ * Get a sub policy model with only the information required for the specified policies from a larger policy model.
+ *
+ * @param sourcePolicyModel the source Apex Model
+ * @param subPolicies the policies to include in sub policy model
+ * @param ignoreInvalidSource Ignore errors on the source model, do the best you can
+ * @return the new Destination Model
+ * @throws ApexModelException on model transfer errors
+ */
+ public static AxPolicyModel getSubPolicyModel(final AxPolicyModel sourcePolicyModel,
+ final Collection<AxArtifactKey> subPolicies, final boolean ignoreInvalidSource) throws ApexModelException {
+ // Validate the source model
+ if (!ignoreInvalidSource) {
+ final AxValidationResult sourceValidationResult = new AxValidationResult();
+ sourcePolicyModel.validate(sourceValidationResult);
+ if (!sourceValidationResult.isValid()) {
+ String message = "source model is invalid: " + sourceValidationResult.toString();
+ LOGGER.warn(message);
+ throw new ApexModelException(message);
+ }
+ }
+
+ // The new policy model
+ final AxPolicyModel newPolicyModel = new AxPolicyModel(sourcePolicyModel.getKey());
+ newPolicyModel.getKeyInformation().setKey(sourcePolicyModel.getKeyInformation().getKey());
+ newPolicyModel.getKeyInformation().getKeyInfoMap().put(sourcePolicyModel.getKey(),
+ sourcePolicyModel.getKeyInformation().getKeyInfoMap().get(sourcePolicyModel.getKey()));
+ newPolicyModel.getKeyInformation().getKeyInfoMap().put(sourcePolicyModel.getKeyInformation().getKey(),
+ sourcePolicyModel.getKeyInformation().getKeyInfoMap()
+ .get(sourcePolicyModel.getKeyInformation().getKey()));
+
+ //  Get the events, tasks, context maps, and data types used by each policy
+ final Set<AxArtifactKey> contextSchemaSet = new TreeSet<>();
+ final Set<AxArtifactKey> eventSet = new TreeSet<>();
+ final Set<AxArtifactKey> contextAlbumSet = new TreeSet<>();
+ final Set<AxArtifactKey> taskSet = new TreeSet<>();
+
+ newPolicyModel.getPolicies().setKey(sourcePolicyModel.getPolicies().getKey());
+ newPolicyModel.getKeyInformation().getKeyInfoMap().put(sourcePolicyModel.getPolicies().getKey(),
+ sourcePolicyModel.getKeyInformation().getKeyInfoMap().get(sourcePolicyModel.getPolicies().getKey()));
+ for (final AxArtifactKey subPolicyKey : subPolicies) {
+ final AxPolicy subPolicy = sourcePolicyModel.getPolicies().getPolicyMap().get(subPolicyKey);
+ if (subPolicy == null) {
+ LOGGER.warn("source sub policy not found: {}", subPolicyKey);
+ continue;
+ }
+
+ // Transfer the policy across
+ newPolicyModel.getPolicies().getPolicyMap().put(subPolicyKey, subPolicy);
+ newPolicyModel.getKeyInformation().getKeyInfoMap().put(subPolicyKey,
+ sourcePolicyModel.getKeyInformation().getKeyInfoMap().get(subPolicyKey));
+
+ // Get the references for this policy
+ final PolicyAnalysisResult analysisResult = new PolicyAnalyser().analyse(sourcePolicyModel, subPolicy);
+ contextSchemaSet.addAll(analysisResult.getUsedContextSchemas());
+ eventSet.addAll(analysisResult.getUsedEvents());
+ contextAlbumSet.addAll(analysisResult.getUsedContextAlbums());
+ taskSet.addAll(analysisResult.getUsedTasks());
+
+ }
+
+ // Now add all the referenced data types, events, context maps, and tasks to the policy
+ // model
+ newPolicyModel.getSchemas().setKey(sourcePolicyModel.getSchemas().getKey());
+ newPolicyModel.getKeyInformation().getKeyInfoMap().put(sourcePolicyModel.getSchemas().getKey(),
+ sourcePolicyModel.getKeyInformation().getKeyInfoMap().get(sourcePolicyModel.getSchemas().getKey()));
+ for (final AxArtifactKey contextSchemaKey : contextSchemaSet) {
+ newPolicyModel.getSchemas().getSchemasMap().put(contextSchemaKey,
+ sourcePolicyModel.getSchemas().getSchemasMap().get(contextSchemaKey));
+ newPolicyModel.getKeyInformation().getKeyInfoMap().put(contextSchemaKey,
+ sourcePolicyModel.getKeyInformation().getKeyInfoMap().get(contextSchemaKey));
+ }
+ newPolicyModel.getEvents().setKey(sourcePolicyModel.getEvents().getKey());
+ newPolicyModel.getKeyInformation().getKeyInfoMap().put(sourcePolicyModel.getEvents().getKey(),
+ sourcePolicyModel.getKeyInformation().getKeyInfoMap().get(sourcePolicyModel.getEvents().getKey()));
+ for (final AxArtifactKey eventKey : eventSet) {
+ newPolicyModel.getEvents().getEventMap().put(eventKey,
+ sourcePolicyModel.getEvents().getEventMap().get(eventKey));
+ newPolicyModel.getKeyInformation().getKeyInfoMap().put(eventKey,
+ sourcePolicyModel.getKeyInformation().getKeyInfoMap().get(eventKey));
+ }
+ newPolicyModel.getAlbums().setKey(sourcePolicyModel.getAlbums().getKey());
+ newPolicyModel.getKeyInformation().getKeyInfoMap().put(sourcePolicyModel.getAlbums().getKey(),
+ sourcePolicyModel.getKeyInformation().getKeyInfoMap().get(sourcePolicyModel.getAlbums().getKey()));
+ for (final AxArtifactKey contextAlbumKey : contextAlbumSet) {
+ newPolicyModel.getAlbums().getAlbumsMap().put(contextAlbumKey,
+ sourcePolicyModel.getAlbums().getAlbumsMap().get(contextAlbumKey));
+ newPolicyModel.getKeyInformation().getKeyInfoMap().put(contextAlbumKey,
+ sourcePolicyModel.getKeyInformation().getKeyInfoMap().get(contextAlbumKey));
+ }
+ newPolicyModel.getTasks().setKey(sourcePolicyModel.getTasks().getKey());
+ newPolicyModel.getKeyInformation().getKeyInfoMap().put(sourcePolicyModel.getTasks().getKey(),
+ sourcePolicyModel.getKeyInformation().getKeyInfoMap().get(sourcePolicyModel.getTasks().getKey()));
+ for (final AxArtifactKey taskKey : taskSet) {
+ newPolicyModel.getTasks().getTaskMap().put(taskKey, sourcePolicyModel.getTasks().getTaskMap().get(taskKey));
+ newPolicyModel.getKeyInformation().getKeyInfoMap().put(taskKey,
+ sourcePolicyModel.getKeyInformation().getKeyInfoMap().get(taskKey));
+ }
+
+ // That's it, return the model
+ return newPolicyModel;
+ }
+}
diff --git a/model/src/main/java/org/onap/policy/apex/model/policymodel/handling/package-info.java b/model/src/main/java/org/onap/policy/apex/model/policymodel/handling/package-info.java
new file mode 100644
index 000000000..f84befd26
--- /dev/null
+++ b/model/src/main/java/org/onap/policy/apex/model/policymodel/handling/package-info.java
@@ -0,0 +1,26 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * Copyright (C) 2016-2018 Ericsson. All rights reserved.
+ * ================================================================================
+ * 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=========================================================
+ */
+
+/**
+ * Contains utility classes used to handle APEX polcies and policy models.
+ *
+ * @author Liam Fallon (liam.fallon@ericsson.com)
+ */
+package org.onap.policy.apex.model.policymodel.handling;
diff --git a/model/src/main/java/org/onap/policy/apex/model/utilities/CollectionUtils.java b/model/src/main/java/org/onap/policy/apex/model/utilities/CollectionUtils.java
new file mode 100644
index 000000000..9636ea7ce
--- /dev/null
+++ b/model/src/main/java/org/onap/policy/apex/model/utilities/CollectionUtils.java
@@ -0,0 +1,109 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * Copyright (C) 2016-2018 Ericsson. All rights reserved.
+ * Modifications Copyright (C) 2021 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.apex.model.utilities;
+
+import java.util.List;
+import java.util.ListIterator;
+import lombok.AccessLevel;
+import lombok.NoArgsConstructor;
+
+/**
+ * This is common utility class with static methods for handling collections.
+ *
+ * @author Liam Fallon (liam.fallon@ericsson.com)
+ */
+@NoArgsConstructor(access = AccessLevel.PRIVATE)
+public final class CollectionUtils {
+
+ /**
+ * Compare two lists, checks for equality, then for equality on members.
+ *
+ * @param <T> The type of the lists being compared
+ * @param leftList The leftmost List
+ * @param rightList The rightmost list
+ * @return an integer indicating how different the lists are
+ */
+ public static <T> int compareLists(final List<? extends Comparable<T>> leftList,
+ final List<? extends Comparable<T>> rightList) {
+ // Check for nulls
+ if (leftList == null && rightList == null) {
+ return 0;
+ }
+ if (leftList != null && rightList == null) {
+ return -1;
+ }
+ if (leftList == null) {
+ return 1;
+ }
+
+ // Check for equality
+ if (leftList.equals(rightList)) {
+ return 0;
+ }
+
+ return compareListEntries(leftList, rightList);
+ }
+
+ /**
+ * Compare two lists for equality on members.
+ *
+ * @param <T> The type of the lists being compared
+ * @param leftList The leftmost List
+ * @param rightList The rightmost list
+ * @return an integer indicating how different the lists are
+ */
+ private static <T> int compareListEntries(final List<? extends Comparable<T>> leftList,
+ final List<? extends Comparable<T>> rightList) {
+
+ // Iterate down the lists till we find a difference
+ final ListIterator<?> leftIterator = leftList.listIterator();
+ final ListIterator<?> rightIterator = rightList.listIterator();
+
+ while (true) {
+ // Check the iterators
+ if (!leftIterator.hasNext() && !rightIterator.hasNext()) {
+ return 0;
+ }
+ if (leftIterator.hasNext() && !rightIterator.hasNext()) {
+ return -1;
+ }
+ if (!leftIterator.hasNext() && rightIterator.hasNext()) {
+ return 1;
+ }
+
+ // Get the next objects
+ @SuppressWarnings("unchecked")
+ final var leftObject = (T) leftIterator.next();
+ @SuppressWarnings("unchecked")
+ final var rightObject = (T) rightIterator.next();
+
+ // Compare the objects
+ @SuppressWarnings("unchecked")
+ final int comparisonResult = ((Comparable<T>) leftObject).compareTo(rightObject);
+
+ // Check the comparison result
+ if (comparisonResult != 0) {
+ return comparisonResult;
+ }
+ }
+ }
+}
diff --git a/model/src/main/java/org/onap/policy/apex/model/utilities/DirectoryDeleteShutdownHook.java b/model/src/main/java/org/onap/policy/apex/model/utilities/DirectoryDeleteShutdownHook.java
new file mode 100644
index 000000000..21c417c4d
--- /dev/null
+++ b/model/src/main/java/org/onap/policy/apex/model/utilities/DirectoryDeleteShutdownHook.java
@@ -0,0 +1,67 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * Copyright (C) 2018 Ericsson. All rights reserved.
+ * Modifications Copyright (C) 2020 Nordix Foundation.
+ * Modifications Copyright (C) 2021 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.apex.model.utilities;
+
+import java.io.File;
+import java.io.IOException;
+import java.nio.file.Files;
+import org.slf4j.ext.XLogger;
+import org.slf4j.ext.XLoggerFactory;
+
+/**
+ * The Class DirectoryShutdownHook removes the contents of a directory and the directory itself at shutdown.
+ *
+ * @author Liam Fallon (liam.fallon@ericsson.com)
+ */
+final class DirectoryDeleteShutdownHook extends Thread {
+
+ private static final XLogger LOGGER = XLoggerFactory.getXLogger(DirectoryUtils.class);
+
+ // The directory we are acting on
+ private final File tempDir;
+
+ /**
+ * Constructor that defines the directory to act on at shutdown.
+ *
+ * @param tempDir The temporary directory to delete
+ */
+ DirectoryDeleteShutdownHook(final File tempDir) {
+ this.tempDir = tempDir;
+ }
+
+ /**
+ * {@inheritDoc}.
+ */
+ @Override
+ public void run() {
+ if (tempDir.exists()) {
+ // Empty and delete the directory
+ DirectoryUtils.emptyDirectory(tempDir);
+ try {
+ Files.delete(tempDir.toPath());
+ } catch (IOException e) {
+ LOGGER.warn("Failed to delete directory {}", tempDir, e);
+ }
+ }
+ }
+}
diff --git a/model/src/main/java/org/onap/policy/apex/model/utilities/DirectoryUtils.java b/model/src/main/java/org/onap/policy/apex/model/utilities/DirectoryUtils.java
new file mode 100644
index 000000000..b0e8332b1
--- /dev/null
+++ b/model/src/main/java/org/onap/policy/apex/model/utilities/DirectoryUtils.java
@@ -0,0 +1,110 @@
+/*
+ * ============LICENSE_START=======================================================
+ * Copyright (C) 2016-2018 Ericsson. All rights reserved.
+ * Modifications Copyright (C) 2020 Nordix Foundation.
+ * Modifications Copyright (C) 2021 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.apex.model.utilities;
+
+import java.io.File;
+import java.io.IOException;
+import java.nio.file.Files;
+import lombok.AccessLevel;
+import lombok.NoArgsConstructor;
+import org.slf4j.ext.XLogger;
+import org.slf4j.ext.XLoggerFactory;
+
+/**
+ * This is common utility class with static methods for handling directories. It is an abstract class to prevent any
+ * direct instantiation and private constructor to prevent extending this class.
+ *
+ * @author Liam Fallon (liam.fallon@ericsson.com)
+ */
+@NoArgsConstructor(access = AccessLevel.PRIVATE)
+public final class DirectoryUtils {
+ // Get a reference to the logger
+ private static final XLogger LOGGER = XLoggerFactory.getXLogger(DirectoryUtils.class);
+
+ /**
+ * Method to get an empty temporary directory in the system temporary directory on the local machine that will be
+ * deleted on (normal) shutdown.
+ *
+ * @param nameprefix The prefix of the filename. System.nanoTime() will be appended to the pattern to create a
+ * unique file pattern
+ * @return The temporary directory
+ */
+ public static File getLocalTempDirectory(final String nameprefix) {
+ try {
+ // Get the name of the temporary directory
+ final String tempDirName = System.getProperty("java.io.tmpdir") + "/" + nameprefix + System.nanoTime();
+ final var tempDir = new File(tempDirName);
+
+ // Delete the directory if it already exists
+ if (tempDir.exists()) {
+ return null;
+ }
+
+ // Make the directory
+ tempDir.mkdirs();
+
+ // Add a shutdown hook that deletes the directory contents when the JVM closes
+ Runtime.getRuntime().addShutdownHook(new DirectoryDeleteShutdownHook(tempDir));
+
+ LOGGER.trace("creating temp directory\"{}\" : ", tempDir.getAbsolutePath());
+ return tempDir;
+ } catch (final Exception e) {
+ LOGGER.debug("error creating temp directory\"{}\" : " + e.getMessage(), e);
+ return null;
+ }
+ }
+
+ /**
+ * Method to recursively delete all the files in a directory.
+ *
+ * @param tempDir the directory to empty
+ * @return true if the operation succeeds, false otherwise
+ */
+ public static boolean emptyDirectory(final File tempDir) {
+ // Sanity check
+ if (!tempDir.exists() || !tempDir.isDirectory()) {
+ return false;
+ }
+
+ // Walk the directory structure deleting files as we go
+ final File[] files = tempDir.listFiles();
+ if (files != null) {
+ for (final File directoryFile : files) {
+ // Check if this is a directory itself
+ if (directoryFile.isDirectory()) {
+ // Recurse into the sub directory and empty it
+ emptyDirectory(directoryFile);
+ }
+
+ // Delete the directory entry
+ try {
+ Files.delete(directoryFile.toPath());
+ } catch (IOException e) {
+ LOGGER.warn("Failed to delete directory file {}", directoryFile, e);
+ }
+ }
+ }
+
+ return true;
+ }
+}
diff --git a/model/src/main/java/org/onap/policy/apex/model/utilities/TreeMapUtils.java b/model/src/main/java/org/onap/policy/apex/model/utilities/TreeMapUtils.java
new file mode 100644
index 000000000..d8bb469cf
--- /dev/null
+++ b/model/src/main/java/org/onap/policy/apex/model/utilities/TreeMapUtils.java
@@ -0,0 +1,126 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * Copyright (C) 2016-2018 Ericsson. All rights reserved.
+ * Modifications Copyright (C) 2021 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.apex.model.utilities;
+
+import java.util.AbstractMap.SimpleEntry;
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.Map.Entry;
+import java.util.NavigableMap;
+import lombok.AccessLevel;
+import lombok.NoArgsConstructor;
+
+/**
+ * This class provides utility functions for tree maps. A function to find the nearest match in the tree map to an input
+ * string is provided.
+ *
+ * @author Liam Fallon (liam.fallon@ericsson.com)
+ */
+@NoArgsConstructor(access = AccessLevel.PRIVATE)
+public final class TreeMapUtils {
+
+ /**
+ * Find the list of entries that matches a given word, for example "p" will match "put", "policy", and "push".
+ *
+ * @param <T> the generic type for the value of the tree map
+ * @param searchMap the map that the method operates on
+ * @param word the word to search for
+ * @return the list of entries in the {@code searchMap} that match the {@code word}
+ */
+ public static <T> List<Entry<String, T>> findMatchingEntries(final NavigableMap<String, T> searchMap,
+ final String word) {
+ final List<Entry<String, T>> foundNodes = new ArrayList<>();
+
+ // A straight match check
+ if (searchMap.containsKey(word)) {
+ foundNodes.add(new SimpleEntry<>(word, searchMap.get(word)));
+ return foundNodes;
+ }
+
+ // Set up the beginning point for our search for a list of near matches
+ String foundKeyword = searchMap.floorKey(word);
+ if (foundKeyword == null) {
+ foundKeyword = searchMap.firstKey();
+ } else {
+ foundKeyword = searchMap.higherKey(foundKeyword);
+ }
+
+ // Find all the nodes that start with the word we are searching for
+ while (foundKeyword != null) {
+ if (foundKeyword.startsWith(word)) {
+ foundNodes.add(new SimpleEntry<>(foundKeyword, searchMap.get(foundKeyword)));
+ foundKeyword = searchMap.higherKey(foundKeyword);
+ } else {
+ break;
+ }
+ }
+ return foundNodes;
+ }
+
+ /**
+ * Compares two maps.
+ * @param <K> Key type
+ * @param <V> Value type
+ * @param leftMap left map
+ * @param rightMap right map
+ * @return an integer indicating how different the maps are
+ */
+ @SuppressWarnings("unchecked")
+ public static <K, V> int compareMaps(Map<? extends Comparable<K>, ? extends Comparable<V>> leftMap,
+ Map<? extends Comparable<K>, ? extends Comparable<V>> rightMap) {
+ if (leftMap == rightMap) {
+ return 0;
+ }
+
+ Iterator<?> leftIt = leftMap.entrySet().iterator();
+ Iterator<?> rightIt = rightMap.entrySet().iterator();
+
+ while (leftIt.hasNext() && rightIt.hasNext()) {
+ Map.Entry<?, ?> leftEntry = (Entry<?, ?>) leftIt.next();
+ Map.Entry<?, ?> rightEntry = (Entry<?, ?>) rightIt.next();
+
+ var leftKey = (K) leftEntry.getKey();
+ var rightKey = (K) rightEntry.getKey();
+ int result = ((Comparable<K>) leftKey).compareTo(rightKey);
+ if (result != 0) {
+ return result;
+ }
+
+ var leftValue = (V) leftEntry.getValue();
+ var rightValue = (V) rightEntry.getValue();
+ result = ((Comparable<V>) leftValue).compareTo(rightValue);
+ if (result != 0) {
+ return result;
+ }
+ }
+
+ if (leftIt.hasNext()) {
+ return 1;
+ } else if (rightIt.hasNext()) {
+ return -1;
+ } else {
+ return 0;
+ }
+ }
+}
diff --git a/model/src/main/java/org/onap/policy/apex/model/utilities/comparison/KeyComparer.java b/model/src/main/java/org/onap/policy/apex/model/utilities/comparison/KeyComparer.java
new file mode 100644
index 000000000..f4d628405
--- /dev/null
+++ b/model/src/main/java/org/onap/policy/apex/model/utilities/comparison/KeyComparer.java
@@ -0,0 +1,42 @@
+/*
+ * ============LICENSE_START=======================================================
+ * Copyright (C) 2016-2018 Ericsson. All rights reserved.
+ * ================================================================================
+ * 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.apex.model.utilities.comparison;
+
+/**
+ * This class compares two keys and returns their differences. It is used in bulk comparisons in models where maps of
+ * keys are being compared. The {@link KeyComparer} that is returned does the actual comparison
+ *
+ * @author Liam Fallon (liam.fallon@ericsson.com)
+ * @param <K> the type of key being compared
+ */
+public class KeyComparer<K> {
+
+ /**
+ * Compare two keys and return their differences.
+ *
+ * @param leftKey The left key of the comparison
+ * @param rightKey The right key of the comparison
+ * @return The difference between the keys
+ */
+ public KeyDifference<K> compareKeys(final K leftKey, final K rightKey) {
+ return new KeyDifference<>(leftKey, rightKey);
+ }
+}
diff --git a/model/src/main/java/org/onap/policy/apex/model/utilities/comparison/KeyDifference.java b/model/src/main/java/org/onap/policy/apex/model/utilities/comparison/KeyDifference.java
new file mode 100644
index 000000000..a0395cf16
--- /dev/null
+++ b/model/src/main/java/org/onap/policy/apex/model/utilities/comparison/KeyDifference.java
@@ -0,0 +1,86 @@
+/*
+ * ============LICENSE_START=======================================================
+ * Copyright (C) 2016-2018 Ericsson. All rights reserved.
+ * Modifications Copyright (C) 2021 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.apex.model.utilities.comparison;
+
+import lombok.Getter;
+
+/**
+ * This class is used to template key differences for bulk key comparisons in models. It performs a difference check
+ * between two keys.
+ *
+ * @author Liam Fallon (liam.fallon@ericsson.com)
+ * @param <K> the generic type
+ */
+@Getter
+public class KeyDifference<K> {
+ // The keys being compared
+ private K leftKey;
+ private K rightKey;
+
+ /**
+ * Constructor used to set the keys being compared.
+ *
+ * @param leftKey the left key that is being compared
+ * @param rightKey the right key that is being compared
+ */
+ public KeyDifference(final K leftKey, final K rightKey) {
+ this.leftKey = leftKey;
+ this.rightKey = rightKey;
+ }
+
+ /**
+ * Checks if the left and right keys are equal.
+ *
+ * @return true, if checks if is equal
+ */
+ public boolean isEqual() {
+ return leftKey.equals(rightKey);
+ }
+
+ /**
+ * Gets a string representation of the difference between the keys.
+ *
+ * @param diffsOnly if set, then a blank string is returned if the keys are equal
+ * @return the difference between the keys as a string
+ */
+ public String asString(final boolean diffsOnly) {
+ var builder = new StringBuilder();
+
+ if (leftKey.equals(rightKey)) {
+ if (!diffsOnly) {
+ builder.append("left key ");
+ builder.append(leftKey);
+ builder.append(" equals right key ");
+ builder.append(rightKey);
+ builder.append('\n');
+ }
+ } else {
+ builder.append("left key ");
+ builder.append(leftKey);
+ builder.append(" and right key ");
+ builder.append(rightKey);
+ builder.append(" differ\n");
+ }
+
+ return builder.toString();
+ }
+}
diff --git a/model/src/main/java/org/onap/policy/apex/model/utilities/comparison/KeyedMapComparer.java b/model/src/main/java/org/onap/policy/apex/model/utilities/comparison/KeyedMapComparer.java
new file mode 100644
index 000000000..9943690fa
--- /dev/null
+++ b/model/src/main/java/org/onap/policy/apex/model/utilities/comparison/KeyedMapComparer.java
@@ -0,0 +1,96 @@
+/*
+ * ============LICENSE_START=======================================================
+ * Copyright (C) 2016-2018 Ericsson. All rights reserved.
+ * Modifications Copyright (C) 2021 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.apex.model.utilities.comparison;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.TreeSet;
+
+/**
+ * Compare two maps and returns their differences. The types of the keys and the values in the two maps being comapred
+ * must be the same. The class returns entries that are only in the left map, only in the right map, entries that have
+ * identical keys and different values and entries that have different keys and different values in a
+ * {@link KeyedMapDifference} instance.
+ *
+ * @author Liam Fallon (liam.fallon@ericsson.com)
+ * @param <K> the type of the keys in the maps being compared
+ * @param <V> the type of the values in the maps being compared
+ */
+public class KeyedMapComparer<K, V> {
+ /**
+ * Compare two maps and return their differences in a {@link KeyedMapDifference} instance.
+ *
+ * @param leftMap The left map to be compared
+ * @param rightMap The right map to be compared
+ * @return The common, left only, and right only maps in a {@link KeyedMapDifference} instance
+ */
+ public KeyedMapDifference<K, V> compareMaps(final Map<K, V> leftMap, final Map<K, V> rightMap) {
+ KeyedMapDifference<K, V> result = new KeyedMapDifference<>();
+
+ // Get the keys that are only in the left map
+ Set<K> leftOnlyKeys = new TreeSet<>(leftMap.keySet());
+ leftOnlyKeys.removeAll(rightMap.keySet());
+
+ // Get the keys that are only in the right map
+ Set<K> rightOnlyKeys = new TreeSet<>(rightMap.keySet());
+ rightOnlyKeys.removeAll(leftMap.keySet());
+
+ // Find the keys common across both maps
+ Set<K> commonKeys = new TreeSet<>(rightMap.keySet());
+ commonKeys.addAll(leftMap.keySet());
+ commonKeys.removeAll(leftOnlyKeys);
+ commonKeys.removeAll(rightOnlyKeys);
+
+ // Now save the left values
+ for (K key : leftOnlyKeys) {
+ result.getLeftOnly().put(key, leftMap.get(key));
+ }
+
+ // Now save the right values
+ for (K key : rightOnlyKeys) {
+ result.getRightOnly().put(key, rightMap.get(key));
+ }
+
+ // Save the common values to two maps, an identical and different map
+ for (K key : commonKeys) {
+ // Check if the values are identical in each map
+ var leftValue = leftMap.get(key);
+ var rightValue = rightMap.get(key);
+
+ // Store as appropriate
+ if (leftValue.equals(rightValue)) {
+ result.getIdenticalValues().put(key, leftValue);
+ } else {
+ // Store the two values
+ List<V> valueList = new ArrayList<>();
+ valueList.add(leftValue);
+ valueList.add(rightValue);
+
+ result.getDifferentValues().put(key, valueList);
+ }
+ }
+
+ return result;
+ }
+}
diff --git a/model/src/main/java/org/onap/policy/apex/model/utilities/comparison/KeyedMapDifference.java b/model/src/main/java/org/onap/policy/apex/model/utilities/comparison/KeyedMapDifference.java
new file mode 100644
index 000000000..e10854926
--- /dev/null
+++ b/model/src/main/java/org/onap/policy/apex/model/utilities/comparison/KeyedMapDifference.java
@@ -0,0 +1,174 @@
+/*
+ * ============LICENSE_START=======================================================
+ * Copyright (C) 2016-2018 Ericsson. All rights reserved.
+ * Modifications Copyright (C) 2021 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.apex.model.utilities.comparison;
+
+import java.util.List;
+import java.util.Map;
+import java.util.Map.Entry;
+import java.util.TreeMap;
+import lombok.Getter;
+
+/**
+ * This class holds the result of a difference check between two keyed maps. Four results are returned in the class. The
+ * {@code leftOnly} result is the entries that appear only in the left map. the {@code rightOnly} result is the entries
+ * that appear only in the right map. The {@code differentValues} result are the entries that have the same key but
+ * different values in the maps being compared. The {@code identicalValues} result are the entries with identical keys
+ * and values in both maps being compared.
+ *
+ * @author Liam Fallon (liam.fallon@ericsson.com)
+ * @param <K> the generic type
+ * @param <V> the generic type
+ */
+@Getter
+public class KeyedMapDifference<K, V> {
+ private static final String KEY = "key=";
+ private static final String VALUE = ",value=";
+
+ // Three maps to hold the comparison result
+ private Map<K, V> leftOnly = new TreeMap<>();
+ private Map<K, V> rightOnly = new TreeMap<>();
+ private Map<K, V> identicalValues = new TreeMap<>();
+ private Map<K, List<V>> differentValues = new TreeMap<>();
+
+ /**
+ * Return a string representation of the differences.
+ *
+ * @param diffsOnly if set, then a blank string is returned if the maps are equal
+ * @param keysOnly if set, then a terse string that prints only the keys is returned, otherwise both keys and values
+ * are printed
+ * @return the string
+ */
+ public String asString(final boolean diffsOnly, final boolean keysOnly) {
+ var builder = new StringBuilder();
+
+ if (leftOnly.isEmpty()) {
+ if (!diffsOnly) {
+ builder.append("*** all left keys in right\n");
+ }
+ } else {
+ builder.append(getInOneSideOnlyAsString(leftOnly, "left", keysOnly));
+ }
+
+ if (rightOnly.isEmpty()) {
+ if (!diffsOnly) {
+ builder.append("*** all right keys in left\n");
+ }
+ } else {
+ builder.append(getInOneSideOnlyAsString(rightOnly, "right", keysOnly));
+ }
+
+ if (differentValues.isEmpty()) {
+ if (!diffsOnly) {
+ builder.append("*** all values in left and right are identical\n");
+ }
+ } else {
+ builder.append(getDifferencesAsString(keysOnly));
+ }
+
+ if (!diffsOnly) {
+ builder.append(getIdenticalsAsString(keysOnly));
+ }
+
+ return builder.toString();
+ }
+
+ /**
+ * Output the entries in a map with entries that are in one side only as a string.
+ *
+ * @param sideMap the map for the side being checked
+ * @param sideMapString the string that represents the map in output strings
+ * @param keysOnly if true, just add key information and not entries
+ * @return the entries as a string
+ */
+ private Object getInOneSideOnlyAsString(final Map<K, V> sideMap, final String sideMapString,
+ final boolean keysOnly) {
+ var builder = new StringBuilder();
+
+ builder.append("*** list of keys on " + sideMapString + " only\n");
+ for (Entry<K, V> leftEntry : sideMap.entrySet()) {
+ builder.append(KEY);
+ builder.append(leftEntry.getKey());
+ if (!keysOnly) {
+ builder.append(VALUE);
+ builder.append(leftEntry.getValue());
+ }
+ builder.append('\n');
+ }
+
+ return builder.toString();
+ }
+
+ /**
+ * Output the differences between two the maps as a string.
+ *
+ * @param keysOnly if true, just add key information and not entries
+ * @return the differences as a string
+ */
+ private String getDifferencesAsString(final boolean keysOnly) {
+ var builder = new StringBuilder();
+
+ builder.append("*** list of differing entries between left and right\n");
+ for (Entry<K, List<V>> differentEntry : differentValues.entrySet()) {
+ builder.append(KEY);
+ builder.append(differentEntry.getKey());
+ if (!keysOnly) {
+ builder.append(",values={");
+ var first = true;
+ for (V differentEntryValue : differentEntry.getValue()) {
+ builder.append(differentEntryValue);
+ if (first) {
+ first = false;
+ } else {
+ builder.append(',');
+ }
+ }
+ builder.append("}");
+ }
+ builder.append('\n');
+ }
+
+ return builder.toString();
+ }
+
+ /**
+ * Output the identical entries in the maps as a string.
+ *
+ * @param keysOnly if true, just add key information and not entries
+ * @return the identical entries as a string
+ */
+ private String getIdenticalsAsString(final boolean keysOnly) {
+ var builder = new StringBuilder();
+
+ builder.append("*** list of identical entries in left and right\n");
+ for (Entry<K, V> identicalEntry : identicalValues.entrySet()) {
+ builder.append(KEY);
+ builder.append(identicalEntry.getKey());
+ if (!keysOnly) {
+ builder.append(VALUE);
+ builder.append(identicalEntry.getValue());
+ }
+ builder.append('\n');
+ }
+
+ return builder.toString();
+ }
+}
diff --git a/model/src/main/java/org/onap/policy/apex/model/utilities/comparison/package-info.java b/model/src/main/java/org/onap/policy/apex/model/utilities/comparison/package-info.java
new file mode 100644
index 000000000..f0488eacd
--- /dev/null
+++ b/model/src/main/java/org/onap/policy/apex/model/utilities/comparison/package-info.java
@@ -0,0 +1,26 @@
+/*
+ * ============LICENSE_START=======================================================
+ * Copyright (C) 2016-2018 Ericsson. All rights reserved.
+ * ================================================================================
+ * 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=========================================================
+ */
+
+/**
+ * Provides utility template classes that compare keys and maps of any type.
+ *
+ * @author Liam Fallon (liam.fallon@ericsson.com)
+ */
+package org.onap.policy.apex.model.utilities.comparison;
diff --git a/model/src/main/java/org/onap/policy/apex/model/utilities/json/JsonHandler.java b/model/src/main/java/org/onap/policy/apex/model/utilities/json/JsonHandler.java
new file mode 100644
index 000000000..66e389fa2
--- /dev/null
+++ b/model/src/main/java/org/onap/policy/apex/model/utilities/json/JsonHandler.java
@@ -0,0 +1,50 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * Copyright (C) 2016-2018 Ericsson. All rights reserved.
+ * Modifications Copyright (C) 2021 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * 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.apex.model.utilities.json;
+
+import com.google.gson.Gson;
+import com.google.gson.GsonBuilder;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.io.Reader;
+
+/**
+ * This class reads objects of the given class from an input stream.
+ *
+ * @author Liam Fallon (liam.fallon@ericsson.com)
+ * @param <P> the generic type
+ */
+public class JsonHandler<P> {
+ private static final Gson GSON = new GsonBuilder().serializeNulls().create();
+
+ /**
+ * This method reads objects of a given class from an input stream.
+ *
+ * @param inputClass The class to read
+ * @param inputStream the input stream to read from
+ * @return the object read
+ */
+ public P read(final Class<P> inputClass, final InputStream inputStream) {
+ final Reader jsonResourceReader = new InputStreamReader(inputStream);
+ return GSON.fromJson(jsonResourceReader, inputClass);
+ }
+}
diff --git a/model/src/main/java/org/onap/policy/apex/model/utilities/json/package-info.java b/model/src/main/java/org/onap/policy/apex/model/utilities/json/package-info.java
new file mode 100755
index 000000000..f9d1304d7
--- /dev/null
+++ b/model/src/main/java/org/onap/policy/apex/model/utilities/json/package-info.java
@@ -0,0 +1,26 @@
+/*
+ * ============LICENSE_START=======================================================
+ * Copyright (C) 2016-2018 Ericsson. All rights reserved.
+ * ================================================================================
+ * 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=========================================================
+ */
+
+/**
+ * Provides a utility class for reading JSON streams.
+ *
+ * @author Liam Fallon (liam.fallon@ericsson.com)
+ */
+package org.onap.policy.apex.model.utilities.json;
diff --git a/model/src/main/java/org/onap/policy/apex/model/utilities/package-info.java b/model/src/main/java/org/onap/policy/apex/model/utilities/package-info.java
new file mode 100644
index 000000000..446d009ba
--- /dev/null
+++ b/model/src/main/java/org/onap/policy/apex/model/utilities/package-info.java
@@ -0,0 +1,26 @@
+/*
+ * ============LICENSE_START=======================================================
+ * Copyright (C) 2016-2018 Ericsson. All rights reserved.
+ * ================================================================================
+ * 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=========================================================
+ */
+
+/**
+ * Provides utility classes that are used in APEX models and indeed in other packages that use APEX models.
+ *
+ * @author Liam Fallon (liam.fallon@ericsson.com)
+ */
+package org.onap.policy.apex.model.utilities;