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