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 <T> boolean delete(@NonNull String sessionName, @NonNull Class<T> fact) {
924 PolicySession session = getSession(sessionName);
925 KieSession kieSession = session.getKieSession();
927 boolean success = true;
928 Collection<FactHandle> factHandles = kieSession.getFactHandles(new ClassObjectFilter(fact));
929 for (FactHandle factHandle : factHandles) {
931 kieSession.delete(factHandle);
932 } catch (Exception e) {
933 logger.warn("Object cannot be retrieved from fact {}", factHandle, e);
941 public <T> boolean delete(@NonNull Class<T> fact) {
942 return this.getSessionNames().stream().map((ss) -> delete(ss, fact)).reduce(false, Boolean::logicalOr);
947 public Class<?> fetchModelClass(String className) {
948 return ReflectionUtil.fetchClass(this.policyContainer.getClassLoader(), className);
952 * Get recent source events.
954 * @return the recentSourceEvents
957 public Object[] getRecentSourceEvents() {
958 synchronized (this.recentSourceEvents) {
959 Object[] events = new Object[recentSourceEvents.size()];
960 return recentSourceEvents.toArray(events);
965 * Get recent sink events.
967 * @return the recentSinkEvents
970 public String[] getRecentSinkEvents() {
971 synchronized (this.recentSinkEvents) {
972 String[] events = new String[recentSinkEvents.size()];
973 return recentSinkEvents.toArray(events);
978 public boolean isBrained() {
984 public String toString() {
985 StringBuilder builder = new StringBuilder();
987 .append("MavenDroolsController [policyContainer=")
988 .append((policyContainer != null) ? policyContainer.getName() : "NULL")
993 .append(", modelClassLoaderHash=")
994 .append(modelClassLoaderHash)
996 return builder.toString();