[POLICY-52] pdp-d: PolicyEngine junits
[policy/drools-pdp.git] / policy-management / src / main / java / org / openecomp / policy / drools / system / internal / AggregatedPolicyController.java
1 /*-
2  * ============LICENSE_START=======================================================
3  * policy-management
4  * ================================================================================
5  * Copyright (C) 2017 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.openecomp.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.openecomp.policy.drools.controller.DroolsController;
30 import org.openecomp.policy.drools.event.comm.Topic;
31 import org.openecomp.policy.drools.event.comm.TopicEndpoint;
32 import org.openecomp.policy.drools.event.comm.TopicListener;
33 import org.openecomp.policy.drools.event.comm.TopicSink;
34 import org.openecomp.policy.drools.event.comm.TopicSource;
35 import org.openecomp.policy.drools.features.PolicyControllerFeatureAPI;
36 import org.openecomp.policy.drools.persistence.SystemPersistence;
37 import org.openecomp.policy.drools.properties.PolicyProperties;
38 import org.openecomp.policy.drools.protocol.configuration.DroolsConfiguration;
39 import org.openecomp.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<String, TopicSink>();
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                         throws IllegalArgumentException {
116                 
117                 this.name = name;
118                 
119                 /*
120                  * 1. Register read topics with network infrastructure (ueb, dmaap, rest)
121                  * 2. Register write topics with network infrastructure (ueb, dmaap, rest)
122                  * 3. Register with drools infrastructure
123                  */
124                 
125                 // Create/Reuse Readers/Writers for all event sources endpoints
126                 
127                 this.sources = TopicEndpoint.manager.addTopicSources(properties);
128                 this.sinks = TopicEndpoint.manager.addTopicSinks(properties);
129                 
130                 initDrools(properties);         
131                 initSinks();            
132                 
133                 /* persist new properties */
134                 SystemPersistence.manager.storeController(name, properties);    
135                 this.properties = properties;
136         }
137         
138         /**
139          * initialize drools layer
140          * @throws IllegalArgumentException if invalid parameters are passed in
141          */
142         protected void initDrools(Properties properties) throws IllegalArgumentException {
143                 try {
144                         // Register with drools infrastructure
145                         this.droolsController = DroolsController.factory.build(properties, sources, sinks);
146                 } catch (Exception | LinkageError e) {
147                         logger.error("{}: cannot init-drools because of {}", this, e.getMessage(), e);
148                         throw new IllegalArgumentException(e);
149                 }
150         }
151         
152         /**
153          * initialize sinks
154          * @throws IllegalArgumentException if invalid parameters are passed in
155          */
156         protected void initSinks() throws IllegalArgumentException {
157                 this.topic2Sinks.clear();
158                 for (TopicSink sink: sinks) {
159                         this.topic2Sinks.put(sink.getTopic(), sink);
160                 }
161         }
162         
163         /**
164          * {@inheritDoc}
165          */
166         @Override
167         public boolean updateDrools(DroolsConfiguration newDroolsConfiguration) {
168                 
169                 DroolsConfiguration oldDroolsConfiguration =
170                                 new DroolsConfiguration(this.droolsController.getArtifactId(),
171                                                                                 this.droolsController.getGroupId(), 
172                                                         this.droolsController.getVersion());
173                 
174                 if (oldDroolsConfiguration.getGroupId().equalsIgnoreCase(newDroolsConfiguration.getGroupId()) &&
175                         oldDroolsConfiguration.getArtifactId().equalsIgnoreCase(newDroolsConfiguration.getArtifactId()) &&
176                         oldDroolsConfiguration.getVersion().equalsIgnoreCase(newDroolsConfiguration.getVersion())) {
177                         logger.warn("{}: cannot update-drools: identical configuration {} vs {}", 
178                                             this, oldDroolsConfiguration, newDroolsConfiguration);
179                         return true;
180                 }
181                 
182                 try {
183                         /* Drools Controller created, update initialization properties for restarts */
184                         
185                         this.properties.setProperty(PolicyProperties.RULES_GROUPID, newDroolsConfiguration.getGroupId());
186                         this.properties.setProperty(PolicyProperties.RULES_ARTIFACTID, newDroolsConfiguration.getArtifactId());
187                         this.properties.setProperty(PolicyProperties.RULES_VERSION, newDroolsConfiguration.getVersion());
188                                         
189                         SystemPersistence.manager.storeController(name, this.properties);
190                         
191                         this.initDrools(this.properties);
192                         
193                         /* set drools controller to current locked status */
194                         
195                         if (this.isLocked())
196                                 this.droolsController.lock();
197                         else
198                                 this.droolsController.unlock();
199                         
200                         /* set drools controller to current alive status */
201                         
202                         if (this.isAlive())
203                                 this.droolsController.start();
204                         else
205                                 this.droolsController.stop();
206                         
207                 } catch (IllegalArgumentException e) {
208                         logger.error("{}: cannot update-drools because of {}", this, e.getMessage(), e);
209                         return false;
210                 }       
211                 
212                 return true;
213         }
214
215         /**
216          * {@inheritDoc}
217          */
218         @Override
219         public String getName() {
220                 return this.name;
221         }
222
223         /**
224          * {@inheritDoc}
225          */
226         @Override
227         public boolean start() throws IllegalStateException {           
228                 logger.info("{}: start", this);
229                 
230                 for (PolicyControllerFeatureAPI feature : PolicyControllerFeatureAPI.providers.getList()) {
231                         try {
232                                 if (feature.beforeStart(this))
233                                         return true;
234                         } catch (Exception e) {
235                                 logger.error("{}: feature {} before-start failure because of {}",  
236                                          this, feature.getClass().getName(), e.getMessage(), e);
237                         }
238                 }
239                 
240                 if (this.isLocked())
241                         throw new IllegalStateException("Policy Controller " + name  + " is locked");
242
243                 synchronized(this) {
244                         if (this.alive)
245                                 return true;
246                         
247                         this.alive = true;
248                 }
249
250                 boolean success = this.droolsController.start();
251                 
252                 // register for events
253                 
254                 for (TopicSource source: sources) {
255                         source.register(this);
256                 }
257                 
258                 for (TopicSink sink: sinks) {
259                         try {
260                                 sink.start();
261                         } catch (Exception e) {
262                                 logger.error("{}: cannot start {} because of {}",  
263                                      this, sink, e.getMessage(), e);
264                         }
265                 }
266                 
267                 for (PolicyControllerFeatureAPI feature : PolicyControllerFeatureAPI.providers.getList()) {
268                         try {
269                                 if (feature.afterStart(this))
270                                         return true;
271                         } catch (Exception e) {
272                                 logger.error("{}: feature {} after-start failure because of {}",  
273                                      this, feature.getClass().getName(), e.getMessage(), e);
274                         }
275                 }
276                 
277                 return success;
278         }
279
280         /**
281          * {@inheritDoc}
282          */
283         @Override
284         public boolean stop() {
285                 logger.info("{}: stop", this);
286                 
287                 for (PolicyControllerFeatureAPI feature : PolicyControllerFeatureAPI.providers.getList()) {
288                         try {
289                                 if (feature.beforeStop(this))
290                                         return true;
291                         } catch (Exception e) {
292                                 logger.error("{}: feature {} before-stop failure because of {}",  
293                                      this, feature.getClass().getName(), e.getMessage(), e);
294                         }
295                 }
296                 
297                 /* stop regardless locked state */
298                 
299                 synchronized(this) {
300                         if (!this.alive)
301                                 return true;
302                         
303                         this.alive = false;
304                 }
305                 
306                 // 1. Stop registration
307                 
308                 for (TopicSource source: sources) {
309                         source.unregister(this);
310                 }
311                 
312                 boolean success = this.droolsController.stop();
313                 
314                 for (PolicyControllerFeatureAPI feature : PolicyControllerFeatureAPI.providers.getList()) {
315                         try {
316                                 if (feature.afterStop(this))
317                                         return true;
318                         } catch (Exception e) {
319                                 logger.error("{}: feature {} after-stop failure because of {}",  
320                                  this, feature.getClass().getName(), e.getMessage(), e);
321                         }
322                 }
323                 
324                 return success;
325         }
326         
327         /**
328          * {@inheritDoc}
329          */
330         @Override
331         public void shutdown() throws IllegalStateException {
332                 logger.info("{}: shutdown", this);
333                 
334                 for (PolicyControllerFeatureAPI feature : PolicyControllerFeatureAPI.providers.getList()) {
335                         try {
336                                 if (feature.beforeShutdown(this))
337                                         return;
338                         } catch (Exception e) {
339                                 logger.error("{}: feature {} before-shutdown failure because of {}",  
340                                  this, feature.getClass().getName(), 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                         } catch (Exception e) {
353                                 logger.error("{}: feature {} after-shutdown failure because of {}",  
354                              this, feature.getClass().getName(), e.getMessage(), e);
355                         }
356                 }
357         }
358         
359         /**
360          * {@inheritDoc}
361          */
362         @Override
363         public void halt() throws IllegalStateException {
364                 logger.info("{}: halt", this);
365                 
366                 for (PolicyControllerFeatureAPI feature : PolicyControllerFeatureAPI.providers.getList()) {
367                         try {
368                                 if (feature.beforeHalt(this))
369                                         return;
370                         } catch (Exception e) {
371                                 logger.error("{}: feature {} before-halt failure because of {}",  
372                              this, feature.getClass().getName(), e.getMessage(), e);
373                         }
374                 }
375                 
376                 this.stop();    
377                 DroolsController.factory.destroy(this.droolsController);
378                 SystemPersistence.manager.deleteController(this.name);
379                 
380                 for (PolicyControllerFeatureAPI feature : PolicyControllerFeatureAPI.providers.getList()) {
381                         try {
382                                 if (feature.afterHalt(this))
383                                         return;
384                         } catch (Exception e) {
385                                 logger.error("{}: feature {} after-halt failure because of {}",  
386                              this, feature.getClass().getName(), e.getMessage(), e);
387                         }
388                 }
389         }
390         
391         /**
392          * {@inheritDoc}
393          */
394         @Override
395         public void onTopicEvent(Topic.CommInfrastructure commType, 
396                                             String topic, String event) {
397
398                 if (logger.isDebugEnabled())
399                         logger.debug("{}: event offered from {}:{}: {}", this, commType, topic, event);
400                 
401                 for (PolicyControllerFeatureAPI feature : PolicyControllerFeatureAPI.providers.getList()) {
402                         try {
403                                 if (feature.beforeOffer(this, commType, topic, event))
404                                         return;
405                         } catch (Exception e) {
406                                 logger.error("{}: feature {} before-offer failure because of {}",  
407                              this, feature.getClass().getName(), e.getMessage(), e);
408                         }
409                 }
410                 
411                 if (this.locked)
412                         return;
413                 
414                 if (!this.alive)
415                         return;
416                 
417                 boolean success = this.droolsController.offer(topic, event);
418                 
419                 for (PolicyControllerFeatureAPI feature : PolicyControllerFeatureAPI.providers.getList()) {
420                         try {
421                                 if (feature.afterOffer(this, commType, topic, event, success))
422                                         return;
423                         } catch (Exception e) {
424                                 logger.error("{}: feature {} after-offer failure because of {}",  
425                              this, feature.getClass().getName(), e.getMessage(), e);
426                         }
427                 }
428         }
429         
430         /**
431          * {@inheritDoc}
432          */
433         @Override
434         public boolean deliver(Topic.CommInfrastructure commType, 
435                                        String topic, Object event)
436                 throws IllegalArgumentException, IllegalStateException,
437                UnsupportedOperationException {  
438                 
439                 if (logger.isDebugEnabled())
440                         logger.debug("{}: deliver event to {}:{}: {}", this, commType, topic, event);
441                 
442                 for (PolicyControllerFeatureAPI feature : PolicyControllerFeatureAPI.providers.getList()) {
443                         try {
444                                 if (feature.beforeDeliver(this, commType, topic, event))
445                                         return true;
446                         } catch (Exception e) {
447                                 logger.error("{}: feature {} before-deliver failure because of {}",  
448                              this, feature.getClass().getName(), e.getMessage(), e);
449                         }
450                 }
451                 
452                 if (topic == null || topic.isEmpty())
453                         throw new IllegalArgumentException("Invalid Topic");
454                 
455                 if (event == null)
456                         throw new IllegalArgumentException("Invalid Event");
457                 
458                 if (!this.isAlive())
459                         throw new IllegalStateException("Policy Engine is stopped");
460                 
461                 if (this.isLocked())
462                         throw new IllegalStateException("Policy Engine is locked");
463                 
464                 if (!this.topic2Sinks.containsKey(topic)) {
465                         logger.warn("{}: cannot deliver event because the sink {}:{} is not registered: {}", 
466                                              this, commType, topic, event);
467                         throw new IllegalArgumentException("Unsuported topic " + topic + " for delivery");
468                 }
469                 
470                 boolean success = this.droolsController.deliver(this.topic2Sinks.get(topic), event);
471                 
472                 for (PolicyControllerFeatureAPI feature : PolicyControllerFeatureAPI.providers.getList()) {
473                         try {
474                                 if (feature.afterDeliver(this, commType, topic, event, success))
475                                         return success;
476                         } catch (Exception e) {
477                                 logger.error("{}: feature {} after-deliver failure because of {}",  
478                              this, feature.getClass().getName(), e.getMessage(), e);
479                         }
480                 }
481                 
482                 return success;
483         }
484
485         /**
486          * {@inheritDoc}
487          */
488         @Override
489         public boolean isAlive() {
490                 return this.alive;
491         }
492
493         /**
494          * {@inheritDoc}
495          */
496         @Override
497         public boolean lock() {
498                 logger.info("{}: lock", this);
499                 
500                 for (PolicyControllerFeatureAPI feature : PolicyControllerFeatureAPI.providers.getList()) {
501                         try {
502                                 if (feature.beforeLock(this))
503                                         return true;
504                         } catch (Exception e) {
505                                 logger.error("{}: feature {} before-lock failure because of {}",  
506                              this, feature.getClass().getName(), e.getMessage(), e);
507                         }
508                 }
509                 
510                 synchronized(this) {
511                         if (this.locked)
512                                 return true;
513                         
514                         this.locked = true;
515                 }
516                 
517                 // it does not affect associated sources/sinks, they are
518                 // autonomous entities
519                 
520                 boolean success = this.droolsController.lock();
521                 
522                 for (PolicyControllerFeatureAPI feature : PolicyControllerFeatureAPI.providers.getList()) {
523                         try {
524                                 if (feature.afterLock(this))
525                                         return true;
526                         } catch (Exception e) {
527                                 logger.error("{}: feature {} after-lock failure because of {}",  
528                              this, feature.getClass().getName(), e.getMessage(), e);
529                         }
530                 }
531                 
532                 return success;
533         }
534
535         /**
536          * {@inheritDoc}
537          */
538         @Override
539         public boolean unlock() {
540                 
541                 logger.info("{}: unlock", this);
542                 
543                 for (PolicyControllerFeatureAPI feature : PolicyControllerFeatureAPI.providers.getList()) {
544                         try {
545                                 if (feature.beforeUnlock(this))
546                                         return true;
547                         } catch (Exception e) {
548                                 logger.error("{}: feature {} before-unlock failure because of {}",  
549                              this, feature.getClass().getName(), e.getMessage(), e);
550                         }
551                 }
552                 
553                 synchronized(this) {
554                         if (!this.locked)
555                                 return true;
556                         
557                         this.locked = false;
558                 }
559                 
560                 boolean success = this.droolsController.unlock();
561                 
562                 for (PolicyControllerFeatureAPI feature : PolicyControllerFeatureAPI.providers.getList()) {
563                         try {
564                                 if (feature.afterUnlock(this))
565                                         return true;
566                         } catch (Exception e) {
567                                 logger.error("{}: feature {} after-unlock failure because of {}",  
568                              this, feature.getClass().getName(), e.getMessage(), e);
569                         }
570                 }
571                 
572                 return success;
573         }
574
575         /**
576          * {@inheritDoc}
577          */
578         @Override
579         public boolean isLocked() {
580                 return this.locked;
581         }
582
583         /**
584          * {@inheritDoc}
585          */
586         @Override
587         public List<? extends TopicSource> getTopicSources() {
588                 return this.sources;
589         }
590
591         /**
592          * {@inheritDoc}
593          */
594         @Override
595         public List<? extends TopicSink> getTopicSinks() {
596                 return this.sinks;
597         }
598
599         /**
600          * {@inheritDoc}
601          */
602         @Override
603         public DroolsController getDrools() {
604                 return this.droolsController;
605         }
606         
607
608         /**
609          * {@inheritDoc}
610          */
611         @Override
612         @JsonIgnore
613         public Properties getProperties() {
614                 return this.properties;
615         }
616
617         @Override
618         public String toString() {
619                 StringBuilder builder = new StringBuilder();
620                 builder.append("AggregatedPolicyController [name=").append(name).append(", alive=").append(alive).append(", locked=").append(locked)
621                                 .append(", droolsController=").append(droolsController).append("]");
622                 return builder.toString();
623         }
624
625 }
626