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