Fix topic lower case for kafka compability
[policy/drools-applications.git] / controlloop / common / rules-test / src / main / java / org / onap / policy / controlloop / common / rules / test / Rules.java
1 /*-
2  * ============LICENSE_START=======================================================
3  * ONAP
4  * ================================================================================
5  * Copyright (C) 2020-2022 AT&T Intellectual Property. All rights reserved.
6  * Modifications Copyright (C) 2023-2024 Nordix Foundation.
7  * ================================================================================
8  * Licensed under the Apache License, Version 2.0 (the "License");
9  * you may not use this file except in compliance with the License.
10  * You may obtain a copy of the License at
11  *
12  *      http://www.apache.org/licenses/LICENSE-2.0
13  *
14  * Unless required by applicable law or agreed to in writing, software
15  * distributed under the License is distributed on an "AS IS" BASIS,
16  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
17  * See the License for the specific language governing permissions and
18  * limitations under the License.
19  * ============LICENSE_END=========================================================
20  */
21
22 package org.onap.policy.controlloop.common.rules.test;
23
24 import static org.awaitility.Awaitility.await;
25 import static org.junit.jupiter.api.Assertions.assertEquals;
26 import static org.junit.jupiter.api.Assertions.assertNotNull;
27 import static org.junit.jupiter.api.Assertions.assertTrue;
28
29 import java.io.File;
30 import java.io.FileNotFoundException;
31 import java.io.IOException;
32 import java.util.Collections;
33 import java.util.List;
34 import java.util.Properties;
35 import java.util.concurrent.CountDownLatch;
36 import java.util.concurrent.TimeUnit;
37 import lombok.Getter;
38 import org.apache.commons.lang3.StringUtils;
39 import org.kie.api.event.rule.AfterMatchFiredEvent;
40 import org.kie.api.event.rule.BeforeMatchFiredEvent;
41 import org.kie.api.event.rule.DefaultAgendaEventListener;
42 import org.kie.api.event.rule.DefaultRuleRuntimeEventListener;
43 import org.kie.api.event.rule.MatchCancelledEvent;
44 import org.kie.api.event.rule.MatchCreatedEvent;
45 import org.kie.api.event.rule.ObjectDeletedEvent;
46 import org.kie.api.event.rule.ObjectInsertedEvent;
47 import org.kie.api.event.rule.ObjectUpdatedEvent;
48 import org.kie.api.event.rule.RuleRuntimeEventListener;
49 import org.kie.api.runtime.KieSession;
50 import org.onap.policy.common.utils.coder.CoderException;
51 import org.onap.policy.common.utils.coder.StandardCoder;
52 import org.onap.policy.common.utils.logging.LoggerUtils;
53 import org.onap.policy.common.utils.resources.ResourceUtils;
54 import org.onap.policy.controlloop.ControlLoopEvent;
55 import org.onap.policy.controlloop.drl.legacy.ControlLoopParams;
56 import org.onap.policy.controlloop.eventmanager.ControlLoopEventManager;
57 import org.onap.policy.drools.controller.DroolsController;
58 import org.onap.policy.drools.persistence.SystemPersistence;
59 import org.onap.policy.drools.persistence.SystemPersistenceConstants;
60 import org.onap.policy.drools.system.PolicyController;
61 import org.onap.policy.drools.system.PolicyControllerConstants;
62 import org.onap.policy.drools.system.PolicyControllerFactory;
63 import org.onap.policy.drools.system.PolicyEngine;
64 import org.onap.policy.drools.system.PolicyEngineConstants;
65 import org.onap.policy.drools.util.KieUtils;
66 import org.onap.policy.models.tosca.authorative.concepts.ToscaPolicy;
67 import org.onap.policy.models.tosca.authorative.concepts.ToscaServiceTemplate;
68 import org.slf4j.Logger;
69 import org.slf4j.LoggerFactory;
70
71 /**
72  * Mechanism by which junit tests can manage the rule engine.
73  */
74 @Getter
75 public class Rules {
76     private static final Logger logger = LoggerFactory.getLogger(Rules.class);
77     private static final StandardCoder coder = new StandardCoder();
78     private static final String POLICY_MSG = "policy ";
79
80     /**
81      * PDP-D Engine.
82      */
83     private final PolicyEngine pdpd = makeEngine();
84
85     /**
86      * PDP-D Configuration Repository.
87      */
88     private final SystemPersistence pdpdRepo = makePdpdRepo();
89
90
91     private final String controllerName;
92
93     private PolicyController controller;
94
95
96     /**
97      * Constructs the object.
98      *
99      * @param controllerName name of the controller
100      */
101     public Rules(String controllerName) {
102         this.controllerName = controllerName;
103     }
104
105     /**
106      * Configures various items, including the PDP-D Engine.
107      *
108      * @param resourceDir path to resource directory
109      */
110     public void configure(String resourceDir) {
111         pdpdRepo.setConfigurationDir("src/test/resources/config");
112
113         try {
114             var kmoduleFile = new File(resourceDir + "/META-INF/kmodule.xml");
115             var pomFile = new File("src/test/resources/" + controllerName + ".pom");
116             var resourceDir2 = resourceDir + "/org/onap/policy/controlloop/";
117             var ruleFile = new File(resourceDir + File.separator + controllerName + ".drl");
118             List<File> ruleFiles = Collections.singletonList(ruleFile);
119
120             installArtifact(kmoduleFile, pomFile, resourceDir2, ruleFiles);
121         } catch (IOException e) {
122             throw new IllegalArgumentException("cannot configure KIE session for " + controllerName, e);
123         }
124
125         setupLogging();
126
127         pdpd.configure(new Properties());
128     }
129
130     /**
131      * Starts various items, including the PDP-D Engine.
132      */
133     public void start() {
134         controller = pdpd.createPolicyController(controllerName, pdpdRepo.getControllerProperties(controllerName));
135         pdpd.start();
136
137         setupDroolsLogging();
138     }
139
140     /**
141      * Stop PDP-D.
142      */
143     public void destroy() {
144         getControllerFactory().shutdown(controllerName);
145         pdpd.stop();
146     }
147
148     /**
149      * Removes various facts from working memory, including the Policy and Params, as well
150      * as any event managers and events.
151      */
152     public void resetFacts() {
153         List<Class<?>> classes = List.of(ToscaPolicy.class, ControlLoopParams.class, ControlLoopEventManager.class,
154                         ControlLoopEvent.class);
155
156         // delete all objects of the listed classes
157         DroolsController drools = controller.getDrools();
158         classes.forEach(drools::delete);
159
160         // wait for them to be deleted
161         for (Class<?> clazz : classes) {
162             await(clazz.getSimpleName()).atMost(5, TimeUnit.SECONDS)
163                             .until(() -> drools.facts(controllerName, clazz).isEmpty());
164         }
165     }
166
167     /**
168      * Installs a policy from policy/models (examples) repo.
169      */
170     public ToscaPolicy setupPolicyFromTemplate(String templatePath, String policyName) {
171         try {
172             return setupPolicy(getPolicyFromTemplate(templatePath, policyName));
173
174         } catch (CoderException e) {
175             throw new IllegalArgumentException(POLICY_MSG + policyName, e);
176
177         } catch (InterruptedException e) {
178             Thread.currentThread().interrupt();
179             throw new IllegalArgumentException(POLICY_MSG + policyName, e);
180         }
181     }
182
183     private ToscaPolicy getPolicyFromTemplate(String resourcePath, String policyName) throws CoderException {
184         var policyJson = ResourceUtils.getResourceAsString(resourcePath);
185         if (policyJson == null) {
186             throw new CoderException(new FileNotFoundException(resourcePath));
187         }
188
189         ToscaServiceTemplate serviceTemplate = coder.decode(policyJson, ToscaServiceTemplate.class);
190         ToscaPolicy policy = serviceTemplate.getToscaTopologyTemplate().getPolicies().get(0).get(policyName);
191         assertNotNull(policy);
192
193         /*
194          * name and version are used within a drl. api component and drools core will
195          * ensure that these are populated.
196          */
197         if (StringUtils.isBlank(policy.getName())) {
198             policy.setName(policyName);
199         }
200
201         if (StringUtils.isBlank(policy.getVersion())) {
202             policy.setVersion(policy.getTypeVersion());
203         }
204
205         return serviceTemplate.getToscaTopologyTemplate().getPolicies().get(0).get(policyName);
206     }
207
208     /**
209      * Installs a given policy.
210      */
211     public ToscaPolicy setupPolicyFromFile(String policyPath) {
212         try {
213             return setupPolicy(getPolicyFromFile(policyPath));
214
215         } catch (CoderException e) {
216             throw new IllegalArgumentException(POLICY_MSG + policyPath, e);
217
218         } catch (InterruptedException e) {
219             Thread.currentThread().interrupt();
220             throw new IllegalArgumentException(POLICY_MSG + policyPath, e);
221         }
222     }
223
224     /**
225      * Get policy from file.
226      */
227     public static ToscaPolicy getPolicyFromFile(String policyPath) throws CoderException {
228         var policyJson = ResourceUtils.getResourceAsString(policyPath);
229         if (policyJson == null) {
230             throw new CoderException(new FileNotFoundException(policyPath));
231         }
232
233         if (policyPath.startsWith("policies/")) {
234             // using policy/models examples where policies are wrapped with the ToscaServiceTemplate
235             // for API component provisioning
236             logger.info("retrieving policy from policy models examples");
237             ToscaServiceTemplate template = coder.decode(policyJson, ToscaServiceTemplate.class);
238             if (template.getToscaTopologyTemplate().getPolicies().size() == 1) {
239                 return template.getToscaTopologyTemplate().getPolicies().get(0).values().iterator().next();
240             }
241         }
242
243         return coder.decode(policyJson, ToscaPolicy.class);
244     }
245
246     protected ToscaPolicy setupPolicy(ToscaPolicy policy) throws InterruptedException {
247         final KieObjectExpectedCallback<?> policyTracker = new KieObjectInsertedExpectedCallback<>(policy);
248         final KieObjectExpectedCallback<?> paramsTracker =
249                         new KieClassInsertedExpectedCallback<>(ControlLoopParams.class);
250
251         controller.getDrools().offer(policy);
252
253         assertTrue(policyTracker.isNotified());
254         assertTrue(paramsTracker.isNotified());
255
256         assertEquals(1, controller.getDrools().facts(controllerName, ToscaPolicy.class).stream()
257                         .filter(anotherPolicy -> anotherPolicy == policy).count());
258
259         assertEquals(1, controller.getDrools().facts(controllerName, ControlLoopParams.class).stream()
260                         .filter(params -> params.getToscaPolicy() == policy).count());
261         return policy;
262     }
263
264     /**
265      * Sets up overall logging.
266      */
267     private void setupLogging() {
268         LoggerUtils.setLevel(LoggerUtils.ROOT_LOGGER, "WARN");
269         LoggerUtils.setLevel("org.eclipse.jetty", "WARN");
270         LoggerUtils.setLevel("org.onap.policy.controlloop", "INFO");
271         LoggerUtils.setLevel("network", "INFO");
272     }
273
274     /**
275      * Sets up Drools Logging for events of interest.
276      */
277     private void setupDroolsLogging() {
278         var session = getKieSession();
279
280         session.addEventListener(new RuleListenerLogger());
281         session.addEventListener(new AgendaListenerLogger());
282     }
283
284     /**
285      * Logs Modifications to Working Memory.
286      */
287     private static class RuleListenerLogger implements RuleRuntimeEventListener {
288         @Override
289         public void objectInserted(ObjectInsertedEvent event) {
290             String ruleName = (event.getRule() != null) ? event.getRule().getName() : "null";
291             logger.info("RULE {}: inserted {}", ruleName, event.getObject());
292         }
293
294         @Override
295         public void objectUpdated(ObjectUpdatedEvent event) {
296             String ruleName = (event.getRule() != null) ? event.getRule().getName() : "null";
297             logger.info("RULE {}: updated {}", ruleName, event.getObject());
298
299         }
300
301         @Override
302         public void objectDeleted(ObjectDeletedEvent event) {
303             String ruleName = (event.getRule() != null) ? event.getRule().getName() : "null";
304             logger.info("RULE {}: deleted {}", ruleName, event.getOldObject());
305         }
306     }
307
308     /**
309      * Logs Rule Matches.
310      */
311     private static class AgendaListenerLogger extends DefaultAgendaEventListener {
312         @Override
313         public void matchCreated(MatchCreatedEvent event) {
314             logger.info("RULE {}: match created", event.getMatch().getRule().getName());
315         }
316
317         @Override
318         public void matchCancelled(MatchCancelledEvent event) {
319             logger.info("RULE {}: match cancelled", event.getMatch().getRule().getName());
320         }
321
322         @Override
323         public void beforeMatchFired(BeforeMatchFiredEvent event) {
324             logger.info("RULE {}: before match fired", event.getMatch().getRule().getName());
325         }
326
327         @Override
328         public void afterMatchFired(AfterMatchFiredEvent event) {
329             logger.info("RULE {}: after match fired", event.getMatch().getRule().getName());
330         }
331     }
332
333     /**
334      * Base Class to track Working Memory updates for objects of type T.
335      */
336     private abstract class KieObjectExpectedCallback<T> extends DefaultRuleRuntimeEventListener {
337         protected T subject;
338
339         protected CountDownLatch countDownLatch = new CountDownLatch(1);
340
341         public KieObjectExpectedCallback(T affected) {
342             subject = affected;
343             register();
344         }
345
346         public boolean isNotified() throws InterruptedException {
347             return countDownLatch.await(9L, TimeUnit.SECONDS);
348         }
349
350         protected void callbacked() {
351             unregister();
352             countDownLatch.countDown();
353         }
354
355         public KieObjectExpectedCallback<T> register() {
356             getKieSession().addEventListener(this);
357             return this;
358         }
359
360         public KieObjectExpectedCallback<T> unregister() {
361             getKieSession().removeEventListener(this);
362             return this;
363         }
364     }
365
366     /**
367      * Tracks inserts in Working Memory for an object of type T.
368      */
369     private class KieObjectInsertedExpectedCallback<T> extends KieObjectExpectedCallback<T> {
370         public KieObjectInsertedExpectedCallback(T affected) {
371             super(affected);
372         }
373
374         @Override
375         public void objectInserted(ObjectInsertedEvent event) {
376             if (subject == event.getObject()) {
377                 callbacked();
378             }
379         }
380     }
381
382     /**
383      * Tracks inserts in Working Memory for any object of class T.
384      */
385     private class KieClassInsertedExpectedCallback<T> extends KieObjectInsertedExpectedCallback<T> {
386
387         public KieClassInsertedExpectedCallback(T affected) {
388             super(affected);
389         }
390
391         @Override
392         public void objectInserted(ObjectInsertedEvent event) {
393             if (subject == event.getObject().getClass()) {
394                 callbacked();
395             }
396         }
397     }
398
399     // these may be overridden by junit tests
400
401
402     protected PolicyEngine makeEngine() {
403         return PolicyEngineConstants.getManager();
404     }
405
406
407     protected SystemPersistence makePdpdRepo() {
408         return SystemPersistenceConstants.getManager();
409     }
410
411     protected KieSession getKieSession() {
412         return getControllerFactory().get(controllerName).getDrools().getContainer().getPolicySession(controllerName)
413                         .getKieSession();
414     }
415
416     protected PolicyControllerFactory getControllerFactory() {
417         return PolicyControllerConstants.getFactory();
418     }
419
420     protected void installArtifact(File kmoduleFile, File pomFile, String resourceDir, List<File> ruleFiles)
421                     throws IOException {
422
423         KieUtils.installArtifact(kmoduleFile, pomFile, resourceDir, ruleFiles);
424     }
425 }