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