e8e592b091f229cdad507a10c987da4fd8003c87
[policy/apex-pdp.git] /
1 /*-
2  * ============LICENSE_START=======================================================
3  *  Copyright (C) 2016-2018 Ericsson. All rights reserved.
4  *  Modifications Copyright (C) 2019-2020 Nordix Foundation.
5  * ================================================================================
6  * Licensed under the Apache License, Version 2.0 (the "License");
7  * you may not use this file except in compliance with the License.
8  * You may obtain a copy of the License at
9  *
10  *      http://www.apache.org/licenses/LICENSE-2.0
11  *
12  * Unless required by applicable law or agreed to in writing, software
13  * distributed under the License is distributed on an "AS IS" BASIS,
14  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15  * See the License for the specific language governing permissions and
16  * limitations under the License.
17  *
18  * SPDX-License-Identifier: Apache-2.0
19  * ============LICENSE_END=========================================================
20  */
21
22 package org.onap.policy.apex.service.engine.event.impl.jsonprotocolplugin;
23
24 import com.google.gson.Gson;
25 import com.google.gson.GsonBuilder;
26 import com.google.gson.JsonElement;
27 import com.google.gson.JsonObject;
28 import com.google.gson.internal.LinkedTreeMap;
29 import java.util.ArrayList;
30 import java.util.Collection;
31 import java.util.List;
32 import org.onap.policy.apex.context.SchemaHelper;
33 import org.onap.policy.apex.context.impl.schema.SchemaHelperFactory;
34 import org.onap.policy.apex.model.basicmodel.service.ModelService;
35 import org.onap.policy.apex.model.eventmodel.concepts.AxEvent;
36 import org.onap.policy.apex.model.eventmodel.concepts.AxEvents;
37 import org.onap.policy.apex.model.eventmodel.concepts.AxField;
38 import org.onap.policy.apex.service.engine.event.ApexEvent;
39 import org.onap.policy.apex.service.engine.event.ApexEventException;
40 import org.onap.policy.apex.service.engine.event.ApexEventProtocolConverter;
41 import org.onap.policy.apex.service.engine.event.ApexEventRuntimeException;
42 import org.onap.policy.apex.service.parameters.eventprotocol.EventProtocolParameters;
43 import org.slf4j.ext.XLogger;
44 import org.slf4j.ext.XLoggerFactory;
45
46 /**
47  * The Class Apex2JSONEventConverter converts {@link ApexEvent} instances to and from JSON string representations of
48  * Apex events.
49  *
50  * @author Liam Fallon (liam.fallon@ericsson.com)
51  */
52 public class Apex2JsonEventConverter implements ApexEventProtocolConverter {
53     private static final XLogger LOGGER = XLoggerFactory.getXLogger(Apex2JsonEventConverter.class);
54
55     // Recurring string constants
56     private static final String ERROR_PARSING = "error parsing ";
57     private static final String ERROR_CODING = "error coding ";
58
59     // The parameters for the JSON event protocol
60     private JsonEventProtocolParameters jsonPars;
61
62     /**
63      * {@inheritDoc}.
64      */
65     @Override
66     public void init(final EventProtocolParameters parameters) {
67         // Check and get the JSON parameters
68         if (!(parameters instanceof JsonEventProtocolParameters)) {
69             final String errorMessage = "specified consumer properties are not applicable to the JSON event protocol";
70             LOGGER.warn(errorMessage);
71             throw new ApexEventRuntimeException(errorMessage);
72         }
73
74         jsonPars = (JsonEventProtocolParameters) parameters;
75     }
76
77     /**
78      * {@inheritDoc}.
79      */
80     @Override
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");
86         }
87
88         // Cast the event to a string, if our conversion is correctly configured, this cast should
89         // always work
90         String jsonEventString = null;
91         try {
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);
97         }
98
99         // The list of events we will return
100         final List<ApexEvent> eventList = new ArrayList<>();
101
102         try {
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,
105                             Object.class);
106
107             // Check if we have a list of objects
108             if (decodedJsonObject instanceof List) {
109                 eventList.addAll(decodeEventList(eventName, jsonEventString, decodedJsonObject));
110             } else {
111                 eventList.add(jsonStringApexEvent(eventName, jsonEventString));
112             }
113         } catch (final Exception e) {
114             throw new ApexEventException("Failed to unmarshal JSON event, event=" + jsonEventString, e);
115         }
116
117         // Return the list of events we have unmarshalled
118         return eventList;
119     }
120
121     /**
122      * Decode a list of Apex events.
123      *
124      * @param eventName the name of the incoming events
125      * @param jsonEventString the JSON representation of the event list
126      * @param decodedJsonObject The JSON list object
127      * @return a list of decoded Apex events
128      * @throws ApexEventException on event decoding errors
129      */
130     private Collection<? extends ApexEvent> decodeEventList(final String eventName, String jsonEventString,
131                     final Object decodedJsonObject) throws ApexEventException {
132
133         final List<ApexEvent> eventList = new ArrayList<>();
134
135         // Check if it's a list of JSON objects or a list of strings
136         @SuppressWarnings("unchecked")
137         final List<Object> decodedJsonList = (List<Object>) decodedJsonObject;
138
139         // Decode each of the list elements in sequence
140         for (final Object jsonListObject : decodedJsonList) {
141             if (jsonListObject instanceof String) {
142                 eventList.add(jsonStringApexEvent(eventName, (String) jsonListObject));
143             } else if (jsonListObject instanceof JsonObject) {
144                 eventList.add(jsonObject2ApexEvent(eventName, (JsonObject) jsonListObject));
145             } else if (jsonListObject instanceof LinkedTreeMap) {
146                 eventList.add(jsonObject2ApexEvent(eventName, new Gson().toJsonTree(jsonListObject).getAsJsonObject()));
147             } else {
148                 throw new ApexEventException("incoming event (" + jsonEventString
149                                 + ") is a JSON object array containing an invalid object " + jsonListObject);
150             }
151         }
152
153         return eventList;
154     }
155
156     /**
157      * {@inheritDoc}.
158      */
159     @Override
160     public Object fromApexEvent(final ApexEvent apexEvent) throws ApexEventException {
161         // Check the Apex event
162         if (apexEvent == null) {
163             LOGGER.warn("event processing failed, Apex event is null");
164             throw new ApexEventException("event processing failed, Apex event is null");
165         }
166
167         if (jsonPars.getPojoField() == null) {
168             return fromApexEventWithFields(apexEvent);
169         } else {
170             return fromApexEventPojo(apexEvent);
171         }
172     }
173
174     /**
175      * Serialise an Apex event to a JSON string field by field.
176      *
177      * @param apexEvent the event to Serialise
178      * @return the Serialise event as JSON
179      * @throws ApexEventException exceptions on marshaling to JSON
180      */
181     private Object fromApexEventWithFields(final ApexEvent apexEvent) {
182         // Get the event definition for the event from the model service
183         final AxEvent eventDefinition = ModelService.getModel(AxEvents.class).get(apexEvent.getName(),
184                         apexEvent.getVersion());
185
186         // Use a GSON Json object to marshal the Apex event to JSON
187         final Gson gson = new GsonBuilder().serializeNulls().setPrettyPrinting().create();
188         final JsonObject jsonObject = new JsonObject();
189
190         jsonObject.addProperty(ApexEvent.NAME_HEADER_FIELD, apexEvent.getName());
191         jsonObject.addProperty(ApexEvent.VERSION_HEADER_FIELD, apexEvent.getVersion());
192         jsonObject.addProperty(ApexEvent.NAMESPACE_HEADER_FIELD, apexEvent.getNameSpace());
193         jsonObject.addProperty(ApexEvent.SOURCE_HEADER_FIELD, apexEvent.getSource());
194         jsonObject.addProperty(ApexEvent.TARGET_HEADER_FIELD, apexEvent.getTarget());
195
196         if (apexEvent.getExceptionMessage() != null) {
197             jsonObject.addProperty(ApexEvent.EXCEPTION_MESSAGE_HEADER_FIELD, apexEvent.getExceptionMessage());
198         }
199
200         for (final AxField eventField : eventDefinition.getFields()) {
201             final String fieldName = eventField.getKey().getLocalName();
202
203             if (!apexEvent.containsKey(fieldName)) {
204                 if (!eventField.getOptional()) {
205                     final String errorMessage = ERROR_CODING + eventDefinition.getId() + " event to Json. " + "Field \""
206                                     + fieldName + "\" is missing, but is mandatory. Fields: " + apexEvent;
207                     LOGGER.debug(errorMessage);
208                     throw new ApexEventRuntimeException(errorMessage);
209                 }
210                 continue;
211             }
212
213             final Object fieldValue = apexEvent.get(fieldName);
214
215             // Get the schema helper
216             final SchemaHelper fieldSchemaHelper = new SchemaHelperFactory().createSchemaHelper(eventField.getKey(),
217                             eventField.getSchema());
218             jsonObject.add(fieldName, (JsonElement) fieldSchemaHelper.marshal2Object(fieldValue));
219         }
220
221         // Output JSON string in a pretty format
222         return gson.toJson(jsonObject);
223     }
224
225     /**
226      * Serialise an Apex event to a JSON string as a single POJO.
227      *
228      * @param apexEvent the event to Serialise
229      * @return the Serialise event as JSON
230      * @throws ApexEventException exceptions on marshaling to JSON
231      */
232     private Object fromApexEventPojo(ApexEvent apexEvent) throws ApexEventException {
233         // Get the event definition for the event from the model service
234         final AxEvent eventDefinition = ModelService.getModel(AxEvents.class).get(apexEvent.getName(),
235                         apexEvent.getVersion());
236
237         if (eventDefinition.getFields().isEmpty()) {
238             final String errorMessage = ERROR_CODING + eventDefinition.getId() + " event to Json, Field "
239                             + jsonPars.getPojoField() + " not found, no fields defined on event.";
240             LOGGER.debug(errorMessage);
241             throw new ApexEventException(errorMessage);
242         }
243
244         if (eventDefinition.getFields().size() != 1) {
245             final String errorMessage = ERROR_CODING + eventDefinition.getId() + " event to Json, Field "
246                             + jsonPars.getPojoField() + ", "
247                             + " one and only one field may be defined on a POJO event definition.";
248             LOGGER.debug(errorMessage);
249             throw new ApexEventException(errorMessage);
250         }
251
252         AxField pojoFieldDefinition = eventDefinition.getFields().iterator().next();
253
254         if (!jsonPars.getPojoField().equals(pojoFieldDefinition.getKey().getLocalName())) {
255             final String errorMessage = ERROR_CODING + eventDefinition.getId() + " event to Json. Field "
256                             + jsonPars.getPojoField() + " not found on POJO event definition.";
257             LOGGER.debug(errorMessage);
258             throw new ApexEventException(errorMessage);
259         }
260
261         final Object fieldValue = apexEvent.get(jsonPars.getPojoField());
262
263         // Get the schema helper
264         final SchemaHelper fieldSchemaHelper = new SchemaHelperFactory()
265                         .createSchemaHelper(pojoFieldDefinition.getKey(), pojoFieldDefinition.getSchema());
266
267         return fieldSchemaHelper.marshal2String(fieldValue);
268     }
269
270     /**
271      * This method converts a JSON object into an Apex event.
272      *
273      * @param eventName the name of the event
274      * @param jsonEventString the JSON string that holds the event
275      * @return the apex event that we have converted the JSON object into
276      * @throws ApexEventException thrown on unmarshaling exceptions
277      */
278     private ApexEvent jsonStringApexEvent(final String eventName, final String jsonEventString)
279                     throws ApexEventException {
280         // Use GSON to read the event string
281         final JsonObject jsonObject = new GsonBuilder().serializeNulls().create().fromJson(jsonEventString,
282                         JsonObject.class);
283
284         if (jsonObject == null || !jsonObject.isJsonObject()) {
285             throw new ApexEventException(
286                             "incoming event (" + jsonEventString + ") is not a JSON object or an JSON object array");
287         }
288
289         return jsonObject2ApexEvent(eventName, jsonObject);
290     }
291
292     /**
293      * This method converts a JSON object into an Apex event.
294      *
295      * @param eventName the name of the event
296      * @param jsonObject the JSON object that holds the event
297      * @return the apex event that we have converted the JSON object into
298      * @throws ApexEventException thrown on unmarshaling exceptions
299      */
300     private ApexEvent jsonObject2ApexEvent(final String eventName, final JsonObject jsonObject)
301                     throws ApexEventException {
302         // Process the mandatory Apex header
303         final ApexEvent apexEvent = processApexEventHeader(eventName, jsonObject);
304
305         // Get the event definition for the event from the model service
306         final AxEvent eventDefinition = ModelService.getModel(AxEvents.class).get(apexEvent.getName(),
307                         apexEvent.getVersion());
308
309         if (jsonPars.getPojoField() == null) {
310             jsonObject2ApexEventWithFields(jsonObject, apexEvent, eventDefinition);
311         } else {
312             jsonObject2ApexEventPojo(jsonObject, apexEvent, eventDefinition);
313         }
314
315         return apexEvent;
316     }
317
318     /**
319      * Decode an Apex event field by field.
320      *
321      * @param jsonObject the JSON representation of the event
322      * @param apexEvent the incoming event header
323      * @param eventDefinition the definition of the event from the model
324      * @throws ApexEventException on decode errors
325      */
326     private void jsonObject2ApexEventWithFields(final JsonObject jsonObject, final ApexEvent apexEvent,
327                     final AxEvent eventDefinition) throws ApexEventException {
328         // Iterate over the input fields in the event
329         for (final AxField eventField : eventDefinition.getFields()) {
330             final String fieldName = eventField.getKey().getLocalName();
331             if (!hasJsonField(jsonObject, fieldName)) {
332                 if (!eventField.getOptional()) {
333                     final String errorMessage = ERROR_PARSING + eventDefinition.getId() + " event from Json. "
334                                     + "Field \"" + fieldName + "\" is missing, but is mandatory.";
335                     LOGGER.debug(errorMessage);
336                     throw new ApexEventException(errorMessage);
337                 }
338                 continue;
339             }
340
341             final JsonElement fieldValue = getJsonField(jsonObject, fieldName, null, !eventField.getOptional());
342
343             if (fieldValue != null && !fieldValue.isJsonNull()) {
344                 // Get the schema helper
345                 final SchemaHelper fieldSchemaHelper = new SchemaHelperFactory().createSchemaHelper(eventField.getKey(),
346                                 eventField.getSchema());
347                 apexEvent.put(fieldName, fieldSchemaHelper.createNewInstance(fieldValue));
348             } else {
349                 apexEvent.put(fieldName, null);
350             }
351         }
352     }
353
354     /**
355      * Decode an Apex event as a single POJO.
356      *
357      * @param jsonObject the JSON representation of the event
358      * @param apexEvent the incoming event header
359      * @param eventDefinition the definition of the event from the model
360      * @throws ApexEventException on decode errors
361      */
362     private void jsonObject2ApexEventPojo(JsonObject jsonObject, ApexEvent apexEvent, AxEvent eventDefinition)
363                     throws ApexEventException {
364
365         if (eventDefinition.getFields().isEmpty()) {
366             final String errorMessage = ERROR_PARSING + eventDefinition.getId() + " event from Json, Field "
367                             + jsonPars.getPojoField() + " not found, no fields defined on event.";
368             LOGGER.debug(errorMessage);
369             throw new ApexEventException(errorMessage);
370         }
371
372         if (eventDefinition.getFields().size() != 1) {
373             final String errorMessage = ERROR_PARSING + eventDefinition.getId() + " event from Json, Field "
374                             + jsonPars.getPojoField()
375                             + ", one and only one field may be defined on a POJO event definition.";
376             LOGGER.debug(errorMessage);
377             throw new ApexEventException(errorMessage);
378         }
379
380         AxField pojoFieldDefinition = eventDefinition.getFields().iterator().next();
381
382         if (!jsonPars.getPojoField().equals(pojoFieldDefinition.getKey().getLocalName())) {
383             final String errorMessage = ERROR_PARSING + eventDefinition.getId() + " event from Json. Field "
384                             + jsonPars.getPojoField() + " not found on POJO event definition.";
385             LOGGER.debug(errorMessage);
386             throw new ApexEventException(errorMessage);
387         }
388
389         // Get the schema helper
390         final SchemaHelper fieldSchemaHelper = new SchemaHelperFactory()
391                         .createSchemaHelper(pojoFieldDefinition.getKey(), pojoFieldDefinition.getSchema());
392         apexEvent.put(jsonPars.getPojoField(), fieldSchemaHelper.createNewInstance(jsonObject));
393     }
394
395     /**
396      * This method processes the event header of an Apex event.
397      *
398      * @param parameterEventName the name of the event from the parameters
399      * @param jsonObject the JSON object containing the JSON representation of the incoming event
400      * @return an apex event constructed using the header fields of the event
401      * @throws ApexEventRuntimeException the apex event runtime exception
402      * @throws ApexEventException on invalid events with missing header fields
403      */
404     private ApexEvent processApexEventHeader(final String parameterEventName, final JsonObject jsonObject)
405                     throws ApexEventException {
406
407         final String eventName = getHeaderName(jsonObject, parameterEventName);
408
409         String eventVersion = getHeaderVersion(jsonObject);
410
411         final AxEvent eventDefinition = ModelService.getModel(AxEvents.class).get(eventName, eventVersion);
412
413         if (eventDefinition == null) {
414             if (eventVersion == null) {
415                 throw new ApexEventRuntimeException(
416                                 "an event definition for an event named \"" + eventName + "\" not found in Apex model");
417             } else {
418                 throw new ApexEventRuntimeException("an event definition for an event named \"" + eventName
419                                 + "\" with version \"" + eventVersion + "\" not found in Apex model");
420             }
421         }
422
423         // Use the defined event version if no version is specified on the incoming fields
424         if (eventVersion == null) {
425             eventVersion = eventDefinition.getKey().getVersion();
426         }
427
428         final String eventNamespace = getHeaderNamespace(jsonObject, eventName, eventDefinition);
429         final String eventSource = getHeaderSource(jsonObject, eventDefinition);
430         final String eventTarget = getHeaderTarget(jsonObject, eventDefinition);
431
432         return new ApexEvent(eventName, eventVersion, eventNamespace, eventSource, eventTarget);
433     }
434
435     /**
436      * Determine the name field of the event header.
437      *
438      * @param jsonObject the event in JSON format
439      * @param parameterEventName the configured event name from the parameters
440      * @return the event name to use on the event header
441      */
442     private String getHeaderName(final JsonObject jsonObject, final String parameterEventName) {
443         final String jsonEventName = getJsonStringField(jsonObject, ApexEvent.NAME_HEADER_FIELD,
444                         jsonPars.getNameAlias(), ApexEvent.NAME_REGEXP, false);
445
446         // Check that an event name has been specified
447         if (jsonEventName == null && parameterEventName == null) {
448             throw new ApexEventRuntimeException(
449                             "event received without mandatory parameter \"name\" on configuration or on event");
450         }
451
452         // Check if an event name was specified on the event parameters
453         if (jsonEventName != null) {
454             if (parameterEventName != null && !parameterEventName.equals(jsonEventName)) {
455                 LOGGER.warn("The incoming event name \"{}\" does not match the configured event name \"{}\","
456                                 + " using configured event name", jsonEventName, parameterEventName);
457             }
458             return jsonEventName;
459         } else {
460             return parameterEventName;
461         }
462     }
463
464     /**
465      * Determine the version field of the event header.
466      *
467      * @param jsonObject the event in JSON format
468      * @return the event version
469      */
470     private String getHeaderVersion(final JsonObject jsonObject) {
471         // Find the event definition in the model service. If version is null, the newest event
472         // definition in the model service is used
473         return getJsonStringField(jsonObject, ApexEvent.VERSION_HEADER_FIELD, jsonPars.getVersionAlias(),
474                         ApexEvent.VERSION_REGEXP, false);
475     }
476
477     /**
478      * Determine the name space field of the event header.
479      *
480      * @param jsonObject the event in JSON format
481      * @param eventName the name of the event
482      * @param eventDefinition the definition of the event structure
483      * @return the event version
484      */
485     private String getHeaderNamespace(final JsonObject jsonObject, final String name, final AxEvent eventDefinition) {
486         // Check the name space is OK if it is defined, if not, use the name space from the model
487         String namespace = getJsonStringField(jsonObject, ApexEvent.NAMESPACE_HEADER_FIELD,
488                         jsonPars.getNameSpaceAlias(), ApexEvent.NAMESPACE_REGEXP, false);
489         if (namespace != null) {
490             if (!namespace.equals(eventDefinition.getNameSpace())) {
491                 throw new ApexEventRuntimeException("namespace \"" + namespace + "\" on event \"" + name
492                                 + "\" does not match namespace \"" + eventDefinition.getNameSpace()
493                                 + "\" for that event in the Apex model");
494             }
495         } else {
496             namespace = eventDefinition.getNameSpace();
497         }
498         return namespace;
499     }
500
501     /**
502      * Determine the source field of the event header.
503      *
504      * @param jsonObject the event in JSON format
505      * @param eventDefinition the definition of the event structure
506      * @return the event version
507      */
508     private String getHeaderSource(final JsonObject jsonObject, final AxEvent eventDefinition) {
509         // For source, use the defined source only if the source is not found on the incoming event
510         String source = getJsonStringField(jsonObject, ApexEvent.SOURCE_HEADER_FIELD, jsonPars.getSourceAlias(),
511                         ApexEvent.SOURCE_REGEXP, false);
512         if (source == null) {
513             source = eventDefinition.getSource();
514         }
515         return source;
516     }
517
518     /**
519      * Determine the target field of the event header.
520      *
521      * @param jsonObject the event in JSON format
522      * @param eventDefinition the definition of the event structure
523      * @return the event version
524      */
525     private String getHeaderTarget(final JsonObject jsonObject, final AxEvent eventDefinition) {
526         // For target, use the defined source only if the source is not found on the incoming event
527         String target = getJsonStringField(jsonObject, ApexEvent.TARGET_HEADER_FIELD, jsonPars.getTargetAlias(),
528                         ApexEvent.TARGET_REGEXP, false);
529         if (target == null) {
530             target = eventDefinition.getTarget();
531         }
532         return target;
533     }
534
535     /**
536      * This method gets an event string field from a JSON object.
537      *
538      * @param jsonObject the JSON object containing the JSON representation of the incoming event
539      * @param fieldName the field name to find in the event
540      * @param fieldAlias the alias for the field to find in the event, overrides the field name if it is not null
541      * @param fieldRegexp the regular expression to check the field against for validity
542      * @param mandatory true if the field is mandatory
543      * @return the value of the field in the JSON object or null if the field is optional
544      * @throws ApexEventRuntimeException the apex event runtime exception
545      */
546     private String getJsonStringField(final JsonObject jsonObject, final String fieldName, final String fieldAlias,
547                     final String fieldRegexp, final boolean mandatory) {
548         // Get the JSON field for the string field
549         final JsonElement jsonField = getJsonField(jsonObject, fieldName, fieldAlias, mandatory);
550
551         // Null strings are allowed
552         if (jsonField == null || jsonField.isJsonNull()) {
553             return null;
554         }
555
556         // Check if this is a string field
557         String fieldValueString = null;
558         try {
559             fieldValueString = jsonField.getAsString();
560         } catch (final Exception e) {
561             // The element is not a string so throw an error
562             throw new ApexEventRuntimeException("field \"" + fieldName + "\" with type \""
563                             + jsonField.getClass().getName() + "\" is not a string value", e);
564         }
565
566         // Is regular expression checking required
567         if (fieldRegexp == null) {
568             return fieldValueString;
569         }
570
571         // Check the event field against its regular expression
572         if (!fieldValueString.matches(fieldRegexp)) {
573             throw new ApexEventRuntimeException(
574                             "field \"" + fieldName + "\" with value \"" + fieldValueString + "\" is invalid");
575         }
576
577         return fieldValueString;
578     }
579
580     /**
581      * This method gets an event field from a JSON object.
582      *
583      * @param jsonObject the JSON object containing the JSON representation of the incoming event
584      * @param fieldName the field name to find in the event
585      * @param fieldAlias the alias for the field to find in the event, overrides the field name if it is not null
586      * @param mandatory true if the field is mandatory
587      * @return the value of the field in the JSON object or null if the field is optional
588      * @throws ApexEventRuntimeException the apex event runtime exception
589      */
590     private JsonElement getJsonField(final JsonObject jsonObject, final String fieldName, final String fieldAlias,
591                     final boolean mandatory) {
592
593         // Check if we should use the alias for this field
594         String fieldToFind = fieldName;
595         if (fieldAlias != null) {
596             fieldToFind = fieldAlias;
597         }
598
599         // Get the event field
600         final JsonElement eventElement = jsonObject.get(fieldToFind);
601         if (eventElement == null) {
602             if (!mandatory) {
603                 return null;
604             } else {
605                 throw new ApexEventRuntimeException("mandatory field \"" + fieldToFind + "\" is missing");
606             }
607         }
608
609         return eventElement;
610     }
611
612     /**
613      * This method if a JSON object has a named field.
614      *
615      * @param jsonObject the JSON object containing the JSON representation of the incoming event
616      * @param fieldName the field name to find in the event
617      * @return true if the field is present
618      * @throws ApexEventRuntimeException the apex event runtime exception
619      */
620     private boolean hasJsonField(final JsonObject jsonObject, final String fieldName) {
621         // check for the field
622         return jsonObject.has(fieldName);
623     }
624 }