b088af78a04d466c5212f2b8df463068ae7caada
[policy/drools-applications.git] /
1 /*-
2  * ============LICENSE_START=======================================================
3  * policy-yaml
4  * ================================================================================
5  * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
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
10  * 
11  *      http://www.apache.org/licenses/LICENSE-2.0
12  * 
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.
18  * ============LICENSE_END=========================================================
19  */
20
21 package org.onap.policy.controlloop.compiler;
22
23 import com.google.common.collect.ImmutableList;
24 import com.google.common.collect.ImmutableMap;
25
26 import java.io.InputStream;
27 import java.io.Serializable;
28 import java.util.Collections;
29 import java.util.HashMap;
30 import java.util.List;
31 import java.util.Map;
32 import java.util.Map.Entry;
33
34 import org.jgrapht.DirectedGraph;
35 import org.jgrapht.graph.ClassBasedEdgeFactory;
36 import org.jgrapht.graph.DefaultEdge;
37 import org.jgrapht.graph.DirectedMultigraph;
38 import org.onap.policy.controlloop.policy.ControlLoop;
39 import org.onap.policy.controlloop.policy.ControlLoopPolicy;
40 import org.onap.policy.controlloop.policy.FinalResult;
41 import org.onap.policy.controlloop.policy.Policy;
42 import org.onap.policy.controlloop.policy.PolicyResult;
43 import org.onap.policy.controlloop.policy.TargetType;
44 import org.slf4j.Logger;
45 import org.slf4j.LoggerFactory;
46 import org.yaml.snakeyaml.Yaml;
47 import org.yaml.snakeyaml.constructor.Constructor;
48
49
50 public class ControlLoopCompiler implements Serializable {
51     private static final String OPERATION_POLICY = "Operation Policy ";
52     private static final long serialVersionUID = 1L;
53     private static final Logger LOGGER = LoggerFactory.getLogger(ControlLoopCompiler.class.getName());
54     
55     public static ControlLoopPolicy compile(ControlLoopPolicy policy, 
56                     ControlLoopCompilerCallback callback) throws CompilerException {
57         //
58         // Ensure the control loop is sane
59         //
60         validateControlLoop(policy.getControlLoop(), callback);
61         //
62         // Validate the policies
63         //
64         validatePolicies(policy, callback);
65         
66         return policy;
67     }
68     
69     public static ControlLoopPolicy compile(InputStream yamlSpecification, 
70                     ControlLoopCompilerCallback callback) throws CompilerException {
71         Yaml yaml = new Yaml(new Constructor(ControlLoopPolicy.class));
72         Object obj = yaml.load(yamlSpecification);
73         if (obj == null) {
74             throw new CompilerException("Could not parse yaml specification.");
75         }
76         if (! (obj instanceof ControlLoopPolicy)) {
77             throw new CompilerException("Yaml could not parse specification into required ControlLoopPolicy object");
78         }
79         return ControlLoopCompiler.compile((ControlLoopPolicy) obj, callback);
80     }
81     
82     private static void validateControlLoop(ControlLoop controlLoop, 
83                     ControlLoopCompilerCallback callback) throws CompilerException {
84         if (controlLoop == null && callback != null) {
85             callback.onError("controlLoop cannot be null");
86         }
87         if (controlLoop!=null){
88             if ((controlLoop.getControlLoopName() == null || controlLoop.getControlLoopName().length() < 1) 
89                             && callback != null) {
90                 callback.onError("Missing controlLoopName");
91             }
92             if ((!controlLoop.getVersion().contentEquals(ControlLoop.getVERSION())) && callback != null) {
93                 callback.onError("Unsupported version for this compiler");
94             }
95             if (controlLoop.getTrigger_policy() == null || controlLoop.getTrigger_policy().length() < 1) {
96                 throw new CompilerException("trigger_policy is not valid");
97             }
98         }
99     }
100
101     private static void validatePolicies(ControlLoopPolicy policy, 
102                     ControlLoopCompilerCallback callback) throws CompilerException {
103         if (policy == null) {
104             throw new CompilerException("policy cannot be null");
105         }
106         if (policy.getPolicies() == null) {
107             callback.onWarning("controlLoop is an open loop.");   
108         } else {
109             //
110             // For this version we can use a directed multigraph, in the future we may not be able to
111             //
112             DirectedGraph<NodeWrapper, LabeledEdge> graph = 
113                             new DirectedMultigraph<>(new ClassBasedEdgeFactory<NodeWrapper, 
114                                             LabeledEdge>(LabeledEdge.class));
115             //
116             // Check to see if the trigger Event is for OpenLoop, we do so by
117             // attempting to create a FinalResult object from it. If its a policy id, this should
118             // return null.
119             //
120             FinalResult triggerResult = FinalResult.toResult(policy.getControlLoop().getTrigger_policy());
121             TriggerNodeWrapper triggerNode;
122             //
123             // Did this turn into a FinalResult object?
124             //
125             if (triggerResult != null) {
126                 validateOpenLoopPolicy(policy, triggerResult, callback);
127                 return;
128                 //
129             } else {
130                 validatePoliciesContainTriggerPolicyAndCombinedTimeoutIsOk(policy, callback);
131                 triggerNode = new TriggerNodeWrapper(policy.getControlLoop().getControlLoopName());
132             }
133             //
134             // Add in the trigger node
135             //
136             graph.addVertex(triggerNode);
137             //
138             // Add in our Final Result nodes. All paths should end to these nodes.
139             //
140             FinalResultNodeWrapper finalSuccess = new FinalResultNodeWrapper(FinalResult.FINAL_SUCCESS);
141             FinalResultNodeWrapper finalFailure = new FinalResultNodeWrapper(FinalResult.FINAL_FAILURE);
142             FinalResultNodeWrapper finalFailureTimeout = new FinalResultNodeWrapper(FinalResult.FINAL_FAILURE_TIMEOUT);
143             FinalResultNodeWrapper finalFailureRetries = new FinalResultNodeWrapper(FinalResult.FINAL_FAILURE_RETRIES);
144             FinalResultNodeWrapper finalFailureException = 
145                             new FinalResultNodeWrapper(FinalResult.FINAL_FAILURE_EXCEPTION);
146             FinalResultNodeWrapper finalFailureGuard = new FinalResultNodeWrapper(FinalResult.FINAL_FAILURE_GUARD);
147             graph.addVertex(finalSuccess);
148             graph.addVertex(finalFailure);
149             graph.addVertex(finalFailureTimeout);
150             graph.addVertex(finalFailureRetries);
151             graph.addVertex(finalFailureException);
152             graph.addVertex(finalFailureGuard);
153             //
154             // Work through the policies and add them in as nodes.
155             //
156             Map<Policy, PolicyNodeWrapper> mapNodes = addPoliciesAsNodes(policy, graph, triggerNode, callback);
157             //
158             // last sweep to connect remaining edges for policy results
159             //
160             for (Policy operPolicy : policy.getPolicies()) {
161                 PolicyNodeWrapper node = mapNodes.get(operPolicy);
162                 //
163                 // Just ensure this has something
164                 //
165                 if (node == null) {
166                     continue;
167                 }
168                 addEdge(graph, mapNodes, operPolicy.getId(), operPolicy.getSuccess(), finalSuccess, 
169                                 PolicyResult.SUCCESS, node);
170                 addEdge(graph, mapNodes, operPolicy.getId(), operPolicy.getFailure(), finalFailure, 
171                                 PolicyResult.FAILURE, node);
172                 addEdge(graph, mapNodes, operPolicy.getId(), operPolicy.getFailure_timeout(), finalFailureTimeout, 
173                                 PolicyResult.FAILURE_TIMEOUT, node);
174                 addEdge(graph, mapNodes, operPolicy.getId(), operPolicy.getFailure_retries(), finalFailureRetries, 
175                                 PolicyResult.FAILURE_RETRIES, node);
176                 addEdge(graph, mapNodes, operPolicy.getId(), operPolicy.getFailure_exception(), finalFailureException, 
177                                 PolicyResult.FAILURE_EXCEPTION, node);
178                 addEdge(graph, mapNodes, operPolicy.getId(), operPolicy.getFailure_guard(), finalFailureGuard, 
179                                 PolicyResult.FAILURE_GUARD, node);
180             }
181             validateNodesAndEdges(graph, callback);
182         }   
183     }
184     
185     private static void validateOpenLoopPolicy(ControlLoopPolicy policy, FinalResult triggerResult, 
186                     ControlLoopCompilerCallback callback) throws CompilerException {
187         //
188         // Ensure they didn't use some other FinalResult code
189         //
190         if (triggerResult != FinalResult.FINAL_OPENLOOP) {
191             throw new CompilerException("Unexpected Final Result for trigger_policy, should only be " 
192         + FinalResult.FINAL_OPENLOOP.toString() + " or a valid Policy ID");
193         }
194         //
195         // They really shouldn't have any policies attached.
196         //
197         if ((policy.getPolicies() != null || policy.getPolicies().isEmpty()) && callback != null ) {
198             callback.onWarning("Open Loop policy contains policies. The policies will never be invoked.");
199         }
200     }
201     
202     private static void validatePoliciesContainTriggerPolicyAndCombinedTimeoutIsOk(ControlLoopPolicy policy, 
203                     ControlLoopCompilerCallback callback) throws CompilerException {
204         int sum = 0;
205         boolean triggerPolicyFound = false;
206         for (Policy operPolicy : policy.getPolicies()) {
207             sum += operPolicy.getTimeout().intValue();
208             if (policy.getControlLoop().getTrigger_policy().equals(operPolicy.getId())) {
209                 triggerPolicyFound = true;
210             }
211         }
212         if (policy.getControlLoop().getTimeout().intValue() < sum && callback != null) {
213             callback.onError("controlLoop overall timeout is less than the sum of operational policy timeouts.");
214         }
215         
216         if (!triggerPolicyFound) {
217             throw new CompilerException("Unexpected value for trigger_policy, should only be " 
218         + FinalResult.FINAL_OPENLOOP.toString() + " or a valid Policy ID");
219         }
220     }
221     
222     private static Map<Policy, PolicyNodeWrapper> addPoliciesAsNodes(ControlLoopPolicy policy, 
223             DirectedGraph<NodeWrapper, LabeledEdge> graph, TriggerNodeWrapper triggerNode, 
224             ControlLoopCompilerCallback callback) {
225         Map<Policy, PolicyNodeWrapper> mapNodes = new HashMap<>();
226         for (Policy operPolicy : policy.getPolicies()) {
227             //
228             // Is it still ok to add?
229             //
230             if (!okToAdd(operPolicy, callback)) {
231                 //
232                 // Do not add it in
233                 //
234                 continue;
235             }
236             //
237             // Create wrapper policy node and save it into our map so we can
238             // easily retrieve it.
239             //
240             PolicyNodeWrapper node = new PolicyNodeWrapper(operPolicy);
241             mapNodes.put(operPolicy, node);
242             graph.addVertex(node);
243             //
244             // Is this the trigger policy?
245             //
246             if (operPolicy.getId().equals(policy.getControlLoop().getTrigger_policy())) {
247                 //
248                 // Yes add an edge from our trigger event node to this policy
249                 //
250                 graph.addEdge(triggerNode, node, new LabeledEdge(triggerNode, node, new TriggerEdgeWrapper("ONSET")));
251             }
252         }
253         return mapNodes;
254     }
255     
256     private static void addEdge(DirectedGraph<NodeWrapper, LabeledEdge> graph, Map<Policy, PolicyNodeWrapper> mapNodes,
257                     String policyId, String connectedPolicy, 
258                     FinalResultNodeWrapper finalResultNodeWrapper, 
259                     PolicyResult policyResult, NodeWrapper node) throws CompilerException {
260         FinalResult finalResult = FinalResult.toResult(finalResultNodeWrapper.getID());
261         if (FinalResult.isResult(connectedPolicy, finalResult)) {
262             graph.addEdge(node, finalResultNodeWrapper, new LabeledEdge(node, finalResultNodeWrapper, 
263                             new FinalResultEdgeWrapper(finalResult)));
264         } else {
265             PolicyNodeWrapper toNode = findPolicyNode(mapNodes, connectedPolicy);
266             if (toNode == null) {
267                 throw new CompilerException(OPERATION_POLICY + policyId + " is connected to unknown policy " 
268             + connectedPolicy);
269             } else {
270                 graph.addEdge(node, toNode, new LabeledEdge(node, toNode, new PolicyResultEdgeWrapper(policyResult)));
271             }
272         }
273     }
274     
275     private static void validateNodesAndEdges(DirectedGraph<NodeWrapper, LabeledEdge> graph, 
276                     ControlLoopCompilerCallback callback) throws CompilerException {
277         for (NodeWrapper node : graph.vertexSet()) {
278             if (node instanceof TriggerNodeWrapper) {
279                 validateTriggerNodeWrapper(graph, node);
280             } else if (node instanceof FinalResultNodeWrapper) {
281                 validateFinalResultNodeWrapper(graph, node);
282             } else if (node instanceof PolicyNodeWrapper) {
283                 validatePolicyNodeWrapper(graph, node, callback);
284             }
285             for (LabeledEdge edge : graph.outgoingEdgesOf(node)) {
286                 LOGGER.info(edge.from.getID() + " invokes " + edge.to.getID() + " upon " + edge.edge.getID());
287             }
288         }
289     }
290     
291     private static void validateTriggerNodeWrapper(DirectedGraph<NodeWrapper, LabeledEdge> graph, 
292                     NodeWrapper node) throws CompilerException {
293         if (LOGGER.isDebugEnabled()) {
294             LOGGER.info("Trigger Node {}", node.toString());
295         }
296         if (graph.inDegreeOf(node) > 0 ) {
297             //
298             // Really should NEVER get here unless someone messed up the code above.
299             //
300             throw new CompilerException("No inputs to event trigger");
301         }
302         //
303         // Should always be 1, except in the future we may support multiple events
304         //
305         if (graph.outDegreeOf(node) > 1) {
306             throw new CompilerException("The event trigger should only go to ONE node");
307         }
308     }
309     
310     private static void validateFinalResultNodeWrapper(DirectedGraph<NodeWrapper, LabeledEdge> graph, 
311                     NodeWrapper node) throws CompilerException {
312         if (LOGGER.isDebugEnabled()) {
313             LOGGER.info("FinalResult Node {}", node.toString());
314         }
315         //
316         // FinalResult nodes should NEVER have an out edge
317         //
318         if (graph.outDegreeOf(node) > 0) {
319             throw new CompilerException("FinalResult nodes should never have any out edges.");
320         }
321     }
322     
323     private static void validatePolicyNodeWrapper(DirectedGraph<NodeWrapper, LabeledEdge> graph, 
324                     NodeWrapper node, ControlLoopCompilerCallback callback) throws CompilerException {
325         if (LOGGER.isDebugEnabled()) {
326             LOGGER.info("Policy Node {}", node.toString());
327         }
328         //
329         // All Policy Nodes should have the 5 out degrees defined.
330         //
331         if (graph.outDegreeOf(node) != 6) {
332             throw new CompilerException("Policy node should ALWAYS have 6 out degrees.");
333         }
334         //
335         // All Policy Nodes should have at least 1 in degrees 
336         // 
337         if (graph.inDegreeOf(node) == 0 && callback != null) {
338             callback.onWarning("Policy " + node.getID() + " is not reachable.");
339         }
340     }
341     
342     private static boolean okToAdd(Policy operPolicy, ControlLoopCompilerCallback callback) {
343         boolean isOk = isPolicyIdOk(operPolicy, callback);
344         isOk = isActorOk(operPolicy, callback) ? isOk : false;
345         isOk = isRecipeOk(operPolicy, callback) ? isOk : false;
346         isOk = isTargetOk(operPolicy, callback) ? isOk : false;
347         isOk = arePolicyResultsOk(operPolicy, callback) ? isOk : false;
348         return isOk;
349     }
350     
351     private static boolean isPolicyIdOk(Policy operPolicy, ControlLoopCompilerCallback callback) {
352         boolean isOk = true;
353         if (operPolicy.getId() == null || operPolicy.getId().length() < 1) {
354             if (callback != null) {
355                 callback.onError("Operational Policy has an bad ID");
356             }
357             isOk = false;
358         } else {
359             //
360             // Check if they decided to make the ID a result object
361             //
362             if (PolicyResult.toResult(operPolicy.getId()) != null) {
363                 if (callback != null) {
364                     callback.onError("Policy id is set to a PolicyResult " + operPolicy.getId());
365                 }
366                 isOk = false;
367             }
368             if (FinalResult.toResult(operPolicy.getId()) != null) {
369                 if (callback != null) {
370                     callback.onError("Policy id is set to a FinalResult " + operPolicy.getId());
371                 }
372                 isOk = false;
373             }
374         }
375         return isOk;
376     }
377     
378     private static boolean isActorOk(Policy operPolicy, ControlLoopCompilerCallback callback) {
379         boolean isOk = true;
380         if (operPolicy.getActor() == null) {
381             if (callback != null) {
382                 callback.onError("Policy actor is null");
383             }
384             isOk = false;
385         }
386         //
387         // Construct a list for all valid actors
388         //
389         ImmutableList<String> actors = ImmutableList.of("APPC", "AOTS", "MSO", "SDNO", "SDNR", "AAI");
390         //
391         if (operPolicy.getActor() != null && (!actors.contains(operPolicy.getActor())) ) {
392             if (callback != null) {
393                 callback.onError("Policy actor is invalid");
394             }
395             isOk = false;
396         }
397         return isOk;
398     }
399     
400     private static boolean isRecipeOk(Policy operPolicy, ControlLoopCompilerCallback callback) {
401         boolean isOk = true;
402         if (operPolicy.getRecipe() == null) {
403             if (callback != null) {
404                 callback.onError("Policy recipe is null");
405             }
406             isOk = false;
407         }
408         //
409         // NOTE: We need a way to find the acceptable recipe values (either Enum or a database that has these)
410         // 
411         ImmutableMap<String, List<String>> recipes = new ImmutableMap.Builder<String, List<String>>()
412                 .put("APPC", ImmutableList.of("Restart", "Rebuild", "Migrate", "ModifyConfig"))
413                 .put("AOTS", ImmutableList.of("checkMaintenanceWindow", 
414                                 "checkENodeBTicketHours", 
415                                 "checkEquipmentStatus", 
416                                 "checkEimStatus", 
417                                 "checkEquipmentMaintenance"))
418                 .put("MSO", ImmutableList.of("VF Module Create"))
419                 .put("SDNO", ImmutableList.of("health-diagnostic-type", 
420                                 "health-diagnostic", 
421                                 "health-diagnostic-history", 
422                                 "health-diagnostic-commands", 
423                                 "health-diagnostic-aes"))
424                 .put("SDNR", ImmutableList.of("Restart", "Reboot"))
425                 .build();
426         //
427         if (operPolicy.getRecipe() != null 
428                         && (!recipes.getOrDefault(operPolicy.getActor(), 
429                                         Collections.emptyList()).contains(operPolicy.getRecipe()))) {
430             if (callback != null) {
431                 callback.onError("Policy recipe is invalid");
432             }
433             isOk = false;
434         }
435         return isOk;
436     }
437     
438     private static boolean isTargetOk(Policy operPolicy, ControlLoopCompilerCallback callback) {
439         boolean isOk = true;
440         if (operPolicy.getTarget() == null) {
441             if (callback != null) {
442                 callback.onError("Policy target is null");
443             }
444             isOk = false;
445         }
446         if (operPolicy.getTarget() != null 
447                         && operPolicy.getTarget().getType() != TargetType.VM 
448                         && operPolicy.getTarget().getType() != TargetType.VFC 
449                         && operPolicy.getTarget().getType() != TargetType.PNF) {
450             if (callback != null) {
451                 callback.onError("Policy target is invalid");
452             }
453             isOk = false;
454         }
455         return isOk;
456     }
457     
458     private static boolean arePolicyResultsOk(Policy operPolicy, ControlLoopCompilerCallback callback) {
459         //
460         // Check that policy results are connected to either default final * or another policy
461         //
462         boolean isOk = isSuccessPolicyResultOk(operPolicy, callback);
463         isOk = isFailurePolicyResultOk(operPolicy, callback) ? isOk : false;
464         isOk = isFailureRetriesPolicyResultOk(operPolicy, callback) ? isOk : false;
465         isOk = isFailureTimeoutPolicyResultOk(operPolicy, callback) ? isOk : false;
466         isOk = isFailureExceptionPolicyResultOk(operPolicy, callback) ? isOk : false;
467         isOk = isFailureGuardPolicyResultOk(operPolicy, callback) ? isOk : false;
468         return isOk;
469     }
470     
471     private static boolean isSuccessPolicyResultOk(Policy operPolicy, ControlLoopCompilerCallback callback) {
472         boolean isOk = true;
473         if (FinalResult.toResult(operPolicy.getSuccess()) != null 
474                         && !operPolicy.getSuccess().equals(FinalResult.FINAL_SUCCESS.toString())) {
475             if (callback != null) {
476                 callback.onError("Policy success is neither another policy nor FINAL_SUCCESS");
477             }
478             isOk = false;
479         }
480         return isOk;
481     }
482     
483     private static boolean isFailurePolicyResultOk(Policy operPolicy, ControlLoopCompilerCallback callback) {
484         boolean isOk = true;
485         if (FinalResult.toResult(operPolicy.getFailure()) != null 
486                         && !operPolicy.getFailure().equals(FinalResult.FINAL_FAILURE.toString())) {
487             if (callback != null) {
488                 callback.onError("Policy failure is neither another policy nor FINAL_FAILURE");
489             }
490             isOk = false;
491         }
492         return isOk;
493     }
494     
495     private static boolean isFailureRetriesPolicyResultOk(Policy operPolicy, ControlLoopCompilerCallback callback) {
496         boolean isOk = true;
497         if (FinalResult.toResult(operPolicy.getFailure_retries()) != null 
498                         && !operPolicy.getFailure_retries().equals(FinalResult.FINAL_FAILURE_RETRIES.toString())) {
499             if (callback != null) {
500                 callback.onError("Policy failure retries is neither another policy nor FINAL_FAILURE_RETRIES");
501             }
502             isOk = false;
503         }
504         return isOk;
505     }
506     
507     private static boolean isFailureTimeoutPolicyResultOk(Policy operPolicy, ControlLoopCompilerCallback callback) {
508         boolean isOk = true;
509         if (FinalResult.toResult(operPolicy.getFailure_timeout()) != null 
510                         && !operPolicy.getFailure_timeout().equals(FinalResult.FINAL_FAILURE_TIMEOUT.toString())) {
511             if (callback != null) {
512                 callback.onError("Policy failure timeout is neither another policy nor FINAL_FAILURE_TIMEOUT");
513             }
514             isOk = false;
515         }
516         return isOk;
517     }
518     
519     private static boolean isFailureExceptionPolicyResultOk(Policy operPolicy, ControlLoopCompilerCallback callback) {
520         boolean isOk = true;
521         if (FinalResult.toResult(operPolicy.getFailure_exception()) != null 
522                         && !operPolicy.getFailure_exception().equals(FinalResult.FINAL_FAILURE_EXCEPTION.toString())) {
523             if (callback != null) {
524                 callback.onError("Policy failure exception is neither another policy nor FINAL_FAILURE_EXCEPTION");
525             }
526             isOk = false;
527         }
528         return isOk;
529     }
530     
531     private static boolean isFailureGuardPolicyResultOk(Policy operPolicy, ControlLoopCompilerCallback callback) {
532         boolean isOk = true;
533         if (FinalResult.toResult(operPolicy.getFailure_guard()) != null 
534                         && !operPolicy.getFailure_guard().equals(FinalResult.FINAL_FAILURE_GUARD.toString())) {
535             if (callback != null) {
536                 callback.onError("Policy failure guard is neither another policy nor FINAL_FAILURE_GUARD");
537             }
538             isOk = false;
539         }
540         return isOk;
541     }
542
543     private static PolicyNodeWrapper findPolicyNode(Map<Policy, PolicyNodeWrapper> mapNodes, String id) {
544         for (Entry<Policy, PolicyNodeWrapper> entry : mapNodes.entrySet()) {
545             if (entry.getKey().getId().equals(id)) {
546                 return entry.getValue();
547             }
548         }
549         return null;
550     }
551     
552     @FunctionalInterface
553     private interface NodeWrapper extends Serializable {
554         public String   getID();
555     }
556     
557     private static class TriggerNodeWrapper implements NodeWrapper {
558         private static final long serialVersionUID = -187644087811478349L;
559         private String closedLoopControlName;
560         
561         public TriggerNodeWrapper(String closedLoopControlName) {
562             this.closedLoopControlName = closedLoopControlName;
563         }
564
565         @Override
566         public String toString() {
567             return "TriggerNodeWrapper [closedLoopControlName=" + closedLoopControlName + "]";
568         }
569
570         @Override
571         public String getID() {
572             return closedLoopControlName;
573         }
574         
575     }
576         
577     private static class FinalResultNodeWrapper implements NodeWrapper {
578         private static final long serialVersionUID = 8540008796302474613L;
579         private FinalResult result;
580
581         public FinalResultNodeWrapper(FinalResult result) {
582             this.result = result;
583         }
584
585         @Override
586         public String toString() {
587             return "FinalResultNodeWrapper [result=" + result + "]";
588         }
589
590         @Override
591         public String getID() {
592             return result.toString();
593         }
594     }
595     
596     private static class PolicyNodeWrapper implements NodeWrapper {
597         private static final long serialVersionUID = 8170162175653823082L;
598         private transient Policy policy;
599         
600         public PolicyNodeWrapper(Policy operPolicy) {
601             this.policy = operPolicy;
602         }
603
604         @Override
605         public String toString() {
606             return "PolicyNodeWrapper [policy=" + policy + "]";
607         }
608
609         @Override
610         public String getID() {
611             return policy.getId();
612         }
613     }
614     
615     @FunctionalInterface
616     private interface EdgeWrapper extends Serializable {
617         public String getID();
618         
619     }
620     
621     private static class TriggerEdgeWrapper implements EdgeWrapper {
622         private static final long serialVersionUID = 2678151552623278863L;
623         private String trigger;
624         
625         public TriggerEdgeWrapper(String trigger) {
626             this.trigger = trigger;
627         }
628
629         @Override
630         public String getID() {
631             return trigger;
632         }
633
634         @Override
635         public String toString() {
636             return "TriggerEdgeWrapper [trigger=" + trigger + "]";
637         }
638         
639     }
640     
641     private static class PolicyResultEdgeWrapper implements EdgeWrapper {
642         private static final long serialVersionUID = 6078569477021558310L;
643         private PolicyResult policyResult;
644
645         public PolicyResultEdgeWrapper(PolicyResult policyResult) {
646             super();
647             this.policyResult = policyResult;
648         }
649
650         @Override
651         public String toString() {
652             return "PolicyResultEdgeWrapper [policyResult=" + policyResult + "]";
653         }
654
655         @Override
656         public String getID() {
657             return policyResult.toString();
658         }
659         
660         
661     }
662     
663     private static class FinalResultEdgeWrapper implements EdgeWrapper {
664         private static final long serialVersionUID = -1486381946896779840L;
665         private FinalResult finalResult;
666         
667         public FinalResultEdgeWrapper(FinalResult result) {
668             this.finalResult = result;
669         }
670
671         @Override
672         public String toString() {
673             return "FinalResultEdgeWrapper [finalResult=" + finalResult + "]";
674         }
675         
676         @Override
677         public String getID() {
678             return finalResult.toString();
679         }
680     }
681     
682     
683     private static class LabeledEdge extends DefaultEdge {
684         private static final long serialVersionUID = 579384429573385524L;
685         
686         private NodeWrapper from;
687         private NodeWrapper to;
688         private EdgeWrapper edge;
689         
690         public LabeledEdge(NodeWrapper from, NodeWrapper to, EdgeWrapper edge) {
691             this.from = from;
692             this.to = to;
693             this.edge = edge;
694         }
695         
696         @SuppressWarnings("unused")
697         public NodeWrapper from() {
698             return from;
699         }
700         
701         @SuppressWarnings("unused")
702         public NodeWrapper to() {
703             return to;
704         }
705         
706         @SuppressWarnings("unused")
707         public EdgeWrapper edge() {
708             return edge;
709         }
710
711         @Override
712         public String toString() {
713             return "LabeledEdge [from=" + from + ", to=" + to + ", edge=" + edge + "]";
714         }
715     }
716
717 }