2 * ============LICENSE_START=======================================================
3 * Copyright (C) 2016-2018 Ericsson. All rights reserved.
4 * Modifications Copyright (C) 2019 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;
31 import javax.persistence.CascadeType;
32 import javax.persistence.Column;
33 import javax.persistence.EmbeddedId;
34 import javax.persistence.Entity;
35 import javax.persistence.JoinColumn;
36 import javax.persistence.JoinTable;
37 import javax.persistence.OneToMany;
38 import javax.persistence.Table;
39 import javax.xml.bind.annotation.XmlAccessType;
40 import javax.xml.bind.annotation.XmlAccessorType;
41 import javax.xml.bind.annotation.XmlElement;
42 import javax.xml.bind.annotation.XmlRootElement;
43 import javax.xml.bind.annotation.XmlType;
45 import org.onap.policy.apex.model.basicmodel.concepts.AxArtifactKey;
46 import org.onap.policy.apex.model.basicmodel.concepts.AxConcept;
47 import org.onap.policy.apex.model.basicmodel.concepts.AxKey;
48 import org.onap.policy.apex.model.basicmodel.concepts.AxReferenceKey;
49 import org.onap.policy.apex.model.basicmodel.concepts.AxValidationMessage;
50 import org.onap.policy.apex.model.basicmodel.concepts.AxValidationResult;
51 import org.onap.policy.apex.model.basicmodel.concepts.AxValidationResult.ValidationResult;
52 import org.onap.policy.common.utils.validation.Assertions;
53 import org.slf4j.ext.XLogger;
54 import org.slf4j.ext.XLoggerFactory;
57 * This class holds the definition of an Apex policy. A policy is made up of a tree of states, each represented by an
58 * {@link AxState} instance. The states of a policy are held in a map in the policy. The state tree is built up at
59 * policy design time by a policy editor and each state is connected to its next state by an {@link AxStateOutput}
62 * <p>Execution of a policy is triggered by an event. A policy starts execution from its first state so the trigger
63 * event for the first sate is the trigger event for the entire policy. Execution from that first state can continue to
64 * one or more subsequent states and so on down branches of states. The state output of the final state in a branch has
65 * no next state, indicating the end of execution of that branch. Therefore, the set of output events from final states
66 * 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
67 * a policy and recursive execution of states in the same execution branch is not allowed, so the same state may not
68 * execute more than once on a single execution of a policy.
70 * <p>The template of a policy is a string that can be used by policy editors to store meta information on the policy
71 * that can be used at design time. The policy template string is not used during policy execution.
73 * <p>During validation of a policy, the validation checks listed below are executed: <ol> <li>The policy key must not
74 * 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
75 * least one state must be defined <li>Keys and values must all be defined, that is not null <li>The key on each entry
76 * 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
77 * policy must be the key of that policy <li>Each state must itself be valid, see validation in {@link AxState} <li>The
78 * 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
79 * 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
80 * in a policy,a warning is issued <li>The state tree of the policy must be valid, see validation in {@link AxStateTree}
85 @Table(name = "AxPolicy")
86 @XmlRootElement(name = "apexPolicy", namespace = "http://www.onap.org/policy/apex-pdp")
87 @XmlAccessorType(XmlAccessType.FIELD)
88 @XmlType(name = "AxPolicy", namespace = "http://www.onap.org/policy/apex-pdp", propOrder =
89 { "key", "template", "stateMap", "firstState" })
91 public class AxPolicy extends AxConcept {
92 private static final long serialVersionUID = -1775614096390365941L;
94 // Logger for this class
95 private static final XLogger LOGGER = XLoggerFactory.getXLogger(AxPolicy.class);
98 @XmlElement(name = "policyKey", required = true)
99 private AxArtifactKey key;
101 @Column(name = "template")
102 @XmlElement(required = true)
103 private String template;
106 @OneToMany(cascade = CascadeType.ALL)
107 @JoinTable(joinColumns = {@JoinColumn(name = "parentKeyName", referencedColumnName = "name"),
108 @JoinColumn(name = "parentKeyVersion", referencedColumnName = "version")})
109 @XmlElement(name = "state", required = true)
110 private Map<String, AxState> stateMap;
113 @Column(name = "firstState")
114 @XmlElement(required = true)
115 private String firstState;
118 * The Default Constructor creates a policy instance with a null key, a blank template and undefined first state.
121 this(new AxArtifactKey());
127 * @param copyConcept the concept to copy from
129 public AxPolicy(final AxPolicy copyConcept) {
134 * The Key Constructor creates a policy instance with the given key, a blank template and undefined first state.
136 * @param key the key of the policy
138 public AxPolicy(final AxArtifactKey key) {
139 this(key, "", new TreeMap<String, AxState>(), "");
143 * This Constructor creates a policy with the given key and all its fields defined.
145 * @param key the key of the policy
146 * @param template the policy template for policy editor metadata
147 * @param stateMap the state map containing the states of the policy
148 * @param firstState the first state that will execute on this policy
150 public AxPolicy(final AxArtifactKey key, final String template, final Map<String, AxState> stateMap,
151 final String firstState) {
153 Assertions.argumentNotNull(key, "key may not be null");
154 Assertions.argumentNotNull(template, "template may not be null");
155 Assertions.argumentNotNull(stateMap, "stateMap may not be null");
156 Assertions.argumentNotNull(firstState, "firstState may not be null");
159 this.template = template;
160 this.stateMap = stateMap;
161 this.firstState = firstState;
165 * Gets a tree that holds all the possible execution paths for this policy. This method may be used for verification
166 * of policies, to find the branches of policy execution and the final states of policies.
168 * @return the state tree of the policy, a tree representing the execution branches of the policy
170 public AxStateTree getStateTree() {
171 return new AxStateTree(this, stateMap.get(firstState), null);
178 public AxArtifactKey getKey() {
186 public List<AxKey> getKeys() {
187 final List<AxKey> keyList = key.getKeys();
188 for (final AxState state : stateMap.values()) {
189 keyList.addAll(state.getKeys());
195 * Sets the key of the policy.
197 * @param key the key of the policy
199 public void setKey(final AxArtifactKey key) {
200 Assertions.argumentNotNull(key, "key may not be null");
205 * Gets the policy template for policy editor metadata.
207 * @return the policy template for policy editor metadata
209 public String getTemplate() {
214 * Sets the policy template for policy editor metadata.
216 * @param template the policy template for policy editor metadata
218 public void setTemplate(final String template) {
219 Assertions.argumentNotNull(template, "template may not be null");
220 this.template = template;
224 * Gets a map containing the states of the policy.
226 * @return the map of states in the policy
228 public Map<String, AxState> getStateMap() {
233 * Sets a map containing the states of the policy.
235 * @param stateMap a map of states in the policy
237 public void setStateMap(final Map<String, AxState> stateMap) {
238 Assertions.argumentNotNull(stateMap, "stateMap may not be null");
239 this.stateMap = stateMap;
243 * Gets the first state of the policy.
245 * @return the first state of the policy
247 public String getFirstState() {
252 * Sets the first state of the policy.
254 * @param firstState the first state of the policy
256 public void setFirstState(final String firstState) {
257 Assertions.argumentNotNull(firstState, "firstState may not be null");
258 this.firstState = firstState;
265 public AxValidationResult validate(final AxValidationResult resultIn) {
266 AxValidationResult result = resultIn;
268 if (key.equals(AxArtifactKey.getNullKey())) {
269 result.addValidationMessage(new AxValidationMessage(key, this.getClass(), ValidationResult.INVALID,
270 "key is a null key"));
273 result = key.validate(result);
275 if (template.trim().length() == 0) {
276 result.addValidationMessage(new AxValidationMessage(key, this.getClass(), ValidationResult.OBSERVATION,
277 "a policy template has not been specified"));
280 if (stateMap.size() == 0) {
281 result.addValidationMessage(new AxValidationMessage(key, this.getClass(), ValidationResult.INVALID,
282 "stateMap may not be empty"));
284 for (final Entry<String, AxState> stateEntry : stateMap.entrySet()) {
285 result = validateStateEntry(stateEntry, result);
289 // Validation continues from this point only if all validation checks this far have been
291 if (!result.isOk()) {
295 // We only check the unused states on models validated this far
296 if (firstState.trim().length() == 0) {
297 result.addValidationMessage(new AxValidationMessage(key, this.getClass(), ValidationResult.INVALID,
298 "no first state specified, first state may not be blank"));
300 if (!stateMap.containsKey(firstState)) {
301 result.addValidationMessage(new AxValidationMessage(key, this.getClass(), ValidationResult.INVALID,
302 "first state not found in stateMap"));
304 validateStateTree(result);
312 * Validate a state entry.
314 * @param stateEntry the state entry to validate
315 * @param result The validation result to append to
316 * @return The result of the validation
318 private AxValidationResult validateStateEntry(final Entry<String, AxState> stateEntry, AxValidationResult result) {
319 if (stateEntry.getKey() == null || stateEntry.getKey().equals(AxKey.NULL_KEY_NAME)) {
320 result.addValidationMessage(new AxValidationMessage(key, this.getClass(), ValidationResult.INVALID,
321 "key on state entry key " + stateEntry.getKey() + " may not be the null key"));
325 if (stateEntry.getValue() == null) {
326 result.addValidationMessage(new AxValidationMessage(key, this.getClass(), ValidationResult.INVALID,
327 "value on state entry value " + stateEntry.getKey() + " may not be null"));
331 if (!stateEntry.getKey().equals(stateEntry.getValue().getKey().getLocalName())) {
332 result.addValidationMessage(new AxValidationMessage(key, this.getClass(), ValidationResult.INVALID,
333 "key on state entry key " + stateEntry.getKey()
334 + " does not equal state entry value local name "
335 + stateEntry.getValue().getKey().getLocalName()));
338 if (!stateEntry.getValue().getKey().getParentArtifactKey().equals(key)) {
339 result.addValidationMessage(new AxValidationMessage(key, this.getClass(), ValidationResult.INVALID,
340 "parent key on state entry key " + stateEntry.getValue().getKey()
341 + " does not equal policy key"));
344 result = stateEntry.getValue().validate(result);
346 for (final AxStateOutput stateOutput : stateEntry.getValue().getStateOutputs().values()) {
347 if (!stateOutput.getNextState().equals(AxReferenceKey.getNullKey())
348 && !stateMap.containsKey(stateOutput.getNextState().getLocalName())) {
349 result.addValidationMessage(new AxValidationMessage(key, this.getClass(), ValidationResult.INVALID,
350 " nextState of state " + stateEntry.getKey() + " not found in StateMap: "
351 + stateOutput.getNextState().getId()));
359 * Validate a state tree to ensure there are no circular references in it.
361 * @param result The validation result to append to
362 * @return The result of the validation
364 private AxValidationResult validateStateTree(AxValidationResult result) {
366 // Constructor validates policy state tree
367 AxStateTree policyStateTree = getStateTree();
369 // Check for unused states
370 final Set<AxState> referencedStateSet = policyStateTree.getReferencedStateSet();
371 final Set<AxState> unreferencedStateSet = new TreeSet<>(stateMap.values());
372 unreferencedStateSet.removeAll(referencedStateSet);
374 for (final AxState unreferencedState : unreferencedStateSet) {
375 result.addValidationMessage(new AxValidationMessage(key, this.getClass(), ValidationResult.WARNING,
376 "state " + unreferencedState.getKey()
377 + " is not referenced in the policy execution tree"));
379 } catch (PolicyRuntimeException pre) {
380 AxValidationMessage validationMessage = new AxValidationMessage(key, this.getClass(),
381 ValidationResult.WARNING, "state tree in policy is invalid");
382 LOGGER.trace(validationMessage.getMessage(), pre);
383 result.addValidationMessage(validationMessage);
393 public void clean() {
395 firstState = firstState.trim();
402 public String toString() {
403 final StringBuilder builder = new StringBuilder();
404 builder.append(this.getClass().getSimpleName());
405 builder.append(":(");
406 builder.append("key=");
408 builder.append(",template=");
409 builder.append(template);
410 builder.append(",stateMap=");
411 builder.append(stateMap);
412 builder.append(",firstState=");
413 builder.append(firstState);
415 return builder.toString();
422 public AxConcept copyTo(final AxConcept targetObject) {
423 Assertions.argumentNotNull(targetObject, "target may not be null");
425 final Object copyObject = targetObject;
426 Assertions.instanceOf(copyObject, AxPolicy.class);
428 final AxPolicy copy = ((AxPolicy) copyObject);
429 copy.setKey(new AxArtifactKey(key));
430 copy.setTemplate(template);
432 final Map<String, AxState> newStateMap = new TreeMap<>();
433 for (final Entry<String, AxState> stateMapEntry : stateMap.entrySet()) {
434 newStateMap.put(stateMapEntry.getKey(), new AxState(stateMapEntry.getValue()));
436 copy.setStateMap(newStateMap);
438 copy.setFirstState(firstState);
447 public int hashCode() {
448 final int prime = 31;
450 result = prime * result + key.hashCode();
451 result = prime * result + template.hashCode();
452 result = prime * result + stateMap.hashCode();
453 result = prime * result + firstState.hashCode();
461 public boolean equals(final Object obj) {
468 if (getClass() != obj.getClass()) {
472 final AxPolicy other = (AxPolicy) obj;
473 if (!key.equals(other.key)) {
476 if (!template.equals(other.template)) {
479 if (!stateMap.equals(other.stateMap)) {
482 return firstState.equals(other.firstState);
489 public int compareTo(final AxConcept otherObj) {
490 if (otherObj == null) {
493 if (this == otherObj) {
496 if (getClass() != otherObj.getClass()) {
497 return this.hashCode() - otherObj.hashCode();
500 final AxPolicy other = (AxPolicy) otherObj;
501 if (!key.equals(other.key)) {
502 return key.compareTo(other.key);
504 if (!template.equals(other.template)) {
505 return template.compareTo(other.template);
507 if (!stateMap.equals(other.stateMap)) {
508 return (stateMap.hashCode() - other.stateMap.hashCode());
510 return firstState.compareTo(other.firstState);