2 * ============LICENSE_START=======================================================
4 * ================================================================================
5 * Copyright (C) 2017-2021 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 java.util.ArrayList;
24 import java.util.Collection;
25 import java.util.HashMap;
26 import java.util.List;
28 import java.util.Objects;
29 import java.util.stream.Collectors;
30 import org.apache.commons.collections4.queue.CircularFifoQueue;
31 import org.checkerframework.checker.nullness.qual.NonNull;
32 import org.drools.core.ClassObjectFilter;
33 import org.kie.api.definition.KiePackage;
34 import org.kie.api.definition.rule.Query;
35 import org.kie.api.runtime.KieSession;
36 import org.kie.api.runtime.rule.FactHandle;
37 import org.kie.api.runtime.rule.QueryResults;
38 import org.kie.api.runtime.rule.QueryResultsRow;
39 import org.onap.policy.common.endpoints.event.comm.TopicSink;
40 import org.onap.policy.common.gson.annotation.GsonJsonIgnore;
41 import org.onap.policy.common.gson.annotation.GsonJsonProperty;
42 import org.onap.policy.common.utils.services.FeatureApiUtils;
43 import org.onap.policy.common.utils.services.OrderedServiceImpl;
44 import org.onap.policy.drools.controller.DroolsController;
45 import org.onap.policy.drools.controller.DroolsControllerConstants;
46 import org.onap.policy.drools.core.PolicyContainer;
47 import org.onap.policy.drools.core.PolicySession;
48 import org.onap.policy.drools.core.jmx.PdpJmx;
49 import org.onap.policy.drools.features.DroolsControllerFeatureApi;
50 import org.onap.policy.drools.features.DroolsControllerFeatureApiConstants;
51 import org.onap.policy.drools.protocol.coders.EventProtocolCoder;
52 import org.onap.policy.drools.protocol.coders.EventProtocolCoderConstants;
53 import org.onap.policy.drools.protocol.coders.EventProtocolParams;
54 import org.onap.policy.drools.protocol.coders.JsonProtocolFilter;
55 import org.onap.policy.drools.protocol.coders.TopicCoderFilterConfiguration;
56 import org.onap.policy.drools.protocol.coders.TopicCoderFilterConfiguration.CustomGsonCoder;
57 import org.onap.policy.drools.protocol.coders.TopicCoderFilterConfiguration.PotentialCoderFilter;
58 import org.onap.policy.drools.utils.ReflectionUtil;
59 import org.slf4j.Logger;
60 import org.slf4j.LoggerFactory;
63 * Maven-based Drools Controller that interacts with the
64 * policy-core PolicyContainer and PolicySession to manage
65 * Drools containers instantiated using Maven.
67 public class MavenDroolsController implements DroolsController {
69 private static final String FACT_RETRIEVE_ERROR = "Object cannot be retrieved from fact {}";
74 private static Logger logger = LoggerFactory.getLogger(MavenDroolsController.class);
77 * Policy Container, the access object to the policy-core layer.
80 protected final PolicyContainer policyContainer;
83 * alive status of this drools controller,
84 * reflects invocation of start()/stop() only.
86 protected volatile boolean alive = false;
89 * locked status of this drools controller,
90 * reflects if i/o drools related operations are permitted,
91 * more specifically: offer() and deliver().
92 * It does not affect the ability to start and stop
93 * underlying drools infrastructure
95 protected volatile boolean locked = false;
98 * list of topics, each with associated decoder classes, each
99 * with a list of associated filters.
101 protected List<TopicCoderFilterConfiguration> decoderConfigurations;
104 * list of topics, each with associated encoder classes, each
105 * with a list of associated filters.
107 protected List<TopicCoderFilterConfiguration> encoderConfigurations;
110 * recent source events processed.
112 protected final CircularFifoQueue<Object> recentSourceEvents = new CircularFifoQueue<>(10);
115 * recent sink events processed.
117 protected final CircularFifoQueue<String> recentSinkEvents = new CircularFifoQueue<>(10);
120 * original Drools Model/Rules classloader hash.
122 protected int modelClassLoaderHash;
125 * Expanded version of the constructor.
127 * @param groupId maven group id
128 * @param artifactId maven artifact id
129 * @param version maven version
130 * @param decoderConfigurations list of topic -> decoders -> filters mapping
131 * @param encoderConfigurations list of topic -> encoders -> filters mapping
133 * @throws IllegalArgumentException invalid arguments passed in
135 public MavenDroolsController(String groupId,
138 List<TopicCoderFilterConfiguration> decoderConfigurations,
139 List<TopicCoderFilterConfiguration> encoderConfigurations) {
141 logger.info("drools-controller instantiation [{}:{}:{}]", groupId, artifactId, version);
143 if (groupId == null || groupId.isEmpty()) {
144 throw new IllegalArgumentException("Missing maven group-id coordinate");
147 if (artifactId == null || artifactId.isEmpty()) {
148 throw new IllegalArgumentException("Missing maven artifact-id coordinate");
151 if (version == null || version.isEmpty()) {
152 throw new IllegalArgumentException("Missing maven version coordinate");
155 this.policyContainer = makePolicyContainer(groupId, artifactId, version);
156 this.init(decoderConfigurations, encoderConfigurations);
158 logger.debug("{}: instantiation completed ", this);
162 * init encoding/decoding configuration.
164 * @param decoderConfigurations list of topic -> decoders -> filters mapping
165 * @param encoderConfigurations list of topic -> encoders -> filters mapping
167 protected void init(List<TopicCoderFilterConfiguration> decoderConfigurations,
168 List<TopicCoderFilterConfiguration> encoderConfigurations) {
170 this.decoderConfigurations = decoderConfigurations;
171 this.encoderConfigurations = encoderConfigurations;
173 this.initCoders(decoderConfigurations, true);
174 this.initCoders(encoderConfigurations, false);
176 this.modelClassLoaderHash = this.policyContainer.getClassLoader().hashCode();
180 public void updateToVersion(String newGroupId, String newArtifactId, String newVersion,
181 List<TopicCoderFilterConfiguration> decoderConfigurations,
182 List<TopicCoderFilterConfiguration> encoderConfigurations)
183 throws LinkageError {
185 logger.info("updating version -> [{}:{}:{}]", newGroupId, newArtifactId, newVersion);
187 validateText(newGroupId, "Missing maven group-id coordinate");
188 validateText(newArtifactId, "Missing maven artifact-id coordinate");
189 validateText(newVersion, "Missing maven version coordinate");
191 validateHasBrain(newGroupId, newArtifactId, newVersion);
193 if (newGroupId.equalsIgnoreCase(this.getGroupId())
194 && newArtifactId.equalsIgnoreCase(this.getArtifactId())
195 && newVersion.equalsIgnoreCase(this.getVersion())) {
196 logger.warn("All in the right version: {}:{}:{} vs. {}", newGroupId, newArtifactId, newVersion, this);
200 validateNewVersion(newGroupId, newArtifactId, newVersion);
203 String messages = this.policyContainer.updateToVersion(newVersion);
204 logger.warn("{} UPGRADE results: {}", this, messages);
207 * If all sucessful (can load new container), now we can remove all coders from previous sessions
214 this.init(decoderConfigurations, encoderConfigurations);
216 logger.info("UPDATE-TO-VERSION: completed {}", this);
219 private void validateText(String text, String errorMessage) {
220 if (text == null || text.isEmpty()) {
221 throw new IllegalArgumentException(errorMessage);
225 private void validateHasBrain(String newGroupId, String newArtifactId, String newVersion) {
226 if (newGroupId.equalsIgnoreCase(DroolsControllerConstants.NO_GROUP_ID)
227 || newArtifactId.equalsIgnoreCase(DroolsControllerConstants.NO_ARTIFACT_ID)
228 || newVersion.equalsIgnoreCase(DroolsControllerConstants.NO_VERSION)) {
229 throw new IllegalArgumentException("BRAINLESS maven coordinates provided: "
230 + newGroupId + ":" + newArtifactId + ":"
235 private void validateNewVersion(String newGroupId, String newArtifactId, String newVersion) {
236 if (!newGroupId.equalsIgnoreCase(this.getGroupId())
237 || !newArtifactId.equalsIgnoreCase(this.getArtifactId())) {
238 throw new IllegalArgumentException(
239 "Group ID and Artifact ID maven coordinates must be identical for the upgrade: "
240 + newGroupId + ":" + newArtifactId + ":"
241 + newVersion + " vs. " + this);
246 * initialize decoders for all the topics supported by this controller
247 * Note this is critical to be done after the Policy Container is
248 * instantiated to be able to fetch the corresponding classes.
250 * @param coderConfigurations list of topic -> decoders -> filters mapping
252 protected void initCoders(List<TopicCoderFilterConfiguration> coderConfigurations,
255 logger.info("INIT-CODERS: {}", this);
257 if (coderConfigurations == null) {
262 for (TopicCoderFilterConfiguration coderConfig: coderConfigurations) {
263 String topic = coderConfig.getTopic();
265 CustomGsonCoder customGsonCoder = getCustomCoder(coderConfig);
267 List<PotentialCoderFilter> coderFilters = coderConfig.getCoderFilters();
268 if (coderFilters == null || coderFilters.isEmpty()) {
272 for (PotentialCoderFilter coderFilter : coderFilters) {
273 String potentialCodedClass = coderFilter.getCodedClass();
274 JsonProtocolFilter protocolFilter = coderFilter.getFilter();
276 if (!isClass(potentialCodedClass)) {
277 throw makeRetrieveEx(potentialCodedClass);
279 logClassFetched(potentialCodedClass);
283 getCoderManager().addDecoder(EventProtocolParams.builder()
284 .groupId(this.getGroupId())
285 .artifactId(this.getArtifactId())
287 .eventClass(potentialCodedClass)
288 .protocolFilter(protocolFilter)
289 .customGsonCoder(customGsonCoder)
290 .modelClassLoaderHash(this.policyContainer.getClassLoader().hashCode()));
292 getCoderManager().addEncoder(
293 EventProtocolParams.builder().groupId(this.getGroupId())
294 .artifactId(this.getArtifactId()).topic(topic)
295 .eventClass(potentialCodedClass).protocolFilter(protocolFilter)
296 .customGsonCoder(customGsonCoder)
297 .modelClassLoaderHash(this.policyContainer.getClassLoader().hashCode()));
303 private CustomGsonCoder getCustomCoder(TopicCoderFilterConfiguration coderConfig) {
304 CustomGsonCoder customGsonCoder = coderConfig.getCustomGsonCoder();
305 if (customGsonCoder != null
306 && customGsonCoder.getClassContainer() != null
307 && !customGsonCoder.getClassContainer().isEmpty()) {
309 String customGsonCoderClass = customGsonCoder.getClassContainer();
310 if (!isClass(customGsonCoderClass)) {
311 throw makeRetrieveEx(customGsonCoderClass);
313 logClassFetched(customGsonCoderClass);
316 return customGsonCoder;
320 * Logs an error and makes an exception for an item that cannot be retrieved.
321 * @param itemName the item to retrieve
322 * @return a new exception
324 private IllegalArgumentException makeRetrieveEx(String itemName) {
325 logger.error("{} cannot be retrieved", itemName);
326 return new IllegalArgumentException(itemName + " cannot be retrieved");
330 * Logs the name of the class that was fetched.
331 * @param className class name fetched
333 private void logClassFetched(String className) {
334 logger.info("CLASS FETCHED {}", className);
341 protected void removeDecoders() {
342 logger.info("REMOVE-DECODERS: {}", this);
344 if (this.decoderConfigurations == null) {
349 for (TopicCoderFilterConfiguration coderConfig: decoderConfigurations) {
350 String topic = coderConfig.getTopic();
351 getCoderManager().removeDecoders(this.getGroupId(), this.getArtifactId(), topic);
358 protected void removeEncoders() {
360 logger.info("REMOVE-ENCODERS: {}", this);
362 if (this.encoderConfigurations == null) {
366 for (TopicCoderFilterConfiguration coderConfig: encoderConfigurations) {
367 String topic = coderConfig.getTopic();
368 getCoderManager().removeEncoders(this.getGroupId(), this.getArtifactId(), topic);
374 public boolean ownsCoder(Class<?> coderClass, int modelHash) {
375 if (!isClass(coderClass.getName())) {
376 logger.error("{}{} cannot be retrieved. ", this, coderClass.getName());
380 if (modelHash == this.modelClassLoaderHash) {
381 logger.info("{}{} class loader matches original drools controller rules classloader {}",
382 coderClass.getName(), this, coderClass.getClassLoader());
385 logger.warn("{}{} class loaders don't match {} vs {}", this, coderClass.getName(),
386 coderClass.getClassLoader(), this.policyContainer.getClassLoader());
392 public boolean start() {
394 logger.info("START: {}", this);
396 synchronized (this) {
403 return this.policyContainer.start();
407 public boolean stop() {
409 logger.info("STOP: {}", this);
411 synchronized (this) {
418 return this.policyContainer.stop();
422 public void shutdown() {
423 logger.info("{}: SHUTDOWN", this);
428 } catch (Exception e) {
429 logger.error("{} SHUTDOWN FAILED because of {}", this, e.getMessage(), e);
431 this.policyContainer.shutdown();
438 logger.info("{}: HALT", this);
443 } catch (Exception e) {
444 logger.error("{} HALT FAILED because of {}", this, e.getMessage(), e);
446 this.policyContainer.destroy();
451 * removes this drools controllers and encoders and decoders from operation.
453 protected void removeCoders() {
454 logger.info("{}: REMOVE-CODERS", this);
457 this.removeDecoders();
458 } catch (IllegalArgumentException e) {
459 logger.error("{} REMOVE-DECODERS FAILED because of {}", this, e.getMessage(), e);
463 this.removeEncoders();
464 } catch (IllegalArgumentException e) {
465 logger.error("{} REMOVE-ENCODERS FAILED because of {}", this, e.getMessage(), e);
470 public boolean isAlive() {
475 public boolean offer(String topic, String event) {
476 logger.debug("{}: OFFER raw event from {}", this, topic);
478 if (this.locked || !this.alive || this.policyContainer.getPolicySessions().isEmpty()) {
482 // 1. Now, check if this topic has a decoder:
484 if (!getCoderManager().isDecodingSupported(this.getGroupId(),
485 this.getArtifactId(),
488 logger.warn("{}: DECODING-UNSUPPORTED {}:{}:{}", this, // NOSONAR
489 topic, this.getGroupId(), this.getArtifactId());
497 anEvent = getCoderManager().decode(this.getGroupId(),
498 this.getArtifactId(),
501 } catch (UnsupportedOperationException uoe) {
502 logger.debug("{}: DECODE FAILED: {} <- {} because of {}", this, topic,
503 event, uoe.getMessage(), uoe);
505 } catch (Exception e) {
506 logger.warn("{}: DECODE FAILED: {} <- {} because of {}", this, topic,
507 event, e.getMessage(), e);
511 return offer(anEvent);
516 * This method always returns "true", which causes a sonar complaint. However,
517 * refactoring or restructuring it would unnecessarily complicate it, thus we'll just
518 * disable the sonar complaint.
521 public <T> boolean offer(T event) { // NOSONAR
522 logger.debug("{}: OFFER event", this);
524 if (this.locked || !this.alive || this.policyContainer.getPolicySessions().isEmpty()) {
528 synchronized (this.recentSourceEvents) {
529 this.recentSourceEvents.add(event);
532 PdpJmx.getInstance().updateOccured();
536 if (FeatureApiUtils.apply(getDroolsProviders().getList(),
537 feature -> feature.beforeInsert(this, event),
538 (feature, ex) -> logger.error("{}: feature {} before-insert failure because of {}", this,
539 feature.getClass().getName(), ex.getMessage(), ex))) {
543 boolean successInject = this.policyContainer.insertAll(event);
544 if (!successInject) {
545 logger.warn("{} Failed to inject into PolicyContainer {}", this, this.getSessionNames());
548 FeatureApiUtils.apply(getDroolsProviders().getList(),
549 feature -> feature.afterInsert(this, event, successInject),
550 (feature, ex) -> logger.error("{}: feature {} after-insert failure because of {}", this,
551 feature.getClass().getName(), ex.getMessage(), ex));
558 public boolean deliver(TopicSink sink, Object event) {
560 logger.info("{}DELIVER: {} FROM {} TO {}", this, event, this, sink);
562 for (DroolsControllerFeatureApi feature : getDroolsProviders().getList()) {
564 if (feature.beforeDeliver(this, sink, event)) {
567 } catch (Exception e) {
568 logger.error("{}: feature {} before-deliver failure because of {}", this, feature.getClass().getName(),
574 throw new IllegalArgumentException(this + " invalid sink");
578 throw new IllegalArgumentException(this + " invalid event");
582 throw new IllegalStateException(this + " is locked");
586 throw new IllegalStateException(this + " is stopped");
590 getCoderManager().encode(sink.getTopic(), event, this);
592 synchronized (this.recentSinkEvents) {
593 this.recentSinkEvents.add(json);
596 boolean success = sink.send(json);
598 for (DroolsControllerFeatureApi feature : getDroolsProviders().getList()) {
600 if (feature.afterDeliver(this, sink, event, json, success)) {
603 } catch (Exception e) {
604 logger.error("{}: feature {} after-deliver failure because of {}", this, feature.getClass().getName(),
614 public String getVersion() {
615 return this.policyContainer.getVersion();
619 public String getArtifactId() {
620 return this.policyContainer.getArtifactId();
624 public String getGroupId() {
625 return this.policyContainer.getGroupId();
629 * Get model class loader hash.
631 * @return the modelClassLoaderHash
633 public int getModelClassLoaderHash() {
634 return modelClassLoaderHash;
638 public synchronized boolean lock() {
639 logger.info("LOCK: {}", this);
646 public synchronized boolean unlock() {
647 logger.info("UNLOCK: {}", this);
654 public boolean isLocked() {
660 public PolicyContainer getContainer() {
661 return this.policyContainer;
664 @GsonJsonProperty("sessions")
666 public List<String> getSessionNames() {
667 return getSessionNames(true);
673 * @param abbreviated true for the short form, otherwise the long form
674 * @return session names
676 protected List<String> getSessionNames(boolean abbreviated) {
677 List<String> sessionNames = new ArrayList<>();
679 for (PolicySession session: this.policyContainer.getPolicySessions()) {
681 sessionNames.add(session.getName());
683 sessionNames.add(session.getFullName());
686 } catch (Exception e) {
687 logger.warn("Can't retrieve CORE sessions", e);
688 sessionNames.add(e.getMessage());
693 @GsonJsonProperty("sessionCoordinates")
695 public List<String> getCanonicalSessionNames() {
696 return getSessionNames(false);
700 public List<String> getBaseDomainNames() {
701 return new ArrayList<>(this.policyContainer.getKieContainer().getKieBaseNames());
705 * provides the underlying core layer container sessions.
707 * @return the attached Policy Container
709 protected List<PolicySession> getSessions() {
710 List<PolicySession> sessions = new ArrayList<>();
711 sessions.addAll(this.policyContainer.getPolicySessions());
716 * provides the underlying core layer container session with name sessionName.
718 * @param sessionName session name
719 * @return the attached Policy Container
720 * @throws IllegalArgumentException when an invalid session name is provided
721 * @throws IllegalStateException when the drools controller is in an invalid state
723 protected PolicySession getSession(String sessionName) {
724 if (sessionName == null || sessionName.isEmpty()) {
725 throw new IllegalArgumentException("A Session Name must be provided");
728 List<PolicySession> sessions = this.getSessions();
729 for (PolicySession session : sessions) {
730 if (sessionName.equals(session.getName()) || sessionName.equals(session.getFullName())) {
735 throw invalidSessNameEx(sessionName);
738 private IllegalArgumentException invalidSessNameEx(String sessionName) {
739 return new IllegalArgumentException("Invalid Session Name: " + sessionName);
743 public Map<String, Integer> factClassNames(String sessionName) {
744 validateSessionName(sessionName);
746 Map<String, Integer> classNames = new HashMap<>();
748 PolicySession session = getSession(sessionName);
749 KieSession kieSession = session.getKieSession();
751 Collection<FactHandle> facts = kieSession.getFactHandles();
752 for (FactHandle fact : facts) {
754 String className = kieSession.getObject(fact).getClass().getName();
755 if (classNames.containsKey(className)) {
756 classNames.put(className, classNames.get(className) + 1);
758 classNames.put(className, 1);
760 } catch (Exception e) {
761 logger.warn(FACT_RETRIEVE_ERROR, fact, e);
768 private void validateSessionName(String sessionName) {
769 if (sessionName == null || sessionName.isEmpty()) {
770 throw invalidSessNameEx(sessionName);
775 public long factCount(String sessionName) {
776 validateSessionName(sessionName);
778 PolicySession session = getSession(sessionName);
779 return session.getKieSession().getFactCount();
783 public List<Object> facts(String sessionName, String className, boolean delete) {
784 validateSessionName(sessionName);
786 if (className == null || className.isEmpty()) {
787 throw new IllegalArgumentException("Invalid Class Name: " + className);
791 ReflectionUtil.fetchClass(this.policyContainer.getClassLoader(), className);
792 if (factClass == null) {
793 throw new IllegalArgumentException("Class cannot be fetched in model's classloader: " + className);
796 PolicySession session = getSession(sessionName);
797 KieSession kieSession = session.getKieSession();
799 List<Object> factObjects = new ArrayList<>();
801 Collection<FactHandle> factHandles = kieSession.getFactHandles(new ClassObjectFilter(factClass));
802 for (FactHandle factHandle : factHandles) {
804 factObjects.add(kieSession.getObject(factHandle));
806 kieSession.delete(factHandle);
808 } catch (Exception e) {
809 logger.warn(FACT_RETRIEVE_ERROR, factHandle, e);
817 public <T> List<T> facts(@NonNull String sessionName, @NonNull Class<T> clazz) {
818 return facts(sessionName, clazz.getName(), false)
820 .filter(clazz::isInstance)
822 .collect(Collectors.toList());
826 public List<Object> factQuery(String sessionName, String queryName, String queriedEntity,
827 boolean delete, Object... queryParams) {
828 validateSessionName(sessionName);
830 if (queryName == null || queryName.isEmpty()) {
831 throw new IllegalArgumentException("Invalid Query Name: " + queryName);
834 if (queriedEntity == null || queriedEntity.isEmpty()) {
835 throw new IllegalArgumentException("Invalid Queried Entity: " + queriedEntity);
838 PolicySession session = getSession(sessionName);
839 KieSession kieSession = session.getKieSession();
841 validateQueryName(kieSession, queryName);
843 List<Object> factObjects = new ArrayList<>();
845 QueryResults queryResults = kieSession.getQueryResults(queryName, queryParams);
846 for (QueryResultsRow row : queryResults) {
848 factObjects.add(row.get(queriedEntity));
850 kieSession.delete(row.getFactHandle(queriedEntity));
852 } catch (Exception e) {
853 logger.warn("Object cannot be retrieved from row: {}", row, e);
860 private void validateQueryName(KieSession kieSession, String queryName) {
861 for (KiePackage kiePackage : kieSession.getKieBase().getKiePackages()) {
862 for (Query q : kiePackage.getQueries()) {
863 if (q.getName() != null && q.getName().equals(queryName)) {
869 throw new IllegalArgumentException("Invalid Query Name: " + queryName);
873 public <T> boolean delete(@NonNull String sessionName, @NonNull T objFact) {
874 KieSession kieSession = getSession(sessionName).getKieSession();
876 // try first to get the object to delete first by reference
878 FactHandle quickFact = kieSession.getFactHandle(objFact);
879 if (quickFact != null) {
880 logger.info("Fast delete of {} from {}", objFact, sessionName);
881 kieSession.delete(quickFact);
885 // otherwise, try to the delete the fact associated with the object by scanning all
886 // facts from the same type and performing object equality. The set of facts
887 // is restricted to those of the same type
889 Collection<FactHandle> factHandles = kieSession.getFactHandles(new ClassObjectFilter(objFact.getClass()));
890 for (FactHandle factHandle : factHandles) {
891 if (Objects.equals(objFact, kieSession.getObject(factHandle))) {
892 logger.info("Slow delete of {} of type {} from {}",
893 objFact, objFact.getClass().getName(), sessionName);
894 kieSession.delete(factHandle);
903 public <T> boolean delete(@NonNull T fact) {
904 return this.getSessionNames().stream().map(ss -> delete(ss, fact)).reduce(false, Boolean::logicalOr);
908 public <T> boolean delete(@NonNull String sessionName, @NonNull Class<T> fact) {
909 PolicySession session = getSession(sessionName);
910 KieSession kieSession = session.getKieSession();
912 boolean success = true;
913 Collection<FactHandle> factHandles = kieSession.getFactHandles(new ClassObjectFilter(fact));
914 for (FactHandle factHandle : factHandles) {
916 kieSession.delete(factHandle);
917 } catch (Exception e) {
918 logger.warn(FACT_RETRIEVE_ERROR, factHandle, e);
926 public <T> boolean delete(@NonNull Class<T> fact) {
927 return this.getSessionNames().stream().map(ss -> delete(ss, fact)).reduce(false, Boolean::logicalOr);
931 public <T> boolean exists(@NonNull String sessionName, @NonNull T objFact) {
932 KieSession kieSession = getSession(sessionName).getKieSession();
933 if (kieSession.getFactHandle(objFact) != null) {
937 // try to find the object by equality comparison instead if it could not be
938 // found by reference
940 Collection<FactHandle> factHandles = kieSession.getFactHandles(new ClassObjectFilter(objFact.getClass()));
941 for (FactHandle factHandle : factHandles) {
942 if (Objects.equals(objFact, kieSession.getObject(factHandle))) {
951 public <T> boolean exists(@NonNull T fact) {
952 return this.getSessionNames().stream().anyMatch(ss -> exists(ss, fact));
956 public Class<?> fetchModelClass(String className) {
957 return ReflectionUtil.fetchClass(this.policyContainer.getClassLoader(), className);
961 * Get recent source events.
963 * @return the recentSourceEvents
966 public Object[] getRecentSourceEvents() {
967 synchronized (this.recentSourceEvents) {
968 Object[] events = new Object[recentSourceEvents.size()];
969 return recentSourceEvents.toArray(events);
974 * Get recent sink events.
976 * @return the recentSinkEvents
979 public String[] getRecentSinkEvents() {
980 synchronized (this.recentSinkEvents) {
981 String[] events = new String[recentSinkEvents.size()];
982 return recentSinkEvents.toArray(events);
987 public boolean isBrained() {
993 public String toString() {
994 StringBuilder builder = new StringBuilder();
996 .append("MavenDroolsController [policyContainer=")
997 .append(policyContainer.getName())
1001 .append(", locked=")
1002 .append(", modelClassLoaderHash=")
1003 .append(modelClassLoaderHash)
1005 return builder.toString();
1008 // these may be overridden by junit tests
1010 protected EventProtocolCoder getCoderManager() {
1011 return EventProtocolCoderConstants.getManager();
1014 protected OrderedServiceImpl<DroolsControllerFeatureApi> getDroolsProviders() {
1015 return DroolsControllerFeatureApiConstants.getProviders();
1018 protected PolicyContainer makePolicyContainer(String groupId, String artifactId, String version) {
1019 return new PolicyContainer(groupId, artifactId, version);
1022 protected boolean isClass(String className) {
1023 return ReflectionUtil.isClass(this.policyContainer.getClassLoader(), className);