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