e41a88987970360d9b6f1a6836df1ccaf356f87a
[policy/drools-pdp.git] /
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.persistence.SystemPersistence;
36 import org.openecomp.policy.drools.properties.PolicyProperties;
37 import org.openecomp.policy.drools.protocol.configuration.DroolsConfiguration;
38 import org.openecomp.policy.drools.system.PolicyController;
39
40 import com.fasterxml.jackson.annotation.JsonIgnore;
41
42 /**
43  * This implementation of the Policy Controller merely aggregates and tracks for
44  * management purposes all underlying resources that this controller depends upon.
45  */
46 public class AggregatedPolicyController implements PolicyController, 
47                                                    TopicListener {
48         
49         /**
50          * Logger
51          */
52         private static Logger  logger = FlexLogger.getLogger(AggregatedPolicyController.class); 
53         
54         /**
55          * identifier for this policy controller
56          */
57         protected final String name;
58         
59         /**
60          * Abstracted Event Sources List regardless communication
61          * technology
62          */
63         protected final List<? extends TopicSource> sources;
64         
65         /**
66          * Abstracted Event Sinks List regardless communication
67          * technology
68          */
69         protected final List<? extends TopicSink> sinks;
70         
71         /**
72          * Mapping topics to sinks
73          */
74         @JsonIgnore
75         protected final HashMap<String, TopicSink> topic2Sinks =
76                 new HashMap<String, TopicSink>();
77         
78         /**
79          * Is this Policy Controller running (alive) ?
80          * reflects invocation of start()/stop() only
81          */
82         protected volatile boolean alive;
83         
84         /**
85          * Is this Policy Controller locked ?
86          * reflects if i/o controller related operations and start 
87          * are permitted,
88          * more specifically: start(), deliver() and onTopicEvent().
89          * It does not affect the ability to stop the
90          * underlying drools infrastructure 
91          */
92         protected volatile boolean locked;
93         
94         /**
95          * Policy Drools Controller
96          */
97         protected volatile DroolsController droolsController;
98         
99         /**
100          * Properties used to initialize controller
101          */
102         protected final Properties properties;
103         
104         /**
105          * Constructor version mainly used for bootstrapping at initialization time
106          * a policy engine controller
107          * 
108          * @param name controller name
109          * @param properties
110          * 
111          * @throws IllegalArgumentException when invalid arguments are provided
112          */
113         public AggregatedPolicyController(String name, Properties properties) 
114                         throws IllegalArgumentException {
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) throws IllegalArgumentException {
142                 try {
143                         // Register with drools infrastructure
144                         this.droolsController = DroolsController.factory.build(properties, sources, sinks);
145                 } catch (Exception | LinkageError e) {
146                         logger.error("BUILD-INIT-DROOLS: " + e.getMessage());
147                         e.printStackTrace();
148                         
149                         // throw back exception as input properties cause problems
150                         throw new IllegalArgumentException(e);
151                 }
152         }
153         
154         /**
155          * initialize sinks
156          * @throws IllegalArgumentException if invalid parameters are passed in
157          */
158         protected void initSinks() throws IllegalArgumentException {
159                 this.topic2Sinks.clear();
160                 for (TopicSink sink: sinks) {
161                         this.topic2Sinks.put(sink.getTopic(), sink);
162                 }
163         }
164         
165         /**
166          * {@inheritDoc}
167          */
168         @Override
169         public boolean updateDrools(DroolsConfiguration newDroolsConfiguration) {
170                 
171                 DroolsConfiguration oldDroolsConfiguration =
172                                 new DroolsConfiguration(this.droolsController.getArtifactId(),
173                                                                                 this.droolsController.getGroupId(), 
174                                                         this.droolsController.getVersion());
175                 
176                 if (oldDroolsConfiguration.getGroupId().equalsIgnoreCase(newDroolsConfiguration.getGroupId()) &&
177                         oldDroolsConfiguration.getArtifactId().equalsIgnoreCase(newDroolsConfiguration.getArtifactId()) &&
178                         oldDroolsConfiguration.getVersion().equalsIgnoreCase(newDroolsConfiguration.getVersion())) {
179                         logger.warn("UPDATE-DROOLS: nothing to do: identical configuration: " + oldDroolsConfiguration +
180                                             " <=> " + newDroolsConfiguration);
181                         return true;
182                 }
183                 
184                 try {
185                         /* Drools Controller created, update initialization properties for restarts */
186                         
187                         this.properties.setProperty(PolicyProperties.RULES_GROUPID, newDroolsConfiguration.getGroupId());
188                         this.properties.setProperty(PolicyProperties.RULES_ARTIFACTID, newDroolsConfiguration.getArtifactId());
189                         this.properties.setProperty(PolicyProperties.RULES_VERSION, newDroolsConfiguration.getVersion());
190                                         
191                         SystemPersistence.manager.storeController(name, this.properties);
192                         
193                         this.initDrools(this.properties);
194                         
195                         /* set drools controller to current locked status */
196                         
197                         if (this.isLocked())
198                                 this.droolsController.lock();
199                         else
200                                 this.droolsController.unlock();
201                         
202                         /* set drools controller to current alive status */
203                         
204                         if (this.isAlive())
205                                 this.droolsController.start();
206                         else
207                                 this.droolsController.stop();
208                         
209                 } catch (IllegalArgumentException e) {
210                         logger.warn("INIT-DROOLS: " + e.getMessage());
211                         e.printStackTrace();
212                         return false;
213                 }       
214                 
215                 return true;
216         }
217
218         /**
219          * {@inheritDoc}
220          */
221         @Override
222         public String getName() {
223                 return this.name;
224         }
225
226         /**
227          * {@inheritDoc}
228          */
229         @Override
230         public boolean start() throws IllegalStateException {
231                 if (logger.isInfoEnabled())
232                         logger.info("START: " + this);
233                 
234                 if (this.isLocked())
235                         throw new IllegalStateException("Policy Controller " + name  + " is locked");
236
237                 synchronized(this) {
238                         if (this.alive)
239                                 return true;
240                         
241                         this.alive = true;
242                 }
243
244                 boolean success = this.droolsController.start();
245                 
246                 // register for events
247                 
248                 for (TopicSource source: sources) {
249                         source.register(this);
250                 }
251                 
252                 for (TopicSink sink: sinks) {
253                         try {
254                                 sink.start();
255                         } catch (Exception e) {
256                                 logger.warn("can't start sink: " + sink + " because of " + e.getMessage());
257                                 e.printStackTrace();
258                         }
259                 }
260                 
261                 return success;
262         }
263
264         /**
265          * {@inheritDoc}
266          */
267         @Override
268         public boolean stop() {
269                 
270                 logger.info("STOP: " + this);
271                 
272                 /* stop regardless locked state */
273                 
274                 synchronized(this) {
275                         if (!this.alive)
276                                 return true;
277                         
278                         this.alive = false;
279                 }
280                 
281                 // 1. Stop registration
282                 
283                 for (TopicSource source: sources) {
284                         source.unregister(this);
285                 }
286                 
287                 boolean success = this.droolsController.stop();
288                 return success;
289         }
290         
291         /**
292          * {@inheritDoc}
293          */
294         @Override
295         public void shutdown() throws IllegalStateException {
296                 if (logger.isInfoEnabled())
297                         logger.info(this  + "SHUTDOWN");
298                 
299                 this.stop();
300                 
301                 DroolsController.factory.shutdown(this.droolsController);
302         }
303         
304         /**
305          * {@inheritDoc}
306          */
307         @Override
308         public void halt() throws IllegalStateException {
309                 if (logger.isInfoEnabled())
310                         logger.info(this + "HALT");
311                 
312                 this.stop();    
313                 DroolsController.factory.destroy(this.droolsController);
314                 SystemPersistence.manager.deleteController(this.name);
315         }
316         
317         /**
318          * {@inheritDoc}
319          */
320         @Override
321         public void onTopicEvent(Topic.CommInfrastructure commType, 
322                                             String topic, String event) {
323
324                 logger.info("EVENT NOTIFICATION: " + commType + ":" + topic + ":" + event +  " INTO " + this);
325                 
326                 if (this.locked)
327                         return;
328                 
329                 if (!this.alive)
330                         return;
331                 
332                 this.droolsController.offer(topic, event);
333         }
334         
335         /**
336          * {@inheritDoc}
337          */
338         @Override
339         public boolean deliver(Topic.CommInfrastructure commType, 
340                                        String topic, Object event)
341                 throws IllegalArgumentException, IllegalStateException,
342                UnsupportedOperationException {  
343                 
344                 logger.info("DELIVER: " + commType + ":" + topic + ":" + event +  " FROM " + this);
345                 
346                 if (topic == null || topic.isEmpty())
347                         throw new IllegalArgumentException("Invalid Topic");
348                 
349                 if (event == null)
350                         throw new IllegalArgumentException("Invalid Event");
351                 
352                 if (!this.isAlive())
353                         throw new IllegalStateException("Policy Engine is stopped");
354                 
355                 if (this.isLocked())
356                         throw new IllegalStateException("Policy Engine is locked");
357                 
358                 if (!this.topic2Sinks.containsKey(topic)) {
359                         logger.error("UNDELIVERED: " + commType + ":" + topic + ":" + event +  " FROM " + this);
360                         throw new IllegalArgumentException
361                                         ("Unsuported topic " + topic + " for delivery");
362                 }
363                 
364                 return this.droolsController.deliver
365                                 (this.topic2Sinks.get(topic), event);
366         }
367
368         /**
369          * {@inheritDoc}
370          */
371         @Override
372         public boolean isAlive() {
373                 return this.alive;
374         }
375
376         /**
377          * {@inheritDoc}
378          */
379         @Override
380         public boolean lock() {
381                 logger.info("LOCK: " + this);
382                 
383                 synchronized(this) {
384                         if (this.locked)
385                                 return true;
386                         
387                         this.locked = true;
388                 }
389                 
390                 // it does not affect associated sources/sinks, they are
391                 // autonomous entities
392                 
393                 return this.droolsController.lock();
394         }
395
396         /**
397          * {@inheritDoc}
398          */
399         @Override
400         public boolean unlock() {
401                 logger.info("UNLOCK: " + this);
402                 
403                 synchronized(this) {
404                         if (!this.locked)
405                                 return true;
406                         
407                         this.locked = false;
408                 }
409                 
410                 return this.droolsController.unlock();
411         }
412
413         /**
414          * {@inheritDoc}
415          */
416         @Override
417         public boolean isLocked() {
418                 return this.locked;
419         }
420
421         /**
422          * {@inheritDoc}
423          */
424         @Override
425         public List<? extends TopicSource> getTopicSources() {
426                 return this.sources;
427         }
428
429         /**
430          * {@inheritDoc}
431          */
432         @Override
433         public List<? extends TopicSink> getTopicSinks() {
434                 return this.sinks;
435         }
436
437         /**
438          * {@inheritDoc}
439          */
440         @Override
441         public DroolsController getDrools() {
442                 return this.droolsController;
443         }
444         
445
446         /**
447          * {@inheritDoc}
448          */
449         @Override
450         @JsonIgnore
451         public Properties getProperties() {
452                 return this.properties;
453         }
454
455         @Override
456         public String toString() {
457                 StringBuilder builder = new StringBuilder();
458                 builder.append("AggregatedPolicyController [name=").append(name).append(", alive=").append(alive).append(", locked=").append(locked)
459                                 .append(", droolsController=").append(droolsController).append("]");
460                 return builder.toString();
461         }
462
463 }
464