Upgrade drools libs from 7.x to 8.x
[policy/drools-pdp.git] / policy-core / src / main / java / org / onap / policy / drools / util / KieUtils.java
1 /*-
2  * ============LICENSE_START=======================================================
3  * ONAP
4  * ================================================================================
5  * Copyright (C) 2018-2021, 2023 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.drools.util;
22
23 import java.io.File;
24 import java.io.IOException;
25 import java.io.InputStream;
26 import java.net.URL;
27 import java.nio.file.Files;
28 import java.util.ArrayList;
29 import java.util.Collection;
30 import java.util.Collections;
31 import java.util.Enumeration;
32 import java.util.HashSet;
33 import java.util.List;
34 import java.util.Optional;
35 import java.util.stream.Collectors;
36 import lombok.AccessLevel;
37 import lombok.NoArgsConstructor;
38 import lombok.NonNull;
39 import org.apache.commons.io.IOUtils;
40 import org.drools.compiler.kie.builder.impl.InternalKieModule;
41 import org.drools.compiler.kproject.models.KieModuleModelImpl;
42 import org.drools.kiesession.rulebase.SessionsAwareKnowledgeBase;
43 import org.kie.api.KieBase;
44 import org.kie.api.KieServices;
45 import org.kie.api.builder.KieBuilder;
46 import org.kie.api.builder.KieFileSystem;
47 import org.kie.api.builder.Message;
48 import org.kie.api.builder.ReleaseId;
49 import org.kie.api.definition.KiePackage;
50 import org.kie.api.definition.rule.Rule;
51 import org.kie.api.runtime.KieContainer;
52 import org.kie.api.runtime.KieSession;
53 import org.kie.scanner.KieMavenRepository;
54 import org.slf4j.Logger;
55 import org.slf4j.LoggerFactory;
56
57 /**
58  * Kie related utilities.
59  */
60 @NoArgsConstructor(access = AccessLevel.PRIVATE)
61 public final class KieUtils {
62
63     private static final Logger logger = LoggerFactory.getLogger(KieUtils.class);
64
65     // resource names used by 'resourceToPackages'
66     private static final String RESOURCE_PREFIX = "src/main/resources/drools";
67     private static final String RESOURCE_SUFFIX = ".drl";
68
69     /**
70      * Installs a rules artifact in the local maven repository.
71      */
72     public static ReleaseId installArtifact(File kmodule, File pom, String drlKJarPath, @NonNull List<File> drls)
73         throws IOException {
74         var kieModule = KieModuleModelImpl.fromXML(kmodule);
75
76         final var kieFileSystem = KieServices.Factory.get().newKieFileSystem();
77         kieFileSystem.writeKModuleXML(kieModule.toXML());
78         kieFileSystem.writePomXML(new String(Files.readAllBytes(pom.toPath())));
79         for (File drl : drls) {
80             kieFileSystem.write(drlKJarPath + drl.getName(), new String(Files.readAllBytes(drl.toPath())));
81         }
82
83         var kieBuilder = build(kieFileSystem);
84         return getReleaseId(kieBuilder, pom);
85     }
86
87     /**
88      * Installs a rules artifact in the local maven repository.
89      */
90     public static ReleaseId installArtifact(File kmodule, File pom, String drlKJarPath, File drl)
91         throws IOException {
92         return installArtifact(kmodule, pom, drlKJarPath, Collections.singletonList(drl));
93     }
94
95     private static ReleaseId getReleaseId(KieBuilder kieBuilder, File pomFile) {
96         var releaseId = kieBuilder.getKieModule().getReleaseId();
97         KieMavenRepository
98             .getKieMavenRepository()
99             .installArtifact(releaseId,
100                 (InternalKieModule) kieBuilder.getKieModule(),
101                 pomFile);
102         return releaseId;
103     }
104
105     /**
106      * Get Knowledge Bases.
107      */
108     public static List<String> getBases(KieContainer container) {
109         return new ArrayList<>(container.getKieBaseNames());
110     }
111
112     /**
113      * Get Packages.
114      */
115     public static List<KiePackage> getPackages(KieContainer container) {
116         return getBases(container).stream()
117             .flatMap(base -> container.getKieBase(base).getKiePackages().stream())
118             .collect(Collectors.toList());
119     }
120
121     /**
122      * Get Package Names.
123      */
124     public static List<String> getPackageNames(KieContainer container) {
125         return getPackages(container).stream()
126             .map(KiePackage::getName)
127             .collect(Collectors.toList());
128     }
129
130     /**
131      * Get Rules.
132      */
133     public static List<Rule> getRules(KieContainer container) {
134         return getPackages(container).stream()
135             .flatMap(kiePackage -> kiePackage.getRules().stream())
136             .collect(Collectors.toList());
137     }
138
139     /**
140      * Get Rule Names.
141      */
142     public static List<String> getRuleNames(KieContainer container) {
143         return getRules(container).stream()
144             .map(Rule::getName)
145             .collect(Collectors.toList());
146     }
147
148     /**
149      * Get Facts.
150      */
151     public static List<Object> getFacts(KieSession session) {
152         return session.getFactHandles().stream()
153             .map(session::getObject)
154             .collect(Collectors.toList());
155     }
156
157     /**
158      * Create Container.
159      */
160     public static KieContainer createContainer(ReleaseId releaseId) {
161         return KieServices.Factory.get().newKieContainer(releaseId);
162     }
163
164     private static KieBuilder build(KieFileSystem kieFileSystem) {
165         var kieBuilder = KieServices.Factory.get().newKieBuilder(kieFileSystem);
166         List<Message> messages = kieBuilder.buildAll().getResults().getMessages();
167         if (messages != null && !messages.isEmpty()) {
168             throw new IllegalArgumentException(messages.toString());
169         }
170         return kieBuilder;
171     }
172
173     /**
174      * Find all Drools resources matching a specified name, and generate a
175      * collection of 'KiePackage' instances from those resources.
176      *
177      * @param classLoader the class loader to use when finding resources, or
178      *     when building the 'KiePackage' collection
179      * @param resourceName the resource name, without a leading '/' character
180      * @return a collection of 'KiePackage' instances, or 'null' in case of
181      *     failure
182      */
183     public static Optional<Collection<KiePackage>> resourceToPackages(ClassLoader classLoader, String resourceName) {
184
185         // find all resources matching 'resourceName'
186         Enumeration<URL> resources;
187         try {
188             resources = classLoader.getResources(resourceName);
189         } catch (IOException e) {
190             logger.error("Exception fetching resources: {}", resourceName, e);
191             return Optional.empty();
192         }
193         if (!resources.hasMoreElements()) {
194             // no resources found
195             return Optional.empty();
196         }
197
198         // generate a 'KieFileSystem' from these resources
199         var kieServices = KieServices.Factory.get();
200         var kfs = kieServices.newKieFileSystem();
201         var index = 1;
202         while (resources.hasMoreElements()) {
203             var url = resources.nextElement();
204             try (InputStream is = url.openStream()) {
205                 // convert a resource to a byte array
206                 byte[] drl = IOUtils.toByteArray(is);
207
208                 // add a new '.drl' entry to the KieFileSystem
209                 kfs.write(RESOURCE_PREFIX + index++ + RESOURCE_SUFFIX, drl);
210             } catch (IOException e) {
211                 logger.error("Couldn't read in {}", url, e);
212                 return Optional.empty();
213             }
214         }
215
216         // do a build of the 'KieFileSystem'
217         var builder = kieServices.newKieBuilder(kfs, classLoader);
218         builder.buildAll();
219         List<Message> results = builder.getResults().getMessages();
220         if (!results.isEmpty()) {
221             logger.error("Kie build failed:\n{}", results);
222             return Optional.empty();
223         }
224
225         // generate a KieContainer, and extract the package list
226         return Optional.of(kieServices.newKieContainer(builder.getKieModule().getReleaseId(), classLoader)
227                .getKieBase().getKiePackages());
228     }
229
230     /**
231      * Add a collection of 'KiePackage' instances to the specified 'KieBase'.
232      *
233      * @param kieBase the 'KieBase' instance to add the packages to
234      * @param kiePackages the collection of packages to add
235      */
236     public static void addKiePackages(KieBase kieBase, Collection<KiePackage> kiePackages) {
237         HashSet<KiePackage> stillNeeded = new HashSet<>(kiePackages);
238
239         // update 'stillNeeded' by removing any packages we already have
240         stillNeeded.removeAll(kieBase.getKiePackages());
241
242         if (!stillNeeded.isEmpty()) {
243             // there are still packages we need to add --
244             // this code makes use of an internal class and method
245             ((SessionsAwareKnowledgeBase) kieBase).addPackages(stillNeeded);
246         }
247     }
248 }