2 * ============LICENSE_START=======================================================
4 * ================================================================================
5 * Copyright (C) 2017-2020 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.common.utils.services.FeatureApiUtils;
45 import org.onap.policy.common.utils.services.OrderedServiceImpl;
46 import org.onap.policy.drools.controller.DroolsController;
47 import org.onap.policy.drools.controller.DroolsControllerConstants;
48 import org.onap.policy.drools.core.PolicyContainer;
49 import org.onap.policy.drools.core.PolicySession;
50 import org.onap.policy.drools.core.jmx.PdpJmx;
51 import org.onap.policy.drools.features.DroolsControllerFeatureApi;
52 import org.onap.policy.drools.features.DroolsControllerFeatureApiConstants;
53 import org.onap.policy.drools.protocol.coders.EventProtocolCoder;
54 import org.onap.policy.drools.protocol.coders.EventProtocolCoderConstants;
55 import org.onap.policy.drools.protocol.coders.EventProtocolParams;
56 import org.onap.policy.drools.protocol.coders.JsonProtocolFilter;
57 import org.onap.policy.drools.protocol.coders.TopicCoderFilterConfiguration;
58 import org.onap.policy.drools.protocol.coders.TopicCoderFilterConfiguration.CustomGsonCoder;
59 import org.onap.policy.drools.protocol.coders.TopicCoderFilterConfiguration.PotentialCoderFilter;
60 import org.onap.policy.drools.utils.ReflectionUtil;
61 import org.slf4j.Logger;
62 import org.slf4j.LoggerFactory;
65 * Maven-based Drools Controller that interacts with the
66 * policy-core PolicyContainer and PolicySession to manage
67 * Drools containers instantiated using Maven.
69 public class MavenDroolsController implements DroolsController {
71 private static final String FACT_RETRIEVE_ERROR = "Object cannot be retrieved from fact {}";
76 private static Logger logger = LoggerFactory.getLogger(MavenDroolsController.class);
79 * Policy Container, the access object to the policy-core layer.
83 protected final PolicyContainer policyContainer;
86 * alive status of this drools controller,
87 * reflects invocation of start()/stop() only.
89 protected volatile boolean alive = false;
92 * locked status of this drools controller,
93 * reflects if i/o drools related operations are permitted,
94 * more specifically: offer() and deliver().
95 * It does not affect the ability to start and stop
96 * underlying drools infrastructure
98 protected volatile boolean locked = false;
101 * list of topics, each with associated decoder classes, each
102 * with a list of associated filters.
104 protected List<TopicCoderFilterConfiguration> decoderConfigurations;
107 * list of topics, each with associated encoder classes, each
108 * with a list of associated filters.
110 protected List<TopicCoderFilterConfiguration> encoderConfigurations;
113 * recent source events processed.
115 protected final CircularFifoQueue<Object> recentSourceEvents = new CircularFifoQueue<>(10);
118 * recent sink events processed.
120 protected final CircularFifoQueue<String> recentSinkEvents = new CircularFifoQueue<>(10);
123 * original Drools Model/Rules classloader hash.
125 protected int modelClassLoaderHash;
128 * Expanded version of the constructor.
130 * @param groupId maven group id
131 * @param artifactId maven artifact id
132 * @param version maven version
133 * @param decoderConfigurations list of topic -> decoders -> filters mapping
134 * @param encoderConfigurations list of topic -> encoders -> filters mapping
136 * @throws IllegalArgumentException invalid arguments passed in
138 public MavenDroolsController(String groupId,
141 List<TopicCoderFilterConfiguration> decoderConfigurations,
142 List<TopicCoderFilterConfiguration> encoderConfigurations) {
144 logger.info("drools-controller instantiation [{}:{}:{}]", groupId, artifactId, version);
146 if (groupId == null || groupId.isEmpty()) {
147 throw new IllegalArgumentException("Missing maven group-id coordinate");
150 if (artifactId == null || artifactId.isEmpty()) {
151 throw new IllegalArgumentException("Missing maven artifact-id coordinate");
154 if (version == null || version.isEmpty()) {
155 throw new IllegalArgumentException("Missing maven version coordinate");
158 this.policyContainer = makePolicyContainer(groupId, artifactId, version);
159 this.init(decoderConfigurations, encoderConfigurations);
161 logger.debug("{}: instantiation completed ", this);
165 * init encoding/decoding configuration.
167 * @param decoderConfigurations list of topic -> decoders -> filters mapping
168 * @param encoderConfigurations list of topic -> encoders -> filters mapping
170 protected void init(List<TopicCoderFilterConfiguration> decoderConfigurations,
171 List<TopicCoderFilterConfiguration> encoderConfigurations) {
173 this.decoderConfigurations = decoderConfigurations;
174 this.encoderConfigurations = encoderConfigurations;
176 this.initCoders(decoderConfigurations, true);
177 this.initCoders(encoderConfigurations, false);
179 this.modelClassLoaderHash = this.policyContainer.getClassLoader().hashCode();
183 public void updateToVersion(String newGroupId, String newArtifactId, String newVersion,
184 List<TopicCoderFilterConfiguration> decoderConfigurations,
185 List<TopicCoderFilterConfiguration> encoderConfigurations)
186 throws LinkageError {
188 logger.info("updating version -> [{}:{}:{}]", newGroupId, newArtifactId, newVersion);
190 validateText(newGroupId, "Missing maven group-id coordinate");
191 validateText(newArtifactId, "Missing maven artifact-id coordinate");
192 validateText(newVersion, "Missing maven version coordinate");
194 validateHasBrain(newGroupId, newArtifactId, newVersion);
196 if (newGroupId.equalsIgnoreCase(this.getGroupId())
197 && newArtifactId.equalsIgnoreCase(this.getArtifactId())
198 && newVersion.equalsIgnoreCase(this.getVersion())) {
199 logger.warn("All in the right version: {}:{}:{} vs. {}", newGroupId, newArtifactId, newVersion, this);
203 validateNewVersion(newGroupId, newArtifactId, newVersion);
206 String messages = this.policyContainer.updateToVersion(newVersion);
207 logger.warn("{} UPGRADE results: {}", this, messages);
210 * If all sucessful (can load new container), now we can remove all coders from previous sessions
217 this.init(decoderConfigurations, encoderConfigurations);
219 logger.info("UPDATE-TO-VERSION: completed {}", this);
222 private void validateText(String text, String errorMessage) {
223 if (text == null || text.isEmpty()) {
224 throw new IllegalArgumentException(errorMessage);
228 private void validateHasBrain(String newGroupId, String newArtifactId, String newVersion) {
229 if (newGroupId.equalsIgnoreCase(DroolsControllerConstants.NO_GROUP_ID)
230 || newArtifactId.equalsIgnoreCase(DroolsControllerConstants.NO_ARTIFACT_ID)
231 || newVersion.equalsIgnoreCase(DroolsControllerConstants.NO_VERSION)) {
232 throw new IllegalArgumentException("BRAINLESS maven coordinates provided: "
233 + newGroupId + ":" + newArtifactId + ":"
238 private void validateNewVersion(String newGroupId, String newArtifactId, String newVersion) {
239 if (!newGroupId.equalsIgnoreCase(this.getGroupId())
240 || !newArtifactId.equalsIgnoreCase(this.getArtifactId())) {
241 throw new IllegalArgumentException(
242 "Group ID and Artifact ID maven coordinates must be identical for the upgrade: "
243 + newGroupId + ":" + newArtifactId + ":"
244 + newVersion + " vs. " + this);
249 * initialize decoders for all the topics supported by this controller
250 * Note this is critical to be done after the Policy Container is
251 * instantiated to be able to fetch the corresponding classes.
253 * @param coderConfigurations list of topic -> decoders -> filters mapping
255 protected void initCoders(List<TopicCoderFilterConfiguration> coderConfigurations,
258 logger.info("INIT-CODERS: {}", this);
260 if (coderConfigurations == null) {
265 for (TopicCoderFilterConfiguration coderConfig: coderConfigurations) {
266 String topic = coderConfig.getTopic();
268 CustomGsonCoder customGsonCoder = getCustomCoder(coderConfig);
270 List<PotentialCoderFilter> coderFilters = coderConfig.getCoderFilters();
271 if (coderFilters == null || coderFilters.isEmpty()) {
275 for (PotentialCoderFilter coderFilter : coderFilters) {
276 String potentialCodedClass = coderFilter.getCodedClass();
277 JsonProtocolFilter protocolFilter = coderFilter.getFilter();
279 if (!isClass(potentialCodedClass)) {
280 throw makeRetrieveEx(potentialCodedClass);
282 logClassFetched(potentialCodedClass);
286 getCoderManager().addDecoder(EventProtocolParams.builder()
287 .groupId(this.getGroupId())
288 .artifactId(this.getArtifactId())
290 .eventClass(potentialCodedClass)
291 .protocolFilter(protocolFilter)
292 .customGsonCoder(customGsonCoder)
293 .modelClassLoaderHash(this.policyContainer.getClassLoader().hashCode()));
295 getCoderManager().addEncoder(
296 EventProtocolParams.builder().groupId(this.getGroupId())
297 .artifactId(this.getArtifactId()).topic(topic)
298 .eventClass(potentialCodedClass).protocolFilter(protocolFilter)
299 .customGsonCoder(customGsonCoder)
300 .modelClassLoaderHash(this.policyContainer.getClassLoader().hashCode()));
306 private CustomGsonCoder getCustomCoder(TopicCoderFilterConfiguration coderConfig) {
307 CustomGsonCoder customGsonCoder = coderConfig.getCustomGsonCoder();
308 if (customGsonCoder != null
309 && customGsonCoder.getClassContainer() != null
310 && !customGsonCoder.getClassContainer().isEmpty()) {
312 String customGsonCoderClass = customGsonCoder.getClassContainer();
313 if (!isClass(customGsonCoderClass)) {
314 throw makeRetrieveEx(customGsonCoderClass);
316 logClassFetched(customGsonCoderClass);
319 return customGsonCoder;
323 * Logs an error and makes an exception for an item that cannot be retrieved.
324 * @param itemName the item to retrieve
325 * @return a new exception
327 private IllegalArgumentException makeRetrieveEx(String itemName) {
328 logger.error("{} cannot be retrieved", itemName);
329 return new IllegalArgumentException(itemName + " cannot be retrieved");
333 * Logs the name of the class that was fetched.
334 * @param className class name fetched
336 private void logClassFetched(String className) {
337 logger.info("CLASS FETCHED {}", className);
344 protected void removeDecoders() {
345 logger.info("REMOVE-DECODERS: {}", this);
347 if (this.decoderConfigurations == null) {
352 for (TopicCoderFilterConfiguration coderConfig: decoderConfigurations) {
353 String topic = coderConfig.getTopic();
354 getCoderManager().removeDecoders(this.getGroupId(), this.getArtifactId(), topic);
361 protected void removeEncoders() {
363 logger.info("REMOVE-ENCODERS: {}", this);
365 if (this.encoderConfigurations == null) {
369 for (TopicCoderFilterConfiguration coderConfig: encoderConfigurations) {
370 String topic = coderConfig.getTopic();
371 getCoderManager().removeEncoders(this.getGroupId(), this.getArtifactId(), topic);
377 public boolean ownsCoder(Class<?> coderClass, int modelHash) {
378 if (!isClass(coderClass.getName())) {
379 logger.error("{}{} cannot be retrieved. ", this, coderClass.getName());
383 if (modelHash == this.modelClassLoaderHash) {
384 logger.info("{}{} class loader matches original drools controller rules classloader {}",
385 coderClass.getName(), this, coderClass.getClassLoader());
388 logger.warn("{}{} class loaders don't match {} vs {}", this, coderClass.getName(),
389 coderClass.getClassLoader(), this.policyContainer.getClassLoader());
395 public boolean start() {
397 logger.info("START: {}", this);
399 synchronized (this) {
406 return this.policyContainer.start();
410 public boolean stop() {
412 logger.info("STOP: {}", this);
414 synchronized (this) {
421 return this.policyContainer.stop();
425 public void shutdown() {
426 logger.info("{}: SHUTDOWN", this);
431 } catch (Exception e) {
432 logger.error("{} SHUTDOWN FAILED because of {}", this, e.getMessage(), e);
434 this.policyContainer.shutdown();
441 logger.info("{}: HALT", this);
446 } catch (Exception e) {
447 logger.error("{} HALT FAILED because of {}", this, e.getMessage(), e);
449 this.policyContainer.destroy();
454 * removes this drools controllers and encoders and decoders from operation.
456 protected void removeCoders() {
457 logger.info("{}: REMOVE-CODERS", this);
460 this.removeDecoders();
461 } catch (IllegalArgumentException e) {
462 logger.error("{} REMOVE-DECODERS FAILED because of {}", this, e.getMessage(), e);
466 this.removeEncoders();
467 } catch (IllegalArgumentException e) {
468 logger.error("{} REMOVE-ENCODERS FAILED because of {}", this, e.getMessage(), e);
473 public boolean isAlive() {
478 public boolean offer(String topic, String event) {
479 logger.debug("{}: OFFER raw event from {}", this, topic);
481 if (this.locked || !this.alive || this.policyContainer.getPolicySessions().isEmpty()) {
485 // 1. Now, check if this topic has a decoder:
487 if (!getCoderManager().isDecodingSupported(this.getGroupId(),
488 this.getArtifactId(),
491 logger.warn("{}: DECODING-UNSUPPORTED {}:{}:{}", this,
492 topic, this.getGroupId(), this.getArtifactId());
500 anEvent = getCoderManager().decode(this.getGroupId(),
501 this.getArtifactId(),
504 } catch (UnsupportedOperationException uoe) {
505 logger.debug("{}: DECODE FAILED: {} <- {} because of {}", this, topic,
506 event, uoe.getMessage(), uoe);
508 } catch (Exception e) {
509 logger.warn("{}: DECODE FAILED: {} <- {} because of {}", this, topic,
510 event, e.getMessage(), e);
514 return offer(anEvent);
519 * This method always returns "true", which causes a sonar complaint. However,
520 * refactoring or restructuring it would unnecessarily complicate it, thus we'll just
521 * disable the sonar complaint.
524 public <T> boolean offer(T event) { // NOSONAR
525 logger.debug("{}: OFFER event", this);
527 if (this.locked || !this.alive || this.policyContainer.getPolicySessions().isEmpty()) {
531 synchronized (this.recentSourceEvents) {
532 this.recentSourceEvents.add(event);
535 PdpJmx.getInstance().updateOccured();
539 if (FeatureApiUtils.apply(getDroolsProviders().getList(),
540 feature -> feature.beforeInsert(this, event),
541 (feature, ex) -> logger.error("{}: feature {} before-insert failure because of {}", this,
542 feature.getClass().getName(), ex.getMessage(), ex))) {
546 boolean successInject = this.policyContainer.insertAll(event);
547 if (!successInject) {
548 logger.warn("{} Failed to inject into PolicyContainer {}", this, this.getSessionNames());
551 FeatureApiUtils.apply(getDroolsProviders().getList(),
552 feature -> feature.afterInsert(this, event, successInject),
553 (feature, ex) -> logger.error("{}: feature {} after-insert failure because of {}", this,
554 feature.getClass().getName(), ex.getMessage(), ex));
561 public boolean deliver(TopicSink sink, Object event) {
563 logger.info("{}DELIVER: {} FROM {} TO {}", this, event, this, sink);
565 for (DroolsControllerFeatureApi feature : getDroolsProviders().getList()) {
567 if (feature.beforeDeliver(this, sink, event)) {
570 } catch (Exception e) {
571 logger.error("{}: feature {} before-deliver failure because of {}", this, feature.getClass().getName(),
577 throw new IllegalArgumentException(this + " invalid sink");
581 throw new IllegalArgumentException(this + " invalid event");
585 throw new IllegalStateException(this + " is locked");
589 throw new IllegalStateException(this + " is stopped");
593 getCoderManager().encode(sink.getTopic(), event, this);
595 synchronized (this.recentSinkEvents) {
596 this.recentSinkEvents.add(json);
599 boolean success = sink.send(json);
601 for (DroolsControllerFeatureApi feature : getDroolsProviders().getList()) {
603 if (feature.afterDeliver(this, sink, event, json, success)) {
606 } catch (Exception e) {
607 logger.error("{}: feature {} after-deliver failure because of {}", this, feature.getClass().getName(),
617 public String getVersion() {
618 return this.policyContainer.getVersion();
622 public String getArtifactId() {
623 return this.policyContainer.getArtifactId();
627 public String getGroupId() {
628 return this.policyContainer.getGroupId();
632 * Get model class loader hash.
634 * @return the modelClassLoaderHash
636 public int getModelClassLoaderHash() {
637 return modelClassLoaderHash;
641 public synchronized boolean lock() {
642 logger.info("LOCK: {}", this);
649 public synchronized boolean unlock() {
650 logger.info("UNLOCK: {}", this);
657 public boolean isLocked() {
664 public PolicyContainer getContainer() {
665 return this.policyContainer;
668 @JsonProperty("sessions")
669 @GsonJsonProperty("sessions")
671 public List<String> getSessionNames() {
672 return getSessionNames(true);
678 * @param abbreviated true for the short form, otherwise the long form
679 * @return session names
681 protected List<String> getSessionNames(boolean abbreviated) {
682 List<String> sessionNames = new ArrayList<>();
684 for (PolicySession session: this.policyContainer.getPolicySessions()) {
686 sessionNames.add(session.getName());
688 sessionNames.add(session.getFullName());
691 } catch (Exception e) {
692 logger.warn("Can't retrieve CORE sessions", e);
693 sessionNames.add(e.getMessage());
698 @JsonProperty("sessionCoordinates")
699 @GsonJsonProperty("sessionCoordinates")
701 public List<String> getCanonicalSessionNames() {
702 return getSessionNames(false);
706 public List<String> getBaseDomainNames() {
707 return new ArrayList<>(this.policyContainer.getKieContainer().getKieBaseNames());
711 * provides the underlying core layer container sessions.
713 * @return the attached Policy Container
715 protected List<PolicySession> getSessions() {
716 List<PolicySession> sessions = new ArrayList<>();
717 sessions.addAll(this.policyContainer.getPolicySessions());
722 * provides the underlying core layer container session with name sessionName.
724 * @param sessionName session name
725 * @return the attached Policy Container
726 * @throws IllegalArgumentException when an invalid session name is provided
727 * @throws IllegalStateException when the drools controller is in an invalid state
729 protected PolicySession getSession(String sessionName) {
730 if (sessionName == null || sessionName.isEmpty()) {
731 throw new IllegalArgumentException("A Session Name must be provided");
734 List<PolicySession> sessions = this.getSessions();
735 for (PolicySession session : sessions) {
736 if (sessionName.equals(session.getName()) || sessionName.equals(session.getFullName())) {
741 throw invalidSessNameEx(sessionName);
744 private IllegalArgumentException invalidSessNameEx(String sessionName) {
745 return new IllegalArgumentException("Invalid Session Name: " + sessionName);
749 public Map<String, Integer> factClassNames(String sessionName) {
750 validateSessionName(sessionName);
752 Map<String, Integer> classNames = new HashMap<>();
754 PolicySession session = getSession(sessionName);
755 KieSession kieSession = session.getKieSession();
757 Collection<FactHandle> facts = kieSession.getFactHandles();
758 for (FactHandle fact : facts) {
760 String className = kieSession.getObject(fact).getClass().getName();
761 if (classNames.containsKey(className)) {
762 classNames.put(className, classNames.get(className) + 1);
764 classNames.put(className, 1);
766 } catch (Exception e) {
767 logger.warn(FACT_RETRIEVE_ERROR, fact, e);
774 private void validateSessionName(String sessionName) {
775 if (sessionName == null || sessionName.isEmpty()) {
776 throw invalidSessNameEx(sessionName);
781 public long factCount(String sessionName) {
782 validateSessionName(sessionName);
784 PolicySession session = getSession(sessionName);
785 return session.getKieSession().getFactCount();
789 public List<Object> facts(String sessionName, String className, boolean delete) {
790 validateSessionName(sessionName);
792 if (className == null || className.isEmpty()) {
793 throw new IllegalArgumentException("Invalid Class Name: " + className);
797 ReflectionUtil.fetchClass(this.policyContainer.getClassLoader(), className);
798 if (factClass == null) {
799 throw new IllegalArgumentException("Class cannot be fetched in model's classloader: " + className);
802 PolicySession session = getSession(sessionName);
803 KieSession kieSession = session.getKieSession();
805 List<Object> factObjects = new ArrayList<>();
807 Collection<FactHandle> factHandles = kieSession.getFactHandles(new ClassObjectFilter(factClass));
808 for (FactHandle factHandle : factHandles) {
810 factObjects.add(kieSession.getObject(factHandle));
812 kieSession.delete(factHandle);
814 } catch (Exception e) {
815 logger.warn(FACT_RETRIEVE_ERROR, factHandle, e);
823 public <T> List<T> facts(@NonNull String sessionName, @NonNull Class<T> clazz) {
824 return facts(sessionName, clazz.getName(), false)
826 .filter(clazz::isInstance)
828 .collect(Collectors.toList());
832 public List<Object> factQuery(String sessionName, String queryName, String queriedEntity,
833 boolean delete, Object... queryParams) {
834 validateSessionName(sessionName);
836 if (queryName == null || queryName.isEmpty()) {
837 throw new IllegalArgumentException("Invalid Query Name: " + queryName);
840 if (queriedEntity == null || queriedEntity.isEmpty()) {
841 throw new IllegalArgumentException("Invalid Queried Entity: " + queriedEntity);
844 PolicySession session = getSession(sessionName);
845 KieSession kieSession = session.getKieSession();
847 validateQueryName(kieSession, queryName);
849 List<Object> factObjects = new ArrayList<>();
851 QueryResults queryResults = kieSession.getQueryResults(queryName, queryParams);
852 for (QueryResultsRow row : queryResults) {
854 factObjects.add(row.get(queriedEntity));
856 kieSession.delete(row.getFactHandle(queriedEntity));
858 } catch (Exception e) {
859 logger.warn("Object cannot be retrieved from row: {}", row, e);
866 private void validateQueryName(KieSession kieSession, String queryName) {
867 for (KiePackage kiePackage : kieSession.getKieBase().getKiePackages()) {
868 for (Query q : kiePackage.getQueries()) {
869 if (q.getName() != null && q.getName().equals(queryName)) {
875 throw new IllegalArgumentException("Invalid Query Name: " + queryName);
879 public <T> boolean delete(@NonNull String sessionName, @NonNull T fact) {
880 String factClassName = fact.getClass().getName();
882 PolicySession session = getSession(sessionName);
883 KieSession kieSession = session.getKieSession();
885 Collection<FactHandle> factHandles = kieSession.getFactHandles(new ClassObjectFilter(fact.getClass()));
886 for (FactHandle factHandle : factHandles) {
888 if (Objects.equals(fact, kieSession.getObject(factHandle))) {
889 logger.info("Deleting {} from {}", factClassName, sessionName);
890 kieSession.delete(factHandle);
893 } catch (Exception e) {
894 logger.warn(FACT_RETRIEVE_ERROR, factHandle, e);
901 public <T> boolean delete(@NonNull T fact) {
902 return this.getSessionNames().stream().map(ss -> delete(ss, fact)).reduce(false, Boolean::logicalOr);
906 public <T> boolean delete(@NonNull String sessionName, @NonNull Class<T> fact) {
907 PolicySession session = getSession(sessionName);
908 KieSession kieSession = session.getKieSession();
910 boolean success = true;
911 Collection<FactHandle> factHandles = kieSession.getFactHandles(new ClassObjectFilter(fact));
912 for (FactHandle factHandle : factHandles) {
914 kieSession.delete(factHandle);
915 } catch (Exception e) {
916 logger.warn(FACT_RETRIEVE_ERROR, factHandle, e);
924 public <T> boolean delete(@NonNull Class<T> fact) {
925 return this.getSessionNames().stream().map(ss -> delete(ss, fact)).reduce(false, Boolean::logicalOr);
930 public Class<?> fetchModelClass(String className) {
931 return ReflectionUtil.fetchClass(this.policyContainer.getClassLoader(), className);
935 * Get recent source events.
937 * @return the recentSourceEvents
940 public Object[] getRecentSourceEvents() {
941 synchronized (this.recentSourceEvents) {
942 Object[] events = new Object[recentSourceEvents.size()];
943 return recentSourceEvents.toArray(events);
948 * Get recent sink events.
950 * @return the recentSinkEvents
953 public String[] getRecentSinkEvents() {
954 synchronized (this.recentSinkEvents) {
955 String[] events = new String[recentSinkEvents.size()];
956 return recentSinkEvents.toArray(events);
961 public boolean isBrained() {
967 public String toString() {
968 StringBuilder builder = new StringBuilder();
970 .append("MavenDroolsController [policyContainer=")
971 .append(policyContainer.getName())
976 .append(", modelClassLoaderHash=")
977 .append(modelClassLoaderHash)
979 return builder.toString();
982 // these may be overridden by junit tests
984 protected EventProtocolCoder getCoderManager() {
985 return EventProtocolCoderConstants.getManager();
988 protected OrderedServiceImpl<DroolsControllerFeatureApi> getDroolsProviders() {
989 return DroolsControllerFeatureApiConstants.getProviders();
992 protected PolicyContainer makePolicyContainer(String groupId, String artifactId, String version) {
993 return new PolicyContainer(groupId, artifactId, version);
996 protected boolean isClass(String className) {
997 return ReflectionUtil.isClass(this.policyContainer.getClassLoader(), className);