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