Removing deprecated DMAAP library
[policy/drools-pdp.git] / policy-core / src / main / java / org / onap / policy / drools / core / PolicyContainer.java
1 /*
2  * ============LICENSE_START=======================================================
3  * policy-core
4  * ================================================================================
5  * Copyright (C) 2017-2021 AT&T Intellectual Property. All rights reserved.
6  * Modifications Copyright (C) 2018 Samsung Electronics Co., Ltd.
7  * Modifications Copyright (C) 2024 Nordix Foundation.
8  * ================================================================================
9  * Licensed under the Apache License, Version 2.0 (the "License");
10  * you may not use this file except in compliance with the License.
11  * You may obtain a copy of the License at
12  *
13  *      http://www.apache.org/licenses/LICENSE-2.0
14  *
15  * Unless required by applicable law or agreed to in writing, software
16  * distributed under the License is distributed on an "AS IS" BASIS,
17  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
18  * See the License for the specific language governing permissions and
19  * limitations under the License.
20  * ============LICENSE_END=========================================================
21  */
22
23 package org.onap.policy.drools.core;
24
25 import java.util.Collection;
26 import java.util.HashMap;
27 import java.util.HashSet;
28 import java.util.List;
29 import java.util.concurrent.ConcurrentHashMap;
30 import lombok.Getter;
31 import org.kie.api.KieServices;
32 import org.kie.api.builder.KieScanner;
33 import org.kie.api.builder.Message;
34 import org.kie.api.builder.ReleaseId;
35 import org.kie.api.builder.Results;
36 import org.kie.api.definition.KiePackage;
37 import org.kie.api.runtime.KieContainer;
38 import org.kie.api.runtime.KieSession;
39 import org.onap.policy.common.capabilities.Startable;
40 import org.onap.policy.drools.util.KieUtils;
41 import org.slf4j.Logger;
42 import org.slf4j.LoggerFactory;
43
44 /**
45  * This class is a wrapper around 'KieContainer', which adds the ability to automatically create and
46  * track KieSession instances.
47  */
48 public class PolicyContainer implements Startable {
49     // get an instance of logger
50     private static final Logger logger = LoggerFactory.getLogger(PolicyContainer.class);
51     // 'KieServices' singleton
52     private static KieServices kieServices = KieServices.Factory.get();
53
54     // set of all 'PolicyContainer' instances
55     private static final HashSet<PolicyContainer> containers = new HashSet<>();
56
57     // maps feature objects to per-PolicyContainer data
58     private ConcurrentHashMap<Object, Object> adjuncts = new ConcurrentHashMap<>();
59
60     // 'KieContainer' associated with this 'PolicyContainer'
61     @Getter
62     private KieContainer kieContainer;
63
64     // indicates whether the PolicyContainer is 'started'
65     // (started = sessions created, threads running)
66     private volatile boolean isStarted = false;
67
68     // maps session name into the associated 'PolicySession' instance
69     private final HashMap<String, PolicySession> sessions = new HashMap<>();
70
71     // if not null, this is a 'KieScanner' looking for updates
72     private KieScanner scanner = null;
73
74     // indicates whether the scanner has been started
75     // (it can block for a long time)
76     private boolean scannerStarted = false;
77
78     private static final String ERROR_STRING = "ERROR: Feature API: {}";
79
80     // packages that are included in all 'KieContainer' instances
81     private static Collection<KiePackage> commonPackages = null;
82
83     // all resources with this name consist of rules that are added to each container
84     private static final String COMMON_PACKAGES_RESOURCE_NAME = "META-INF/drools/drl";
85
86     /**
87      * uses 'groupId', 'artifactId' and 'version', and fetches the associated artifact and remaining
88      * dependencies from the Maven repository to create the 'PolicyContainer' and associated
89      * 'KieContainer'.
90      *
91      * <p>An exception occurs if the creation of the 'KieContainer' fails.
92      *
93      * @param groupId the 'groupId' associated with the artifact
94      * @param artifactId the artifact name
95      * @param version a comma-separated list of possible versions
96      */
97     public PolicyContainer(String groupId, String artifactId, String version) {
98         this(kieServices.newReleaseId(groupId, artifactId, version));
99     }
100
101     /**
102      * uses the 'groupId', 'artifactId' and 'version' information in 'ReleaseId', and fetches the
103      * associated artifact and remaining dependencies from the Maven repository to create the
104      * 'PolicyContainer' and associated 'KieContainer'.
105      *
106      * <p>An exception occurs if the creation of the 'KieContainer' fails.
107      *
108      * @param releaseId indicates the artifact that is to be installed in this container
109      */
110     public PolicyContainer(ReleaseId releaseId) {
111         var newReleaseId = releaseId;
112         if (newReleaseId.getVersion().contains(",")) {
113             // this is actually a comma-separated list of release ids
114             newReleaseId =
115                     loadArtifact(newReleaseId.getGroupId(), newReleaseId.getArtifactId(), newReleaseId.getVersion());
116         } else {
117             kieContainer = kieServices.newKieContainer(newReleaseId);
118         }
119
120         // add common KiePackage instances
121         addCommonPackages();
122         synchronized (containers) {
123             if (newReleaseId != null) {
124                 logger.info("Add a new kieContainer in containers: releaseId: {}", newReleaseId);
125             } else {
126                 logger.warn("input releaseId is null");
127             }
128             containers.add(this);
129         }
130         // 'startScanner(releaseId)' was called at this point, but we have seen
131         // at least one case where the Drools container was repeatedly updated
132         // every 60 seconds. It isn't clear what conditions resulted in this
133         // behavior, so the call was removed. If needed, it can be explicitly
134         // called from a feature.
135     }
136
137     /**
138      * Load an artifact into a new KieContainer. This method handles the case where the 'version' is
139      * actually a comma-separated list of versions.
140      *
141      * @param groupId the 'groupId' associated with the artifact
142      * @param artifactId the artifact name
143      * @param version a comma-separated list of possible versions
144      */
145     private ReleaseId loadArtifact(String groupId, String artifactId, String version) {
146         String[] versions = version.split(",");
147         if (versions.length > 1) {
148             logger.info("Multiple KieContainer versions are specified: {}", version);
149         }
150
151         // indicates a 'newKieContainer' call failed
152         RuntimeException exception = null;
153
154         // set prior to every 'newKieContainer' invocation
155         // (if we are able to create the container, it will be the last
156         // one that was successful)
157         ReleaseId releaseId = null;
158         for (String ver : versions) {
159             try {
160                 // Create a 'ReleaseId' object describing the artifact, and
161                 // create a 'KieContainer' based upon it.
162                 logger.info("Create new KieContainer start, version = {} ...", ver);
163
164                 releaseId = kieServices.newReleaseId(groupId, artifactId, ver);
165                 kieContainer = kieServices.newKieContainer(releaseId);
166
167                 // clear any exception, and break out of the loop
168                 exception = null;
169                 break;
170             } catch (RuntimeException e) {
171                 exception = e;
172             }
173         }
174         if (exception != null) {
175             // all the 'newKieContainer' invocations failed -- throw the
176             // most recent exception
177             throw exception;
178         }
179         return releaseId;
180     }
181
182     /**
183      * Get name.
184      *
185      * @return the name of the container, which is the String equivalent of the 'ReleaseId'. It has
186      *         the form:
187      *
188      *         (groupId + ":" + artifactId + ":" + version)
189      *
190      *         Note that the name changes after a successful call to 'updateToVersion', although
191      *         typically only the 'version' part changes.
192      */
193     public String getName() {
194         return kieContainer.getReleaseId().toString();
195     }
196
197     /**
198      * Get class loader.
199      *
200      * @return the 'ClassLoader' associated with the 'KieContainer' instance
201      */
202     public ClassLoader getClassLoader() {
203         return kieContainer.getClassLoader();
204     }
205
206     /**
207      * Get group Id.
208      *
209      * @return the Maven GroupId of the top-level artifact wrapped by the container.
210      */
211     public String getGroupId() {
212         return kieContainer.getReleaseId().getGroupId();
213     }
214
215     /**
216      * Get artifact id.
217      *
218      * @return the Maven ArtifactId of the top-level artifact wrapped by the container.
219      */
220     public String getArtifactId() {
221         return kieContainer.getReleaseId().getArtifactId();
222     }
223
224     /**
225      * Get version.
226      *
227      * @return the version of the top-level artifact wrapped by the container (this may change as
228      *         updates occur)
229      */
230     public String getVersion() {
231         return kieContainer.getReleaseId().getVersion();
232     }
233
234     /**
235      * Fetch the named 'PolicySession'.
236      *
237      * @param name the name of the KieSession (which is also the name of the associated
238      *        PolicySession)
239      * @return a PolicySession if found, 'null' if not
240      */
241     public PolicySession getPolicySession(String name) {
242         return sessions.get(name);
243     }
244
245     /**
246      * Internal method to create a PolicySession, possibly restoring it from persistent storage.
247      *
248      * @param name of the KieSession and PolicySession
249      * @param kieBaseName name of the associated 'KieBase' instance
250      * @return a new or existing PolicySession, or 'null' if not found
251      */
252     private PolicySession activatePolicySession(String name, String kieBaseName) {
253         synchronized (sessions) {
254             logger.info("activatePolicySession:name :{}", name);
255             PolicySession session = sessions.computeIfAbsent(name, key -> makeSession(name, kieBaseName));
256
257             logger.info("activatePolicySession:session - {} is returned.",
258                             session == null ? "null" : session.getFullName());
259             return session;
260         }
261     }
262
263     private PolicySession makeSession(String name, String kieBaseName) {
264         PolicySession session = null;
265         KieSession kieSession = null;
266
267         // loop through all the features, and give each one
268         // a chance to create the 'KieSession'
269         for (PolicySessionFeatureApi feature : PolicySessionFeatureApiConstants.getImpl().getList()) {
270             try {
271                 if ((kieSession = feature.activatePolicySession(this, name, kieBaseName)) != null) {
272                     break;
273                 }
274             } catch (Exception e) {
275                 logger.error(ERROR_STRING, feature.getClass().getName(), e);
276             }
277         }
278
279         // if none of the features created the session, create one now
280         if (kieSession == null) {
281             kieSession = kieContainer.newKieSession(name);
282         }
283
284         if (kieSession != null) {
285             // creation of 'KieSession' was successful - build
286             // a PolicySession
287             session = new PolicySession(name, this, kieSession);
288
289             // notify features
290             for (PolicySessionFeatureApi feature : PolicySessionFeatureApiConstants.getImpl().getList()) {
291                 try {
292                     feature.newPolicySession(session);
293                 } catch (Exception e) {
294                     logger.error(ERROR_STRING, feature.getClass().getName(), e);
295                 }
296             }
297             logger.info("activatePolicySession:new session was added in sessions with name {}", name);
298         }
299
300         return session;
301     }
302
303     /**
304      * This creates a 'PolicySession' instance within this 'PolicyContainer', and ties it to the
305      * specified 'KieSession'. 'name' must not currently exist within the 'PolicyContainer', and the
306      * 'KieBase' object associated with 'KieSession' must belong to the 'KieContainer'. This method
307      * provides a way for 'KieSession' instances that are created programmatically to fit into this
308      * framework.
309      *
310      * @param name the name for the new 'PolicySession'
311      * @param kieSession a 'KieSession' instance, that will be included in this infrastructure
312      * @return the new 'PolicySession'
313      * @throws IllegalArgumentException if 'kieSession' does not reside within this container
314      * @throws IllegalStateException if a 'PolicySession' already exists with this name
315      */
316     public PolicySession adoptKieSession(String name, KieSession kieSession) {
317
318         if (name == null) {
319             logger.warn("adoptKieSession:input name is null");
320             throw new IllegalArgumentException("KieSession input name is null " + getName());
321         } else if (kieSession == null) {
322             logger.warn("adoptKieSession:input kieSession is null");
323             throw new IllegalArgumentException("KieSession '" + name + "' is null " + getName());
324         } else {
325             logger.info("adoptKieSession:name: {} kieSession: {}", name, kieSession);
326         }
327         // fetch KieBase, and verify it belongs to this KieContainer
328         var match = false;
329         var kieBase = kieSession.getKieBase();
330         logger.info("adoptKieSession:kieBase: {}", kieBase);
331         for (String kieBaseName : kieContainer.getKieBaseNames()) {
332             logger.info("adoptKieSession:kieBaseName: {}", kieBaseName);
333             if (kieBase == kieContainer.getKieBase(kieBaseName)) {
334                 match = true;
335                 break;
336             }
337         }
338         logger.info("adoptKieSession:match {}", match);
339         // if we don't have a match yet, the last chance is to look at the
340         // default KieBase, if it exists
341         if (!match && kieBase != kieContainer.getKieBase()) {
342             throw new IllegalArgumentException(
343                     "KieSession '" + name + "' does not reside within container " + getName());
344         }
345
346         synchronized (sessions) {
347             if (sessions.get(name) != null) {
348                 throw new IllegalStateException("PolicySession '" + name + "' already exists");
349             }
350
351             // create the new 'PolicySession', add it to the table,
352             // and return the object to the caller
353             logger.info("adoptKieSession:create a new policySession with name {}", name);
354             var policySession = new PolicySession(name, this, kieSession);
355             sessions.put(name, policySession);
356
357             // notify features
358             for (PolicySessionFeatureApi feature : PolicySessionFeatureApiConstants.getImpl().getList()) {
359                 try {
360                     feature.newPolicySession(policySession);
361                 } catch (Exception e) {
362                     logger.error(ERROR_STRING, feature.getClass().getName(), e);
363                 }
364             }
365             return policySession;
366         }
367     }
368
369     /**
370      * This call 'KieContainer.updateToVersion()', and returns the associated response as a String.
371      * If successful, the name of this 'PolicyContainer' changes to match the new version.
372      *
373      * @param newVersion this is the version to update to (the 'groupId' and 'artifactId' remain the
374      *        same)
375      * @return the list of messages associated with the update (not sure if this can be 'null', or
376      *         how to determine success/failure)
377      */
378     public String updateToVersion(String newVersion) {
379         var releaseId = kieContainer.getReleaseId();
380         var results = this.updateToVersion(
381                 kieServices.newReleaseId(releaseId.getGroupId(), releaseId.getArtifactId(), newVersion));
382
383         List<Message> messages = results == null ? null : results.getMessages();
384         return messages == null ? null : messages.toString();
385     }
386
387     /**
388      * This calls 'KieContainer.updateToVersion()', and returns the associated response. If
389      * successful, the name of this 'PolicyContainer' changes to match the new version.
390      *
391      * @param releaseId the new artifact (usually new version) to be installed
392      * @return the 'Results' parameter from 'KieContainer.updateToVersion'
393      */
394     public Results updateToVersion(ReleaseId releaseId) {
395         if (releaseId == null) {
396             logger.warn("updateToVersion:input releaseId is null");
397         } else {
398             logger.info("updateToVersion:releaseId {}", releaseId);
399         }
400
401         // stop all session threads
402         for (PolicySession session : sessions.values()) {
403             session.stopThread();
404         }
405
406         // update the version
407         var results = kieContainer.updateToVersion(releaseId);
408
409
410         // add common KiePackage instances
411         addCommonPackages();
412
413         // restart all session threads, and notify the sessions
414         for (PolicySession session : sessions.values()) {
415             session.startThread();
416             session.updated();
417         }
418
419         return results;
420     }
421
422     /**
423      * Get policy containers.
424      *
425      * @return all existing 'PolicyContainer' instances
426      */
427     public static Collection<PolicyContainer> getPolicyContainers() {
428         synchronized (containers) {
429             return new HashSet<>(containers);
430         }
431     }
432
433     /**
434      * Get policy sessions.
435      *
436      * @return all the 'PolicySession' instances
437      */
438     public Collection<PolicySession> getPolicySessions() {
439         // KLUDGE WARNING: this is a temporary workaround -- if there are
440         // no features, we don't have persistence, and 'activate' is never
441         // called. In this case, make sure the container is started.
442         if (PolicySessionFeatureApiConstants.getImpl().getList().isEmpty()) {
443             start();
444         }
445
446         // return current set of PolicySessions
447         synchronized (sessions) {
448             return new HashSet<>(sessions.values());
449         }
450     }
451
452     /**
453      * This method will start a 'KieScanner' (if not currently running), provided that the ReleaseId
454      * version is 'LATEST' or 'RELEASE', or refers to a SNAPSHOT version.
455      *
456      * @param releaseId the release id used to create the container
457      */
458     public synchronized void startScanner(ReleaseId releaseId) {
459         String version = releaseId.getVersion();
460
461         if (scannerStarted || scanner != null || version == null) {
462             return;
463         }
464
465         if (!("LATEST".equals(version) || "RELEASE".equals(version) || version.endsWith("-SNAPSHOT"))) {
466             return;
467         }
468
469         // create the scanner, and poll at 60 second intervals
470         try {
471             scannerStarted = true;
472
473             // start this in a separate thread -- it can block for a long time
474             new Thread("Scanner Starter " + getName()) {
475                 @Override
476                 public void run() {
477                     scanner = kieServices.newKieScanner(kieContainer);
478                     scanner.start(60000L);
479                 }
480             }.start();
481         } catch (Exception e) {
482             // sometimes the scanner initialization fails for some reason
483             logger.error("startScanner error", e);
484         }
485     }
486
487     /**
488      * Insert a fact into a specific named session.
489      *
490      * @param name this is the session name
491      * @param object this is the fact to be inserted into the session
492      * @return 'true' if the named session was found, 'false' if not
493      */
494     public boolean insert(String name, Object object) {
495         // TODO: Should the definition of 'name' be expanded to include an
496         // alternate entry point as well? For example, 'name.entryPoint' (or
497         // something other than '.' if that is a problem).
498         synchronized (sessions) {
499             PolicySession session = sessions.get(name);
500             if (session != null) {
501                 session.insertDrools(object);
502                 return true;
503             }
504         }
505         return false;
506     }
507
508     /**
509      * Insert a fact into all sessions associated with this container.
510      *
511      * @param object this is the fact to be inserted into the sessions
512      * @return 'true' if the fact was inserted into at least one session, 'false' if not
513      */
514     public boolean insertAll(Object object) {
515         var rval = false;
516         synchronized (sessions) {
517             for (PolicySession session : sessions.values()) {
518                 session.insertDrools(object);
519                 rval = true;
520             }
521         }
522         return rval;
523     }
524
525     /*=======================*/
526     /* 'Startable' interface */
527     /*=======================*/
528
529     /**
530      * {@inheritDoc}.
531      */
532     @Override
533     public synchronized boolean start() {   // NOSONAR
534         /*
535          * disabling sonar about returning the same value, because we prefer the code to
536          * be structured this way
537          */
538
539         if (isStarted) {
540             return true;
541         }
542
543         // This will create all 'PolicySession' instances specified in the
544         // 'kmodule.xml' file that don't exist yet
545         for (String kieBaseName : kieContainer.getKieBaseNames()) {
546             for (String kieSessionName : kieContainer.getKieSessionNamesInKieBase(kieBaseName)) {
547                 // if the 'PolicySession' does not currently exist, this method
548                 // call will attempt to create it
549                 var session = activatePolicySession(kieSessionName, kieBaseName);
550                 if (session != null) {
551                     session.startThread();
552                 }
553             }
554         }
555         isStarted = true;
556         return true;
557     }
558
559     /**
560      * {@inheritDoc}.
561      */
562     @Override
563     public synchronized boolean stop() {
564         return (!isStarted || doStop());
565     }
566
567     private boolean doStop() {
568         Collection<PolicySession> localSessions;
569
570         synchronized (sessions) {
571             // local set containing all of the sessions
572             localSessions = new HashSet<>(sessions.values());
573
574             // clear the 'name->session' map in 'PolicyContainer'
575             sessions.clear();
576         }
577         for (PolicySession session : localSessions) {
578             // stop session thread
579             session.stopThread();
580
581             // free KieSession resources
582             session.getKieSession().dispose();
583
584             // notify features
585             for (PolicySessionFeatureApi feature : PolicySessionFeatureApiConstants.getImpl().getList()) {
586                 try {
587                     feature.disposeKieSession(session);
588                 } catch (Exception e) {
589                     logger.error(ERROR_STRING, feature.getClass().getName(), e);
590                 }
591             }
592         }
593         isStarted = false;
594
595         return true;
596     }
597
598     /**
599      * {@inheritDoc}.
600      */
601     @Override
602     public synchronized void shutdown() {
603         // Note that this method does not call 'destroy' on the 'KieSession'
604         // instances, which would remove any associated information in persistent
605         // storage. Should it do this?
606
607         stop();
608         synchronized (containers) {
609             containers.remove(this);
610         }
611
612         // How do we free the resources associated with the KieContainer?
613         // Is garbage collection sufficient?
614     }
615
616     /**
617      * {@inheritDoc}.
618      */
619     @Override
620     public boolean isAlive() {
621         return isStarted;
622     }
623
624     /**
625      * This method is similar to 'shutdown', but it also frees any persistence resources as well.
626      */
627     public synchronized void destroy() {
628         // we need all KieSession instances running in order to free
629         // resources associated with persistence
630         start();
631         Collection<PolicySession> localSessions;
632
633         synchronized (sessions) {
634             // local set containing all of the sessions
635             localSessions = new HashSet<>(sessions.values());
636
637             // clear the 'name->session' map in 'PolicyContainer'
638             sessions.clear();
639         }
640         for (PolicySession session : localSessions) {
641             // stop session thread
642             session.stopThread();
643
644             // free KieSession resources
645             session.getKieSession().destroy();
646
647             // notify features
648             for (PolicySessionFeatureApi feature : PolicySessionFeatureApiConstants.getImpl().getList()) {
649                 try {
650                     feature.destroyKieSession(session);
651                 } catch (Exception e) {
652                     logger.error(ERROR_STRING, feature.getClass().getName(), e);
653                 }
654             }
655         }
656         isStarted = false;
657
658         synchronized (containers) {
659             containers.remove(this);
660         }
661
662         // How do we free the resources associated with the KieContainer?
663         // Is garbage collection sufficient?
664     }
665
666     /**
667      * This method is called when the host goes from the 'standby->active' state.
668      */
669     public static void activate() {
670         // start all of the 'PolicyContainer' instances
671         for (PolicyContainer container : containers) {
672             try {
673                 container.start();
674             } catch (Exception e) {
675                 logger.error("PolicyContainer.start() error in activate", e);
676             }
677         }
678     }
679
680     /**
681      * This method is called when the host goes from the 'active->standby' state.
682      */
683     public static void deactivate() {
684         // deactivate all of the 'PolicyContainer' instances
685         for (PolicyContainer container : containers) {
686             try {
687                 container.stop();
688             } catch (Exception e) {
689                 logger.error("PolicyContainer.start() error in deactivate", e);
690             }
691         }
692     }
693
694     /**
695      * This method does the following:
696      *
697      * <p>1) Initializes logging 2) Starts the DroolsPDP Integrity Monitor 3) Initilaizes persistence
698      *
699      * <p>It no longer reads in properties files, o creates 'PolicyContainer' instances.
700      *
701      * @param args standard 'main' arguments, which are currently ignored
702      */
703     public static void globalInit(String[] args) {
704         var configDir = "config";
705         logger.info("PolicyContainer.main: configDir={}", configDir);
706
707         // invoke 'globalInit' on all the features
708         for (PolicySessionFeatureApi feature : PolicySessionFeatureApiConstants.getImpl().getList()) {
709             try {
710                 feature.globalInit(args, configDir);
711             } catch (Exception e) {
712                 logger.error(ERROR_STRING, feature.getClass().getName(), e);
713             }
714         }
715     }
716
717     /**
718      * Fetch the adjunct object associated with a given feature.
719      *
720      * @param object this is typically the singleton feature object that is used as a key, but it
721      *        might also be useful to use nested objects within the feature as keys.
722      * @return a feature-specific object associated with the key, or 'null' if it is not found.
723      */
724     public Object getAdjunct(Object object) {
725         return adjuncts.get(object);
726     }
727
728     /**
729      * Store the adjunct object associated with a given feature.
730      *
731      * @param object this is typically the singleton feature object that is used as a key, but it
732      *        might also be useful to use nested objects within the feature as keys.
733      * @param value a feature-specific object associated with the key, or 'null' if the
734      *        feature-specific object should be removed
735      */
736     public void setAdjunct(Object object, Object value) {
737         if (value == null) {
738             adjuncts.remove(object);
739         } else {
740             adjuncts.put(object, value);
741         }
742     }
743
744     /**
745      * Add 'KiePackages' that are common to all containers.
746      */
747     private void addCommonPackages() {
748         // contains the list of 'KiePackages' to add to each 'KieBase'
749         Collection<KiePackage> kiePackages;
750         synchronized (PolicyContainer.class) {
751             if (commonPackages == null) {
752                 commonPackages = KieUtils.resourceToPackages(
753                     PolicyContainer.class.getClassLoader(), COMMON_PACKAGES_RESOURCE_NAME).orElse(null);
754                 if (commonPackages == null) {
755                     // a problem occurred, which has already been logged --
756                     // just store an empty collection, so we don't keep doing
757                     // this over again
758                     commonPackages = new HashSet<>();
759                     return;
760                 }
761             }
762             kiePackages = commonPackages;
763         }
764
765         // if we reach this point, 'kiePackages' contains a non-null list
766         // of packages to add
767         for (String name : kieContainer.getKieBaseNames()) {
768             KieUtils.addKiePackages(kieContainer.getKieBase(name), kiePackages);
769         }
770     }
771 }