2 * ============LICENSE_START=======================================================
4 * ================================================================================
5 * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
6 * ================================================================================
7 * Licensed under the Apache License, Version 2.0 (the "License");
8 * you may not use this file except in compliance with the License.
9 * You may obtain a copy of the License at
11 * http://www.apache.org/licenses/LICENSE-2.0
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=========================================================
21 package org.openecomp.policy.drools.protocol.coders;
23 import java.lang.reflect.Field;
24 import java.lang.reflect.InvocationTargetException;
25 import java.lang.reflect.Method;
26 import java.lang.reflect.Type;
27 import java.time.Instant;
28 import java.time.ZonedDateTime;
29 import java.time.format.DateTimeFormatter;
30 import java.util.ArrayList;
31 import java.util.Iterator;
32 import java.util.List;
34 import org.openecomp.policy.common.logging.eelf.MessageCodes;
35 import org.openecomp.policy.common.logging.flexlogger.FlexLogger;
36 import org.openecomp.policy.common.logging.flexlogger.Logger;
37 import org.openecomp.policy.drools.controller.DroolsController;
38 import org.openecomp.policy.drools.protocol.coders.EventProtocolCoder.CoderFilters;
39 import org.openecomp.policy.drools.protocol.coders.TopicCoderFilterConfiguration.CustomCoder;
40 import org.openecomp.policy.drools.protocol.coders.TopicCoderFilterConfiguration.CustomGsonCoder;
41 import org.openecomp.policy.drools.protocol.coders.TopicCoderFilterConfiguration.CustomJacksonCoder;
43 import com.fasterxml.jackson.annotation.JsonIgnore;
44 import com.fasterxml.jackson.core.JsonProcessingException;
45 import com.fasterxml.jackson.databind.DeserializationFeature;
46 import com.fasterxml.jackson.databind.ObjectMapper;
47 import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;
48 import com.google.gson.Gson;
49 import com.google.gson.GsonBuilder;
50 import com.google.gson.JsonDeserializationContext;
51 import com.google.gson.JsonDeserializer;
52 import com.google.gson.JsonElement;
53 import com.google.gson.JsonParseException;
54 import com.google.gson.JsonParser;
55 import com.google.gson.JsonPrimitive;
56 import com.google.gson.JsonSerializationContext;
57 import com.google.gson.JsonSerializer;
60 * Protocol Coding/Decoding Toolset
62 public abstract class ProtocolCoderToolset {
67 protected final String topic;
72 protected final String controllerId;
77 protected final String groupId;
82 protected final String artifactId;
85 * Protocols and associated Filters
87 protected final List<CoderFilters> coders = new ArrayList<CoderFilters>();
90 * Tree model (instead of class model) generic parsing to be able to inspect elements
92 protected JsonParser filteringParser = new JsonParser();
97 protected CustomCoder customCoder;
102 * @param topic the topic
103 * @param controllerId the controller id
104 * @param codedClass the decoded class
105 * @param filters list of filters that apply to the
106 * selection of this decodedClass in case of multiplicity
107 * @throws IllegalArgumentException if invalid data has been passed in
109 public ProtocolCoderToolset(String topic,
114 JsonProtocolFilter filters,
115 CustomCoder customCoder,
116 int modelClassLoaderHash)
117 throws IllegalArgumentException {
119 if (topic == null || controllerId == null ||
120 groupId == null || artifactId == null ||
121 codedClass == null || filters == null ||
122 topic.isEmpty() || controllerId.isEmpty()) {
124 throw new IllegalArgumentException("Invalid input");
128 this.controllerId = controllerId;
129 this.groupId = groupId;
130 this.artifactId = artifactId;
131 this.coders.add(new CoderFilters(codedClass, filters, modelClassLoaderHash));
132 this.customCoder = customCoder;
136 * gets the coder + filters associated with this class name
138 * @param classname class name
139 * @return the decoder filters or null if not found
141 public CoderFilters getCoder(String classname) {
142 for (CoderFilters decoder: this.coders) {
143 if (decoder.factClass.equals(classname)) {
151 * get all coder filters in use
153 * @return coder filters
155 public List<CoderFilters> getCoders() {
160 * add coder or replace it exists
162 * @param eventClass decoder
163 * @param filter filter
165 public void addCoder(String eventClass, JsonProtocolFilter filter, int modelClassLoaderHash) {
167 for (CoderFilters coder: this.coders) {
168 if (coder.factClass.equals(eventClass)) {
169 // this is a better check than checking pointers, just
170 // in case classloader is different and this is just an update
171 coder.factClass = eventClass;
172 coder.filter = filter;
173 coder.modelClassLoaderHash = modelClassLoaderHash;
179 this.coders.add(new CoderFilters(eventClass, filter, modelClassLoaderHash));
185 * @param eventClass decoder
186 * @param filter filter
188 public void removeCoders(String eventClass) {
190 Iterator<CoderFilters> codersIt = this.coders.iterator();
191 while (codersIt.hasNext()) {
192 CoderFilters coder = codersIt.next();
193 if (coder.factClass.equals(eventClass)) {
205 public String getTopic() {return topic;}
208 * gets the controller id
210 * @return the controller id
212 public String getControllerId() {return controllerId;}
215 * @return the groupId
217 public String getGroupId() {
222 * @return the artifactId
224 public String getArtifactId() {
229 * @return the customCoder
231 public CustomCoder getCustomCoder() {
236 * @param customCoder the customCoder to set
238 public void setCustomCoder(CustomCoder customCoder) {
239 this.customCoder = customCoder;
243 * performs filtering on a json string
245 * @param json json string
246 * @return the decoder that passes the filter, otherwise null
247 * @throws UnsupportedOperationException can't filter
248 * @throws IllegalArgumentException invalid input
250 protected CoderFilters filter(String json)
251 throws UnsupportedOperationException, IllegalArgumentException, IllegalStateException {
254 // 1. Get list of decoding classes for this controller Id and topic
255 // 2. If there are no classes, return error
256 // 3. Otherwise, from the available classes for decoding, pick the first one that
257 // passes the filters
259 // Don't parse if it is not necessary
261 if (this.coders.isEmpty()) {
262 // TODO this is an error
263 throw new IllegalStateException("No coders available");
266 if (this.coders.size() == 1) {
267 JsonProtocolFilter filter = this.coders.get(0).getFilter();
268 if (!filter.isRules()) {
269 return this.coders.get(0);
275 event = this.filteringParser.parse(json);
276 } catch (Exception e) {
277 // TODO Auto-generated catch block
279 throw new UnsupportedOperationException(e);
282 for (CoderFilters decoder: this.coders) {
284 boolean accepted = decoder.getFilter().accept(event);
288 } catch (Exception e) {
289 // TODO: handle exception
298 * Decode json into a POJO object
299 * @param json json string
301 * @return a POJO object for the json string
302 * @throws IllegalArgumentException if an invalid parameter has been received
303 * @throws UnsupportedOperationException if parsing into POJO is not possible
305 public abstract Object decode(String json)
306 throws IllegalArgumentException, UnsupportedOperationException, IllegalStateException;
309 * Encodes a POJO object into a JSON String
311 * @param event JSON POJO event to be converted to String
312 * @return JSON string version of POJO object
313 * @throws IllegalArgumentException if an invalid parameter has been received
314 * @throws UnsupportedOperationException if parsing into POJO is not possible
316 public abstract String encode(Object event)
317 throws IllegalArgumentException, UnsupportedOperationException;
320 public String toString() {
321 StringBuilder builder = new StringBuilder();
322 builder.append("ProtocolCoderToolset [topic=").append(topic).append(", controllerId=").append(controllerId)
323 .append(", groupId=").append(groupId).append(", artifactId=").append(artifactId).append(", coders=")
324 .append(coders).append(", filteringParser=").append(filteringParser).append(", customCoder=")
325 .append(customCoder).append("]");
326 return builder.toString();
331 * Tools used for encoding/decoding using Jackson
333 class JacksonProtocolCoderToolset extends ProtocolCoderToolset {
334 private static Logger logger = FlexLogger.getLogger(JacksonProtocolCoderToolset.class);
339 protected final ObjectMapper decoder = new ObjectMapper();
345 protected final ObjectMapper encoder = new ObjectMapper();
348 * Toolset to encode/decode tools associated with a topic
351 * @param decodedClass decoded class of an event
354 public JacksonProtocolCoderToolset(String topic, String controllerId,
355 String groupId, String artifactId,
357 JsonProtocolFilter filter,
358 CustomJacksonCoder customJacksonCoder,
359 int modelClassLoaderHash) {
360 super(topic, controllerId, groupId, artifactId, decodedClass, filter, customJacksonCoder, modelClassLoaderHash);
361 decoder.registerModule(new JavaTimeModule());
362 decoder.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES,
367 * gets the Jackson decoder
369 * @return the Jackson decoder
372 protected ObjectMapper getDecoder() {return decoder;}
375 * gets the Jackson encoder
377 * @return the Jackson encoder
380 protected ObjectMapper getEncoder() {return encoder;}
386 public Object decode(String json)
387 throws IllegalArgumentException, UnsupportedOperationException, IllegalStateException {
389 // 0. Use custom coder if available
391 if (this.customCoder != null) {
392 throw new UnsupportedOperationException
393 ("Jackon Custom Decoder is not supported at this time");
396 DroolsController droolsController =
397 DroolsController.factory.get(groupId, artifactId, "");
398 if (droolsController == null) {
399 String error = "NO-DROOLS-CONTROLLER for: " + json + " IN " + this;
401 throw new IllegalStateException(error);
404 CoderFilters decoderFilter = filter(json);
405 if (decoderFilter == null) {
406 String error = "NO-DECODER for: " + json + " IN " + this;
408 throw new UnsupportedOperationException(error);
411 Class<?> decoderClass;
414 droolsController.fetchModelClass(decoderFilter.getCodedClass());
415 if (decoderClass == null) {
416 String error = "DECODE-ERROR FETCHING MODEL CLASS: " + ":" + json + ":" + this;
418 throw new IllegalStateException(error);
420 } catch (Exception e) {
421 String error = "DECODE-ERROR FETCHING MODEL CLASS: "+ e.getMessage() + ":" + json + ":" + this;
422 logger.warn(MessageCodes.EXCEPTION_ERROR, e, error);
423 throw new UnsupportedOperationException(error, e);
428 Object fact = this.decoder.readValue(json, decoderClass);
430 } catch (Exception e) {
431 String error = "DECODE-ERROR FROM PDP-D FRAMEWORK: "+ json + ":" + e.getMessage() + ":" + this;
432 logger.error(MessageCodes.EXCEPTION_ERROR, e, error);
433 throw new UnsupportedOperationException(error, e);
441 public String encode(Object event)
442 throws IllegalArgumentException, UnsupportedOperationException {
444 // 0. Use custom coder if available
446 if (this.customCoder != null) {
447 throw new UnsupportedOperationException
448 ("Jackon Custom Encoder is not supported at this time");
452 String encodedEvent = this.encoder.writeValueAsString(event);
454 } catch (JsonProcessingException e) {
455 String error = "ENCODE-ERROR: "+ event + " IN " + this;
456 logger.error(MessageCodes.EXCEPTION_ERROR, e, error);
457 throw new UnsupportedOperationException(error, e);
462 public String toString() {
463 StringBuilder builder = new StringBuilder();
464 builder.append("JacksonProtocolCoderToolset [toString()=").append(super.toString()).append("]");
465 return builder.toString();
471 * Tools used for encoding/decoding using Jackson
473 class GsonProtocolCoderToolset extends ProtocolCoderToolset {
475 private static Logger logger = FlexLogger.getLogger(GsonProtocolCoderToolset.class);
477 * Formatter for JSON encoding/decoding
480 public static DateTimeFormatter format = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss.SSSSSSxxx");
483 public static DateTimeFormatter zuluFormat = DateTimeFormatter.ISO_INSTANT;
486 * Adapter for ZonedDateTime
489 public static class GsonUTCAdapter implements JsonSerializer<ZonedDateTime>, JsonDeserializer<ZonedDateTime> {
491 public ZonedDateTime deserialize(JsonElement element, Type type, JsonDeserializationContext context)
492 throws JsonParseException {
494 return ZonedDateTime.parse(element.getAsString(), format);
495 } catch (Exception e) {
496 System.err.println(e);
501 public JsonElement serialize(ZonedDateTime datetime, Type type, JsonSerializationContext context) {
502 return new JsonPrimitive(datetime.format(format));
506 public static class GsonInstantAdapter implements JsonSerializer<Instant>, JsonDeserializer<Instant> {
509 public Instant deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context)
510 throws JsonParseException {
511 return Instant.ofEpochMilli(json.getAsLong());
515 public JsonElement serialize(Instant src, Type typeOfSrc, JsonSerializationContext context) {
516 return new JsonPrimitive(src.toEpochMilli());
526 protected final Gson decoder = new GsonBuilder().disableHtmlEscaping().
527 registerTypeAdapter(ZonedDateTime.class, new GsonUTCAdapter()).
534 protected final Gson encoder = new GsonBuilder().disableHtmlEscaping().
535 registerTypeAdapter(ZonedDateTime.class, new GsonUTCAdapter()).
539 * Toolset to encode/decode tools associated with a topic
542 * @param decodedClass decoded class of an event
545 public GsonProtocolCoderToolset(String topic, String controllerId,
546 String groupId, String artifactId,
548 JsonProtocolFilter filter,
549 CustomGsonCoder customGsonCoder,
550 int modelClassLoaderHash) {
551 super(topic, controllerId, groupId, artifactId, decodedClass, filter, customGsonCoder, modelClassLoaderHash);
555 * gets the Gson decoder
557 * @return the Gson decoder
560 protected Gson getDecoder() {return decoder;}
563 * gets the Gson encoder
565 * @return the Gson encoder
568 protected Gson getEncoder() {return encoder;}
574 public Object decode(String json)
575 throws IllegalArgumentException, UnsupportedOperationException, IllegalStateException {
577 DroolsController droolsController =
578 DroolsController.factory.get(groupId, artifactId, "");
579 if (droolsController == null) {
580 String error = "NO-DROOLS-CONTROLLER for: " + json + " IN " + this;
582 throw new IllegalStateException(error);
585 CoderFilters decoderFilter = filter(json);
586 if (decoderFilter == null) {
587 String error = "NO-DECODER for: " + json + " IN " + this;
589 throw new UnsupportedOperationException(error);
592 Class<?> decoderClass;
595 droolsController.fetchModelClass(decoderFilter.getCodedClass());
596 if (decoderClass == null) {
597 String error = "DECODE-ERROR FETCHING MODEL CLASS: " + ":" + json + ":" + this;
599 throw new IllegalStateException(error);
601 } catch (Exception e) {
602 String error = "DECODE-ERROR FETCHING MODEL CLASS: "+ e.getMessage() + ":" + json + ":" + this;
603 logger.warn(MessageCodes.EXCEPTION_ERROR, e, error);
604 throw new UnsupportedOperationException(error, e);
607 if (this.customCoder != null) {
609 Class<?> gsonClassContainer =
610 droolsController.fetchModelClass(this.customCoder.getClassContainer());
611 Field gsonField = gsonClassContainer.getField(this.customCoder.staticCoderField);
612 Object gsonObject = gsonField.get(null);
613 Method fromJsonMethod = gsonObject.getClass().
615 ("fromJson", new Class[]{String.class, Class.class});
616 Object fact = fromJsonMethod.invoke(gsonObject, json, decoderClass);
618 } catch (NoSuchFieldException | SecurityException | IllegalAccessException |
619 NoSuchMethodException | InvocationTargetException e) {
620 String error = "DECODE-ERROR-FROM-CUSTOM-CODER: " + e.getMessage() + ":" + json + ":" + this;
621 logger.error(MessageCodes.EXCEPTION_ERROR, e, error);
622 throw new UnsupportedOperationException(error, e);
626 Object fact = this.decoder.fromJson(json, decoderClass);
628 } catch (Exception e) {
629 String error = "DECODE-ERROR FROM PDP-D FRAMEWORK: "+ json + ":" + e.getMessage() + ":" + this;
630 logger.error(MessageCodes.EXCEPTION_ERROR, e, error);
631 throw new UnsupportedOperationException(error, e);
642 public String encode(Object event)
643 throws IllegalArgumentException, UnsupportedOperationException {
645 DroolsController droolsController =
646 DroolsController.factory.get(groupId, artifactId, "");
647 if (droolsController == null) {
648 String error = "NO-DROOLS-CONTROLLER for: " + event + " IN " + this;
650 throw new IllegalStateException(error);
653 if (this.customCoder != null) {
655 Class<?> gsonClassContainer =
656 droolsController.fetchModelClass(this.customCoder.getClassContainer());
657 Field gsonField = gsonClassContainer.getField(this.customCoder.staticCoderField);
658 Object gsonObject = gsonField.get(null);
659 Method toJsonMethod = gsonObject.getClass().
661 ("toJson", new Class[]{Object.class});
662 String encodedJson = (String) toJsonMethod.invoke(gsonObject, event);
664 } catch (NoSuchFieldException | SecurityException | IllegalAccessException |
665 NoSuchMethodException | InvocationTargetException e) {
666 String error = "DECODE-ERROR-FROM-CUSTOM-CODER: " + e.getMessage() + ":" + event + ":" + this;
667 logger.error(MessageCodes.EXCEPTION_ERROR, e, error);
668 throw new UnsupportedOperationException(error, e);
672 String encodedEvent = this.encoder.toJson(event);
674 } catch (Exception e) {
675 String error = "ENCODE-ERROR: "+ event + " IN " + this;
676 logger.error(MessageCodes.EXCEPTION_ERROR, e, error);
677 throw new UnsupportedOperationException(error, e);
683 public String toString() {
684 StringBuilder builder = new StringBuilder();
685 builder.append("GsonProtocolCoderToolset [toString()=").append(super.toString()).append("]");
686 return builder.toString();