2 * ============LICENSE_START=======================================================
3 * Copyright (C) 2016-2018 Ericsson. All rights reserved.
4 * Modifications Copyright (C) 2018 Samsung Electronics Co., Ltd.
5 * Modifications Copyright (C) 2019 Nordix Foundation.
6 * ================================================================================
7 * Licensed under the Apache License, Version 2.0 (the "License");
8 * you may not use this file except in compliance with the License.
9 * You may obtain a copy of the License at
11 * http://www.apache.org/licenses/LICENSE-2.0
13 * Unless required by applicable law or agreed to in writing, software
14 * distributed under the License is distributed on an "AS IS" BASIS,
15 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16 * See the License for the specific language governing permissions and
17 * limitations under the License.
19 * SPDX-License-Identifier: Apache-2.0
20 * ============LICENSE_END=========================================================
23 package org.onap.policy.apex.model.policymodel.concepts;
25 import java.util.List;
27 import java.util.Map.Entry;
29 import java.util.TreeMap;
30 import java.util.TreeSet;
31 import javax.persistence.AttributeOverride;
32 import javax.persistence.AttributeOverrides;
33 import javax.persistence.CascadeType;
34 import javax.persistence.CollectionTable;
35 import javax.persistence.Column;
36 import javax.persistence.ElementCollection;
37 import javax.persistence.Embedded;
38 import javax.persistence.EmbeddedId;
39 import javax.persistence.Entity;
40 import javax.persistence.JoinColumn;
41 import javax.persistence.JoinTable;
42 import javax.persistence.ManyToMany;
43 import javax.persistence.OneToOne;
44 import javax.persistence.Table;
45 import javax.xml.bind.Unmarshaller;
46 import javax.xml.bind.annotation.XmlAccessType;
47 import javax.xml.bind.annotation.XmlAccessorType;
48 import javax.xml.bind.annotation.XmlElement;
49 import javax.xml.bind.annotation.XmlRootElement;
50 import javax.xml.bind.annotation.XmlType;
51 import org.onap.policy.apex.model.basicmodel.concepts.AxArtifactKey;
52 import org.onap.policy.apex.model.basicmodel.concepts.AxConcept;
53 import org.onap.policy.apex.model.basicmodel.concepts.AxKey;
54 import org.onap.policy.apex.model.basicmodel.concepts.AxKeyUse;
55 import org.onap.policy.apex.model.basicmodel.concepts.AxReferenceKey;
56 import org.onap.policy.apex.model.basicmodel.concepts.AxValidationMessage;
57 import org.onap.policy.apex.model.basicmodel.concepts.AxValidationResult;
58 import org.onap.policy.apex.model.basicmodel.concepts.AxValidationResult.ValidationResult;
59 import org.onap.policy.common.utils.validation.Assertions;
62 * 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
63 * single input event, its trigger. A state can output many events, but can only output one event on a single execution.
64 * After it executes, a state can pass control to another state or can simply emit its event to an external system. In
65 * the case where a state passes control to another state, the output event of the state becomes the input event of the
66 * next state. The outputs of a state {@link AxStateOutput} are held as a map in the state. Each state output contains
67 * the outgoing event of the state and optionally the next state to pass control to.
69 * <p>A state uses tasks {@link AxTask} to execute its logic. A state holds its tasks in a map and must have at least
70 * one task. A state uses Task Selection Logic {@link AxTaskSelectionLogic} to select which task should be executed in a
71 * given execution cycle. Optional Task Selection Logic can use fields on the incoming event and information from the
72 * context albums available on the state to decide what task to execute in a given context. The default task of a state
73 * is the task that is executed when task Selection Logic is not specified. In cases where only a single task is
74 * specified on a state, the default task must be that task and the state always executes that task.
76 * <p>What happens when a state completes its execution cycle depends on the task that is selected for execution by the
77 * state. Therefore, the action to be performed a state on execution of each task must be defined in the state as a
78 * {@link AxStateTaskReference} instance for each task defined in the state. The {@link AxStateTaskReference} instance
79 * defines the action to be performed as either a {@link AxStateTaskOutputType} of {@link AxStateTaskOutputType#DIRECT}
80 * or {@link AxStateTaskOutputType#LOGIC} and contains an {@link AxReferenceKey} reference to the instance that will
81 * complete the state output.
83 * <p>In the case of direct output, the {@link AxReferenceKey} reference in the {@link AxStateTaskReference} instance is
84 * a reference to an {@link AxStateOutput} instance. The state output defines the event to be emitted by the state and
85 * the next state to pass control to if any. All fields of the executed task are marshaled onto the outgoing event
86 * automatically by Apex.
88 * <p>In the case of logic output, the {@link AxReferenceKey} reference in the {@link AxStateTaskReference} instance is
89 * a reference to State Finalizer Logic in an {@link AxStateFinalizerLogic} instance, which selects the
90 * {@link AxStateOutput} that the state will use. The state finalizer logic uses fields emitted by the executed task and
91 * information from the context albums available on the state to decide what {@link AxStateOutput} to select in a given
92 * context. The state output defines the event to be emitted by the state and the next state to pass control to if any.
93 * The State Finalizer Logic instances for the state are held in a map in the state. State Finalizer Logic must marshal
94 * the fields of the output event in whatever manner it wishes; Apex does not automatically transfer the output fields
95 * from the task directly to the output event.
97 * <p>The Task Selection Logic instance or State Finalizer Logic instances in a state may use information in context
98 * albums to arrive at their task or state output selections. The context albums that the state uses and that should be
99 * made available to the state by Apex policy distribution are held as a set of references to context albums in the
102 * <p>During validation of a state, the validation checks listed below are executed: <ol> <li>The policy key must not be
103 * a null key and must be valid, see validation in {@link AxReferenceKey} <li>The trigger event key must not be a null
104 * key and must be valid, see validation in {@link AxArtifactKey} <li>At least one state output must be defined <li>Each
105 * state output in a state must have that state as its parent <li>Each state output must be valid, see validation in
106 * {@link AxStateOutput} <li>The next state defined in a state output must be unique in a state <li>The default task key
107 * must not be a null key and must be valid, see validation in {@link AxArtifactKey} <li>The default task must appear in
108 * 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
109 * the state must not be a null key and must be valid, see validation in {@link AxArtifactKey} <li>All state task
110 * references for each task in the state must exist and must be valid, see validation in {@link AxStateTaskReference}
111 * <li>Each state task reference in a state must have that state as its parent <li>For direct state outputs from tasks,
112 * the state output must be defined on the state <li>For logic state outputs from tasks, the State Finalizer Logic must
113 * be defined on the state <li>An observation is issued for each state output defined on the state that is not used as a
114 * direct output on a task <li>An observation is issued for each state finalizer logic instance defined on the state
115 * 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
116 * a null key and must be valid, see validation in {@link AxArtifactKey} <li>Task Selection logic in a state must have
117 * that state as its parent <li>Task Selection logic in a state must be valid, see validation in
118 * {@link AxTaskSelectionLogic} <li>Each State Finalizer logic instance in a state must have that state as its parent
119 * <li>Each State Finalizer logic instance in a state must be valid, see validation in {@link AxStateFinalizerLogic}
124 @Table(name = "AxState")
126 @XmlAccessorType(XmlAccessType.FIELD)
127 @XmlRootElement(name = "apexState", namespace = "http://www.onap.org/policy/apex-pdp")
128 @XmlType(name = "AxState", namespace = "http://www.onap.org/policy/apex-pdp", propOrder =
129 { "key", "trigger", "stateOutputs", "contextAlbumReferenceSet", "taskSelectionLogic", "stateFinalizerLogicMap",
130 "defaultTask", "taskReferenceMap" })
132 public class AxState extends AxConcept {
133 private static final String DOES_NOT_EQUAL_STATE_KEY = " does not equal state key";
135 private static final long serialVersionUID = 8041771382337655535L;
138 @XmlElement(name = "stateKey", required = true)
139 private AxReferenceKey key;
143 @AttributeOverrides({@AttributeOverride(name = "name", column = @Column(name = "inTriggerName")),
144 @AttributeOverride(name = "version", column = @Column(name = "inTriggerVersion"))})
145 @Column(name = "trigger")
146 @XmlElement(required = true)
147 private AxArtifactKey trigger;
149 @ManyToMany(cascade = CascadeType.ALL)
151 joinColumns = {@JoinColumn(name = "soParentKeyName", referencedColumnName = "parentKeyName"),
152 @JoinColumn(name = "soParentKeyVersion", referencedColumnName = "parentKeyVersion"),
153 @JoinColumn(name = "soParentLocalName", referencedColumnName = "parentLocalName"),
154 @JoinColumn(name = "soLocalName", referencedColumnName = "localName")},
155 inverseJoinColumns = {@JoinColumn(name = "stateParentKeyName", referencedColumnName = "parentKeyName"),
156 @JoinColumn(name = "stateParentKeyVersion", referencedColumnName = "parentKeyVersion"),
157 @JoinColumn(name = "stateParentLocalName", referencedColumnName = "parentLocalName"),
158 @JoinColumn(name = "stateLocalName", referencedColumnName = "localName")})
159 @XmlElement(name = "stateOutputs", required = true)
160 private Map<String, AxStateOutput> stateOutputs;
163 @CollectionTable(joinColumns = {@JoinColumn(name = "stateParentKeyName", referencedColumnName = "parentKeyName"),
164 @JoinColumn(name = "stateParentKeyVersion", referencedColumnName = "parentKeyVersion"),
165 @JoinColumn(name = "stateParentLocalName", referencedColumnName = "parentLocalName"),
166 @JoinColumn(name = "stateLocalName", referencedColumnName = "localName")})
167 @XmlElement(name = "contextAlbumReference")
168 private Set<AxArtifactKey> contextAlbumReferenceSet;
171 @JoinTable(name = "STATE_TSL_JT",
173 @JoinColumn(name = "tslParentKeyName", referencedColumnName = "parentKeyName", updatable = false,
175 @JoinColumn(name = "tslParentKeyVersion", referencedColumnName = "parentKeyVersion",
176 updatable = false, insertable = false),
177 @JoinColumn(name = "tslParentLocalName ", referencedColumnName = "parentLocalName",
178 updatable = false, insertable = false),
179 @JoinColumn(name = "tslLocalName", referencedColumnName = "localName", updatable = false,
180 insertable = false)})
181 @XmlElement(required = true)
182 private AxTaskSelectionLogic taskSelectionLogic;
184 @ManyToMany(cascade = CascadeType.ALL)
186 joinColumns = {@JoinColumn(name = "sflParentKeyName", referencedColumnName = "parentKeyName"),
187 @JoinColumn(name = "sflParentKeyVersion", referencedColumnName = "parentKeyVersion"),
188 @JoinColumn(name = "sflParentLocalName", referencedColumnName = "parentLocalName"),
189 @JoinColumn(name = "sflLocalName", referencedColumnName = "localName")},
190 inverseJoinColumns = {@JoinColumn(name = "stateParentKeyName", referencedColumnName = "parentKeyName"),
191 @JoinColumn(name = "stateParentKeyVersion", referencedColumnName = "parentKeyVersion"),
192 @JoinColumn(name = "stateParentLocalName", referencedColumnName = "parentLocalName"),
193 @JoinColumn(name = "stateLocalName", referencedColumnName = "localName")})
194 @XmlElement(name = "stateFinalizerLogicMap", required = true)
195 private Map<String, AxStateFinalizerLogic> stateFinalizerLogicMap;
198 @AttributeOverrides({@AttributeOverride(name = "name", column = @Column(name = "defaultTaskName")),
199 @AttributeOverride(name = "version", column = @Column(name = "defaultTaskVersion"))})
200 @Column(name = "defaultTask")
201 @XmlElement(required = true)
202 private AxArtifactKey defaultTask;
204 @ManyToMany(cascade = CascadeType.ALL)
206 joinColumns = {@JoinColumn(name = "trmParentKeyName", referencedColumnName = "parentKeyName"),
207 @JoinColumn(name = "trmParentKeyVersion", referencedColumnName = "parentKeyVersion"),
208 @JoinColumn(name = "trmParentLocalName", referencedColumnName = "parentLocalName"),
209 @JoinColumn(name = "trmLocalName", referencedColumnName = "localName")},
210 inverseJoinColumns = {@JoinColumn(name = "stateParentKeyName", referencedColumnName = "parentKeyName"),
211 @JoinColumn(name = "stateParentKeyVersion", referencedColumnName = "parentKeyVersion"),
212 @JoinColumn(name = "stateParentLocalName", referencedColumnName = "parentLocalName"),
213 @JoinColumn(name = "stateLocalName", referencedColumnName = "localName")})
214 @XmlElement(name = "taskReferences", required = true)
215 private Map<AxArtifactKey, AxStateTaskReference> taskReferenceMap;
219 * The Default Constructor creates a state with a null reference key and with default values for all other fields.
222 this(new AxReferenceKey());
223 contextAlbumReferenceSet = new TreeSet<>();
224 taskReferenceMap = new TreeMap<>();
230 * @param copyConcept the concept to copy from
232 public AxState(final AxState copyConcept) {
237 * The Keyed Constructor creates a state with the given reference key and with default values for all other fields.
239 * @param key the reference key of the state
241 public AxState(final AxReferenceKey key) {
243 this(new AxStateParamsBuilder()
245 .trigger(AxArtifactKey.getNullKey()) // Trigger Reference
246 .stateOutputs(new TreeMap<String, AxStateOutput>()) // State Outputs
247 .contextAlbumReferenceSet(new TreeSet<AxArtifactKey>()) // Context Album Refs
248 .taskSelectionLogic(new AxTaskSelectionLogic()) // Task Selection Logic
249 .stateFinalizerLogicMap(new TreeMap<String, AxStateFinalizerLogic>()) // State Finalizer Logics
250 .defaultTask(AxArtifactKey.getNullKey()) // Default Task
251 .taskReferenceMap(new TreeMap<AxArtifactKey, AxStateTaskReference>()) // Task References
257 * This Constructor creates a state with all its fields defined.
259 * @param axStateParams parameters for state creation
261 // CHECKSTYLE:OFF: checkstyle:parameterNumber
262 public AxState(AxStateParamsBuilder axStateParams) {
264 Assertions.argumentNotNull(axStateParams.getKey(), "key may not be null");
265 Assertions.argumentNotNull(axStateParams.getTrigger(), "trigger may not be null");
266 Assertions.argumentNotNull(axStateParams.getStateOutputs(), "stateOutputs may not be null");
267 Assertions.argumentNotNull(axStateParams.getContextAlbumReferenceSet(),
268 "contextAlbumReferenceSet may not be null");
269 Assertions.argumentNotNull(axStateParams.getTaskSelectionLogic(), "taskSelectionLogic may not be null");
270 Assertions.argumentNotNull(axStateParams.getStateFinalizerLogicMap(), "stateFinalizerLogicMap may not be null");
271 Assertions.argumentNotNull(axStateParams.getDefaultTask(), "defaultTask may not be null");
272 Assertions.argumentNotNull(axStateParams.getTaskReferenceMap(), "taskReferenceMap may not be null");
274 this.key = axStateParams.getKey();
275 this.trigger = axStateParams.getTrigger();
276 this.stateOutputs = axStateParams.getStateOutputs();
277 this.contextAlbumReferenceSet = axStateParams.getContextAlbumReferenceSet();
278 this.taskSelectionLogic = axStateParams.getTaskSelectionLogic();
279 this.stateFinalizerLogicMap = axStateParams.getStateFinalizerLogicMap();
280 this.defaultTask = axStateParams.getDefaultTask();
281 this.taskReferenceMap = axStateParams.getTaskReferenceMap();
283 // CHECKSTYLE:ON: checkstyle:parameterNumber
286 * When a state is unmarshalled from disk or from the database, the parent of contained objects is not defined. This
287 * method is called by JAXB after unmarshaling and is used to set the parent keys of all
288 * {@link AxTaskSelectionLogic}, {@link AxStateOutput}, and {@link AxStateFinalizerLogic} instance in the state.
290 * @param unmarshaler the unmarshaler that is unmarshaling the model
291 * @param parent the parent object of this object in the unmarshaler
293 public void afterUnmarshal(final Unmarshaller unmarshaler, final Object parent) {
294 if (!taskSelectionLogic.getKey().getLocalName().equals(AxKey.NULL_KEY_NAME)) {
295 taskSelectionLogic.getKey().setParentReferenceKey(key);
298 for (final Entry<String, AxStateOutput> soEntry : stateOutputs.entrySet()) {
299 soEntry.getValue().getKey().setParentReferenceKey(key);
302 for (final Entry<String, AxStateFinalizerLogic> sflEntry : stateFinalizerLogicMap.entrySet()) {
303 sflEntry.getValue().getKey().setParentReferenceKey(key);
306 for (final Entry<AxArtifactKey, AxStateTaskReference> trEntry : taskReferenceMap.entrySet()) {
307 trEntry.getValue().getKey().setParentReferenceKey(key);
312 * Gets the names of all the states that this state may pass control to.
314 * @return the list of possible states that may receive control when this state completes execution
316 public Set<String> getNextStateSet() {
317 final Set<String> nextStateSet = new TreeSet<>();
319 for (final AxStateOutput stateOutput : stateOutputs.values()) {
320 nextStateSet.add(stateOutput.getNextState().getLocalName());
329 public AxReferenceKey getKey() {
337 public List<AxKey> getKeys() {
338 final List<AxKey> keyList = key.getKeys();
339 keyList.add(new AxKeyUse(trigger.getKey()));
340 for (final AxStateOutput stateOutput : stateOutputs.values()) {
341 keyList.addAll(stateOutput.getKeys());
343 for (final AxArtifactKey contextAlbumReferenceKey : contextAlbumReferenceSet) {
344 keyList.add(new AxKeyUse(contextAlbumReferenceKey));
346 if (!taskSelectionLogic.getKey().equals(AxReferenceKey.getNullKey())) {
347 keyList.addAll(taskSelectionLogic.getKeys());
349 for (final Entry<String, AxStateFinalizerLogic> stateFinalizerLogicEntry : stateFinalizerLogicMap.entrySet()) {
350 keyList.addAll(stateFinalizerLogicEntry.getValue().getKeys());
352 keyList.add(new AxKeyUse(defaultTask.getKey()));
353 for (final Entry<AxArtifactKey, AxStateTaskReference> taskReferenceEntry : taskReferenceMap.entrySet()) {
354 keyList.add(new AxKeyUse(taskReferenceEntry.getKey()));
356 // A state output is allowed to be used more than once but we only return one usage as a
358 for (AxKey referencedKey : taskReferenceEntry.getValue().getKeys()) {
359 if (keyList.contains(referencedKey)) {
360 keyList.add(referencedKey);
368 * Sets the reference key of the state.
370 * @param key the state reference key
372 public void setKey(final AxReferenceKey key) {
373 Assertions.argumentNotNull(key, "key may not be null");
378 * Gets the event that triggers the state.
380 * @return the event that triggers the state
382 public AxArtifactKey getTrigger() {
387 * Sets the event that triggers the state.
389 * @param trigger the event that triggers the state
391 public void setTrigger(final AxArtifactKey trigger) {
392 Assertions.argumentNotNull(trigger, "trigger may not be null");
393 this.trigger = trigger;
397 * Gets the possible state outputs for the state.
399 * @return the the possible state outputs for the state
401 public Map<String, AxStateOutput> getStateOutputs() {
406 * Sets the the possible state outputs for the state.
408 * @param stateOutputs the the possible state outputs for the state
410 public void setStateOutputs(final Map<String, AxStateOutput> stateOutputs) {
411 Assertions.argumentNotNull(stateOutputs, "stateOutputs may not be null");
412 this.stateOutputs = stateOutputs;
416 * Gets the context album reference set defines the context that may be used by Task Selection Logic and State
417 * Finalizer Logic in the state.
419 * @return the context album reference set defines the context that may be used by Task Selection Logic and State
420 * Finalizer Logic in the state
422 public Set<AxArtifactKey> getContextAlbumReferences() {
423 return contextAlbumReferenceSet;
427 * Sets the context album reference set defines the context that may be used by Task Selection Logic and State
428 * Finalizer Logic in the state.
430 * @param contextAlbumReferences the context album reference set defines the context that may be used by Task
431 * Selection Logic and State Finalizer Logic in the state
433 public void setContextAlbumReferences(final Set<AxArtifactKey> contextAlbumReferences) {
434 Assertions.argumentNotNull(contextAlbumReferences, "contextAlbumReferenceSet may not be null");
435 this.contextAlbumReferenceSet = contextAlbumReferences;
439 * Gets the task selection logic that selects the task a state executes in an execution cycle.
441 * @return the task selection logic that selects the task a state executes in an execution cycle
443 public AxTaskSelectionLogic getTaskSelectionLogic() {
444 return taskSelectionLogic;
448 * Sets the task selection logic that selects the task a state executes in an execution cycle.
450 * @param taskSelectionLogic the task selection logic that selects the task a state executes in an execution cycle
452 public void setTaskSelectionLogic(final AxTaskSelectionLogic taskSelectionLogic) {
453 Assertions.argumentNotNull(taskSelectionLogic, "taskSelectionLogic may not be null");
454 this.taskSelectionLogic = taskSelectionLogic;
458 * Check if task selection logic has been specified the state.
460 * @return true, if task selection logic has been specified
462 public boolean checkSetTaskSelectionLogic() {
463 return !taskSelectionLogic.getKey().equals(AxReferenceKey.getNullKey());
467 * Gets the state finalizer logic instances that selects the state output to use after a task executes in a state
470 * @return the state finalizer logic instances that selects the state output to use after a task executes in a state
473 public Map<String, AxStateFinalizerLogic> getStateFinalizerLogicMap() {
474 return stateFinalizerLogicMap;
478 * Sets the state finalizer logic instances that selects the state output to use after a task executes in a state
481 * @param stateFinalizerLogicMap the state finalizer logic instances that selects the state output to use after a
482 * task executes in a state execution cycle
484 public void setStateFinalizerLogicMap(final Map<String, AxStateFinalizerLogic> stateFinalizerLogicMap) {
485 Assertions.argumentNotNull(stateFinalizerLogicMap, "stateFinalizerLogic may not be null");
486 this.stateFinalizerLogicMap = stateFinalizerLogicMap;
490 * Gets the default task that will execute in a state if Task Selection Logic is not specified.
492 * @return the default task that will execute in a state if Task Selection Logic is not specified
494 public AxArtifactKey getDefaultTask() {
499 * Sets the default task that will execute in a state if Task Selection Logic is not specified.
501 * @param defaultTask the default task that will execute in a state if Task Selection Logic is not specified
503 public void setDefaultTask(final AxArtifactKey defaultTask) {
504 Assertions.argumentNotNull(defaultTask, "defaultTask may not be null");
505 this.defaultTask = defaultTask;
509 * Gets the task reference map that defines the tasks for the state and how the task outputs are handled.
511 * @return the task reference map that defines the tasks for the state and how the task outputs are handled
513 public Map<AxArtifactKey, AxStateTaskReference> getTaskReferences() {
514 return taskReferenceMap;
518 * Sets the task reference map that defines the tasks for the state and how the task outputs are handled.
520 * @param taskReferences the task reference map that defines the tasks for the state and how the task outputs are
523 public void setTaskReferences(final Map<AxArtifactKey, AxStateTaskReference> taskReferences) {
524 Assertions.argumentNotNull(taskReferences, "taskReferenceMap may not be null");
525 this.taskReferenceMap = taskReferences;
532 public AxValidationResult validate(final AxValidationResult resultIn) {
533 AxValidationResult result = resultIn;
535 if (key.equals(AxReferenceKey.getNullKey())) {
536 result.addValidationMessage(new AxValidationMessage(key, this.getClass(), ValidationResult.INVALID,
537 "key is a null key"));
540 result = key.validate(result);
542 if (trigger.equals(AxArtifactKey.getNullKey())) {
543 result.addValidationMessage(new AxValidationMessage(key, this.getClass(), ValidationResult.INVALID,
544 "trigger is a null key: " + trigger));
546 result = trigger.validate(result);
548 if (stateOutputs.size() == 0) {
549 result.addValidationMessage(new AxValidationMessage(key, this.getClass(), ValidationResult.INVALID,
550 "stateOutputs may not be empty"));
552 validateStateOutputs(result);
555 validateContextAlbumReferences(result);
556 result = validateTaskSelectionLogic(result);
557 validateStateFinalizerLogics(result);
559 if (defaultTask.equals(AxArtifactKey.getNullKey())) {
560 result.addValidationMessage(new AxValidationMessage(key, this.getClass(), ValidationResult.INVALID,
561 "default task has a null key: " + defaultTask));
563 result = defaultTask.validate(result);
565 if (taskReferenceMap.size() == 0) {
566 result.addValidationMessage(new AxValidationMessage(key, this.getClass(), ValidationResult.INVALID,
567 "taskReferenceMap may not be empty"));
569 validateStateTaskReferences(result);
576 * Validate the state outputs of the state.
578 * @param result the validation result to append to
580 private void validateStateOutputs(AxValidationResult result) {
581 final Set<String> nextStateNameSet = new TreeSet<>();
582 for (final Entry<String, AxStateOutput> stateOutputEntry : stateOutputs.entrySet()) {
583 if (stateOutputEntry.getValue() == null) {
584 result.addValidationMessage(new AxValidationMessage(key, this.getClass(), ValidationResult.INVALID,
585 "null state output value found on state output " + stateOutputEntry.getKey()));
587 if (!stateOutputEntry.getValue().getKey().getParentReferenceKey().equals(key)) {
588 result.addValidationMessage(new AxValidationMessage(key, this.getClass(), ValidationResult.INVALID,
589 "parent key on state output " + stateOutputEntry.getKey()
590 + DOES_NOT_EQUAL_STATE_KEY));
593 if (stateOutputEntry.getValue().getNextState().getLocalName().equals(key.getLocalName())) {
594 result.addValidationMessage(new AxValidationMessage(key, this.getClass(), ValidationResult.INVALID,
595 "state output next state "
596 + stateOutputEntry.getValue().getNextState().getLocalName()
597 + " may not be this state"));
601 if (nextStateNameSet.contains(stateOutputEntry.getValue().getNextState().getLocalName())) {
602 result.addValidationMessage(new AxValidationMessage(key, this.getClass(), ValidationResult.INVALID,
603 "duplicate state output next state name "
604 + stateOutputEntry.getValue().getNextState().getLocalName()
607 nextStateNameSet.add(stateOutputEntry.getValue().getNextState().getLocalName());
609 result = stateOutputEntry.getValue().validate(result);
615 * Validate the context album references of the state.
617 * @param result the validation result to append to
619 private void validateContextAlbumReferences(AxValidationResult result) {
620 for (final AxArtifactKey contextAlbumReference : contextAlbumReferenceSet) {
621 if (contextAlbumReference.equals(AxArtifactKey.getNullKey())) {
622 result.addValidationMessage(new AxValidationMessage(key, this.getClass(), ValidationResult.INVALID,
623 "key on context album reference entry " + contextAlbumReference.getKey()
624 + " may not be the null key"));
627 result = contextAlbumReference.validate(result);
632 * Validate the task selection logic of the state.
634 * @param result the validation result to append to
635 * @return the result of the validation
637 private AxValidationResult validateTaskSelectionLogic(AxValidationResult result) {
638 if (!taskSelectionLogic.getKey().equals(AxReferenceKey.getNullKey())) {
639 if (!taskSelectionLogic.getKey().getParentReferenceKey().equals(key)) {
640 result.addValidationMessage(new AxValidationMessage(key, this.getClass(), ValidationResult.INVALID,
641 "taskSelectionLogic key " + taskSelectionLogic.getKey().getId()
642 + DOES_NOT_EQUAL_STATE_KEY));
644 result = taskSelectionLogic.validate(result);
651 * Validate all the state finalizer logic of the state.
653 * @param result the validation result to append to
655 private void validateStateFinalizerLogics(AxValidationResult result) {
656 for (final Entry<String, AxStateFinalizerLogic> stateFinalizerLogicEntry : stateFinalizerLogicMap.entrySet()) {
657 if (stateFinalizerLogicEntry.getValue() == null) {
658 result.addValidationMessage(new AxValidationMessage(key, this.getClass(), ValidationResult.INVALID,
659 "null state finalizer logic value found on state finalizer entry "
660 + stateFinalizerLogicEntry.getKey()));
662 if (!stateFinalizerLogicEntry.getValue().getKey().getParentReferenceKey().equals(key)) {
663 result.addValidationMessage(new AxValidationMessage(key, this.getClass(), ValidationResult.INVALID,
664 "stateFinalizerLogic parent key "
665 + stateFinalizerLogicEntry.getValue().getKey().getId()
666 + DOES_NOT_EQUAL_STATE_KEY));
669 result = stateFinalizerLogicEntry.getValue().validate(result);
675 * Validate the tasks used the state.
677 * @param result the validation result to append to
679 private void validateStateTaskReferences(AxValidationResult result) {
680 final Set<String> usedStateOutputNameSet = new TreeSet<>();
681 final Set<String> usedStateFinalizerLogicNameSet = new TreeSet<>();
683 for (final Entry<AxArtifactKey, AxStateTaskReference> taskRefEntry : taskReferenceMap.entrySet()) {
684 if (taskRefEntry.getKey().equals(AxArtifactKey.getNullKey())) {
685 result.addValidationMessage(new AxValidationMessage(key, this.getClass(), ValidationResult.INVALID,
686 "task has a null key: " + taskRefEntry.getKey()));
688 result = taskRefEntry.getKey().validate(result);
690 if (taskRefEntry.getValue() == null) {
691 result.addValidationMessage(new AxValidationMessage(key, this.getClass(), ValidationResult.INVALID,
692 "null task reference value found on task reference " + taskRefEntry.getKey()));
694 result = validateStateTaskReference(taskRefEntry.getKey(), taskRefEntry.getValue(),
695 usedStateOutputNameSet, usedStateFinalizerLogicNameSet, result);
699 final Set<String> unUsedStateOutputNameSet = new TreeSet<>(stateOutputs.keySet());
700 unUsedStateOutputNameSet.removeAll(usedStateOutputNameSet);
701 for (final String unUsedStateOutputName : unUsedStateOutputNameSet) {
702 result.addValidationMessage(new AxValidationMessage(key, this.getClass(), ValidationResult.OBSERVATION,
703 "state output " + unUsedStateOutputName + " is not used directly by any task"));
706 final Set<String> usnUedStateFinalizerLogicNameSet = new TreeSet<>(stateFinalizerLogicMap.keySet());
707 usnUedStateFinalizerLogicNameSet.removeAll(usedStateFinalizerLogicNameSet);
708 for (final String unusedStateFinalizerLogicName : usnUedStateFinalizerLogicNameSet) {
709 result.addValidationMessage(new AxValidationMessage(key, this.getClass(), ValidationResult.OBSERVATION,
710 "state finalizer logic " + unusedStateFinalizerLogicName + " is not used by any task"));
713 if (!taskReferenceMap.containsKey(defaultTask)) {
714 result.addValidationMessage(new AxValidationMessage(key, this.getClass(), ValidationResult.INVALID,
715 "defaultTask " + defaultTask + " not found in taskReferenceMap"));
720 * Validate the references of a task used in a state.
722 * @param taskKey The key of the task
723 * @param taskReference the task reference of the task
724 * @param stateOutputNameSet State outputs that have been used so far, will be appended for this task reference
725 * @param stateFinalizerLogicNameSet State finalizers that have been used so far, may be appended if this task
726 * reference uses a finalzier
727 * @param result the validation result to append to
728 * @return the result of the validation
730 private AxValidationResult validateStateTaskReference(final AxArtifactKey taskKey,
731 final AxStateTaskReference taskReference, Set<String> stateOutputNameSet,
732 Set<String> stateFinalizerLogicNameSet, AxValidationResult result) {
733 if (!taskReference.getKey().getParentReferenceKey().equals(key)) {
734 result.addValidationMessage(new AxValidationMessage(key, this.getClass(), ValidationResult.INVALID,
735 "stateTaskReference parent key " + taskReference.getKey().getId()
736 + DOES_NOT_EQUAL_STATE_KEY));
739 if (taskReference.getStateTaskOutputType().equals(AxStateTaskOutputType.DIRECT)) {
740 if (stateOutputs.containsKey(taskReference.getOutput().getLocalName())) {
741 stateOutputNameSet.add(taskReference.getOutput().getLocalName());
743 result.addValidationMessage(new AxValidationMessage(key, this.getClass(), ValidationResult.INVALID,
744 "state output for task " + taskKey + " not found in stateOutputs"));
746 } else if (taskReference.getStateTaskOutputType().equals(AxStateTaskOutputType.LOGIC)) {
747 if (stateFinalizerLogicMap.containsKey(taskReference.getOutput().getLocalName())) {
748 stateFinalizerLogicNameSet.add(taskReference.getOutput().getLocalName());
750 result.addValidationMessage(new AxValidationMessage(key, this.getClass(), ValidationResult.INVALID,
751 "state finalizer logic for task " + taskKey + " not found in stateFinalizerLogicMap"));
754 result.addValidationMessage(new AxValidationMessage(key, this.getClass(), ValidationResult.INVALID,
755 "stateTaskReference task output type " + taskReference.getStateTaskOutputType()
759 return taskReference.validate(result);
766 public void clean() {
769 for (final AxStateOutput stateOutput : stateOutputs.values()) {
772 for (final AxArtifactKey contextAlbumReference : contextAlbumReferenceSet) {
773 contextAlbumReference.clean();
775 taskSelectionLogic.clean();
776 for (final AxStateFinalizerLogic stateFinalizerLogic : stateFinalizerLogicMap.values()) {
777 stateFinalizerLogic.clean();
780 for (final AxStateTaskReference taskReference : taskReferenceMap.values()) {
781 taskReference.clean();
789 public String toString() {
790 final StringBuilder builder = new StringBuilder();
791 builder.append(this.getClass().getSimpleName());
792 builder.append(":(");
793 builder.append("stateKey=");
795 builder.append(",trigger=");
796 builder.append(trigger);
797 builder.append(",stateOutputs=");
798 builder.append(stateOutputs);
799 builder.append(",contextAlbumReferenceSet=");
800 builder.append(contextAlbumReferenceSet);
801 builder.append(",taskSelectionLogic=");
802 builder.append(taskSelectionLogic);
803 builder.append(",stateFinalizerLogicSet=");
804 builder.append(stateFinalizerLogicMap);
805 builder.append(",defaultTask=");
806 builder.append(defaultTask);
807 builder.append(",taskReferenceMap=");
808 builder.append(taskReferenceMap);
810 return builder.toString();
817 public AxConcept copyTo(final AxConcept targetObject) {
818 Assertions.argumentNotNull(targetObject, "target may not be null");
820 final Object copyObject = targetObject;
821 Assertions.instanceOf(copyObject, AxState.class);
823 final AxState copy = ((AxState) copyObject);
824 copy.setKey(new AxReferenceKey(key));
825 copy.setTrigger(new AxArtifactKey(trigger));
827 final Map<String, AxStateOutput> newStateOutputs = new TreeMap<>();
828 for (final Entry<String, AxStateOutput> stateOutputEntry : stateOutputs.entrySet()) {
829 newStateOutputs.put(stateOutputEntry.getKey(), new AxStateOutput(stateOutputEntry.getValue()));
831 copy.setStateOutputs(newStateOutputs);
833 final Set<AxArtifactKey> newContextUsage = new TreeSet<>();
834 for (final AxArtifactKey contextAlbumReferenceItem : contextAlbumReferenceSet) {
835 newContextUsage.add(new AxArtifactKey(contextAlbumReferenceItem));
837 copy.setContextAlbumReferences(newContextUsage);
839 copy.setTaskSelectionLogic(new AxTaskSelectionLogic(taskSelectionLogic));
841 final Map<String, AxStateFinalizerLogic> newStateFinalizerLogicMap = new TreeMap<>();
842 for (final Entry<String, AxStateFinalizerLogic> stateFinalizerLogicEntry : stateFinalizerLogicMap.entrySet()) {
843 newStateFinalizerLogicMap.put(stateFinalizerLogicEntry.getKey(),
844 new AxStateFinalizerLogic(stateFinalizerLogicEntry.getValue()));
846 copy.setStateFinalizerLogicMap(newStateFinalizerLogicMap);
848 copy.setDefaultTask(new AxArtifactKey(defaultTask));
850 final Map<AxArtifactKey, AxStateTaskReference> newTaskReferenceMap = new TreeMap<>();
851 for (final Entry<AxArtifactKey, AxStateTaskReference> taskReferenceEntry : taskReferenceMap.entrySet()) {
852 newTaskReferenceMap.put(new AxArtifactKey(taskReferenceEntry.getKey()),
853 new AxStateTaskReference(taskReferenceEntry.getValue()));
855 copy.setTaskReferences(newTaskReferenceMap);
864 public int hashCode() {
865 final int prime = 31;
867 result = prime * result + key.hashCode();
868 result = prime * result + trigger.hashCode();
869 result = prime * result + stateOutputs.hashCode();
870 result = prime * result + contextAlbumReferenceSet.hashCode();
871 result = prime * result + taskSelectionLogic.hashCode();
872 result = prime * result + stateFinalizerLogicMap.hashCode();
873 result = prime * result + defaultTask.hashCode();
874 result = prime * result + taskReferenceMap.hashCode();
882 public boolean equals(final Object obj) {
890 if (getClass() != obj.getClass()) {
894 final AxState other = (AxState) obj;
895 if (!key.equals(other.key)) {
898 if (!trigger.equals(other.trigger)) {
901 if (!stateOutputs.equals(other.stateOutputs)) {
904 if (!contextAlbumReferenceSet.equals(other.contextAlbumReferenceSet)) {
907 if (!taskSelectionLogic.equals(other.taskSelectionLogic)) {
910 if (!stateFinalizerLogicMap.equals(other.stateFinalizerLogicMap)) {
913 if (!defaultTask.equals(other.defaultTask)) {
916 return taskReferenceMap.equals(other.taskReferenceMap);
923 public int compareTo(final AxConcept otherObj) {
924 if (otherObj == null) {
927 if (this == otherObj) {
930 if (getClass() != otherObj.getClass()) {
931 return this.hashCode() - otherObj.hashCode();
934 return compareObjectFields((AxState) otherObj);
938 * Compare the object fields on this state to another state.
940 * @param the other state to compare with
941 * @return the result of the comparison
943 private int compareObjectFields(final AxState other) {
944 if (!key.equals(other.key)) {
945 return key.compareTo(other.key);
947 if (!trigger.equals(other.trigger)) {
948 return trigger.compareTo(other.trigger);
950 if (!stateOutputs.equals(other.stateOutputs)) {
951 return stateOutputs.hashCode() - other.stateOutputs.hashCode();
953 if (!contextAlbumReferenceSet.equals(other.contextAlbumReferenceSet)) {
954 return (contextAlbumReferenceSet.hashCode() - other.contextAlbumReferenceSet.hashCode());
956 if (!taskSelectionLogic.equals(other.taskSelectionLogic)) {
957 return taskSelectionLogic.compareTo(other.taskSelectionLogic);
959 if (!stateFinalizerLogicMap.equals(other.stateFinalizerLogicMap)) {
960 return stateFinalizerLogicMap.hashCode() - other.stateFinalizerLogicMap.hashCode();
962 if (!defaultTask.equals(other.defaultTask)) {
963 return defaultTask.compareTo(other.defaultTask);
965 if (!taskReferenceMap.equals(other.taskReferenceMap)) {
966 return (taskReferenceMap.hashCode() - other.taskReferenceMap.hashCode());