2  * ============LICENSE_START=======================================================
 
   4  * ================================================================================
 
   5  * Copyright (C) 2020-2022 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.controlloop.common.rules.test;
 
  23 import static org.awaitility.Awaitility.await;
 
  24 import static org.junit.Assert.assertEquals;
 
  25 import static org.junit.Assert.assertNotNull;
 
  26 import static org.junit.Assert.assertTrue;
 
  29 import java.io.FileNotFoundException;
 
  30 import java.io.IOException;
 
  31 import java.util.Collections;
 
  32 import java.util.List;
 
  33 import java.util.Properties;
 
  34 import java.util.concurrent.CountDownLatch;
 
  35 import java.util.concurrent.TimeUnit;
 
  37 import org.apache.commons.lang3.StringUtils;
 
  38 import org.kie.api.event.rule.AfterMatchFiredEvent;
 
  39 import org.kie.api.event.rule.BeforeMatchFiredEvent;
 
  40 import org.kie.api.event.rule.DefaultAgendaEventListener;
 
  41 import org.kie.api.event.rule.DefaultRuleRuntimeEventListener;
 
  42 import org.kie.api.event.rule.MatchCancelledEvent;
 
  43 import org.kie.api.event.rule.MatchCreatedEvent;
 
  44 import org.kie.api.event.rule.ObjectDeletedEvent;
 
  45 import org.kie.api.event.rule.ObjectInsertedEvent;
 
  46 import org.kie.api.event.rule.ObjectUpdatedEvent;
 
  47 import org.kie.api.event.rule.RuleRuntimeEventListener;
 
  48 import org.kie.api.runtime.KieSession;
 
  49 import org.onap.policy.common.utils.coder.CoderException;
 
  50 import org.onap.policy.common.utils.coder.StandardCoder;
 
  51 import org.onap.policy.common.utils.logging.LoggerUtils;
 
  52 import org.onap.policy.common.utils.resources.ResourceUtils;
 
  53 import org.onap.policy.controlloop.ControlLoopEvent;
 
  54 import org.onap.policy.controlloop.drl.legacy.ControlLoopParams;
 
  55 import org.onap.policy.controlloop.eventmanager.ControlLoopEventManager;
 
  56 import org.onap.policy.drools.controller.DroolsController;
 
  57 import org.onap.policy.drools.persistence.SystemPersistence;
 
  58 import org.onap.policy.drools.persistence.SystemPersistenceConstants;
 
  59 import org.onap.policy.drools.system.PolicyController;
 
  60 import org.onap.policy.drools.system.PolicyControllerConstants;
 
  61 import org.onap.policy.drools.system.PolicyControllerFactory;
 
  62 import org.onap.policy.drools.system.PolicyEngine;
 
  63 import org.onap.policy.drools.system.PolicyEngineConstants;
 
  64 import org.onap.policy.drools.util.KieUtils;
 
  65 import org.onap.policy.models.tosca.authorative.concepts.ToscaPolicy;
 
  66 import org.onap.policy.models.tosca.authorative.concepts.ToscaServiceTemplate;
 
  67 import org.slf4j.Logger;
 
  68 import org.slf4j.LoggerFactory;
 
  71  * Mechanism by which junit tests can manage the rule engine.
 
  74     private static final Logger logger = LoggerFactory.getLogger(Rules.class);
 
  75     private static final StandardCoder coder = new StandardCoder();
 
  76     private static final String POLICY_MSG = "policy ";
 
  82     private final PolicyEngine pdpd = makeEngine();
 
  85      * PDP-D Configuration Repository.
 
  88     private final SystemPersistence pdpdRepo = makePdpdRepo();
 
  92     private final String controllerName;
 
  95     private PolicyController controller;
 
  99      * Constructs the object.
 
 101      * @param controllerName name of the controller
 
 103     public Rules(String controllerName) {
 
 104         this.controllerName = controllerName;
 
 108      * Configures various items, including the PDP-D Engine.
 
 110      * @param resourceDir path to resource directory
 
 112     public void configure(String resourceDir) {
 
 113         pdpdRepo.setConfigurationDir("src/test/resources/config");
 
 116             var kmoduleFile = new File(resourceDir + "/META-INF/kmodule.xml");
 
 117             var pomFile = new File("src/test/resources/" + controllerName + ".pom");
 
 118             var resourceDir2 = resourceDir + "/org/onap/policy/controlloop/";
 
 119             var ruleFile = new File(resourceDir + File.separator + controllerName + ".drl");
 
 120             List<File> ruleFiles = Collections.singletonList(ruleFile);
 
 122             installArtifact(kmoduleFile, pomFile, resourceDir2, ruleFiles);
 
 123         } catch (IOException e) {
 
 124             throw new IllegalArgumentException("cannot configure KIE session for " + controllerName, e);
 
 129         pdpd.configure(new Properties());
 
 133      * Starts various items, including the PDP-D Engine.
 
 135     public void start() {
 
 136         controller = pdpd.createPolicyController(controllerName, pdpdRepo.getControllerProperties(controllerName));
 
 139         setupDroolsLogging();
 
 145     public void destroy() {
 
 146         getControllerFactory().shutdown(controllerName);
 
 151      * Removes various facts from working memory, including the Policy and Params, as well
 
 152      * as any event managers and events.
 
 154     public void resetFacts() {
 
 155         List<Class<?>> classes = List.of(ToscaPolicy.class, ControlLoopParams.class, ControlLoopEventManager.class,
 
 156                         ControlLoopEvent.class);
 
 158         // delete all objects of the listed classes
 
 159         DroolsController drools = controller.getDrools();
 
 160         classes.forEach(drools::delete);
 
 162         // wait for them to be deleted
 
 163         for (Class<?> clazz : classes) {
 
 164             await(clazz.getSimpleName()).atMost(5, TimeUnit.SECONDS)
 
 165                             .until(() -> drools.facts(controllerName, clazz).isEmpty());
 
 170      * Installs a policy from policy/models (examples) repo.
 
 172     public ToscaPolicy setupPolicyFromTemplate(String templatePath, String policyName) {
 
 174             return setupPolicy(getPolicyFromTemplate(templatePath, policyName));
 
 176         } catch (CoderException e) {
 
 177             throw new IllegalArgumentException(POLICY_MSG + policyName, e);
 
 179         } catch (InterruptedException e) {
 
 180             Thread.currentThread().interrupt();
 
 181             throw new IllegalArgumentException(POLICY_MSG + policyName, e);
 
 185     private ToscaPolicy getPolicyFromTemplate(String resourcePath, String policyName) throws CoderException {
 
 186         var policyJson = ResourceUtils.getResourceAsString(resourcePath);
 
 187         if (policyJson == null) {
 
 188             throw new CoderException(new FileNotFoundException(resourcePath));
 
 191         ToscaServiceTemplate serviceTemplate = coder.decode(policyJson, ToscaServiceTemplate.class);
 
 192         ToscaPolicy policy = serviceTemplate.getToscaTopologyTemplate().getPolicies().get(0).get(policyName);
 
 193         assertNotNull(policy);
 
 196          * name and version are used within a drl. api component and drools core will
 
 197          * ensure that these are populated.
 
 199         if (StringUtils.isBlank(policy.getName())) {
 
 200             policy.setName(policyName);
 
 203         if (StringUtils.isBlank(policy.getVersion())) {
 
 204             policy.setVersion(policy.getTypeVersion());
 
 207         return serviceTemplate.getToscaTopologyTemplate().getPolicies().get(0).get(policyName);
 
 211      * Installs a given policy.
 
 213     public ToscaPolicy setupPolicyFromFile(String policyPath) {
 
 215             return setupPolicy(getPolicyFromFile(policyPath));
 
 217         } catch (CoderException e) {
 
 218             throw new IllegalArgumentException(POLICY_MSG + policyPath, e);
 
 220         } catch (InterruptedException e) {
 
 221             Thread.currentThread().interrupt();
 
 222             throw new IllegalArgumentException(POLICY_MSG + policyPath, e);
 
 227      * Get policy from file.
 
 229     public static ToscaPolicy getPolicyFromFile(String policyPath) throws CoderException {
 
 230         var policyJson = ResourceUtils.getResourceAsString(policyPath);
 
 231         if (policyJson == null) {
 
 232             throw new CoderException(new FileNotFoundException(policyPath));
 
 235         if (policyPath.startsWith("policies/")) {
 
 236             // using policy/models examples where policies are wrapped with the ToscaServiceTemplate
 
 237             // for API component provisioning
 
 238             logger.info("retrieving policy from policy models examples");
 
 239             ToscaServiceTemplate template = coder.decode(policyJson, ToscaServiceTemplate.class);
 
 240             if (template.getToscaTopologyTemplate().getPolicies().size() == 1) {
 
 241                 return template.getToscaTopologyTemplate().getPolicies().get(0).values().iterator().next();
 
 245         return coder.decode(policyJson, ToscaPolicy.class);
 
 248     protected ToscaPolicy setupPolicy(ToscaPolicy policy) throws InterruptedException {
 
 249         final KieObjectExpectedCallback<?> policyTracker = new KieObjectInsertedExpectedCallback<>(policy);
 
 250         final KieObjectExpectedCallback<?> paramsTracker =
 
 251                         new KieClassInsertedExpectedCallback<>(ControlLoopParams.class);
 
 253         controller.getDrools().offer(policy);
 
 255         assertTrue(policyTracker.isNotified());
 
 256         assertTrue(paramsTracker.isNotified());
 
 258         assertEquals(1, controller.getDrools().facts(controllerName, ToscaPolicy.class).stream()
 
 259                         .filter(anotherPolicy -> anotherPolicy == policy).count());
 
 261         assertEquals(1, controller.getDrools().facts(controllerName, ControlLoopParams.class).stream()
 
 262                         .filter(params -> params.getToscaPolicy() == policy).count());
 
 267      * Sets up overall logging.
 
 269     private void setupLogging() {
 
 270         LoggerUtils.setLevel(LoggerUtils.ROOT_LOGGER, "WARN");
 
 271         LoggerUtils.setLevel("org.eclipse.jetty", "WARN");
 
 272         LoggerUtils.setLevel("org.onap.policy.controlloop", "INFO");
 
 273         LoggerUtils.setLevel("network", "INFO");
 
 277      * Sets up Drools Logging for events of interest.
 
 279     private void setupDroolsLogging() {
 
 280         var session = getKieSession();
 
 282         session.addEventListener(new RuleListenerLogger());
 
 283         session.addEventListener(new AgendaListenerLogger());
 
 287      * Logs Modifications to Working Memory.
 
 289     private static class RuleListenerLogger implements RuleRuntimeEventListener {
 
 291         public void objectInserted(ObjectInsertedEvent event) {
 
 292             String ruleName = (event.getRule() != null) ? event.getRule().getName() : "null";
 
 293             logger.info("RULE {}: inserted {}", ruleName, event.getObject());
 
 297         public void objectUpdated(ObjectUpdatedEvent event) {
 
 298             String ruleName = (event.getRule() != null) ? event.getRule().getName() : "null";
 
 299             logger.info("RULE {}: updated {}", ruleName, event.getObject());
 
 304         public void objectDeleted(ObjectDeletedEvent event) {
 
 305             String ruleName = (event.getRule() != null) ? event.getRule().getName() : "null";
 
 306             logger.info("RULE {}: deleted {}", ruleName, event.getOldObject());
 
 313     private static class AgendaListenerLogger extends DefaultAgendaEventListener {
 
 315         public void matchCreated(MatchCreatedEvent event) {
 
 316             logger.info("RULE {}: match created", event.getMatch().getRule().getName());
 
 320         public void matchCancelled(MatchCancelledEvent event) {
 
 321             logger.info("RULE {}: match cancelled", event.getMatch().getRule().getName());
 
 325         public void beforeMatchFired(BeforeMatchFiredEvent event) {
 
 326             logger.info("RULE {}: before match fired", event.getMatch().getRule().getName());
 
 330         public void afterMatchFired(AfterMatchFiredEvent event) {
 
 331             logger.info("RULE {}: after match fired", event.getMatch().getRule().getName());
 
 336      * Base Class to track Working Memory updates for objects of type T.
 
 338     private abstract class KieObjectExpectedCallback<T> extends DefaultRuleRuntimeEventListener {
 
 341         protected CountDownLatch countDownLatch = new CountDownLatch(1);
 
 343         public KieObjectExpectedCallback(T affected) {
 
 348         public boolean isNotified() throws InterruptedException {
 
 349             return countDownLatch.await(9L, TimeUnit.SECONDS);
 
 352         protected void callbacked() {
 
 354             countDownLatch.countDown();
 
 357         public KieObjectExpectedCallback<T> register() {
 
 358             getKieSession().addEventListener(this);
 
 362         public KieObjectExpectedCallback<T> unregister() {
 
 363             getKieSession().removeEventListener(this);
 
 369      * Tracks inserts in Working Memory for an object of type T.
 
 371     private class KieObjectInsertedExpectedCallback<T> extends KieObjectExpectedCallback<T> {
 
 372         public KieObjectInsertedExpectedCallback(T affected) {
 
 377         public void objectInserted(ObjectInsertedEvent event) {
 
 378             if (subject == event.getObject()) {
 
 385      * Tracks inserts in Working Memory for any object of class T.
 
 387     private class KieClassInsertedExpectedCallback<T> extends KieObjectInsertedExpectedCallback<T> {
 
 389         public KieClassInsertedExpectedCallback(T affected) {
 
 394         public void objectInserted(ObjectInsertedEvent event) {
 
 395             if (subject == event.getObject().getClass()) {
 
 401     // these may be overridden by junit tests
 
 404     protected PolicyEngine makeEngine() {
 
 405         return PolicyEngineConstants.getManager();
 
 409     protected SystemPersistence makePdpdRepo() {
 
 410         return SystemPersistenceConstants.getManager();
 
 413     protected KieSession getKieSession() {
 
 414         return getControllerFactory().get(controllerName).getDrools().getContainer().getPolicySession(controllerName)
 
 418     protected PolicyControllerFactory getControllerFactory() {
 
 419         return PolicyControllerConstants.getFactory();
 
 422     protected void installArtifact(File kmoduleFile, File pomFile, String resourceDir, List<File> ruleFiles)
 
 425         KieUtils.installArtifact(kmoduleFile, pomFile, resourceDir, ruleFiles);