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 com.google.gson.Gson;
24 import com.google.gson.GsonBuilder;
25 import com.google.gson.JsonElement;
26 import com.google.gson.JsonObject;
28 import java.util.ArrayList;
29 import java.util.List;
31 import org.onap.policy.apex.context.SchemaHelper;
32 import org.onap.policy.apex.context.impl.schema.SchemaHelperFactory;
33 import org.onap.policy.apex.model.basicmodel.service.ModelService;
34 import org.onap.policy.apex.model.eventmodel.concepts.AxEvent;
35 import org.onap.policy.apex.model.eventmodel.concepts.AxEvents;
36 import org.onap.policy.apex.model.eventmodel.concepts.AxField;
37 import org.onap.policy.apex.service.engine.event.ApexEvent;
38 import org.onap.policy.apex.service.engine.event.ApexEventException;
39 import org.onap.policy.apex.service.engine.event.ApexEventProtocolConverter;
40 import org.onap.policy.apex.service.engine.event.ApexEventRuntimeException;
41 import org.onap.policy.apex.service.parameters.eventprotocol.EventProtocolParameters;
42 import org.slf4j.ext.XLogger;
43 import org.slf4j.ext.XLoggerFactory;
46 * The Class Apex2JSONEventConverter converts {@link ApexEvent} instances to and from JSON string representations of
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;
60 * @see org.onap.policy.apex.service.engine.event.ApexEventProtocolConverter#init(org.onap.policy.
61 * apex.service.parameters.eventprotocol.EventProtocolParameters)
64 public void init(final EventProtocolParameters parameters) {
65 // Check and get the JSON parameters
66 if (!(parameters instanceof JsonEventProtocolParameters)) {
67 final String errorMessage = "specified consumer properties are not applicable to the JSON event protocol";
68 LOGGER.warn(errorMessage);
69 throw new ApexEventRuntimeException(errorMessage);
72 jsonPars = (JsonEventProtocolParameters) parameters;
78 * @see org.onap.policy.apex.service.engine.event.ApexEventConverter#toApexEvent(java.lang.String, java.lang.Object)
81 public List<ApexEvent> toApexEvent(final String eventName, final Object eventObject) throws ApexEventException {
82 // Check the event eventObject
83 if (eventObject == null) {
84 LOGGER.warn("event processing failed, event is null");
85 throw new ApexEventException("event processing failed, event is null");
88 // Cast the event to a string, if our conversion is correctly configured, this cast should
90 String jsonEventString = null;
92 jsonEventString = (String) eventObject;
93 } catch (final Exception e) {
94 final String errorMessage = "error converting event \"" + eventObject + "\" to a string";
95 LOGGER.debug(errorMessage, e);
96 throw new ApexEventRuntimeException(errorMessage, e);
99 // The list of events we will return
100 final List<ApexEvent> eventList = new ArrayList<>();
103 // We may have a single JSON object with a single event or an array of JSON objects
104 final Object decodedJsonObject = new GsonBuilder().serializeNulls().create().fromJson(jsonEventString,
107 // Check if we have a list of objects
108 if (decodedJsonObject instanceof List) {
109 // Check if it's a list of JSON objects or a list of strings
110 @SuppressWarnings("unchecked")
111 final List<Object> decodedJsonList = (List<Object>) decodedJsonObject;
113 // Decode each of the list elements in sequence
114 for (final Object jsonListObject : decodedJsonList) {
115 if (jsonListObject instanceof String) {
116 eventList.add(jsonStringApexEvent(eventName, (String) jsonListObject));
117 } else if (jsonListObject instanceof JsonObject) {
118 eventList.add(jsonObject2ApexEvent(eventName, (JsonObject) jsonListObject));
120 throw new ApexEventException("incoming event (" + jsonEventString
121 + ") is a JSON object array containing an invalid object " + jsonListObject);
125 eventList.add(jsonStringApexEvent(eventName, jsonEventString));
127 } catch (final Exception e) {
128 final String errorString = "Failed to unmarshal JSON event: " + e.getMessage() + ", event="
130 LOGGER.warn(errorString, e);
131 throw new ApexEventException(errorString, e);
134 // Return the list of events we have unmarshalled
141 * @see org.onap.policy.apex.service.engine.event.ApexEventConverter#fromApexEvent(org.onap.policy.
142 * apex.service.engine.event.ApexEvent)
145 public Object fromApexEvent(final ApexEvent apexEvent) throws ApexEventException {
146 // Check the Apex event
147 if (apexEvent == null) {
148 LOGGER.warn("event processing failed, Apex event is null");
149 throw new ApexEventException("event processing failed, Apex event is null");
152 // Get the event definition for the event from the model service
153 final AxEvent eventDefinition = ModelService.getModel(AxEvents.class).get(apexEvent.getName(),
154 apexEvent.getVersion());
156 // Use a GSON Json object to marshal the Apex event to JSON
157 final Gson gson = new GsonBuilder().serializeNulls().setPrettyPrinting().create();
158 final JsonObject jsonObject = new JsonObject();
160 jsonObject.addProperty(ApexEvent.NAME_HEADER_FIELD, apexEvent.getName());
161 jsonObject.addProperty(ApexEvent.VERSION_HEADER_FIELD, apexEvent.getVersion());
162 jsonObject.addProperty(ApexEvent.NAMESPACE_HEADER_FIELD, apexEvent.getNameSpace());
163 jsonObject.addProperty(ApexEvent.SOURCE_HEADER_FIELD, apexEvent.getSource());
164 jsonObject.addProperty(ApexEvent.TARGET_HEADER_FIELD, apexEvent.getTarget());
166 if (apexEvent.getExceptionMessage() != null) {
167 jsonObject.addProperty(ApexEvent.EXCEPTION_MESSAGE_HEADER_FIELD, apexEvent.getExceptionMessage());
170 for (final AxField eventField : eventDefinition.getFields()) {
171 final String fieldName = eventField.getKey().getLocalName();
173 if (!apexEvent.containsKey(fieldName)) {
174 if (!eventField.getOptional()) {
175 final String errorMessage = "error parsing " + eventDefinition.getId() + " event to Json. "
176 + "Field \"" + fieldName + "\" is missing, but is mandatory. Fields: " + apexEvent;
177 LOGGER.debug(errorMessage);
178 throw new ApexEventRuntimeException(errorMessage);
183 final Object fieldValue = apexEvent.get(fieldName);
185 // Get the schema helper
186 final SchemaHelper fieldSchemaHelper = new SchemaHelperFactory().createSchemaHelper(eventField.getKey(),
187 eventField.getSchema());
188 jsonObject.add(fieldName, (JsonElement) fieldSchemaHelper.marshal2Object(fieldValue));
191 // Output JSON string in a pretty format
192 return gson.toJson(jsonObject);
196 * This method converts a JSON object into an Apex event.
198 * @param eventName the name of the event
199 * @param jsonEventString the JSON string that holds the event
200 * @return the apex event that we have converted the JSON object into
201 * @throws ApexEventException thrown on unmarshaling exceptions
203 private ApexEvent jsonStringApexEvent(final String eventName, final String jsonEventString)
204 throws ApexEventException {
205 // Use GSON to read the event string
206 final JsonObject jsonObject = new GsonBuilder().serializeNulls().create().fromJson(jsonEventString,
209 if (jsonObject == null || !jsonObject.isJsonObject()) {
210 throw new ApexEventException(
211 "incoming event (" + jsonEventString + ") is not a JSON object or an JSON object array");
214 return jsonObject2ApexEvent(eventName, jsonObject);
218 * This method converts a JSON object into an Apex event.
220 * @param eventName the name of the event
221 * @param jsonObject the JSON object that holds the event
222 * @return the apex event that we have converted the JSON object into
223 * @throws ApexEventException thrown on unmarshaling exceptions
225 private ApexEvent jsonObject2ApexEvent(final String eventName, final JsonObject jsonObject)
226 throws ApexEventException {
227 // Process the mandatory Apex header
228 final ApexEvent apexEvent = processApexEventHeader(eventName, jsonObject);
230 // Get the event definition for the event from the model service
231 final AxEvent eventDefinition = ModelService.getModel(AxEvents.class).get(apexEvent.getName(),
232 apexEvent.getVersion());
234 // Iterate over the input fields in the event
235 for (final AxField eventField : eventDefinition.getFields()) {
236 final String fieldName = eventField.getKey().getLocalName();
237 if (!hasJsonField(jsonObject, fieldName)) {
238 if (!eventField.getOptional()) {
239 final String errorMessage = "error parsing " + eventDefinition.getId() + " event from Json. "
240 + "Field \"" + fieldName + "\" is missing, but is mandatory.";
241 LOGGER.debug(errorMessage);
242 throw new ApexEventException(errorMessage);
247 final JsonElement fieldValue = getJsonField(jsonObject, fieldName, null, !eventField.getOptional());
249 if (fieldValue != null && !fieldValue.isJsonNull()) {
250 // Get the schema helper
251 final SchemaHelper fieldSchemaHelper = new SchemaHelperFactory().createSchemaHelper(eventField.getKey(),
252 eventField.getSchema());
253 apexEvent.put(fieldName, fieldSchemaHelper.createNewInstance(fieldValue));
255 apexEvent.put(fieldName, null);
263 * This method processes the event header of an Apex event.
265 * @param eventName the name of the event
266 * @param jsonObject the JSON object containing the JSON representation of the incoming event
267 * @return an apex event constructed using the header fields of the event
268 * @throws ApexEventRuntimeException the apex event runtime exception
269 * @throws ApexEventException on invalid events with missing header fields
271 private ApexEvent processApexEventHeader(final String eventName, final JsonObject jsonObject)
272 throws ApexEventException {
273 String name = getJsonStringField(jsonObject, ApexEvent.NAME_HEADER_FIELD, jsonPars.getNameAlias(),
274 ApexEvent.NAME_REGEXP, false);
276 // Check that an event name has been specified
277 if (name == null && eventName == null) {
278 throw new ApexEventRuntimeException(
279 "event received without mandatory parameter \"name\" on configuration or on event");
282 // Check if an event name was specified on the event parameters
283 if (eventName != null) {
284 if (name != null && !eventName.equals(name)) {
285 LOGGER.warn("The incoming event name \"{}\" does not match the configured event name \"{}\","
286 + " using configured event name", name, eventName);
291 // Now, find the event definition in the model service. If version is null, the newest event
292 // definition in the model service is used
293 String version = getJsonStringField(jsonObject, ApexEvent.VERSION_HEADER_FIELD, jsonPars.getVersionAlias(),
294 ApexEvent.VERSION_REGEXP, false);
295 final AxEvent eventDefinition = ModelService.getModel(AxEvents.class).get(name, version);
296 if (eventDefinition == null) {
297 throwVersionException(name, version);
300 // Use the defined event version if no version is specified on the incoming fields
301 if (version == null) {
302 version = eventDefinition.getKey().getVersion();
305 // Check the name space is OK if it is defined, if not, use the name space from the model
306 String namespace = getJsonStringField(jsonObject, ApexEvent.NAMESPACE_HEADER_FIELD,
307 jsonPars.getNameSpaceAlias(), ApexEvent.NAMESPACE_REGEXP, false);
308 if (namespace != null) {
309 if (!namespace.equals(eventDefinition.getNameSpace())) {
310 throw new ApexEventRuntimeException("namespace \"" + namespace + "\" on event \"" + name
311 + "\" does not match namespace \"" + eventDefinition.getNameSpace()
312 + "\" for that event in the Apex model");
315 namespace = eventDefinition.getNameSpace();
318 // For source, use the defined source only if the source is not found on the incoming event
319 String source = getJsonStringField(jsonObject, ApexEvent.SOURCE_HEADER_FIELD, jsonPars.getSourceAlias(),
320 ApexEvent.SOURCE_REGEXP, false);
321 if (source == null) {
322 source = eventDefinition.getSource();
325 // For target, use the defined source only if the source is not found on the incoming event
326 String target = getJsonStringField(jsonObject, ApexEvent.TARGET_HEADER_FIELD, jsonPars.getTargetAlias(),
327 ApexEvent.TARGET_REGEXP, false);
328 if (target == null) {
329 target = eventDefinition.getTarget();
332 return new ApexEvent(name, version, namespace, source, target);
336 * Throw an exception on event name and/or version with the correct text.
337 * @param name The event name
338 * @param version The event version
340 private void throwVersionException(String name, String version) {
341 if (version == null) {
342 throw new ApexEventRuntimeException(
343 "an event definition for an event named \"" + name + "\" not found in Apex model");
346 throw new ApexEventRuntimeException("an event definition for an event named \"" + name
347 + "\" with version \"" + version + "\" not found in Apex model");
352 * This method gets an event string field from a JSON object.
354 * @param jsonObject the JSON object containing the JSON representation of the incoming event
355 * @param fieldName the field name to find in the event
356 * @param fieldAlias the alias for the field to find in the event, overrides the field name if it is not null
357 * @param fieldRegexp the regular expression to check the field against for validity
358 * @param mandatory true if the field is mandatory
359 * @return the value of the field in the JSON object or null if the field is optional
360 * @throws ApexEventRuntimeException the apex event runtime exception
362 private String getJsonStringField(final JsonObject jsonObject, final String fieldName, final String fieldAlias,
363 final String fieldRegexp, final boolean mandatory) {
364 // Get the JSON field for the string field
365 final JsonElement jsonField = getJsonField(jsonObject, fieldName, fieldAlias, mandatory);
367 // Null strings are allowed
368 if (jsonField == null || jsonField.isJsonNull()) {
372 // Check if this is a string field
373 String fieldValueString = null;
375 fieldValueString = jsonField.getAsString();
376 } catch (final Exception e) {
377 // The element is not a string so throw an error
378 throw new ApexEventRuntimeException("field \"" + fieldName + "\" with type \""
379 + jsonField.getClass().getCanonicalName() + "\" is not a string value", e);
382 // Is regular expression checking required
383 if (fieldRegexp == null) {
384 return fieldValueString;
387 // Check the event field against its regular expression
388 if (!fieldValueString.matches(fieldRegexp)) {
389 throw new ApexEventRuntimeException(
390 "field \"" + fieldName + "\" with value \"" + fieldValueString + "\" is invalid");
393 return fieldValueString;
397 * This method gets an event field from a JSON object.
399 * @param jsonObject the JSON object containing the JSON representation of the incoming event
400 * @param fieldName the field name to find in the event
401 * @param fieldAlias the alias for the field to find in the event, overrides the field name if it is not null
402 * @param mandatory true if the field is mandatory
403 * @return the value of the field in the JSON object or null if the field is optional
404 * @throws ApexEventRuntimeException the apex event runtime exception
406 private JsonElement getJsonField(final JsonObject jsonObject, final String fieldName, final String fieldAlias,
407 final boolean mandatory) {
409 // Check if we should use the alias for this field
410 String fieldToFind = fieldName;
411 if (fieldAlias != null) {
412 fieldToFind = fieldAlias;
415 // Get the event field
416 final JsonElement eventElement = jsonObject.get(fieldToFind);
417 if (eventElement == null) {
421 throw new ApexEventRuntimeException("mandatory field \"" + fieldToFind + "\" is missing");
429 * This method if a JSON object has a named field.
431 * @param jsonObject the JSON object containing the JSON representation of the incoming event
432 * @param fieldName the field name to find in the event
433 * @return true if the field is present
434 * @throws ApexEventRuntimeException the apex event runtime exception
436 private boolean hasJsonField(final JsonObject jsonObject, final String fieldName) {
437 // check for the field
438 return jsonObject.has(fieldName);