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