0b17f196ff8edb9c1bee296e3f54a10e6fc02797
[policy/drools-applications.git] /
1 /*-
2  * ============LICENSE_START=======================================================
3  * ONAP
4  * ================================================================================
5  * Copyright (C) 2020 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.tdjam;
22
23 import static org.onap.policy.drools.properties.DroolsPropertyConstants.PROPERTY_CONTROLLER_TYPE;
24
25 import java.io.ByteArrayOutputStream;
26 import java.io.PrintStream;
27 import java.util.ArrayList;
28 import java.util.Collection;
29 import java.util.HashMap;
30 import java.util.HashSet;
31 import java.util.List;
32 import java.util.Map;
33 import java.util.Properties;
34 import java.util.UUID;
35 import java.util.concurrent.ConcurrentHashMap;
36 import org.onap.policy.common.endpoints.event.comm.Topic;
37 import org.onap.policy.common.endpoints.event.comm.TopicEndpointManager;
38 import org.onap.policy.common.endpoints.event.comm.TopicListener;
39 import org.onap.policy.common.endpoints.event.comm.TopicSource;
40 import org.onap.policy.controlloop.CanonicalOnset;
41 import org.onap.policy.controlloop.ControlLoopEvent;
42 import org.onap.policy.controlloop.ControlLoopException;
43 import org.onap.policy.controlloop.ControlLoopNotificationType;
44 import org.onap.policy.controlloop.ControlLoopResponse;
45 import org.onap.policy.controlloop.VirtualControlLoopEvent;
46 import org.onap.policy.controlloop.VirtualControlLoopNotification;
47 import org.onap.policy.controlloop.drl.legacy.ControlLoopParams;
48 import org.onap.policy.controlloop.eventmanager.ControlLoopEventManager2;
49 import org.onap.policy.controlloop.utils.ControlLoopUtils;
50 import org.onap.policy.drools.controller.DroolsController;
51 import org.onap.policy.drools.features.DroolsControllerFeatureApi;
52 import org.onap.policy.drools.features.PolicyControllerFeatureApi;
53 import org.onap.policy.drools.protocol.coders.EventProtocolCoder.CoderFilters;
54 import org.onap.policy.drools.protocol.coders.EventProtocolCoderConstants;
55 import org.onap.policy.drools.protocol.coders.ProtocolCoderToolset;
56 import org.onap.policy.drools.protocol.coders.TopicCoderFilterConfiguration;
57 import org.onap.policy.drools.system.PolicyController;
58 import org.onap.policy.drools.system.PolicyEngineConstants;
59 import org.onap.policy.extension.system.NonDroolsPolicyController;
60 import org.onap.policy.models.tosca.authorative.concepts.ToscaPolicy;
61 import org.slf4j.Logger;
62 import org.slf4j.LoggerFactory;
63
64 /**
65  * This replaces a Drools session with Java code. Although Drools memory
66  * is simulated when running the Junit tests, there is no actual use of
67  * Drools here.
68  */
69 public class TdjamController extends NonDroolsPolicyController {
70     private static Logger logger = LoggerFactory.getLogger(TdjamController.class);
71
72     // the 'controller.type' property is set to this value
73     private static final String TDJAM_CONTROLLER_BUILDER_TAG = "tdjam";
74
75     // additional data associated with session
76     private final String groupId;
77     private final String artifactId;
78
79     // top-level tosca policy table (first key = name, second key = version)
80     private final Map<String, Map<String, ToscaPolicy>> toscaPolicies = new HashMap<>();
81
82     // maps 'controlLoopControlName' to 'ControlLoopParams'
83     private final Map<String, ControlLoopParams> controlLoopParams = new HashMap<>();
84
85     // maps 'requestId' to 'ControlLoopEventManager'
86     private final Map<UUID, ControlLoopEventManager> eventManagers = new ConcurrentHashMap<>();
87
88     // maps onset to 'ControlLoopEventManager'
89     private final Map<VirtualControlLoopEvent, ControlLoopEventManager> onsetToEventManager = new ConcurrentHashMap<>();
90
91     // maps 'topic' to 'TopicData'
92     private final Map<String, TopicData> topicDataTable = new ConcurrentHashMap<>();
93
94     /* ============================================================ */
95
96     /**
97      * Initialize a new 'TdjamController'.
98      *
99      * @param name the controller name
100      * @param properties properties defining the controller
101      */
102     public TdjamController(String name, Properties properties) {
103         super(name, properties);
104
105         this.groupId = getGroupId();
106         this.artifactId = getArtifactId();
107
108         init();
109     }
110
111     private void init() {
112         // go through all of the incoming message decoders associated
113         // with this controller
114         for (ProtocolCoderToolset pct :
115                 EventProtocolCoderConstants.getManager()
116                 .getDecoders(groupId, artifactId)) {
117             // go through the 'CoderFilters' instances, and see if there are
118             // any that we are interested in
119             for (CoderFilters cf : pct.getCoders()) {
120                 try {
121                     Class<?> clazz = Class.forName(cf.getCodedClass());
122                     if (ControlLoopEvent.class.isAssignableFrom(clazz)) {
123                         // this one is of interest
124                         logger.debug("TdjamController using CoderFilters: {}", cf);
125                         getTopicData(pct.getTopic());
126                     }
127                 } catch (ClassNotFoundException e) {
128                     logger.error("CoderFilter refers to unknown class: {}",
129                                  cf.getCodedClass(), e);
130                 }
131             }
132         }
133
134         // start all 'TopicData' instances
135         for (TopicData topicData : topicDataTable.values()) {
136             topicData.start();
137         }
138     }
139
140     @Override
141     public <T> boolean offer(T object) {
142         if (object instanceof ToscaPolicy) {
143             addToscaPolicy((ToscaPolicy) object);
144             return true;
145         }
146         return false;
147     }
148
149     /**
150      * Add or replace a ToscaPolicy instance. The policy is keyed by name and
151      * version.
152      *
153      * @param toscaPolicy the ToscaPolicy being added
154      * @return if a ToscaPolicy with this name/version previously existed within
155      *     this TdjamController, it is returned; otherwise, 'null' is returned.
156      */
157     public synchronized ToscaPolicy addToscaPolicy(ToscaPolicy toscaPolicy) {
158         Map<String, ToscaPolicy> level2 =
159             toscaPolicies.computeIfAbsent(toscaPolicy.getName(),
160                 key -> new HashMap<String, ToscaPolicy>());
161         ToscaPolicy prev = level2.put(toscaPolicy.getVersion(), toscaPolicy);
162         if (prev != null) {
163             // update 'ControlLoopParams' entries
164             for (ControlLoopParams clp : controlLoopParams.values()) {
165                 if (clp.getToscaPolicy() == prev) {
166                     clp.setToscaPolicy(toscaPolicy);
167                 }
168             }
169         }
170         logger.debug("ToscaPolicy name={}, version={}, count={}, prev={}",
171             toscaPolicy.getName(), toscaPolicy.getVersion(), toscaPolicies.size(), (prev != null));
172         dumpTables();
173
174         // attempt to create a 'ControlLoopParams' instance from this object
175         ControlLoopParams params =
176             ControlLoopUtils.toControlLoopParams(toscaPolicy);
177         if (params != null) {
178             addControlLoopParams(params);
179         }
180         return prev;
181     }
182
183     /**
184      * Remove a ToscaPolicy instance associated with the specified name and
185      * version.
186      *
187      * @param name the name of the ToscaPolicy to remove
188      * @param version the version of the ToscaPolicy to remove
189      * @return the ToscaPolicy that was removed, or 'null' if not found
190      */
191     public synchronized ToscaPolicy removeToscaPolicy(String name, String version) {
192         ToscaPolicy prev = null;
193         Map<String, ToscaPolicy> level2 = toscaPolicies.get(name);
194
195         if (level2 != null && (prev = level2.remove(version)) != null) {
196             // remove all 'ControlLoopParams' entries referencing this policy
197             for (ControlLoopParams clp :
198                     new ArrayList<>(controlLoopParams.values())) {
199                 if (clp.getToscaPolicy() == prev) {
200                     controlLoopParams.remove(clp.getClosedLoopControlName());
201                 }
202             }
203         }
204         return prev;
205     }
206
207     /**
208      * Fetch a ToscaPolicy instance associated with the specified name and
209      * version.
210      *
211      * @param name the name of the ToscaPolicy
212      * @param version the version of the ToscaPolicy
213      * @return the ToscaPolicy, or 'null' if not found
214      */
215     public synchronized ToscaPolicy getToscaPolicy(String name, String version) {
216         Map<String, ToscaPolicy> level2 = toscaPolicies.get(name);
217         return (level2 == null ? null : level2.get(version));
218     }
219
220     /**
221      * Return a collection of all ToscaPolicy instances.
222      *
223      * @return all ToscaPolicy instances
224      */
225     public synchronized Collection<ToscaPolicy> getAllToscaPolicies() {
226         HashSet<ToscaPolicy> rval = new HashSet<>();
227         for (Map<String, ToscaPolicy> map : toscaPolicies.values()) {
228             rval.addAll(map.values());
229         }
230         return rval;
231     }
232
233     /**
234      * Add a new 'ControlLoopParams' instance -- they are keyed by
235      * 'closedLoopControlName'.
236      *
237      * @param clp the 'ControlLoopParams' instance to add
238      * @return the 'ControlLoopParams' instance previously associated with the
239      *     'closedLoopControlName' ('null' if it didn't exist)
240      */
241     public synchronized ControlLoopParams addControlLoopParams(ControlLoopParams clp) {
242         ToscaPolicy toscaPolicy =
243             getToscaPolicy(clp.getPolicyName(), clp.getPolicyVersion());
244         if (toscaPolicy == null) {
245             // there needs to be a 'ToscaPolicy' instance with a matching
246             // name/version
247             logger.debug("Missing ToscaPolicy, name={}, version={}",
248                          clp.getPolicyName(), clp.getPolicyVersion());
249             return clp;
250         }
251
252         clp.setToscaPolicy(toscaPolicy);
253         ControlLoopParams prev =
254             controlLoopParams.put(clp.getClosedLoopControlName(), clp);
255
256         logger.debug("ControlLoopParams name={}, version={}, closedLoopControlName={}, count={}, prev={}",
257                      clp.getPolicyName(), clp.getPolicyVersion(),
258                      clp.getClosedLoopControlName(), controlLoopParams.size(), (prev != null));
259         dumpTables();
260         return prev;
261     }
262
263     /**
264      * Return a collection of all ControlLoopParams instances.
265      *
266      * @return all ControlLoopParams instances
267      */
268     public synchronized Collection<ControlLoopParams> getAllControlLoopParams() {
269         return new ArrayList<>(controlLoopParams.values());
270     }
271
272     /**
273      * Return a collection of all EventManager  instances.
274      *
275      * @return all EventManager instances
276      *
277      */
278     public synchronized Collection<ControlLoopEventManager> getAllEventManagers() {
279         return new ArrayList<>(eventManagers.values());
280     }
281
282     /**
283      * Return a collection of all onsetToEventManager  instances.
284      *
285      * @return all onsetToEventManager instances
286      *
287      */
288     public synchronized Collection<ControlLoopEventManager> getAllOnsetToEventManager() {
289         return new ArrayList<>(onsetToEventManager.values());
290     }
291
292     /**
293      *  Reset the controller.
294      *
295      */
296     public synchronized void reset() {
297         toscaPolicies.clear();
298         controlLoopParams.clear();
299         eventManagers.clear();
300         onsetToEventManager.clear();
301     }
302
303     @Override
304     public boolean stop() {
305         super.stop();
306
307         // stop all 'TopicData' instances
308         for (TopicData topicData : topicDataTable.values()) {
309             topicData.stop();
310         }
311         return true;
312     }
313
314     /**
315      * Remove a ControlLoopParams instance associated with the specified
316      * 'closedLoopControlName'.
317      *
318      * @param closedLoopControlName the closedLoopControlName identifying the
319      *     ControlLoopParams instance
320      * @return the 'ControlLoopParams' instance, 'null' if not found
321      */
322     public synchronized ControlLoopParams removeControlLoopParams(String closedLoopControlName) {
323         return controlLoopParams.remove(closedLoopControlName);
324     }
325
326     /**
327      * Dump out the ToscaPolicy and ControlLoopParams tables in
328      * human-readable form.
329      */
330     private void dumpTables() {
331         ByteArrayOutputStream bos = new ByteArrayOutputStream();
332         PrintStream out = new PrintStream(bos, true);
333
334         // name(25) version(10) closedLoopControlName(...)
335
336         String format = "%-25s %-10s %s\n";
337         out.println("ToscaPolicy Table");
338         out.format(format, "Name", "Version", "");
339         out.format(format, "----", "-------", "");
340
341         for (Map<String, ToscaPolicy> level2 : toscaPolicies.values()) {
342             for (ToscaPolicy tp : level2.values()) {
343                 out.format(format, tp.getName(), tp.getVersion(), "");
344             }
345         }
346
347         out.println("\nControlLoopParams Table");
348         out.format(format, "Name", "Version", "ClosedLoopControlName");
349         out.format(format, "----", "-------", "---------------------");
350         for (ControlLoopParams cp : controlLoopParams.values()) {
351             out.format(format, cp.getPolicyName(), cp.getPolicyVersion(),
352                        cp.getClosedLoopControlName());
353         }
354
355         logger.debug(new String(bos.toByteArray()));
356     }
357
358     /**
359      * Find or create a 'TopicData' instance associated with the specified
360      * topic name.
361      *
362      * @param name the topic name
363      * @return the new or existing 'TopicData' instance associated with 'name'
364      */
365     private TopicData getTopicData(String name) {
366         return topicDataTable.computeIfAbsent(name, key -> new TopicData(name));
367     }
368
369     /* ============================================================ */
370
371     /**
372      * Process an incoming 'ControlLoopEvent'.
373      *
374      * @param event the incoming 'ControlLoopEvent'
375      */
376     private void processEvent(ControlLoopEvent event) {
377         String clName = event.getClosedLoopControlName();
378         ControlLoopParams params = controlLoopParams.get(clName);
379         if (params == null) {
380             logger.debug("No ControlLoopParams for event: {}", event);
381             return;
382         }
383
384         UUID requestId = event.getRequestId();
385         if (event instanceof CanonicalOnset) {
386             CanonicalOnset coEvent = (CanonicalOnset) event;
387
388             if (requestId == null) {
389                 // the requestId should not be 'null'
390                 handleNullRequestId(coEvent, params);
391                 return;
392             }
393
394             ControlLoopEventManager manager = onsetToEventManager.computeIfAbsent(coEvent, key -> {
395                 // a ControlLoopEventManager does not yet exist for this
396                 // 'event' -- create one, with the initial event
397                 try {
398                     ControlLoopEventManager mgr = new ControlLoopEventManager(params, coEvent);
399                     eventManagers.put(requestId, mgr);
400                     return mgr;
401                 } catch (ControlLoopException e) {
402                     logger.error("Exception creating ControlLoopEventManager", e);
403                     return null;
404                 }
405             });
406
407             if (manager != null && !manager.getSerialWorkQueue().isRunning()) {
408                 // new manager - start it by processing the initial event
409                 manager.getSerialWorkQueue().start();
410                 return;
411             }
412         }
413
414         if (event instanceof VirtualControlLoopEvent) {
415             ControlLoopEventManager manager = eventManagers.get(requestId);
416             if (manager != null) {
417                 manager.getSerialWorkQueue()
418                                 .queueAndRun(() -> manager.subsequentEvent((VirtualControlLoopEvent) event));
419                 return;
420             }
421         }
422
423         // this block of code originally appeared in the 'EVENT.CLEANUP'
424         // Drools rule
425         String ruleName = "EVENT.CLEANUP";
426
427         logger.info("{}: {}", clName, ruleName);
428         logger.debug("{}: {}: orphan event={}", clName, ruleName, event);
429     }
430
431     /**
432      * Generate and send a notification message in response to a 'CanonicalOnset'
433      * with a null 'requestId'.
434      *
435      * @param event the CanonicalOnset event
436      * @param params the associated ControlLoopParams
437      */
438     private void handleNullRequestId(CanonicalOnset event,
439                                      ControlLoopParams params) {
440         // this block of code originally appeared in the 'EVENT' Drools rule
441         String ruleName = "EVENT";
442         String clName = event.getClosedLoopControlName();
443
444         VirtualControlLoopNotification notification =
445             new VirtualControlLoopNotification(event);
446         notification.setNotification(ControlLoopNotificationType.REJECTED);
447         notification.setFrom("policy");
448         notification.setMessage("Missing requestId");
449         notification.setPolicyName(params.getPolicyName() + "." + ruleName);
450         notification.setPolicyScope(params.getPolicyScope());
451         notification.setPolicyVersion(params.getPolicyVersion());
452
453         //
454         // Generate notification
455         //
456         try {
457             PolicyEngineConstants.getManager().deliver("POLICY-CL-MGT", notification);
458
459         } catch (RuntimeException e) {
460             logger.warn("{}: {}.{}: event={} exception generating notification",
461                         clName, params.getPolicyName(), ruleName,
462                         event, e);
463         }
464     }
465
466     /* ============================================================ */
467
468     /**
469      * This nested class corresponds to a single topic name. At present, the
470      * only topics that are directly handled by this class are
471      * 'ControlLoopEvent', and subclasses (hence, the call to 'processEvent').
472      * If other event types later need to be directly handled, this may need to
473      * become an abstract class, with subclasses for the various event types.
474      */
475     private class TopicData implements TopicListener {
476         // topic name
477         private String name;
478
479         // set of 'TopicSource' instances associated with this topic
480         // (probably only one, but the underlying APIs support a list)
481         private List<TopicSource> topicSources = null;
482
483         /**
484          * Constructor -- initialize the 'TopicData' instance.
485          *
486          * @param name the topic name
487          */
488         private TopicData(String name) {
489             this.name = name;
490         }
491
492         /**
493          * Register all of the 'TopicSource' instances associated with this
494          * topic, and start the listeners.
495          */
496         private void start() {
497             if (topicSources == null) {
498                 // locate topic sources
499                 ArrayList<String> topics = new ArrayList<>();
500                 topics.add(name);
501                 topicSources = TopicEndpointManager.getManager().getTopicSources(topics);
502             }
503
504             for (TopicSource consumer : topicSources) {
505                 consumer.register(this);
506                 consumer.start();
507             }
508         }
509
510         /**
511          * Unregister all of the 'TopicSource' instances associated with this
512          * topic, and stop the listeners.
513          */
514         private void stop() {
515             if (topicSources != null) {
516                 for (TopicSource consumer : topicSources) {
517                     consumer.unregister(this);
518                     consumer.stop();
519                 }
520             }
521         }
522
523         /*===========================*/
524         /* 'TopicListener' interface */
525         /*===========================*/
526
527         @Override
528         public void onTopicEvent(Topic.CommInfrastructure commType, String topic, String event) {
529             logger.debug("TopicData.onTopicEvent: {}", event);
530             Object decodedObject =
531                 EventProtocolCoderConstants.getManager().decode(groupId, artifactId, topic, event);
532             if (decodedObject != null) {
533                 logger.debug("Decoded to object of {}", decodedObject.getClass());
534                 if (decodedObject instanceof ControlLoopEvent) {
535                     PolicyEngineConstants.getManager().getExecutorService().execute(() ->
536                         processEvent((ControlLoopEvent) decodedObject));
537                 }
538             }
539         }
540     }
541
542     /* ============================================================ */
543
544     /**
545      * This is a 'ControlLoopEventManager2' variant designed to run under
546      * 'TdjamController'.
547      */
548     private class ControlLoopEventManager extends ControlLoopEventManager2 {
549         private static final long serialVersionUID = 1L;
550
551         // used to serialize method calls from multiple threads, which avoids the
552         // need for additional synchronization
553         private final SerialWorkQueue serialWorkQueue;
554
555         private final ControlLoopParams params;
556
557         // onset event
558         private final CanonicalOnset event;
559
560         /**
561          * Constructor - initialize a ControlLoopEventManager.
562          *
563          * @param params the 'ControlLoopParam's instance associated with the
564          *     'closedLoopControlName'
565          * @param event the initial ControlLoopEvent
566          */
567         private ControlLoopEventManager(ControlLoopParams params, CanonicalOnset event)
568             throws ControlLoopException {
569
570             super(params, event);
571             this.params = params;
572             this.event = event;
573             this.serialWorkQueue = new SerialWorkQueue(this::initialEvent);
574         }
575
576         /**
577          * Return the SerialWorkQueue.
578          *
579          * @return the SerialWorkQueue
580          */
581         private SerialWorkQueue getSerialWorkQueue() {
582             return serialWorkQueue;
583         }
584
585         /**
586          * This is a notification from the base class that a state transition
587          * has occurred.
588          */
589         @Override
590         protected void notifyUpdate() {
591             update();
592         }
593
594         /**
595          * Process the initial event from DCAE that caused the
596          * 'ControlLoopEventManager' to be created.
597          */
598         private void initialEvent() {
599             // this block of code originally appeared in the 'EVENT' Drools rule
600             String ruleName = "EVENT";
601             UUID requestId = event.getRequestId();
602             String clName = event.getClosedLoopControlName();
603
604             VirtualControlLoopNotification notification;
605
606             try {
607                 //
608                 // Check the event, because we need it to not be null when
609                 // we create the ControlLoopEventManager. The ControlLoopEventManager
610                 // will do extra syntax checking as well as check if the closed loop is disabled.
611                 //
612                 try {
613                     start();
614                 } catch (Exception e) {
615                     eventManagers.remove(requestId, this);
616                     onsetToEventManager.remove(event, this);
617                     throw e;
618                 }
619                 notification = makeNotification();
620                 notification.setNotification(ControlLoopNotificationType.ACTIVE);
621                 notification.setPolicyName(params.getPolicyName() + "." + ruleName);
622             } catch (Exception e) {
623                 logger.warn("{}: {}.{}", clName, params.getPolicyName(), ruleName, e);
624                 notification = new VirtualControlLoopNotification(event);
625                 notification.setNotification(ControlLoopNotificationType.REJECTED);
626                 notification.setMessage("Exception occurred: " + e.getMessage());
627                 notification.setPolicyName(params.getPolicyName() + "." + ruleName);
628                 notification.setPolicyScope(params.getPolicyScope());
629                 notification.setPolicyVersion(params.getPolicyVersion());
630             }
631             //
632             // Generate notification
633             //
634             try {
635                 PolicyEngineConstants.getManager().deliver("POLICY-CL-MGT", notification);
636
637             } catch (RuntimeException e) {
638                 logger.warn("{}: {}.{}: event={} exception generating notification",
639                             clName, params.getPolicyName(), ruleName,
640                             event, e);
641             }
642         }
643
644         /**
645          * Process a subsequent event from DCAE.
646          *
647          * @param event the VirtualControlLoopEvent event
648          */
649         private void subsequentEvent(VirtualControlLoopEvent event) {
650             // this block of code originally appeared in the
651             // 'EVENT.MANAGER>NEW.EVENT' Drools rule
652             String ruleName = "EVENT.MANAGER.NEW.EVENT";
653
654             //
655             // Check what kind of event this is
656             //
657             switch (onNewEvent(event)) {
658                 case SYNTAX_ERROR:
659                     //
660                     // Ignore any bad syntax events
661                     //
662                     logger.warn("{}: {}.{}: syntax error",
663                                 getClosedLoopControlName(), getPolicyName(), ruleName);
664                     break;
665
666                 case FIRST_ABATEMENT:
667                 case SUBSEQUENT_ABATEMENT:
668                     //
669                     // TODO: handle the abatement.  Currently, it's just discarded.
670                     //
671                     break;
672
673                 case FIRST_ONSET:
674                 case SUBSEQUENT_ONSET:
675                 default:
676                     //
677                     // We don't care about subsequent onsets
678                     //
679                     logger.warn("{}: {}.{}: subsequent onset",
680                                 getClosedLoopControlName(), getPolicyName(), ruleName);
681                     break;
682             }
683         }
684
685         /**
686          * Called when a state transition occurs.
687          */
688         private void update() {
689             // handle synchronization by running it under the SerialWorkQueue
690             getSerialWorkQueue().queueAndRun(() -> {
691                 if (isActive()) {
692                     updateActive();
693                 } else {
694                     updateInactive();
695                 }
696             });
697         }
698
699         /**
700          * Called when a state transition occurs, and we are in the active state.
701          */
702         private void updateActive() {
703             if (!isUpdated()) {
704                 // no notification needed
705                 return;
706             }
707
708             // this block of code originally appeared in the
709             // 'EVENT.MANAGER.PROCESSING' Drools rule
710             String ruleName = "EVENT.MANAGER.PROCESSING";
711             VirtualControlLoopNotification notification =
712                 getNotification();
713
714             logger.info("{}: {}.{}: manager={}",
715                         getClosedLoopControlName(), getPolicyName(), ruleName,
716                         this);
717             //
718             // Generate notification
719             //
720             try {
721                 notification.setPolicyName(getPolicyName() + "." + ruleName);
722                 PolicyEngineConstants.getManager().deliver("POLICY-CL-MGT", notification);
723
724             } catch (RuntimeException e) {
725                 logger.warn("{}: {}.{}: manager={} exception generating notification",
726                             getClosedLoopControlName(), getPolicyName(), ruleName,
727                             this, e);
728             }
729             //
730             // Generate Response notification
731             //
732             try {
733                 ControlLoopResponse clResponse = getControlLoopResponse();
734                 if (clResponse != null) {
735                     PolicyEngineConstants.getManager().deliver("DCAE_CL_RSP", clResponse);
736                 }
737
738             } catch (RuntimeException e) {
739                 logger.warn("{}: {}.{}: manager={} exception generating Response notification",
740                             getClosedLoopControlName(), getPolicyName(), ruleName,
741                             this, e);
742             }
743             //
744             // Discard this message and wait for the next response.
745             //
746             nextStep();
747             update();
748         }
749
750         /**
751          * Called when a state transition has occurred, and we are not in the
752          * active state.
753          */
754         private void updateInactive() {
755             // this block of code originally appeared in the 'EVENT.MANAGER.FINAL'
756             // Drools rule
757             String ruleName = "EVENT.MANAGER.FINAL";
758             VirtualControlLoopNotification notification =
759                 getNotification();
760
761             logger.info("{}: {}.{}: manager={}",
762                         getClosedLoopControlName(), getPolicyName(), ruleName,
763                         this);
764             //
765             // Generate notification
766             //
767             try {
768                 notification.setPolicyName(getPolicyName() + "." + ruleName);
769                 PolicyEngineConstants.getManager().deliver("POLICY-CL-MGT", notification);
770             } catch (RuntimeException e) {
771                 logger.warn("{}: {}.{}: manager={} exception generating notification",
772                             getClosedLoopControlName(), getPolicyName(), ruleName,
773                             this, e);
774             }
775             //
776             // Destroy the manager
777             //
778             destroy();
779
780             // Remove the entry from the table
781             eventManagers.remove(getRequestId(), this);
782             onsetToEventManager.remove(event, this);
783         }
784     }
785
786     /* ============================================================ */
787
788     /**
789      * An instance of this class is called by 'IndexedPolicyControllerFactory'.
790      * It does the build operation when the value of the 'controller.type'
791      * property matches the value of TDJAM_CONTROLLER_BUILDER_TAG.
792      */
793     public static class PolicyBuilder implements PolicyControllerFeatureApi {
794         @Override
795         public int getSequenceNumber() {
796             return 1;
797         }
798
799         @Override
800         public PolicyController beforeInstance(String name, Properties properties) {
801             if (TDJAM_CONTROLLER_BUILDER_TAG.equals(properties.getProperty(PROPERTY_CONTROLLER_TYPE))) {
802                 return new TdjamController(name, properties);
803             }
804             return null;
805         }
806     }
807
808     /* ============================================================ */
809
810     /**
811      * An instance of this class is called by 'IndexedDroolsControllerFactory'.
812      * It does the build operation when the value of the 'controller.type'
813      * property matches the value of TDJAM_CONTROLLER_BUILDER_TAG.
814      */
815     public static class DroolsBuilder implements DroolsControllerFeatureApi {
816         @Override
817         public int getSequenceNumber() {
818             return 1;
819         }
820
821         @Override
822         public DroolsController beforeInstance(Properties properties,
823                                       String groupId, String artifactId, String version,
824                                       List<TopicCoderFilterConfiguration> decoderConfigurations,
825                                       List<TopicCoderFilterConfiguration> encoderConfigurations) throws LinkageError {
826
827             if (TDJAM_CONTROLLER_BUILDER_TAG.equals(properties.getProperty(PROPERTY_CONTROLLER_TYPE))) {
828                 return TdjamController.getBuildInProgress();
829             }
830             return null;
831         }
832     }
833 }