9689776cbabf9023ffc9bcf9011b77e419b765c5
[policy/drools-pdp.git] /
1 /*-
2  * ============LICENSE_START=======================================================
3  * policy-management
4  * ================================================================================
5  * Copyright (C) 2017 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
10  * 
11  *      http://www.apache.org/licenses/LICENSE-2.0
12  * 
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=========================================================
19  */
20
21 package org.onap.policy.drools.controller.internal;
22
23 import java.util.ArrayList;
24 import java.util.Collection;
25 import java.util.HashMap;
26 import java.util.List;
27 import java.util.Map;
28
29 import org.apache.commons.collections4.queue.CircularFifoQueue;
30 import org.drools.core.ClassObjectFilter;
31 import org.kie.api.definition.KiePackage;
32 import org.kie.api.definition.rule.Query;
33 import org.kie.api.runtime.KieSession;
34 import org.kie.api.runtime.rule.FactHandle;
35 import org.kie.api.runtime.rule.QueryResults;
36 import org.kie.api.runtime.rule.QueryResultsRow;
37 import org.slf4j.Logger;
38 import org.slf4j.LoggerFactory;
39 import org.onap.policy.drools.controller.DroolsController;
40 import org.onap.policy.drools.core.PolicyContainer;
41 import org.onap.policy.drools.core.PolicySession;
42 import org.onap.policy.drools.core.jmx.PdpJmx;
43 import org.onap.policy.drools.event.comm.TopicSink;
44 import org.onap.policy.drools.protocol.coders.EventProtocolCoder;
45 import org.onap.policy.drools.protocol.coders.JsonProtocolFilter;
46 import org.onap.policy.drools.protocol.coders.TopicCoderFilterConfiguration;
47 import org.onap.policy.drools.protocol.coders.TopicCoderFilterConfiguration.CustomGsonCoder;
48 import org.onap.policy.drools.protocol.coders.TopicCoderFilterConfiguration.CustomJacksonCoder;
49 import org.onap.policy.drools.protocol.coders.TopicCoderFilterConfiguration.PotentialCoderFilter;
50 import org.onap.policy.drools.utils.ReflectionUtil;
51
52 import com.fasterxml.jackson.annotation.JsonIgnore;
53 import com.fasterxml.jackson.annotation.JsonProperty;
54
55 /**
56  * Maven-based Drools Controller that interacts with the 
57  * policy-core PolicyContainer and PolicySession to manage
58  * Drools containers instantiated using Maven.
59  */
60 public class MavenDroolsController implements DroolsController {
61         
62         /**
63          * logger 
64          */
65         private static Logger  logger = LoggerFactory.getLogger(MavenDroolsController.class);
66         
67         /**
68          * Policy Container, the access object to the policy-core layer
69          */
70         @JsonIgnore
71         protected final PolicyContainer policyContainer;
72         
73         /**
74          * alive status of this drools controller, 
75          * reflects invocation of start()/stop() only
76          */
77         protected volatile boolean alive = false;
78         
79         /**
80          * locked status of this drools controller,
81          * reflects if i/o drools related operations are permitted,
82          * more specifically: offer() and deliver().
83          * It does not affect the ability to start and stop 
84          * underlying drools infrastructure
85          */
86         protected volatile boolean locked = false;
87         
88         /**
89          * list of topics, each with associated decoder classes, each
90          * with a list of associated filters.
91          */
92         protected List<TopicCoderFilterConfiguration> decoderConfigurations;
93         
94         /**
95          * list of topics, each with associated encoder classes, each
96          * with a list of associated filters.
97          */
98         protected List<TopicCoderFilterConfiguration> encoderConfigurations;
99         
100         /**
101          * recent source events processed
102          */
103         protected final CircularFifoQueue<Object> recentSourceEvents = new CircularFifoQueue<Object>(10);
104         
105         /**
106          * recent sink events processed
107          */
108         protected final CircularFifoQueue<String> recentSinkEvents = new CircularFifoQueue<String>(10);
109         
110         /**
111          * original Drools Model/Rules classloader hash
112          */
113         protected int modelClassLoaderHash;
114
115         /**
116          * Expanded version of the constructor 
117          * 
118          * @param groupId maven group id
119          * @param artifactId maven artifact id
120          * @param version maven version
121          * @param decoderConfiguration list of topic -> decoders -> filters mapping
122          * @param encoderConfiguration list of topic -> encoders -> filters mapping
123          * 
124          * @throws IllegalArgumentException invalid arguments passed in
125          */
126         public MavenDroolsController(String groupId, 
127                                                                  String artifactId, 
128                                                                  String version,
129                                                                  List<TopicCoderFilterConfiguration> decoderConfigurations,
130                                                                  List<TopicCoderFilterConfiguration> encoderConfigurations) {
131                 
132                 logger.info("drools-controller instantiation [{}:{}:{}]", groupId, artifactId, version);
133                 
134                 if (groupId == null || groupId.isEmpty())
135                         throw new IllegalArgumentException("Missing maven group-id coordinate");
136         
137                 if (artifactId == null || artifactId.isEmpty())
138                         throw new IllegalArgumentException("Missing maven artifact-id coordinate");
139                 
140                 if (version == null || version.isEmpty())
141                         throw new IllegalArgumentException("Missing maven version coordinate");
142                 
143                 this.policyContainer= new PolicyContainer(groupId, artifactId, version);
144                 this.init(decoderConfigurations, encoderConfigurations);
145                 
146                 logger.debug("{}: instantiation completed ", this);
147         }
148         
149         /**
150          * init encoding/decoding configuration
151          * @param decoderConfiguration list of topic -> decoders -> filters mapping
152          * @param encoderConfiguration list of topic -> encoders -> filters mapping
153          */
154         protected void init(List<TopicCoderFilterConfiguration> decoderConfigurations,
155                                     List<TopicCoderFilterConfiguration> encoderConfigurations) {
156                 
157                 this.decoderConfigurations = decoderConfigurations;
158                 this.encoderConfigurations = encoderConfigurations;
159                 
160                 this.initCoders(decoderConfigurations, true);
161                 this.initCoders(encoderConfigurations, false);
162                 
163                 this.modelClassLoaderHash = this.policyContainer.getClassLoader().hashCode();           
164         }
165         
166         @Override
167         public void updateToVersion(String newGroupId, String newArtifactId, String newVersion,
168                                             List<TopicCoderFilterConfiguration> decoderConfigurations,
169                                             List<TopicCoderFilterConfiguration> encoderConfigurations) 
170                 throws LinkageError {
171                 
172                 logger.info("{}: updating version -> [{}:{}:{}]", newGroupId, newArtifactId, newVersion);
173                 
174                 if (newGroupId == null || newGroupId.isEmpty())
175                         throw new IllegalArgumentException("Missing maven group-id coordinate");
176         
177                 if (newArtifactId == null || newArtifactId.isEmpty())
178                         throw new IllegalArgumentException("Missing maven artifact-id coordinate");
179                 
180                 if (newVersion == null || newVersion.isEmpty())
181                         throw new IllegalArgumentException("Missing maven version coordinate");
182                 
183                 if (newGroupId.equalsIgnoreCase(DroolsController.NO_GROUP_ID) || 
184                         newArtifactId.equalsIgnoreCase(DroolsController.NO_ARTIFACT_ID) || 
185                         newVersion.equalsIgnoreCase(DroolsController.NO_VERSION)) {
186                                 throw new IllegalArgumentException("BRAINLESS maven coordinates provided: " + 
187                                                                            newGroupId + ":" + newArtifactId + ":" + 
188                                                                            newVersion);
189                 }
190                 
191                 if (newGroupId.equalsIgnoreCase(this.getGroupId()) && 
192                         newArtifactId.equalsIgnoreCase(this.getArtifactId()) &&
193                         newVersion.equalsIgnoreCase(this.getVersion())) {
194                                 logger.warn("Al in the right version: " + newGroupId + ":" + 
195                                         newArtifactId + ":" +  newVersion + " vs. " + this);
196                                 return;
197                 }
198                 
199                 if (!newGroupId.equalsIgnoreCase(this.getGroupId()) || 
200                         !newArtifactId.equalsIgnoreCase(this.getArtifactId())) {
201                                 throw new IllegalArgumentException("Group ID and Artifact ID maven coordinates must be identical for the upgrade: " + 
202                                                                                newGroupId + ":" + newArtifactId + ":" + 
203                                                                                newVersion + " vs. " + this);
204                 }
205
206                 /* upgrade */
207                 String messages = this.policyContainer.updateToVersion(newVersion);
208                 if (logger.isWarnEnabled())
209                         logger.warn(this + "UPGRADE results: " + messages);
210                 
211                 /*
212                  * If all sucessful (can load new container), now we can remove all coders from previous sessions
213                  */
214                 this.removeCoders();
215                 
216                 /*
217                  * add the new coders
218                  */
219                 this.init(decoderConfigurations, encoderConfigurations);
220                 
221                 if (logger.isInfoEnabled())
222                         logger.info("UPDATE-TO-VERSION: completed " +  this);
223         }
224
225         /**
226          * initialize decoders for all the topics supported by this controller
227          * Note this is critical to be done after the Policy Container is
228          * instantiated to be able to fetch the corresponding classes.
229          * 
230          * @param decoderConfiguration list of topic -> decoders -> filters mapping
231          */
232         protected void initCoders(List<TopicCoderFilterConfiguration> coderConfigurations, 
233                                           boolean decoder) {
234                 
235                 if (logger.isInfoEnabled())
236                         logger.info("INIT-CODERS: " +  this);
237                 
238                 if (coderConfigurations == null) {
239                         return;
240                 }
241                         
242
243                 for (TopicCoderFilterConfiguration coderConfig: coderConfigurations) {
244                         String topic = coderConfig.getTopic();
245                         
246                         CustomGsonCoder customGsonCoder = coderConfig.getCustomGsonCoder();
247                         if (coderConfig.getCustomGsonCoder() != null && 
248                                 coderConfig.getCustomGsonCoder().getClassContainer() != null &&
249                                 !coderConfig.getCustomGsonCoder().getClassContainer().isEmpty()) {
250                                         
251                                 String customGsonCoderClass = coderConfig.getCustomGsonCoder().getClassContainer();
252                                 if (!ReflectionUtil.isClass(this.policyContainer.getClassLoader(),
253                                                                    customGsonCoderClass)) {
254                                         logger.error(customGsonCoderClass + " cannot be retrieved");
255                                         throw new IllegalArgumentException(customGsonCoderClass + " cannot be retrieved");
256                                 } else {
257                                         if (logger.isInfoEnabled())
258                                                 logger.info("CLASS FETCHED " + customGsonCoderClass);
259                                 }
260                         }
261                         
262                         CustomJacksonCoder customJacksonCoder = coderConfig.getCustomJacksonCoder();
263                         if (coderConfig.getCustomJacksonCoder() != null && 
264                                 coderConfig.getCustomJacksonCoder().getClassContainer() != null &&
265                                 !coderConfig.getCustomJacksonCoder().getClassContainer().isEmpty()) {
266                                 
267                                 String customJacksonCoderClass = coderConfig.getCustomJacksonCoder().getClassContainer();
268                                 if (!ReflectionUtil.isClass(this.policyContainer.getClassLoader(),
269                                                                    customJacksonCoderClass)) {
270                                         logger.error(customJacksonCoderClass + " cannot be retrieved");
271                                         throw new IllegalArgumentException(customJacksonCoderClass + " cannot be retrieved");
272                                 } else {
273                                         if (logger.isInfoEnabled())
274                                                 logger.info("CLASS FETCHED " + customJacksonCoderClass);
275                                 }
276                         }       
277                         
278                         List<PotentialCoderFilter> coderFilters = coderConfig.getCoderFilters();
279                         if (coderFilters == null || coderFilters.isEmpty()) {
280                                 continue;
281                         }
282                         
283                         for (PotentialCoderFilter coderFilter : coderFilters) {
284                                 String potentialCodedClass = coderFilter.getCodedClass();
285                                 JsonProtocolFilter protocolFilter = coderFilter.getFilter();
286                                 
287                                 if (!ReflectionUtil.isClass(this.policyContainer.getClassLoader(), 
288                                                                    potentialCodedClass)) {
289                                         logger.error(potentialCodedClass + " cannot be retrieved");
290                                         throw new IllegalArgumentException(potentialCodedClass + " cannot be retrieved");
291                                 } else {
292                                         if (logger.isInfoEnabled())
293                                                 logger.info("CLASS FETCHED " + potentialCodedClass);
294                                 }                       
295                                 
296                                 if (decoder)
297                                         EventProtocolCoder.manager.addDecoder(this.getGroupId(), this.getArtifactId(), 
298                                                                                       topic, potentialCodedClass, protocolFilter,
299                                                                                       customGsonCoder,
300                                                                                       customJacksonCoder,
301                                                                                       this.policyContainer.getClassLoader().hashCode());
302                                 else
303                                         EventProtocolCoder.manager.addEncoder(this.getGroupId(), this.getArtifactId(), 
304                                                           topic, potentialCodedClass, protocolFilter,
305                                                                                       customGsonCoder,
306                                                                                       customJacksonCoder,
307                                                                                       this.policyContainer.getClassLoader().hashCode());
308                         }
309                 }
310         }
311         
312
313         /**
314          * remove decoders.
315          */
316         protected void removeDecoders(){
317                 if (logger.isInfoEnabled())
318                         logger.info("REMOVE-DECODERS: " +  this);
319                 
320                 if (this.decoderConfigurations == null) {
321                         return;
322                 }
323                         
324
325                 for (TopicCoderFilterConfiguration coderConfig: decoderConfigurations) {
326                         String topic = coderConfig.getTopic();                                                          
327                         EventProtocolCoder.manager.removeDecoders
328                                                 (this.getGroupId(), this.getArtifactId(), topic);
329                 }
330         }
331         
332         /**
333          * remove decoders.
334          */
335         protected void removeEncoders() {
336                 
337                 if (logger.isInfoEnabled())
338                         logger.info("REMOVE-ENCODERS: " +  this);
339                 
340                 if (this.encoderConfigurations == null)
341                         return;
342                         
343
344                 for (TopicCoderFilterConfiguration coderConfig: encoderConfigurations) {
345                         String topic = coderConfig.getTopic();                                                          
346                         EventProtocolCoder.manager.removeEncoders
347                                                 (this.getGroupId(), this.getArtifactId(), topic);
348                 }
349         }
350         
351
352         @Override
353         public boolean ownsCoder(Class<? extends Object> coderClass, int modelHash) {
354                 if (!ReflectionUtil.isClass
355                                 (this.policyContainer.getClassLoader(), coderClass.getCanonicalName())) {
356                         logger.error(this + coderClass.getCanonicalName() + " cannot be retrieved. ");
357                         return false;
358                 }
359                 
360                 if (modelHash == this.modelClassLoaderHash) {
361                         if (logger.isInfoEnabled())
362                                 logger.info(coderClass.getCanonicalName() + 
363                                                     this + " class loader matches original drools controller rules classloader " +
364                                         coderClass.getClassLoader());
365                         return true;
366                 } else {
367                         if (logger.isWarnEnabled())
368                                 logger.warn(this + coderClass.getCanonicalName() + " class loaders don't match  " +
369                                 coderClass.getClassLoader() + " vs " + 
370                                                     this.policyContainer.getClassLoader());
371                         return false;
372                 }
373         }
374         
375         @Override
376         public boolean start() {
377                 
378                 if (logger.isInfoEnabled())
379                         logger.info("START: " + this);
380                 
381                 synchronized (this) {                   
382                         if (this.alive)
383                                 return true;
384                         
385                         this.alive = true;
386                 }
387                 
388                 return this.policyContainer.start();
389         }
390         
391         @Override
392         public boolean stop() {
393                 
394                 logger.info("STOP: " + this);
395                 
396                 synchronized (this) {
397                         if (!this.alive)
398                                 return true;
399                         
400                         this.alive = false;
401                 }
402                 
403                 return this.policyContainer.stop();
404         }
405
406         @Override
407         public void shutdown() {                
408                 logger.info("{}: SHUTDOWN", this);
409                 
410                 try {
411                         this.stop();
412                         this.removeCoders();
413                 } catch (Exception e) {
414                         logger.error("{} SHUTDOWN FAILED because of {}", this, e.getMessage(), e);
415                 } finally {
416                         this.policyContainer.shutdown();
417                 }
418
419         }
420         
421         @Override
422         public void halt() {
423                 logger.info("{}: HALT", this);
424                 
425                 try {
426                         this.stop();
427                         this.removeCoders();
428                 } catch (Exception e) {
429                         logger.error("{} HALT FAILED because of {}", this, e.getMessage(), e);
430                 } finally {
431                         this.policyContainer.destroy();
432                 }       
433         }
434         
435         /**
436          * removes this drools controllers and encoders and decoders from operation
437          */
438         protected void removeCoders() {
439                 logger.info("{}: REMOVE-CODERS", this);
440                 
441                 try {
442                         this.removeDecoders();
443                 } catch (IllegalArgumentException e) {
444                         logger.error("{} REMOVE-DECODERS FAILED because of {}", this, e.getMessage(), e);
445                 }
446                 
447                 try {
448                         this.removeEncoders();
449                 } catch (IllegalArgumentException e) {
450                         logger.error("{} REMOVE-ENCODERS FAILED because of {}", this, e.getMessage(), e);
451                 }
452         }
453
454         @Override
455         public boolean isAlive() {
456                 return this.alive;
457         }
458         
459         @Override
460         public boolean offer(String topic, String event) {
461                 logger.debug("{}: OFFER: {} <- {}", this, topic, event);
462                 
463                 if (this.locked)
464                         return true;
465                 
466                 if (!this.alive)
467                         return true;
468                 
469                 // 0. Check if the policy container has any sessions
470                 
471                 if (this.policyContainer.getPolicySessions().isEmpty()) {
472                         // no sessions
473                         return true;
474                 }
475                 
476                 // 1. Now, check if this topic has a decoder:
477                 
478                 if (!EventProtocolCoder.manager.isDecodingSupported(this.getGroupId(), 
479                                                                               this.getArtifactId(), 
480                                                                               topic)) {
481                         
482                         logger.warn("{}: DECODING-UNSUPPORTED {}:{}:{}", this, 
483                                             topic, this.getGroupId(), this.getArtifactId());
484                         return true;
485                 }
486                 
487                 // 2. Decode
488                 
489                 Object anEvent;
490                 try {
491                         anEvent = EventProtocolCoder.manager.decode(this.getGroupId(), 
492                                                                               this.getArtifactId(), 
493                                                                               topic, 
494                                                                               event);
495                 } catch (UnsupportedOperationException uoe) {
496                         logger.debug("{}: DECODE FAILED: {} <- {} because of {}", this, topic, 
497                                              event, uoe.getMessage(), uoe);
498                         return true;
499                 } catch (Exception e) {
500                         logger.warn("{}: DECODE FAILED: {} <- {} because of {}", this, topic, 
501                                         event, e.getMessage(), e);
502                         return true;
503                 }
504                 
505                 synchronized(this.recentSourceEvents) {
506                         this.recentSourceEvents.add(anEvent);
507                 }
508                 
509                 // increment event count for Nagios monitoring
510                 PdpJmx.getInstance().updateOccured();  
511                 
512                 // Broadcast
513                 
514                 if (logger.isInfoEnabled())
515                         logger.info(this + "BROADCAST-INJECT of " + event + " FROM " + topic + " INTO " + this.policyContainer.getName());              
516                 
517                 if (!this.policyContainer.insertAll(anEvent))
518                         logger.warn(this + "Failed to inject into PolicyContainer " + this.getSessionNames());
519                 
520                 return true;
521         }       
522         
523         @Override
524         public boolean deliver(TopicSink sink, Object event) 
525                         throws UnsupportedOperationException {
526                 
527                 if (logger.isInfoEnabled())
528                         logger.info(this + "DELIVER: " +  event + " FROM " + this + " TO " + sink);
529                 
530                 if (sink == null)
531                         throw new IllegalArgumentException
532                                                 (this +  " invalid sink");
533                 
534                 if (event == null)
535                         throw new IllegalArgumentException
536                                                 (this +  " invalid event");
537                 
538                 if (this.locked)
539                         throw new IllegalStateException
540                                                         (this +  " is locked");
541                 
542                 if (!this.alive)
543                         throw new IllegalStateException
544                                                         (this +  " is stopped");
545                 
546                 String json =
547                                 EventProtocolCoder.manager.encode(sink.getTopic(), event, this);
548                 
549                 synchronized(this.recentSinkEvents) {
550                         this.recentSinkEvents.add(json);
551                 }
552                 
553                 return sink.send(json);
554                 
555         }
556
557         @Override
558         public String getVersion() {
559                 return this.policyContainer.getVersion();
560         }
561         
562         @Override
563         public String getArtifactId() {
564                 return this.policyContainer.getArtifactId();
565         }
566         
567         @Override
568         public String getGroupId() {
569                 return this.policyContainer.getGroupId();
570         }
571
572         /**
573          * @return the modelClassLoaderHash
574          */
575         public int getModelClassLoaderHash() {
576                 return modelClassLoaderHash;
577         }
578
579         @Override
580         public synchronized boolean lock() {
581                 logger.info("LOCK: " +  this);
582                 
583                 this.locked = true;
584                 return true;
585         }
586
587         @Override
588         public synchronized boolean unlock() {
589                 logger.info("UNLOCK: " +  this);
590                 
591                 this.locked = false;
592                 return true;
593         }
594
595         @Override
596         public boolean isLocked() {
597                 return this.locked;
598         }
599         
600         @JsonIgnore
601         @Override
602         public PolicyContainer getContainer() {
603                 return this.policyContainer;
604         }
605         
606         @JsonProperty("sessions")
607         @Override
608         public List<String> getSessionNames() {
609                 return getSessionNames(true);
610         }
611         
612         @JsonProperty("sessionCoordinates")
613         @Override
614         public List<String> getCanonicalSessionNames() {
615                 return getSessionNames(false);
616         }
617         
618         /**
619          * get session names
620          * @param abbreviated true for the short form, otherwise the long form
621          * @return session names
622          */
623         protected List<String> getSessionNames(boolean abbreviated) {
624                 List<String> sessionNames = new ArrayList<>();
625                 try {
626                         for (PolicySession session: this.policyContainer.getPolicySessions()) {
627                                 if (abbreviated)
628                                         sessionNames.add(session.getName());
629                                 else
630                                         sessionNames.add(session.getFullName());
631                         }
632                 } catch (Exception e) {
633                         logger.warn("Can't retrieve CORE sessions: " + e.getMessage(), e);
634                         sessionNames.add(e.getMessage());
635                 }
636                 return sessionNames;
637         }
638         
639         /**
640          * provides the underlying core layer container sessions
641          * 
642          * @return the attached Policy Container
643          */
644         protected List<PolicySession> getSessions() {
645                 List<PolicySession> sessions = new ArrayList<>();
646                 sessions.addAll(this.policyContainer.getPolicySessions());
647                 return sessions;
648         }
649         
650         /**
651          * provides the underlying core layer container session with name sessionName
652          * 
653          * @param sessionName session name
654          * @return the attached Policy Container
655          * @throws IllegalArgumentException when an invalid session name is provided
656          * @throws IllegalStateException when the drools controller is in an invalid state
657          */
658         protected PolicySession getSession(String sessionName) {
659                 if (sessionName == null || sessionName.isEmpty())
660                         throw new IllegalArgumentException("A Session Name must be provided");
661                 
662                 List<PolicySession> sessions = this.getSessions();
663                 for (PolicySession session : sessions) {
664                         if (sessionName.equals(session.getName()) || sessionName.equals(session.getFullName()))
665                                 return session;                         
666                 }
667                 
668                 throw new IllegalArgumentException("Invalid Session Name: " + sessionName);
669         }
670         
671         @Override
672         public Map<String,Integer> factClassNames(String sessionName) {         
673                 if (sessionName == null || sessionName.isEmpty())
674                         throw new IllegalArgumentException("Invalid Session Name: " + sessionName);
675
676                 Map<String,Integer> classNames = new HashMap<>();
677                 
678                 PolicySession session = getSession(sessionName);
679                 KieSession kieSession = session.getKieSession();
680
681                 Collection<FactHandle> facts = session.getKieSession().getFactHandles();
682                 for (FactHandle fact : facts) {
683                         try {
684                                 String className = kieSession.getObject(fact).getClass().getName();
685                                 if (classNames.containsKey(className))
686                                         classNames.put(className, classNames.get(className) + 1);
687                                 else
688                                         classNames.put(className, 1);
689                         } catch (Exception e) {
690                                 logger.warn("Object cannot be retrieved from fact {}", fact, e);
691                         }                       
692                 }       
693                 
694                 return classNames;
695         }
696         
697         @Override
698         public long factCount(String sessionName) {             
699                 if (sessionName == null || sessionName.isEmpty())
700                         throw new IllegalArgumentException("Invalid Session Name: " + sessionName);
701                 
702                 PolicySession session = getSession(sessionName);
703                 return session.getKieSession().getFactCount();  
704         }
705         
706         @Override
707         public List<Object> facts(String sessionName, String className, boolean delete) {               
708                 if (sessionName == null || sessionName.isEmpty())
709                         throw new IllegalArgumentException("Invalid Session Name: " + sessionName);
710                 
711                 if (className == null || className.isEmpty())
712                         throw new IllegalArgumentException("Invalid Class Name: " + className);
713                 
714                 Class<?> factClass = 
715                                 ReflectionUtil.fetchClass(this.policyContainer.getClassLoader(), className);
716                 if (factClass == null)
717                         throw new IllegalArgumentException("Class cannot be fetched in model's classloader: " + className);
718                 
719                 PolicySession session = getSession(sessionName);
720                 KieSession kieSession = session.getKieSession();
721                 
722                 List<Object> factObjects = new ArrayList<>();
723                 
724                 Collection<FactHandle> factHandles = kieSession.getFactHandles(new ClassObjectFilter(factClass));
725                 for (FactHandle factHandle : factHandles) {
726                         try {
727                                 factObjects.add(kieSession.getObject(factHandle));
728                                 if (delete)
729                                         kieSession.delete(factHandle);                                  
730                         } catch (Exception e) {
731                                 logger.warn("Object cannot be retrieved from fact {}", factHandle, e);
732                         }
733                 }               
734                 
735                 return factObjects;
736         }
737         
738         @Override
739         public List<Object> factQuery(String sessionName, String queryName, String queriedEntity, boolean delete, Object... queryParams) {              
740                 if (sessionName == null || sessionName.isEmpty())
741                         throw new IllegalArgumentException("Invalid Session Name: " + sessionName);
742                 
743                 if (queryName == null || queryName.isEmpty())
744                         throw new IllegalArgumentException("Invalid Query Name: " + queryName);
745                 
746                 if (queriedEntity == null || queriedEntity.isEmpty())
747                         throw new IllegalArgumentException("Invalid Queried Entity: " + queriedEntity);
748                 
749                 PolicySession session = getSession(sessionName);
750                 KieSession kieSession = session.getKieSession();
751                 
752                 boolean found = false;
753                 for (KiePackage kiePackage : kieSession.getKieBase().getKiePackages()) {
754                         for (Query q : kiePackage.getQueries()) {
755                                 if (q.getName() != null && q.getName().equals(queryName)) {
756                                         found = true;
757                                         break;
758                                 }
759                         }
760                 }
761                 if (!found)
762                         throw new IllegalArgumentException("Invalid Query Name: " + queryName);
763         
764                 List<Object> factObjects = new ArrayList<>();
765                 
766                 QueryResults queryResults = kieSession.getQueryResults(queryName, queryParams);
767                 for (QueryResultsRow row : queryResults) {
768                         try {
769                                 factObjects.add(row.get(queriedEntity));
770                                 if (delete)
771                                         kieSession.delete(row.getFactHandle(queriedEntity));
772                         } catch (Exception e) {
773                                 logger.warn("Object cannot be retrieved from row: {}", row, e);
774                         }
775                 }
776                 
777                 return factObjects;
778         }
779         
780         @Override
781         public Class<?> fetchModelClass(String className) {
782                 Class<?> modelClass = 
783                         ReflectionUtil.fetchClass(this.policyContainer.getClassLoader(), className);
784                 return modelClass;
785         }
786
787         /**
788          * @return the recentSourceEvents
789          */
790         @Override
791         public Object[] getRecentSourceEvents() {
792                 synchronized(this.recentSourceEvents) {
793                         Object[] events = new Object[recentSourceEvents.size()];
794                         return recentSourceEvents.toArray(events);
795                 }
796         }
797
798         /**
799          * @return the recentSinkEvents
800          */
801         @Override
802         public String[] getRecentSinkEvents() {
803                 synchronized(this.recentSinkEvents) {
804                         String[] events = new String[recentSinkEvents.size()];
805                         return recentSinkEvents.toArray(events);
806                 }
807         }
808         
809         @Override
810         public boolean isBrained() {
811                 return true;
812         }
813         
814
815         @Override
816         public String toString() {
817                 StringBuilder builder = new StringBuilder();
818                 builder.append("MavenDroolsController [policyContainer=")
819                        .append((policyContainer != null) ? policyContainer.getName() : "NULL").append(":")
820                        .append(", alive=")
821                            .append(alive).append(", locked=")
822                            .append(", modelClassLoaderHash=").append(modelClassLoaderHash).append("]");
823                 return builder.toString();
824         }
825
826 }