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.common.utils.services.OrderedServiceImpl;
45 import org.onap.policy.drools.controller.DroolsController;
46 import org.onap.policy.drools.controller.DroolsControllerConstants;
47 import org.onap.policy.drools.core.PolicyContainer;
48 import org.onap.policy.drools.core.PolicySession;
49 import org.onap.policy.drools.core.jmx.PdpJmx;
50 import org.onap.policy.drools.features.DroolsControllerFeatureApi;
51 import org.onap.policy.drools.features.DroolsControllerFeatureApiConstants;
52 import org.onap.policy.drools.protocol.coders.EventProtocolCoder;
53 import org.onap.policy.drools.protocol.coders.EventProtocolCoderConstants;
54 import org.onap.policy.drools.protocol.coders.EventProtocolParams;
55 import org.onap.policy.drools.protocol.coders.JsonProtocolFilter;
56 import org.onap.policy.drools.protocol.coders.TopicCoderFilterConfiguration;
57 import org.onap.policy.drools.protocol.coders.TopicCoderFilterConfiguration.CustomGsonCoder;
58 import org.onap.policy.drools.protocol.coders.TopicCoderFilterConfiguration.PotentialCoderFilter;
59 import org.onap.policy.drools.utils.ReflectionUtil;
60 import org.slf4j.Logger;
61 import org.slf4j.LoggerFactory;
64 * Maven-based Drools Controller that interacts with the
65 * policy-core PolicyContainer and PolicySession to manage
66 * Drools containers instantiated using Maven.
68 public class MavenDroolsController implements DroolsController {
70 private static final String FACT_RETRIEVE_ERROR = "Object cannot be retrieved from fact {}";
75 private static Logger logger = LoggerFactory.getLogger(MavenDroolsController.class);
78 * Policy Container, the access object to the policy-core layer.
82 protected final PolicyContainer policyContainer;
85 * alive status of this drools controller,
86 * reflects invocation of start()/stop() only.
88 protected volatile boolean alive = false;
91 * locked status of this drools controller,
92 * reflects if i/o drools related operations are permitted,
93 * more specifically: offer() and deliver().
94 * It does not affect the ability to start and stop
95 * underlying drools infrastructure
97 protected volatile boolean locked = false;
100 * list of topics, each with associated decoder classes, each
101 * with a list of associated filters.
103 protected List<TopicCoderFilterConfiguration> decoderConfigurations;
106 * list of topics, each with associated encoder classes, each
107 * with a list of associated filters.
109 protected List<TopicCoderFilterConfiguration> encoderConfigurations;
112 * recent source events processed.
114 protected final CircularFifoQueue<Object> recentSourceEvents = new CircularFifoQueue<>(10);
117 * recent sink events processed.
119 protected final CircularFifoQueue<String> recentSinkEvents = new CircularFifoQueue<>(10);
122 * original Drools Model/Rules classloader hash.
124 protected int modelClassLoaderHash;
127 * Expanded version of the constructor.
129 * @param groupId maven group id
130 * @param artifactId maven artifact id
131 * @param version maven version
132 * @param decoderConfigurations list of topic -> decoders -> filters mapping
133 * @param encoderConfigurations list of topic -> encoders -> filters mapping
135 * @throws IllegalArgumentException invalid arguments passed in
137 public MavenDroolsController(String groupId,
140 List<TopicCoderFilterConfiguration> decoderConfigurations,
141 List<TopicCoderFilterConfiguration> encoderConfigurations) {
143 logger.info("drools-controller instantiation [{}:{}:{}]", groupId, artifactId, version);
145 if (groupId == null || groupId.isEmpty()) {
146 throw new IllegalArgumentException("Missing maven group-id coordinate");
149 if (artifactId == null || artifactId.isEmpty()) {
150 throw new IllegalArgumentException("Missing maven artifact-id coordinate");
153 if (version == null || version.isEmpty()) {
154 throw new IllegalArgumentException("Missing maven version coordinate");
157 this.policyContainer = makePolicyContainer(groupId, artifactId, version);
158 this.init(decoderConfigurations, encoderConfigurations);
160 logger.debug("{}: instantiation completed ", this);
164 * init encoding/decoding configuration.
166 * @param decoderConfigurations list of topic -> decoders -> filters mapping
167 * @param encoderConfigurations list of topic -> encoders -> filters mapping
169 protected void init(List<TopicCoderFilterConfiguration> decoderConfigurations,
170 List<TopicCoderFilterConfiguration> encoderConfigurations) {
172 this.decoderConfigurations = decoderConfigurations;
173 this.encoderConfigurations = encoderConfigurations;
175 this.initCoders(decoderConfigurations, true);
176 this.initCoders(encoderConfigurations, false);
178 this.modelClassLoaderHash = this.policyContainer.getClassLoader().hashCode();
182 public void updateToVersion(String newGroupId, String newArtifactId, String newVersion,
183 List<TopicCoderFilterConfiguration> decoderConfigurations,
184 List<TopicCoderFilterConfiguration> encoderConfigurations)
185 throws LinkageError {
187 logger.info("updating version -> [{}:{}:{}]", newGroupId, newArtifactId, newVersion);
189 if (newGroupId == null || newGroupId.isEmpty()) {
190 throw new IllegalArgumentException("Missing maven group-id coordinate");
193 if (newArtifactId == null || newArtifactId.isEmpty()) {
194 throw new IllegalArgumentException("Missing maven artifact-id coordinate");
197 if (newVersion == null || newVersion.isEmpty()) {
198 throw new IllegalArgumentException("Missing maven version coordinate");
201 if (newGroupId.equalsIgnoreCase(DroolsControllerConstants.NO_GROUP_ID)
202 || newArtifactId.equalsIgnoreCase(DroolsControllerConstants.NO_ARTIFACT_ID)
203 || newVersion.equalsIgnoreCase(DroolsControllerConstants.NO_VERSION)) {
204 throw new IllegalArgumentException("BRAINLESS maven coordinates provided: "
205 + newGroupId + ":" + newArtifactId + ":"
209 if (newGroupId.equalsIgnoreCase(this.getGroupId())
210 && newArtifactId.equalsIgnoreCase(this.getArtifactId())
211 && newVersion.equalsIgnoreCase(this.getVersion())) {
212 logger.warn("All in the right version: " + newGroupId + ":"
213 + newArtifactId + ":" + newVersion + " vs. " + this);
217 if (!newGroupId.equalsIgnoreCase(this.getGroupId())
218 || !newArtifactId.equalsIgnoreCase(this.getArtifactId())) {
219 throw new IllegalArgumentException(
220 "Group ID and Artifact ID maven coordinates must be identical for the upgrade: "
221 + newGroupId + ":" + newArtifactId + ":"
222 + newVersion + " vs. " + this);
226 String messages = this.policyContainer.updateToVersion(newVersion);
227 logger.warn("{} UPGRADE results: {}", this, messages);
230 * If all sucessful (can load new container), now we can remove all coders from previous sessions
237 this.init(decoderConfigurations, encoderConfigurations);
239 logger.info("UPDATE-TO-VERSION: completed {}", this);
243 * initialize decoders for all the topics supported by this controller
244 * Note this is critical to be done after the Policy Container is
245 * instantiated to be able to fetch the corresponding classes.
247 * @param coderConfigurations list of topic -> decoders -> filters mapping
249 protected void initCoders(List<TopicCoderFilterConfiguration> coderConfigurations,
252 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 (customGsonCoder != null
264 && customGsonCoder.getClassContainer() != null
265 && !customGsonCoder.getClassContainer().isEmpty()) {
267 String customGsonCoderClass = customGsonCoder.getClassContainer();
268 if (!isClass(customGsonCoderClass)) {
269 throw makeRetrieveEx(customGsonCoderClass);
271 logClassFetched(customGsonCoderClass);
275 List<PotentialCoderFilter> coderFilters = coderConfig.getCoderFilters();
276 if (coderFilters == null || coderFilters.isEmpty()) {
280 for (PotentialCoderFilter coderFilter : coderFilters) {
281 String potentialCodedClass = coderFilter.getCodedClass();
282 JsonProtocolFilter protocolFilter = coderFilter.getFilter();
284 if (!isClass(potentialCodedClass)) {
285 throw makeRetrieveEx(potentialCodedClass);
287 logClassFetched(potentialCodedClass);
291 getCoderManager().addDecoder(EventProtocolParams.builder()
292 .groupId(this.getGroupId())
293 .artifactId(this.getArtifactId())
295 .eventClass(potentialCodedClass)
296 .protocolFilter(protocolFilter)
297 .customGsonCoder(customGsonCoder)
298 .modelClassLoaderHash(this.policyContainer.getClassLoader().hashCode()));
300 getCoderManager().addEncoder(
301 EventProtocolParams.builder().groupId(this.getGroupId())
302 .artifactId(this.getArtifactId()).topic(topic)
303 .eventClass(potentialCodedClass).protocolFilter(protocolFilter)
304 .customGsonCoder(customGsonCoder)
305 .modelClassLoaderHash(this.policyContainer.getClassLoader().hashCode()));
312 * Logs an error and makes an exception for an item that cannot be retrieved.
313 * @param itemName the item to retrieve
314 * @return a new exception
316 private IllegalArgumentException makeRetrieveEx(String itemName) {
317 logger.error("{} cannot be retrieved", itemName);
318 return new IllegalArgumentException(itemName + " cannot be retrieved");
322 * Logs the name of the class that was fetched.
323 * @param className class name fetched
325 private void logClassFetched(String className) {
326 logger.info("CLASS FETCHED {}", className);
333 protected void removeDecoders() {
334 logger.info("REMOVE-DECODERS: {}", this);
336 if (this.decoderConfigurations == null) {
341 for (TopicCoderFilterConfiguration coderConfig: decoderConfigurations) {
342 String topic = coderConfig.getTopic();
343 getCoderManager().removeDecoders(this.getGroupId(), this.getArtifactId(), topic);
350 protected void removeEncoders() {
352 logger.info("REMOVE-ENCODERS: {}", this);
354 if (this.encoderConfigurations == null) {
358 for (TopicCoderFilterConfiguration coderConfig: encoderConfigurations) {
359 String topic = coderConfig.getTopic();
360 getCoderManager().removeEncoders(this.getGroupId(), this.getArtifactId(), topic);
366 public boolean ownsCoder(Class<? extends Object> coderClass, int modelHash) {
367 if (!isClass(coderClass.getName())) {
368 logger.error("{}{} cannot be retrieved. ", this, coderClass.getName());
372 if (modelHash == this.modelClassLoaderHash) {
373 logger.info("{}{} class loader matches original drools controller rules classloader {}",
374 coderClass.getName(), this, coderClass.getClassLoader());
377 logger.warn("{}{} class loaders don't match {} vs {}", this, coderClass.getName(),
378 coderClass.getClassLoader(), this.policyContainer.getClassLoader());
384 public boolean start() {
386 logger.info("START: {}", this);
388 synchronized (this) {
395 return this.policyContainer.start();
399 public boolean stop() {
401 logger.info("STOP: {}", this);
403 synchronized (this) {
410 return this.policyContainer.stop();
414 public void shutdown() {
415 logger.info("{}: SHUTDOWN", this);
420 } catch (Exception e) {
421 logger.error("{} SHUTDOWN FAILED because of {}", this, e.getMessage(), e);
423 this.policyContainer.shutdown();
430 logger.info("{}: HALT", this);
435 } catch (Exception e) {
436 logger.error("{} HALT FAILED because of {}", this, e.getMessage(), e);
438 this.policyContainer.destroy();
443 * removes this drools controllers and encoders and decoders from operation.
445 protected void removeCoders() {
446 logger.info("{}: REMOVE-CODERS", this);
449 this.removeDecoders();
450 } catch (IllegalArgumentException e) {
451 logger.error("{} REMOVE-DECODERS FAILED because of {}", this, e.getMessage(), e);
455 this.removeEncoders();
456 } catch (IllegalArgumentException e) {
457 logger.error("{} REMOVE-ENCODERS FAILED because of {}", this, e.getMessage(), e);
462 public boolean isAlive() {
467 public boolean offer(String topic, String event) {
468 logger.debug("{}: OFFER raw event from {}", this, topic);
470 if (this.locked || !this.alive || this.policyContainer.getPolicySessions().isEmpty()) {
474 // 1. Now, check if this topic has a decoder:
476 if (!getCoderManager().isDecodingSupported(this.getGroupId(),
477 this.getArtifactId(),
480 logger.warn("{}: DECODING-UNSUPPORTED {}:{}:{}", this,
481 topic, this.getGroupId(), this.getArtifactId());
489 anEvent = getCoderManager().decode(this.getGroupId(),
490 this.getArtifactId(),
493 } catch (UnsupportedOperationException uoe) {
494 logger.debug("{}: DECODE FAILED: {} <- {} because of {}", this, topic,
495 event, uoe.getMessage(), uoe);
497 } catch (Exception e) {
498 logger.warn("{}: DECODE FAILED: {} <- {} because of {}", this, topic,
499 event, e.getMessage(), e);
503 return offer(anEvent);
508 public <T> boolean offer(T event) {
509 logger.debug("{}: OFFER event", this);
511 if (this.locked || !this.alive || this.policyContainer.getPolicySessions().isEmpty()) {
515 synchronized (this.recentSourceEvents) {
516 this.recentSourceEvents.add(event);
519 PdpJmx.getInstance().updateOccured();
523 for (DroolsControllerFeatureApi feature : getDroolsProviders().getList()) {
525 if (feature.beforeInsert(this, event)) {
528 } catch (Exception e) {
529 logger.error("{}: feature {} before-insert failure because of {}",
530 this, feature.getClass().getName(), e.getMessage(), e);
534 boolean successInject = this.policyContainer.insertAll(event);
535 if (!successInject) {
536 logger.warn(this + "Failed to inject into PolicyContainer {}", this.getSessionNames());
539 for (DroolsControllerFeatureApi feature : getDroolsProviders().getList()) {
541 if (feature.afterInsert(this, event, successInject)) {
544 } catch (Exception e) {
545 logger.error("{}: feature {} after-insert failure because of {}",
546 this, feature.getClass().getName(), e.getMessage(), e);
555 public boolean deliver(TopicSink sink, Object event) {
557 logger.info("{}DELIVER: {} FROM {} TO {}", this, event, this, sink);
559 for (DroolsControllerFeatureApi feature : getDroolsProviders().getList()) {
561 if (feature.beforeDeliver(this, sink, event)) {
565 catch (Exception e) {
566 logger.error("{}: feature {} before-deliver failure because of {}", this, feature.getClass().getName(),
572 throw new IllegalArgumentException(this + " invalid sink");
576 throw new IllegalArgumentException(this + " invalid event");
580 throw new IllegalStateException(this + " is locked");
584 throw new IllegalStateException(this + " is stopped");
588 getCoderManager().encode(sink.getTopic(), event, this);
590 synchronized (this.recentSinkEvents) {
591 this.recentSinkEvents.add(json);
594 boolean success = sink.send(json);
596 for (DroolsControllerFeatureApi feature : getDroolsProviders().getList()) {
598 if (feature.afterDeliver(this, sink, event, json, success)) {
602 catch (Exception e) {
603 logger.error("{}: feature {} after-deliver failure because of {}", this, feature.getClass().getName(),
613 public String getVersion() {
614 return this.policyContainer.getVersion();
618 public String getArtifactId() {
619 return this.policyContainer.getArtifactId();
623 public String getGroupId() {
624 return this.policyContainer.getGroupId();
628 * Get model class loader hash.
630 * @return the modelClassLoaderHash
632 public int getModelClassLoaderHash() {
633 return modelClassLoaderHash;
637 public synchronized boolean lock() {
638 logger.info("LOCK: {}", this);
645 public synchronized boolean unlock() {
646 logger.info("UNLOCK: {}", this);
653 public boolean isLocked() {
660 public PolicyContainer getContainer() {
661 return this.policyContainer;
664 @JsonProperty("sessions")
665 @GsonJsonProperty("sessions")
667 public List<String> getSessionNames() {
668 return getSessionNames(true);
674 * @param abbreviated true for the short form, otherwise the long form
675 * @return session names
677 protected List<String> getSessionNames(boolean abbreviated) {
678 List<String> sessionNames = new ArrayList<>();
680 for (PolicySession session: this.policyContainer.getPolicySessions()) {
682 sessionNames.add(session.getName());
684 sessionNames.add(session.getFullName());
687 } catch (Exception e) {
688 logger.warn("Can't retrieve CORE sessions: " + e.getMessage(), e);
689 sessionNames.add(e.getMessage());
694 @JsonProperty("sessionCoordinates")
695 @GsonJsonProperty("sessionCoordinates")
697 public List<String> getCanonicalSessionNames() {
698 return getSessionNames(false);
702 public List<String> getBaseDomainNames() {
703 return new ArrayList<>(this.policyContainer.getKieContainer().getKieBaseNames());
707 * provides the underlying core layer container sessions.
709 * @return the attached Policy Container
711 protected List<PolicySession> getSessions() {
712 List<PolicySession> sessions = new ArrayList<>();
713 sessions.addAll(this.policyContainer.getPolicySessions());
718 * provides the underlying core layer container session with name sessionName.
720 * @param sessionName session name
721 * @return the attached Policy Container
722 * @throws IllegalArgumentException when an invalid session name is provided
723 * @throws IllegalStateException when the drools controller is in an invalid state
725 protected PolicySession getSession(String sessionName) {
726 if (sessionName == null || sessionName.isEmpty()) {
727 throw new IllegalArgumentException("A Session Name must be provided");
730 List<PolicySession> sessions = this.getSessions();
731 for (PolicySession session : sessions) {
732 if (sessionName.equals(session.getName()) || sessionName.equals(session.getFullName())) {
737 throw invalidSessNameEx(sessionName);
740 private IllegalArgumentException invalidSessNameEx(String sessionName) {
741 return new IllegalArgumentException("Invalid Session Name: " + sessionName);
745 public Map<String,Integer> factClassNames(String sessionName) {
746 validateSessionName(sessionName);
748 Map<String,Integer> classNames = new HashMap<>();
750 PolicySession session = getSession(sessionName);
751 KieSession kieSession = session.getKieSession();
753 Collection<FactHandle> facts = kieSession.getFactHandles();
754 for (FactHandle fact : facts) {
756 String className = kieSession.getObject(fact).getClass().getName();
757 if (classNames.containsKey(className)) {
758 classNames.put(className, classNames.get(className) + 1);
760 classNames.put(className, 1);
762 } catch (Exception e) {
763 logger.warn(FACT_RETRIEVE_ERROR, fact, e);
770 private void validateSessionName(String sessionName) {
771 if (sessionName == null || sessionName.isEmpty()) {
772 throw invalidSessNameEx(sessionName);
777 public long factCount(String sessionName) {
778 validateSessionName(sessionName);
780 PolicySession session = getSession(sessionName);
781 return session.getKieSession().getFactCount();
785 public List<Object> facts(String sessionName, String className, boolean delete) {
786 validateSessionName(sessionName);
788 if (className == null || className.isEmpty()) {
789 throw new IllegalArgumentException("Invalid Class Name: " + className);
793 ReflectionUtil.fetchClass(this.policyContainer.getClassLoader(), className);
794 if (factClass == null) {
795 throw new IllegalArgumentException("Class cannot be fetched in model's classloader: " + className);
798 PolicySession session = getSession(sessionName);
799 KieSession kieSession = session.getKieSession();
801 List<Object> factObjects = new ArrayList<>();
803 Collection<FactHandle> factHandles = kieSession.getFactHandles(new ClassObjectFilter(factClass));
804 for (FactHandle factHandle : factHandles) {
806 factObjects.add(kieSession.getObject(factHandle));
808 kieSession.delete(factHandle);
810 } catch (Exception e) {
811 logger.warn(FACT_RETRIEVE_ERROR, factHandle, e);
819 public <T> List<T> facts(@NonNull String sessionName, @NonNull Class<T> clazz) {
820 return facts(sessionName, clazz.getName(), false)
822 .filter(clazz::isInstance)
824 .collect(Collectors.toList());
828 public List<Object> factQuery(String sessionName, String queryName, String queriedEntity,
829 boolean delete, Object... queryParams) {
830 validateSessionName(sessionName);
832 if (queryName == null || queryName.isEmpty()) {
833 throw new IllegalArgumentException("Invalid Query Name: " + queryName);
836 if (queriedEntity == null || queriedEntity.isEmpty()) {
837 throw new IllegalArgumentException("Invalid Queried Entity: " + queriedEntity);
840 PolicySession session = getSession(sessionName);
841 KieSession kieSession = session.getKieSession();
843 boolean found = false;
844 for (KiePackage kiePackage : kieSession.getKieBase().getKiePackages()) {
845 for (Query q : kiePackage.getQueries()) {
846 if (q.getName() != null && q.getName().equals(queryName)) {
853 throw new IllegalArgumentException("Invalid Query Name: " + queryName);
856 List<Object> factObjects = new ArrayList<>();
858 QueryResults queryResults = kieSession.getQueryResults(queryName, queryParams);
859 for (QueryResultsRow row : queryResults) {
861 factObjects.add(row.get(queriedEntity));
863 kieSession.delete(row.getFactHandle(queriedEntity));
865 } catch (Exception e) {
866 logger.warn("Object cannot be retrieved from row: {}", row, e);
874 public <T> boolean delete(@NonNull String sessionName, @NonNull T fact) {
875 String factClassName = fact.getClass().getName();
877 PolicySession session = getSession(sessionName);
878 KieSession kieSession = session.getKieSession();
880 Collection<FactHandle> factHandles = kieSession.getFactHandles(new ClassObjectFilter(fact.getClass()));
881 for (FactHandle factHandle : factHandles) {
883 if (Objects.equals(fact, kieSession.getObject(factHandle))) {
884 logger.info("Deleting {} from {}", factClassName, sessionName);
885 kieSession.delete(factHandle);
888 } catch (Exception e) {
889 logger.warn(FACT_RETRIEVE_ERROR, factHandle, e);
896 public <T> boolean delete(@NonNull T fact) {
897 return this.getSessionNames().stream().map(ss -> delete(ss, fact)).reduce(false, Boolean::logicalOr);
901 public <T> boolean delete(@NonNull String sessionName, @NonNull Class<T> fact) {
902 PolicySession session = getSession(sessionName);
903 KieSession kieSession = session.getKieSession();
905 boolean success = true;
906 Collection<FactHandle> factHandles = kieSession.getFactHandles(new ClassObjectFilter(fact));
907 for (FactHandle factHandle : factHandles) {
909 kieSession.delete(factHandle);
910 } catch (Exception e) {
911 logger.warn(FACT_RETRIEVE_ERROR, factHandle, e);
919 public <T> boolean delete(@NonNull Class<T> fact) {
920 return this.getSessionNames().stream().map(ss -> delete(ss, fact)).reduce(false, Boolean::logicalOr);
925 public Class<?> fetchModelClass(String className) {
926 return ReflectionUtil.fetchClass(this.policyContainer.getClassLoader(), className);
930 * Get recent source events.
932 * @return the recentSourceEvents
935 public Object[] getRecentSourceEvents() {
936 synchronized (this.recentSourceEvents) {
937 Object[] events = new Object[recentSourceEvents.size()];
938 return recentSourceEvents.toArray(events);
943 * Get recent sink events.
945 * @return the recentSinkEvents
948 public String[] getRecentSinkEvents() {
949 synchronized (this.recentSinkEvents) {
950 String[] events = new String[recentSinkEvents.size()];
951 return recentSinkEvents.toArray(events);
956 public boolean isBrained() {
962 public String toString() {
963 StringBuilder builder = new StringBuilder();
965 .append("MavenDroolsController [policyContainer=")
966 .append(policyContainer.getName())
971 .append(", modelClassLoaderHash=")
972 .append(modelClassLoaderHash)
974 return builder.toString();
977 // these may be overridden by junit tests
979 protected EventProtocolCoder getCoderManager() {
980 return EventProtocolCoderConstants.getManager();
983 protected OrderedServiceImpl<DroolsControllerFeatureApi> getDroolsProviders() {
984 return DroolsControllerFeatureApiConstants.getProviders();
987 protected PolicyContainer makePolicyContainer(String groupId, String artifactId, String version) {
988 return new PolicyContainer(groupId, artifactId, version);
991 protected boolean isClass(String className) {
992 return ReflectionUtil.isClass(this.policyContainer.getClassLoader(), className);