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