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