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