0729c706602196cd15e8513b5da587e4d9ae5c11
[policy/drools-pdp.git] /
1 /*
2  * ============LICENSE_START=======================================================
3  * ONAP
4  * ================================================================================
5  * Copyright (C) 2019-2020, 2021 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.protocol.coders;
22
23 import java.util.ArrayList;
24 import java.util.HashMap;
25 import java.util.Iterator;
26 import java.util.List;
27 import java.util.Map;
28 import lombok.AccessLevel;
29 import lombok.NoArgsConstructor;
30 import org.onap.policy.drools.controller.DroolsController;
31 import org.onap.policy.drools.controller.DroolsControllerConstants;
32 import org.onap.policy.drools.protocol.coders.EventProtocolCoder.CoderFilters;
33 import org.slf4j.Logger;
34 import org.slf4j.LoggerFactory;
35
36 /**
37  * This protocol Coder that does its best attempt to decode/encode, selecting the best class and best fitted json
38  * parsing tools.
39  */
40 @NoArgsConstructor(access = AccessLevel.PROTECTED)
41 abstract class GenericEventProtocolCoder {
42     private static final String INVALID_ARTIFACT_ID_MSG = "Invalid artifact id";
43     private static final String INVALID_GROUP_ID_MSG = "Invalid group id";
44     private static final String INVALID_TOPIC_MSG = "Invalid Topic";
45     private static final String UNSUPPORTED_MSG = "Unsupported";
46     private static final String UNSUPPORTED_EX_MSG = "Unsupported:";
47     private static final String MISSING_CLASS = "class must be provided";
48
49     private static final Logger logger = LoggerFactory.getLogger(GenericEventProtocolCoder.class);
50
51     /**
52      * Mapping topic:controller-id -> /<protocol-decoder-toolset/> where protocol-coder-toolset contains
53      * a gson-protocol-coder-toolset.
54      */
55     protected final HashMap<String, ProtocolCoderToolset> coders =
56             new HashMap<>();
57
58     /**
59      * Mapping topic + classname -> Protocol Set.
60      */
61     protected final HashMap<String, List<ProtocolCoderToolset>>
62             reverseCoders = new HashMap<>();
63
64     /**
65      * Index a new coder.
66      */
67     public void add(EventProtocolParams eventProtocolParams) {
68         if (eventProtocolParams.getGroupId() == null || eventProtocolParams.getGroupId().isEmpty()) {
69             throw new IllegalArgumentException(INVALID_GROUP_ID_MSG);
70         }
71
72         if (eventProtocolParams.getArtifactId() == null || eventProtocolParams.getArtifactId().isEmpty()) {
73             throw new IllegalArgumentException(INVALID_ARTIFACT_ID_MSG);
74         }
75
76         if (eventProtocolParams.getTopic() == null || eventProtocolParams.getTopic().isEmpty()) {
77             throw new IllegalArgumentException(INVALID_TOPIC_MSG);
78         }
79
80         if (eventProtocolParams.getEventClass() == null) {
81             throw new IllegalArgumentException("Invalid Event Class");
82         }
83
84         String key = this.codersKey(eventProtocolParams.getGroupId(), eventProtocolParams.getArtifactId(),
85                 eventProtocolParams.getTopic());
86         String reverseKey = this.reverseCodersKey(eventProtocolParams.getTopic(), eventProtocolParams.getEventClass());
87
88         synchronized (this) {
89             if (coders.containsKey(key)) {
90                 ProtocolCoderToolset toolset = coders.get(key);
91
92                 logger.info("{}: adding coders for existing {}: {}", this, key, toolset);
93
94                 toolset
95                         .addCoder(
96                                 eventProtocolParams.getEventClass(),
97                                 eventProtocolParams.getProtocolFilter(),
98                                 eventProtocolParams.getModelClassLoaderHash());
99
100                 if (!reverseCoders.containsKey(reverseKey)) {
101                     logger.info(
102                             "{}: adding new reverse coders (multiple classes case) for {}:{}: {}",
103                             this,
104                             reverseKey,
105                             key,
106                             toolset);
107
108                     List<ProtocolCoderToolset> reverseMappings =
109                             new ArrayList<>();
110                     reverseMappings.add(toolset);
111                     reverseCoders.put(reverseKey, reverseMappings);
112                 }
113                 return;
114             }
115
116             var coderTools = new GsonProtocolCoderToolset(eventProtocolParams, key);
117
118             logger.info("{}: adding coders for new {}: {}", this, key, coderTools);
119
120             coders.put(key, coderTools);
121
122             addReverseCoder(coderTools, key, reverseKey);
123         }
124     }
125
126     private void addReverseCoder(GsonProtocolCoderToolset coderTools, String key, String reverseKey) {
127         if (reverseCoders.containsKey(reverseKey)) {
128             // There is another controller (different group id/artifact id/topic)
129             // that shares the class and the topic.
130
131             List<ProtocolCoderToolset> toolsets =
132                     reverseCoders.get(reverseKey);
133             var present = false;
134             for (ProtocolCoderToolset parserSet : toolsets) {
135                 // just doublecheck
136                 present = parserSet.getControllerId().equals(key);
137                 if (present) {
138                     /* anomaly */
139                     logger.error(
140                             "{}: unexpected toolset reverse mapping found for {}:{}: {}",
141                             this,
142                             reverseKey,
143                             key,
144                             parserSet);
145                 }
146             }
147
148             if (!present) {
149                 logger.info("{}: adding coder set for {}: {} ", this, reverseKey, coderTools);
150                 toolsets.add(coderTools);
151             }
152         } else {
153             List<ProtocolCoderToolset> toolsets = new ArrayList<>();
154             toolsets.add(coderTools);
155
156             logger.info("{}: adding toolset for reverse key {}: {}", this, reverseKey, toolsets);
157             reverseCoders.put(reverseKey, toolsets);
158         }
159     }
160
161     /**
162      * produces key for indexing toolset entries.
163      *
164      * @param groupId    group id
165      * @param artifactId artifact id
166      * @param topic      topic
167      * @return index key
168      */
169     protected String codersKey(String groupId, String artifactId, String topic) {
170         return groupId + ":" + artifactId + ":" + topic;
171     }
172
173     /**
174      * produces a key for the reverse index.
175      *
176      * @param topic      topic
177      * @param eventClass coded class
178      * @return reverse index key
179      */
180     protected String reverseCodersKey(String topic, String eventClass) {
181         return topic + ":" + eventClass;
182     }
183
184     /**
185      * remove coder.
186      *
187      * @param groupId    group id
188      * @param artifactId artifact id
189      * @param topic      topic
190      * @throws IllegalArgumentException if invalid input
191      */
192     public void remove(String groupId, String artifactId, String topic) {
193
194         if (groupId == null || groupId.isEmpty()) {
195             throw new IllegalArgumentException(INVALID_GROUP_ID_MSG);
196         }
197
198         if (artifactId == null || artifactId.isEmpty()) {
199             throw new IllegalArgumentException(INVALID_ARTIFACT_ID_MSG);
200         }
201
202         if (topic == null || topic.isEmpty()) {
203             throw new IllegalArgumentException(INVALID_TOPIC_MSG);
204         }
205
206         String key = this.codersKey(groupId, artifactId, topic);
207
208         synchronized (this) {
209             if (coders.containsKey(key)) {
210                 ProtocolCoderToolset coderToolset = coders.remove(key);
211
212                 logger.info("{}: removed toolset for {}: {}", this, key, coderToolset);
213
214                 for (CoderFilters codeFilter : coderToolset.getCoders()) {
215                     String className = codeFilter.getFactClass();
216                     String reverseKey = this.reverseCodersKey(topic, className);
217                     removeReverseCoder(key, reverseKey);
218                 }
219             }
220         }
221     }
222
223     private void removeReverseCoder(String key, String reverseKey) {
224         if (!this.reverseCoders.containsKey(reverseKey)) {
225             return;
226         }
227
228         List<ProtocolCoderToolset> toolsets =
229                 this.reverseCoders.get(reverseKey);
230         Iterator<ProtocolCoderToolset> toolsetsIter =
231                 toolsets.iterator();
232         while (toolsetsIter.hasNext()) {
233             ProtocolCoderToolset toolset = toolsetsIter.next();
234             if (toolset.getControllerId().equals(key)) {
235                 logger.info(
236                         "{}: removed coder from toolset for {} from reverse mapping", this, reverseKey);
237                 toolsetsIter.remove();
238             }
239         }
240
241         if (this.reverseCoders.get(reverseKey).isEmpty()) {
242             logger.info("{}: removing reverse mapping for {}: ", this, reverseKey);
243             this.reverseCoders.remove(reverseKey);
244         }
245     }
246
247     /**
248      * does it support coding.
249      *
250      * @param groupId    group id
251      * @param artifactId artifact id
252      * @param topic      topic
253      * @return true if its is codable
254      */
255     public boolean isCodingSupported(String groupId, String artifactId, String topic) {
256
257         if (groupId == null || groupId.isEmpty()) {
258             throw new IllegalArgumentException(INVALID_GROUP_ID_MSG);
259         }
260
261         if (artifactId == null || artifactId.isEmpty()) {
262             throw new IllegalArgumentException(INVALID_ARTIFACT_ID_MSG);
263         }
264
265         if (topic == null || topic.isEmpty()) {
266             throw new IllegalArgumentException(INVALID_TOPIC_MSG);
267         }
268
269         String key = this.codersKey(groupId, artifactId, topic);
270         synchronized (this) {
271             return coders.containsKey(key);
272         }
273     }
274
275     /**
276      * decode a json string into an Object.
277      *
278      * @param groupId    group id
279      * @param artifactId artifact id
280      * @param topic      topic
281      * @param json       json string to convert to object
282      * @return the decoded object
283      * @throws IllegalArgumentException      if invalid argument is provided
284      * @throws UnsupportedOperationException if the operation cannot be performed
285      */
286     public Object decode(String groupId, String artifactId, String topic, String json) {
287
288         if (!isCodingSupported(groupId, artifactId, topic)) {
289             throw new IllegalArgumentException(
290                     UNSUPPORTED_EX_MSG + codersKey(groupId, artifactId, topic) + " for encoding");
291         }
292
293         String key = this.codersKey(groupId, artifactId, topic);
294         ProtocolCoderToolset coderTools = coders.get(key);
295         try {
296             Object event = coderTools.decode(json);
297             if (event != null) {
298                 return event;
299             }
300         } catch (Exception e) {
301             logger.debug("{}, cannot decode {}", this, json, e);
302         }
303
304         throw new UnsupportedOperationException("Cannot decode with gson");
305     }
306
307     /**
308      * encode an object into a json string.
309      *
310      * @param groupId    group id
311      * @param artifactId artifact id
312      * @param topic      topic
313      * @param event      object to convert to string
314      * @return the json string
315      * @throws IllegalArgumentException      if invalid argument is provided
316      * @throws UnsupportedOperationException if the operation cannot be performed
317      */
318     public String encode(String groupId, String artifactId, String topic, Object event) {
319
320         if (!isCodingSupported(groupId, artifactId, topic)) {
321             throw new IllegalArgumentException(UNSUPPORTED_EX_MSG + codersKey(groupId, artifactId, topic));
322         }
323
324         if (event == null) {
325             throw new IllegalArgumentException("Unsupported topic:" + topic);
326         }
327
328         // reuse the decoder set, since there must be affinity in the model
329         String key = this.codersKey(groupId, artifactId, topic);
330         return this.encodeInternal(key, event);
331     }
332
333     /**
334      * encode an object into a json string.
335      *
336      * @param topic topic
337      * @param event object to convert to string
338      * @return the json string
339      * @throws IllegalArgumentException      if invalid argument is provided
340      * @throws UnsupportedOperationException if the operation cannot be performed
341      */
342     public String encode(String topic, Object event) {
343
344         if (event == null) {
345             throw new IllegalArgumentException("Invalid encoded class");
346         }
347
348         if (topic == null || topic.isEmpty()) {
349             throw new IllegalArgumentException("Invalid topic");
350         }
351
352         String reverseKey = this.reverseCodersKey(topic, event.getClass().getName());
353         if (!this.reverseCoders.containsKey(reverseKey)) {
354             throw new IllegalArgumentException("no reverse coder has been found");
355         }
356
357         List<ProtocolCoderToolset> toolsets =
358                 this.reverseCoders.get(reverseKey);
359
360         String key =
361                 codersKey(
362                         toolsets.get(0).getGroupId(), toolsets.get(0).getArtifactId(), topic);
363         return this.encodeInternal(key, event);
364     }
365
366     /**
367      * encode an object into a json string.
368      *
369      * @param topic        topic
370      * @param encodedClass object to convert to string
371      * @return the json string
372      * @throws IllegalArgumentException      if invalid argument is provided
373      * @throws UnsupportedOperationException if the operation cannot be performed
374      */
375     public String encode(String topic, Object encodedClass, DroolsController droolsController) {
376
377         if (encodedClass == null) {
378             throw new IllegalArgumentException("Invalid encoded class");
379         }
380
381         if (topic == null || topic.isEmpty()) {
382             throw new IllegalArgumentException("Invalid topic");
383         }
384
385         String key = codersKey(droolsController.getGroupId(), droolsController.getArtifactId(), topic);
386         return this.encodeInternal(key, encodedClass);
387     }
388
389     /**
390      * encode an object into a json string.
391      *
392      * @param key   identifier
393      * @param event object to convert to string
394      * @return the json string
395      * @throws IllegalArgumentException      if invalid argument is provided
396      * @throws UnsupportedOperationException if the operation cannot be performed
397      */
398     protected String encodeInternal(String key, Object event) {
399
400         logger.debug("{}: encode for {}: {}", this, key, event);    // NOSONAR
401
402         /*
403          * It seems that sonar declares the previous logging line as a security vulnerability
404          * when logging the topic variable.   The static code analysis indicates that
405          * the path starts in org.onap.policy.drools.server.restful.RestManager::decode(),
406          * but the request is rejected if the topic contains invalid characters (the sonar description
407          * mentions "/r/n/t" characters) all of which are validated against in the checkValidNameInput(topic).
408          * Furthermore production instances are assumed not to have debug enabled, nor the REST telemetry API
409          * should be published externally.  An additional note is that Path URLs containing spaces and newlines
410          * will be rejected earlier in the HTTP protocol libraries (jetty) so an URL of the form
411          * "https://../to\npic" won't even make it here.
412          */
413
414         ProtocolCoderToolset coderTools = coders.get(key);
415         try {
416             String json = coderTools.encode(event);
417             if (json != null && !json.isEmpty()) {
418                 return json;
419             }
420         } catch (Exception e) {
421             logger.warn("{}: cannot encode (first) for {}: {}", this, key, event, e);
422         }
423
424         throw new UnsupportedOperationException("Cannot decode with gson");
425     }
426
427     /**
428      * Drools creators.
429      *
430      * @param topic        topic
431      * @param encodedClass encoded class
432      * @return list of controllers
433      * @throws IllegalStateException    illegal state
434      * @throws IllegalArgumentException argument
435      */
436     protected List<DroolsController> droolsCreators(String topic, Object encodedClass) {
437
438         List<DroolsController> droolsControllers = new ArrayList<>();
439
440         String reverseKey = this.reverseCodersKey(topic, encodedClass.getClass().getName());
441         if (!this.reverseCoders.containsKey(reverseKey)) {
442             logger.warn("{}: no reverse mapping for {}", this, reverseKey);
443             return droolsControllers;
444         }
445
446         List<ProtocolCoderToolset> toolsets =
447                 this.reverseCoders.get(reverseKey);
448
449         // There must be multiple toolsets associated with <topic,classname> reverseKey
450         // case 2 different controllers use the same models and register the same encoder for
451         // the same topic.  This is assumed not to occur often but for the purpose of encoding
452         // but there should be no side-effects.  Ownership is crosscheck against classname and
453         // classloader reference.
454
455         if (toolsets == null || toolsets.isEmpty()) {
456             throw new IllegalStateException(
457                     "No Encoders toolsets available for topic "
458                             + topic
459                             + " encoder "
460                             + encodedClass.getClass().getName());
461         }
462
463         for (ProtocolCoderToolset encoderSet : toolsets) {
464             addToolsetControllers(droolsControllers, encodedClass, encoderSet);
465         }
466
467         if (droolsControllers.isEmpty()) {
468             throw new IllegalStateException(
469                     "No Encoders toolsets available for "
470                             + topic
471                             + ":"
472                             + encodedClass.getClass().getName());
473         }
474
475         return droolsControllers;
476     }
477
478     private void addToolsetControllers(List<DroolsController> droolsControllers, Object encodedClass,
479                     ProtocolCoderToolset encoderSet) {
480         // figure out the right toolset
481         String groupId = encoderSet.getGroupId();
482         String artifactId = encoderSet.getArtifactId();
483         List<CoderFilters> coderFilters = encoderSet.getCoders();
484         for (CoderFilters coder : coderFilters) {
485             if (coder.getFactClass().equals(encodedClass.getClass().getName())) {
486                 var droolsController =
487                                 DroolsControllerConstants.getFactory().get(groupId, artifactId, "");
488                 if (droolsController.ownsCoder(
489                         encodedClass.getClass(), coder.getModelClassLoaderHash())) {
490                     droolsControllers.add(droolsController);
491                 }
492             }
493         }
494     }
495
496     /**
497      * get all filters by maven coordinates and topic.
498      *
499      * @param groupId    group id
500      * @param artifactId artifact id
501      * @param topic      topic
502      * @return list of coders
503      * @throws IllegalArgumentException if invalid input
504      */
505     public List<CoderFilters> getFilters(String groupId, String artifactId, String topic) {
506
507         if (!isCodingSupported(groupId, artifactId, topic)) {
508             throw new IllegalArgumentException(UNSUPPORTED_EX_MSG + codersKey(groupId, artifactId, topic));
509         }
510
511         String key = this.codersKey(groupId, artifactId, topic);
512         ProtocolCoderToolset coderTools = coders.get(key);
513         return coderTools.getCoders();
514     }
515
516     /**
517      * get all coders by maven coordinates and topic.
518      *
519      * @param groupId    group id
520      * @param artifactId artifact id
521      * @return list of coders
522      * @throws IllegalArgumentException if invalid input
523      */
524     public List<CoderFilters> getFilters(String groupId, String artifactId) {
525
526         if (groupId == null || groupId.isEmpty()) {
527             throw new IllegalArgumentException(INVALID_GROUP_ID_MSG);
528         }
529
530         if (artifactId == null || artifactId.isEmpty()) {
531             throw new IllegalArgumentException(INVALID_ARTIFACT_ID_MSG);
532         }
533
534         String key = this.codersKey(groupId, artifactId, "");
535
536         List<CoderFilters> codersFilters = new ArrayList<>();
537         for (Map.Entry<String, ProtocolCoderToolset> entry :
538                 coders.entrySet()) {
539             if (entry.getKey().startsWith(key)) {
540                 codersFilters.addAll(entry.getValue().getCoders());
541             }
542         }
543
544         return codersFilters;
545     }
546
547     /**
548      * get all filters by maven coordinates, topic, and classname.
549      *
550      * @param groupId    group id
551      * @param artifactId artifact id
552      * @param topic      topic
553      * @param classname  classname
554      * @return list of coders
555      * @throws IllegalArgumentException if invalid input
556      */
557     public CoderFilters getFilters(
558             String groupId, String artifactId, String topic, String classname) {
559
560         if (!isCodingSupported(groupId, artifactId, topic)) {
561             throw new IllegalArgumentException(UNSUPPORTED_EX_MSG + codersKey(groupId, artifactId, topic));
562         }
563
564         if (classname == null || classname.isEmpty()) {
565             throw new IllegalArgumentException("classname must be provided");
566         }
567
568         String key = this.codersKey(groupId, artifactId, topic);
569         ProtocolCoderToolset coderTools = coders.get(key);
570         return coderTools.getCoder(classname);
571     }
572
573     /**
574      * get all coders by maven coordinates and topic.
575      *
576      * @param groupId    group id
577      * @param artifactId artifact id
578      * @param topic      topic
579      * @return list of coders
580      * @throws IllegalArgumentException if invalid input
581      */
582     public ProtocolCoderToolset getCoders(
583             String groupId, String artifactId, String topic) {
584
585         if (!isCodingSupported(groupId, artifactId, topic)) {
586             throw new IllegalArgumentException(UNSUPPORTED_EX_MSG + codersKey(groupId, artifactId, topic));
587         }
588
589         String key = this.codersKey(groupId, artifactId, topic);
590         return coders.get(key);
591     }
592
593     /**
594      * get all coders by maven coordinates and topic.
595      *
596      * @param groupId    group id
597      * @param artifactId artifact id
598      * @return list of coders
599      * @throws IllegalArgumentException if invalid input
600      */
601     public List<ProtocolCoderToolset> getCoders(
602             String groupId, String artifactId) {
603
604         if (groupId == null || groupId.isEmpty()) {
605             throw new IllegalArgumentException(INVALID_GROUP_ID_MSG);
606         }
607
608         if (artifactId == null || artifactId.isEmpty()) {
609             throw new IllegalArgumentException(INVALID_ARTIFACT_ID_MSG);
610         }
611
612         String key = this.codersKey(groupId, artifactId, "");
613
614         List<ProtocolCoderToolset> coderToolset = new ArrayList<>();
615         for (Map.Entry<String, ProtocolCoderToolset> entry :
616                 coders.entrySet()) {
617             if (entry.getKey().startsWith(key)) {
618                 coderToolset.add(entry.getValue());
619             }
620         }
621
622         return coderToolset;
623     }
624
625     /**
626      * get coded based on class and topic.
627      *
628      * @param topic      topic
629      * @param codedClass class
630      * @return list of reverse filters
631      */
632     public List<CoderFilters> getReverseFilters(String topic, String codedClass) {
633
634         if (topic == null || topic.isEmpty()) {
635             throw new IllegalArgumentException(UNSUPPORTED_MSG);
636         }
637
638         if (codedClass == null) {
639             throw new IllegalArgumentException(MISSING_CLASS);
640         }
641
642         String key = this.reverseCodersKey(topic, codedClass);
643         List<ProtocolCoderToolset> toolsets = this.reverseCoders.get(key);
644         if (toolsets == null) {
645             throw new IllegalArgumentException("No Coder found for " + key);
646         }
647
648         List<CoderFilters> coderFilters = new ArrayList<>();
649         for (ProtocolCoderToolset toolset : toolsets) {
650             coderFilters.addAll(toolset.getCoders());
651         }
652
653         return coderFilters;
654     }
655
656     /**
657      * returns group and artifact id of the creator of the encoder.
658      *
659      * @param topic topic
660      * @param fact  fact
661      * @return the drools controller
662      */
663     DroolsController getDroolsController(String topic, Object fact) {
664
665         if (topic == null || topic.isEmpty()) {
666             throw new IllegalArgumentException(UNSUPPORTED_MSG);
667         }
668
669         if (fact == null) {
670             throw new IllegalArgumentException(MISSING_CLASS);
671         }
672
673         List<DroolsController> droolsControllers = droolsCreators(topic, fact);
674
675         if (droolsControllers.isEmpty()) {
676             throw new IllegalArgumentException("Invalid Topic: " + topic);
677         }
678
679         if (droolsControllers.size() > 1) {
680             logger.warn(
681                     "{}: multiple drools-controller {} for {}:{} ",
682                     this,
683                     droolsControllers,
684                     topic,
685                     fact.getClass().getName());
686             // continue
687         }
688         return droolsControllers.get(0);
689     }
690
691     /**
692      * returns group and artifact id of the creator of the encoder.
693      *
694      * @param topic topic
695      * @param fact  fact
696      * @return list of drools controllers
697      */
698     List<DroolsController> getDroolsControllers(String topic, Object fact) {
699
700         if (topic == null || topic.isEmpty()) {
701             throw new IllegalArgumentException(UNSUPPORTED_MSG);
702         }
703
704         if (fact == null) {
705             throw new IllegalArgumentException(MISSING_CLASS);
706         }
707
708         List<DroolsController> droolsControllers = droolsCreators(topic, fact);
709         if (droolsControllers.size() > 1) {
710             // unexpected
711             logger.warn(
712                     "{}: multiple drools-controller {} for {}:{} ",
713                     this,
714                     droolsControllers,
715                     topic,
716                     fact.getClass().getName());
717             // continue
718         }
719         return droolsControllers;
720     }
721
722     /*
723      * Note: this only logs the KEYSETS, thus lombok ToString annotation is not used.
724      * Otherwise, it results in too much verbosity.
725      */
726     @Override
727     public String toString() {
728         return "GenericEventProtocolCoder [coders="
729                 + coders.keySet()
730                 + ", reverseCoders="
731                 + reverseCoders.keySet()
732                 + "]";
733     }
734 }