blob: d8f256c50804f0279d71699feed6457727ac4b92 [file] [log] [blame]
/**
* 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.pulsar.config.validation;
import java.lang.reflect.InvocationTargetException;
import java.util.Arrays;
import java.util.HashSet;
import java.util.Map;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* System defined Validator Annotations.
*/
public class ValidatorImpls {
private static final Logger log = LoggerFactory.getLogger(ValidatorImpls.class);
/**
* 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(String.format("Field '%s' must be a Positive Number", name));
}
@Override
public void validateField(String name, Object o) {
validateField(name, this.includeZero, 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(String.format("Field '%s' cannot be null!", name));
}
}
}
/**
* 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 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 that the item implements a certain type.
*/
public static class ImplementsClassValidator extends Validator {
Class<?> classImplements;
public ImplementsClassValidator(Map<String, Object> params) {
this.classImplements = (Class<?>) params.get(ConfigValidationAnnotations.ValidatorParams.IMPLEMENTS_CLASS);
}
public ImplementsClassValidator(Class<?> classImplements) {
this.classImplements = classImplements;
}
@Override
public void validateField(String name, Object o) {
if (o == null) {
return;
}
SimpleTypeValidator.validateField(name, String.class, o);
String className = (String) o;
try {
ClassLoader clsLoader = Thread.currentThread().getContextClassLoader();
Class<?> objectClass = clsLoader.loadClass(className);
if (!this.classImplements.isAssignableFrom(objectClass)) {
throw new IllegalArgumentException(
String.format("Field '%s' with value '%s' does not implement %s ",
name, o, this.classImplements.getName()));
}
} catch (ClassNotFoundException e) {
throw new RuntimeException(e);
}
}
}
/**
* validates class implements one of these classes.
*/
public static class ImplementsClassesValidator extends Validator {
Class<?>[] classesImplements;
public ImplementsClassesValidator(Map<String, Object> params) {
this.classesImplements = (Class<?>[]) params.get(
ConfigValidationAnnotations.ValidatorParams.IMPLEMENTS_CLASSES);
}
@Override
public void validateField(String name, Object o) {
if (o == null) {
return;
}
SimpleTypeValidator.validateField(name, String.class, o);
String className = (String) o;
int count = 0;
for (Class<?> classImplements : classesImplements) {
Class<?> objectClass = null;
try {
objectClass = loadClass(className);
} catch (ClassNotFoundException e) {
throw new IllegalArgumentException("Cannot find/load class " + className);
}
if (classImplements.isAssignableFrom(objectClass)) {
count++;
}
}
if (count == 0) {
throw new IllegalArgumentException(
String.format("Field '%s' with value '%s' does not implement any of these classes %s",
name, o, Arrays.toString(classesImplements)));
}
}
}
/**
* 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 be 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 that the string is equal to one of the specified ones in the list.
*/
public static class StringValidator extends Validator {
public StringValidator() {
}
private HashSet<String> acceptedValues = null;
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);
}
}
}
}
/**
* 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 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);
}
}
private static Class<?> loadClass(String className) throws ClassNotFoundException {
Class<?> objectClass;
try {
objectClass = Class.forName(className);
} catch (ClassNotFoundException e) {
ClassLoader clsLoader = Thread.currentThread().getContextClassLoader();
if (clsLoader != null) {
objectClass = clsLoader.loadClass(className);
} else {
throw e;
}
}
return objectClass;
}
}