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