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