2e15895bca0cdbafb8ecf7fe9466b1530ae0008e
[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 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.onap.policy.controlloop.common.rules.test;
22
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;
27
28 import java.io.File;
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;
36 import lombok.Getter;
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.resources.ResourceUtils;
52 import org.onap.policy.controlloop.ControlLoopEvent;
53 import org.onap.policy.controlloop.drl.legacy.ControlLoopParams;
54 import org.onap.policy.controlloop.eventmanager.ControlLoopEventManager;
55 import org.onap.policy.controlloop.eventmanager.ControlLoopEventManager2;
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.drools.utils.logging.LoggerUtil;
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 public class Rules {
75     private static final Logger logger = LoggerFactory.getLogger(Rules.class);
76     private static final StandardCoder coder = new StandardCoder();
77     private static final String POLICY_MSG = "policy ";
78
79     /**
80      * PDP-D Engine.
81      */
82     @Getter
83     private final PolicyEngine pdpd = makeEngine();
84
85     /**
86      * PDP-D Configuration Repository.
87      */
88     @Getter
89     private final SystemPersistence pdpdRepo = makePdpdRepo();
90
91
92     @Getter
93     private final String controllerName;
94
95     @Getter
96     private PolicyController controller;
97
98
99     /**
100      * Constructs the object.
101      *
102      * @param controllerName name of the controller
103      */
104     public Rules(String controllerName) {
105         this.controllerName = controllerName;
106     }
107
108     /**
109      * Configures various items, including the PDP-D Engine.
110      *
111      * @param resourceDir path to resource directory
112      */
113     public void configure(String resourceDir) {
114         pdpdRepo.setConfigurationDir("src/test/resources/config");
115
116         try {
117             File kmoduleFile = new File(resourceDir + "/META-INF/kmodule.xml");
118             File pomFile = new File("src/test/resources/" + controllerName + ".pom");
119             String resourceDir2 = resourceDir + "/org/onap/policy/controlloop/";
120             File ruleFile = new File(resourceDir + File.separator + controllerName + ".drl");
121             List<File> ruleFiles = Collections.singletonList(ruleFile);
122
123             installArtifact(kmoduleFile, pomFile, resourceDir2, ruleFiles);
124         } catch (IOException e) {
125             throw new IllegalArgumentException("cannot configure KIE session for " + controllerName, e);
126         }
127
128         setupLogging();
129
130         pdpd.configure(new Properties());
131     }
132
133     /**
134      * Starts various items, including the PDP-D Engine.
135      */
136     public void start() {
137         controller = pdpd.createPolicyController(controllerName, pdpdRepo.getControllerProperties(controllerName));
138         pdpd.start();
139
140         setupDroolsLogging();
141     }
142
143     /**
144      * Stop PDP-D.
145      */
146     public void destroy() {
147         getControllerFactory().shutdown(controllerName);
148         pdpd.stop();
149     }
150
151     /**
152      * Removes various facts from working memory, including the Policy and Params, as well
153      * as any event managers and events.
154      */
155     public void resetFacts() {
156         List<Class<?>> classes = List.of(ToscaPolicy.class, ControlLoopParams.class, ControlLoopEventManager2.class,
157                         ControlLoopEvent.class);
158
159         // delete all objects of the listed classes
160         DroolsController drools = controller.getDrools();
161         classes.forEach(drools::delete);
162
163         // wait for them to be deleted
164         for (Class<?> clazz : classes) {
165             await(clazz.getSimpleName()).atMost(5, TimeUnit.SECONDS)
166                             .until(() -> drools.facts(controllerName, clazz).isEmpty());
167         }
168
169         /*
170          * We can't delete this class directly; we have to wait for the rules to clean it
171          * up, because the rule also cleans up a number of other associated objects.
172          */
173         Class<?> clazz = ControlLoopEventManager.class;
174         await(clazz.getSimpleName()).atMost(5, TimeUnit.SECONDS)
175                         .until(() -> drools.facts(controllerName, clazz).isEmpty());
176     }
177
178     /**
179      * Installs a policy from policy/models (examples) repo.
180      */
181     public ToscaPolicy setupPolicyFromTemplate(String templatePath, String policyName) {
182         try {
183             return setupPolicy(getPolicyFromTemplate(templatePath, policyName));
184
185         } catch (CoderException e) {
186             throw new IllegalArgumentException(POLICY_MSG + policyName, e);
187
188         } catch (InterruptedException e) {
189             Thread.currentThread().interrupt();
190             throw new IllegalArgumentException(POLICY_MSG + policyName, e);
191         }
192     }
193
194     private ToscaPolicy getPolicyFromTemplate(String resourcePath, String policyName) throws CoderException {
195         String policyJson = ResourceUtils.getResourceAsString(resourcePath);
196         if (policyJson == null) {
197             throw new CoderException(new FileNotFoundException(resourcePath));
198         }
199
200         ToscaServiceTemplate serviceTemplate = coder.decode(policyJson, ToscaServiceTemplate.class);
201         ToscaPolicy policy = serviceTemplate.getToscaTopologyTemplate().getPolicies().get(0).get(policyName);
202         assertNotNull(policy);
203
204         /*
205          * name and version are used within a drl. api component and drools core will
206          * ensure that these are populated.
207          */
208         if (StringUtils.isBlank(policy.getName())) {
209             policy.setName(policyName);
210         }
211
212         if (StringUtils.isBlank(policy.getVersion())) {
213             policy.setVersion(policy.getTypeVersion());
214         }
215
216         return serviceTemplate.getToscaTopologyTemplate().getPolicies().get(0).get(policyName);
217     }
218
219     /**
220      * Installs a given policy.
221      */
222     public ToscaPolicy setupPolicyFromFile(String policyPath) {
223         try {
224             return setupPolicy(getPolicyFromFile(policyPath));
225
226         } catch (CoderException e) {
227             throw new IllegalArgumentException(POLICY_MSG + policyPath, e);
228
229         } catch (InterruptedException e) {
230             Thread.currentThread().interrupt();
231             throw new IllegalArgumentException(POLICY_MSG + policyPath, e);
232         }
233     }
234
235     private ToscaPolicy getPolicyFromFile(String policyPath) throws CoderException {
236         String policyJson = ResourceUtils.getResourceAsString(policyPath);
237         if (policyJson == null) {
238             throw new CoderException(new FileNotFoundException(policyPath));
239         }
240
241         return coder.decode(policyJson, ToscaPolicy.class);
242     }
243
244     protected ToscaPolicy setupPolicy(ToscaPolicy policy) throws InterruptedException {
245         final KieObjectExpectedCallback<?> policyTracker = new KieObjectInsertedExpectedCallback<>(policy);
246         final KieObjectExpectedCallback<?> paramsTracker =
247                         new KieClassInsertedExpectedCallback<>(ControlLoopParams.class);
248
249         controller.getDrools().offer(policy);
250
251         assertTrue(policyTracker.isNotified());
252         assertTrue(paramsTracker.isNotified());
253
254         assertEquals(1, controller.getDrools().facts(controllerName, ToscaPolicy.class).stream()
255                         .filter(anotherPolicy -> anotherPolicy == policy).count());
256
257         assertEquals(1, controller.getDrools().facts(controllerName, ControlLoopParams.class).stream()
258                         .filter(params -> params.getToscaPolicy() == policy).count());
259         return policy;
260     }
261
262     /**
263      * Sets up overall logging.
264      */
265     private void setupLogging() {
266         LoggerUtil.setLevel(LoggerUtil.ROOT_LOGGER, "WARN");
267         LoggerUtil.setLevel("org.eclipse.jetty", "WARN");
268         LoggerUtil.setLevel("org.onap.policy.controlloop", "INFO");
269         LoggerUtil.setLevel("network", "INFO");
270     }
271
272     /**
273      * Sets up Drools Logging for events of interest.
274      */
275     private void setupDroolsLogging() {
276         KieSession session = getKieSession();
277
278         session.addEventListener(new RuleListenerLogger());
279         session.addEventListener(new AgendaListenerLogger());
280     }
281
282     /**
283      * Logs Modifications to Working Memory.
284      */
285     private static class RuleListenerLogger implements RuleRuntimeEventListener {
286         @Override
287         public void objectInserted(ObjectInsertedEvent event) {
288             String ruleName = (event.getRule() != null) ? event.getRule().getName() : "null";
289             logger.info("RULE {}: inserted {}", ruleName, event.getObject());
290         }
291
292         @Override
293         public void objectUpdated(ObjectUpdatedEvent event) {
294             String ruleName = (event.getRule() != null) ? event.getRule().getName() : "null";
295             logger.info("RULE {}: updated {}", ruleName, event.getObject());
296
297         }
298
299         @Override
300         public void objectDeleted(ObjectDeletedEvent event) {
301             String ruleName = (event.getRule() != null) ? event.getRule().getName() : "null";
302             logger.info("RULE {}: deleted {}", ruleName, event.getOldObject());
303         }
304     }
305
306     /**
307      * Logs Rule Matches.
308      */
309     private static class AgendaListenerLogger extends DefaultAgendaEventListener {
310         @Override
311         public void matchCreated(MatchCreatedEvent event) {
312             logger.info("RULE {}: match created", event.getMatch().getRule().getName());
313         }
314
315         @Override
316         public void matchCancelled(MatchCancelledEvent event) {
317             logger.info("RULE {}: match cancelled", event.getMatch().getRule().getName());
318         }
319
320         @Override
321         public void beforeMatchFired(BeforeMatchFiredEvent event) {
322             logger.info("RULE {}: before match fired", event.getMatch().getRule().getName());
323         }
324
325         @Override
326         public void afterMatchFired(AfterMatchFiredEvent event) {
327             logger.info("RULE {}: after match fired", event.getMatch().getRule().getName());
328         }
329     }
330
331     /**
332      * Base Class to track Working Memory updates for objects of type T.
333      */
334     private abstract class KieObjectExpectedCallback<T> extends DefaultRuleRuntimeEventListener {
335         protected T subject;
336
337         protected CountDownLatch countDownLatch = new CountDownLatch(1);
338
339         public KieObjectExpectedCallback(T affected) {
340             subject = affected;
341             register();
342         }
343
344         public boolean isNotified() throws InterruptedException {
345             return countDownLatch.await(9L, TimeUnit.SECONDS);
346         }
347
348         protected void callbacked() {
349             unregister();
350             countDownLatch.countDown();
351         }
352
353         public KieObjectExpectedCallback<T> register() {
354             getKieSession().addEventListener(this);
355             return this;
356         }
357
358         public KieObjectExpectedCallback<T> unregister() {
359             getKieSession().removeEventListener(this);
360             return this;
361         }
362     }
363
364     /**
365      * Tracks inserts in Working Memory for an object of type T.
366      */
367     private class KieObjectInsertedExpectedCallback<T> extends KieObjectExpectedCallback<T> {
368         public KieObjectInsertedExpectedCallback(T affected) {
369             super(affected);
370         }
371
372         @Override
373         public void objectInserted(ObjectInsertedEvent event) {
374             if (subject == event.getObject()) {
375                 callbacked();
376             }
377         }
378     }
379
380     /**
381      * Tracks inserts in Working Memory for any object of class T.
382      */
383     private class KieClassInsertedExpectedCallback<T> extends KieObjectInsertedExpectedCallback<T> {
384
385         public KieClassInsertedExpectedCallback(T affected) {
386             super(affected);
387         }
388
389         @Override
390         public void objectInserted(ObjectInsertedEvent event) {
391             if (subject == event.getObject().getClass()) {
392                 callbacked();
393             }
394         }
395     }
396
397     // these may be overridden by junit tests
398
399
400     protected PolicyEngine makeEngine() {
401         return PolicyEngineConstants.getManager();
402     }
403
404
405     protected SystemPersistence makePdpdRepo() {
406         return SystemPersistenceConstants.getManager();
407     }
408
409     protected KieSession getKieSession() {
410         return getControllerFactory().get(controllerName).getDrools().getContainer().getPolicySession(controllerName)
411                         .getKieSession();
412     }
413
414     protected PolicyControllerFactory getControllerFactory() {
415         return PolicyControllerConstants.getFactory();
416     }
417
418     protected void installArtifact(File kmoduleFile, File pomFile, String resourceDir, List<File> ruleFiles)
419                     throws IOException {
420
421         KieUtils.installArtifact(kmoduleFile, pomFile, resourceDir, ruleFiles);
422     }
423 }