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