1 package org.openecomp.policy.drools.protocol.coders;
3 import java.lang.reflect.Field;
4 import java.lang.reflect.InvocationTargetException;
5 import java.lang.reflect.Method;
6 import java.lang.reflect.Type;
7 import java.time.Instant;
8 import java.time.ZonedDateTime;
9 import java.time.format.DateTimeFormatter;
10 import java.util.ArrayList;
11 import java.util.Iterator;
12 import java.util.List;
14 import org.openecomp.policy.common.logging.eelf.MessageCodes;
15 import org.openecomp.policy.common.logging.flexlogger.FlexLogger;
16 import org.openecomp.policy.common.logging.flexlogger.Logger;
17 import org.openecomp.policy.drools.controller.DroolsController;
18 import org.openecomp.policy.drools.protocol.coders.EventProtocolCoder.CoderFilters;
19 import org.openecomp.policy.drools.protocol.coders.TopicCoderFilterConfiguration.CustomCoder;
20 import org.openecomp.policy.drools.protocol.coders.TopicCoderFilterConfiguration.CustomGsonCoder;
21 import org.openecomp.policy.drools.protocol.coders.TopicCoderFilterConfiguration.CustomJacksonCoder;
23 import com.fasterxml.jackson.annotation.JsonIgnore;
24 import com.fasterxml.jackson.core.JsonProcessingException;
25 import com.fasterxml.jackson.databind.DeserializationFeature;
26 import com.fasterxml.jackson.databind.ObjectMapper;
27 import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;
28 import com.google.gson.Gson;
29 import com.google.gson.GsonBuilder;
30 import com.google.gson.JsonDeserializationContext;
31 import com.google.gson.JsonDeserializer;
32 import com.google.gson.JsonElement;
33 import com.google.gson.JsonParseException;
34 import com.google.gson.JsonParser;
35 import com.google.gson.JsonPrimitive;
36 import com.google.gson.JsonSerializationContext;
37 import com.google.gson.JsonSerializer;
40 * Protocol Coding/Decoding Toolset
42 public abstract class ProtocolCoderToolset {
47 protected final String topic;
52 protected final String controllerId;
57 protected final String groupId;
62 protected final String artifactId;
65 * Protocols and associated Filters
67 protected final List<CoderFilters> coders = new ArrayList<CoderFilters>();
70 * Tree model (instead of class model) generic parsing to be able to inspect elements
72 protected JsonParser filteringParser = new JsonParser();
77 protected CustomCoder customCoder;
82 * @param topic the topic
83 * @param controllerId the controller id
84 * @param codedClass the decoded class
85 * @param filters list of filters that apply to the
86 * selection of this decodedClass in case of multiplicity
87 * @throws IllegalArgumentException if invalid data has been passed in
89 public ProtocolCoderToolset(String topic,
94 JsonProtocolFilter filters,
95 CustomCoder customCoder,
96 int modelClassLoaderHash)
97 throws IllegalArgumentException {
99 if (topic == null || controllerId == null ||
100 groupId == null || artifactId == null ||
101 codedClass == null || filters == null ||
102 topic.isEmpty() || controllerId.isEmpty()) {
104 throw new IllegalArgumentException("Invalid input");
108 this.controllerId = controllerId;
109 this.groupId = groupId;
110 this.artifactId = artifactId;
111 this.coders.add(new CoderFilters(codedClass, filters, modelClassLoaderHash));
112 this.customCoder = customCoder;
116 * gets the coder + filters associated with this class name
118 * @param classname class name
119 * @return the decoder filters or null if not found
121 public CoderFilters getCoder(String classname) {
122 for (CoderFilters decoder: this.coders) {
123 if (decoder.factClass.equals(classname)) {
131 * get all coder filters in use
133 * @return coder filters
135 public List<CoderFilters> getCoders() {
140 * add coder or replace it exists
142 * @param eventClass decoder
143 * @param filter filter
145 public void addCoder(String eventClass, JsonProtocolFilter filter, int modelClassLoaderHash) {
147 for (CoderFilters coder: this.coders) {
148 if (coder.factClass.equals(eventClass)) {
149 // this is a better check than checking pointers, just
150 // in case classloader is different and this is just an update
151 coder.factClass = eventClass;
152 coder.filter = filter;
153 coder.modelClassLoaderHash = modelClassLoaderHash;
159 this.coders.add(new CoderFilters(eventClass, filter, modelClassLoaderHash));
165 * @param eventClass decoder
166 * @param filter filter
168 public void removeCoders(String eventClass) {
170 Iterator<CoderFilters> codersIt = this.coders.iterator();
171 while (codersIt.hasNext()) {
172 CoderFilters coder = codersIt.next();
173 if (coder.factClass.equals(eventClass)) {
185 public String getTopic() {return topic;}
188 * gets the controller id
190 * @return the controller id
192 public String getControllerId() {return controllerId;}
195 * @return the groupId
197 public String getGroupId() {
202 * @return the artifactId
204 public String getArtifactId() {
209 * @return the customCoder
211 public CustomCoder getCustomCoder() {
216 * @param customCoder the customCoder to set
218 public void setCustomCoder(CustomCoder customCoder) {
219 this.customCoder = customCoder;
223 * performs filtering on a json string
225 * @param json json string
226 * @return the decoder that passes the filter, otherwise null
227 * @throws UnsupportedOperationException can't filter
228 * @throws IllegalArgumentException invalid input
230 protected CoderFilters filter(String json)
231 throws UnsupportedOperationException, IllegalArgumentException, IllegalStateException {
234 // 1. Get list of decoding classes for this controller Id and topic
235 // 2. If there are no classes, return error
236 // 3. Otherwise, from the available classes for decoding, pick the first one that
237 // passes the filters
239 // Don't parse if it is not necessary
241 if (this.coders.isEmpty()) {
242 // TODO this is an error
243 throw new IllegalStateException("No coders available");
246 if (this.coders.size() == 1) {
247 JsonProtocolFilter filter = this.coders.get(0).getFilter();
248 if (!filter.isRules()) {
249 return this.coders.get(0);
255 event = this.filteringParser.parse(json);
256 } catch (Exception e) {
257 // TODO Auto-generated catch block
259 throw new UnsupportedOperationException(e);
262 for (CoderFilters decoder: this.coders) {
264 boolean accepted = decoder.getFilter().accept(event);
268 } catch (Exception e) {
269 // TODO: handle exception
278 * Decode json into a POJO object
279 * @param json json string
281 * @return a POJO object for the json string
282 * @throws IllegalArgumentException if an invalid parameter has been received
283 * @throws UnsupportedOperationException if parsing into POJO is not possible
285 public abstract Object decode(String json)
286 throws IllegalArgumentException, UnsupportedOperationException, IllegalStateException;
289 * Encodes a POJO object into a JSON String
291 * @param event JSON POJO event to be converted to String
292 * @return JSON string version of POJO object
293 * @throws IllegalArgumentException if an invalid parameter has been received
294 * @throws UnsupportedOperationException if parsing into POJO is not possible
296 public abstract String encode(Object event)
297 throws IllegalArgumentException, UnsupportedOperationException;
300 public String toString() {
301 StringBuilder builder = new StringBuilder();
302 builder.append("ProtocolCoderToolset [topic=").append(topic).append(", controllerId=").append(controllerId)
303 .append(", groupId=").append(groupId).append(", artifactId=").append(artifactId).append(", coders=")
304 .append(coders).append(", filteringParser=").append(filteringParser).append(", customCoder=")
305 .append(customCoder).append("]");
306 return builder.toString();
311 * Tools used for encoding/decoding using Jackson
313 class JacksonProtocolCoderToolset extends ProtocolCoderToolset {
314 private static Logger logger = FlexLogger.getLogger(JacksonProtocolCoderToolset.class);
319 protected final ObjectMapper decoder = new ObjectMapper();
325 protected final ObjectMapper encoder = new ObjectMapper();
328 * Toolset to encode/decode tools associated with a topic
331 * @param decodedClass decoded class of an event
334 public JacksonProtocolCoderToolset(String topic, String controllerId,
335 String groupId, String artifactId,
337 JsonProtocolFilter filter,
338 CustomJacksonCoder customJacksonCoder,
339 int modelClassLoaderHash) {
340 super(topic, controllerId, groupId, artifactId, decodedClass, filter, customJacksonCoder, modelClassLoaderHash);
341 decoder.registerModule(new JavaTimeModule());
342 decoder.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES,
347 * gets the Jackson decoder
349 * @return the Jackson decoder
352 protected ObjectMapper getDecoder() {return decoder;}
355 * gets the Jackson encoder
357 * @return the Jackson encoder
360 protected ObjectMapper getEncoder() {return encoder;}
366 public Object decode(String json)
367 throws IllegalArgumentException, UnsupportedOperationException, IllegalStateException {
369 // 0. Use custom coder if available
371 if (this.customCoder != null) {
372 throw new UnsupportedOperationException
373 ("Jackon Custom Decoder is not supported at this time");
376 DroolsController droolsController =
377 DroolsController.factory.get(groupId, artifactId, "");
378 if (droolsController == null) {
379 String error = "NO-DROOLS-CONTROLLER for: " + json + " IN " + this;
381 throw new IllegalStateException(error);
384 CoderFilters decoderFilter = filter(json);
385 if (decoderFilter == null) {
386 String error = "NO-DECODER for: " + json + " IN " + this;
388 throw new UnsupportedOperationException(error);
391 Class<?> decoderClass;
394 droolsController.fetchModelClass(decoderFilter.getCodedClass());
395 if (decoderClass == null) {
396 String error = "DECODE-ERROR FETCHING MODEL CLASS: " + ":" + json + ":" + this;
398 throw new IllegalStateException(error);
400 } catch (Exception e) {
401 String error = "DECODE-ERROR FETCHING MODEL CLASS: "+ e.getMessage() + ":" + json + ":" + this;
402 logger.warn(MessageCodes.EXCEPTION_ERROR, e, error);
403 throw new UnsupportedOperationException(error, e);
408 Object fact = this.decoder.readValue(json, decoderClass);
410 } catch (Exception e) {
411 String error = "DECODE-ERROR FROM PDP-D FRAMEWORK: "+ json + ":" + e.getMessage() + ":" + this;
412 logger.error(MessageCodes.EXCEPTION_ERROR, e, error);
413 throw new UnsupportedOperationException(error, e);
421 public String encode(Object event)
422 throws IllegalArgumentException, UnsupportedOperationException {
424 // 0. Use custom coder if available
426 if (this.customCoder != null) {
427 throw new UnsupportedOperationException
428 ("Jackon Custom Encoder is not supported at this time");
432 String encodedEvent = this.encoder.writeValueAsString(event);
434 } catch (JsonProcessingException e) {
435 String error = "ENCODE-ERROR: "+ event + " IN " + this;
436 logger.error(MessageCodes.EXCEPTION_ERROR, e, error);
437 throw new UnsupportedOperationException(error, e);
442 public String toString() {
443 StringBuilder builder = new StringBuilder();
444 builder.append("JacksonProtocolCoderToolset [toString()=").append(super.toString()).append("]");
445 return builder.toString();
451 * Tools used for encoding/decoding using Jackson
453 class GsonProtocolCoderToolset extends ProtocolCoderToolset {
455 private static Logger logger = FlexLogger.getLogger(GsonProtocolCoderToolset.class);
457 * Formatter for JSON encoding/decoding
460 public static DateTimeFormatter format = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss.SSSSSSxxx");
463 public static DateTimeFormatter zuluFormat = DateTimeFormatter.ISO_INSTANT;
466 * Adapter for ZonedDateTime
469 public static class GsonUTCAdapter implements JsonSerializer<ZonedDateTime>, JsonDeserializer<ZonedDateTime> {
471 public ZonedDateTime deserialize(JsonElement element, Type type, JsonDeserializationContext context)
472 throws JsonParseException {
474 return ZonedDateTime.parse(element.getAsString(), format);
475 } catch (Exception e) {
476 System.err.println(e);
481 public JsonElement serialize(ZonedDateTime datetime, Type type, JsonSerializationContext context) {
482 return new JsonPrimitive(datetime.format(format));
486 public static class GsonInstantAdapter implements JsonSerializer<Instant>, JsonDeserializer<Instant> {
489 public Instant deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context)
490 throws JsonParseException {
491 return Instant.ofEpochMilli(json.getAsLong());
495 public JsonElement serialize(Instant src, Type typeOfSrc, JsonSerializationContext context) {
496 return new JsonPrimitive(src.toEpochMilli());
506 protected final Gson decoder = new GsonBuilder().disableHtmlEscaping().
507 registerTypeAdapter(ZonedDateTime.class, new GsonUTCAdapter()).
514 protected final Gson encoder = new GsonBuilder().disableHtmlEscaping().
515 registerTypeAdapter(ZonedDateTime.class, new GsonUTCAdapter()).
519 * Toolset to encode/decode tools associated with a topic
522 * @param decodedClass decoded class of an event
525 public GsonProtocolCoderToolset(String topic, String controllerId,
526 String groupId, String artifactId,
528 JsonProtocolFilter filter,
529 CustomGsonCoder customGsonCoder,
530 int modelClassLoaderHash) {
531 super(topic, controllerId, groupId, artifactId, decodedClass, filter, customGsonCoder, modelClassLoaderHash);
535 * gets the Gson decoder
537 * @return the Gson decoder
540 protected Gson getDecoder() {return decoder;}
543 * gets the Gson encoder
545 * @return the Gson encoder
548 protected Gson getEncoder() {return encoder;}
554 public Object decode(String json)
555 throws IllegalArgumentException, UnsupportedOperationException, IllegalStateException {
557 DroolsController droolsController =
558 DroolsController.factory.get(groupId, artifactId, "");
559 if (droolsController == null) {
560 String error = "NO-DROOLS-CONTROLLER for: " + json + " IN " + this;
562 throw new IllegalStateException(error);
565 CoderFilters decoderFilter = filter(json);
566 if (decoderFilter == null) {
567 String error = "NO-DECODER for: " + json + " IN " + this;
569 throw new UnsupportedOperationException(error);
572 Class<?> decoderClass;
575 droolsController.fetchModelClass(decoderFilter.getCodedClass());
576 if (decoderClass == null) {
577 String error = "DECODE-ERROR FETCHING MODEL CLASS: " + ":" + json + ":" + this;
579 throw new IllegalStateException(error);
581 } catch (Exception e) {
582 String error = "DECODE-ERROR FETCHING MODEL CLASS: "+ e.getMessage() + ":" + json + ":" + this;
583 logger.warn(MessageCodes.EXCEPTION_ERROR, e, error);
584 throw new UnsupportedOperationException(error, e);
587 if (this.customCoder != null) {
589 Class<?> gsonClassContainer =
590 droolsController.fetchModelClass(this.customCoder.getClassContainer());
591 Field gsonField = gsonClassContainer.getField(this.customCoder.staticCoderField);
592 Object gsonObject = gsonField.get(null);
593 Method fromJsonMethod = gsonObject.getClass().
595 ("fromJson", new Class[]{String.class, Class.class});
596 Object fact = fromJsonMethod.invoke(gsonObject, json, decoderClass);
598 } catch (NoSuchFieldException | SecurityException | IllegalAccessException |
599 NoSuchMethodException | InvocationTargetException e) {
600 String error = "DECODE-ERROR-FROM-CUSTOM-CODER: " + e.getMessage() + ":" + json + ":" + this;
601 logger.error(MessageCodes.EXCEPTION_ERROR, e, error);
602 throw new UnsupportedOperationException(error, e);
606 Object fact = this.decoder.fromJson(json, decoderClass);
608 } catch (Exception e) {
609 String error = "DECODE-ERROR FROM PDP-D FRAMEWORK: "+ json + ":" + e.getMessage() + ":" + this;
610 logger.error(MessageCodes.EXCEPTION_ERROR, e, error);
611 throw new UnsupportedOperationException(error, e);
622 public String encode(Object event)
623 throws IllegalArgumentException, UnsupportedOperationException {
625 DroolsController droolsController =
626 DroolsController.factory.get(groupId, artifactId, "");
627 if (droolsController == null) {
628 String error = "NO-DROOLS-CONTROLLER for: " + event + " IN " + this;
630 throw new IllegalStateException(error);
633 if (this.customCoder != null) {
635 Class<?> gsonClassContainer =
636 droolsController.fetchModelClass(this.customCoder.getClassContainer());
637 Field gsonField = gsonClassContainer.getField(this.customCoder.staticCoderField);
638 Object gsonObject = gsonField.get(null);
639 Method toJsonMethod = gsonObject.getClass().
641 ("toJson", new Class[]{Object.class});
642 String encodedJson = (String) toJsonMethod.invoke(gsonObject, event);
644 } catch (NoSuchFieldException | SecurityException | IllegalAccessException |
645 NoSuchMethodException | InvocationTargetException e) {
646 String error = "DECODE-ERROR-FROM-CUSTOM-CODER: " + e.getMessage() + ":" + event + ":" + this;
647 logger.error(MessageCodes.EXCEPTION_ERROR, e, error);
648 throw new UnsupportedOperationException(error, e);
652 String encodedEvent = this.encoder.toJson(event);
654 } catch (Exception e) {
655 String error = "ENCODE-ERROR: "+ event + " IN " + this;
656 logger.error(MessageCodes.EXCEPTION_ERROR, e, error);
657 throw new UnsupportedOperationException(error, e);
663 public String toString() {
664 StringBuilder builder = new StringBuilder();
665 builder.append("GsonProtocolCoderToolset [toString()=").append(super.toString()).append("]");
666 return builder.toString();