2 * ============LICENSE_START=======================================================
3 * Copyright (C) 2016-2018 Ericsson. All rights reserved.
4 * ================================================================================
5 * Licensed under the Apache License, Version 2.0 (the "License");
6 * you may not use this file except in compliance with the License.
7 * You may obtain a copy of the License at
9 * http://www.apache.org/licenses/LICENSE-2.0
11 * Unless required by applicable law or agreed to in writing, software
12 * distributed under the License is distributed on an "AS IS" BASIS,
13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 * See the License for the specific language governing permissions and
15 * limitations under the License.
17 * SPDX-License-Identifier: Apache-2.0
18 * ============LICENSE_END=========================================================
21 package org.onap.policy.apex.plugins.event.protocol.yaml;
23 import java.util.ArrayList;
24 import java.util.LinkedHashMap;
25 import java.util.List;
28 import org.onap.policy.apex.context.SchemaHelper;
29 import org.onap.policy.apex.context.impl.schema.SchemaHelperFactory;
30 import org.onap.policy.apex.model.basicmodel.service.ModelService;
31 import org.onap.policy.apex.model.eventmodel.concepts.AxEvent;
32 import org.onap.policy.apex.model.eventmodel.concepts.AxEvents;
33 import org.onap.policy.apex.model.eventmodel.concepts.AxField;
34 import org.onap.policy.apex.service.engine.event.ApexEvent;
35 import org.onap.policy.apex.service.engine.event.ApexEventException;
36 import org.onap.policy.apex.service.engine.event.ApexEventProtocolConverter;
37 import org.onap.policy.apex.service.engine.event.ApexEventRuntimeException;
38 import org.onap.policy.apex.service.parameters.eventprotocol.EventProtocolParameters;
39 import org.slf4j.ext.XLogger;
40 import org.slf4j.ext.XLoggerFactory;
41 import org.yaml.snakeyaml.Yaml;
43 import org.yaml.snakeyaml.DumperOptions.FlowStyle;
46 * The Class Apex2YamlEventConverter converts {@link ApexEvent} instances to and from YAML string representations of
49 * @author Liam Fallon (liam.fallon@ericsson.com)
51 public class Apex2YamlEventConverter implements ApexEventProtocolConverter {
52 private static final XLogger LOGGER = XLoggerFactory.getXLogger(Apex2YamlEventConverter.class);
54 // The parameters for the YAML event protocol
55 private YamlEventProtocolParameters yamlPars;
60 * @see org.onap.policy.apex.service.engine.event.ApexEventProtocolConverter#init(org.onap.policy.
61 * apex.service.parameters.eventprotocol.EventProtocolParameters)
64 public void init(final EventProtocolParameters parameters) {
65 // Check and get the YAML parameters
66 if (!(parameters instanceof YamlEventProtocolParameters)) {
67 final String errorMessage = "specified consumer properties are not applicable to the YAML event protocol";
68 LOGGER.warn(errorMessage);
69 throw new ApexEventRuntimeException(errorMessage);
72 yamlPars = (YamlEventProtocolParameters) parameters;
78 * @see org.onap.policy.apex.service.engine.event.ApexEventConverter#toApexEvent(java.lang.String, java.lang.Object)
81 public List<ApexEvent> toApexEvent(final String eventName, final Object eventObject) throws ApexEventException {
82 // Check the event eventObject
83 if (eventObject == null) {
84 LOGGER.warn("event processing failed, event is null");
85 throw new ApexEventException("event processing failed, event is null");
88 // Cast the event to a string, if our conversion is correctly configured, this cast should
90 if (!(eventObject instanceof String)) {
91 final String errorMessage = "error converting event \"" + eventObject + "\" to a string";
92 LOGGER.debug(errorMessage);
93 throw new ApexEventException(errorMessage);
96 final String yamlEventString = (String) eventObject;
98 // The list of events we will return
99 final List<ApexEvent> eventList = new ArrayList<>();
101 // Convert the YAML document string into an object
102 Object yamlObject = new Yaml().load(yamlEventString);
104 // If the incoming YAML did not create a map it is a primitive type or a collection so we
105 // convert it into a map for processing
107 if (yamlObject != null && yamlObject instanceof Map) {
108 // We already have a map so just cast the object
109 yamlMap = (Map<?, ?>) yamlObject;
112 // Create a single entry map, new map creation and assignment is to avoid a
113 // type checking warning
114 LinkedHashMap<String, Object> newYamlMap = new LinkedHashMap<>();
115 newYamlMap.put(yamlPars.getYamlFieldName(), yamlObject);
116 yamlMap = newYamlMap;
120 eventList.add(yamlMap2ApexEvent(eventName, yamlMap));
121 } catch (final Exception e) {
122 final String errorString = "Failed to unmarshal YAML event: " + e.getMessage() + ", event="
124 LOGGER.warn(errorString, e);
125 throw new ApexEventException(errorString, e);
128 // Return the list of events we have unmarshalled
135 * @see org.onap.policy.apex.service.engine.event.ApexEventConverter#fromApexEvent(org.onap.policy.
136 * apex.service.engine.event.ApexEvent)
139 public Object fromApexEvent(final ApexEvent apexEvent) throws ApexEventException {
140 // Check the Apex event
141 if (apexEvent == null) {
142 LOGGER.warn("event processing failed, Apex event is null");
143 throw new ApexEventException("event processing failed, Apex event is null");
146 // Get the event definition for the event from the model service
147 final AxEvent eventDefinition = ModelService.getModel(AxEvents.class).get(apexEvent.getName(),
148 apexEvent.getVersion());
150 // Create a map for output of the APEX event to YAML
151 LinkedHashMap<String, Object> yamlMap = new LinkedHashMap<>();
153 yamlMap.put(ApexEvent.NAME_HEADER_FIELD, apexEvent.getName());
154 yamlMap.put(ApexEvent.VERSION_HEADER_FIELD, apexEvent.getVersion());
155 yamlMap.put(ApexEvent.NAMESPACE_HEADER_FIELD, apexEvent.getNameSpace());
156 yamlMap.put(ApexEvent.SOURCE_HEADER_FIELD, apexEvent.getSource());
157 yamlMap.put(ApexEvent.TARGET_HEADER_FIELD, apexEvent.getTarget());
159 if (apexEvent.getExceptionMessage() != null) {
160 yamlMap.put(ApexEvent.EXCEPTION_MESSAGE_HEADER_FIELD, apexEvent.getExceptionMessage());
163 for (final AxField eventField : eventDefinition.getFields()) {
164 final String fieldName = eventField.getKey().getLocalName();
166 if (!apexEvent.containsKey(fieldName)) {
167 if (!eventField.getOptional()) {
168 final String errorMessage = "error parsing " + eventDefinition.getId() + " event to Json. "
169 + "Field \"" + fieldName + "\" is missing, but is mandatory. Fields: " + apexEvent;
170 LOGGER.debug(errorMessage);
171 throw new ApexEventRuntimeException(errorMessage);
176 yamlMap.put(fieldName, apexEvent.get(fieldName));
179 // Use Snake YAML to convert the APEX event to YAML
180 Yaml yaml = new Yaml();
181 return yaml.dumpAs(yamlMap, null, FlowStyle.BLOCK);
185 * This method converts a YAML map into an Apex event.
187 * @param eventName the name of the event
188 * @param yamlMap the YAML map that holds the event
189 * @return the apex event that we have converted the JSON object into
190 * @throws ApexEventException
191 * thrown on unmarshaling exceptions
193 private ApexEvent yamlMap2ApexEvent(final String eventName, final Map<?, ?> yamlMap)
194 throws ApexEventException {
195 // Process the mandatory Apex header
196 final ApexEvent apexEvent = processApexEventHeader(eventName, yamlMap);
198 // Get the event definition for the event from the model service
199 final AxEvent eventDefinition = ModelService.getModel(AxEvents.class).get(apexEvent.getName(),
200 apexEvent.getVersion());
202 // Iterate over the input fields in the event
203 for (final AxField eventField : eventDefinition.getFields()) {
204 final String fieldName = eventField.getKey().getLocalName();
205 if (!yamlMap.containsKey(fieldName)) {
206 if (!eventField.getOptional()) {
207 final String errorMessage = "error parsing " + eventDefinition.getId() + " event from Json. "
208 + "Field \"" + fieldName + "\" is missing, but is mandatory.";
209 LOGGER.debug(errorMessage);
210 throw new ApexEventException(errorMessage);
215 final Object fieldValue = getYamlField(yamlMap, fieldName, null, !eventField.getOptional());
217 if (fieldValue != null) {
218 // Get the schema helper
219 final SchemaHelper fieldSchemaHelper = new SchemaHelperFactory().createSchemaHelper(eventField.getKey(),
220 eventField.getSchema());
221 apexEvent.put(fieldName, fieldSchemaHelper.createNewInstance(fieldValue));
223 apexEvent.put(fieldName, null);
231 * This method processes the event header of an Apex event.
233 * @param eventName the name of the event
234 * @param yamlMap the YAML map that holds the event
235 * @return an apex event constructed using the header fields of the event
236 * @throws ApexEventRuntimeException the apex event runtime exception
237 * @throws ApexEventException on invalid events with missing header fields
239 private ApexEvent processApexEventHeader(final String eventName, final Map<?, ?> yamlMap)
240 throws ApexEventException {
241 // Get the event header fields
243 String name = getYamlStringField(yamlMap, ApexEvent.NAME_HEADER_FIELD, yamlPars.getNameAlias(), ApexEvent.NAME_REGEXP, false);
244 String version = getYamlStringField(yamlMap, ApexEvent.VERSION_HEADER_FIELD, yamlPars.getVersionAlias(), ApexEvent.VERSION_REGEXP, false);
245 String namespace = getYamlStringField(yamlMap, ApexEvent.NAMESPACE_HEADER_FIELD, yamlPars.getNameSpaceAlias(), ApexEvent.NAMESPACE_REGEXP, false);
246 String source = getYamlStringField(yamlMap, ApexEvent.SOURCE_HEADER_FIELD, yamlPars.getSourceAlias(), ApexEvent.SOURCE_REGEXP, false);
247 String target = getYamlStringField(yamlMap, ApexEvent.TARGET_HEADER_FIELD, yamlPars.getTargetAlias(), ApexEvent.TARGET_REGEXP, false);
250 // Check that an event name has been specified
251 if (name == null && eventName == null) {
252 throw new ApexEventRuntimeException(
253 "event received without mandatory parameter \"name\" on configuration or on event");
256 // Check if an event name was specified on the event parameters
257 if (eventName != null) {
258 if (name != null && !eventName.equals(name)) {
259 LOGGER.warn("The incoming event name \"{}\" does not match the configured event name \"{}\", using configured event name",
265 // Now, find the event definition in the model service. If version is null, the newest event
266 // definition in the model service is used
267 final AxEvent eventDefinition = ModelService.getModel(AxEvents.class).get(name, version);
268 if (eventDefinition == null) {
269 throw new ApexEventRuntimeException("an event definition for an event named \"" + name
270 + "\" with version \"" + version + "\" not found in Apex model");
273 // Use the defined event version if no version is specified on the incoming fields
274 if (version == null) {
275 version = eventDefinition.getKey().getVersion();
278 // Check the name space is OK if it is defined, if not, use the name space from the model
279 if (namespace != null) {
280 if (!namespace.equals(eventDefinition.getNameSpace())) {
281 throw new ApexEventRuntimeException("namespace \"" + namespace + "\" on event \"" + name
282 + "\" does not match namespace \"" + eventDefinition.getNameSpace()
283 + "\" for that event in the Apex model");
286 namespace = eventDefinition.getNameSpace();
289 // For source, use the defined source only if the source is not found on the incoming event
290 if (source == null) {
291 source = eventDefinition.getSource();
294 // For target, use the defined source only if the source is not found on the incoming event
295 if (target == null) {
296 target = eventDefinition.getTarget();
299 return new ApexEvent(name, version, namespace, source, target);
303 * This method gets an event string field from a JSON object.
306 * the YAML containing the YAML representation of the incoming event
308 * the field name to find in the event
310 * the alias for the field to find in the event, overrides the field name if it is not null
312 * the regular expression to check the field against for validity
314 * true if the field is mandatory
315 * @return the value of the field in the JSON object or null if the field is optional
316 * @throws ApexEventRuntimeException
317 * the apex event runtime exception
319 private String getYamlStringField(final Map<?, ?> yamlMap, final String fieldName, final String fieldAlias,
320 final String fieldRE, final boolean mandatory) {
321 // Get the YAML field for the string field
322 final Object yamlField = getYamlField(yamlMap, fieldName, fieldAlias, mandatory);
324 // Null strings are allowed
325 if (yamlField == null) {
329 if (!(yamlField instanceof String)) {
330 // The element is not a string so throw an error
331 throw new ApexEventRuntimeException("field \"" + fieldName + "\" with type \""
332 + yamlField.getClass().getCanonicalName() + "\" is not a string value");
335 final String fieldValueString = (String) yamlField;
337 // Is regular expression checking required
338 if (fieldRE == null) {
339 return fieldValueString;
342 // Check the event field against its regular expression
343 if (!fieldValueString.matches(fieldRE)) {
344 throw new ApexEventRuntimeException(
345 "field \"" + fieldName + "\" with value \"" + fieldValueString + "\" is invalid");
348 return fieldValueString;
352 * This method gets an event field from a YAML object.
355 * the YAML containing the YAML representation of the incoming event
357 * the field name to find in the event
359 * the alias for the field to find in the event, overrides the field name if it is not null
361 * true if the field is mandatory
362 * @return the value of the field in the YAML object or null if the field is optional
363 * @throws ApexEventRuntimeException
364 * the apex event runtime exception
366 private Object getYamlField(final Map<?, ?> yamlMap, final String fieldName, final String fieldAlias,
367 final boolean mandatory) {
369 // Check if we should use the alias for this field
370 String fieldToFind = fieldName;
371 if (fieldAlias != null) {
372 fieldToFind = fieldAlias;
375 // Get the event field
376 final Object eventElement = yamlMap.get(fieldToFind);
377 if (eventElement == null) {
381 throw new ApexEventRuntimeException("mandatory field \"" + fieldToFind + "\" is missing");