Removing deprecated DMAAP library
[policy/drools-pdp.git] / policy-management / src / main / java / org / onap / policy / drools / server / restful / RestManager.java
1 /*
2  * ============LICENSE_START=======================================================
3  * ONAP
4  * ================================================================================
5  * Copyright (C) 2017-2021 AT&T Intellectual Property. All rights reserved.
6  * Modifications Copyright (C) 2021, 2023-2024 Nordix Foundation.
7  * ================================================================================
8  * Licensed under the Apache License, Version 2.0 (the "License");
9  * you may not use this file except in compliance with the License.
10  * You may obtain a copy of the License at
11  *
12  *      http://www.apache.org/licenses/LICENSE-2.0
13  *
14  * Unless required by applicable law or agreed to in writing, software
15  * distributed under the License is distributed on an "AS IS" BASIS,
16  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
17  * See the License for the specific language governing permissions and
18  * limitations under the License.
19  * ============LICENSE_END=========================================================
20  */
21
22 package org.onap.policy.drools.server.restful;
23
24 import ch.qos.logback.classic.LoggerContext;
25 import com.google.re2j.Pattern;
26 import jakarta.ws.rs.Consumes;
27 import jakarta.ws.rs.DELETE;
28 import jakarta.ws.rs.DefaultValue;
29 import jakarta.ws.rs.GET;
30 import jakarta.ws.rs.POST;
31 import jakarta.ws.rs.PUT;
32 import jakarta.ws.rs.Path;
33 import jakarta.ws.rs.PathParam;
34 import jakarta.ws.rs.Produces;
35 import jakarta.ws.rs.QueryParam;
36 import jakarta.ws.rs.core.MediaType;
37 import jakarta.ws.rs.core.Response;
38 import jakarta.ws.rs.core.Response.Status;
39 import java.io.BufferedReader;
40 import java.io.IOException;
41 import java.io.InputStream;
42 import java.io.InputStreamReader;
43 import java.util.ArrayList;
44 import java.util.Arrays;
45 import java.util.Collections;
46 import java.util.HashMap;
47 import java.util.List;
48 import java.util.Map;
49 import java.util.Objects;
50 import java.util.Properties;
51 import java.util.UUID;
52 import java.util.function.Function;
53 import java.util.function.Supplier;
54 import java.util.stream.Collectors;
55 import lombok.AllArgsConstructor;
56 import lombok.Getter;
57 import lombok.Setter;
58 import lombok.ToString;
59 import org.onap.policy.common.endpoints.event.comm.Topic;
60 import org.onap.policy.common.endpoints.event.comm.Topic.CommInfrastructure;
61 import org.onap.policy.common.endpoints.event.comm.TopicEndpointManager;
62 import org.onap.policy.common.endpoints.event.comm.TopicSink;
63 import org.onap.policy.common.endpoints.event.comm.TopicSource;
64 import org.onap.policy.common.endpoints.http.server.YamlMessageBodyHandler;
65 import org.onap.policy.common.utils.logging.LoggerUtils;
66 import org.onap.policy.drools.controller.DroolsController;
67 import org.onap.policy.drools.properties.DroolsPropertyConstants;
68 import org.onap.policy.drools.protocol.coders.EventProtocolCoder.CoderFilters;
69 import org.onap.policy.drools.protocol.coders.EventProtocolCoderConstants;
70 import org.onap.policy.drools.protocol.coders.JsonProtocolFilter;
71 import org.onap.policy.drools.protocol.coders.ProtocolCoderToolset;
72 import org.onap.policy.drools.protocol.configuration.ControllerConfiguration;
73 import org.onap.policy.drools.protocol.configuration.PdpdConfiguration;
74 import org.onap.policy.drools.system.PolicyController;
75 import org.onap.policy.drools.system.PolicyControllerConstants;
76 import org.onap.policy.drools.system.PolicyEngineConstants;
77 import org.slf4j.Logger;
78 import org.slf4j.LoggerFactory;
79
80 /**
81  * Telemetry JAX-RS Interface to the PDP-D.
82  */
83
84 @Path("/policy/pdp")
85 @Produces({MediaType.APPLICATION_JSON, YamlMessageBodyHandler.APPLICATION_YAML})
86 @Consumes({MediaType.APPLICATION_JSON, YamlMessageBodyHandler.APPLICATION_YAML})
87 @ToString
88 public class RestManager implements SwaggerApi, DefaultApi, FeaturesApi, InputsApi,
89         PropertiesApi, EnvironmentApi, SwitchesApi, ControllersApi,
90         TopicsApi, ToolsApi {
91
92     private static final String OFFER_FAILED = "{}: cannot offer to topic {} because of {}";
93     private static final String CANNOT_PERFORM_OPERATION = "cannot perform operation";
94     private static final String NO_FILTERS = " no filters";
95     private static final String NOT_FOUND = " not found: ";
96     private static final String NOT_FOUND_MSG = " not found";
97     private static final String DOES_NOT_EXIST_MSG = " does not exist";
98     private static final String NOT_ACCEPTABLE_MSG = " not acceptable";
99     private static final String FETCH_POLICY_FAILED = "{}: cannot get policy-controller because of {}";
100     private static final String FETCH_POLICY_BY_NAME_FAILED = "{}: cannot get policy-controller {} because of {}";
101     private static final String FETCH_POLICY_BY_TOPIC_FAILED =
102         "{}: cannot get policy-controller {} topic {} because of {}";
103     private static final String FETCH_DROOLS_FAILED = "{}: cannot get drools-controller {} because of {}";
104     private static final String FETCH_DROOLS_BY_ENTITY_FAILED =
105         "{}: cannot get: drools-controller {}, session {}, query {}, entity {} because of {}";
106     private static final String FETCH_DROOLS_BY_PARAMS_FAILED =
107         "{}: cannot get: drools-controller {}, session {}, query {}, entity {}, params {} because of {}";
108     private static final String FETCH_DROOLS_BY_FACTTYPE_FAILED =
109         "{}: cannot get: drools-controller {}, session {}, factType {}, because of {}";
110     private static final String FETCH_DECODERS_BY_POLICY_FAILED =
111         "{}: cannot get decoders for policy-controller {} because of {}";
112     private static final String FETCH_DECODERS_BY_TOPIC_FAILED =
113         "{}: cannot get decoders for policy-controller {} topic {} because of {}";
114     private static final String FETCH_DECODER_BY_TYPE_FAILED =
115         "{}: cannot get decoder filters for policy-controller {} topic {} type {} because of {}";
116     private static final String FETCH_DECODER_BY_FILTER_FAILED =
117         "{}: cannot get decoder filters for policy-controller {} topic {} type {} filters {} because of {}";
118     private static final String FETCH_ENCODER_BY_FILTER_FAILED =
119         "{}: cannot get encoder filters for policy-controller {} because of {}";
120
121     private static final String SWAGGER = "/swagger/swagger.json";
122
123     /**
124      * Logger.
125      */
126     private static final Logger logger = LoggerFactory.getLogger(RestManager.class);
127
128     /**
129      * Feed Ports into Resources.
130      */
131     private static final List<String> INPUTS = Collections.singletonList("configuration");
132
133     /**
134      * Resource Toggles.
135      */
136     private static final List<String> SWITCHES = Arrays.asList("activation", "lock");
137
138     /**
139      * GET.
140      *
141      * @return response object
142      */
143     @Override
144     @GET
145     @Path("engine/swagger")
146     public Response swagger() {
147
148         try (InputStream inputStream = getClass().getResourceAsStream(SWAGGER);
149             BufferedReader reader = new BufferedReader(
150                 new InputStreamReader(Objects.requireNonNull(inputStream)))) {
151             String contents = reader.lines()
152                     .collect(Collectors.joining(System.lineSeparator()));
153             return Response.status(Response.Status.OK)
154                         .entity(contents)
155                         .build();
156         } catch (IOException e) {
157             logger.error("Cannot read swagger.json {} because of {}", e.getMessage(), e);
158             return Response.status(Response.Status.INTERNAL_SERVER_ERROR)
159                     .build();
160         }
161
162     }
163
164     /**
165      * GET.
166      *
167      * @return response object
168      */
169     @Override
170     @GET
171     @Path("engine")
172     public Response engine() {
173         return Response.status(Response.Status.OK).entity(PolicyEngineConstants.getManager()).build();
174     }
175
176     /**
177      * DELETE.
178      *
179      * @return response object
180      */
181     @Override
182     @DELETE
183     @Path("engine")
184     public Response engineShutdown() {
185         try {
186             PolicyEngineConstants.getManager().shutdown();
187         } catch (final IllegalStateException e) {
188             logger.error("{}: cannot shutdown {} because of {}", this, PolicyEngineConstants.getManager(),
189                 e.getMessage(), e);
190             return Response.status(Response.Status.BAD_REQUEST).entity(PolicyEngineConstants.getManager()).build();
191         }
192
193         return Response.status(Response.Status.OK).entity(PolicyEngineConstants.getManager()).build();
194     }
195
196     /**
197      * GET.
198      *
199      * @return response object
200      */
201     @Override
202     @GET
203     @Path("engine/features")
204     public Response engineFeatures() {
205         return Response.status(Response.Status.OK).entity(PolicyEngineConstants.getManager().getFeatures()).build();
206     }
207
208     @Override
209     @GET
210     @Path("engine/features/inventory")
211     public Response engineFeaturesInventory() {
212         return Response.status(Response.Status.OK).entity(PolicyEngineConstants.getManager().getFeatureProviders())
213             .build();
214     }
215
216     /**
217      * GET.
218      *
219      * @return response object
220      */
221     @Override
222     @GET
223     @Path("engine/features/{featureName}")
224     public Response engineFeature(@PathParam("featureName") String featureName) {
225         try {
226             return Response.status(Response.Status.OK)
227                 .entity(PolicyEngineConstants.getManager().getFeatureProvider(featureName)).build();
228         } catch (final IllegalArgumentException iae) {
229             logger.debug("feature unavailable: {}", featureName, iae);
230             return Response.status(Response.Status.NOT_FOUND).entity(new Error(iae.getMessage())).build();
231         }
232     }
233
234     /**
235      * GET.
236      *
237      * @return response object
238      */
239     @Override
240     @GET
241     @Path("engine/inputs")
242     public Response engineInputs() {
243         return Response.status(Response.Status.OK).entity(INPUTS).build();
244     }
245
246     /**
247      * POST.
248      *
249      * @return response object
250      */
251     @Override
252     @POST
253     @Path("engine/inputs/configuration")
254     public Response engineUpdate(PdpdConfiguration configuration) {
255         final PolicyController controller = null;
256         boolean success;
257         try {
258             success = PolicyEngineConstants.getManager().configure(configuration);
259         } catch (final Exception e) {
260             success = false;
261             logger.info("{}: cannot configure {} because of {}", this, PolicyEngineConstants.getManager(),
262                 e.getMessage(), e);
263         }
264
265         if (!success) {
266             return Response.status(Response.Status.NOT_ACCEPTABLE).entity(new Error(CANNOT_PERFORM_OPERATION))
267                 .build();
268         } else {
269             return Response.status(Response.Status.OK).entity(controller).build();
270         }
271     }
272
273     /**
274      * GET.
275      *
276      * @return response object
277      */
278     @Override
279     @GET
280     @Path("engine/properties")
281     public Response engineProperties() {
282         return Response.status(Response.Status.OK).entity(PolicyEngineConstants.getManager().getProperties()).build();
283     }
284
285     /**
286      * GET.
287      *
288      * @return response object
289      */
290     @Override
291     @GET
292     @Path("engine/environment")
293     public Response engineEnvironment() {
294         return Response.status(Response.Status.OK).entity(PolicyEngineConstants.getManager().getEnvironment()).build();
295     }
296
297     /**
298      * GET.
299      *
300      * @return response object
301      */
302     @Override
303     @GET
304     @Path("engine/environment/{envProperty}")
305     @Consumes(MediaType.TEXT_PLAIN)
306     public Response engineEnvironmentProperty(@PathParam("envProperty") String envProperty) {
307         return Response.status(Response.Status.OK)
308             .entity(PolicyEngineConstants.getManager().getEnvironmentProperty(envProperty)).build();
309     }
310
311     /**
312      * PUT.
313      *
314      * @return response object
315      */
316     @Override
317     @PUT
318     @Path("engine/environment/{envProperty}")
319     @Consumes(MediaType.TEXT_PLAIN)
320     @Produces(MediaType.TEXT_PLAIN)
321     public Response engineEnvironmentAdd(@PathParam("envProperty") String envProperty, String envValue) {
322         final String previousValue = PolicyEngineConstants.getManager().setEnvironmentProperty(envProperty, envValue);
323         return Response.status(Response.Status.OK).entity(previousValue).build();
324     }
325
326     /**
327      * GET.
328      *
329      * @return response object
330      */
331     @Override
332     @GET
333     @Path("engine/switches")
334     public Response engineSwitches() {
335         return Response.status(Response.Status.OK).entity(SWITCHES).build();
336     }
337
338     /**
339      * PUT.
340      *
341      * @return response object
342      */
343     @Override
344     @PUT
345     @Path("engine/switches/activation")
346     public Response engineActivation() {
347         var success = true;
348         try {
349             PolicyEngineConstants.getManager().activate();
350         } catch (final Exception e) {
351             success = false;
352             logger.info("{}: cannot activate {} because of {}", this, PolicyEngineConstants.getManager(),
353                 e.getMessage(), e);
354         }
355
356         if (!success) {
357             return Response.status(Response.Status.NOT_ACCEPTABLE).entity(new Error(CANNOT_PERFORM_OPERATION))
358                 .build();
359         } else {
360             return Response.status(Response.Status.OK).entity(PolicyEngineConstants.getManager()).build();
361         }
362     }
363
364     /**
365      * DELETE.
366      *
367      * @return response object
368      */
369     @Override
370     @DELETE
371     @Path("engine/switches/activation")
372     public Response engineDeactivation() {
373         var success = true;
374         try {
375             PolicyEngineConstants.getManager().deactivate();
376         } catch (final Exception e) {
377             success = false;
378             logger.info("{}: cannot deactivate {} because of {}", this, PolicyEngineConstants.getManager(),
379                 e.getMessage(), e);
380         }
381
382         if (!success) {
383             return Response.status(Response.Status.NOT_ACCEPTABLE).entity(new Error(CANNOT_PERFORM_OPERATION))
384                 .build();
385         } else {
386             return Response.status(Response.Status.OK).entity(PolicyEngineConstants.getManager()).build();
387         }
388     }
389
390     /**
391      * PUT.
392      *
393      * @return response object
394      */
395     @Override
396     @PUT
397     @Path("engine/switches/lock")
398     public Response engineLock() {
399         final boolean success = PolicyEngineConstants.getManager().lock();
400         if (success) {
401             return Response.status(Status.OK).entity(PolicyEngineConstants.getManager()).build();
402         } else {
403             return Response.status(Status.NOT_ACCEPTABLE).entity(new Error(CANNOT_PERFORM_OPERATION)).build();
404         }
405     }
406
407     /**
408      * DELETE.
409      *
410      * @return response object
411      */
412     @Override
413     @DELETE
414     @Path("engine/switches/lock")
415     public Response engineUnlock() {
416         final boolean success = PolicyEngineConstants.getManager().unlock();
417         if (success) {
418             return Response.status(Status.OK).entity(PolicyEngineConstants.getManager()).build();
419         } else {
420             return Response.status(Status.NOT_ACCEPTABLE).entity(new Error(CANNOT_PERFORM_OPERATION)).build();
421         }
422     }
423
424     /**
425      * GET.
426      *
427      * @return response object
428      */
429     @Override
430     @GET
431     @Path("engine/controllers")
432     public Response controllers() {
433         return Response.status(Response.Status.OK).entity(PolicyEngineConstants.getManager().getPolicyControllerIds())
434             .build();
435     }
436
437     /**
438      * GET.
439      *
440      * @return response object
441      */
442     @Override
443     @GET
444     @Path("engine/controllers/inventory")
445     public Response controllerInventory() {
446         return Response.status(Response.Status.OK).entity(PolicyEngineConstants.getManager().getPolicyControllers())
447             .build();
448     }
449
450     /**
451      * POST.
452      *
453      * @return response object
454      */
455     @Override
456     @POST
457     @Path("engine/controllers")
458     public Response controllerAdd(Properties config) {
459         if (config == null) {
460             return Response.status(Response.Status.BAD_REQUEST).entity(new Error("A configuration must be provided"))
461                 .build();
462         }
463
464         final String controllerName = config.getProperty(DroolsPropertyConstants.PROPERTY_CONTROLLER_NAME);
465         if (controllerName == null || controllerName.isEmpty()) {
466             return Response.status(Response.Status.BAD_REQUEST)
467                 .entity(new Error(
468                     "Configuration must have an entry for " + DroolsPropertyConstants.PROPERTY_CONTROLLER_NAME))
469                 .build();
470         }
471
472         PolicyController controller;
473         try {
474             controller = PolicyControllerConstants.getFactory().get(controllerName);
475             if (controller != null) {
476                 return Response.status(Response.Status.NOT_MODIFIED).entity(controller).build();
477             }
478         } catch (final IllegalArgumentException e) {
479             logger.trace("OK ", e);
480             // This is OK
481         } catch (final IllegalStateException e) {
482             logger.info(FETCH_POLICY_FAILED, this, e.getMessage(), e);
483             return Response.status(Response.Status.NOT_ACCEPTABLE).entity(new Error(controllerName + NOT_FOUND_MSG))
484                 .build();
485         }
486
487         try {
488             controller = PolicyEngineConstants.getManager().createPolicyController(
489                 config.getProperty(DroolsPropertyConstants.PROPERTY_CONTROLLER_NAME), config);
490         } catch (IllegalArgumentException | IllegalStateException e) {
491             logger.warn("{}: cannot create policy-controller because of {}", this, e.getMessage(), e);
492             return Response.status(Response.Status.BAD_REQUEST).entity(new Error(e.getMessage())).build();
493         }
494
495         try {
496             final boolean success = controller.start();
497             if (!success) {
498                 logger.info("{}: cannot start {}", this, controller);
499                 return Response.status(Response.Status.PARTIAL_CONTENT)
500                     .entity(new Error(controllerName + " can't be started")).build();
501             }
502         } catch (final IllegalStateException e) {
503             logger.info("{}: cannot start {} because of {}", this, controller, e.getMessage(), e);
504             return Response.status(Response.Status.PARTIAL_CONTENT).entity(controller).build();
505         }
506
507         return Response.status(Response.Status.CREATED).entity(controller).build();
508     }
509
510     /**
511      * GET.
512      *
513      * @return response object
514      */
515     @Override
516     @GET
517     @Path("engine/controllers/features")
518     public Response controllerFeatures() {
519         return Response.status(Response.Status.OK).entity(PolicyEngineConstants.getManager().getFeatures()).build();
520     }
521
522     /**
523      * GET.
524      *
525      * @return response object
526      */
527     @Override
528     @GET
529     @Path("engine/controllers/features/inventory")
530     public Response controllerFeaturesInventory() {
531         return Response.status(Response.Status.OK)
532             .entity(PolicyControllerConstants.getFactory().getFeatureProviders()).build();
533     }
534
535     /**
536      * GET.
537      *
538      * @return response object
539      */
540     @Override
541     @GET
542     @Path("engine/controllers/features/{featureName}")
543     public Response controllerFeature(@PathParam("featureName") String featureName) {
544         try {
545             return Response.status(Response.Status.OK)
546                 .entity(PolicyControllerConstants.getFactory().getFeatureProvider(featureName))
547                 .build();
548         } catch (final IllegalArgumentException iae) {
549             logger.debug("{}: cannot feature {} because of {}", this, featureName, iae.getMessage(), iae);
550             return Response.status(Response.Status.NOT_FOUND).entity(new Error(iae.getMessage())).build();
551         }
552     }
553
554     /**
555      * GET.
556      *
557      * @return response object
558      */
559     @Override
560     @GET
561     @Path("engine/controllers/{controller}")
562     public Response controller(@PathParam("controller") String controllerName) {
563
564         return catchArgStateGenericEx(
565             () -> Response.status(Response.Status.OK)
566                 .entity(PolicyControllerConstants.getFactory().get(controllerName)).build(),
567             e -> {
568                 logger.debug(FETCH_POLICY_BY_NAME_FAILED, this, controllerName, e.getMessage(), e);
569                 return (controllerName);
570             });
571     }
572
573     /**
574      * DELETE.
575      *
576      * @return response object
577      */
578     @Override
579     @DELETE
580     @Path("engine/controllers/{controller}")
581     public Response controllerDelete(@PathParam("controller") String controllerName) {
582
583         PolicyController controller;
584         try {
585             controller = PolicyControllerConstants.getFactory().get(controllerName);
586             if (controller == null) {
587                 return Response.status(Response.Status.BAD_REQUEST)
588                     .entity(new Error(controllerName + DOES_NOT_EXIST_MSG)).build();
589             }
590         } catch (final IllegalArgumentException e) {
591             logger.debug(FETCH_POLICY_BY_NAME_FAILED, this, controllerName, e.getMessage(), e);
592             return Response.status(Response.Status.BAD_REQUEST)
593                 .entity(new Error(controllerName + NOT_FOUND + e.getMessage())).build();
594         } catch (final IllegalStateException e) {
595             logger.debug(FETCH_POLICY_BY_NAME_FAILED, this, controllerName, e.getMessage(), e);
596             return Response.status(Response.Status.NOT_ACCEPTABLE)
597                 .entity(new Error(controllerName + NOT_ACCEPTABLE_MSG)).build();
598         }
599
600         try {
601             PolicyEngineConstants.getManager().removePolicyController(controllerName);
602         } catch (IllegalArgumentException | IllegalStateException e) {
603             logger.debug("{}: cannot remove policy-controller {} because of {}", this, controllerName, e.getMessage(),
604                 e);
605             return Response.status(Response.Status.INTERNAL_SERVER_ERROR).entity(new Error(e.getMessage())).build();
606         }
607
608         return Response.status(Response.Status.OK).entity(controller).build();
609     }
610
611     /**
612      * GET.
613      *
614      * @return response object
615      */
616     @Override
617     @GET
618     @Path("engine/controllers/{controller}/properties")
619
620     public Response controllerProperties(@PathParam("controller") String controllerName) {
621
622         return catchArgStateGenericEx(() -> {
623             final PolicyController controller = PolicyControllerConstants.getFactory().get(controllerName);
624             return Response.status(Response.Status.OK).entity(controller.getProperties()).build();
625
626         }, e -> {
627             logger.debug(FETCH_POLICY_BY_NAME_FAILED, this, controllerName, e.getMessage(), e);
628             return (controllerName);
629         });
630     }
631
632     /**
633      * GET.
634      *
635      * @return response object
636      */
637     @Override
638     @GET
639     @Path("engine/controllers/{controller}/inputs")
640     public Response controllerInputs(@PathParam("controller") String controllerName) {
641         return Response.status(Response.Status.OK).entity(INPUTS).build();
642     }
643
644     /**
645      * POST.
646      *
647      * @return response object
648      */
649     @Override
650     @POST
651     @Path("engine/controllers/{controller}/inputs/configuration")
652     public Response controllerUpdate(ControllerConfiguration controllerConfiguration,
653             @PathParam("controller") String controllerName) {
654
655         if (controllerName == null || controllerName.isEmpty() || controllerConfiguration == null
656             || !controllerName.equals(controllerConfiguration.getName())) {
657             return Response.status(Response.Status.BAD_REQUEST)
658                 .entity("A valid or matching controller names must be provided").build();
659         }
660
661         return catchArgStateGenericEx(() -> {
662             var controller =
663                 PolicyEngineConstants.getManager().updatePolicyController(controllerConfiguration);
664             if (controller == null) {
665                 return Response.status(Response.Status.BAD_REQUEST)
666                     .entity(new Error(controllerName + DOES_NOT_EXIST_MSG)).build();
667             }
668
669             return Response.status(Response.Status.OK).entity(controller).build();
670
671         }, e -> {
672             logger.info("{}: cannot update policy-controller {} because of {}", this, controllerName,
673                 e.getMessage(), e);
674             return (controllerName);
675         });
676     }
677
678     /**
679      * GET.
680      *
681      * @return response object
682      */
683     @Override
684     @GET
685     @Path("engine/controllers/{controller}/switches")
686     public Response controllerSwitches(@PathParam("controller") String controllerName) {
687         return Response.status(Response.Status.OK).entity(SWITCHES).build();
688     }
689
690     /**
691      * PUT.
692      *
693      * @return response object
694      */
695     @Override
696     @PUT
697     @Path("engine/controllers/{controller}/switches/lock")
698     public Response controllerLock(@PathParam("controller") String controllerName) {
699         var policyController = PolicyControllerConstants.getFactory().get(controllerName);
700         final boolean success = policyController.lock();
701         if (success) {
702             return Response.status(Status.OK).entity(policyController).build();
703         } else {
704             return Response.status(Status.NOT_ACCEPTABLE)
705                 .entity(new Error("Controller " + controllerName + " cannot be locked")).build();
706         }
707     }
708
709     /**
710      * DELETE.
711      *
712      * @return response object
713      */
714     @Override
715     @DELETE
716     @Path("engine/controllers/{controller}/switches/lock")
717     public Response controllerUnlock(@PathParam("controller") String controllerName) {
718         var policyController = PolicyControllerConstants.getFactory().get(controllerName);
719         final boolean success = policyController.unlock();
720         if (success) {
721             return Response.status(Status.OK).entity(policyController).build();
722         } else {
723             return Response.status(Status.NOT_ACCEPTABLE)
724                 .entity(new Error("Controller " + controllerName + " cannot be unlocked")).build();
725         }
726     }
727
728     /**
729      * GET.
730      *
731      * @return response object
732      */
733     @Override
734     @GET
735     @Path("engine/controllers/{controller}/drools")
736     public Response drools(@PathParam("controller") String controllerName) {
737
738         return catchArgStateGenericEx(() -> {
739             var drools = this.getDroolsController(controllerName);
740             return Response.status(Response.Status.OK).entity(drools).build();
741
742         }, e -> {
743             logger.debug(FETCH_DROOLS_FAILED, this, controllerName, e.getMessage(), e);
744             return (controllerName);
745         });
746     }
747
748     /**
749      * GET.
750      *
751      * @return response object
752      */
753     @Override
754     @GET
755     @Path("engine/controllers/{controller}/drools/facts")
756     public Response droolsFacts2(@PathParam("controller") String controllerName) {
757
758         return catchArgStateGenericEx(() -> {
759             final Map<String, Long> sessionCounts = new HashMap<>();
760             var drools = this.getDroolsController(controllerName);
761             for (final String session : drools.getSessionNames()) {
762                 sessionCounts.put(session, drools.factCount(session));
763             }
764             return sessionCounts;
765
766         }, e -> {
767             logger.debug(FETCH_POLICY_BY_NAME_FAILED, this, controllerName, e.getMessage(), e);
768             return controllerName;
769         });
770     }
771
772     /**
773      * GET.
774      *
775      * @return response object
776      */
777     @Override
778     @GET
779     @Path("engine/controllers/{controller}/drools/facts/{session}")
780     public Response droolsFacts1(@PathParam("controller") String controllerName,
781         @PathParam("session") String sessionName) {
782
783         return catchArgStateGenericEx(() -> {
784             var drools = this.getDroolsController(controllerName);
785             return drools.factClassNames(sessionName);
786
787         }, e -> {
788             logger.debug(FETCH_DROOLS_FAILED, this, controllerName, e.getMessage(), e);
789             return (controllerName + ":" + sessionName);
790         });
791     }
792
793     /**
794      * GET.
795      *
796      * @return response object
797      */
798     @Override
799     @GET
800     @Path("engine/controllers/{controller}/drools/facts/{session}/{factType}")
801     public Response droolsFacts(
802         @PathParam("controller") String controllerName,
803         @PathParam("session") String sessionName,
804         @PathParam("factType") String factType,
805         @DefaultValue("false") @QueryParam("count") boolean count) {
806
807         return catchArgStateGenericEx(() -> {
808             var drools = this.getDroolsController(controllerName);
809             final List<Object> facts = drools.facts(sessionName, factType, false);
810             return (count ? facts.size() : facts);
811
812         }, e -> {
813             logger.debug(FETCH_POLICY_BY_NAME_FAILED, this, controllerName, e.getMessage(), e);
814             return (controllerName + ":" + sessionName + ":" + factType);
815         });
816     }
817
818     /**
819      * GET.
820      *
821      * @return response object
822      */
823     @Override
824     @GET
825     @Path("engine/controllers/{controller}/drools/facts/{session}/{query}/{queriedEntity}")
826     public Response droolsFacts3(
827         @PathParam("controller") String controllerName,
828         @PathParam("session") String sessionName,
829         @PathParam("query") String queryName,
830         @PathParam("queriedEntity") String queriedEntity,
831         @DefaultValue("false") @QueryParam("count") boolean count) {
832
833         return catchArgStateGenericEx(() -> {
834             var drools = this.getDroolsController(controllerName);
835             final List<Object> facts = drools.factQuery(sessionName, queryName, queriedEntity, false);
836             return (count ? facts.size() : facts);
837
838         }, e -> {
839             logger.debug(FETCH_DROOLS_BY_ENTITY_FAILED, this,
840                 controllerName, sessionName, queryName, queriedEntity, e.getMessage(), e);
841             return (controllerName + ":" + sessionName + ":" + queryName + queriedEntity);
842         });
843     }
844
845     /**
846      * POST.
847      *
848      * @return response object
849      */
850     @Override
851     @POST
852     @Path("engine/controllers/{controller}/drools/facts/{session}/{query}/{queriedEntity}")
853     public Response droolsFacts4(
854         @PathParam("controller") String controllerName,
855         @PathParam("session") String sessionName,
856         @PathParam("query") String queryName,
857         @PathParam("queriedEntity") String queriedEntity,
858         List<Object> queryParameters) {
859
860         return catchArgStateGenericEx(() -> {
861             var drools = this.getDroolsController(controllerName);
862             if (queryParameters == null || queryParameters.isEmpty()) {
863                 return drools.factQuery(sessionName, queryName, queriedEntity, false);
864             } else {
865                 return drools.factQuery(sessionName, queryName, queriedEntity, false, queryParameters.toArray());
866             }
867
868         }, e -> {
869             logger.debug(FETCH_DROOLS_BY_PARAMS_FAILED,
870                 this, controllerName, sessionName, queryName, queriedEntity, queryParameters, e.getMessage(), e);
871             return (controllerName + ":" + sessionName + ":" + queryName + queriedEntity);
872         });
873     }
874
875     /**
876      * DELETE.
877      *
878      * @return response object
879      */
880     @Override
881     @DELETE
882     @Path("engine/controllers/{controller}/drools/facts/{session}/{factType}")
883     public Response droolsFactsDelete1(
884         @PathParam("controller") String controllerName,
885         @PathParam("session") String sessionName,
886         @PathParam("factType") String factType) {
887
888         return catchArgStateGenericEx(() -> {
889             var drools = this.getDroolsController(controllerName);
890             return drools.facts(sessionName, factType, true);
891
892         }, e -> {
893             logger.debug(FETCH_DROOLS_BY_FACTTYPE_FAILED, this,
894                 controllerName, sessionName, factType, e.getMessage(), e);
895             return (controllerName + ":" + sessionName + ":" + factType);
896         });
897     }
898
899     /**
900      * DELETE.
901      *
902      * @return response object
903      */
904     @Override
905     @DELETE
906     @Path("engine/controllers/{controller}/drools/facts/{session}/{query}/{queriedEntity}")
907     public Response droolsFactsDelete(
908         @PathParam("controller") String controllerName,
909         @PathParam("session") String sessionName,
910         @PathParam("query") String queryName,
911         @PathParam("queriedEntity") String queriedEntity) {
912
913         return catchArgStateGenericEx(() -> {
914             var drools = this.getDroolsController(controllerName);
915             return drools.factQuery(sessionName, queryName, queriedEntity, true);
916
917
918         }, e -> {
919             logger.debug(FETCH_DROOLS_BY_PARAMS_FAILED,
920                 this, controllerName, sessionName, queryName, queriedEntity, e.getMessage(), e);
921             return (controllerName + ":" + sessionName + ":" + queryName + queriedEntity);
922         });
923     }
924
925     /**
926      * POST.
927      *
928      * @return response object
929      */
930     @Override
931     @POST
932     @Path("engine/controllers/tools/coders/decoders/filters/rule")
933     public Response rules(String expression) {
934         return Response.status(Status.OK).entity(new JsonProtocolFilter(expression)).build();
935     }
936
937     /**
938      * GET.
939      *
940      * @return response object
941      */
942     @Override
943     @GET
944     @Path("engine/controllers/{controller}/decoders")
945     public Response decoders(@PathParam("controller") String controllerName) {
946
947         return catchArgStateGenericEx(() -> {
948             var drools = this.getDroolsController(controllerName);
949             return EventProtocolCoderConstants.getManager().getDecoders(drools.getGroupId(), drools.getArtifactId());
950
951         }, e -> {
952             logger.debug(FETCH_DECODERS_BY_POLICY_FAILED, this, controllerName,
953                 e.getMessage(), e);
954             return (controllerName);
955         });
956     }
957
958     /**
959      * GET.
960      *
961      * @return response object
962      */
963     @Override
964     @GET
965     @Path("engine/controllers/{controller}/decoders/filters")
966     public Response decoderFilters(@PathParam("controller") String controllerName) {
967
968         return catchArgStateGenericEx(() -> {
969             var drools = this.getDroolsController(controllerName);
970             return EventProtocolCoderConstants.getManager()
971                 .getDecoderFilters(drools.getGroupId(), drools.getArtifactId());
972
973         }, e -> {
974             logger.debug(FETCH_DECODERS_BY_POLICY_FAILED, this, controllerName, e.getMessage(), e);
975             return (controllerName);
976         });
977     }
978
979     /**
980      * GET.
981      *
982      * @return response object
983      */
984     @Override
985     @GET
986     @Path("engine/controllers/{controller}/decoders/{topic}")
987     public Response decoder(
988         @PathParam("controller") String controllerName,
989         @PathParam("topic") String topic) {
990
991         return catchArgStateGenericEx(() -> {
992             var drools = this.getDroolsController(controllerName);
993             return EventProtocolCoderConstants.getManager()
994                 .getDecoders(drools.getGroupId(), drools.getArtifactId(), topic);
995
996         }, e -> {
997             logger.debug(FETCH_DECODERS_BY_TOPIC_FAILED, this, controllerName, topic, e.getMessage(), e);
998             return (controllerName + ":" + topic);
999         });
1000     }
1001
1002     /**
1003      * GET.
1004      *
1005      * @return response object
1006      */
1007     @Override
1008     @GET
1009     @Path("engine/controllers/{controller}/decoders/{topic}/filters")
1010     public Response decoderFilter2(
1011         @PathParam("controller") String controllerName,
1012         @PathParam("topic") String topic) {
1013
1014         return catchArgStateGenericEx(() -> {
1015             var drools = this.getDroolsController(controllerName);
1016             final ProtocolCoderToolset decoder = EventProtocolCoderConstants.getManager()
1017                 .getDecoders(drools.getGroupId(), drools.getArtifactId(), topic);
1018             if (decoder == null) {
1019                 return Response.status(Response.Status.BAD_REQUEST).entity(new Error(topic + DOES_NOT_EXIST_MSG))
1020                     .build();
1021             } else {
1022                 return decoder.getCoders();
1023             }
1024
1025         }, e -> {
1026             logger.debug(FETCH_DECODERS_BY_TOPIC_FAILED, this, controllerName, topic, e.getMessage(), e);
1027             return (controllerName + ":" + topic);
1028         });
1029     }
1030
1031     /**
1032      * GET.
1033      *
1034      * @return response object
1035      */
1036     @Override
1037     @GET
1038     @Path("engine/controllers/{controller}/decoders/{topic}/filters/{factType}")
1039     public Response decoderFilter1(
1040         @PathParam("controller") String controllerName,
1041         @PathParam("topic") String topic,
1042         @PathParam("factType") String factClass) {
1043
1044         return catchArgStateGenericEx(() -> {
1045             var drools = this.getDroolsController(controllerName);
1046             final ProtocolCoderToolset decoder = EventProtocolCoderConstants.getManager()
1047                 .getDecoders(drools.getGroupId(), drools.getArtifactId(), topic);
1048             final CoderFilters filters = decoder.getCoder(factClass);
1049             if (filters == null) {
1050                 return Response.status(Response.Status.BAD_REQUEST)
1051                     .entity(new Error(topic + ":" + factClass + DOES_NOT_EXIST_MSG)).build();
1052             } else {
1053                 return filters;
1054             }
1055
1056         }, e -> {
1057             logger.debug(FETCH_DECODER_BY_TYPE_FAILED, this,
1058                 controllerName, topic, factClass, e.getMessage(), e);
1059             return (controllerName + ":" + topic + ":" + factClass);
1060         });
1061     }
1062
1063     /**
1064      * PUT.
1065      *
1066      * @return response object
1067      */
1068     @Override
1069     @PUT
1070     @Path("engine/controllers/{controller}/decoders/{topic}/filters/{factType}")
1071     public Response decoderFilter(
1072             JsonProtocolFilter configFilters,
1073             @PathParam("controller") String controllerName,
1074             @PathParam("topic") String topic,
1075             @PathParam("factType") String factClass) {
1076
1077         if (configFilters == null) {
1078             return Response.status(Response.Status.BAD_REQUEST).entity(new Error("Configuration Filters not provided"))
1079                 .build();
1080         }
1081
1082         return catchArgStateGenericEx(() -> {
1083             var drools = this.getDroolsController(controllerName);
1084             final ProtocolCoderToolset decoder = EventProtocolCoderConstants.getManager()
1085                 .getDecoders(drools.getGroupId(), drools.getArtifactId(), topic);
1086             final CoderFilters filters = decoder.getCoder(factClass);
1087             if (filters == null) {
1088                 return Response.status(Response.Status.BAD_REQUEST)
1089                     .entity(new Error(topic + ":" + factClass + DOES_NOT_EXIST_MSG)).build();
1090             }
1091             filters.setFilter(configFilters);
1092             return filters;
1093
1094         }, e -> {
1095             logger.debug(FETCH_DECODER_BY_FILTER_FAILED,
1096                 this, controllerName, topic, factClass, configFilters, e.getMessage(), e);
1097             return (controllerName + ":" + topic + ":" + factClass);
1098         });
1099     }
1100
1101     /**
1102      * GET.
1103      *
1104      * @return response object
1105      */
1106     @Override
1107     @GET
1108     @Path("engine/controllers/{controller}/decoders/{topic}/filters/{factType}/rule")
1109     public Response decoderFilterRules(
1110         @PathParam("controller") String controllerName,
1111         @PathParam("topic") String topic,
1112         @PathParam("factType") String factClass) {
1113
1114         return catchArgStateGenericEx(() -> {
1115             var drools = this.getDroolsController(controllerName);
1116             final ProtocolCoderToolset decoder = EventProtocolCoderConstants.getManager()
1117                 .getDecoders(drools.getGroupId(), drools.getArtifactId(), topic);
1118
1119             final CoderFilters filters = decoder.getCoder(factClass);
1120             if (filters == null) {
1121                 return Response.status(Response.Status.BAD_REQUEST)
1122                     .entity(new Error(controllerName + ":" + topic + ":" + factClass + DOES_NOT_EXIST_MSG)).build();
1123             }
1124
1125             final JsonProtocolFilter filter = filters.getFilter();
1126             if (filter == null) {
1127                 return Response.status(Response.Status.BAD_REQUEST)
1128                     .entity(new Error(controllerName + ":" + topic + ":" + factClass + NO_FILTERS)).build();
1129             }
1130
1131             return filter.getRule();
1132
1133         }, e -> {
1134             logger.debug(FETCH_DECODER_BY_TYPE_FAILED, this,
1135                 controllerName, topic, factClass, e.getMessage(), e);
1136             return (controllerName + ":" + topic + ":" + factClass);
1137         });
1138     }
1139
1140     /**
1141      * DELETE.
1142      *
1143      * @return response object
1144      */
1145     @Override
1146     @DELETE
1147     @Path("engine/controllers/{controller}/decoders/{topic}/filters/{factType}/rule")
1148     public Response decoderFilterRuleDelete(
1149         @PathParam("controller") String controllerName,
1150         @PathParam("topic") String topic,
1151         @PathParam("factType") String factClass) {
1152
1153         return catchArgStateGenericEx(() -> {
1154             var drools = this.getDroolsController(controllerName);
1155             final ProtocolCoderToolset decoder = EventProtocolCoderConstants.getManager()
1156                 .getDecoders(drools.getGroupId(), drools.getArtifactId(), topic);
1157
1158             final CoderFilters filters = decoder.getCoder(factClass);
1159             if (filters == null) {
1160                 return Response.status(Response.Status.BAD_REQUEST)
1161                     .entity(new Error(controllerName + ":" + topic + ":" + factClass + DOES_NOT_EXIST_MSG)).build();
1162             }
1163
1164             final JsonProtocolFilter filter = filters.getFilter();
1165             if (filter == null) {
1166                 return Response.status(Response.Status.BAD_REQUEST)
1167                     .entity(new Error(controllerName + ":" + topic + ":" + factClass + NO_FILTERS)).build();
1168             }
1169
1170             filter.setRule(null);
1171             return filter.getRule();
1172
1173         }, e -> {
1174             logger.debug(FETCH_DECODER_BY_TYPE_FAILED,
1175                 this, controllerName, topic, factClass, e.getMessage(), e);
1176             return (controllerName + ":" + topic + ":" + factClass);
1177         });
1178     }
1179
1180     /**
1181      * PUT.
1182      *
1183      * @return response object
1184      */
1185     @Override
1186     @PUT
1187     @Path("engine/controllers/{controller}/decoders/{topic}/filters/{factType}/rule")
1188     public Response decoderFilterRule(
1189         @PathParam("controller") String controllerName,
1190         @PathParam("topic") String topic,
1191         @PathParam("factType") String factClass,
1192         String rule) {
1193
1194         return catchArgStateGenericEx(() -> decoderFilterRule2(controllerName, topic, factClass, rule), e -> {
1195             logger.debug("{}: cannot access decoder filter rules for policy-controller {} "
1196                 + "topic {} type {} because of {}",
1197                 this, controllerName, topic, factClass, e.getMessage(), e);
1198             return (controllerName + ":" + topic + ":" + factClass);
1199         });
1200     }
1201
1202     private Object decoderFilterRule2(String controllerName, String topic, String factClass, String rule) {
1203         var drools = this.getDroolsController(controllerName);
1204         final ProtocolCoderToolset decoder = EventProtocolCoderConstants.getManager()
1205             .getDecoders(drools.getGroupId(), drools.getArtifactId(), topic);
1206
1207         final CoderFilters filters = decoder.getCoder(factClass);
1208         if (filters == null) {
1209             return Response.status(Response.Status.BAD_REQUEST)
1210                 .entity(new Error(controllerName + ":" + topic + ":" + factClass + DOES_NOT_EXIST_MSG)).build();
1211         }
1212
1213         final JsonProtocolFilter filter = filters.getFilter();
1214         if (filter == null) {
1215             return Response.status(Response.Status.BAD_REQUEST)
1216                 .entity(new Error(controllerName + ":" + topic + ":" + factClass + NO_FILTERS)).build();
1217         }
1218
1219         if (rule == null || rule.isEmpty()) {
1220             return Response.status(Response.Status.BAD_REQUEST).entity(new Error(controllerName + ":" + topic + ":"
1221                 + factClass + " no filter rule provided")).build();
1222         }
1223
1224         filter.setRule(rule);
1225         return filter.getRule();
1226     }
1227
1228     /**
1229      * POST.
1230      *
1231      * @return response object
1232      */
1233     @Override
1234     @POST
1235     @Path("engine/controllers/{controller}/decoders/{topic}")
1236     @Consumes(MediaType.TEXT_PLAIN)
1237     public Response decode(
1238         @PathParam("controller") String controllerName,
1239         @PathParam("topic") String topic,
1240         String json) {
1241
1242         if (!checkValidNameInput(controllerName)) {
1243             return Response.status(Response.Status.NOT_ACCEPTABLE)
1244                 .entity(new Error("controllerName contains whitespaces " + NOT_ACCEPTABLE_MSG)).build();
1245         }
1246
1247         if (!checkValidNameInput(topic)) {
1248             return Response.status(Response.Status.NOT_ACCEPTABLE)
1249                 .entity(new Error("topic contains whitespaces " + NOT_ACCEPTABLE_MSG)).build();
1250         }
1251
1252         PolicyController policyController;
1253         try {
1254             policyController = PolicyControllerConstants.getFactory().get(controllerName);
1255         } catch (final IllegalArgumentException e) {
1256             logger.debug(FETCH_DECODERS_BY_TOPIC_FAILED, this,
1257                 controllerName, topic, e.getMessage(), e);
1258             return Response.status(Response.Status.NOT_FOUND)
1259                 .entity(new Error(controllerName + ":" + topic + ":" + NOT_FOUND_MSG)).build();
1260         } catch (final IllegalStateException e) {
1261             logger.debug(FETCH_DECODERS_BY_TOPIC_FAILED, this,
1262                 controllerName, topic, e.getMessage(), e);
1263             return Response.status(Response.Status.NOT_ACCEPTABLE)
1264                 .entity(new Error(controllerName + ":" + topic + ":" + NOT_ACCEPTABLE_MSG)).build();
1265         }
1266
1267         var result = new CodingResult();
1268         result.setDecoding(false);
1269         result.setEncoding(false);
1270         result.setJsonEncoding(null);
1271
1272         Object event;
1273         try {
1274             event = EventProtocolCoderConstants.getManager().decode(policyController.getDrools().getGroupId(),
1275                 policyController.getDrools().getArtifactId(), topic, json);
1276             result.setDecoding(true);
1277         } catch (final Exception e) {
1278             logger.debug(FETCH_POLICY_BY_TOPIC_FAILED, this, controllerName, topic,
1279                 e.getMessage(), e);
1280             return Response.status(Response.Status.BAD_REQUEST).entity(new Error(e.getMessage())).build();
1281         }
1282
1283         try {
1284             result.setJsonEncoding(EventProtocolCoderConstants.getManager().encode(topic, event));
1285             result.setEncoding(true);
1286         } catch (final Exception e) {
1287             // continue so to propagate decoding results ..
1288             logger.debug("{}: cannot encode for policy-controller {} topic {} because of {}", this, controllerName,
1289                 topic, e.getMessage(), e);
1290         }
1291
1292         return Response.status(Response.Status.OK).entity(result).build();
1293     }
1294
1295     /**
1296      * GET.
1297      *
1298      * @return response object
1299      */
1300     @Override
1301     @GET
1302     @Path("engine/controllers/{controller}/encoders")
1303     public Response encoderFilters(@PathParam("controller") String controllerName) {
1304
1305         return catchArgStateGenericEx(() -> {
1306             final PolicyController controller = PolicyControllerConstants.getFactory().get(controllerName);
1307             var drools = controller.getDrools();
1308             return EventProtocolCoderConstants.getManager()
1309                 .getEncoderFilters(drools.getGroupId(), drools.getArtifactId());
1310
1311         }, e -> {
1312             logger.debug(FETCH_ENCODER_BY_FILTER_FAILED, this, controllerName,
1313                 e.getMessage(), e);
1314             return (controllerName);
1315         });
1316     }
1317
1318     @Override
1319     @GET
1320     @Path("engine/topics")
1321     public Response topics() {
1322         return Response.status(Response.Status.OK).entity(TopicEndpointManager.getManager()).build();
1323     }
1324
1325     @Override
1326     @GET
1327     @Path("engine/topics/switches")
1328     public Response topicSwitches() {
1329         return Response.status(Response.Status.OK).entity(SWITCHES).build();
1330     }
1331
1332     /**
1333      * PUT.
1334      *
1335      * @return response object
1336      */
1337     @Override
1338     @PUT
1339     @Path("engine/topics/switches/lock")
1340     public Response topicsLock() {
1341         final boolean success = TopicEndpointManager.getManager().lock();
1342         if (success) {
1343             return Response.status(Status.OK).entity(TopicEndpointManager.getManager()).build();
1344         } else {
1345             return Response.status(Status.NOT_ACCEPTABLE).entity(new Error(CANNOT_PERFORM_OPERATION)).build();
1346         }
1347     }
1348
1349     /**
1350      * DELETE.
1351      *
1352      * @return response object
1353      */
1354     @Override
1355     @DELETE
1356     @Path("engine/topics/switches/lock")
1357     public Response topicsUnlock() {
1358         final boolean success = TopicEndpointManager.getManager().unlock();
1359         if (success) {
1360             return Response.status(Status.OK).entity(TopicEndpointManager.getManager()).build();
1361         } else {
1362             return Response.status(Status.NOT_ACCEPTABLE).entity(new Error(CANNOT_PERFORM_OPERATION)).build();
1363         }
1364     }
1365
1366     /**
1367      * GET.
1368      *
1369      * @return response object
1370      */
1371     @Override
1372     @GET
1373     @Path("engine/topics/sources")
1374     public Response sources() {
1375         return Response.status(Response.Status.OK).entity(TopicEndpointManager.getManager().getTopicSources()).build();
1376     }
1377
1378     /**
1379      * GET.
1380      *
1381      * @return response object
1382      */
1383     @Override
1384     @GET
1385     @Path("engine/topics/sinks")
1386     public Response sinks() {
1387         return Response.status(Response.Status.OK).entity(TopicEndpointManager.getManager().getTopicSinks()).build();
1388     }
1389
1390     /**
1391      * GET sources of a communication type.
1392      */
1393     @Override
1394     @GET
1395     @Path("engine/topics/sources/{comm: ueb|kafka|noop}")
1396     public Response commSources(
1397         @PathParam("comm") String comm) {
1398         if (!checkValidNameInput(comm)) {
1399             return Response
1400                 .status(Response.Status.NOT_ACCEPTABLE)
1401                 .entity(new Error("source communication mechanism contains whitespaces " + NOT_ACCEPTABLE_MSG))
1402                 .build();
1403         }
1404
1405         List<TopicSource> sources = new ArrayList<>();
1406         var status = Status.OK;
1407         switch (CommInfrastructure.valueOf(comm.toUpperCase())) {
1408             case UEB:
1409                 sources.addAll(TopicEndpointManager.getManager().getUebTopicSources());
1410                 break;
1411             case NOOP:
1412                 sources.addAll(TopicEndpointManager.getManager().getNoopTopicSources());
1413                 break;
1414             case KAFKA:
1415                 sources.addAll(TopicEndpointManager.getManager().getKafkaTopicSources());
1416                 break;
1417             default:
1418                 status = Status.BAD_REQUEST;
1419                 logger.debug("Invalid communication mechanism");
1420                 break;
1421         }
1422         return Response.status(status).entity(sources).build();
1423     }
1424
1425     /**
1426      * GET sinks of a communication type.
1427      */
1428     @Override
1429     @GET
1430     @Path("engine/topics/sinks/{comm: ueb|kafka|noop}")
1431     public Response commSinks(
1432         @PathParam("comm") String comm) {
1433         if (!checkValidNameInput(comm)) {
1434             return Response
1435                 .status(Response.Status.NOT_ACCEPTABLE)
1436                 .entity(new Error("sink communication mechanism contains whitespaces " + NOT_ACCEPTABLE_MSG))
1437                 .build();
1438         }
1439
1440         List<TopicSink> sinks = new ArrayList<>();
1441         var status = Status.OK;
1442         switch (CommInfrastructure.valueOf(comm.toUpperCase())) {
1443             case UEB:
1444                 sinks.addAll(TopicEndpointManager.getManager().getUebTopicSinks());
1445                 break;
1446             case NOOP:
1447                 sinks.addAll(TopicEndpointManager.getManager().getNoopTopicSinks());
1448                 break;
1449             case KAFKA:
1450                 sinks.addAll(TopicEndpointManager.getManager().getKafkaTopicSinks());
1451                 break;
1452             default:
1453                 status = Status.BAD_REQUEST;
1454                 logger.debug("Invalid communication mechanism");
1455                 break;
1456         }
1457         return Response.status(status).entity(sinks).build();
1458     }
1459
1460     /**
1461      * GET a source.
1462      */
1463     @Override
1464     @GET
1465     @Path("engine/topics/sources/{comm: ueb|kafka|noop}/{topic}")
1466     public Response sourceTopic(
1467         @PathParam("comm") String comm,
1468         @PathParam("topic") String topic) {
1469         return Response
1470             .status(Response.Status.OK)
1471             .entity(TopicEndpointManager.getManager()
1472                 .getTopicSource(CommInfrastructure.valueOf(comm.toUpperCase()), topic))
1473             .build();
1474     }
1475
1476     /**
1477      * GET a sink.
1478      */
1479     @Override
1480     @GET
1481     @Path("engine/topics/sinks/{comm: ueb|kafka|noop}/{topic}")
1482     public Response sinkTopic(
1483         @PathParam("comm") String comm,
1484         @PathParam("topic") String topic) {
1485         return Response
1486             .status(Response.Status.OK)
1487             .entity(TopicEndpointManager.getManager()
1488                 .getTopicSink(CommInfrastructure.valueOf(comm.toUpperCase()), topic))
1489             .build();
1490     }
1491
1492     /**
1493      * GET a source events.
1494      */
1495     @Override
1496     @GET
1497     @Path("engine/topics/sources/{comm: ueb|kafka|noop}/{topic}/events")
1498     public Response sourceEvents(
1499         @PathParam("comm") String comm,
1500         @PathParam("topic") String topic) {
1501         return Response.status(Status.OK)
1502             .entity(Arrays.asList(TopicEndpointManager.getManager()
1503                 .getTopicSource(CommInfrastructure.valueOf(comm.toUpperCase()), topic)
1504                 .getRecentEvents()))
1505             .build();
1506     }
1507
1508     /**
1509      * GET a sink events.
1510      */
1511     @Override
1512     @GET
1513     @Path("engine/topics/sinks/{comm: ueb|kafka|noop}/{topic}/events")
1514     public Response sinkEvents(
1515         @PathParam("comm") String comm,
1516         @PathParam("topic") String topic) {
1517         return Response.status(Status.OK)
1518             .entity(Arrays.asList(TopicEndpointManager.getManager()
1519                 .getTopicSink(CommInfrastructure.valueOf(comm.toUpperCase()), topic)
1520                 .getRecentEvents()))
1521             .build();
1522     }
1523
1524     /**
1525      * GET source topic switches.
1526      */
1527     @Override
1528     @GET
1529     @Path("engine/topics/sources/{comm: ueb|kafka|noop}/{topic}/switches")
1530     public Response commSourceTopicSwitches(
1531         @PathParam("comm") String comm,
1532         @PathParam("topic") String topic) {
1533         return Response.status(Response.Status.OK).entity(SWITCHES).build();
1534     }
1535
1536     /**
1537      * GET sink topic switches.
1538      */
1539     @Override
1540     @GET
1541     @Path("engine/topics/sinks/{comm: ueb|kafka|noop}/{topic}/switches")
1542     public Response commSinkTopicSwitches(
1543         @PathParam("comm") String comm,
1544         @PathParam("topic") String topic) {
1545         return Response.status(Response.Status.OK).entity(SWITCHES).build();
1546     }
1547
1548     /**
1549      * PUTs a lock on a topic.
1550      */
1551     @Override
1552     @PUT
1553     @Path("engine/topics/sources/{comm: ueb|kafka|noop}/{topic}/switches/lock")
1554     public Response commSourceTopicLock(
1555         @PathParam("comm") String comm,
1556         @PathParam("topic") String topic) {
1557         var source =
1558             TopicEndpointManager.getManager().getTopicSource(CommInfrastructure.valueOf(comm.toUpperCase()), topic);
1559         return getResponse(topic, source.lock(), source);
1560     }
1561
1562     /**
1563      * DELETEs the lock on a topic.
1564      */
1565     @Override
1566     @DELETE
1567     @Path("engine/topics/sources/{comm: ueb|kafka|noop}/{topic}/switches/lock")
1568     public Response commSourceTopicUnlock(
1569         @PathParam("comm") String comm,
1570         @PathParam("topic") String topic) {
1571         var source =
1572             TopicEndpointManager.getManager().getTopicSource(CommInfrastructure.valueOf(comm.toUpperCase()), topic);
1573         return getResponse(topic, source.unlock(), source);
1574     }
1575
1576     /**
1577      * Starts a topic source.
1578      */
1579     @Override
1580     @PUT
1581     @Path("engine/topics/sources/{comm: ueb|kafka|noop}/{topic}/switches/activation")
1582     public Response commSourceTopicActivation(
1583         @PathParam("comm") String comm,
1584         @PathParam("topic") String topic) {
1585         var source =
1586             TopicEndpointManager.getManager().getTopicSource(CommInfrastructure.valueOf(comm.toUpperCase()), topic);
1587         return getResponse(topic, source.start(), source);
1588     }
1589
1590     /**
1591      * Stops a topic source.
1592      */
1593     @Override
1594     @DELETE
1595     @Path("engine/topics/sources/{comm: ueb|kafka|noop}/{topic}/switches/activation")
1596     public Response commSourceTopicDeactivation(
1597         @PathParam("comm") String comm,
1598         @PathParam("topic") String topic) {
1599         var source =
1600             TopicEndpointManager.getManager().getTopicSource(CommInfrastructure.valueOf(comm.toUpperCase()), topic);
1601         return getResponse(topic, source.stop(), source);
1602     }
1603
1604     /**
1605      * PUTs a lock on a topic.
1606      */
1607     @Override
1608     @PUT
1609     @Path("engine/topics/sinks/{comm: ueb|kafka|noop}/{topic}/switches/lock")
1610     public Response commSinkTopicLock(
1611         @PathParam("comm") String comm,
1612         @PathParam("topic") String topic) {
1613         var sink =
1614             TopicEndpointManager.getManager().getTopicSink(CommInfrastructure.valueOf(comm.toUpperCase()), topic);
1615         return getResponse(topic, sink.lock(), sink);
1616     }
1617
1618     /**
1619      * DELETEs the lock on a topic.
1620      */
1621     @Override
1622     @DELETE
1623     @Path("engine/topics/sinks/{comm: ueb|kafka|noop}/{topic}/switches/lock")
1624     public Response commSinkTopicUnlock(
1625         @PathParam("comm") String comm,
1626         @PathParam("topic") String topic) {
1627         var sink =
1628             TopicEndpointManager.getManager().getTopicSink(CommInfrastructure.valueOf(comm.toUpperCase()), topic);
1629         return getResponse(topic, sink.unlock(), sink);
1630     }
1631
1632     /**
1633      * Starts a topic sink.
1634      */
1635     @Override
1636     @PUT
1637     @Path("engine/topics/sinks/{comm: ueb|kafka|noop}/{topic}/switches/activation")
1638     public Response commSinkTopicActivation(
1639         @PathParam("comm") String comm,
1640         @PathParam("topic") String topic) {
1641         var sink =
1642             TopicEndpointManager.getManager().getTopicSink(CommInfrastructure.valueOf(comm.toUpperCase()), topic);
1643         return getResponse(topic, sink.start(), sink);
1644     }
1645
1646     /**
1647      * Stops a topic sink.
1648      */
1649     @Override
1650     @DELETE
1651     @Path("engine/topics/sinks/{comm: ueb|kafka|noop}/{topic}/switches/activation")
1652     public Response commSinkTopicDeactivation(
1653         @PathParam("comm") String comm,
1654         @PathParam("topic") String topic) {
1655         var sink =
1656             TopicEndpointManager.getManager().getTopicSink(CommInfrastructure.valueOf(comm.toUpperCase()), topic);
1657         return getResponse(topic, sink.stop(), sink);
1658     }
1659
1660     private Response getResponse(String topicName, boolean success, Topic topic) {
1661         if (success) {
1662             return Response.status(Status.OK).entity(topic).build();
1663         } else {
1664             return Response.status(Status.NOT_ACCEPTABLE).entity(makeTopicOperError(topicName)).build();
1665         }
1666     }
1667
1668     private Error makeTopicOperError(String topic) {
1669         return new Error("cannot perform operation on " + topic);
1670     }
1671
1672     /**
1673      * Offers an event to a topic in a communication infrastructure.
1674      *
1675      * @return response object
1676      */
1677     @Override
1678     @PUT
1679     @Path("engine/topics/sources/{comm: ueb|kafka|noop}/{topic}/events")
1680     @Consumes(MediaType.TEXT_PLAIN)
1681     public Response commEventOffer(
1682         @PathParam("comm") String comm,
1683         @PathParam("topic") String topic,
1684         String json) {
1685
1686         return catchArgStateGenericEx(() -> {
1687             var source = TopicEndpointManager.getManager()
1688                 .getTopicSource(CommInfrastructure.valueOf(comm.toUpperCase()), topic);
1689             if (source.offer(json)) {
1690                 return Arrays.asList(source.getRecentEvents());
1691             } else {
1692                 return Response.status(Status.NOT_ACCEPTABLE).entity(new Error("Failure to inject event over " + topic))
1693                     .build();
1694             }
1695
1696         }, e -> {
1697             logger.debug(OFFER_FAILED, this, topic, e.getMessage(), e);
1698             return (topic);
1699         });
1700     }
1701
1702     /**
1703      * GET.
1704      *
1705      * @return response object
1706      */
1707     @Override
1708     @GET
1709     @Path("engine/tools/uuid")
1710     @Produces(MediaType.TEXT_PLAIN)
1711     public Response uuid() {
1712         return Response.status(Status.OK).entity(UUID.randomUUID().toString()).build();
1713     }
1714
1715     /**
1716      * GET.
1717      *
1718      * @return response object
1719      */
1720     @Override
1721     @GET
1722     @Path("engine/tools/loggers")
1723     public Response loggers() {
1724         final List<String> names = new ArrayList<>();
1725         if (!(LoggerFactory.getILoggerFactory() instanceof LoggerContext context)) {
1726             logger.warn("The SLF4J logger factory is not configured for logback");
1727             return Response.status(Status.INTERNAL_SERVER_ERROR).entity(names).build();
1728         }
1729
1730         for (final Logger lgr : context.getLoggerList()) {
1731             names.add(lgr.getName());
1732         }
1733
1734         return Response.status(Status.OK).entity(names).build();
1735     }
1736
1737     /**
1738      * GET.
1739      *
1740      * @return response object
1741      */
1742     @Override
1743     @GET
1744     @Path("engine/tools/loggers/{logger}")
1745     @Produces(MediaType.TEXT_PLAIN)
1746     public Response loggerName1(@PathParam("logger") String loggerName) {
1747         if (!(LoggerFactory.getILoggerFactory() instanceof LoggerContext context)) {
1748             logger.warn("The SLF4J logger factory is not configured for logback");
1749             return Response.status(Status.INTERNAL_SERVER_ERROR).build();
1750         }
1751
1752         var lgr = context.getLogger(loggerName);
1753         if (lgr == null) {
1754             return Response.status(Status.NOT_FOUND).build();
1755         }
1756
1757         final String loggerLevel = (lgr.getLevel() != null) ? lgr.getLevel().toString() : "";
1758         return Response.status(Status.OK).entity(loggerLevel).build();
1759     }
1760
1761     /**
1762      * PUT.
1763      *
1764      * @return response object
1765      */
1766     @Override
1767     @PUT
1768     @Path("engine/tools/loggers/{logger}/{level}")
1769     @Produces(MediaType.TEXT_PLAIN)
1770     @Consumes(MediaType.TEXT_PLAIN)
1771     public Response loggerName(@PathParam("logger") String loggerName, @PathParam("level") String loggerLevel) {
1772
1773         String newLevel;
1774         try {
1775             if (!checkValidNameInput(loggerName)) {
1776                 return Response.status(Response.Status.NOT_ACCEPTABLE)
1777                     .entity(new Error("logger name: " + NOT_ACCEPTABLE_MSG))
1778                     .build();
1779             }
1780             if (!Pattern.matches("^[a-zA-Z]{3,5}$", loggerLevel)) {
1781                 return Response.status(Response.Status.NOT_ACCEPTABLE)
1782                     .entity(new Error("logger level: " + NOT_ACCEPTABLE_MSG))
1783                     .build();
1784             }
1785             newLevel = LoggerUtils.setLevel(loggerName, loggerLevel);
1786         } catch (final IllegalArgumentException e) {
1787             logger.warn("{}: invalid operation for logger {} and level {}", this, loggerName, loggerLevel, e);
1788             return Response.status(Status.NOT_FOUND).build();
1789         } catch (final IllegalStateException e) {
1790             logger.warn("{}: logging framework unavailable for {} / {}", this, loggerName, loggerLevel, e);
1791             return Response.status(Status.INTERNAL_SERVER_ERROR).build();
1792         }
1793
1794         return Response.status(Status.OK).entity(newLevel
1795
1796         ).build();
1797     }
1798
1799     /**
1800      * gets the underlying drools controller from the named policy controller.
1801      *
1802      * @param controllerName the policy controller name
1803      * @return the underlying drools controller
1804      * @throws IllegalArgumentException if an invalid controller name has been passed in
1805      */
1806     protected DroolsController getDroolsController(String controllerName) {
1807         final PolicyController controller = PolicyControllerConstants.getFactory().get(controllerName);
1808         if (controller == null) {
1809             throw new IllegalArgumentException(controllerName + DOES_NOT_EXIST_MSG);
1810         }
1811
1812         var drools = controller.getDrools();
1813         if (drools == null) {
1814             throw new IllegalArgumentException(controllerName + " has no drools configuration");
1815         }
1816
1817         return drools;
1818     }
1819
1820     /**
1821      * Invokes a function and returns the generated response, catching illegal argument,
1822      * illegal state, and generic runtime exceptions.
1823      *
1824      * @param responder function that will generate a response. If it returns a "Response"
1825      *        object, then that object is returned as-is. Otherwise, this method will
1826      *        return an "OK" Response, using the function's return value as the "entity"
1827      * @param errorMsg function that will generate an error message prefix to be included
1828      *        in responses generated as a result of catching an exception
1829      * @return a response
1830      */
1831     private Response catchArgStateGenericEx(Supplier<Object> responder, Function<Exception, String> errorMsg) {
1832         try {
1833             Object result = responder.get();
1834             if (result instanceof Response) {
1835                 return (Response) result;
1836             }
1837
1838             return Response.status(Response.Status.OK).entity(result).build();
1839
1840         } catch (final IllegalArgumentException e) {
1841             return Response.status(Response.Status.NOT_FOUND).entity(new Error(errorMsg.apply(e) + NOT_FOUND_MSG))
1842                 .build();
1843
1844         } catch (final IllegalStateException e) {
1845             return Response.status(Response.Status.NOT_ACCEPTABLE)
1846                 .entity(new Error(errorMsg.apply(e) + NOT_ACCEPTABLE_MSG)).build();
1847
1848         } catch (final RuntimeException e) {
1849             errorMsg.apply(e);
1850             return Response.status(Response.Status.INTERNAL_SERVER_ERROR).entity(new Error(e.getMessage())).build();
1851         }
1852     }
1853
1854     public static boolean checkValidNameInput(String test) {
1855         return Pattern.matches("\\S+", test);
1856     }
1857
1858     /*
1859      * Helper classes for aggregation of results
1860      */
1861
1862     /**
1863      * Coding/Encoding Results Aggregation Helper class.
1864      */
1865     @Getter
1866     @Setter
1867     public static class CodingResult {
1868         /**
1869          * serialized output.
1870          */
1871
1872         private String jsonEncoding;
1873         /**
1874          * encoding result.
1875          */
1876
1877         private Boolean encoding;
1878
1879         /**
1880          * decoding result.
1881          */
1882         private Boolean decoding;
1883     }
1884
1885     /**
1886      * Generic Error Reporting class.
1887      */
1888     @AllArgsConstructor
1889     public static class Error {
1890         private String msg;
1891
1892         public String getError() {
1893             return msg;
1894         }
1895
1896         public void setError(String msg) {
1897             this.msg = msg;
1898         }
1899     }
1900 }