| /** |
| * Licensed to the Apache Software Foundation (ASF) under one or more contributor license agreements. See the NOTICE file distributed with |
| * this work for additional information regarding copyright ownership. The ASF licenses this file to you 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.apache.storm.validation; |
| |
| import java.io.BufferedReader; |
| import java.io.IOException; |
| import java.io.InputStreamReader; |
| import java.lang.annotation.Annotation; |
| import java.lang.reflect.Field; |
| import java.lang.reflect.InvocationTargetException; |
| import java.lang.reflect.Method; |
| import java.net.URL; |
| import java.util.ArrayList; |
| import java.util.Arrays; |
| import java.util.HashMap; |
| import java.util.HashSet; |
| import java.util.List; |
| import java.util.Map; |
| import java.util.Set; |
| import org.apache.storm.Config; |
| import org.apache.storm.utils.Utils; |
| import org.apache.storm.validation.ConfigValidationAnnotations.ValidatorParams; |
| import org.slf4j.Logger; |
| import org.slf4j.LoggerFactory; |
| |
| /** |
| * Provides functionality for validating configuration fields. |
| */ |
| public class ConfigValidation { |
| private static final Logger LOG = LoggerFactory.getLogger(ConfigValidation.class); |
| //We follow the model of service loaders (Even though it is not a service). |
| private static final String CONFIG_CLASSES_NAME = "META-INF/services/" + Validated.class.getName(); |
| |
| /* |
| * Validator definitions |
| */ |
| //The following come from the JVm Specification table 4.4 |
| // https://docs.oracle.com/javase/specs/jvms/se7/html/jvms-4.html#jvms-4.5 |
| private static final int ACC_PUBLIC = 0x0001; |
| private static final int ACC_STATIC = 0x0008; |
| private static final int ACC_FINAL = 0x0010; |
| private static final int DESIRED_FIELD_ACC = ACC_PUBLIC | ACC_STATIC | ACC_FINAL; |
| private static List<Class<?>> configClasses = null; |
| |
| public static synchronized List<Class<?>> getConfigClasses() { |
| if (configClasses == null) { |
| List<Class<?>> ret = new ArrayList<>(); |
| Set<String> classesToScan = new HashSet<>(); |
| classesToScan.add(Config.class.getName()); |
| for (URL url : Utils.findResources(CONFIG_CLASSES_NAME)) { |
| try { |
| try (BufferedReader in = new BufferedReader(new InputStreamReader(url.openStream()))) { |
| String line; |
| while ((line = in.readLine()) != null) { |
| line = line.replaceAll("#.*$", "").trim(); |
| if (!line.isEmpty()) { |
| classesToScan.add(line); |
| } |
| } |
| } |
| } catch (IOException e) { |
| throw new RuntimeException("Error trying to read " + url, e); |
| } |
| } |
| for (String clazz : classesToScan) { |
| try { |
| ret.add(Class.forName(clazz)); |
| } catch (ClassNotFoundException e) { |
| throw new RuntimeException(e); |
| } |
| } |
| LOG.debug("Will use {} for validation", ret); |
| configClasses = ret; |
| } |
| return configClasses; |
| } |
| |
| /** |
| * Validates a field given field name as string uses Config.java as the default config class |
| * |
| * @param fieldName provided as a string |
| * @param conf map of confs |
| */ |
| public static void validateField(String fieldName, Map<String, Object> conf) { |
| validateField(fieldName, conf, getConfigClasses()); |
| } |
| |
| /** |
| * Validates a field given field name as string. |
| * |
| * @param fieldName provided as a string |
| * @param conf map of confs |
| * @param configs config class |
| */ |
| public static void validateField(String fieldName, Map<String, Object> conf, List<Class<?>> configs) { |
| Field field = null; |
| for (Class<?> clazz : configs) { |
| try { |
| field = clazz.getField(fieldName); |
| } catch (NoSuchFieldException e) { |
| //Ignored |
| } |
| } |
| if (field == null) { |
| throw new RuntimeException("Could not find " + fieldName + " in any of " + configs); |
| } |
| validateField(field, conf); |
| } |
| |
| /** |
| * Validates a field given field. Calls correct ValidatorField method based on which fields are declared for the corresponding |
| * annotation. |
| * |
| * @param field field that needs to be validated |
| * @param conf map of confs |
| */ |
| public static void validateField(Field field, Map<String, Object> conf) { |
| Annotation[] annotations = field.getAnnotations(); |
| if (annotations.length == 0) { |
| LOG.warn("Field {} does not have validator annotation", field); |
| } |
| try { |
| for (Annotation annotation : annotations) { |
| if (annotation.annotationType().equals(Deprecated.class)) { |
| LOG.warn("{} is a deprecated config please see {}.{} for more information.", |
| field.get(null), field.getDeclaringClass(), field.getName()); |
| continue; |
| } |
| String type = annotation.annotationType().getName(); |
| Class<?> validatorClass = null; |
| Class<?>[] classes = ConfigValidationAnnotations.class.getDeclaredClasses(); |
| //check if annotation is one of our |
| for (Class<?> clazz : classes) { |
| if (clazz.getName().equals(type)) { |
| validatorClass = clazz; |
| break; |
| } |
| } |
| if (validatorClass != null) { |
| Object v = validatorClass.cast(annotation); |
| String key = (String) field.get(null); |
| @SuppressWarnings("unchecked") |
| Class<Validator> clazz = (Class<Validator>) validatorClass |
| .getMethod(ConfigValidationAnnotations.ValidatorParams.VALIDATOR_CLASS).invoke(v); |
| Validator o = null; |
| Map<String, Object> params = getParamsFromAnnotation(validatorClass, v); |
| //two constructor signatures used to initialize validators. |
| //One constructor takes input a Map of arguments, the other doesn't take any arguments (default constructor) |
| //If validator has a constructor that takes a Map as an argument call that constructor |
| if (hasConstructor(clazz, Map.class)) { |
| o = clazz.getConstructor(Map.class).newInstance(params); |
| } else { //If not call default constructor |
| o = clazz.newInstance(); |
| } |
| o.validateField(field.getName(), conf.get(key)); |
| } |
| } |
| } catch (NoSuchMethodException | IllegalAccessException | InstantiationException | InvocationTargetException e) { |
| throw new RuntimeException(e); |
| } |
| } |
| |
| /** |
| * Validate topology conf. |
| * @param topoConf The topology conf. |
| */ |
| public static void validateTopoConf(Map<String, Object> topoConf) { |
| validateFields(topoConf, Arrays.asList(Config.class)); |
| } |
| |
| /** |
| * Validate all confs in map. |
| * |
| * @param conf map of configs |
| */ |
| public static void validateFields(Map<String, Object> conf) { |
| validateFields(conf, getConfigClasses()); |
| } |
| |
| /** |
| * Validate all confs in map. |
| * |
| * @param conf map of configs |
| * @param classes config class |
| */ |
| public static void validateFields(Map<String, Object> conf, List<Class<?>> classes) { |
| for (Class<?> clazz : classes) { |
| for (Field field : clazz.getDeclaredFields()) { |
| if (!isFieldAllowed(field)) { |
| continue; |
| } |
| Object keyObj = null; |
| try { |
| keyObj = field.get(null); |
| } catch (IllegalAccessException e) { |
| //This should not happen because we checked for PUBLIC in isFieldAllowed |
| throw new RuntimeException(e); |
| } |
| //make sure that defined key is string in case wrong stuff got put into Config.java |
| if (keyObj instanceof String) { |
| String confKey = (String) keyObj; |
| if (conf.containsKey(confKey)) { |
| validateField(field, conf); |
| } |
| } |
| } |
| } |
| } |
| |
| public static boolean isFieldAllowed(Field field) { |
| return field.getAnnotation(NotConf.class) == null |
| && String.class.equals(field.getType()) |
| && ((field.getModifiers() & DESIRED_FIELD_ACC) == DESIRED_FIELD_ACC) && !field.isSynthetic(); |
| } |
| |
| private static Map<String, Object> getParamsFromAnnotation(Class<?> validatorClass, Object v) |
| throws InvocationTargetException, IllegalAccessException { |
| Map<String, Object> params = new HashMap<String, Object>(); |
| for (Method method : validatorClass.getDeclaredMethods()) { |
| |
| Object value = null; |
| try { |
| value = (Object) method.invoke(v); |
| } catch (IllegalArgumentException ex) { |
| value = null; |
| } |
| if (value != null) { |
| params.put(method.getName(), value); |
| } |
| } |
| return params; |
| } |
| |
| private static boolean hasConstructor(Class<?> clazz, Class<?> paramClass) { |
| Class<?>[] classes = { paramClass }; |
| try { |
| clazz.getConstructor(classes); |
| } catch (NoSuchMethodException e) { |
| return false; |
| } |
| return true; |
| } |
| |
| public abstract static class Validator { |
| public Validator(Map<String, Object> params) { |
| } |
| |
| public Validator() { |
| } |
| |
| public abstract void validateField(String name, Object o); |
| } |
| |
| /** |
| * Validates if an object is not null. |
| */ |
| |
| public static class NotNullValidator extends Validator { |
| |
| @Override |
| public void validateField(String name, Object o) { |
| if (o == null) { |
| throw new IllegalArgumentException("Field " + name + "cannot be null! Actual value: " + o); |
| } |
| } |
| } |
| |
| /** |
| * Validates basic types. |
| */ |
| public static class SimpleTypeValidator extends Validator { |
| |
| private Class<?> type; |
| |
| public SimpleTypeValidator(Map<String, Object> params) { |
| this.type = (Class<?>) params.get(ConfigValidationAnnotations.ValidatorParams.TYPE); |
| } |
| |
| public static void validateField(String name, Class<?> type, Object o) { |
| if (o == null) { |
| return; |
| } |
| if (type.isInstance(o)) { |
| return; |
| } |
| throw new IllegalArgumentException( |
| "Field " + name + " must be of type " + type + ". Object: " + o + " actual type: " + o.getClass()); |
| } |
| |
| @Override |
| public void validateField(String name, Object o) { |
| validateField(name, this.type, o); |
| } |
| } |
| |
| /** |
| * Checks if the named type derives from the specified Class. |
| */ |
| public static class DerivedTypeValidator extends Validator { |
| |
| private Class<?> baseType; |
| |
| public DerivedTypeValidator(Map<String, Object> params) { |
| this.baseType = (Class<?>) params.get(ValidatorParams.BASE_TYPE); |
| } |
| |
| public static void validateField(String name, Class<?> baseType, Object actualTypeName) { |
| if (actualTypeName == null) { |
| return; |
| } |
| try { |
| Class<?> actualType = Class.forName(actualTypeName.toString()); |
| if (baseType.isAssignableFrom(actualType)) { |
| return; |
| } |
| throw new IllegalArgumentException( |
| "Field " + name + " must represent a type that derives from '" + baseType + "'. Specified type = " + actualTypeName); |
| } catch (ClassNotFoundException e) { |
| throw new IllegalArgumentException(e.getMessage()); |
| } |
| } |
| |
| @Override |
| public void validateField(String name, Object actualTypeName) { |
| validateField(name, this.baseType, actualTypeName); |
| } |
| } |
| |
| public static class StringValidator extends Validator { |
| |
| private HashSet<String> acceptedValues = null; |
| |
| public StringValidator() { |
| } |
| |
| public StringValidator(Map<String, Object> params) { |
| |
| this.acceptedValues = |
| new HashSet<String>(Arrays.asList((String[]) params.get(ConfigValidationAnnotations.ValidatorParams.ACCEPTED_VALUES))); |
| |
| if (this.acceptedValues.isEmpty() || (this.acceptedValues.size() == 1 && this.acceptedValues.contains(""))) { |
| this.acceptedValues = null; |
| } |
| } |
| |
| @Override |
| public void validateField(String name, Object o) { |
| SimpleTypeValidator.validateField(name, String.class, o); |
| if (this.acceptedValues != null) { |
| if (!this.acceptedValues.contains((String) o)) { |
| throw new IllegalArgumentException( |
| "Field " + name + " is not an accepted value. Value: " + o + " Accepted values: " + this.acceptedValues); |
| } |
| } |
| } |
| } |
| |
| public static class BooleanValidator extends Validator { |
| |
| @Override |
| public void validateField(String name, Object o) { |
| SimpleTypeValidator.validateField(name, Boolean.class, o); |
| } |
| } |
| |
| public static class NumberValidator extends Validator { |
| |
| @Override |
| public void validateField(String name, Object o) { |
| SimpleTypeValidator.validateField(name, Number.class, o); |
| } |
| } |
| |
| public static class DoubleValidator extends Validator { |
| |
| @Override |
| public void validateField(String name, Object o) { |
| SimpleTypeValidator.validateField(name, Double.class, o); |
| } |
| } |
| |
| /** |
| * Validates a Integer. |
| */ |
| public static class IntegerValidator extends Validator { |
| |
| @Override |
| public void validateField(String name, Object o) { |
| validateInteger(name, o); |
| } |
| |
| public void validateInteger(String name, Object o) { |
| if (o == null) { |
| return; |
| } |
| final long i; |
| if (o instanceof Number |
| && (i = ((Number) o).longValue()) == ((Number) o).doubleValue()) { |
| if (i <= Integer.MAX_VALUE && i >= Integer.MIN_VALUE) { |
| return; |
| } |
| } |
| throw new IllegalArgumentException("Field " + name + " must be an Integer within type range."); |
| } |
| } |
| |
| /** |
| * Validates an entry for ImpersonationAclUser. |
| */ |
| public static class ImpersonationAclUserEntryValidator extends Validator { |
| |
| @Override |
| public void validateField(String name, Object o) { |
| if (o == null) { |
| return; |
| } |
| ConfigValidationUtils.NestableFieldValidator validator = |
| ConfigValidationUtils.mapFv(ConfigValidationUtils.fv(String.class, false), |
| ConfigValidationUtils.listFv(String.class, false), false); |
| validator.validateField(name, o); |
| @SuppressWarnings("unchecked") |
| Map<String, List<String>> mapObject = (Map<String, List<String>>) o; |
| if (!mapObject.containsKey("hosts")) { |
| throw new IllegalArgumentException(name + " should contain Map entry with key: hosts"); |
| } |
| if (!mapObject.containsKey("groups")) { |
| throw new IllegalArgumentException(name + " should contain Map entry with key: groups"); |
| } |
| } |
| } |
| |
| /** |
| * validates a list of has no duplicates. |
| */ |
| public static class NoDuplicateInListValidator extends Validator { |
| |
| @Override |
| public void validateField(String name, Object field) { |
| if (field == null) { |
| return; |
| } |
| //check if iterable |
| SimpleTypeValidator.validateField(name, Iterable.class, field); |
| HashSet<Object> objectSet = new HashSet<Object>(); |
| for (Object o : (Iterable<?>) field) { |
| if (objectSet.contains(o)) { |
| throw new IllegalArgumentException(name + " should contain no duplicate elements. Duplicated element: " + o); |
| } |
| objectSet.add(o); |
| } |
| } |
| } |
| |
| /** |
| * Validates a String or a list of Strings. |
| */ |
| public static class StringOrStringListValidator extends Validator { |
| |
| private ConfigValidationUtils.FieldValidator fv = ConfigValidationUtils.listFv(String.class, false); |
| |
| @Override |
| public void validateField(String name, Object o) { |
| |
| if (o == null || o instanceof String) { |
| // A null value or a String value is acceptable |
| return; |
| } |
| this.fv.validateField(name, o); |
| } |
| } |
| |
| /** |
| * Validates Kryo Registration. |
| */ |
| public static class KryoRegValidator extends Validator { |
| |
| @SuppressWarnings("unchecked") |
| @Override |
| public void validateField(String name, Object o) { |
| if (o == null) { |
| return; |
| } |
| if (o instanceof Iterable) { |
| for (Object e : (Iterable<?>) o) { |
| if (e instanceof Map) { |
| for (Map.Entry<Object, Object> entry : ((Map<Object, Object>) e).entrySet()) { |
| if (!(entry.getKey() instanceof String) |
| || !(entry.getValue() instanceof String)) { |
| throw new IllegalArgumentException( |
| "Each element of the list " + name + " must be a String or a Map of Strings"); |
| } |
| } |
| } else if (!(e instanceof String)) { |
| throw new IllegalArgumentException( |
| "Each element of the list " + name + " must be a String or a Map of Strings"); |
| } |
| } |
| return; |
| } |
| throw new IllegalArgumentException( |
| "Field " + name + " must be an Iterable containing only Strings or Maps of Strings"); |
| } |
| } |
| |
| /* |
| * Methods for validating confs |
| */ |
| |
| /** |
| * Validates if a number is a power of 2. |
| */ |
| public static class PowerOf2Validator extends Validator { |
| |
| @Override |
| public void validateField(String name, Object o) { |
| if (o == null) { |
| return; |
| } |
| final long i; |
| if (o instanceof Number |
| && (i = ((Number) o).longValue()) == ((Number) o).doubleValue()) { |
| // Test whether the integer is a power of 2. |
| if (i > 0 && (i & (i - 1)) == 0) { |
| return; |
| } |
| } |
| throw new IllegalArgumentException("Field " + name + " must be a power of 2."); |
| } |
| } |
| |
| /** |
| * Validates each entry in a list. |
| */ |
| public static class ListEntryTypeValidator extends Validator { |
| |
| private Class<?> type; |
| |
| public ListEntryTypeValidator(Map<String, Object> params) { |
| this.type = (Class<?>) params.get(ConfigValidationAnnotations.ValidatorParams.TYPE); |
| } |
| |
| public static void validateField(String name, Class<?> type, Object o) { |
| ConfigValidationUtils.NestableFieldValidator validator = ConfigValidationUtils.listFv(type, false); |
| validator.validateField(name, o); |
| } |
| |
| @Override |
| public void validateField(String name, Object o) { |
| validateField(name, this.type, o); |
| } |
| } |
| |
| /** |
| * Validates each entry in a list against a list of custom Validators. Each validator in the list of validators must inherit or be an |
| * instance of Validator class |
| */ |
| public static class ListEntryCustomValidator extends Validator { |
| |
| private Class<?>[] entryValidators; |
| |
| public ListEntryCustomValidator(Map<String, Object> params) { |
| this.entryValidators = (Class<?>[]) params.get(ConfigValidationAnnotations.ValidatorParams.ENTRY_VALIDATOR_CLASSES); |
| } |
| |
| public static void validateField(String name, Class<?>[] validators, Object o) |
| throws IllegalAccessException, InstantiationException, NoSuchMethodException, InvocationTargetException { |
| if (o == null) { |
| return; |
| } |
| //check if iterable |
| SimpleTypeValidator.validateField(name, Iterable.class, o); |
| for (Object entry : (Iterable<?>) o) { |
| for (Class<?> validator : validators) { |
| Object v = validator.getConstructor().newInstance(); |
| if (v instanceof Validator) { |
| ((Validator) v).validateField(name + " list entry", entry); |
| } else { |
| LOG.warn( |
| "validator: {} cannot be used in ListEntryCustomValidator. Individual entry validators must a instance of " |
| + "Validator class", |
| validator.getName()); |
| } |
| } |
| } |
| } |
| |
| @Override |
| public void validateField(String name, Object o) { |
| try { |
| validateField(name, this.entryValidators, o); |
| } catch (NoSuchMethodException | IllegalAccessException | InvocationTargetException | InstantiationException e) { |
| throw new RuntimeException(e); |
| } |
| } |
| } |
| |
| /** |
| * validates each key and value in a map of a certain type. |
| */ |
| public static class MapEntryTypeValidator extends Validator { |
| |
| private Class<?> keyType; |
| private Class<?> valueType; |
| |
| public MapEntryTypeValidator(Map<String, Object> params) { |
| this.keyType = (Class<?>) params.get(ConfigValidationAnnotations.ValidatorParams.KEY_TYPE); |
| this.valueType = (Class<?>) params.get(ConfigValidationAnnotations.ValidatorParams.VALUE_TYPE); |
| } |
| |
| public static void validateField(String name, Class<?> keyType, Class<?> valueType, Object o) { |
| ConfigValidationUtils.NestableFieldValidator validator = ConfigValidationUtils.mapFv(keyType, valueType, false); |
| validator.validateField(name, o); |
| } |
| |
| @Override |
| public void validateField(String name, Object o) { |
| validateField(name, this.keyType, this.valueType, o); |
| } |
| } |
| |
| /** |
| * validates each key and each value against the respective arrays of validators. |
| */ |
| public static class MapEntryCustomValidator extends Validator { |
| |
| private Class<?>[] keyValidators; |
| private Class<?>[] valueValidators; |
| |
| public MapEntryCustomValidator(Map<String, Object> params) { |
| this.keyValidators = (Class<?>[]) params.get(ConfigValidationAnnotations.ValidatorParams.KEY_VALIDATOR_CLASSES); |
| this.valueValidators = (Class<?>[]) params.get(ConfigValidationAnnotations.ValidatorParams.VALUE_VALIDATOR_CLASSES); |
| } |
| |
| @SuppressWarnings("unchecked") |
| public static void validateField(String name, Class<?>[] keyValidators, Class<?>[] valueValidators, Object o) |
| throws IllegalAccessException, InstantiationException, NoSuchMethodException, InvocationTargetException { |
| if (o == null) { |
| return; |
| } |
| //check if Map |
| SimpleTypeValidator.validateField(name, Map.class, o); |
| for (Map.Entry<Object, Object> entry : ((Map<Object, Object>) o).entrySet()) { |
| for (Class<?> kv : keyValidators) { |
| Object keyValidator = kv.getConstructor().newInstance(); |
| if (keyValidator instanceof Validator) { |
| ((Validator) keyValidator).validateField(name + " Map key", entry.getKey()); |
| } else { |
| LOG.warn( |
| "validator: {} cannot be used in MapEntryCustomValidator to validate keys. Individual entry validators must " |
| + "a instance of Validator class", |
| kv.getName()); |
| } |
| } |
| for (Class<?> vv : valueValidators) { |
| Object valueValidator = vv.getConstructor().newInstance(); |
| if (valueValidator instanceof Validator) { |
| ((Validator) valueValidator).validateField(name + " Map value", entry.getValue()); |
| } else { |
| LOG.warn( |
| "validator: {} cannot be used in MapEntryCustomValidator to validate values. Individual entry validators " |
| + "must a instance of Validator class", |
| vv.getName()); |
| } |
| } |
| } |
| } |
| |
| @Override |
| public void validateField(String name, Object o) { |
| try { |
| validateField(name, this.keyValidators, this.valueValidators, o); |
| } catch (IllegalAccessException | InstantiationException | NoSuchMethodException | InvocationTargetException e) { |
| throw new RuntimeException(e); |
| } |
| } |
| } |
| |
| /** |
| * Validates a positive number. |
| */ |
| public static class PositiveNumberValidator extends Validator { |
| |
| private boolean includeZero; |
| |
| public PositiveNumberValidator() { |
| this.includeZero = false; |
| } |
| |
| public PositiveNumberValidator(Map<String, Object> params) { |
| this.includeZero = (boolean) params.get(ConfigValidationAnnotations.ValidatorParams.INCLUDE_ZERO); |
| } |
| |
| public static void validateField(String name, boolean includeZero, Object o) { |
| if (o == null) { |
| return; |
| } |
| if (o instanceof Number) { |
| if (includeZero) { |
| if (((Number) o).doubleValue() >= 0.0) { |
| return; |
| } |
| } else { |
| if (((Number) o).doubleValue() > 0.0) { |
| return; |
| } |
| } |
| } |
| throw new IllegalArgumentException("Field " + name + " must be a Positive Number"); |
| } |
| |
| @Override |
| public void validateField(String name, Object o) { |
| validateField(name, this.includeZero, o); |
| } |
| } |
| |
| public static class ClusterMetricRegistryValidator extends Validator { |
| |
| @Override |
| public void validateField(String name, Object o) { |
| if (o == null) { |
| return; |
| } |
| SimpleTypeValidator.validateField(name, Map.class, o); |
| if (!((Map<?, ?>) o).containsKey("class")) { |
| throw new IllegalArgumentException("Field " + name + " must have map entry with key: class"); |
| } |
| |
| SimpleTypeValidator.validateField(name, String.class, ((Map<?, ?>) o).get("class")); |
| } |
| } |
| |
| public static class MetricRegistryValidator extends Validator { |
| |
| @Override |
| public void validateField(String name, Object o) { |
| if (o == null) { |
| return; |
| } |
| SimpleTypeValidator.validateField(name, Map.class, o); |
| if (!((Map<?, ?>) o).containsKey("class")) { |
| throw new IllegalArgumentException("Field " + name + " must have map entry with key: class"); |
| } |
| if (!((Map<?, ?>) o).containsKey("parallelism.hint")) { |
| throw new IllegalArgumentException("Field " + name + " must have map entry with key: parallelism.hint"); |
| } |
| |
| SimpleTypeValidator.validateField(name, String.class, ((Map<?, ?>) o).get("class")); |
| new IntegerValidator().validateField(name, ((Map<?, ?>) o).get("parallelism.hint")); |
| } |
| } |
| |
| public static class MetricReportersValidator extends Validator { |
| private static final String NIMBUS = "nimbus"; |
| private static final String SUPERVISOR = "supervisor"; |
| private static final String WORKER = "worker"; |
| private static final String CLASS = "class"; |
| private static final String FILTER = "filter"; |
| private static final String DAEMONS = "daemons"; |
| |
| @Override |
| public void validateField(String name, Object o) { |
| if (o == null) { |
| return; |
| } |
| SimpleTypeValidator.validateField(name, Map.class, o); |
| if (!((Map) o).containsKey(CLASS)) { |
| throw new IllegalArgumentException("Field " + name + " must have map entry with key: class"); |
| } |
| if (!((Map) o).containsKey(DAEMONS)) { |
| throw new IllegalArgumentException("Field " + name + " must have map entry with key: daemons"); |
| } else { |
| // daemons can only be 'nimbus', 'supervisor', or 'worker' |
| Object list = ((Map) o).get(DAEMONS); |
| if (!(list instanceof List)) { |
| throw new IllegalArgumentException("Field 'daemons' must be a non-null list."); |
| } |
| List daemonList = (List) list; |
| for (Object string : daemonList) { |
| if (string instanceof String |
| && (string.equals(NIMBUS) || string.equals(SUPERVISOR) || string.equals(WORKER))) { |
| continue; |
| } |
| throw new IllegalArgumentException("Field 'daemons' must contain at least one of the following:" |
| + " \"nimbus\", \"supervisor\", or \"worker\""); |
| } |
| |
| } |
| if (((Map) o).containsKey(FILTER)) { |
| Map filterMap = (Map) ((Map) o).get(FILTER); |
| SimpleTypeValidator.validateField(CLASS, String.class, filterMap.get(CLASS)); |
| } |
| SimpleTypeValidator.validateField(name, String.class, ((Map) o).get(CLASS)); |
| } |
| } |
| |
| public static class EventLoggerRegistryValidator extends Validator { |
| |
| @Override |
| public void validateField(String name, Object o) { |
| if (o == null) { |
| return; |
| } |
| SimpleTypeValidator.validateField(name, Map.class, o); |
| if (!((Map<?, ?>) o).containsKey("class")) { |
| throw new IllegalArgumentException("Field " + name + " must have map entry with key: class"); |
| } |
| |
| SimpleTypeValidator.validateField(name, String.class, ((Map<?, ?>) o).get("class")); |
| |
| if (((Map<?, ?>) o).containsKey("arguments")) { |
| SimpleTypeValidator.validateField(name, Map.class, ((Map<?, ?>) o).get("arguments")); |
| } |
| } |
| } |
| |
| public static class MapOfStringToMapOfStringToObjectValidator extends Validator { |
| @Override |
| public void validateField(String name, Object o) { |
| ConfigValidationUtils.NestableFieldValidator validator = |
| ConfigValidationUtils.mapFv(ConfigValidationUtils.fv(String.class, false), |
| ConfigValidationUtils.mapFv(String.class, Object.class, true), true); |
| validator.validateField(name, o); |
| } |
| } |
| |
| public static class PacemakerAuthTypeValidator extends Validator { |
| @Override |
| public void validateField(String name, Object o) { |
| if (o == null) { |
| throw new IllegalArgumentException("Field " + name + " must be set."); |
| } |
| |
| if (o instanceof String |
| && (((String) o).equals("NONE") || ((String) o).equals("DIGEST") |
| || ((String) o).equals("KERBEROS"))) { |
| return; |
| } |
| throw new IllegalArgumentException("Field " + name + " must be one of \"NONE\", \"DIGEST\", or \"KERBEROS\""); |
| } |
| } |
| |
| public static class UserResourcePoolEntryValidator extends Validator { |
| |
| @Override |
| public void validateField(String name, Object o) { |
| if (o == null) { |
| return; |
| } |
| SimpleTypeValidator.validateField(name, Map.class, o); |
| Map<?, ?> m = (Map<?, ?>) o; |
| if (!m.containsKey("cpu")) { |
| throw new IllegalArgumentException("Field " + name + " must have map entry with key: cpu"); |
| } |
| if (!m.containsKey("memory")) { |
| throw new IllegalArgumentException("Field " + name + " must have map entry with key: memory"); |
| } |
| |
| SimpleTypeValidator.validateField(name, Number.class, m.get("cpu")); |
| SimpleTypeValidator.validateField(name, Number.class, m.get("memory")); |
| } |
| } |
| |
| public static class ImplementsClassValidator extends Validator { |
| |
| Class<?> classImplements; |
| |
| public ImplementsClassValidator(Map<String, Object> params) { |
| this.classImplements = (Class<?>) params.get(ConfigValidationAnnotations.ValidatorParams.IMPLEMENTS_CLASS); |
| } |
| |
| @Override |
| public void validateField(String name, Object o) { |
| if (o == null) { |
| return; |
| } |
| SimpleTypeValidator.validateField(name, String.class, o); |
| String className = (String) o; |
| try { |
| Class<?> objectClass = Class.forName(className); |
| if (!this.classImplements.isAssignableFrom(objectClass)) { |
| throw new IllegalArgumentException("Field " + name + " with value " + o |
| + " does not implement " + this.classImplements.getName()); |
| } |
| } catch (ClassNotFoundException e) { |
| //To support topologies of older version to run, we might have to loose the constraints so that |
| //the configs of older version can pass the validation. |
| if (className.startsWith("backtype.storm")) { |
| LOG.error("ClassNotFoundException: {}", className); |
| LOG.warn("Replace backtype.storm with org.apache.storm and try to validate again"); |
| LOG.warn("We loosen some constraints here to support topologies of older version running on the current version"); |
| validateField(name, className.replace("backtype.storm", "org.apache.storm")); |
| } else { |
| throw new RuntimeException("Failed to validate config " + name + " with value " + className, e); |
| } |
| } |
| } |
| } |
| |
| /** |
| * Validates a list of a list of Strings. |
| */ |
| public static class ListOfListOfStringValidator extends Validator { |
| |
| @Override |
| public void validateField(String name, Object o) throws IllegalArgumentException { |
| if (o == null) { |
| return; |
| } |
| if (o instanceof List) { |
| for (Object entry1 : (List) o) { |
| if (entry1 instanceof List) { |
| for (Object entry2 : (List) entry1) { |
| if (!(entry2 instanceof String)) { |
| throw new IllegalArgumentException( |
| "Field " + name + " must be an Iterable containing only List of List of Strings"); |
| } |
| } |
| } else { |
| throw new IllegalArgumentException( |
| "Field " + name + " must be an Iterable containing only List of List of Strings"); |
| } |
| } |
| } else { |
| throw new IllegalArgumentException( |
| "Field " + name + " must be an Iterable containing only List of List of Strings"); |
| } |
| } |
| } |
| } |