637e0c148f135f50c3d7edbf76ca370724e40459
[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.google.gson.Gson;
24 import com.google.gson.GsonBuilder;
25 import com.google.gson.JsonDeserializationContext;
26 import com.google.gson.JsonDeserializer;
27 import com.google.gson.JsonElement;
28 import com.google.gson.JsonPrimitive;
29 import com.google.gson.JsonSerializationContext;
30 import com.google.gson.JsonSerializer;
31 import java.lang.reflect.Field;
32 import java.lang.reflect.Method;
33 import java.lang.reflect.Type;
34 import java.time.Instant;
35 import java.time.ZonedDateTime;
36 import java.time.format.DateTimeFormatter;
37 import org.onap.policy.common.gson.annotation.GsonJsonIgnore;
38 import org.onap.policy.drools.controller.DroolsController;
39 import org.onap.policy.drools.controller.DroolsControllerConstants;
40 import org.onap.policy.drools.protocol.coders.EventProtocolCoder.CoderFilters;
41 import org.slf4j.Logger;
42 import org.slf4j.LoggerFactory;
43
44 /**
45  * Tools used for encoding/decoding using GSON.
46  */
47 class GsonProtocolCoderToolset extends ProtocolCoderToolset {
48     private static final String CANNOT_FETCH_CLASS = "{}: cannot fetch application class {}";
49     private static final String FETCH_CLASS_EX_MSG = "cannot fetch application class ";
50
51     /**
52      * Logger.
53      */
54     private static final Logger logger = LoggerFactory.getLogger(GsonProtocolCoderToolset.class);
55
56     /**
57      * Formatter for JSON encoding/decoding.
58      */
59     @GsonJsonIgnore
60     public static final DateTimeFormatter format = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss.SSSSSSxxx");
61
62     @GsonJsonIgnore
63     public static final DateTimeFormatter zuluFormat = DateTimeFormatter.ISO_INSTANT;
64
65     /**
66      * Adapter for ZonedDateTime.
67      */
68     public static class GsonUtcAdapter implements JsonSerializer<ZonedDateTime>, JsonDeserializer<ZonedDateTime> {
69         @Override
70         public ZonedDateTime deserialize(JsonElement element, Type type,
71                 JsonDeserializationContext context) {
72             try {
73                 return ZonedDateTime.parse(element.getAsString(), format);
74             } catch (final Exception e) {
75                 logger.info("GsonUTCAdapter: cannot parse {} because of {}", element, e.getMessage(), e);
76             }
77             return null;
78         }
79
80         @Override
81         public JsonElement serialize(ZonedDateTime datetime, Type type,
82                 JsonSerializationContext context) {
83             return new JsonPrimitive(datetime.format(format));
84         }
85     }
86
87     public static class GsonInstantAdapter implements JsonSerializer<Instant>, JsonDeserializer<Instant> {
88
89         @Override
90         public Instant deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) {
91             return Instant.ofEpochMilli(json.getAsLong());
92         }
93
94         @Override
95         public JsonElement serialize(Instant src, Type typeOfSrc, JsonSerializationContext context) {
96             return new JsonPrimitive(src.toEpochMilli());
97         }
98
99     }
100
101
102     /**
103      * decoder.
104      */
105     @GsonJsonIgnore
106     protected final Gson decoder = new GsonBuilder().disableHtmlEscaping()
107         .registerTypeAdapter(ZonedDateTime.class, new GsonUtcAdapter())
108         .registerTypeAdapter(Instant.class, new GsonInstantAdapter()).create();
109
110     /**
111      * encoder.
112      */
113     @GsonJsonIgnore
114     protected final Gson encoder = new GsonBuilder().disableHtmlEscaping()
115         .registerTypeAdapter(ZonedDateTime.class, new GsonUtcAdapter())
116         .registerTypeAdapter(Instant.class, new GsonInstantAdapter()).create();
117
118     /**
119      * Toolset to encode/decode tools associated with a topic.
120      *
121      * @param eventProtocolParams parameter object for event encoder
122      * @param controllerId controller id
123      */
124     public GsonProtocolCoderToolset(EventProtocolParams eventProtocolParams, String controllerId) {
125         super(eventProtocolParams, controllerId);
126     }
127
128     /**
129      * gets the Gson decoder.
130      *
131      * @return the Gson decoder
132      */
133     @GsonJsonIgnore
134     protected Gson getDecoder() {
135         return this.decoder;
136     }
137
138     /**
139      * gets the Gson encoder.
140      *
141      * @return the Gson encoder
142      */
143     @GsonJsonIgnore
144     protected Gson getEncoder() {
145         return this.encoder;
146     }
147
148     /**
149      * {@inheritDoc}.
150      */
151     @Override
152     public Object decode(String json) {
153
154         final DroolsController droolsController =
155                         DroolsControllerConstants.getFactory().get(this.groupId, this.artifactId, "");
156         if (droolsController == null) {
157             logger.warn("{}: no drools-controller to process {}", this, json);
158             throw new IllegalStateException("no drools-controller to process event");
159         }
160
161         final CoderFilters decoderFilter = this.filter(json);
162         if (decoderFilter == null) {
163             logger.debug("{}: no decoder to process {}", this, json);
164             throw new UnsupportedOperationException("no decoder to process event");
165         }
166
167         Class<?> decoderClass;
168         try {
169             decoderClass = droolsController.fetchModelClass(decoderFilter.getCodedClass());
170             if (decoderClass == null) {
171                 logger.warn(CANNOT_FETCH_CLASS, this, decoderFilter.getCodedClass());
172                 throw new IllegalStateException(
173                         FETCH_CLASS_EX_MSG + decoderFilter.getCodedClass());
174             }
175         } catch (final Exception e) {
176             logger.warn(CANNOT_FETCH_CLASS, this, decoderFilter.getCodedClass());
177             throw new UnsupportedOperationException(
178                     FETCH_CLASS_EX_MSG + decoderFilter.getCodedClass(), e);
179         }
180
181         if (this.customCoder != null) {
182             try {
183                 final Class<?> gsonClassContainer =
184                         droolsController.fetchModelClass(this.customCoder.getClassContainer());
185                 final Field gsonField = gsonClassContainer.getField(this.customCoder.staticCoderField);
186                 final Object gsonObject = gsonField.get(null);
187                 final Method fromJsonMethod = gsonObject.getClass().getDeclaredMethod("fromJson",
188                         String.class, Class.class);
189                 return fromJsonMethod.invoke(gsonObject, json, decoderClass);
190             } catch (final Exception e) {
191                 logger.warn(CANNOT_FETCH_CLASS, this, decoderFilter.getCodedClass());
192                 throw new UnsupportedOperationException(
193                         FETCH_CLASS_EX_MSG + decoderFilter.getCodedClass(), e);
194             }
195         } else {
196             try {
197                 return this.decoder.fromJson(json, decoderClass);
198             } catch (final Exception e) {
199                 logger.warn("{} cannot decode {} into {}", this, json, decoderClass.getName());
200                 throw new UnsupportedOperationException(
201                         "cannont decode into " + decoderFilter.getCodedClass(), e);
202             }
203         }
204     }
205
206     /**
207      * {@inheritDoc}.
208      */
209     @Override
210     public String encode(Object event) {
211
212         if (this.customCoder != null) {
213             try {
214                 final DroolsController droolsController =
215                                 DroolsControllerConstants.getFactory().get(this.groupId, this.artifactId, null);
216                 final Class<?> gsonClassContainer =
217                         droolsController.fetchModelClass(this.customCoder.getClassContainer());
218                 final Field gsonField = gsonClassContainer.getField(this.customCoder.staticCoderField);
219                 final Object gsonObject = gsonField.get(null);
220                 final Method toJsonMethod =
221                         gsonObject.getClass().getDeclaredMethod("toJson", Object.class);
222                 return (String) toJsonMethod.invoke(gsonObject, event);
223             } catch (final Exception e) {
224                 logger.warn("{} cannot custom-encode {}", this, event);
225                 throw new UnsupportedOperationException("event cannot be encoded", e);
226             }
227         } else {
228             try {
229                 return this.encoder.toJson(event);
230             } catch (final Exception e) {
231                 logger.warn("{} cannot encode {}", this, event);
232                 throw new UnsupportedOperationException("event cannot be encoded", e);
233             }
234         }
235     }
236
237     @Override
238     public String toString() {
239         final StringBuilder builder = new StringBuilder();
240         builder.append("GsonProtocolCoderToolset [toString()=").append(super.toString()).append("]");
241         return builder.toString();
242     }
243 }