/**
* Copyright 2015 Otto (GmbH & Co KG)
*
* 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.
*/
package com.ottogroup.bi.spqr.pipeline;
import java.util.HashSet;
import java.util.Set;
import org.apache.commons.lang3.StringUtils;
import com.ottogroup.bi.spqr.pipeline.component.MicroPipelineComponentConfiguration;
import com.ottogroup.bi.spqr.pipeline.component.MicroPipelineComponentType;
import com.ottogroup.bi.spqr.pipeline.queue.StreamingMessageQueueConfiguration;
/**
* Validates provided {@link MicroPipelineConfiguration} for being compliant with requirement which
* ensure that
* <ul>
* <li>configuration must not be null</li>
* <li>set of queues to interconnect components must not be empty</li>
* <li>assigned queue identifiers must be unique within pipeline boundaries</li>
* <li>set of components must not be empty</li>
* <li>assigned component identifiers must be unique within pipeline boundaries</li>
* <li>each component configuration must show a valid type</li>
* <li>each component configuration must show a <i>name</i> and <i>version</i> which both reference existing artifacts</li>
* <li>each component must point to a valid <i>in-queue</i> - unless the component is of type {@link MicroPipelineComponentType#SOURCE}</li>
* <li>each component must point to a valid <i>out-queue</i> - unless the component is of type {@link MicroPipelineComponentType#EMITTER}</li>
* </ul>
* @author mnxfst
* @since Apr 13, 2015
*/
public class MicroPipelineValidator {
/**
* Validates the contents of a provided {@link MicroPipelineConfiguration} for being compliant with a required format
* and errors that may be inferred from provided contents
* @param configuration
* @return
*/
public MicroPipelineValidationResult validate(final MicroPipelineConfiguration configuration) {
///////////////////////////////////////////////////////////////////////////////////
// validate configuration, components and queues for not being null
if(configuration == null)
return MicroPipelineValidationResult.MISSING_CONFIGURATION;
if(configuration.getComponents() == null || configuration.getComponents().isEmpty())
return MicroPipelineValidationResult.MISSING_COMPONENTS;
if(configuration.getQueues() == null || configuration.getQueues().isEmpty())
return MicroPipelineValidationResult.MISSING_QUEUES;
//
///////////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////////
// validate queues and store their identifiers in set for further look-ups executed
// on component evaluation
Set<String> queueIdentifiers = new HashSet<>();
for(final StreamingMessageQueueConfiguration queueCfg : configuration.getQueues()) {
// queue identifier must neither be null nor empty
if(StringUtils.isBlank(queueCfg.getId()))
return MicroPipelineValidationResult.MISSING_QUEUE_ID;
// convert to trimmed lower-case representation and check if it is unique
String tempId = StringUtils.lowerCase(StringUtils.trim(queueCfg.getId()));
if(queueIdentifiers.contains(tempId))
return MicroPipelineValidationResult.NON_UNIQUE_QUEUE_ID;
queueIdentifiers.add(tempId);
}
//
///////////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////////
// validate components
Set<String> componentIdentifiers = new HashSet<>();
for(final MicroPipelineComponentConfiguration componentCfg : configuration.getComponents()) {
MicroPipelineValidationResult componentValidationResult = validateComponent(componentCfg, queueIdentifiers, componentIdentifiers);
if(componentValidationResult != MicroPipelineValidationResult.OK)
return componentValidationResult;
// add identifier to set of managed components
componentIdentifiers.add(StringUtils.lowerCase(StringUtils.trim(componentCfg.getId())));
}
//
///////////////////////////////////////////////////////////////////////////////////
// no errors found so far which could be inferred from configuration
return MicroPipelineValidationResult.OK;
}
/**
* Validates the provided {@link StreamingMessageQueueConfiguration} for being compliant with basic requirements
* set for queues inside {@link MicroPipelineConfiguration}
* @param queueCfg
* @param queueIdentifiers
* @return
*/
protected MicroPipelineValidationResult validateQueue(final StreamingMessageQueueConfiguration queueCfg, final Set<String> queueIdentifiers) {
// the queue configuration must not be null ... for obvious reasons ;-)
if(queueCfg == null)
return MicroPipelineValidationResult.MISSING_QUEUE_CONFIGURATION;
// queue identifier must neither be null nor empty
if(StringUtils.isBlank(queueCfg.getId()))
return MicroPipelineValidationResult.MISSING_QUEUE_ID;
// convert to trimmed lower-case representation and check if it is unique
String tempId = StringUtils.lowerCase(StringUtils.trim(queueCfg.getId()));
if(queueIdentifiers.contains(tempId))
return MicroPipelineValidationResult.NON_UNIQUE_QUEUE_ID;
return MicroPipelineValidationResult.OK;
}
/**
* Validates a single {@link MicroPipelineComponentConfiguration}
* @param componentCfg
* @param queueIdentifiers previously extracted queue identifiers
* @param componentIdentifiers previously extracted component identifiers
* @return
*/
protected MicroPipelineValidationResult validateComponent(final MicroPipelineComponentConfiguration componentCfg, final Set<String> queueIdentifiers, final Set<String> componentIdentifiers) {
///////////////////////////////////////////////////////////////////////////////////
// component and its id, name, version and type for neither being null empty
if(componentCfg == null)
return MicroPipelineValidationResult.MISSING_COMPONENT_CONFIGURATION;
if(StringUtils.isBlank(componentCfg.getId()))
return MicroPipelineValidationResult.MISSING_COMPONENT_ID;
if(StringUtils.isBlank(componentCfg.getName()))
return MicroPipelineValidationResult.MISSING_COMPONENT_NAME;
if(StringUtils.isBlank(componentCfg.getVersion()))
return MicroPipelineValidationResult.MISSING_COMPONENT_VERSION;
if(componentCfg.getType() == null)
return MicroPipelineValidationResult.MISSING_COMPONENT_TYPE;
//
///////////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////////
// convert id to trimmed lower-case representation and check if it is unique
String tempId = StringUtils.lowerCase(StringUtils.trim(componentCfg.getId()));
if(componentIdentifiers.contains(tempId))
return MicroPipelineValidationResult.NON_UNIQUE_COMPONENT_ID;
//
///////////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////////
// validate queue settings for specific component types
if(componentCfg.getType() == MicroPipelineComponentType.SOURCE) {
// source component must reference a destination queue only - source queue references are not permitted
if(StringUtils.isNotBlank(componentCfg.getFromQueue()))
return MicroPipelineValidationResult.NOT_PERMITTED_SOURCE_QUEUE_REF;
// the identifier of the destination queue must neither be null nor empty
if(StringUtils.isBlank(componentCfg.getToQueue()))
return MicroPipelineValidationResult.MISSING_DESTINATION_QUEUE;
// the identifier of the destination queue must reference an existing queue
String destinationQueueId = StringUtils.lowerCase(StringUtils.trim(componentCfg.getToQueue()));
if(!queueIdentifiers.contains(destinationQueueId))
return MicroPipelineValidationResult.UNKNOWN_DESTINATION_QUEUE;
} else if(componentCfg.getType() == MicroPipelineComponentType.DIRECT_RESPONSE_OPERATOR ||
componentCfg.getType() == MicroPipelineComponentType.DELAYED_RESPONSE_OPERATOR) {
// operators must reference source and destination queues alike - both variable values must not be empty
if(StringUtils.isBlank(componentCfg.getFromQueue()))
return MicroPipelineValidationResult.MISSING_SOURCE_QUEUE;
if(StringUtils.isBlank(componentCfg.getToQueue()))
return MicroPipelineValidationResult.MISSING_DESTINATION_QUEUE;
// the identifier of the source queue must reference an existing queue
String sourceQueueId = StringUtils.lowerCase(StringUtils.trim(componentCfg.getFromQueue()));
if(!queueIdentifiers.contains(sourceQueueId))
return MicroPipelineValidationResult.UNKNOWN_SOURCE_QUEUE;
// the identifier of the destination queue must reference an existing queue
String destinationQueueId = StringUtils.lowerCase(StringUtils.trim(componentCfg.getToQueue()));
if(!queueIdentifiers.contains(destinationQueueId))
return MicroPipelineValidationResult.UNKNOWN_DESTINATION_QUEUE;
} else if(componentCfg.getType() == MicroPipelineComponentType.EMITTER) {
// emitter component must reference a source queue only - destination queue references are not permitted
if(StringUtils.isNotBlank(componentCfg.getToQueue()))
return MicroPipelineValidationResult.NOT_PERMITTED_DESTINATION_QUEUE_REF;
// the identifier of the source queue must neither be null nor empty
if(StringUtils.isBlank(componentCfg.getFromQueue()))
return MicroPipelineValidationResult.MISSING_SOURCE_QUEUE;
// the identifier of the destination queue must reference an existing queue
String destinationQueueId = StringUtils.lowerCase(StringUtils.trim(componentCfg.getFromQueue()));
if(!queueIdentifiers.contains(destinationQueueId))
return MicroPipelineValidationResult.UNKNOWN_SOURCE_QUEUE;
}
// return true if no error could be derived from component configuration
return MicroPipelineValidationResult.OK;
}
}