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