2 * ============LICENSE_START=======================================================
3 * Copyright (C) 2016-2018 Ericsson. All rights reserved.
4 * ================================================================================
5 * Licensed under the Apache License, Version 2.0 (the "License");
6 * you may not use this file except in compliance with the License.
7 * You may obtain a copy of the License at
9 * http://www.apache.org/licenses/LICENSE-2.0
11 * Unless required by applicable law or agreed to in writing, software
12 * distributed under the License is distributed on an "AS IS" BASIS,
13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 * See the License for the specific language governing permissions and
15 * limitations under the License.
17 * SPDX-License-Identifier: Apache-2.0
18 * ============LICENSE_END=========================================================
21 package org.onap.policy.apex.service.engine.event.impl.jsonprotocolplugin;
23 import java.util.ArrayList;
24 import java.util.List;
26 import org.onap.policy.apex.context.SchemaHelper;
27 import org.onap.policy.apex.context.impl.schema.SchemaHelperFactory;
28 import org.onap.policy.apex.model.basicmodel.service.ModelService;
29 import org.onap.policy.apex.model.eventmodel.concepts.AxEvent;
30 import org.onap.policy.apex.model.eventmodel.concepts.AxEvents;
31 import org.onap.policy.apex.model.eventmodel.concepts.AxField;
32 import org.onap.policy.apex.service.engine.event.ApexEvent;
33 import org.onap.policy.apex.service.engine.event.ApexEventException;
34 import org.onap.policy.apex.service.engine.event.ApexEventProtocolConverter;
35 import org.onap.policy.apex.service.engine.event.ApexEventRuntimeException;
36 import org.onap.policy.apex.service.parameters.eventprotocol.EventProtocolParameters;
37 import org.slf4j.ext.XLogger;
38 import org.slf4j.ext.XLoggerFactory;
40 import com.google.gson.Gson;
41 import com.google.gson.GsonBuilder;
42 import com.google.gson.JsonElement;
43 import com.google.gson.JsonObject;
46 * The Class Apex2JSONEventConverter converts {@link ApexEvent} instances to and from JSON string
47 * representations of Apex events.
49 * @author Liam Fallon (liam.fallon@ericsson.com)
51 public class Apex2JSONEventConverter implements ApexEventProtocolConverter {
52 private static final XLogger LOGGER = XLoggerFactory.getXLogger(Apex2JSONEventConverter.class);
54 // The parameters for the JSON event protocol
55 private JSONEventProtocolParameters jsonPars;
61 * org.onap.policy.apex.service.engine.event.ApexEventProtocolConverter#init(org.onap.policy.
62 * apex.service.parameters.eventprotocol.EventProtocolParameters)
65 public void init(final EventProtocolParameters parameters) {
66 // Check and get the JSON parameters
67 if (!(parameters instanceof JSONEventProtocolParameters)) {
68 final String errorMessage = "specified consumer properties are not applicable to the JSON event protocol";
69 LOGGER.warn(errorMessage);
70 throw new ApexEventRuntimeException(errorMessage);
73 jsonPars = (JSONEventProtocolParameters) parameters;
80 * org.onap.policy.apex.service.engine.event.ApexEventConverter#toApexEvent(java.lang.String,
84 public List<ApexEvent> toApexEvent(final String eventName, final Object eventObject) throws ApexEventException {
85 // Check the event eventObject
86 if (eventObject == null) {
87 LOGGER.warn("event processing failed, event is null");
88 throw new ApexEventException("event processing failed, event is null");
91 // Cast the event to a string, if our conversion is correctly configured, this cast should
93 String jsonEventString = null;
95 jsonEventString = (String) eventObject;
96 } catch (final Exception e) {
97 final String errorMessage = "error converting event \"" + eventObject + "\" to a string";
98 LOGGER.debug(errorMessage, e);
99 throw new ApexEventRuntimeException(errorMessage, e);
102 // The list of events we will return
103 final List<ApexEvent> eventList = new ArrayList<ApexEvent>();
106 // We may have a single JSON object with a single event or an array of JSON objects
107 final Object decodedJsonObject =
108 new GsonBuilder().serializeNulls().create().fromJson(jsonEventString, Object.class);
110 // Check if we have a list of objects
111 if (decodedJsonObject instanceof List) {
112 // Check if it's a list of JSON objects or a list of strings
113 @SuppressWarnings("unchecked")
114 final List<Object> decodedJsonList = (List<Object>) decodedJsonObject;
116 // Decode each of the list elements in sequence
117 for (final Object jsonListObject : decodedJsonList) {
118 if (jsonListObject instanceof String) {
119 eventList.add(jsonStringApexEvent(eventName, (String) jsonListObject));
120 } else if (jsonListObject instanceof JsonObject) {
121 eventList.add(jsonObject2ApexEvent(eventName, (JsonObject) jsonListObject));
123 throw new ApexEventException("incoming event (" + jsonEventString
124 + ") is a JSON object array containing an invalid object " + jsonListObject);
128 eventList.add(jsonStringApexEvent(eventName, jsonEventString));
130 } catch (final Exception e) {
131 final String errorString =
132 "Failed to unmarshal JSON event: " + e.getMessage() + ", event=" + jsonEventString;
133 LOGGER.warn(errorString, e);
134 throw new ApexEventException(errorString, e);
137 // Return the list of events we have unmarshalled
145 * org.onap.policy.apex.service.engine.event.ApexEventConverter#fromApexEvent(org.onap.policy.
146 * apex.service.engine.event.ApexEvent)
149 public Object fromApexEvent(final ApexEvent apexEvent) throws ApexEventException {
150 // Check the Apex event
151 if (apexEvent == null) {
152 LOGGER.warn("event processing failed, Apex event is null");
153 throw new ApexEventException("event processing failed, Apex event is null");
156 // Get the event definition for the event from the model service
157 final AxEvent eventDefinition =
158 ModelService.getModel(AxEvents.class).get(apexEvent.getName(), apexEvent.getVersion());
160 // Use a GSON Json object to marshal the Apex event to JSON
161 final Gson gson = new GsonBuilder().serializeNulls().setPrettyPrinting().create();
162 final JsonObject jsonObject = new JsonObject();
164 jsonObject.addProperty(ApexEvent.NAME_HEADER_FIELD, apexEvent.getName());
165 jsonObject.addProperty(ApexEvent.VERSION_HEADER_FIELD, apexEvent.getVersion());
166 jsonObject.addProperty(ApexEvent.NAMESPACE_HEADER_FIELD, apexEvent.getNameSpace());
167 jsonObject.addProperty(ApexEvent.SOURCE_HEADER_FIELD, apexEvent.getSource());
168 jsonObject.addProperty(ApexEvent.TARGET_HEADER_FIELD, apexEvent.getTarget());
170 if (apexEvent.getExceptionMessage() != null) {
171 jsonObject.addProperty(ApexEvent.EXCEPTION_MESSAGE_HEADER_FIELD, apexEvent.getExceptionMessage());
174 for (final AxField eventField : eventDefinition.getFields()) {
175 final String fieldName = eventField.getKey().getLocalName();
177 if (!apexEvent.containsKey(fieldName)) {
178 if (!eventField.getOptional()) {
179 final String errorMessage = "error parsing " + eventDefinition.getID() + " event to Json. "
180 + "Field \"" + fieldName + "\" is missing, but is mandatory. Fields: " + apexEvent;
181 LOGGER.debug(errorMessage);
182 throw new ApexEventRuntimeException(errorMessage);
187 final Object fieldValue = apexEvent.get(fieldName);
189 // Get the schema helper
190 final SchemaHelper fieldSchemaHelper =
191 new SchemaHelperFactory().createSchemaHelper(eventField.getKey(), eventField.getSchema());
192 jsonObject.add(fieldName, fieldSchemaHelper.marshal2JsonElement(fieldValue));
195 // Output JSON string in a pretty format
196 return gson.toJson(jsonObject);
200 * This method converts a JSON object into an Apex event.
202 * @param eventName the name of the event
203 * @param jsonEventString the JSON string that holds the event
204 * @return the apex event that we have converted the JSON object into
205 * @throws ApexEventException thrown on unmarshaling exceptions
207 private ApexEvent jsonStringApexEvent(final String eventName, final String jsonEventString)
208 throws ApexEventException {
209 // Use GSON to read the event string
210 final JsonObject jsonObject =
211 new GsonBuilder().serializeNulls().create().fromJson(jsonEventString, JsonObject.class);
213 if (jsonObject == null || !jsonObject.isJsonObject()) {
214 throw new ApexEventException(
215 "incoming event (" + jsonEventString + ") is not a JSON object or an JSON object array");
218 return jsonObject2ApexEvent(eventName, jsonObject);
222 * This method converts a JSON object into an Apex event.
224 * @param eventName the name of the event
225 * @param jsonObject the JSON object that holds the event
226 * @return the apex event that we have converted the JSON object into
227 * @throws ApexEventException thrown on unmarshaling exceptions
229 private ApexEvent jsonObject2ApexEvent(final String eventName, final JsonObject jsonObject)
230 throws ApexEventException {
231 // Process the mandatory Apex header
232 final ApexEvent apexEvent = processApexEventHeader(eventName, jsonObject);
234 // Get the event definition for the event from the model service
235 final AxEvent eventDefinition =
236 ModelService.getModel(AxEvents.class).get(apexEvent.getName(), apexEvent.getVersion());
238 // Iterate over the input fields in the event
239 for (final AxField eventField : eventDefinition.getFields()) {
240 final String fieldName = eventField.getKey().getLocalName();
241 if (!hasJSONField(jsonObject, fieldName)) {
242 if (!eventField.getOptional()) {
243 final String errorMessage = "error parsing " + eventDefinition.getID() + " event from Json. "
244 + "Field \"" + fieldName + "\" is missing, but is mandatory.";
245 LOGGER.debug(errorMessage);
246 throw new ApexEventException(errorMessage);
251 final JsonElement fieldValue = getJSONField(jsonObject, fieldName, null, !eventField.getOptional());
253 if (fieldValue != null && !fieldValue.isJsonNull()) {
254 // Get the schema helper
255 final SchemaHelper fieldSchemaHelper =
256 new SchemaHelperFactory().createSchemaHelper(eventField.getKey(), eventField.getSchema());
257 apexEvent.put(fieldName, fieldSchemaHelper.createNewInstance(fieldValue));
259 apexEvent.put(fieldName, null);
267 * This method processes the event header of an Apex event.
269 * @param eventName the name of the event
270 * @param jsonObject the JSON object containing the JSON representation of the incoming event
271 * @return an apex event constructed using the header fields of the event
272 * @throws ApexEventRuntimeException the apex event runtime exception
273 * @throws ApexEventException on invalid events with missing header fields
275 private ApexEvent processApexEventHeader(final String eventName, final JsonObject jsonObject)
276 throws ApexEventRuntimeException, ApexEventException {
277 // Get the event header fields
279 String name = getJSONStringField(jsonObject, ApexEvent.NAME_HEADER_FIELD, jsonPars.getNameAlias(), ApexEvent.NAME_REGEXP, false);
280 String version = getJSONStringField(jsonObject, ApexEvent.VERSION_HEADER_FIELD, jsonPars.getVersionAlias(), ApexEvent.VERSION_REGEXP, false);
281 String namespace = getJSONStringField(jsonObject, ApexEvent.NAMESPACE_HEADER_FIELD, jsonPars.getNameSpaceAlias(), ApexEvent.NAMESPACE_REGEXP, false);
282 String source = getJSONStringField(jsonObject, ApexEvent.SOURCE_HEADER_FIELD, jsonPars.getSourceAlias(), ApexEvent.SOURCE_REGEXP, false);
283 String target = getJSONStringField(jsonObject, ApexEvent.TARGET_HEADER_FIELD, jsonPars.getTargetAlias(), ApexEvent.TARGET_REGEXP, false);
286 // Check if an event name was specified on the event parameters
287 if (eventName != null) {
288 if (name != null && !eventName.equals(name)) {
289 LOGGER.warn("The incoming event name \"" + name + "\" does not match the configured event name \""
290 + eventName + "\", using configured event name");
295 throw new ApexEventRuntimeException(
296 "event received without mandatory parameter \"name\" on configuration or on event");
300 // Now, find the event definition in the model service. If version is null, the newest event
301 // definition in the model service is used
302 final AxEvent eventDefinition = ModelService.getModel(AxEvents.class).get(name, version);
303 if (eventDefinition == null) {
304 if (version == null) {
305 throw new ApexEventRuntimeException(
306 "an event definition for an event named \"" + name + "\" not found in Apex model");
308 throw new ApexEventRuntimeException("an event definition for an event named \"" + name
309 + "\" with version \"" + version + "\" not found in Apex model");
313 // Use the defined event version if no version is specified on the incoming fields
314 if (version == null) {
315 version = eventDefinition.getKey().getVersion();
318 // Check the name space is OK if it is defined, if not, use the name space from the model
319 if (namespace != null) {
320 if (!namespace.equals(eventDefinition.getNameSpace())) {
321 throw new ApexEventRuntimeException(
322 "namespace \"" + namespace + "\" on event \"" + name + "\" does not match namespace \""
323 + eventDefinition.getNameSpace() + "\" for that event in the Apex model");
326 namespace = eventDefinition.getNameSpace();
329 // For source, use the defined source only if the source is not found on the incoming event
330 if (source == null) {
331 source = eventDefinition.getSource();
334 // For target, use the defined source only if the source is not found on the incoming event
335 if (target == null) {
336 target = eventDefinition.getTarget();
339 return new ApexEvent(name, version, namespace, source, target);
343 * This method gets an event string field from a JSON object.
345 * @param jsonObject the JSON object containing the JSON representation of the incoming event
346 * @param fieldName the field name to find in the event
347 * @param fieldAlias the alias for the field to find in the event, overrides the field name if
349 * @param fieldRE the regular expression to check the field against for validity
350 * @param mandatory true if the field is mandatory
351 * @return the value of the field in the JSON object or null if the field is optional
352 * @throws ApexEventRuntimeException the apex event runtime exception
354 private String getJSONStringField(final JsonObject jsonObject, final String fieldName, final String fieldAlias,
355 final String fieldRE, final boolean mandatory) throws ApexEventRuntimeException {
356 // Get the JSON field for the string field
357 final JsonElement jsonField = getJSONField(jsonObject, fieldName, fieldAlias, mandatory);
359 // Null strings are allowed
360 if (jsonField == null || jsonField.isJsonNull()) {
364 // Check if this is a string field
365 String fieldValueString = null;
367 fieldValueString = jsonField.getAsString();
368 } catch (final Exception e) {
369 // The element is not a string so throw an error
370 throw new ApexEventRuntimeException("field \"" + fieldName + "\" with type \""
371 + jsonField.getClass().getCanonicalName() + "\" is not a string value");
374 // Is regular expression checking required
375 if (fieldRE == null) {
376 return fieldValueString;
379 // Check the event field against its regular expression
380 if (!fieldValueString.matches(fieldRE)) {
381 throw new ApexEventRuntimeException(
382 "field \"" + fieldName + "\" with value \"" + fieldValueString + "\" is invalid");
385 return fieldValueString;
389 * This method gets an event field from a JSON object.
391 * @param jsonObject the JSON object containing the JSON representation of the incoming event
392 * @param fieldName the field name to find in the event
393 * @param fieldAlias the alias for the field to find in the event, overrides the field name if
395 * @param mandatory true if the field is mandatory
396 * @return the value of the field in the JSON object or null if the field is optional
397 * @throws ApexEventRuntimeException the apex event runtime exception
399 private JsonElement getJSONField(final JsonObject jsonObject, final String fieldName, final String fieldAlias,
400 final boolean mandatory) throws ApexEventRuntimeException {
402 // Check if we should use the alias for this field
403 String fieldToFind = fieldName;
404 if (fieldAlias != null) {
405 fieldToFind = fieldAlias;
408 // Get the event field
409 final JsonElement eventElement = jsonObject.get(fieldToFind);
410 if (eventElement == null) {
414 throw new ApexEventRuntimeException("mandatory field \"" + fieldToFind + "\" is missing");
422 * This method if a JSON object has a named field.
424 * @param jsonObject the JSON object containing the JSON representation of the incoming event
425 * @param fieldName the field name to find in the event
426 * @return true if the field is present
427 * @throws ApexEventRuntimeException the apex event runtime exception
429 private boolean hasJSONField(final JsonObject jsonObject, final String fieldName) throws ApexEventRuntimeException {
430 // check for the field
431 return jsonObject.has(fieldName);