[POLICY-9,POLICY-18] warnings + Controller hooks
[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.openecomp.policy.common.logging.flexlogger.FlexLogger;
28 import org.openecomp.policy.common.logging.flexlogger.Logger;
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 = FlexLogger.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("BUILD-INIT-DROOLS: " + e.getMessage());
148                         e.printStackTrace();
149                         
150                         // throw back exception as input properties cause problems
151                         throw new IllegalArgumentException(e);
152                 }
153         }
154         
155         /**
156          * initialize sinks
157          * @throws IllegalArgumentException if invalid parameters are passed in
158          */
159         protected void initSinks() throws IllegalArgumentException {
160                 this.topic2Sinks.clear();
161                 for (TopicSink sink: sinks) {
162                         this.topic2Sinks.put(sink.getTopic(), sink);
163                 }
164         }
165         
166         /**
167          * {@inheritDoc}
168          */
169         @Override
170         public boolean updateDrools(DroolsConfiguration newDroolsConfiguration) {
171                 
172                 DroolsConfiguration oldDroolsConfiguration =
173                                 new DroolsConfiguration(this.droolsController.getArtifactId(),
174                                                                                 this.droolsController.getGroupId(), 
175                                                         this.droolsController.getVersion());
176                 
177                 if (oldDroolsConfiguration.getGroupId().equalsIgnoreCase(newDroolsConfiguration.getGroupId()) &&
178                         oldDroolsConfiguration.getArtifactId().equalsIgnoreCase(newDroolsConfiguration.getArtifactId()) &&
179                         oldDroolsConfiguration.getVersion().equalsIgnoreCase(newDroolsConfiguration.getVersion())) {
180                         logger.warn("UPDATE-DROOLS: nothing to do: identical configuration: " + oldDroolsConfiguration +
181                                             " <=> " + newDroolsConfiguration);
182                         return true;
183                 }
184                 
185                 try {
186                         /* Drools Controller created, update initialization properties for restarts */
187                         
188                         this.properties.setProperty(PolicyProperties.RULES_GROUPID, newDroolsConfiguration.getGroupId());
189                         this.properties.setProperty(PolicyProperties.RULES_ARTIFACTID, newDroolsConfiguration.getArtifactId());
190                         this.properties.setProperty(PolicyProperties.RULES_VERSION, newDroolsConfiguration.getVersion());
191                                         
192                         SystemPersistence.manager.storeController(name, this.properties);
193                         
194                         this.initDrools(this.properties);
195                         
196                         /* set drools controller to current locked status */
197                         
198                         if (this.isLocked())
199                                 this.droolsController.lock();
200                         else
201                                 this.droolsController.unlock();
202                         
203                         /* set drools controller to current alive status */
204                         
205                         if (this.isAlive())
206                                 this.droolsController.start();
207                         else
208                                 this.droolsController.stop();
209                         
210                 } catch (IllegalArgumentException e) {
211                         logger.warn("INIT-DROOLS: " + e.getMessage());
212                         e.printStackTrace();
213                         return false;
214                 }       
215                 
216                 return true;
217         }
218
219         /**
220          * {@inheritDoc}
221          */
222         @Override
223         public String getName() {
224                 return this.name;
225         }
226
227         /**
228          * {@inheritDoc}
229          */
230         @Override
231         public boolean start() throws IllegalStateException {
232                 
233                 if (logger.isInfoEnabled())
234                         logger.info("START: " +  this);
235                 
236                 for (PolicyControllerFeatureAPI feature : PolicyControllerFeatureAPI.providers.getList()) {
237                         try {
238                                 if (feature.beforeStart(this))
239                                         return true;
240                         } catch (Exception e) {
241                                 logger.warn("ERROR: Feature API: " + feature.getClass().getName() + e.getMessage(), e);
242                         }
243                 }
244                 
245                 if (this.isLocked())
246                         throw new IllegalStateException("Policy Controller " + name  + " is locked");
247
248                 synchronized(this) {
249                         if (this.alive)
250                                 return true;
251                         
252                         this.alive = true;
253                 }
254
255                 boolean success = this.droolsController.start();
256                 
257                 // register for events
258                 
259                 for (TopicSource source: sources) {
260                         source.register(this);
261                 }
262                 
263                 for (TopicSink sink: sinks) {
264                         try {
265                                 sink.start();
266                         } catch (Exception e) {
267                                 logger.warn("can't start sink: " + sink + " because of " + e.getMessage());
268                                 e.printStackTrace();
269                         }
270                 }
271                 
272                 for (PolicyControllerFeatureAPI feature : PolicyControllerFeatureAPI.providers.getList()) {
273                         try {
274                                 if (feature.afterStart(this))
275                                         return true;
276                         } catch (Exception e) {
277                                 logger.warn("ERROR: Feature API: " + feature.getClass().getName() + e.getMessage(), e);
278                         }
279                 }
280                 
281                 return success;
282         }
283
284         /**
285          * {@inheritDoc}
286          */
287         @Override
288         public boolean stop() {
289                 
290                 if (logger.isInfoEnabled())
291                         logger.info("STOP: " + this);
292                 
293                 for (PolicyControllerFeatureAPI feature : PolicyControllerFeatureAPI.providers.getList()) {
294                         try {
295                                 if (feature.beforeStop(this))
296                                         return true;
297                         } catch (Exception e) {
298                                 logger.warn("ERROR: Feature API: " + feature.getClass().getName() + e.getMessage(), e);
299                         }
300                 }
301                 
302                 /* stop regardless locked state */
303                 
304                 synchronized(this) {
305                         if (!this.alive)
306                                 return true;
307                         
308                         this.alive = false;
309                 }
310                 
311                 // 1. Stop registration
312                 
313                 for (TopicSource source: sources) {
314                         source.unregister(this);
315                 }
316                 
317                 boolean success = this.droolsController.stop();
318                 
319                 for (PolicyControllerFeatureAPI feature : PolicyControllerFeatureAPI.providers.getList()) {
320                         try {
321                                 if (feature.afterStop(this))
322                                         return true;
323                         } catch (Exception e) {
324                                 logger.warn("ERROR: Feature API: " + feature.getClass().getName() + e.getMessage(), e);
325                         }
326                 }
327                 
328                 return success;
329         }
330         
331         /**
332          * {@inheritDoc}
333          */
334         @Override
335         public void shutdown() throws IllegalStateException {
336                 if (logger.isInfoEnabled())
337                         logger.info("SHUTDOWN: " + this);
338                 
339                 for (PolicyControllerFeatureAPI feature : PolicyControllerFeatureAPI.providers.getList()) {
340                         try {
341                                 if (feature.beforeShutdown(this))
342                                         return;
343                         } catch (Exception e) {
344                                 logger.warn("ERROR: Feature API: " + feature.getClass().getName() + e.getMessage(), e);
345                         }
346                 }
347                 
348                 this.stop();
349                 
350                 DroolsController.factory.shutdown(this.droolsController);
351                 
352                 for (PolicyControllerFeatureAPI feature : PolicyControllerFeatureAPI.providers.getList()) {
353                         try {
354                                 if (feature.afterShutdown(this))
355                                         return;
356                         } catch (Exception e) {
357                                 logger.warn("ERROR: Feature API: " + feature.getClass().getName() + e.getMessage(), e);
358                         }
359                 }
360         }
361         
362         /**
363          * {@inheritDoc}
364          */
365         @Override
366         public void halt() throws IllegalStateException {
367                 if (logger.isInfoEnabled())
368                         logger.info("HALT: " + this);
369                 
370                 for (PolicyControllerFeatureAPI feature : PolicyControllerFeatureAPI.providers.getList()) {
371                         try {
372                                 if (feature.beforeHalt(this))
373                                         return;
374                         } catch (Exception e) {
375                                 logger.warn("ERROR: Feature API: " + feature.getClass().getName() + e.getMessage(), e);
376                         }
377                 }
378                 
379                 this.stop();    
380                 DroolsController.factory.destroy(this.droolsController);
381                 SystemPersistence.manager.deleteController(this.name);
382                 
383                 for (PolicyControllerFeatureAPI feature : PolicyControllerFeatureAPI.providers.getList()) {
384                         try {
385                                 if (feature.afterHalt(this))
386                                         return;
387                         } catch (Exception e) {
388                                 logger.warn("ERROR: Feature API: " + feature.getClass().getName() + e.getMessage(), e);
389                         }
390                 }
391         }
392         
393         /**
394          * {@inheritDoc}
395          */
396         @Override
397         public void onTopicEvent(Topic.CommInfrastructure commType, 
398                                             String topic, String event) {
399
400                 if (logger.isDebugEnabled())
401                         logger.debug("EVENT NOTIFICATION: " + commType + ":" + topic + ":" + event +  " INTO " + this);
402                 
403                 for (PolicyControllerFeatureAPI feature : PolicyControllerFeatureAPI.providers.getList()) {
404                         try {
405                                 if (feature.beforeOffer(this, commType, topic, event))
406                                         return;
407                         } catch (Exception e) {
408                                 logger.warn("ERROR: Feature API: " + feature.getClass().getName() + e.getMessage(), e);
409                         }
410                 }
411                 
412                 if (this.locked)
413                         return;
414                 
415                 if (!this.alive)
416                         return;
417                 
418                 boolean success = this.droolsController.offer(topic, event);
419                 
420                 for (PolicyControllerFeatureAPI feature : PolicyControllerFeatureAPI.providers.getList()) {
421                         try {
422                                 if (feature.afterOffer(this, commType, topic, event, success))
423                                         return;
424                         } catch (Exception e) {
425                                 logger.warn("ERROR: Feature API: " + 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: " + commType + ":" + topic + ":" + event +  " FROM " + this);
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.warn("ERROR: Feature API: " + feature.getClass().getName() + e.getMessage(), e);
448                         }
449                 }
450                 
451                 if (topic == null || topic.isEmpty())
452                         throw new IllegalArgumentException("Invalid Topic");
453                 
454                 if (event == null)
455                         throw new IllegalArgumentException("Invalid Event");
456                 
457                 if (!this.isAlive())
458                         throw new IllegalStateException("Policy Engine is stopped");
459                 
460                 if (this.isLocked())
461                         throw new IllegalStateException("Policy Engine is locked");
462                 
463                 if (!this.topic2Sinks.containsKey(topic)) {
464                         logger.error("UNDELIVERED: " + commType + ":" + topic + ":" + event +  " FROM " + this);
465                         throw new IllegalArgumentException
466                                         ("Unsuported topic " + topic + " for delivery");
467                 }
468                 
469                 boolean success = this.droolsController.deliver(this.topic2Sinks.get(topic), event);
470                 
471                 for (PolicyControllerFeatureAPI feature : PolicyControllerFeatureAPI.providers.getList()) {
472                         try {
473                                 if (feature.afterDeliver(this, commType, topic, event, success))
474                                         return success;
475                         } catch (Exception e) {
476                                 logger.warn("ERROR: Feature API: " + feature.getClass().getName() + e.getMessage(), e);
477                         }
478                 }
479                 
480                 return success;
481         }
482
483         /**
484          * {@inheritDoc}
485          */
486         @Override
487         public boolean isAlive() {
488                 return this.alive;
489         }
490
491         /**
492          * {@inheritDoc}
493          */
494         @Override
495         public boolean lock() {
496                 if (logger.isInfoEnabled())
497                         logger.info("LOCK: " + this);
498                 
499                 for (PolicyControllerFeatureAPI feature : PolicyControllerFeatureAPI.providers.getList()) {
500                         try {
501                                 if (feature.beforeLock(this))
502                                         return true;
503                         } catch (Exception e) {
504                                 logger.warn("ERROR: Feature API: " + feature.getClass().getName() + e.getMessage(), e);
505                         }
506                 }
507                 
508                 synchronized(this) {
509                         if (this.locked)
510                                 return true;
511                         
512                         this.locked = true;
513                 }
514                 
515                 // it does not affect associated sources/sinks, they are
516                 // autonomous entities
517                 
518                 boolean success = this.droolsController.lock();
519                 
520                 for (PolicyControllerFeatureAPI feature : PolicyControllerFeatureAPI.providers.getList()) {
521                         try {
522                                 if (feature.afterLock(this))
523                                         return true;
524                         } catch (Exception e) {
525                                 logger.warn("ERROR: Feature API: " + 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                 if (logger.isInfoEnabled())
539                         logger.info("UNLOCK: " + this);
540                 
541                 for (PolicyControllerFeatureAPI feature : PolicyControllerFeatureAPI.providers.getList()) {
542                         try {
543                                 if (feature.beforeUnlock(this))
544                                         return true;
545                         } catch (Exception e) {
546                                 logger.warn("ERROR: Feature API: " + 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.warn("ERROR: Feature API: " + feature.getClass().getName() + e.getMessage(), e);
565                         }
566                 }
567                 
568                 return success;
569         }
570
571         /**
572          * {@inheritDoc}
573          */
574         @Override
575         public boolean isLocked() {
576                 return this.locked;
577         }
578
579         /**
580          * {@inheritDoc}
581          */
582         @Override
583         public List<? extends TopicSource> getTopicSources() {
584                 return this.sources;
585         }
586
587         /**
588          * {@inheritDoc}
589          */
590         @Override
591         public List<? extends TopicSink> getTopicSinks() {
592                 return this.sinks;
593         }
594
595         /**
596          * {@inheritDoc}
597          */
598         @Override
599         public DroolsController getDrools() {
600                 return this.droolsController;
601         }
602         
603
604         /**
605          * {@inheritDoc}
606          */
607         @Override
608         @JsonIgnore
609         public Properties getProperties() {
610                 return this.properties;
611         }
612
613         @Override
614         public String toString() {
615                 StringBuilder builder = new StringBuilder();
616                 builder.append("AggregatedPolicyController [name=").append(name).append(", alive=").append(alive).append(", locked=").append(locked)
617                                 .append(", droolsController=").append(droolsController).append("]");
618                 return builder.toString();
619         }
620
621 }
622