2  * ============LICENSE_START=======================================================
 
   4  * ================================================================================
 
   5  * Copyright (C) 2017-2019 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.controller.internal;
 
  23 import com.fasterxml.jackson.annotation.JsonIgnore;
 
  24 import com.fasterxml.jackson.annotation.JsonProperty;
 
  25 import java.util.ArrayList;
 
  26 import java.util.Collection;
 
  27 import java.util.HashMap;
 
  28 import java.util.List;
 
  30 import java.util.Objects;
 
  31 import java.util.stream.Collectors;
 
  32 import org.apache.commons.collections4.queue.CircularFifoQueue;
 
  33 import org.checkerframework.checker.nullness.qual.NonNull;
 
  34 import org.drools.core.ClassObjectFilter;
 
  35 import org.kie.api.definition.KiePackage;
 
  36 import org.kie.api.definition.rule.Query;
 
  37 import org.kie.api.runtime.KieSession;
 
  38 import org.kie.api.runtime.rule.FactHandle;
 
  39 import org.kie.api.runtime.rule.QueryResults;
 
  40 import org.kie.api.runtime.rule.QueryResultsRow;
 
  41 import org.onap.policy.common.endpoints.event.comm.TopicSink;
 
  42 import org.onap.policy.common.gson.annotation.GsonJsonIgnore;
 
  43 import org.onap.policy.common.gson.annotation.GsonJsonProperty;
 
  44 import org.onap.policy.drools.controller.DroolsController;
 
  45 import org.onap.policy.drools.core.PolicyContainer;
 
  46 import org.onap.policy.drools.core.PolicySession;
 
  47 import org.onap.policy.drools.core.jmx.PdpJmx;
 
  48 import org.onap.policy.drools.features.DroolsControllerFeatureAPI;
 
  49 import org.onap.policy.drools.protocol.coders.EventProtocolCoder;
 
  50 import org.onap.policy.drools.protocol.coders.EventProtocolParams;
 
  51 import org.onap.policy.drools.protocol.coders.JsonProtocolFilter;
 
  52 import org.onap.policy.drools.protocol.coders.TopicCoderFilterConfiguration;
 
  53 import org.onap.policy.drools.protocol.coders.TopicCoderFilterConfiguration.CustomGsonCoder;
 
  54 import org.onap.policy.drools.protocol.coders.TopicCoderFilterConfiguration.PotentialCoderFilter;
 
  55 import org.onap.policy.drools.utils.ReflectionUtil;
 
  56 import org.slf4j.Logger;
 
  57 import org.slf4j.LoggerFactory;
 
  60  * Maven-based Drools Controller that interacts with the
 
  61  * policy-core PolicyContainer and PolicySession to manage
 
  62  * Drools containers instantiated using Maven.
 
  64 public class MavenDroolsController implements DroolsController {
 
  69     private static Logger  logger = LoggerFactory.getLogger(MavenDroolsController.class);
 
  72      * Policy Container, the access object to the policy-core layer.
 
  76     protected final PolicyContainer policyContainer;
 
  79      * alive status of this drools controller,
 
  80      * reflects invocation of start()/stop() only.
 
  82     protected volatile boolean alive = false;
 
  85      * locked status of this drools controller,
 
  86      * reflects if i/o drools related operations are permitted,
 
  87      * more specifically: offer() and deliver().
 
  88      * It does not affect the ability to start and stop
 
  89      * underlying drools infrastructure
 
  91     protected volatile boolean locked = false;
 
  94      * list of topics, each with associated decoder classes, each
 
  95      * with a list of associated filters.
 
  97     protected List<TopicCoderFilterConfiguration> decoderConfigurations;
 
 100      * list of topics, each with associated encoder classes, each
 
 101      * with a list of associated filters.
 
 103     protected List<TopicCoderFilterConfiguration> encoderConfigurations;
 
 106      * recent source events processed.
 
 108     protected final CircularFifoQueue<Object> recentSourceEvents = new CircularFifoQueue<>(10);
 
 111      * recent sink events processed.
 
 113     protected final CircularFifoQueue<String> recentSinkEvents = new CircularFifoQueue<>(10);
 
 116      * original Drools Model/Rules classloader hash.
 
 118     protected int modelClassLoaderHash;
 
 121      * Expanded version of the constructor.
 
 123      * @param groupId maven group id
 
 124      * @param artifactId maven artifact id
 
 125      * @param version maven version
 
 126      * @param decoderConfigurations list of topic -> decoders -> filters mapping
 
 127      * @param encoderConfigurations list of topic -> encoders -> filters mapping
 
 129      * @throws IllegalArgumentException invalid arguments passed in
 
 131     public MavenDroolsController(String groupId,
 
 134             List<TopicCoderFilterConfiguration> decoderConfigurations,
 
 135             List<TopicCoderFilterConfiguration> encoderConfigurations) {
 
 137         logger.info("drools-controller instantiation [{}:{}:{}]", groupId, artifactId, version);
 
 139         if (groupId == null || groupId.isEmpty()) {
 
 140             throw new IllegalArgumentException("Missing maven group-id coordinate");
 
 143         if (artifactId == null || artifactId.isEmpty()) {
 
 144             throw new IllegalArgumentException("Missing maven artifact-id coordinate");
 
 147         if (version == null || version.isEmpty()) {
 
 148             throw new IllegalArgumentException("Missing maven version coordinate");
 
 151         this.policyContainer = new PolicyContainer(groupId, artifactId, version);
 
 152         this.init(decoderConfigurations, encoderConfigurations);
 
 154         logger.debug("{}: instantiation completed ", this);
 
 158      * init encoding/decoding configuration.
 
 160      * @param decoderConfigurations list of topic -> decoders -> filters mapping
 
 161      * @param encoderConfigurations list of topic -> encoders -> filters mapping
 
 163     protected void init(List<TopicCoderFilterConfiguration> decoderConfigurations,
 
 164             List<TopicCoderFilterConfiguration> encoderConfigurations) {
 
 166         this.decoderConfigurations = decoderConfigurations;
 
 167         this.encoderConfigurations = encoderConfigurations;
 
 169         this.initCoders(decoderConfigurations, true);
 
 170         this.initCoders(encoderConfigurations, false);
 
 172         this.modelClassLoaderHash = this.policyContainer.getClassLoader().hashCode();
 
 176     public void updateToVersion(String newGroupId, String newArtifactId, String newVersion,
 
 177             List<TopicCoderFilterConfiguration> decoderConfigurations,
 
 178             List<TopicCoderFilterConfiguration> encoderConfigurations)
 
 179                     throws LinkageError {
 
 181         logger.info("updating version -> [{}:{}:{}]", newGroupId, newArtifactId, newVersion);
 
 183         if (newGroupId == null || newGroupId.isEmpty()) {
 
 184             throw new IllegalArgumentException("Missing maven group-id coordinate");
 
 187         if (newArtifactId == null || newArtifactId.isEmpty()) {
 
 188             throw new IllegalArgumentException("Missing maven artifact-id coordinate");
 
 191         if (newVersion == null || newVersion.isEmpty()) {
 
 192             throw new IllegalArgumentException("Missing maven version coordinate");
 
 195         if (newGroupId.equalsIgnoreCase(DroolsController.NO_GROUP_ID)
 
 196                 || newArtifactId.equalsIgnoreCase(DroolsController.NO_ARTIFACT_ID)
 
 197                 || newVersion.equalsIgnoreCase(DroolsController.NO_VERSION)) {
 
 198             throw new IllegalArgumentException("BRAINLESS maven coordinates provided: "
 
 199                     + newGroupId + ":" + newArtifactId + ":"
 
 203         if (newGroupId.equalsIgnoreCase(this.getGroupId())
 
 204                 && newArtifactId.equalsIgnoreCase(this.getArtifactId())
 
 205                 && newVersion.equalsIgnoreCase(this.getVersion())) {
 
 206             logger.warn("Al in the right version: " + newGroupId + ":"
 
 207                     + newArtifactId + ":" +  newVersion + " vs. " + this);
 
 211         if (!newGroupId.equalsIgnoreCase(this.getGroupId())
 
 212                 || !newArtifactId.equalsIgnoreCase(this.getArtifactId())) {
 
 213             throw new IllegalArgumentException(
 
 214                     "Group ID and Artifact ID maven coordinates must be identical for the upgrade: "
 
 215                     + newGroupId + ":" + newArtifactId + ":"
 
 216                     + newVersion + " vs. " + this);
 
 220         String messages = this.policyContainer.updateToVersion(newVersion);
 
 221         if (logger.isWarnEnabled()) {
 
 222             logger.warn("{} UPGRADE results: {}", this, messages);
 
 226          * If all sucessful (can load new container), now we can remove all coders from previous sessions
 
 233         this.init(decoderConfigurations, encoderConfigurations);
 
 235         if (logger.isInfoEnabled()) {
 
 236             logger.info("UPDATE-TO-VERSION: completed " +  this);
 
 241      * initialize decoders for all the topics supported by this controller
 
 242      * Note this is critical to be done after the Policy Container is
 
 243      * instantiated to be able to fetch the corresponding classes.
 
 245      * @param coderConfigurations list of topic -> decoders -> filters mapping
 
 247     protected void initCoders(List<TopicCoderFilterConfiguration> coderConfigurations,
 
 250         if (logger.isInfoEnabled()) {
 
 251             logger.info("INIT-CODERS: " +  this);
 
 254         if (coderConfigurations == null) {
 
 259         for (TopicCoderFilterConfiguration coderConfig: coderConfigurations) {
 
 260             String topic = coderConfig.getTopic();
 
 262             CustomGsonCoder customGsonCoder = coderConfig.getCustomGsonCoder();
 
 263             if (coderConfig.getCustomGsonCoder() != null
 
 264                     && coderConfig.getCustomGsonCoder().getClassContainer() != null
 
 265                     && !coderConfig.getCustomGsonCoder().getClassContainer().isEmpty()) {
 
 267                 String customGsonCoderClass = coderConfig.getCustomGsonCoder().getClassContainer();
 
 268                 if (!ReflectionUtil.isClass(this.policyContainer.getClassLoader(),
 
 269                         customGsonCoderClass)) {
 
 270                     throw makeRetrieveEx(customGsonCoderClass);
 
 272                     if (logger.isInfoEnabled()) {
 
 273                         logClassFetched(customGsonCoderClass);
 
 278             List<PotentialCoderFilter> coderFilters = coderConfig.getCoderFilters();
 
 279             if (coderFilters == null || coderFilters.isEmpty()) {
 
 283             for (PotentialCoderFilter coderFilter : coderFilters) {
 
 284                 String potentialCodedClass = coderFilter.getCodedClass();
 
 285                 JsonProtocolFilter protocolFilter = coderFilter.getFilter();
 
 287                 if (!ReflectionUtil.isClass(this.policyContainer.getClassLoader(),
 
 288                         potentialCodedClass)) {
 
 289                     throw makeRetrieveEx(potentialCodedClass);
 
 291                     if (logger.isInfoEnabled()) {
 
 292                         logClassFetched(potentialCodedClass);
 
 297                     EventProtocolCoder.manager.addDecoder(EventProtocolParams.builder()
 
 298                             .groupId(this.getGroupId())
 
 299                             .artifactId(this.getArtifactId())
 
 301                             .eventClass(potentialCodedClass)
 
 302                             .protocolFilter(protocolFilter)
 
 303                             .customGsonCoder(customGsonCoder)
 
 304                             .modelClassLoaderHash(this.policyContainer.getClassLoader().hashCode()));
 
 306                     EventProtocolCoder.manager.addEncoder(
 
 307                             EventProtocolParams.builder().groupId(this.getGroupId())
 
 308                                     .artifactId(this.getArtifactId()).topic(topic)
 
 309                                     .eventClass(potentialCodedClass).protocolFilter(protocolFilter)
 
 310                                     .customGsonCoder(customGsonCoder)
 
 311                                     .modelClassLoaderHash(this.policyContainer.getClassLoader().hashCode()));
 
 318      * Logs an error and makes an exception for an item that cannot be retrieved.
 
 319      * @param itemName the item to retrieve
 
 320      * @return a new exception
 
 322     private IllegalArgumentException makeRetrieveEx(String itemName) {
 
 323         logger.error("{} cannot be retrieved", itemName);
 
 324         return new IllegalArgumentException(itemName + " cannot be retrieved");
 
 328      * Logs the name of the class that was fetched.
 
 329      * @param className class name fetched
 
 331     private void logClassFetched(String className) {
 
 332         logger.info("CLASS FETCHED {}", className);
 
 339     protected void removeDecoders() {
 
 340         if (logger.isInfoEnabled()) {
 
 341             logger.info("REMOVE-DECODERS: {}",  this);
 
 344         if (this.decoderConfigurations == null) {
 
 349         for (TopicCoderFilterConfiguration coderConfig: decoderConfigurations) {
 
 350             String topic = coderConfig.getTopic();
 
 351             EventProtocolCoder.manager.removeDecoders(this.getGroupId(), this.getArtifactId(), topic);
 
 358     protected void removeEncoders() {
 
 360         if (logger.isInfoEnabled()) {
 
 361             logger.info("REMOVE-ENCODERS: {}",  this);
 
 364         if (this.encoderConfigurations == null) {
 
 368         for (TopicCoderFilterConfiguration coderConfig: encoderConfigurations) {
 
 369             String topic = coderConfig.getTopic();
 
 370             EventProtocolCoder.manager.removeEncoders(this.getGroupId(), this.getArtifactId(), topic);
 
 376     public boolean ownsCoder(Class<? extends Object> coderClass, int modelHash) {
 
 377         if (!ReflectionUtil.isClass(this.policyContainer.getClassLoader(), coderClass.getCanonicalName())) {
 
 378             logger.error("{}{} cannot be retrieved. ", this, coderClass.getCanonicalName());
 
 382         if (modelHash == this.modelClassLoaderHash) {
 
 383             if (logger.isInfoEnabled()) {
 
 384                 logger.info(coderClass.getCanonicalName()
 
 385                         + this + " class loader matches original drools controller rules classloader "
 
 386                         + coderClass.getClassLoader());
 
 390             if (logger.isWarnEnabled()) {
 
 391                 logger.warn(this + coderClass.getCanonicalName() + " class loaders don't match  "
 
 392                         + coderClass.getClassLoader() + " vs "
 
 393                         + this.policyContainer.getClassLoader());
 
 400     public boolean start() {
 
 402         if (logger.isInfoEnabled()) {
 
 403             logger.info("START: {}", this);
 
 406         synchronized (this) {
 
 413         return this.policyContainer.start();
 
 417     public boolean stop() {
 
 419         logger.info("STOP: {}", this);
 
 421         synchronized (this) {
 
 428         return this.policyContainer.stop();
 
 432     public void shutdown() {
 
 433         logger.info("{}: SHUTDOWN", this);
 
 438         } catch (Exception e) {
 
 439             logger.error("{} SHUTDOWN FAILED because of {}", this, e.getMessage(), e);
 
 441             this.policyContainer.shutdown();
 
 448         logger.info("{}: HALT", this);
 
 453         } catch (Exception e) {
 
 454             logger.error("{} HALT FAILED because of {}", this, e.getMessage(), e);
 
 456             this.policyContainer.destroy();
 
 461      * removes this drools controllers and encoders and decoders from operation.
 
 463     protected void removeCoders() {
 
 464         logger.info("{}: REMOVE-CODERS", this);
 
 467             this.removeDecoders();
 
 468         } catch (IllegalArgumentException e) {
 
 469             logger.error("{} REMOVE-DECODERS FAILED because of {}", this, e.getMessage(), e);
 
 473             this.removeEncoders();
 
 474         } catch (IllegalArgumentException e) {
 
 475             logger.error("{} REMOVE-ENCODERS FAILED because of {}", this, e.getMessage(), e);
 
 480     public boolean isAlive() {
 
 485     public boolean offer(String topic, String event) {
 
 486         logger.debug("{}: OFFER raw event from {}", this, topic);
 
 488         if (this.locked || !this.alive || this.policyContainer.getPolicySessions().isEmpty()) {
 
 492         // 1. Now, check if this topic has a decoder:
 
 494         if (!EventProtocolCoder.manager.isDecodingSupported(this.getGroupId(),
 
 495                 this.getArtifactId(),
 
 498             logger.warn("{}: DECODING-UNSUPPORTED {}:{}:{}", this,
 
 499                     topic, this.getGroupId(), this.getArtifactId());
 
 507             anEvent = EventProtocolCoder.manager.decode(this.getGroupId(),
 
 508                     this.getArtifactId(),
 
 511         } catch (UnsupportedOperationException uoe) {
 
 512             logger.debug("{}: DECODE FAILED: {} <- {} because of {}", this, topic,
 
 513                     event, uoe.getMessage(), uoe);
 
 515         } catch (Exception e) {
 
 516             logger.warn("{}: DECODE FAILED: {} <- {} because of {}", this, topic,
 
 517                     event, e.getMessage(), e);
 
 521         return offer(anEvent);
 
 526     public <T> boolean offer(T event) {
 
 527         logger.debug("{}: OFFER event", this);
 
 529         if (this.locked || !this.alive || this.policyContainer.getPolicySessions().isEmpty()) {
 
 533         synchronized (this.recentSourceEvents) {
 
 534             this.recentSourceEvents.add(event);
 
 537         PdpJmx.getInstance().updateOccured();
 
 541         for (DroolsControllerFeatureAPI feature : DroolsControllerFeatureAPI.providers.getList()) {
 
 543                 if (feature.beforeInsert(this, event)) {
 
 546             } catch (Exception e) {
 
 547                 logger.error("{}: feature {} before-insert failure because of {}",
 
 548                     this, feature.getClass().getName(), e.getMessage(), e);
 
 552         boolean successInject = this.policyContainer.insertAll(event);
 
 553         if (!successInject) {
 
 554             logger.warn(this + "Failed to inject into PolicyContainer {}", this.getSessionNames());
 
 557         for (DroolsControllerFeatureAPI feature : DroolsControllerFeatureAPI.providers.getList()) {
 
 559                 if (feature.afterInsert(this, event, successInject)) {
 
 562             } catch (Exception e) {
 
 563                 logger.error("{}: feature {} after-insert failure because of {}",
 
 564                     this, feature.getClass().getName(), e.getMessage(), e);
 
 573     public boolean deliver(TopicSink sink, Object event) {
 
 575         if (logger.isInfoEnabled()) {
 
 576             logger.info("{}DELIVER: {} FROM {} TO {}", this, event, this, sink);
 
 579         for (DroolsControllerFeatureAPI feature : DroolsControllerFeatureAPI.providers.getList()) {
 
 581                 if (feature.beforeDeliver(this, sink, event)) {
 
 585             catch (Exception e) {
 
 586                 logger.error("{}: feature {} before-deliver failure because of {}", this, feature.getClass().getName(),
 
 592             throw new IllegalArgumentException(this +  " invalid sink");
 
 596             throw new IllegalArgumentException(this +  " invalid event");
 
 600             throw new IllegalStateException(this +  " is locked");
 
 604             throw new IllegalStateException(this +  " is stopped");
 
 608                 EventProtocolCoder.manager.encode(sink.getTopic(), event, this);
 
 610         synchronized (this.recentSinkEvents) {
 
 611             this.recentSinkEvents.add(json);
 
 614         boolean success = sink.send(json);
 
 616         for (DroolsControllerFeatureAPI feature : DroolsControllerFeatureAPI.providers.getList()) {
 
 618                 if (feature.afterDeliver(this, sink, event, json, success)) {
 
 622             catch (Exception e) {
 
 623                 logger.error("{}: feature {} after-deliver failure because of {}", this, feature.getClass().getName(),
 
 633     public String getVersion() {
 
 634         return this.policyContainer.getVersion();
 
 638     public String getArtifactId() {
 
 639         return this.policyContainer.getArtifactId();
 
 643     public String getGroupId() {
 
 644         return this.policyContainer.getGroupId();
 
 648      * Get model class loader hash.
 
 650      * @return the modelClassLoaderHash
 
 652     public int getModelClassLoaderHash() {
 
 653         return modelClassLoaderHash;
 
 657     public synchronized boolean lock() {
 
 658         logger.info("LOCK: {}",  this);
 
 665     public synchronized boolean unlock() {
 
 666         logger.info("UNLOCK: {}",  this);
 
 673     public boolean isLocked() {
 
 680     public PolicyContainer getContainer() {
 
 681         return this.policyContainer;
 
 684     @JsonProperty("sessions")
 
 685     @GsonJsonProperty("sessions")
 
 687     public List<String> getSessionNames() {
 
 688         return getSessionNames(true);
 
 694      * @param abbreviated true for the short form, otherwise the long form
 
 695      * @return session names
 
 697     protected List<String> getSessionNames(boolean abbreviated) {
 
 698         List<String> sessionNames = new ArrayList<>();
 
 700             for (PolicySession session: this.policyContainer.getPolicySessions()) {
 
 702                     sessionNames.add(session.getName());
 
 704                     sessionNames.add(session.getFullName());
 
 707         } catch (Exception e) {
 
 708             logger.warn("Can't retrieve CORE sessions: " + e.getMessage(), e);
 
 709             sessionNames.add(e.getMessage());
 
 714     @JsonProperty("sessionCoordinates")
 
 715     @GsonJsonProperty("sessionCoordinates")
 
 717     public List<String> getCanonicalSessionNames() {
 
 718         return getSessionNames(false);
 
 722     public List<String> getBaseDomainNames() {
 
 723         return new ArrayList<>(this.policyContainer.getKieContainer().getKieBaseNames());
 
 727      * provides the underlying core layer container sessions.
 
 729      * @return the attached Policy Container
 
 731     protected List<PolicySession> getSessions() {
 
 732         List<PolicySession> sessions = new ArrayList<>();
 
 733         sessions.addAll(this.policyContainer.getPolicySessions());
 
 738      * provides the underlying core layer container session with name sessionName.
 
 740      * @param sessionName session name
 
 741      * @return the attached Policy Container
 
 742      * @throws IllegalArgumentException when an invalid session name is provided
 
 743      * @throws IllegalStateException when the drools controller is in an invalid state
 
 745     protected PolicySession getSession(String sessionName) {
 
 746         if (sessionName == null || sessionName.isEmpty()) {
 
 747             throw new IllegalArgumentException("A Session Name must be provided");
 
 750         List<PolicySession> sessions = this.getSessions();
 
 751         for (PolicySession session : sessions) {
 
 752             if (sessionName.equals(session.getName()) || sessionName.equals(session.getFullName())) {
 
 757         throw invalidSessNameEx(sessionName);
 
 760     private IllegalArgumentException invalidSessNameEx(String sessionName) {
 
 761         return new IllegalArgumentException("Invalid Session Name: " + sessionName);
 
 765     public Map<String,Integer> factClassNames(String sessionName) {
 
 766         if (sessionName == null || sessionName.isEmpty()) {
 
 767             throw invalidSessNameEx(sessionName);
 
 770         Map<String,Integer> classNames = new HashMap<>();
 
 772         PolicySession session = getSession(sessionName);
 
 773         KieSession kieSession = session.getKieSession();
 
 775         Collection<FactHandle> facts = session.getKieSession().getFactHandles();
 
 776         for (FactHandle fact : facts) {
 
 778                 String className = kieSession.getObject(fact).getClass().getName();
 
 779                 if (classNames.containsKey(className)) {
 
 780                     classNames.put(className, classNames.get(className) + 1);
 
 782                     classNames.put(className, 1);
 
 784             } catch (Exception e) {
 
 785                 logger.warn("Object cannot be retrieved from fact {}", fact, e);
 
 793     public long factCount(String sessionName) {
 
 794         if (sessionName == null || sessionName.isEmpty()) {
 
 795             throw invalidSessNameEx(sessionName);
 
 798         PolicySession session = getSession(sessionName);
 
 799         return session.getKieSession().getFactCount();
 
 803     public List<Object> facts(String sessionName, String className, boolean delete) {
 
 804         if (sessionName == null || sessionName.isEmpty()) {
 
 805             throw invalidSessNameEx(sessionName);
 
 808         if (className == null || className.isEmpty()) {
 
 809             throw new IllegalArgumentException("Invalid Class Name: " + className);
 
 813                 ReflectionUtil.fetchClass(this.policyContainer.getClassLoader(), className);
 
 814         if (factClass == null) {
 
 815             throw new IllegalArgumentException("Class cannot be fetched in model's classloader: " + className);
 
 818         PolicySession session = getSession(sessionName);
 
 819         KieSession kieSession = session.getKieSession();
 
 821         List<Object> factObjects = new ArrayList<>();
 
 823         Collection<FactHandle> factHandles = kieSession.getFactHandles(new ClassObjectFilter(factClass));
 
 824         for (FactHandle factHandle : factHandles) {
 
 826                 factObjects.add(kieSession.getObject(factHandle));
 
 828                     kieSession.delete(factHandle);
 
 830             } catch (Exception e) {
 
 831                 logger.warn("Object cannot be retrieved from fact {}", factHandle, e);
 
 839     public <T> List<T> facts(@NonNull String sessionName, @NonNull Class<T> clazz) {
 
 840         return facts(sessionName, clazz.getCanonicalName(), false)
 
 842             .filter(clazz::isInstance)
 
 844             .collect(Collectors.toList());
 
 848     public List<Object> factQuery(String sessionName, String queryName, String queriedEntity,
 
 849             boolean delete, Object... queryParams) {
 
 850         if (sessionName == null || sessionName.isEmpty()) {
 
 851             throw invalidSessNameEx(sessionName);
 
 854         if (queryName == null || queryName.isEmpty()) {
 
 855             throw new IllegalArgumentException("Invalid Query Name: " + queryName);
 
 858         if (queriedEntity == null || queriedEntity.isEmpty()) {
 
 859             throw new IllegalArgumentException("Invalid Queried Entity: " + queriedEntity);
 
 862         PolicySession session = getSession(sessionName);
 
 863         KieSession kieSession = session.getKieSession();
 
 865         boolean found = false;
 
 866         for (KiePackage kiePackage : kieSession.getKieBase().getKiePackages()) {
 
 867             for (Query q : kiePackage.getQueries()) {
 
 868                 if (q.getName() != null && q.getName().equals(queryName)) {
 
 875             throw new IllegalArgumentException("Invalid Query Name: " + queryName);
 
 878         List<Object> factObjects = new ArrayList<>();
 
 880         QueryResults queryResults = kieSession.getQueryResults(queryName, queryParams);
 
 881         for (QueryResultsRow row : queryResults) {
 
 883                 factObjects.add(row.get(queriedEntity));
 
 885                     kieSession.delete(row.getFactHandle(queriedEntity));
 
 887             } catch (Exception e) {
 
 888                 logger.warn("Object cannot be retrieved from row: {}", row, e);
 
 896     public <T> boolean delete(@NonNull String sessionName, @NonNull T fact) {
 
 897         String factClassName = fact.getClass().getName();
 
 899         PolicySession session = getSession(sessionName);
 
 900         KieSession kieSession = session.getKieSession();
 
 902         Collection<FactHandle> factHandles = kieSession.getFactHandles(new ClassObjectFilter(fact.getClass()));
 
 903         for (FactHandle factHandle : factHandles) {
 
 905                 if (Objects.equals(fact, kieSession.getObject(factHandle))) {
 
 906                     logger.info("Deleting {} from {}", factClassName, sessionName);
 
 907                     kieSession.delete(factHandle);
 
 910             } catch (Exception e) {
 
 911                 logger.warn("Object cannot be retrieved from fact {}", factHandle, e);
 
 918     public <T> boolean delete(@NonNull T fact) {
 
 919         return this.getSessionNames().stream().map((ss) -> delete(ss, fact)).reduce(false, Boolean::logicalOr);
 
 923     public Class<?> fetchModelClass(String className) {
 
 924         return ReflectionUtil.fetchClass(this.policyContainer.getClassLoader(), className);
 
 928      * Get recent source events.
 
 930      * @return the recentSourceEvents
 
 933     public Object[] getRecentSourceEvents() {
 
 934         synchronized (this.recentSourceEvents) {
 
 935             Object[] events = new Object[recentSourceEvents.size()];
 
 936             return recentSourceEvents.toArray(events);
 
 941      * Get recent sink events.
 
 943      * @return the recentSinkEvents
 
 946     public String[] getRecentSinkEvents() {
 
 947         synchronized (this.recentSinkEvents) {
 
 948             String[] events = new String[recentSinkEvents.size()];
 
 949             return recentSinkEvents.toArray(events);
 
 954     public boolean isBrained() {
 
 960     public String toString() {
 
 961         StringBuilder builder = new StringBuilder();
 
 963             .append("MavenDroolsController [policyContainer=")
 
 964             .append((policyContainer != null) ? policyContainer.getName() : "NULL")
 
 969             .append(", modelClassLoaderHash=")
 
 970             .append(modelClassLoaderHash)
 
 972         return builder.toString();