/*******************************************************************************
* Copyright (c) 2013 GigaSpaces Technologies Ltd. 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.
*******************************************************************************/
package org.cloudifysource.dsl.internal.validators;
import java.io.File;
import java.util.Arrays;
import java.util.List;
import org.apache.commons.lang.StringUtils;
import org.cloudifysource.domain.DSLValidation;
import org.cloudifysource.domain.IsolationSLA;
import org.cloudifysource.domain.MetricGroup;
import org.cloudifysource.domain.Service;
import org.cloudifysource.domain.Unit;
import org.cloudifysource.domain.internal.ServiceTierType;
import org.cloudifysource.domain.scalingrules.ScalingRuleDetails;
import org.cloudifysource.dsl.internal.CloudifyConstants;
import org.cloudifysource.dsl.internal.DSLUtils;
import org.cloudifysource.dsl.internal.DSLValidationContext;
import org.cloudifysource.dsl.internal.DSLValidationException;
/**
*
* @author adaml
*
*/
public class ServiceValidator implements DSLValidator {
private Service entity;
@Override
public void setDSLEntity(final Object dslEntity) {
this.entity = (Service) dslEntity;
}
@DSLValidation
public void validateDefaultValues(final DSLValidationContext validationContext)
throws DSLValidationException {
validateInstanceNumber();
validateServiceType();
}
@DSLValidation
public void validateRetriesLimit(final DSLValidationContext validationContext)
throws DSLValidationException {
if (this.entity.getRetries() < -1) {
throw new DSLValidationException(
"Valid values for the retries field are -1 (for always retry) and above. "
+ "Set to '0' for no retries.");
}
}
@DSLValidation
public void validateMultitenantStorage(final DSLValidationContext validationContext) throws DSLValidationException {
if (entity.getNumInstances() > 1 && isMultitenant() && entity.getStorage().getTemplate() != null) {
throw new DSLValidationException(
"numInstances must be 1 when used in a Multi-tenant configuration with static storage defined.");
}
}
private boolean isMultitenant() {
final IsolationSLA isolationSla = entity.getIsolationSLA();
if (isolationSla != null) {
if (isolationSla.getDedicated() != null) {
return false;
}
return true;
} else {
return false;
}
}
private void validateServiceType() throws DSLValidationException {
boolean typeExists = false;
final String[] enumAsString = new String[ServiceTierType.values().length];
int counter = 0;
for (final ServiceTierType tierType : ServiceTierType.values()) {
enumAsString[counter] = tierType.toString();
counter++;
if (tierType.toString().equalsIgnoreCase(this.entity.getType())) {
typeExists = true;
break;
}
}
if (!typeExists) {
throw new DSLValidationException("The service type '" + this.entity.getType() + "' is undefined."
+ "The known service types include " + Arrays.toString(enumAsString));
}
}
private void validateInstanceNumber() throws DSLValidationException {
if (this.entity.getNumInstances() > this.entity.getMaxAllowedInstances()) {
throw new DSLValidationException("The requested number of instances ("
+ this.entity.getNumInstances() + ") exceeds the maximum number of instances allowed"
+ " (" + this.entity.getMaxAllowedInstances() + ") for service " + this.entity.getName() + ".");
}
}
/**
* Validate the icon property (if set) points to an existing file.
*
* @param validationContext
* The DSLValidationContext object
* @throws DSLValidationException
* Indicates the icon could not be found
*/
@DSLValidation
public void validateIcon(final DSLValidationContext validationContext) throws DSLValidationException {
boolean isServiceFile = false;
final String serviceSuffix = DSLUtils.SERVICE_DSL_FILE_NAME_SUFFIX.trim();
final String filePath = validationContext.getFilePath().trim();
// execute this validation only if an icon was set and this is a Service's groovy file (not an Application's)
if (filePath.endsWith(serviceSuffix)) {
isServiceFile = true;
}
if (this.entity.getIcon() != null && isServiceFile) {
final File dslFile = new File(filePath);
File iconFile = new File(dslFile.getParent(), this.entity.getIcon());
if (!iconFile.isFile()) {
// check the icon at the extended path location, required for service that extend another Service file
File dslDirectory = dslFile.getParentFile();
if (this.entity.getExtendedServicesPaths() != null) {
for (final String extendedPath : this.entity.getExtendedServicesPaths()) {
if (isAbsolutePath(extendedPath)) {
dslDirectory = new File(extendedPath);
} else {
// climb up the extend hierarchy to look for the icno
dslDirectory = new File(dslDirectory.getAbsolutePath() + "/" + extendedPath);
}
iconFile = new File(dslDirectory.getAbsolutePath(), this.entity.getIcon());
if (iconFile.isFile()) {
break;
}
}
}
if (!iconFile.isFile()) {
throw new DSLValidationException("The icon file \"" + iconFile.getAbsolutePath()
+ "\" does not exist.");
}
}
}
}
private boolean isAbsolutePath(final String extendedPath) {
return new File(extendedPath).isAbsolute();
}
@DSLValidation
public void validateCustomProperties(final DSLValidationContext validationContext)
throws DSLValidationException {
if (this.entity.getCustomProperties().containsKey(CloudifyConstants.CUSTOM_PROPERTY_MONITORS_CACHE_EXPIRATION_TIMEOUT)) {
try {
Long.parseLong(this.entity.getCustomProperties()
.get(CloudifyConstants.CUSTOM_PROPERTY_MONITORS_CACHE_EXPIRATION_TIMEOUT));
} catch (final NumberFormatException e) {
throw new DSLValidationException("The "
+ CloudifyConstants.CUSTOM_PROPERTY_MONITORS_CACHE_EXPIRATION_TIMEOUT
+ " property must be a long value", e);
}
}
}
@SuppressWarnings("rawtypes")
@DSLValidation
public void validateUserInterfaceObjectIsWellDefined(final DSLValidationContext validationContext)
throws DSLValidationException {
if (this.entity.getUserInterface() != null) {
// Validate metric list
final List<MetricGroup> metricGroups = this.entity.getUserInterface().getMetricGroups();
for (final MetricGroup metricGroup : metricGroups) {
for (final Object metric : metricGroup.getMetrics()) {
if (metric instanceof List<?>) {
if (!(((List) metric).get(0) instanceof String)) {
throw new DSLValidationException("the defined metric " + metric.toString() + " is invalid."
+
" metric name should be of type 'String'");
}
if (!(((List) metric).get(1) instanceof Unit)) {
throw new DSLValidationException("the defined metric " + metric.toString() + " is invalid."
+
" metric axisYUnit should be of type org.cloudifysource.dsl.Unit");
}
if (!(((List) metric).size() == 2)) {
throw new DSLValidationException("the defined metric " + metric.toString() + " is invalid."
+
" metric should be defined as String or as a list [String, Unit]");
}
} else {
if (!(metric instanceof String)) {
throw new DSLValidationException("the defined metric " + metric.toString() + " is invalid."
+
" metric name should be of type 'String'");
}
}
}
}
}
}
/**
* Validates that the name property exists and is not empty or invalid.
*
* @param validationContext
* @throws DSLValidationException
*/
@DSLValidation
public void validateName(final DSLValidationContext validationContext)
throws DSLValidationException {
if (StringUtils.isBlank(entity.getName())) {
throw new DSLValidationException("Service.validateName: The service's name "
+ (entity.getName() == null ? "is missing" : "is empty"));
}
DSLUtils.validateRecipeName(entity.getName());
}
@DSLValidation
public void validateInstanceSize(final DSLValidationContext validationContext)
throws DSLValidationException {
if (entity.getMinAllowedInstances() <= 0) {
throw new DSLValidationException("Minimum number of instances ("
+ this.entity.getMinAllowedInstances()
+ ") must be 1 or higher.");
}
if (this.entity.getMinAllowedInstances() > this.entity.getMaxAllowedInstances()) {
throw new DSLValidationException(
"maximum number of instances ("
+ this.entity.getMaxAllowedInstances()
+ ") must be equal or greater than the minimum number of instances ("
+ this.entity.getMinAllowedInstances() + ")");
}
if (this.entity.getMinAllowedInstances() > this.entity.getNumInstances()) {
throw new DSLValidationException(
"number of instances ("
+ this.entity.getNumInstances()
+ ") must be equal or greater than the minimum number of instances ("
+ this.entity.getMinAllowedInstances() + ")");
}
if (this.entity.getNumInstances() > this.entity.getMaxAllowedInstances()) {
throw new DSLValidationException(
"number of instances ("
+ this.entity.getNumInstances()
+ ") must be equal or less than the maximum number of instances ("
+ this.entity.getMaxAllowedInstances() + ")");
}
if (this.entity.getNumInstances() <= 0) {
throw new DSLValidationException(
"number of instances must be set to a positive integer");
}
}
@DSLValidation
public void validateScalingRules(final DSLValidationContext validationContext)
throws DSLValidationException {
if (this.entity.getScalingRules() != null) {
for (final ScalingRuleDetails scalingRule : this.entity.getScalingRules()) {
final Object serviceStatisticsObject = scalingRule
.getServiceStatistics();
if (serviceStatisticsObject == null) {
throw new DSLValidationException(
"scalingRule must specify serviceStatistics (either a closure or "
+ "reference a predefined serviceStatistics name).");
}
}
}
}
}