2 * ============LICENSE_START=======================================================
4 * ================================================================================
5 * Copyright (C) 2017-2019 AT&T Intellectual Property. All rights reserved.
6 * ================================================================================
7 * Licensed under the Apache License, Version 2.0 (the "License");
8 * you may not use this file except in compliance with the License.
9 * You may obtain a copy of the License at
11 * http://www.apache.org/licenses/LICENSE-2.0
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=========================================================
21 package org.onap.policy.drools.system.internal;
23 import com.fasterxml.jackson.annotation.JsonIgnore;
24 import java.util.ArrayList;
25 import java.util.Arrays;
26 import java.util.HashMap;
27 import java.util.List;
28 import java.util.Properties;
29 import java.util.stream.Collectors;
30 import org.onap.policy.common.endpoints.event.comm.Topic;
31 import org.onap.policy.common.endpoints.event.comm.TopicEndpoint;
32 import org.onap.policy.common.endpoints.event.comm.TopicEndpointManager;
33 import org.onap.policy.common.endpoints.event.comm.TopicListener;
34 import org.onap.policy.common.endpoints.event.comm.TopicSink;
35 import org.onap.policy.common.endpoints.event.comm.TopicSource;
36 import org.onap.policy.common.gson.annotation.GsonJsonIgnore;
37 import org.onap.policy.drools.controller.DroolsController;
38 import org.onap.policy.drools.controller.DroolsControllerFactory;
39 import org.onap.policy.drools.features.PolicyControllerFeatureAPI;
40 import org.onap.policy.drools.persistence.SystemPersistence;
41 import org.onap.policy.drools.properties.DroolsProperties;
42 import org.onap.policy.drools.protocol.configuration.DroolsConfiguration;
43 import org.onap.policy.drools.system.PolicyController;
44 import org.onap.policy.models.tosca.authorative.concepts.ToscaPolicyTypeIdentifier;
45 import org.slf4j.Logger;
46 import org.slf4j.LoggerFactory;
49 * This implementation of the Policy Controller merely aggregates and tracks for management purposes
50 * all underlying resources that this controller depends upon.
52 public class AggregatedPolicyController implements PolicyController, TopicListener {
57 private static final Logger logger = LoggerFactory.getLogger(AggregatedPolicyController.class);
60 * identifier for this policy controller.
62 private final String name;
65 * Abstracted Event Sources List regardless communication technology.
67 private final List<? extends TopicSource> sources;
70 * Abstracted Event Sinks List regardless communication technology.
72 private final List<? extends TopicSink> sinks;
75 * Mapping topics to sinks.
79 private final HashMap<String, TopicSink> topic2Sinks = new HashMap<>();
82 * Is this Policy Controller running (alive) ? reflects invocation of start()/stop() only.
84 private volatile boolean alive;
87 * Is this Policy Controller locked ? reflects if i/o controller related operations and start
88 * are permitted, more specifically: start(), deliver() and onTopicEvent(). It does not affect
89 * the ability to stop the underlying drools infrastructure
91 private volatile boolean locked;
94 * Policy Drools Controller.
96 private volatile DroolsController droolsController;
99 * Properties used to initialize controller.
101 private final Properties properties;
106 private List<ToscaPolicyTypeIdentifier> policyTypes;
109 * Constructor version mainly used for bootstrapping at initialization time a policy engine
112 * @param name controller name
115 * @throws IllegalArgumentException when invalid arguments are provided
117 public AggregatedPolicyController(String name, Properties properties) {
122 * 1. Register read topics with network infrastructure (ueb, dmaap, rest) 2. Register write
123 * topics with network infrastructure (ueb, dmaap, rest) 3. Register with drools
127 // Create/Reuse Readers/Writers for all event sources endpoints
129 this.sources = getEndpointManager().addTopicSources(properties);
130 this.sinks = getEndpointManager().addTopicSinks(properties);
132 initDrools(properties);
135 /* persist new properties */
136 getPersistenceManager().storeController(name, properties);
137 this.properties = properties;
139 this.policyTypes = getPolicyTypesFromProperties();
143 public List<ToscaPolicyTypeIdentifier> getPolicyTypes() {
144 if (!policyTypes.isEmpty()) {
148 return droolsController
149 .getBaseDomainNames()
151 .map(d -> new ToscaPolicyTypeIdentifier(d, DroolsProperties.DEFAULT_CONTROLLER_POLICY_TYPE_VERSION))
152 .collect(Collectors.toList());
155 protected List<ToscaPolicyTypeIdentifier> getPolicyTypesFromProperties() {
156 List<ToscaPolicyTypeIdentifier> policyTypeIds = new ArrayList<>();
158 String ptiPropValue = properties.getProperty(DroolsProperties.PROPERTY_CONTROLLER_POLICY_TYPES);
159 if (ptiPropValue == null) {
160 return policyTypeIds;
163 List<String> ptiPropList = new ArrayList<>(Arrays.asList(ptiPropValue.split("\\s*,\\s*")));
164 for (String pti : ptiPropList) {
165 String[] ptv = pti.split(":");
166 if (ptv.length == 1) {
167 policyTypeIds.add(new ToscaPolicyTypeIdentifier(ptv[0],
168 DroolsProperties.DEFAULT_CONTROLLER_POLICY_TYPE_VERSION));
169 } else if (ptv.length == 2) {
170 policyTypeIds.add(new ToscaPolicyTypeIdentifier(ptv[0], ptv[1]));
174 return policyTypeIds;
178 * initialize drools layer.
180 * @throws IllegalArgumentException if invalid parameters are passed in
182 private void initDrools(Properties properties) {
184 // Register with drools infrastructure
185 this.droolsController = getDroolsFactory().build(properties, sources, sinks);
186 } catch (Exception | LinkageError e) {
187 logger.error("{}: cannot init-drools because of {}", this, e.getMessage(), e);
188 throw new IllegalArgumentException(e);
195 * @throws IllegalArgumentException if invalid parameters are passed in
197 private void initSinks() {
198 this.topic2Sinks.clear();
199 for (TopicSink sink : sinks) {
200 this.topic2Sinks.put(sink.getTopic(), sink);
208 public boolean updateDrools(DroolsConfiguration newDroolsConfiguration) {
210 DroolsConfiguration oldDroolsConfiguration = new DroolsConfiguration(this.droolsController.getArtifactId(),
211 this.droolsController.getGroupId(), this.droolsController.getVersion());
213 if (oldDroolsConfiguration.getGroupId().equalsIgnoreCase(newDroolsConfiguration.getGroupId())
214 && oldDroolsConfiguration.getArtifactId().equalsIgnoreCase(newDroolsConfiguration.getArtifactId())
215 && oldDroolsConfiguration.getVersion().equalsIgnoreCase(newDroolsConfiguration.getVersion())) {
216 logger.warn("{}: cannot update-drools: identical configuration {} vs {}", this, oldDroolsConfiguration,
217 newDroolsConfiguration);
222 /* Drools Controller created, update initialization properties for restarts */
224 this.properties.setProperty(DroolsProperties.RULES_GROUPID, newDroolsConfiguration.getGroupId());
225 this.properties.setProperty(DroolsProperties.RULES_ARTIFACTID, newDroolsConfiguration.getArtifactId());
226 this.properties.setProperty(DroolsProperties.RULES_VERSION, newDroolsConfiguration.getVersion());
228 getPersistenceManager().storeController(name, this.properties);
230 this.initDrools(this.properties);
232 /* set drools controller to current locked status */
234 if (this.isLocked()) {
235 this.droolsController.lock();
237 this.droolsController.unlock();
240 /* set drools controller to current alive status */
242 if (this.isAlive()) {
243 this.droolsController.start();
245 this.droolsController.stop();
248 } catch (IllegalArgumentException e) {
249 logger.error("{}: cannot update-drools because of {}", this, e.getMessage(), e);
260 public String getName() {
268 public boolean start() {
269 logger.info("{}: start", this);
271 for (PolicyControllerFeatureAPI feature : getProviders()) {
273 if (feature.beforeStart(this)) {
276 } catch (Exception e) {
277 logger.error("{}: feature {} before-start failure because of {}", this, feature.getClass().getName(),
282 if (this.isLocked()) {
283 throw new IllegalStateException("Policy Controller " + name + " is locked");
286 synchronized (this) {
294 final boolean success = this.droolsController.start();
296 // register for events
298 for (TopicSource source : sources) {
299 source.register(this);
302 for (TopicSink sink : sinks) {
305 } catch (Exception e) {
306 logger.error("{}: cannot start {} because of {}", this, sink, e.getMessage(), e);
310 for (PolicyControllerFeatureAPI feature : getProviders()) {
312 if (feature.afterStart(this)) {
315 } catch (Exception e) {
316 logger.error("{}: feature {} after-start failure because of {}", this, feature.getClass().getName(),
328 public boolean stop() {
329 logger.info("{}: stop", this);
331 for (PolicyControllerFeatureAPI feature : getProviders()) {
333 if (feature.beforeStop(this)) {
336 } catch (Exception e) {
337 logger.error("{}: feature {} before-stop failure because of {}", this, feature.getClass().getName(),
342 /* stop regardless locked state */
344 synchronized (this) {
352 // 1. Stop registration
354 for (TopicSource source : sources) {
355 source.unregister(this);
358 boolean success = this.droolsController.stop();
360 for (PolicyControllerFeatureAPI feature : getProviders()) {
362 if (feature.afterStop(this)) {
365 } catch (Exception e) {
366 logger.error("{}: feature {} after-stop failure because of {}", this, feature.getClass().getName(),
378 public void shutdown() {
379 logger.info("{}: shutdown", this);
381 for (PolicyControllerFeatureAPI feature : getProviders()) {
383 if (feature.beforeShutdown(this)) {
386 } catch (Exception e) {
387 logger.error("{}: feature {} before-shutdown failure because of {}", this, feature.getClass().getName(),
394 getDroolsFactory().shutdown(this.droolsController);
396 for (PolicyControllerFeatureAPI feature : getProviders()) {
398 if (feature.afterShutdown(this)) {
401 } catch (Exception e) {
402 logger.error("{}: feature {} after-shutdown failure because of {}", this, feature.getClass().getName(),
413 logger.info("{}: halt", this);
415 for (PolicyControllerFeatureAPI feature : getProviders()) {
417 if (feature.beforeHalt(this)) {
420 } catch (Exception e) {
421 logger.error("{}: feature {} before-halt failure because of {}", this, feature.getClass().getName(),
427 getDroolsFactory().destroy(this.droolsController);
428 getPersistenceManager().deleteController(this.name);
430 for (PolicyControllerFeatureAPI feature : getProviders()) {
432 if (feature.afterHalt(this)) {
435 } catch (Exception e) {
436 logger.error("{}: feature {} after-halt failure because of {}", this, feature.getClass().getName(),
446 public void onTopicEvent(Topic.CommInfrastructure commType, String topic, String event) {
447 logger.debug("{}: raw event offered from {}:{}: {}", this, commType, topic, event);
453 for (PolicyControllerFeatureAPI feature : getProviders()) {
455 if (feature.beforeOffer(this, commType, topic, event)) {
458 } catch (Exception e) {
459 logger.error("{}: feature {} before-offer failure because of {}", this, feature.getClass().getName(),
464 boolean success = this.droolsController.offer(topic, event);
466 for (PolicyControllerFeatureAPI feature : getProviders()) {
468 if (feature.afterOffer(this, commType, topic, event, success)) {
471 } catch (Exception e) {
472 logger.error("{}: feature {} after-offer failure because of {}", this, feature.getClass().getName(),
479 public <T> boolean offer(T event) {
480 logger.debug("{}: event offered: {}", this, event);
486 for (PolicyControllerFeatureAPI feature : getProviders()) {
488 if (feature.beforeOffer(this, event)) {
491 } catch (Exception e) {
492 logger.error("{}: feature {} before-offer failure because of {}", this, feature.getClass().getName(),
497 boolean success = this.droolsController.offer(event);
499 for (PolicyControllerFeatureAPI feature : getProviders()) {
501 if (feature.afterOffer(this, event, success)) {
504 } catch (Exception e) {
505 logger.error("{}: feature {} after-offer failure because of {}", this, feature.getClass().getName(),
513 private boolean skipOffer() {
514 return isLocked() || !isAlive();
521 public boolean deliver(Topic.CommInfrastructure commType, String topic, Object event) {
523 logger.debug("{}: deliver event to {}:{}: {}", this, commType, topic, event);
525 for (PolicyControllerFeatureAPI feature : getProviders()) {
527 if (feature.beforeDeliver(this, commType, topic, event)) {
530 } catch (Exception e) {
531 logger.error("{}: feature {} before-deliver failure because of {}", this, feature.getClass().getName(),
536 if (topic == null || topic.isEmpty()) {
537 throw new IllegalArgumentException("Invalid Topic");
541 throw new IllegalArgumentException("Invalid Event");
544 if (!this.isAlive()) {
545 throw new IllegalStateException("Policy Engine is stopped");
548 if (this.isLocked()) {
549 throw new IllegalStateException("Policy Engine is locked");
552 if (!this.topic2Sinks.containsKey(topic)) {
553 logger.warn("{}: cannot deliver event because the sink {}:{} is not registered: {}", this, commType, topic,
555 throw new IllegalArgumentException("Unsupported topic " + topic + " for delivery");
558 boolean success = this.droolsController.deliver(this.topic2Sinks.get(topic), event);
560 for (PolicyControllerFeatureAPI feature : getProviders()) {
562 if (feature.afterDeliver(this, commType, topic, event, success)) {
565 } catch (Exception e) {
566 logger.error("{}: feature {} after-deliver failure because of {}", this, feature.getClass().getName(),
578 public boolean isAlive() {
586 public boolean lock() {
587 logger.info("{}: lock", this);
589 for (PolicyControllerFeatureAPI feature : getProviders()) {
591 if (feature.beforeLock(this)) {
594 } catch (Exception e) {
595 logger.error("{}: feature {} before-lock failure because of {}", this, feature.getClass().getName(),
600 synchronized (this) {
608 // it does not affect associated sources/sinks, they are
609 // autonomous entities
611 boolean success = this.droolsController.lock();
613 for (PolicyControllerFeatureAPI feature : getProviders()) {
615 if (feature.afterLock(this)) {
618 } catch (Exception e) {
619 logger.error("{}: feature {} after-lock failure because of {}", this, feature.getClass().getName(),
631 public boolean unlock() {
633 logger.info("{}: unlock", this);
635 for (PolicyControllerFeatureAPI feature : getProviders()) {
637 if (feature.beforeUnlock(this)) {
640 } catch (Exception e) {
641 logger.error("{}: feature {} before-unlock failure because of {}", this, feature.getClass().getName(),
646 synchronized (this) {
654 boolean success = this.droolsController.unlock();
656 for (PolicyControllerFeatureAPI feature : getProviders()) {
658 if (feature.afterUnlock(this)) {
661 } catch (Exception e) {
662 logger.error("{}: feature {} after-unlock failure because of {}", this, feature.getClass().getName(),
674 public boolean isLocked() {
682 public List<? extends TopicSource> getTopicSources() {
690 public List<? extends TopicSink> getTopicSinks() {
698 public DroolsController getDrools() {
699 return this.droolsController;
709 public Properties getProperties() {
710 return this.properties;
714 public String toString() {
715 return "AggregatedPolicyController [name=" + name + ", alive=" + alive
716 + ", locked=" + locked + ", droolsController=" + droolsController + "]";
719 // the following methods may be overridden by junit tests
721 protected SystemPersistence getPersistenceManager() {
722 return SystemPersistence.manager;
725 protected TopicEndpoint getEndpointManager() {
726 return TopicEndpointManager.getManager();
729 protected DroolsControllerFactory getDroolsFactory() {
730 return DroolsController.factory;
733 protected List<PolicyControllerFeatureAPI> getProviders() {
734 return PolicyControllerFeatureAPI.providers.getList();