2 * ============LICENSE_START=======================================================
3 * Copyright (C) 2016-2018 Ericsson. All rights reserved.
4 * Modifications Copyright (C) 2019-2020 Nordix Foundation.
5 * ================================================================================
6 * Licensed under the Apache License, Version 2.0 (the "License");
7 * you may not use this file except in compliance with the License.
8 * You may obtain a copy of the License at
10 * http://www.apache.org/licenses/LICENSE-2.0
12 * Unless required by applicable law or agreed to in writing, software
13 * distributed under the License is distributed on an "AS IS" BASIS,
14 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 * See the License for the specific language governing permissions and
16 * limitations under the License.
18 * SPDX-License-Identifier: Apache-2.0
19 * ============LICENSE_END=========================================================
22 package org.onap.policy.apex.model.policymodel.concepts;
24 import java.util.List;
26 import java.util.Map.Entry;
28 import java.util.TreeMap;
29 import java.util.TreeSet;
30 import javax.persistence.CascadeType;
31 import javax.persistence.Column;
32 import javax.persistence.EmbeddedId;
33 import javax.persistence.Entity;
34 import javax.persistence.JoinColumn;
35 import javax.persistence.JoinTable;
36 import javax.persistence.OneToMany;
37 import javax.persistence.Table;
38 import javax.xml.bind.annotation.XmlAccessType;
39 import javax.xml.bind.annotation.XmlAccessorType;
40 import javax.xml.bind.annotation.XmlElement;
41 import javax.xml.bind.annotation.XmlRootElement;
42 import javax.xml.bind.annotation.XmlType;
43 import org.onap.policy.apex.model.basicmodel.concepts.AxArtifactKey;
44 import org.onap.policy.apex.model.basicmodel.concepts.AxConcept;
45 import org.onap.policy.apex.model.basicmodel.concepts.AxKey;
46 import org.onap.policy.apex.model.basicmodel.concepts.AxReferenceKey;
47 import org.onap.policy.apex.model.basicmodel.concepts.AxValidationMessage;
48 import org.onap.policy.apex.model.basicmodel.concepts.AxValidationResult;
49 import org.onap.policy.apex.model.basicmodel.concepts.AxValidationResult.ValidationResult;
50 import org.onap.policy.common.utils.validation.Assertions;
51 import org.slf4j.ext.XLogger;
52 import org.slf4j.ext.XLoggerFactory;
55 * This class holds the definition of an Apex policy. A policy is made up of a tree of states, each represented by an
56 * {@link AxState} instance. The states of a policy are held in a map in the policy. The state tree is built up at
57 * policy design time by a policy editor and each state is connected to its next state by an {@link AxStateOutput}
60 * <p>Execution of a policy is triggered by an event. A policy starts execution from its first state so the trigger
61 * event for the first sate is the trigger event for the entire policy. Execution from that first state can continue to
62 * one or more subsequent states and so on down branches of states. The state output of the final state in a branch has
63 * no next state, indicating the end of execution of that branch. Therefore, the set of output events from final states
64 * 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
65 * a policy and recursive execution of states in the same execution branch is not allowed, so the same state may not
66 * execute more than once on a single execution of a policy.
68 * <p>The template of a policy is a string that can be used by policy editors to store meta information on the policy
69 * that can be used at design time. The policy template string is not used during policy execution.
71 * <p>During validation of a policy, the validation checks listed below are executed: <ol> <li>The policy key must not
72 * 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
73 * least one state must be defined <li>Keys and values must all be defined, that is not null <li>The key on each entry
74 * 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
75 * policy must be the key of that policy <li>Each state must itself be valid, see validation in {@link AxState} <li>The
76 * 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
77 * 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
78 * in a policy,a warning is issued <li>The state tree of the policy must be valid, see validation in {@link AxStateTree}
83 @Table(name = "AxPolicy")
84 @XmlRootElement(name = "apexPolicy", namespace = "http://www.onap.org/policy/apex-pdp")
85 @XmlAccessorType(XmlAccessType.FIELD)
86 @XmlType(name = "AxPolicy", namespace = "http://www.onap.org/policy/apex-pdp", propOrder =
87 { "key", "template", "stateMap", "firstState" })
89 public class AxPolicy extends AxConcept {
90 private static final long serialVersionUID = -1775614096390365941L;
92 // Logger for this class
93 private static final XLogger LOGGER = XLoggerFactory.getXLogger(AxPolicy.class);
96 @XmlElement(name = "policyKey", required = true)
97 private AxArtifactKey key;
99 @Column(name = "template")
100 @XmlElement(required = true)
101 private String template;
104 @OneToMany(cascade = CascadeType.ALL)
105 @JoinTable(joinColumns = {@JoinColumn(name = "parentKeyName", referencedColumnName = "name"),
106 @JoinColumn(name = "parentKeyVersion", referencedColumnName = "version")})
107 @XmlElement(name = "state", required = true)
108 private Map<String, AxState> stateMap;
111 @Column(name = "firstState")
112 @XmlElement(required = true)
113 private String firstState;
116 * The Default Constructor creates a policy instance with a null key, a blank template and undefined first state.
119 this(new AxArtifactKey());
125 * @param copyConcept the concept to copy from
127 public AxPolicy(final AxPolicy copyConcept) {
132 * The Key Constructor creates a policy instance with the given key, a blank template and undefined first state.
134 * @param key the key of the policy
136 public AxPolicy(final AxArtifactKey key) {
137 this(key, "", new TreeMap<>(), "");
141 * This Constructor creates a policy with the given key and all its fields defined.
143 * @param key the key of the policy
144 * @param template the policy template for policy editor metadata
145 * @param stateMap the state map containing the states of the policy
146 * @param firstState the first state that will execute on this policy
148 public AxPolicy(final AxArtifactKey key, final String template, final Map<String, AxState> stateMap,
149 final String firstState) {
151 Assertions.argumentNotNull(key, "key may not be null");
152 Assertions.argumentNotNull(template, "template may not be null");
153 Assertions.argumentNotNull(stateMap, "stateMap may not be null");
154 Assertions.argumentNotNull(firstState, "firstState may not be null");
157 this.template = template;
158 this.stateMap = stateMap;
159 this.firstState = firstState;
163 * Gets a tree that holds all the possible execution paths for this policy. This method may be used for verification
164 * of policies, to find the branches of policy execution and the final states of policies.
166 * @return the state tree of the policy, a tree representing the execution branches of the policy
168 public AxStateTree getStateTree() {
169 return new AxStateTree(this, stateMap.get(firstState), null);
176 public AxArtifactKey getKey() {
184 public List<AxKey> getKeys() {
185 final List<AxKey> keyList = key.getKeys();
186 for (final AxState state : stateMap.values()) {
187 keyList.addAll(state.getKeys());
193 * Sets the key of the policy.
195 * @param key the key of the policy
197 public void setKey(final AxArtifactKey key) {
198 Assertions.argumentNotNull(key, "key may not be null");
203 * Gets the policy template for policy editor metadata.
205 * @return the policy template for policy editor metadata
207 public String getTemplate() {
212 * Sets the policy template for policy editor metadata.
214 * @param template the policy template for policy editor metadata
216 public void setTemplate(final String template) {
217 Assertions.argumentNotNull(template, "template may not be null");
218 this.template = template;
222 * Gets a map containing the states of the policy.
224 * @return the map of states in the policy
226 public Map<String, AxState> getStateMap() {
231 * Sets a map containing the states of the policy.
233 * @param stateMap a map of states in the policy
235 public void setStateMap(final Map<String, AxState> stateMap) {
236 Assertions.argumentNotNull(stateMap, "stateMap may not be null");
237 this.stateMap = stateMap;
241 * Gets the first state of the policy.
243 * @return the first state of the policy
245 public String getFirstState() {
250 * Sets the first state of the policy.
252 * @param firstState the first state of the policy
254 public void setFirstState(final String firstState) {
255 Assertions.argumentNotNull(firstState, "firstState may not be null");
256 this.firstState = firstState;
263 public AxValidationResult validate(final AxValidationResult resultIn) {
264 AxValidationResult result = resultIn;
266 if (key.equals(AxArtifactKey.getNullKey())) {
267 result.addValidationMessage(new AxValidationMessage(key, this.getClass(), ValidationResult.INVALID,
268 "key is a null key"));
271 result = key.validate(result);
273 if (template.trim().length() == 0) {
274 result.addValidationMessage(new AxValidationMessage(key, this.getClass(), ValidationResult.OBSERVATION,
275 "a policy template has not been specified"));
278 if (stateMap.size() == 0) {
279 result.addValidationMessage(new AxValidationMessage(key, this.getClass(), ValidationResult.INVALID,
280 "stateMap may not be empty"));
282 for (final Entry<String, AxState> stateEntry : stateMap.entrySet()) {
283 result = validateStateEntry(stateEntry, result);
287 // Validation continues from this point only if all validation checks this far have been
289 if (!result.isOk()) {
293 // We only check the unused states on models validated this far
294 if (firstState.trim().length() == 0) {
295 result.addValidationMessage(new AxValidationMessage(key, this.getClass(), ValidationResult.INVALID,
296 "no first state specified, first state may not be blank"));
298 if (!stateMap.containsKey(firstState)) {
299 result.addValidationMessage(new AxValidationMessage(key, this.getClass(), ValidationResult.INVALID,
300 "first state not found in stateMap"));
302 validateStateTree(result);
310 * Validate a state entry.
312 * @param stateEntry the state entry to validate
313 * @param result The validation result to append to
314 * @return The result of the validation
316 private AxValidationResult validateStateEntry(final Entry<String, AxState> stateEntry, AxValidationResult result) {
317 if (stateEntry.getKey() == null || stateEntry.getKey().equals(AxKey.NULL_KEY_NAME)) {
318 result.addValidationMessage(new AxValidationMessage(key, this.getClass(), ValidationResult.INVALID,
319 "key on state entry key " + stateEntry.getKey() + " may not be the null key"));
323 if (stateEntry.getValue() == null) {
324 result.addValidationMessage(new AxValidationMessage(key, this.getClass(), ValidationResult.INVALID,
325 "value on state entry value " + stateEntry.getKey() + " may not be null"));
329 if (!stateEntry.getKey().equals(stateEntry.getValue().getKey().getLocalName())) {
330 result.addValidationMessage(new AxValidationMessage(key, this.getClass(), ValidationResult.INVALID,
331 "key on state entry key " + stateEntry.getKey()
332 + " does not equal state entry value local name "
333 + stateEntry.getValue().getKey().getLocalName()));
336 if (!stateEntry.getValue().getKey().getParentArtifactKey().equals(key)) {
337 result.addValidationMessage(new AxValidationMessage(key, this.getClass(), ValidationResult.INVALID,
338 "parent key on state entry key " + stateEntry.getValue().getKey()
339 + " does not equal policy key"));
342 result = stateEntry.getValue().validate(result);
344 for (final AxStateOutput stateOutput : stateEntry.getValue().getStateOutputs().values()) {
345 if (!stateOutput.getNextState().equals(AxReferenceKey.getNullKey())
346 && !stateMap.containsKey(stateOutput.getNextState().getLocalName())) {
347 result.addValidationMessage(new AxValidationMessage(key, this.getClass(), ValidationResult.INVALID,
348 " nextState of state " + stateEntry.getKey() + " not found in StateMap: "
349 + stateOutput.getNextState().getId()));
357 * Validate a state tree to ensure there are no circular references in it.
359 * @param result The validation result to append to
360 * @return The result of the validation
362 private AxValidationResult validateStateTree(AxValidationResult result) {
364 // Constructor validates policy state tree
365 AxStateTree policyStateTree = getStateTree();
367 // Check for unused states
368 final Set<AxState> referencedStateSet = policyStateTree.getReferencedStateSet();
369 final Set<AxState> unreferencedStateSet = new TreeSet<>(stateMap.values());
370 unreferencedStateSet.removeAll(referencedStateSet);
372 for (final AxState unreferencedState : unreferencedStateSet) {
373 result.addValidationMessage(new AxValidationMessage(key, this.getClass(), ValidationResult.WARNING,
374 "state " + unreferencedState.getKey()
375 + " is not referenced in the policy execution tree"));
377 } catch (PolicyRuntimeException pre) {
378 AxValidationMessage validationMessage = new AxValidationMessage(key, this.getClass(),
379 ValidationResult.WARNING, "state tree in policy is invalid");
380 LOGGER.trace(validationMessage.getMessage(), pre);
381 result.addValidationMessage(validationMessage);
391 public void clean() {
393 firstState = firstState.trim();
400 public String toString() {
401 final StringBuilder builder = new StringBuilder();
402 builder.append(this.getClass().getSimpleName());
403 builder.append(":(");
404 builder.append("key=");
406 builder.append(",template=");
407 builder.append(template);
408 builder.append(",stateMap=");
409 builder.append(stateMap);
410 builder.append(",firstState=");
411 builder.append(firstState);
413 return builder.toString();
420 public AxConcept copyTo(final AxConcept targetObject) {
421 Assertions.argumentNotNull(targetObject, "target may not be null");
423 final Object copyObject = targetObject;
424 Assertions.instanceOf(copyObject, AxPolicy.class);
426 final AxPolicy copy = ((AxPolicy) copyObject);
427 copy.setKey(new AxArtifactKey(key));
428 copy.setTemplate(template);
430 final Map<String, AxState> newStateMap = new TreeMap<>();
431 for (final Entry<String, AxState> stateMapEntry : stateMap.entrySet()) {
432 newStateMap.put(stateMapEntry.getKey(), new AxState(stateMapEntry.getValue()));
434 copy.setStateMap(newStateMap);
436 copy.setFirstState(firstState);
445 public int hashCode() {
446 final int prime = 31;
448 result = prime * result + key.hashCode();
449 result = prime * result + template.hashCode();
450 result = prime * result + stateMap.hashCode();
451 result = prime * result + firstState.hashCode();
459 public boolean equals(final Object obj) {
466 if (getClass() != obj.getClass()) {
470 final AxPolicy other = (AxPolicy) obj;
471 if (!key.equals(other.key)) {
474 if (!template.equals(other.template)) {
477 if (!stateMap.equals(other.stateMap)) {
480 return firstState.equals(other.firstState);
487 public int compareTo(final AxConcept otherObj) {
488 if (otherObj == null) {
491 if (this == otherObj) {
494 if (getClass() != otherObj.getClass()) {
495 return this.hashCode() - otherObj.hashCode();
498 final AxPolicy other = (AxPolicy) otherObj;
499 if (!key.equals(other.key)) {
500 return key.compareTo(other.key);
502 if (!template.equals(other.template)) {
503 return template.compareTo(other.template);
505 if (!stateMap.equals(other.stateMap)) {
506 return (stateMap.hashCode() - other.stateMap.hashCode());
508 return firstState.compareTo(other.firstState);