51b28d6620c462ab8385117b66b6fa63adaee8ee
[policy/drools-pdp.git] /
1 /*
2  * ============LICENSE_START=======================================================
3  * ONAP
4  * ================================================================================
5  * Copyright (C) 2019-2020 AT&T Intellectual Property. All rights reserved.
6  * ================================================================================
7  * Licensed under the Apache License, Version 2.0 (the "License");
8  * you may not use this file except in compliance with the License.
9  * You may obtain a copy of the License at
10  *
11  *      http://www.apache.org/licenses/LICENSE-2.0
12  *
13  * Unless required by applicable law or agreed to in writing, software
14  * distributed under the License is distributed on an "AS IS" BASIS,
15  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16  * See the License for the specific language governing permissions and
17  * limitations under the License.
18  * ============LICENSE_END=========================================================
19  */
20
21 package org.onap.policy.drools.protocol.coders;
22
23 import com.fasterxml.jackson.annotation.JsonIgnore;
24 import com.google.gson.Gson;
25 import com.google.gson.GsonBuilder;
26 import com.google.gson.JsonDeserializationContext;
27 import com.google.gson.JsonDeserializer;
28 import com.google.gson.JsonElement;
29 import com.google.gson.JsonPrimitive;
30 import com.google.gson.JsonSerializationContext;
31 import com.google.gson.JsonSerializer;
32 import java.lang.reflect.Field;
33 import java.lang.reflect.Method;
34 import java.lang.reflect.Type;
35 import java.time.Instant;
36 import java.time.ZonedDateTime;
37 import java.time.format.DateTimeFormatter;
38 import org.onap.policy.common.gson.annotation.GsonJsonIgnore;
39 import org.onap.policy.drools.controller.DroolsController;
40 import org.onap.policy.drools.controller.DroolsControllerConstants;
41 import org.onap.policy.drools.protocol.coders.EventProtocolCoder.CoderFilters;
42 import org.slf4j.Logger;
43 import org.slf4j.LoggerFactory;
44
45 /**
46  * Tools used for encoding/decoding using GSON.
47  */
48 class GsonProtocolCoderToolset extends ProtocolCoderToolset {
49     private static final String CANNOT_FETCH_CLASS = "{}: cannot fetch application class {}";
50     private static final String FETCH_CLASS_EX_MSG = "cannot fetch application class ";
51
52     /**
53      * Logger.
54      */
55     private static final Logger logger = LoggerFactory.getLogger(GsonProtocolCoderToolset.class);
56
57     /**
58      * Formatter for JSON encoding/decoding.
59      */
60     @JsonIgnore
61     @GsonJsonIgnore
62     public static final DateTimeFormatter format = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss.SSSSSSxxx");
63
64     @JsonIgnore
65     @GsonJsonIgnore
66     public static final DateTimeFormatter zuluFormat = DateTimeFormatter.ISO_INSTANT;
67
68     /**
69      * Adapter for ZonedDateTime.
70      */
71     public static class GsonUtcAdapter implements JsonSerializer<ZonedDateTime>, JsonDeserializer<ZonedDateTime> {
72         @Override
73         public ZonedDateTime deserialize(JsonElement element, Type type,
74                 JsonDeserializationContext context) {
75             try {
76                 return ZonedDateTime.parse(element.getAsString(), format);
77             } catch (final Exception e) {
78                 logger.info("GsonUTCAdapter: cannot parse {} because of {}", element, e.getMessage(), e);
79             }
80             return null;
81         }
82
83         @Override
84         public JsonElement serialize(ZonedDateTime datetime, Type type,
85                 JsonSerializationContext context) {
86             return new JsonPrimitive(datetime.format(format));
87         }
88     }
89
90     public static class GsonInstantAdapter implements JsonSerializer<Instant>, JsonDeserializer<Instant> {
91
92         @Override
93         public Instant deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) {
94             return Instant.ofEpochMilli(json.getAsLong());
95         }
96
97         @Override
98         public JsonElement serialize(Instant src, Type typeOfSrc, JsonSerializationContext context) {
99             return new JsonPrimitive(src.toEpochMilli());
100         }
101
102     }
103
104
105     /**
106      * decoder.
107      */
108     @JsonIgnore
109     @GsonJsonIgnore
110     protected final Gson decoder = new GsonBuilder().disableHtmlEscaping()
111         .registerTypeAdapter(ZonedDateTime.class, new GsonUtcAdapter())
112         .registerTypeAdapter(Instant.class, new GsonInstantAdapter()).create();
113
114     /**
115      * encoder.
116      */
117     @JsonIgnore
118     @GsonJsonIgnore
119     protected final Gson encoder = new GsonBuilder().disableHtmlEscaping()
120         .registerTypeAdapter(ZonedDateTime.class, new GsonUtcAdapter())
121         .registerTypeAdapter(Instant.class, new GsonInstantAdapter()).create();
122
123     /**
124      * Toolset to encode/decode tools associated with a topic.
125      *
126      * @param eventProtocolParams parameter object for event encoder
127      * @param controllerId controller id
128      */
129     public GsonProtocolCoderToolset(EventProtocolParams eventProtocolParams, String controllerId) {
130         super(eventProtocolParams, controllerId);
131     }
132
133     /**
134      * gets the Gson decoder.
135      *
136      * @return the Gson decoder
137      */
138     @JsonIgnore
139     @GsonJsonIgnore
140     protected Gson getDecoder() {
141         return this.decoder;
142     }
143
144     /**
145      * gets the Gson encoder.
146      *
147      * @return the Gson encoder
148      */
149     @JsonIgnore
150     @GsonJsonIgnore
151     protected Gson getEncoder() {
152         return this.encoder;
153     }
154
155     /**
156      * {@inheritDoc}.
157      */
158     @Override
159     public Object decode(String json) {
160
161         final DroolsController droolsController =
162                         DroolsControllerConstants.getFactory().get(this.groupId, this.artifactId, "");
163         if (droolsController == null) {
164             logger.warn("{}: no drools-controller to process {}", this, json);
165             throw new IllegalStateException("no drools-controller to process event");
166         }
167
168         final CoderFilters decoderFilter = this.filter(json);
169         if (decoderFilter == null) {
170             logger.debug("{}: no decoder to process {}", this, json);
171             throw new UnsupportedOperationException("no decoder to process event");
172         }
173
174         Class<?> decoderClass;
175         try {
176             decoderClass = droolsController.fetchModelClass(decoderFilter.getCodedClass());
177             if (decoderClass == null) {
178                 logger.warn(CANNOT_FETCH_CLASS, this, decoderFilter.getCodedClass());
179                 throw new IllegalStateException(
180                         FETCH_CLASS_EX_MSG + decoderFilter.getCodedClass());
181             }
182         } catch (final Exception e) {
183             logger.warn(CANNOT_FETCH_CLASS, this, decoderFilter.getCodedClass());
184             throw new UnsupportedOperationException(
185                     FETCH_CLASS_EX_MSG + decoderFilter.getCodedClass(), e);
186         }
187
188         if (this.customCoder != null) {
189             try {
190                 final Class<?> gsonClassContainer =
191                         droolsController.fetchModelClass(this.customCoder.getClassContainer());
192                 final Field gsonField = gsonClassContainer.getField(this.customCoder.staticCoderField);
193                 final Object gsonObject = gsonField.get(null);
194                 final Method fromJsonMethod = gsonObject.getClass().getDeclaredMethod("fromJson",
195                         String.class, Class.class);
196                 return fromJsonMethod.invoke(gsonObject, json, decoderClass);
197             } catch (final Exception e) {
198                 logger.warn(CANNOT_FETCH_CLASS, this, decoderFilter.getCodedClass());
199                 throw new UnsupportedOperationException(
200                         FETCH_CLASS_EX_MSG + decoderFilter.getCodedClass(), e);
201             }
202         } else {
203             try {
204                 return this.decoder.fromJson(json, decoderClass);
205             } catch (final Exception e) {
206                 logger.warn("{} cannot decode {} into {}", this, json, decoderClass.getName());
207                 throw new UnsupportedOperationException(
208                         "cannont decode into " + decoderFilter.getCodedClass(), e);
209             }
210         }
211     }
212
213     /**
214      * {@inheritDoc}.
215      */
216     @Override
217     public String encode(Object event) {
218
219         if (this.customCoder != null) {
220             try {
221                 final DroolsController droolsController =
222                                 DroolsControllerConstants.getFactory().get(this.groupId, this.artifactId, null);
223                 final Class<?> gsonClassContainer =
224                         droolsController.fetchModelClass(this.customCoder.getClassContainer());
225                 final Field gsonField = gsonClassContainer.getField(this.customCoder.staticCoderField);
226                 final Object gsonObject = gsonField.get(null);
227                 final Method toJsonMethod =
228                         gsonObject.getClass().getDeclaredMethod("toJson", Object.class);
229                 return (String) toJsonMethod.invoke(gsonObject, event);
230             } catch (final Exception e) {
231                 logger.warn("{} cannot custom-encode {}", this, event);
232                 throw new UnsupportedOperationException("event cannot be encoded", e);
233             }
234         } else {
235             try {
236                 return this.encoder.toJson(event);
237             } catch (final Exception e) {
238                 logger.warn("{} cannot encode {}", this, event);
239                 throw new UnsupportedOperationException("event cannot be encoded", e);
240             }
241         }
242     }
243
244     @Override
245     public String toString() {
246         final StringBuilder builder = new StringBuilder();
247         builder.append("GsonProtocolCoderToolset [toString()=").append(super.toString()).append("]");
248         return builder.toString();
249     }
250 }