581184eed584b08052c3d4dd45b7b28812a83db7
[policy/drools-pdp.git] /
1 /*
2  * ============LICENSE_START=======================================================
3  * policy-management
4  * ================================================================================
5  * Copyright (C) 2017-2018 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.drools.system.internal;
22
23 import com.fasterxml.jackson.annotation.JsonIgnore;
24
25 import java.util.HashMap;
26 import java.util.List;
27 import java.util.Properties;
28
29 import org.onap.policy.common.endpoints.event.comm.Topic;
30 import org.onap.policy.common.endpoints.event.comm.TopicEndpoint;
31 import org.onap.policy.common.endpoints.event.comm.TopicListener;
32 import org.onap.policy.common.endpoints.event.comm.TopicSink;
33 import org.onap.policy.common.endpoints.event.comm.TopicSource;
34 import org.onap.policy.drools.controller.DroolsController;
35 import org.onap.policy.drools.controller.DroolsControllerFactory;
36 import org.onap.policy.drools.features.PolicyControllerFeatureAPI;
37 import org.onap.policy.drools.persistence.SystemPersistence;
38 import org.onap.policy.drools.properties.DroolsProperties;
39 import org.onap.policy.drools.protocol.configuration.DroolsConfiguration;
40 import org.onap.policy.drools.system.PolicyController;
41 import org.slf4j.Logger;
42 import org.slf4j.LoggerFactory;
43
44 /**
45  * This implementation of the Policy Controller merely aggregates and tracks for management purposes
46  * all underlying resources that this controller depends upon.
47  */
48 public class AggregatedPolicyController implements PolicyController, TopicListener {
49
50     /**
51      * Logger.
52      */
53     private static final Logger logger = LoggerFactory.getLogger(AggregatedPolicyController.class);
54
55     /**
56      * Used to access various objects.  Can be overridden by junit tests.
57      */
58     private static Factory factory = new Factory();
59
60     /**
61      * identifier for this policy controller.
62      */
63     private final String name;
64
65     /**
66      * Abstracted Event Sources List regardless communication technology.
67      */
68     private final List<? extends TopicSource> sources;
69
70     /**
71      * Abstracted Event Sinks List regardless communication technology.
72      */
73     private final List<? extends TopicSink> sinks;
74
75     /**
76      * Mapping topics to sinks.
77      */
78     @JsonIgnore
79     private final HashMap<String, TopicSink> topic2Sinks = new HashMap<>();
80
81     /**
82      * Is this Policy Controller running (alive) ? reflects invocation of start()/stop() only.
83      */
84     private volatile boolean alive;
85
86     /**
87      * Is this Policy Controller locked ? reflects if i/o controller related operations and start
88      * are permitted, more specifically: start(), deliver() and onTopicEvent(). It does not affect
89      * the ability to stop the underlying drools infrastructure
90      */
91     private volatile boolean locked;
92
93     /**
94      * Policy Drools Controller.
95      */
96     private volatile DroolsController droolsController;
97
98     /**
99      * Properties used to initialize controller.
100      */
101     private final Properties properties;
102
103     /**
104      * Constructor version mainly used for bootstrapping at initialization time a policy engine
105      * controller.
106      * 
107      * @param name controller name
108      * @param properties
109      * 
110      * @throws IllegalArgumentException when invalid arguments are provided
111      */
112     public AggregatedPolicyController(String name, Properties properties) {
113
114         this.name = name;
115
116         /*
117          * 1. Register read topics with network infrastructure (ueb, dmaap, rest) 2. Register write
118          * topics with network infrastructure (ueb, dmaap, rest) 3. Register with drools
119          * infrastructure
120          */
121
122         // Create/Reuse Readers/Writers for all event sources endpoints
123
124         this.sources = factory.getEndpointManager().addTopicSources(properties);
125         this.sinks = factory.getEndpointManager().addTopicSinks(properties);
126
127         initDrools(properties);
128         initSinks();
129
130         /* persist new properties */
131         factory.getPersistenceManager().storeController(name, properties);
132         this.properties = properties;
133     }
134
135     /**
136      * initialize drools layer.
137      * 
138      * @throws IllegalArgumentException if invalid parameters are passed in
139      */
140     private void initDrools(Properties properties) {
141         try {
142             // Register with drools infrastructure
143             this.droolsController = factory.getDroolsFactory().build(properties, sources, sinks);
144         } catch (Exception | LinkageError e) {
145             logger.error("{}: cannot init-drools because of {}", this, e.getMessage(), e);
146             throw new IllegalArgumentException(e);
147         }
148     }
149
150     /**
151      * initialize sinks.
152      * 
153      * @throws IllegalArgumentException if invalid parameters are passed in
154      */
155     private void initSinks() {
156         this.topic2Sinks.clear();
157         for (TopicSink sink : sinks) {
158             this.topic2Sinks.put(sink.getTopic(), sink);
159         }
160     }
161
162     /**
163      * {@inheritDoc}.
164      */
165     @Override
166     public boolean updateDrools(DroolsConfiguration newDroolsConfiguration) {
167
168         DroolsConfiguration oldDroolsConfiguration = new DroolsConfiguration(this.droolsController.getArtifactId(),
169                 this.droolsController.getGroupId(), this.droolsController.getVersion());
170
171         if (oldDroolsConfiguration.getGroupId().equalsIgnoreCase(newDroolsConfiguration.getGroupId())
172                 && oldDroolsConfiguration.getArtifactId().equalsIgnoreCase(newDroolsConfiguration.getArtifactId())
173                 && oldDroolsConfiguration.getVersion().equalsIgnoreCase(newDroolsConfiguration.getVersion())) {
174             logger.warn("{}: cannot update-drools: identical configuration {} vs {}", this, oldDroolsConfiguration,
175                     newDroolsConfiguration);
176             return true;
177         }
178
179         try {
180             /* Drools Controller created, update initialization properties for restarts */
181
182             this.properties.setProperty(DroolsProperties.RULES_GROUPID, newDroolsConfiguration.getGroupId());
183             this.properties.setProperty(DroolsProperties.RULES_ARTIFACTID, newDroolsConfiguration.getArtifactId());
184             this.properties.setProperty(DroolsProperties.RULES_VERSION, newDroolsConfiguration.getVersion());
185
186             factory.getPersistenceManager().storeController(name, this.properties);
187
188             this.initDrools(this.properties);
189
190             /* set drools controller to current locked status */
191
192             if (this.isLocked()) {
193                 this.droolsController.lock();
194             } else {
195                 this.droolsController.unlock();
196             }
197
198             /* set drools controller to current alive status */
199
200             if (this.isAlive()) {
201                 this.droolsController.start();
202             } else {
203                 this.droolsController.stop();
204             }
205
206         } catch (IllegalArgumentException e) {
207             logger.error("{}: cannot update-drools because of {}", this, e.getMessage(), e);
208             return false;
209         }
210
211         return true;
212     }
213
214     /**
215      * {@inheritDoc}.
216      */
217     @Override
218     public String getName() {
219         return this.name;
220     }
221
222     /**
223      * {@inheritDoc}.
224      */
225     @Override
226     public boolean start() {
227         logger.info("{}: start", this);
228
229         for (PolicyControllerFeatureAPI feature : factory.getFeatureProviders()) {
230             try {
231                 if (feature.beforeStart(this)) {
232                     return true;
233                 }
234             } catch (Exception e) {
235                 logger.error("{}: feature {} before-start failure because of {}", this, feature.getClass().getName(),
236                         e.getMessage(), e);
237             }
238         }
239
240         if (this.isLocked()) {
241             throw new IllegalStateException("Policy Controller " + name + " is locked");
242         }
243
244         synchronized (this) {
245             if (this.alive) {
246                 return true;
247             }
248
249             this.alive = true;
250         }
251
252         final boolean success = this.droolsController.start();
253
254         // register for events
255
256         for (TopicSource source : sources) {
257             source.register(this);
258         }
259
260         for (TopicSink sink : sinks) {
261             try {
262                 sink.start();
263             } catch (Exception e) {
264                 logger.error("{}: cannot start {} because of {}", this, sink, e.getMessage(), e);
265             }
266         }
267
268         for (PolicyControllerFeatureAPI feature : factory.getFeatureProviders()) {
269             try {
270                 if (feature.afterStart(this)) {
271                     return true;
272                 }
273             } catch (Exception e) {
274                 logger.error("{}: feature {} after-start failure because of {}", this, feature.getClass().getName(),
275                         e.getMessage(), e);
276             }
277         }
278
279         return success;
280     }
281
282     /**
283      * {@inheritDoc}.
284      */
285     @Override
286     public boolean stop() {
287         logger.info("{}: stop", this);
288
289         for (PolicyControllerFeatureAPI feature : factory.getFeatureProviders()) {
290             try {
291                 if (feature.beforeStop(this)) {
292                     return true;
293                 }
294             } catch (Exception e) {
295                 logger.error("{}: feature {} before-stop failure because of {}", this, feature.getClass().getName(),
296                         e.getMessage(), e);
297             }
298         }
299
300         /* stop regardless locked state */
301
302         synchronized (this) {
303             if (!this.alive) {
304                 return true;
305             }
306
307             this.alive = false;
308         }
309
310         // 1. Stop registration
311
312         for (TopicSource source : sources) {
313             source.unregister(this);
314         }
315
316         boolean success = this.droolsController.stop();
317
318         for (PolicyControllerFeatureAPI feature : factory.getFeatureProviders()) {
319             try {
320                 if (feature.afterStop(this)) {
321                     return true;
322                 }
323             } catch (Exception e) {
324                 logger.error("{}: feature {} after-stop failure because of {}", this, feature.getClass().getName(),
325                         e.getMessage(), e);
326             }
327         }
328
329         return success;
330     }
331
332     /**
333      * {@inheritDoc}.
334      */
335     @Override
336     public void shutdown() {
337         logger.info("{}: shutdown", this);
338
339         for (PolicyControllerFeatureAPI feature : factory.getFeatureProviders()) {
340             try {
341                 if (feature.beforeShutdown(this)) {
342                     return;
343                 }
344             } catch (Exception e) {
345                 logger.error("{}: feature {} before-shutdown failure because of {}", this, feature.getClass().getName(),
346                         e.getMessage(), e);
347             }
348         }
349
350         this.stop();
351
352         factory.getDroolsFactory().shutdown(this.droolsController);
353
354         for (PolicyControllerFeatureAPI feature : factory.getFeatureProviders()) {
355             try {
356                 if (feature.afterShutdown(this)) {
357                     return;
358                 }
359             } catch (Exception e) {
360                 logger.error("{}: feature {} after-shutdown failure because of {}", this, feature.getClass().getName(),
361                         e.getMessage(), e);
362             }
363         }
364     }
365
366     /**
367      * {@inheritDoc}.
368      */
369     @Override
370     public void halt() {
371         logger.info("{}: halt", this);
372
373         for (PolicyControllerFeatureAPI feature : factory.getFeatureProviders()) {
374             try {
375                 if (feature.beforeHalt(this)) {
376                     return;
377                 }
378             } catch (Exception e) {
379                 logger.error("{}: feature {} before-halt failure because of {}", this, feature.getClass().getName(),
380                         e.getMessage(), e);
381             }
382         }
383
384         this.stop();
385         factory.getDroolsFactory().destroy(this.droolsController);
386         factory.getPersistenceManager().deleteController(this.name);
387
388         for (PolicyControllerFeatureAPI feature : factory.getFeatureProviders()) {
389             try {
390                 if (feature.afterHalt(this)) {
391                     return;
392                 }
393             } catch (Exception e) {
394                 logger.error("{}: feature {} after-halt failure because of {}", this, feature.getClass().getName(),
395                         e.getMessage(), e);
396             }
397         }
398     }
399
400     /**
401      * {@inheritDoc}.
402      */
403     @Override
404     public void onTopicEvent(Topic.CommInfrastructure commType, String topic, String event) {
405
406         logger.debug("{}: event offered from {}:{}: {}", this, commType, topic, event);
407
408         for (PolicyControllerFeatureAPI feature : factory.getFeatureProviders()) {
409             try {
410                 if (feature.beforeOffer(this, commType, topic, event)) {
411                     return;
412                 }
413             } catch (Exception e) {
414                 logger.error("{}: feature {} before-offer failure because of {}", this, feature.getClass().getName(),
415                         e.getMessage(), e);
416             }
417         }
418
419         if (this.locked) {
420             return;
421         }
422
423         if (!this.alive) {
424             return;
425         }
426
427         boolean success = this.droolsController.offer(topic, event);
428
429         for (PolicyControllerFeatureAPI feature : factory.getFeatureProviders()) {
430             try {
431                 if (feature.afterOffer(this, commType, topic, event, success)) {
432                     return;
433                 }
434             } catch (Exception e) {
435                 logger.error("{}: feature {} after-offer failure because of {}", this, feature.getClass().getName(),
436                         e.getMessage(), e);
437             }
438         }
439     }
440
441     /**
442      * {@inheritDoc}.
443      */
444     @Override
445     public boolean deliver(Topic.CommInfrastructure commType, String topic, Object event) {
446
447         logger.debug("{}: deliver event to {}:{}: {}", this, commType, topic, event);
448
449         for (PolicyControllerFeatureAPI feature : factory.getFeatureProviders()) {
450             try {
451                 if (feature.beforeDeliver(this, commType, topic, event)) {
452                     return true;
453                 }
454             } catch (Exception e) {
455                 logger.error("{}: feature {} before-deliver failure because of {}", this, feature.getClass().getName(),
456                         e.getMessage(), e);
457             }
458         }
459
460         if (topic == null || topic.isEmpty()) {
461             throw new IllegalArgumentException("Invalid Topic");
462         }
463
464         if (event == null) {
465             throw new IllegalArgumentException("Invalid Event");
466         }
467
468         if (!this.isAlive()) {
469             throw new IllegalStateException("Policy Engine is stopped");
470         }
471
472         if (this.isLocked()) {
473             throw new IllegalStateException("Policy Engine is locked");
474         }
475
476         if (!this.topic2Sinks.containsKey(topic)) {
477             logger.warn("{}: cannot deliver event because the sink {}:{} is not registered: {}", this, commType, topic,
478                     event);
479             throw new IllegalArgumentException("Unsupported topic " + topic + " for delivery");
480         }
481
482         boolean success = this.droolsController.deliver(this.topic2Sinks.get(topic), event);
483
484         for (PolicyControllerFeatureAPI feature : factory.getFeatureProviders()) {
485             try {
486                 if (feature.afterDeliver(this, commType, topic, event, success)) {
487                     return success;
488                 }
489             } catch (Exception e) {
490                 logger.error("{}: feature {} after-deliver failure because of {}", this, feature.getClass().getName(),
491                         e.getMessage(), e);
492             }
493         }
494
495         return success;
496     }
497
498     /**
499      * {@inheritDoc}.
500      */
501     @Override
502     public boolean isAlive() {
503         return this.alive;
504     }
505
506     /**
507      * {@inheritDoc}.
508      */
509     @Override
510     public boolean lock() {
511         logger.info("{}: lock", this);
512
513         for (PolicyControllerFeatureAPI feature : factory.getFeatureProviders()) {
514             try {
515                 if (feature.beforeLock(this)) {
516                     return true;
517                 }
518             } catch (Exception e) {
519                 logger.error("{}: feature {} before-lock failure because of {}", this, feature.getClass().getName(),
520                         e.getMessage(), e);
521             }
522         }
523
524         synchronized (this) {
525             if (this.locked) {
526                 return true;
527             }
528
529             this.locked = true;
530         }
531
532         // it does not affect associated sources/sinks, they are
533         // autonomous entities
534
535         boolean success = this.droolsController.lock();
536
537         for (PolicyControllerFeatureAPI feature : factory.getFeatureProviders()) {
538             try {
539                 if (feature.afterLock(this)) {
540                     return true;
541                 }
542             } catch (Exception e) {
543                 logger.error("{}: feature {} after-lock failure because of {}", this, feature.getClass().getName(),
544                         e.getMessage(), e);
545             }
546         }
547
548         return success;
549     }
550
551     /**
552      * {@inheritDoc}.
553      */
554     @Override
555     public boolean unlock() {
556
557         logger.info("{}: unlock", this);
558
559         for (PolicyControllerFeatureAPI feature : factory.getFeatureProviders()) {
560             try {
561                 if (feature.beforeUnlock(this)) {
562                     return true;
563                 }
564             } catch (Exception e) {
565                 logger.error("{}: feature {} before-unlock failure because of {}", this, feature.getClass().getName(),
566                         e.getMessage(), e);
567             }
568         }
569
570         synchronized (this) {
571             if (!this.locked) {
572                 return true;
573             }
574
575             this.locked = false;
576         }
577
578         boolean success = this.droolsController.unlock();
579
580         for (PolicyControllerFeatureAPI feature : factory.getFeatureProviders()) {
581             try {
582                 if (feature.afterUnlock(this)) {
583                     return true;
584                 }
585             } catch (Exception e) {
586                 logger.error("{}: feature {} after-unlock failure because of {}", this, feature.getClass().getName(),
587                         e.getMessage(), e);
588             }
589         }
590
591         return success;
592     }
593
594     /**
595      * {@inheritDoc}.
596      */
597     @Override
598     public boolean isLocked() {
599         return this.locked;
600     }
601
602     /**
603      * {@inheritDoc}.
604      */
605     @Override
606     public List<? extends TopicSource> getTopicSources() {
607         return this.sources;
608     }
609
610     /**
611      * {@inheritDoc}.
612      */
613     @Override
614     public List<? extends TopicSink> getTopicSinks() {
615         return this.sinks;
616     }
617
618     /**
619      * {@inheritDoc}.
620      */
621     @Override
622     public DroolsController getDrools() {
623         return this.droolsController;
624     }
625
626
627     /**
628      * {@inheritDoc}.
629      */
630     @Override
631     @JsonIgnore
632     public Properties getProperties() {
633         return this.properties;
634     }
635
636     @Override
637     public String toString() {
638         return "AggregatedPolicyController [name=" + name + ", alive=" + alive
639                 + ", locked=" + locked + ", droolsController=" + droolsController + "]";
640     }
641
642     /**
643      * Factory to access various objects.  Can be overridden by junit tests.
644      */
645     public static class Factory {
646         
647         public SystemPersistence getPersistenceManager() {
648             return SystemPersistence.manager;
649         }
650
651         public TopicEndpoint getEndpointManager() {
652             return TopicEndpoint.manager;
653         }
654
655         public DroolsControllerFactory getDroolsFactory() {
656             return DroolsController.factory;
657         }
658
659         public List<PolicyControllerFeatureAPI> getFeatureProviders() {
660             return PolicyControllerFeatureAPI.providers.getList();
661         }
662     }
663 }
664