/*-
* ============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.service.parameters;
import java.util.Arrays;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import org.onap.policy.apex.service.parameters.engineservice.EngineServiceParameters;
import org.onap.policy.apex.service.parameters.eventhandler.EventHandlerParameters;
import org.onap.policy.apex.service.parameters.eventhandler.EventHandlerPeeredMode;
import org.onap.policy.common.parameters.GroupValidationResult;
import org.onap.policy.common.parameters.ParameterGroup;
import org.onap.policy.common.parameters.ValidationStatus;
import org.onap.policy.common.utils.validation.ParameterValidationUtils;
/**
* The main container parameter class for an Apex service.
*
*
The following parameters are defined:
- engineServiceParameters: The parameters for the Apex engine service
* itself, such as the number of engine threads to run and the deployment port number to use.
- eventOutputParameters:
* A map of parameters for event outputs that Apex will use to emit events. Apex emits events on all outputs
*
- eventInputParameters: A map or parameters for event inputs from which Apex will consume events. Apex reads events
* from all its event inputs.
*
* @author Liam Fallon (liam.fallon@ericsson.com)
*/
public class ApexParameters implements ParameterGroup {
// Parameter group name
private String name;
// Constants for recurring strings
private static final String JAVA_PROPERTIES = "javaProperties";
private static final String PEER_STRING = "peer ";
private static final String EVENT_INPUT_PARAMETERS_STRING = "eventInputParameters";
private static final String EVENT_OUTPUT_PARAMETERS_STRING = "eventOutputParameters";
private static final String FOR_PEERED_MODE_STRING = " for peered mode ";
// Properties for the Java JVM
private String[][] javaProperties = null;
// Parameters for the engine service and the engine threads in the engine service
private EngineServiceParameters engineServiceParameters;
// Parameters for the event outputs that Apex will use to send events on its outputs
private Map eventOutputParameters = new LinkedHashMap<>();
// Parameters for the event inputs that Apex will use to receive events on its inputs
private Map eventInputParameters = new LinkedHashMap<>();
/**
* Constructor to create an apex parameters instance and register the instance with the parameter service.
*/
public ApexParameters() {
super();
// Set the name for the parameters
this.name = ApexParameterConstants.MAIN_GROUP_NAME;
}
/**
* Gets the parameters for the Apex engine service.
*
* @return the engine service parameters
*/
public EngineServiceParameters getEngineServiceParameters() {
return engineServiceParameters;
}
/**
* Sets the engine service parameters.
*
* @param engineServiceParameters the engine service parameters
*/
public void setEngineServiceParameters(final EngineServiceParameters engineServiceParameters) {
this.engineServiceParameters = engineServiceParameters;
}
/**
* Gets the event output parameter map.
*
* @return the parameters for all event outputs
*/
public Map getEventOutputParameters() {
return eventOutputParameters;
}
/**
* Sets the event output parameters.
*
* @param eventOutputParameters the event outputs parameters
*/
public void setEventOutputParameters(final Map eventOutputParameters) {
this.eventOutputParameters = eventOutputParameters;
}
/**
* Gets the event input parameter map.
*
* @return the parameters for all event inputs
*/
public Map getEventInputParameters() {
return eventInputParameters;
}
/**
* Sets the event input parameters.
*
* @param eventInputParameters the event input parameters
*/
public void setEventInputParameters(final Map eventInputParameters) {
this.eventInputParameters = eventInputParameters;
}
@Override
public String getName() {
return name;
}
@Override
public void setName(final String name) {
this.name = name;
}
/**
* Check if Java properties have been specified.
*
* @return true if Java properties have been specified
*/
public boolean checkJavaPropertiesSet() {
return javaProperties != null && javaProperties.length > 0;
}
/**
* Gets the Java properties that have been specified.
*
* @return the Java properties that have been specified
*/
public String[][] getJavaProperties() {
return javaProperties;
}
@Override
public GroupValidationResult validate() {
GroupValidationResult result = new GroupValidationResult(this);
validateJavaProperties(result);
if (engineServiceParameters == null) {
result.setResult("engineServiceParameters", ValidationStatus.INVALID,
"engine service parameters are not specified");
} else {
result.setResult("engineServiceParameters", engineServiceParameters.validate());
}
// Sanity check, we must have an entry in both output and input maps
if (eventInputParameters.isEmpty()) {
result.setResult(EVENT_INPUT_PARAMETERS_STRING, ValidationStatus.INVALID,
"at least one event input must be specified");
}
if (eventOutputParameters.isEmpty()) {
result.setResult(EVENT_OUTPUT_PARAMETERS_STRING, ValidationStatus.INVALID,
"at least one event output must be specified");
}
// Validate that the values of all parameters are ok
validateEventHandlerMap(EVENT_INPUT_PARAMETERS_STRING, result, eventInputParameters);
validateEventHandlerMap(EVENT_OUTPUT_PARAMETERS_STRING, result, eventOutputParameters);
// Only do peer mode validate if there are no other errors
if (result.isValid()) {
for (final EventHandlerPeeredMode peeredMode : EventHandlerPeeredMode.values()) {
validatePeeredMode(result, peeredMode);
}
}
return result;
}
/**
* This method validates the java properties variable if it is present.
*
* @param result the result of the validation
*/
private void validateJavaProperties(GroupValidationResult result) {
if (javaProperties == null) {
return;
}
StringBuilder errorMessageBuilder = new StringBuilder();
for (String[] javaProperty : javaProperties) {
if (javaProperty == null) {
errorMessageBuilder.append("java properties array entry is null\n");
} else if (javaProperty.length != 2) {
errorMessageBuilder.append("java properties array entries must have one key and one value: "
+ Arrays.deepToString(javaProperty) + "\n");
} else if (!ParameterValidationUtils.validateStringParameter(javaProperty[0])) {
errorMessageBuilder
.append("java properties key is null or blank: " + Arrays.deepToString(javaProperty) + "\n");
} else if (!ParameterValidationUtils.validateStringParameter(javaProperty[1])) {
errorMessageBuilder
.append("java properties value is null or blank: " + Arrays.deepToString(javaProperty) + "\n");
}
}
if (errorMessageBuilder.length() > 0) {
result.setResult(JAVA_PROPERTIES, ValidationStatus.INVALID, errorMessageBuilder.toString());
}
}
/**
* This method validates the parameters in an event handler map.
*
* @param eventHandlerType the type of the event handler to use on error messages
* @param result the result object to use to return validation messages
* @param parsForValidation The event handler parameters to validate (input or output)
*/
private void validateEventHandlerMap(final String eventHandlerType, final GroupValidationResult result,
final Map parsForValidation) {
for (final Entry parameterEntry : parsForValidation.entrySet()) {
if (parameterEntry.getKey() == null || parameterEntry.getKey().trim().isEmpty()) {
result.setResult(eventHandlerType, parameterEntry.getKey(), ValidationStatus.INVALID,
"invalid " + eventHandlerType + " name \"" + parameterEntry.getKey() + "\"");
} else if (parameterEntry.getValue() == null) {
result.setResult(eventHandlerType, parameterEntry.getKey(), ValidationStatus.INVALID,
"invalid/Null event input prameters specified for " + eventHandlerType + " name \""
+ parameterEntry.getKey() + "\" ");
} else {
result.setResult(eventHandlerType, parameterEntry.getKey(), parameterEntry.getValue().validate());
}
parameterEntry.getValue().setName(parameterEntry.getKey());
// Validate parameters for peered mode settings
for (final EventHandlerPeeredMode peeredMode : EventHandlerPeeredMode.values()) {
validatePeeredModeParameters(eventHandlerType, result, parameterEntry, peeredMode);
}
}
}
/**
* Validate parameter values for event handlers in a peered mode.
*
* @param eventHandlerType The event handler type we are checking
* @param result The result object to which to append any error messages
* @param parameterEntry The entry to check the peered mode on
* @param peeredMode The mode to check
*/
private void validatePeeredModeParameters(final String eventHandlerType, final GroupValidationResult result,
final Entry parameterEntry, final EventHandlerPeeredMode peeredMode) {
final String messagePreamble = "specified peered mode \"" + peeredMode + "\"";
final String peer = parameterEntry.getValue().getPeer(peeredMode);
if (parameterEntry.getValue().isPeeredMode(peeredMode)) {
if (peer == null || peer.trim().isEmpty()) {
result.setResult(eventHandlerType, parameterEntry.getKey(), ValidationStatus.INVALID,
messagePreamble + " mandatory parameter not specified or is null");
}
if (parameterEntry.getValue().getPeerTimeout(peeredMode) < 0) {
result.setResult(eventHandlerType, parameterEntry.getKey(), ValidationStatus.INVALID,
messagePreamble + " timeout value \"" + parameterEntry.getValue().getPeerTimeout(peeredMode)
+ "\" is illegal, specify a non-negative timeout value in milliseconds");
}
} else {
if (peer != null) {
result.setResult(eventHandlerType, parameterEntry.getKey(), ValidationStatus.INVALID, messagePreamble
+ " peer is illegal on " + eventHandlerType + " \"" + parameterEntry.getKey() + "\" ");
}
if (parameterEntry.getValue().getPeerTimeout(peeredMode) != 0) {
result.setResult(eventHandlerType, parameterEntry.getKey(), ValidationStatus.INVALID, messagePreamble
+ " timeout is illegal on " + eventHandlerType + " \"" + parameterEntry.getKey() + "\"");
}
}
}
/**
* This method validates that the settings are valid for the given peered mode.
*
* @param result The result object to which to append any error messages
* @param peeredMode The peered mode to check
*/
private void validatePeeredMode(final GroupValidationResult result, final EventHandlerPeeredMode peeredMode) {
// Find the input and output event handlers that use this peered mode
final Map inputParametersUsingMode = new HashMap<>();
final Map outputParametersUsingMode = new HashMap<>();
// Find input and output parameters using this mode
for (final Entry inputParameterEntry : eventInputParameters.entrySet()) {
if (inputParameterEntry.getValue().isPeeredMode(peeredMode)) {
inputParametersUsingMode.put(inputParameterEntry.getKey(), inputParameterEntry.getValue());
}
}
for (final Entry outputParameterEntry : eventOutputParameters.entrySet()) {
if (outputParameterEntry.getValue().isPeeredMode(peeredMode)) {
outputParametersUsingMode.put(outputParameterEntry.getKey(), outputParameterEntry.getValue());
}
}
// Validate the parameters for each side of the peered mode parameters
validatePeeredModePeers(EVENT_INPUT_PARAMETERS_STRING, result, peeredMode, inputParametersUsingMode,
outputParametersUsingMode);
validatePeeredModePeers(EVENT_OUTPUT_PARAMETERS_STRING, result, peeredMode, outputParametersUsingMode,
inputParametersUsingMode);
}
/**
* This method validates that the settings are valid for the event handlers on one.
*
* @param handlerMapVariableName the variable name of the map on which the paired parameters are being checked
* @param result The result object to which to append any error messages
* @param leftModeParameters The mode parameters being checked
* @param rightModeParameters The mode parameters being referenced by the checked parameters
*/
private void validatePeeredModePeers(final String handlerMapVariableName, final GroupValidationResult result,
final EventHandlerPeeredMode peeredMode, final Map leftModeParameterMap,
final Map rightModeParameterMap) {
// These sets are used to check for duplicate references on the both sides
final Set leftCheckDuplicateSet = new HashSet<>();
final Set rightCheckDuplicateSet = new HashSet<>();
// Check for missing peers, all peers are set because we have checked them previously so no
// need for null checks
for (final Entry leftModeParameterEntry : leftModeParameterMap.entrySet()) {
final String leftSidePeer = leftModeParameterEntry.getValue().getPeer(peeredMode);
final EventHandlerParameters leftModeParameters = leftModeParameterEntry.getValue();
final EventHandlerParameters rightModeParameters = rightModeParameterMap.get(leftSidePeer);
// Check that the peer reference is OK
if (rightModeParameters == null) {
result.setResult(handlerMapVariableName, leftModeParameterEntry.getKey(), ValidationStatus.INVALID,
PEER_STRING + '"' + leftModeParameters.getPeer(peeredMode) + FOR_PEERED_MODE_STRING + peeredMode
+ " does not exist or is not defined with the same peered mode");
continue;
}
// Now check that the right side peer is the left side event handler
final String rightSidePeer = rightModeParameters.getPeer(peeredMode);
if (!rightSidePeer.equals(leftModeParameterEntry.getKey())) {
result.setResult(handlerMapVariableName, leftModeParameterEntry.getKey(), ValidationStatus.INVALID,
PEER_STRING + '"' + leftModeParameters.getPeer(peeredMode) + FOR_PEERED_MODE_STRING + peeredMode
+ ", value \"" + rightSidePeer + "\" on peer \"" + leftSidePeer
+ "\" does not equal event handler \"" + leftModeParameterEntry.getKey() + "\"");
} else {
// Check for duplicates
if (!leftCheckDuplicateSet.add(leftSidePeer)) {
result.setResult(handlerMapVariableName, leftModeParameterEntry.getKey(), ValidationStatus.INVALID,
PEER_STRING + '"' + leftModeParameters.getPeer(peeredMode) + FOR_PEERED_MODE_STRING + peeredMode
+ ", peer value \"" + leftSidePeer + "\" on event handler \""
+ leftModeParameterEntry.getKey() + "\" is used more than once");
}
if (!rightCheckDuplicateSet.add(rightSidePeer)) {
result.setResult(handlerMapVariableName, leftModeParameterEntry.getKey(), ValidationStatus.INVALID,
PEER_STRING + '"' + leftModeParameters.getPeer(peeredMode) + FOR_PEERED_MODE_STRING + peeredMode
+ ", peer value \"" + rightSidePeer + "\" on peer \"" + leftSidePeer
+ "\" on event handler \"" + leftModeParameterEntry.getKey() + "\" is used more than once");
}
}
if (!crossCheckPeeredTimeoutValues(leftModeParameters, rightModeParameters, peeredMode)) {
result.setResult(handlerMapVariableName, leftModeParameterEntry.getKey(), ValidationStatus.INVALID,
PEER_STRING + '"' + leftModeParameters.getPeer(peeredMode) + FOR_PEERED_MODE_STRING + peeredMode
+ " timeout " + leftModeParameters.getPeerTimeout(peeredMode) + " on event handler \""
+ leftModeParameters.getName() + "\" does not equal timeout "
+ rightModeParameters.getPeerTimeout(peeredMode) + " on event handler \""
+ rightModeParameters.getName() + "\"");
}
}
}
/**
* Validate the timeout values on two peers.
*
* @param leftModeParameters The parameters of the left hand peer
* @param peeredMode The peered mode being checked
* @return true if the timeout values are cross checked as being OK
*/
private boolean crossCheckPeeredTimeoutValues(final EventHandlerParameters leftModeParameters,
final EventHandlerParameters rightModeParameters, final EventHandlerPeeredMode peeredMode) {
// Cross-set the timeouts if they are not specified
if (leftModeParameters.getPeerTimeout(peeredMode) != 0) {
if (rightModeParameters.getPeerTimeout(peeredMode) != 0) {
if (leftModeParameters.getPeerTimeout(peeredMode) != rightModeParameters.getPeerTimeout(peeredMode)) {
return false;
}
} else {
rightModeParameters.setPeerTimeout(peeredMode, leftModeParameters.getPeerTimeout(peeredMode));
}
} else {
if (rightModeParameters.getPeerTimeout(peeredMode) != 0) {
leftModeParameters.setPeerTimeout(peeredMode, rightModeParameters.getPeerTimeout(peeredMode));
}
}
return true;
}
}