Changes for checkstyle 8.32
[policy/apex-pdp.git] / model / policy-model / src / main / java / org / onap / policy / apex / model / policymodel / concepts / AxPolicy.java
1 /*-
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
9  *
10  *      http://www.apache.org/licenses/LICENSE-2.0
11  *
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.
17  *
18  * SPDX-License-Identifier: Apache-2.0
19  * ============LICENSE_END=========================================================
20  */
21
22 package org.onap.policy.apex.model.policymodel.concepts;
23
24 import java.util.List;
25 import java.util.Map;
26 import java.util.Map.Entry;
27 import java.util.Set;
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;
53
54 /**
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}
58  * instance.
59  *
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.
67  *
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.
70  *
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}
79  * </ol>
80  */
81
82 @Entity
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" })
88
89 public class AxPolicy extends AxConcept {
90     private static final long serialVersionUID = -1775614096390365941L;
91
92     // Logger for this class
93     private static final XLogger LOGGER = XLoggerFactory.getXLogger(AxPolicy.class);
94
95     @EmbeddedId
96     @XmlElement(name = "policyKey", required = true)
97     private AxArtifactKey key;
98
99     @Column(name = "template")
100     @XmlElement(required = true)
101     private String template;
102
103     // @formatter:off
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;
109     // @formatter:on
110
111     @Column(name = "firstState")
112     @XmlElement(required = true)
113     private String firstState;
114
115     /**
116      * The Default Constructor creates a policy instance with a null key, a blank template and undefined first state.
117      */
118     public AxPolicy() {
119         this(new AxArtifactKey());
120     }
121
122     /**
123      * Copy constructor.
124      *
125      * @param copyConcept the concept to copy from
126      */
127     public AxPolicy(final AxPolicy copyConcept) {
128         super(copyConcept);
129     }
130
131     /**
132      * The Key Constructor creates a policy instance with the given key, a blank template and undefined first state.
133      *
134      * @param key the key of the policy
135      */
136     public AxPolicy(final AxArtifactKey key) {
137         this(key, "", new TreeMap<String, AxState>(), "");
138     }
139
140     /**
141      * This Constructor creates a policy with the given key and all its fields defined.
142      *
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
147      */
148     public AxPolicy(final AxArtifactKey key, final String template, final Map<String, AxState> stateMap,
149                     final String firstState) {
150         super();
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");
155
156         this.key = key;
157         this.template = template;
158         this.stateMap = stateMap;
159         this.firstState = firstState;
160     }
161
162     /**
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.
165      *
166      * @return the state tree of the policy, a tree representing the execution branches of the policy
167      */
168     public AxStateTree getStateTree() {
169         return new AxStateTree(this, stateMap.get(firstState), null);
170     }
171
172     /**
173      * {@inheritDoc}.
174      */
175     @Override
176     public AxArtifactKey getKey() {
177         return key;
178     }
179
180     /**
181      * {@inheritDoc}.
182      */
183     @Override
184     public List<AxKey> getKeys() {
185         final List<AxKey> keyList = key.getKeys();
186         for (final AxState state : stateMap.values()) {
187             keyList.addAll(state.getKeys());
188         }
189         return keyList;
190     }
191
192     /**
193      * Sets the key of the policy.
194      *
195      * @param key the key of the policy
196      */
197     public void setKey(final AxArtifactKey key) {
198         Assertions.argumentNotNull(key, "key may not be null");
199         this.key = key;
200     }
201
202     /**
203      * Gets the policy template for policy editor metadata.
204      *
205      * @return the policy template for policy editor metadata
206      */
207     public String getTemplate() {
208         return template;
209     }
210
211     /**
212      * Sets the policy template for policy editor metadata.
213      *
214      * @param template the policy template for policy editor metadata
215      */
216     public void setTemplate(final String template) {
217         Assertions.argumentNotNull(template, "template may not be null");
218         this.template = template;
219     }
220
221     /**
222      * Gets a map containing the states of the policy.
223      *
224      * @return the map of states in the policy
225      */
226     public Map<String, AxState> getStateMap() {
227         return stateMap;
228     }
229
230     /**
231      * Sets a map containing the states of the policy.
232      *
233      * @param stateMap a map of states in the policy
234      */
235     public void setStateMap(final Map<String, AxState> stateMap) {
236         Assertions.argumentNotNull(stateMap, "stateMap may not be null");
237         this.stateMap = stateMap;
238     }
239
240     /**
241      * Gets the first state of the policy.
242      *
243      * @return the first state of the policy
244      */
245     public String getFirstState() {
246         return firstState;
247     }
248
249     /**
250      * Sets the first state of the policy.
251      *
252      * @param firstState the first state of the policy
253      */
254     public void setFirstState(final String firstState) {
255         Assertions.argumentNotNull(firstState, "firstState may not be null");
256         this.firstState = firstState;
257     }
258
259     /**
260      * {@inheritDoc}.
261      */
262     @Override
263     public AxValidationResult validate(final AxValidationResult resultIn) {
264         AxValidationResult result = resultIn;
265
266         if (key.equals(AxArtifactKey.getNullKey())) {
267             result.addValidationMessage(new AxValidationMessage(key, this.getClass(), ValidationResult.INVALID,
268                             "key is a null key"));
269         }
270
271         result = key.validate(result);
272
273         if (template.trim().length() == 0) {
274             result.addValidationMessage(new AxValidationMessage(key, this.getClass(), ValidationResult.OBSERVATION,
275                             "a policy template has not been specified"));
276         }
277
278         if (stateMap.size() == 0) {
279             result.addValidationMessage(new AxValidationMessage(key, this.getClass(), ValidationResult.INVALID,
280                             "stateMap may not be empty"));
281         } else {
282             for (final Entry<String, AxState> stateEntry : stateMap.entrySet()) {
283                 result = validateStateEntry(stateEntry, result);
284             }
285         }
286
287         // Validation continues from this point only if all validation checks this far have been
288         // passed
289         if (!result.isOk()) {
290             return result;
291         }
292
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"));
297         } else {
298             if (!stateMap.containsKey(firstState)) {
299                 result.addValidationMessage(new AxValidationMessage(key, this.getClass(), ValidationResult.INVALID,
300                                 "first state not found in stateMap"));
301             } else {
302                 validateStateTree(result);
303             }
304         }
305
306         return result;
307     }
308
309     /**
310      * Validate a state entry.
311      *
312      * @param stateEntry the state entry to validate
313      * @param result The validation result to append to
314      * @return The result of the validation
315      */
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"));
320             return result;
321         }
322
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"));
326             return result;
327         }
328
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()));
334         }
335
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"));
340         }
341
342         result = stateEntry.getValue().validate(result);
343
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()));
350             }
351         }
352
353         return result;
354     }
355
356     /**
357      * Validate a state tree to ensure there are no circular references in it.
358      *
359      * @param result The validation result to append to
360      * @return The result of the validation
361      */
362     private AxValidationResult validateStateTree(AxValidationResult result) {
363         try {
364             // Constructor validates policy state tree
365             AxStateTree policyStateTree = getStateTree();
366
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);
371
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"));
376             }
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);
382         }
383
384         return result;
385     }
386
387     /**
388      * {@inheritDoc}.
389      */
390     @Override
391     public void clean() {
392         key.clean();
393         firstState = firstState.trim();
394     }
395
396     /**
397      * {@inheritDoc}.
398      */
399     @Override
400     public String toString() {
401         final StringBuilder builder = new StringBuilder();
402         builder.append(this.getClass().getSimpleName());
403         builder.append(":(");
404         builder.append("key=");
405         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);
412         builder.append(")");
413         return builder.toString();
414     }
415
416     /**
417      * {@inheritDoc}.
418      */
419     @Override
420     public AxConcept copyTo(final AxConcept targetObject) {
421         Assertions.argumentNotNull(targetObject, "target may not be null");
422
423         final Object copyObject = targetObject;
424         Assertions.instanceOf(copyObject, AxPolicy.class);
425
426         final AxPolicy copy = ((AxPolicy) copyObject);
427         copy.setKey(new AxArtifactKey(key));
428         copy.setTemplate(template);
429
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()));
433         }
434         copy.setStateMap(newStateMap);
435
436         copy.setFirstState(firstState);
437
438         return copy;
439     }
440
441     /**
442      * {@inheritDoc}.
443      */
444     @Override
445     public int hashCode() {
446         final int prime = 31;
447         int result = 1;
448         result = prime * result + key.hashCode();
449         result = prime * result + template.hashCode();
450         result = prime * result + stateMap.hashCode();
451         result = prime * result + firstState.hashCode();
452         return result;
453     }
454
455     /**
456      * {@inheritDoc}.
457      */
458     @Override
459     public boolean equals(final Object obj) {
460         if (obj == null) {
461             return false;
462         }
463         if (this == obj) {
464             return true;
465         }
466         if (getClass() != obj.getClass()) {
467             return false;
468         }
469
470         final AxPolicy other = (AxPolicy) obj;
471         if (!key.equals(other.key)) {
472             return false;
473         }
474         if (!template.equals(other.template)) {
475             return false;
476         }
477         if (!stateMap.equals(other.stateMap)) {
478             return false;
479         }
480         return firstState.equals(other.firstState);
481     }
482
483     /**
484      * {@inheritDoc}.
485      */
486     @Override
487     public int compareTo(final AxConcept otherObj) {
488         if (otherObj == null) {
489             return -1;
490         }
491         if (this == otherObj) {
492             return 0;
493         }
494         if (getClass() != otherObj.getClass()) {
495             return this.hashCode() - otherObj.hashCode();
496         }
497
498         final AxPolicy other = (AxPolicy) otherObj;
499         if (!key.equals(other.key)) {
500             return key.compareTo(other.key);
501         }
502         if (!template.equals(other.template)) {
503             return template.compareTo(other.template);
504         }
505         if (!stateMap.equals(other.stateMap)) {
506             return (stateMap.hashCode() - other.stateMap.hashCode());
507         }
508         return firstState.compareTo(other.firstState);
509     }
510 }