2 * ============LICENSE_START=======================================================
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
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.util;
24 import java.io.IOException;
25 import java.io.InputStream;
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;
58 * Kie related utilities.
60 @NoArgsConstructor(access = AccessLevel.PRIVATE)
61 public final class KieUtils {
63 private static final Logger logger = LoggerFactory.getLogger(KieUtils.class);
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";
70 * Installs a rules artifact in the local maven repository.
72 public static ReleaseId installArtifact(File kmodule, File pom, String drlKJarPath, @NonNull List<File> drls)
74 var kieModule = KieModuleModelImpl.fromXML(kmodule);
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())));
83 var kieBuilder = build(kieFileSystem);
84 return getReleaseId(kieBuilder, pom);
88 * Installs a rules artifact in the local maven repository.
90 public static ReleaseId installArtifact(File kmodule, File pom, String drlKJarPath, File drl)
92 return installArtifact(kmodule, pom, drlKJarPath, Collections.singletonList(drl));
95 private static ReleaseId getReleaseId(KieBuilder kieBuilder, File pomFile) {
96 var releaseId = kieBuilder.getKieModule().getReleaseId();
98 .getKieMavenRepository()
99 .installArtifact(releaseId,
100 (InternalKieModule) kieBuilder.getKieModule(),
106 * Get Knowledge Bases.
108 public static List<String> getBases(KieContainer container) {
109 return new ArrayList<>(container.getKieBaseNames());
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());
124 public static List<String> getPackageNames(KieContainer container) {
125 return getPackages(container).stream()
126 .map(KiePackage::getName)
127 .collect(Collectors.toList());
133 public static List<Rule> getRules(KieContainer container) {
134 return getPackages(container).stream()
135 .flatMap(kiePackage -> kiePackage.getRules().stream())
136 .collect(Collectors.toList());
142 public static List<String> getRuleNames(KieContainer container) {
143 return getRules(container).stream()
145 .collect(Collectors.toList());
151 public static List<Object> getFacts(KieSession session) {
152 return session.getFactHandles().stream()
153 .map(session::getObject)
154 .collect(Collectors.toList());
160 public static KieContainer createContainer(ReleaseId releaseId) {
161 return KieServices.Factory.get().newKieContainer(releaseId);
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());
174 * Find all Drools resources matching a specified name, and generate a
175 * collection of 'KiePackage' instances from those resources.
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
183 public static Optional<Collection<KiePackage>> resourceToPackages(ClassLoader classLoader, String resourceName) {
185 // find all resources matching 'resourceName'
186 Enumeration<URL> resources;
188 resources = classLoader.getResources(resourceName);
189 } catch (IOException e) {
190 logger.error("Exception fetching resources: {}", resourceName, e);
191 return Optional.empty();
193 if (!resources.hasMoreElements()) {
194 // no resources found
195 return Optional.empty();
198 // generate a 'KieFileSystem' from these resources
199 var kieServices = KieServices.Factory.get();
200 var kfs = kieServices.newKieFileSystem();
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);
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();
216 // do a build of the 'KieFileSystem'
217 var builder = kieServices.newKieBuilder(kfs, classLoader);
219 List<Message> results = builder.getResults().getMessages();
220 if (!results.isEmpty()) {
221 logger.error("Kie build failed:\n{}", results);
222 return Optional.empty();
225 // generate a KieContainer, and extract the package list
226 return Optional.of(kieServices.newKieContainer(builder.getKieModule().getReleaseId(), classLoader)
227 .getKieBase().getKiePackages());
231 * Add a collection of 'KiePackage' instances to the specified 'KieBase'.
233 * @param kieBase the 'KieBase' instance to add the packages to
234 * @param kiePackages the collection of packages to add
236 public static void addKiePackages(KieBase kieBase, Collection<KiePackage> kiePackages) {
237 HashSet<KiePackage> stillNeeded = new HashSet<>(kiePackages);
239 // update 'stillNeeded' by removing any packages we already have
240 stillNeeded.removeAll(kieBase.getKiePackages());
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);