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