diff options
author | liamfallon <liam.fallon@ericsson.com> | 2018-06-13 16:50:59 +0100 |
---|---|---|
committer | liamfallon <liam.fallon@ericsson.com> | 2018-06-14 11:22:00 +0100 |
commit | 4fcf04234e5e1b1d4338f12d982a45edf317d795 (patch) | |
tree | a17fb2a9dd70064145312b75c9d1019086cb11c1 /plugins/plugins-event/plugins-event-protocol/plugins-event-protocol-yaml/src/main/java | |
parent | 2e1da7176952e013276ef29638a313f3a1b49c00 (diff) |
Add YAML plugin and cleanup for checkstyle
Bringing in the APEX YAML plugin, allows events with YAML bodies
to be consumed by APEX.
Also clean up of event protocol interface to make it not specific
for JSON.
Also some checkstyle changes on classes impacted by event
protocol interface change.
Change-Id: I7c3867ac508096fd8acad2488e61db87dfa1d6bd
Issue-ID: POLICY-862
Signed-off-by: liamfallon <liam.fallon@ericsson.com>
Diffstat (limited to 'plugins/plugins-event/plugins-event-protocol/plugins-event-protocol-yaml/src/main/java')
3 files changed, 618 insertions, 0 deletions
diff --git a/plugins/plugins-event/plugins-event-protocol/plugins-event-protocol-yaml/src/main/java/org/onap/policy/apex/plugins/event/protocol/yaml/Apex2YamlEventConverter.java b/plugins/plugins-event/plugins-event-protocol/plugins-event-protocol-yaml/src/main/java/org/onap/policy/apex/plugins/event/protocol/yaml/Apex2YamlEventConverter.java new file mode 100644 index 000000000..689b1e730 --- /dev/null +++ b/plugins/plugins-event/plugins-event-protocol/plugins-event-protocol-yaml/src/main/java/org/onap/policy/apex/plugins/event/protocol/yaml/Apex2YamlEventConverter.java @@ -0,0 +1,387 @@ +/*- + * ============LICENSE_START======================================================= + * Copyright (C) 2016-2018 Ericsson. All rights reserved. + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * SPDX-License-Identifier: Apache-2.0 + * ============LICENSE_END========================================================= + */ + +package org.onap.policy.apex.plugins.event.protocol.yaml; + +import java.util.ArrayList; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; + +import org.onap.policy.apex.context.SchemaHelper; +import org.onap.policy.apex.context.impl.schema.SchemaHelperFactory; +import org.onap.policy.apex.model.basicmodel.service.ModelService; +import org.onap.policy.apex.model.eventmodel.concepts.AxEvent; +import org.onap.policy.apex.model.eventmodel.concepts.AxEvents; +import org.onap.policy.apex.model.eventmodel.concepts.AxField; +import org.onap.policy.apex.service.engine.event.ApexEvent; +import org.onap.policy.apex.service.engine.event.ApexEventException; +import org.onap.policy.apex.service.engine.event.ApexEventProtocolConverter; +import org.onap.policy.apex.service.engine.event.ApexEventRuntimeException; +import org.onap.policy.apex.service.parameters.eventprotocol.EventProtocolParameters; +import org.slf4j.ext.XLogger; +import org.slf4j.ext.XLoggerFactory; +import org.yaml.snakeyaml.Yaml; + +import org.yaml.snakeyaml.DumperOptions.FlowStyle; + +/** + * The Class Apex2YamlEventConverter converts {@link ApexEvent} instances to and from YAML string representations of + * Apex events. + * + * @author Liam Fallon (liam.fallon@ericsson.com) + */ +public class Apex2YamlEventConverter implements ApexEventProtocolConverter { + private static final XLogger LOGGER = XLoggerFactory.getXLogger(Apex2YamlEventConverter.class); + + // The parameters for the YAML event protocol + private YamlEventProtocolParameters yamlPars; + + /* + * (non-Javadoc) + * + * @see org.onap.policy.apex.service.engine.event.ApexEventProtocolConverter#init(org.onap.policy. + * apex.service.parameters.eventprotocol.EventProtocolParameters) + */ + @Override + public void init(final EventProtocolParameters parameters) { + // Check and get the YAML parameters + if (!(parameters instanceof YamlEventProtocolParameters)) { + final String errorMessage = "specified consumer properties are not applicable to the YAML event protocol"; + LOGGER.warn(errorMessage); + throw new ApexEventRuntimeException(errorMessage); + } + + yamlPars = (YamlEventProtocolParameters) parameters; + } + + /* + * (non-Javadoc) + * + * @see org.onap.policy.apex.service.engine.event.ApexEventConverter#toApexEvent(java.lang.String, java.lang.Object) + */ + @Override + public List<ApexEvent> toApexEvent(final String eventName, final Object eventObject) throws ApexEventException { + // Check the event eventObject + if (eventObject == null) { + LOGGER.warn("event processing failed, event is null"); + throw new ApexEventException("event processing failed, event is null"); + } + + // Cast the event to a string, if our conversion is correctly configured, this cast should + // always work + if (!(eventObject instanceof String)) { + final String errorMessage = "error converting event \"" + eventObject + "\" to a string"; + LOGGER.debug(errorMessage); + throw new ApexEventException(errorMessage); + } + + final String yamlEventString = (String) eventObject; + + // The list of events we will return + final List<ApexEvent> eventList = new ArrayList<>(); + + // Convert the YAML document string into an object + Object yamlObject = new Yaml().load(yamlEventString); + + // If the incoming YAML did not create a map it is a primitive type or a collection so we + // convert it into a map for processing + Map<?, ?> yamlMap; + if (yamlObject != null && yamlObject instanceof Map) { + // We already have a map so just cast the object + yamlMap = (Map<?, ?>) yamlObject; + } + else { + // Create a single entry map, new map creation and assignment is to avoid a + // type checking warning + LinkedHashMap<String, Object> newYamlMap = new LinkedHashMap<>(); + newYamlMap.put(yamlPars.getYamlFieldName(), yamlObject); + yamlMap = newYamlMap; + } + + try { + eventList.add(yamlMap2ApexEvent(eventName, yamlMap)); + } catch (final Exception e) { + final String errorString = "Failed to unmarshal YAML event: " + e.getMessage() + ", event=" + + yamlEventString; + LOGGER.warn(errorString, e); + throw new ApexEventException(errorString, e); + } + + // Return the list of events we have unmarshalled + return eventList; + } + + /* + * (non-Javadoc) + * + * @see org.onap.policy.apex.service.engine.event.ApexEventConverter#fromApexEvent(org.onap.policy. + * apex.service.engine.event.ApexEvent) + */ + @Override + public Object fromApexEvent(final ApexEvent apexEvent) throws ApexEventException { + // Check the Apex event + if (apexEvent == null) { + LOGGER.warn("event processing failed, Apex event is null"); + throw new ApexEventException("event processing failed, Apex event is null"); + } + + // Get the event definition for the event from the model service + final AxEvent eventDefinition = ModelService.getModel(AxEvents.class).get(apexEvent.getName(), + apexEvent.getVersion()); + + // Create a map for output of the APEX event to YAML + LinkedHashMap<String, Object> yamlMap = new LinkedHashMap<>(); + + yamlMap.put(ApexEvent.NAME_HEADER_FIELD, apexEvent.getName()); + yamlMap.put(ApexEvent.VERSION_HEADER_FIELD, apexEvent.getVersion()); + yamlMap.put(ApexEvent.NAMESPACE_HEADER_FIELD, apexEvent.getNameSpace()); + yamlMap.put(ApexEvent.SOURCE_HEADER_FIELD, apexEvent.getSource()); + yamlMap.put(ApexEvent.TARGET_HEADER_FIELD, apexEvent.getTarget()); + + if (apexEvent.getExceptionMessage() != null) { + yamlMap.put(ApexEvent.EXCEPTION_MESSAGE_HEADER_FIELD, apexEvent.getExceptionMessage()); + } + + for (final AxField eventField : eventDefinition.getFields()) { + final String fieldName = eventField.getKey().getLocalName(); + + if (!apexEvent.containsKey(fieldName)) { + if (!eventField.getOptional()) { + final String errorMessage = "error parsing " + eventDefinition.getID() + " event to Json. " + + "Field \"" + fieldName + "\" is missing, but is mandatory. Fields: " + apexEvent; + LOGGER.debug(errorMessage); + throw new ApexEventRuntimeException(errorMessage); + } + continue; + } + + yamlMap.put(fieldName, apexEvent.get(fieldName)); + } + + // Use Snake YAML to convert the APEX event to YAML + Yaml yaml = new Yaml(); + return yaml.dumpAs(yamlMap, null, FlowStyle.BLOCK); + } + + /** + * This method converts a YAML map into an Apex event. + * + * @param eventName the name of the event + * @param yamlMap the YAML map that holds the event + * @return the apex event that we have converted the JSON object into + * @throws ApexEventException + * thrown on unmarshaling exceptions + */ + private ApexEvent yamlMap2ApexEvent(final String eventName, final Map<?, ?> yamlMap) + throws ApexEventException { + // Process the mandatory Apex header + final ApexEvent apexEvent = processApexEventHeader(eventName, yamlMap); + + // Get the event definition for the event from the model service + final AxEvent eventDefinition = ModelService.getModel(AxEvents.class).get(apexEvent.getName(), + apexEvent.getVersion()); + + // Iterate over the input fields in the event + for (final AxField eventField : eventDefinition.getFields()) { + final String fieldName = eventField.getKey().getLocalName(); + if (!yamlMap.containsKey(fieldName)) { + if (!eventField.getOptional()) { + final String errorMessage = "error parsing " + eventDefinition.getID() + " event from Json. " + + "Field \"" + fieldName + "\" is missing, but is mandatory."; + LOGGER.debug(errorMessage); + throw new ApexEventException(errorMessage); + } + continue; + } + + final Object fieldValue = getYamlField(yamlMap, fieldName, null, !eventField.getOptional()); + + if (fieldValue != null) { + // Get the schema helper + final SchemaHelper fieldSchemaHelper = new SchemaHelperFactory().createSchemaHelper(eventField.getKey(), + eventField.getSchema()); + apexEvent.put(fieldName, fieldSchemaHelper.createNewInstance(fieldValue)); + } else { + apexEvent.put(fieldName, null); + } + } + return apexEvent; + + } + + /** + * This method processes the event header of an Apex event. + * + * @param eventName the name of the event + * @param yamlMap the YAML map that holds the event + * @return an apex event constructed using the header fields of the event + * @throws ApexEventRuntimeException the apex event runtime exception + * @throws ApexEventException on invalid events with missing header fields + */ + private ApexEvent processApexEventHeader(final String eventName, final Map<?, ?> yamlMap) + throws ApexEventException { + // Get the event header fields + // @formatter:off + String name = getYamlStringField(yamlMap, ApexEvent.NAME_HEADER_FIELD, yamlPars.getNameAlias(), ApexEvent.NAME_REGEXP, false); + String version = getYamlStringField(yamlMap, ApexEvent.VERSION_HEADER_FIELD, yamlPars.getVersionAlias(), ApexEvent.VERSION_REGEXP, false); + String namespace = getYamlStringField(yamlMap, ApexEvent.NAMESPACE_HEADER_FIELD, yamlPars.getNameSpaceAlias(), ApexEvent.NAMESPACE_REGEXP, false); + String source = getYamlStringField(yamlMap, ApexEvent.SOURCE_HEADER_FIELD, yamlPars.getSourceAlias(), ApexEvent.SOURCE_REGEXP, false); + String target = getYamlStringField(yamlMap, ApexEvent.TARGET_HEADER_FIELD, yamlPars.getTargetAlias(), ApexEvent.TARGET_REGEXP, false); + // @formatter:on + + // Check that an event name has been specified + if (name == null && eventName == null) { + throw new ApexEventRuntimeException( + "event received without mandatory parameter \"name\" on configuration or on event"); + } + + // Check if an event name was specified on the event parameters + if (eventName != null) { + if (name != null && !eventName.equals(name)) { + LOGGER.warn("The incoming event name \"{}\" does not match the configured event name \"{}\", using configured event name", + name, eventName); + } + name = eventName; + } + + // Now, find the event definition in the model service. If version is null, the newest event + // definition in the model service is used + final AxEvent eventDefinition = ModelService.getModel(AxEvents.class).get(name, version); + if (eventDefinition == null) { + throw new ApexEventRuntimeException("an event definition for an event named \"" + name + + "\" with version \"" + version + "\" not found in Apex model"); + } + + // Use the defined event version if no version is specified on the incoming fields + if (version == null) { + version = eventDefinition.getKey().getVersion(); + } + + // Check the name space is OK if it is defined, if not, use the name space from the model + if (namespace != null) { + if (!namespace.equals(eventDefinition.getNameSpace())) { + throw new ApexEventRuntimeException("namespace \"" + namespace + "\" on event \"" + name + + "\" does not match namespace \"" + eventDefinition.getNameSpace() + + "\" for that event in the Apex model"); + } + } else { + namespace = eventDefinition.getNameSpace(); + } + + // For source, use the defined source only if the source is not found on the incoming event + if (source == null) { + source = eventDefinition.getSource(); + } + + // For target, use the defined source only if the source is not found on the incoming event + if (target == null) { + target = eventDefinition.getTarget(); + } + + return new ApexEvent(name, version, namespace, source, target); + } + + /** + * This method gets an event string field from a JSON object. + * + * @param yamlMap + * the YAML containing the YAML representation of the incoming event + * @param fieldName + * the field name to find in the event + * @param fieldAlias + * the alias for the field to find in the event, overrides the field name if it is not null + * @param fieldRE + * the regular expression to check the field against for validity + * @param mandatory + * true if the field is mandatory + * @return the value of the field in the JSON object or null if the field is optional + * @throws ApexEventRuntimeException + * the apex event runtime exception + */ + private String getYamlStringField(final Map<?, ?> yamlMap, final String fieldName, final String fieldAlias, + final String fieldRE, final boolean mandatory) { + // Get the YAML field for the string field + final Object yamlField = getYamlField(yamlMap, fieldName, fieldAlias, mandatory); + + // Null strings are allowed + if (yamlField == null) { + return null; + } + + if (!(yamlField instanceof String)) { + // The element is not a string so throw an error + throw new ApexEventRuntimeException("field \"" + fieldName + "\" with type \"" + + yamlField.getClass().getCanonicalName() + "\" is not a string value"); + } + + final String fieldValueString = (String) yamlField; + + // Is regular expression checking required + if (fieldRE == null) { + return fieldValueString; + } + + // Check the event field against its regular expression + if (!fieldValueString.matches(fieldRE)) { + throw new ApexEventRuntimeException( + "field \"" + fieldName + "\" with value \"" + fieldValueString + "\" is invalid"); + } + + return fieldValueString; + } + + /** + * This method gets an event field from a YAML object. + * + * @param yamlMap + * the YAML containing the YAML representation of the incoming event + * @param fieldName + * the field name to find in the event + * @param fieldAlias + * the alias for the field to find in the event, overrides the field name if it is not null + * @param mandatory + * true if the field is mandatory + * @return the value of the field in the YAML object or null if the field is optional + * @throws ApexEventRuntimeException + * the apex event runtime exception + */ + private Object getYamlField(final Map<?, ?> yamlMap, final String fieldName, final String fieldAlias, + final boolean mandatory) { + + // Check if we should use the alias for this field + String fieldToFind = fieldName; + if (fieldAlias != null) { + fieldToFind = fieldAlias; + } + + // Get the event field + final Object eventElement = yamlMap.get(fieldToFind); + if (eventElement == null) { + if (!mandatory) { + return null; + } else { + throw new ApexEventRuntimeException("mandatory field \"" + fieldToFind + "\" is missing"); + } + } + + return eventElement; + } +} diff --git a/plugins/plugins-event/plugins-event-protocol/plugins-event-protocol-yaml/src/main/java/org/onap/policy/apex/plugins/event/protocol/yaml/YamlEventProtocolParameters.java b/plugins/plugins-event/plugins-event-protocol/plugins-event-protocol-yaml/src/main/java/org/onap/policy/apex/plugins/event/protocol/yaml/YamlEventProtocolParameters.java new file mode 100644 index 000000000..861e9cd8f --- /dev/null +++ b/plugins/plugins-event/plugins-event-protocol/plugins-event-protocol-yaml/src/main/java/org/onap/policy/apex/plugins/event/protocol/yaml/YamlEventProtocolParameters.java @@ -0,0 +1,204 @@ +/*- + * ============LICENSE_START======================================================= + * Copyright (C) 2016-2018 Ericsson. All rights reserved. + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * SPDX-License-Identifier: Apache-2.0 + * ============LICENSE_END========================================================= + */ + +package org.onap.policy.apex.plugins.event.protocol.yaml; + +import org.onap.policy.apex.service.parameters.eventprotocol.EventProtocolTextTokenDelimitedParameters; + +/** + * Event protocol parameters for YAML as an event protocol. + * + * The parameters for this plugin are: + * <ol> + * <li>nameAlias: The field in a YAML event to use as an alias for the event name. This parameter is + * optional. + * <li>versionAlias: The field in a YAML event to use as an alias for the event version. This + * parameter is optional. + * <li>nameSpaceAlias: The field in a YAML event to use as an alias for the event name space. This + * parameter is optional. + * <li>sourceAlias: The field in a YAML event to use as an alias for the event source. This + * parameter is optional. + * <li>targetAlias: The field in a YAML event to use as an alias for the event target. This + * parameter is optional. + * <li>yamlFieldName: The name of the field in the APEX event that will contain the unmarshaled YAML object. The + * parameter is optional and defaults to the value "yaml_field". + * </ol> + * + * @author Liam Fallon (liam.fallon@ericsson.com) + */ +public class YamlEventProtocolParameters extends EventProtocolTextTokenDelimitedParameters { + /** The label of this event protocol. */ + public static final String YAML_EVENT_PROTOCOL_LABEL = "YAML"; + + // Constants for text block delimiters + private static final String YAML_START_TEXT_DELIMITER_TOKEN = "---"; + private static final String YAML_END_TEXT_DELIMITER_TOKEN = "..."; + + // Default parameter values + private static final String DEFAULT_YAML_FIELD_NAME = "yaml_field"; + + // Aliases for Apex event header fields + // @formatter:off + private String nameAlias = null; + private String versionAlias = null; + private String nameSpaceAlias = null; + private String sourceAlias = null; + private String targetAlias = null; + private String yamlFieldName = DEFAULT_YAML_FIELD_NAME; + // @formatter:on + + /** + * Constructor to create a YAML event protocol parameter instance and register the instance with + * the parameter service. + */ + public YamlEventProtocolParameters() { + this(YamlEventProtocolParameters.class.getCanonicalName(), YAML_EVENT_PROTOCOL_LABEL); + } + + /** + * Constructor to create an event protocol parameters instance with the name of a sub class of + * this class. + * + * @param parameterClassName the class name of a sub class of this class + * @param eventProtocolLabel the name of the event protocol for this plugin + */ + public YamlEventProtocolParameters(final String parameterClassName, final String eventProtocolLabel) { + super(parameterClassName); + + // Set the event protocol properties for the YAML event protocol + this.setLabel(eventProtocolLabel); + + // Set the delimiter token for text blocks of YAML events + this.setStartDelimiterToken(YAML_START_TEXT_DELIMITER_TOKEN); + this.setEndDelimiterToken(YAML_END_TEXT_DELIMITER_TOKEN); + + // Set the event protocol plugin class + this.setEventProtocolPluginClass(Apex2YamlEventConverter.class.getCanonicalName()); + } + + /** + * Gets the name alias. + * + * @return the name alias + */ + public String getNameAlias() { + return nameAlias; + } + + /** + * Gets the version alias. + * + * @return the version alias + */ + public String getVersionAlias() { + return versionAlias; + } + + /** + * Gets the name space alias. + * + * @return the name space alias + */ + public String getNameSpaceAlias() { + return nameSpaceAlias; + } + + /** + * Gets the source alias. + * + * @return the source alias + */ + public String getSourceAlias() { + return sourceAlias; + } + + /** + * Gets the target alias. + * + * @return the target alias + */ + public String getTargetAlias() { + return targetAlias; + } + + /** + * Gets the YAML field name. + * + * @return the YAML field name + */ + public String getYamlFieldName() { + return yamlFieldName; + } + + /** + * Sets the name alias. + * + * @param nameAlias the new name alias + */ + public void setNameAlias(String nameAlias) { + this.nameAlias = nameAlias; + } + + /** + * Sets the version alias. + * + * @param versionAlias the new version alias + */ + public void setVersionAlias(String versionAlias) { + this.versionAlias = versionAlias; + } + + /** + * Sets the name space alias. + * + * @param nameSpaceAlias the new name space alias + */ + public void setNameSpaceAlias(String nameSpaceAlias) { + this.nameSpaceAlias = nameSpaceAlias; + } + + /** + * Sets the source alias. + * + * @param sourceAlias the new source alias + */ + public void setSourceAlias(String sourceAlias) { + this.sourceAlias = sourceAlias; + } + + /** + * Sets the target alias. + * + * @param targetAlias the new target alias + */ + public void setTargetAlias(String targetAlias) { + this.targetAlias = targetAlias; + } + + /** + * Sets the encapsulating object name. + * + * @param yamlFieldName + * the new YAML field name + */ + public void setYamlFieldName(String yamlFieldName) { + this.yamlFieldName = yamlFieldName; + } +} diff --git a/plugins/plugins-event/plugins-event-protocol/plugins-event-protocol-yaml/src/main/java/org/onap/policy/apex/plugins/event/protocol/yaml/package-info.java b/plugins/plugins-event/plugins-event-protocol/plugins-event-protocol-yaml/src/main/java/org/onap/policy/apex/plugins/event/protocol/yaml/package-info.java new file mode 100644 index 000000000..ce11d33a3 --- /dev/null +++ b/plugins/plugins-event/plugins-event-protocol/plugins-event-protocol-yaml/src/main/java/org/onap/policy/apex/plugins/event/protocol/yaml/package-info.java @@ -0,0 +1,27 @@ +/*- + * ============LICENSE_START======================================================= + * Copyright (C) 2018 Ericsson. All rights reserved. + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * SPDX-License-Identifier: Apache-2.0 + * ============LICENSE_END========================================================= + */ + +/** + * Contains implementations of Apex event protocol converter plugins for events in YAML format. + * + * @author Liam Fallon (liam.fallon@ericsson.com) + */ + +package org.onap.policy.apex.plugins.event.protocol.yaml; |