/*
* 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.nifi.processor.util;
import java.io.File;
import java.net.MalformedURLException;
import java.net.URI;
import java.net.URL;
import java.nio.charset.Charset;
import java.nio.charset.UnsupportedCharsetException;
import java.text.NumberFormat;
import java.text.ParseException;
import java.time.Instant;
import java.util.Arrays;
import java.util.List;
import java.util.concurrent.TimeUnit;
import java.util.regex.Pattern;
import org.apache.nifi.components.PropertyValue;
import org.apache.nifi.components.ValidationContext;
import org.apache.nifi.components.ValidationResult;
import org.apache.nifi.components.Validator;
import org.apache.nifi.expression.AttributeExpression.ResultType;
import org.apache.nifi.flowfile.FlowFile;
import org.apache.nifi.processor.DataUnit;
import org.apache.nifi.util.FormatUtils;
public class StandardValidators {
//
//
// STATICALLY DEFINED VALIDATORS
//
//
public static final Validator ATTRIBUTE_KEY_VALIDATOR = new Validator() {
@Override
public ValidationResult validate(final String subject, final String input, final ValidationContext context) {
final ValidationResult.Builder builder = new ValidationResult.Builder();
builder.subject(subject).input(input);
if (context.isExpressionLanguageSupported(subject) && context.isExpressionLanguagePresent(input)) {
return builder.valid(true).explanation("Contains Expression Language").build();
}
try {
FlowFile.KeyValidator.validateKey(input);
builder.valid(true);
} catch (final IllegalArgumentException e) {
builder.valid(false).explanation(e.getMessage());
}
return builder.build();
}
};
public static final Validator ATTRIBUTE_KEY_PROPERTY_NAME_VALIDATOR = new Validator() {
@Override
public ValidationResult validate(final String subject, final String input, final ValidationContext context) {
final ValidationResult.Builder builder = new ValidationResult.Builder();
builder.subject("Property Name").input(subject);
if (context.isExpressionLanguageSupported(subject) && context.isExpressionLanguagePresent(input)) {
return builder.valid(true).explanation("Contains Expression Language").build();
}
try {
FlowFile.KeyValidator.validateKey(subject);
builder.valid(true);
} catch (final IllegalArgumentException e) {
builder.valid(false).explanation(e.getMessage());
}
return builder.build();
}
};
public static final Validator POSITIVE_INTEGER_VALIDATOR = new Validator() {
@Override
public ValidationResult validate(final String subject, final String value, final ValidationContext context) {
if (context.isExpressionLanguageSupported(subject) && context.isExpressionLanguagePresent(value)) {
return new ValidationResult.Builder().subject(subject).input(value).explanation("Expression Language Present").valid(true).build();
}
String reason = null;
try {
final int intVal = Integer.parseInt(value);
if (intVal <= 0) {
reason = "not a positive value";
}
} catch (final NumberFormatException e) {
reason = "not a valid integer";
}
return new ValidationResult.Builder().subject(subject).input(value).explanation(reason).valid(reason == null).build();
}
};
public static final Validator POSITIVE_LONG_VALIDATOR = new Validator() {
@Override
public ValidationResult validate(final String subject, final String value, final ValidationContext context) {
if (context.isExpressionLanguageSupported(subject) && context.isExpressionLanguagePresent(value)) {
return new ValidationResult.Builder().subject(subject).input(value).explanation("Expression Language Present").valid(true).build();
}
String reason = null;
try {
final long longVal = Long.parseLong(value);
if (longVal <= 0) {
reason = "not a positive value";
}
} catch (final NumberFormatException e) {
reason = "not a valid 64-bit integer";
}
return new ValidationResult.Builder().subject(subject).input(value).explanation(reason).valid(reason == null).build();
}
};
public static final Validator NUMBER_VALIDATOR = new Validator() {
@Override
public ValidationResult validate(final String subject, final String value, final ValidationContext context) {
if (context.isExpressionLanguageSupported(subject) && context.isExpressionLanguagePresent(value)) {
return new ValidationResult.Builder().subject(subject).input(value).explanation("Expression Language Present").valid(true).build();
}
String reason = null;
try {
NumberFormat.getInstance().parse(value);
} catch (ParseException e) {
reason = "not a valid Number";
}
return new ValidationResult.Builder().subject(subject).input(value).explanation(reason).valid(reason == null).build();
}
};
public static final Validator PORT_VALIDATOR = createLongValidator(1, 65535, true);
/**
* {@link Validator} that ensures that value's length > 0
*/
public static final Validator NON_EMPTY_VALIDATOR = new Validator() {
@Override
public ValidationResult validate(final String subject, final String value, final ValidationContext context) {
return new ValidationResult.Builder().subject(subject).input(value).valid(value != null && !value.isEmpty()).explanation(subject + " cannot be empty").build();
}
};
/**
* {@link Validator} that ensures that value's length > 0 and that expression language is present
*/
public static final Validator NON_EMPTY_EL_VALIDATOR = new Validator() {
@Override
public ValidationResult validate(String subject, String input, ValidationContext context) {
if (context.isExpressionLanguageSupported(subject) && context.isExpressionLanguagePresent(input)) {
return new ValidationResult.Builder().subject(subject).input(input).explanation("Expression Language Present").valid(true).build();
}
return StandardValidators.NON_EMPTY_VALIDATOR.validate(subject, input, context);
}
};
/**
* {@link Validator} that ensures that value is a non-empty comma separated list of hostname:port
*/
public static final Validator HOSTNAME_PORT_LIST_VALIDATOR = new Validator() {
@Override
public ValidationResult validate(String subject, String input, ValidationContext context) {
// expression language
if (context.isExpressionLanguageSupported(subject) && context.isExpressionLanguagePresent(input)) {
return new ValidationResult.Builder().subject(subject).input(input).explanation("Expression Language Present").valid(true).build();
}
// not empty
ValidationResult nonEmptyValidatorResult = StandardValidators.NON_EMPTY_VALIDATOR.validate(subject, input, context);
if (!nonEmptyValidatorResult.isValid()) {
return nonEmptyValidatorResult;
}
// check format
final List<String> hostnamePortList = Arrays.asList(input.split(","));
for (String hostnamePort : hostnamePortList) {
String[] addresses = hostnamePort.split(":");
// Protect against invalid input like http://127.0.0.1:9300 (URL scheme should not be there)
if (addresses.length != 2) {
return new ValidationResult.Builder().subject(subject).input(input).explanation(
"Must be in hostname:port form (no scheme such as http://").valid(false).build();
}
// Validate the port
String port = addresses[1].trim();
ValidationResult portValidatorResult = StandardValidators.PORT_VALIDATOR.validate(subject, port, context);
if (!portValidatorResult.isValid()) {
return portValidatorResult;
}
}
return new ValidationResult.Builder().subject(subject).input(input).explanation("Valid cluster definition").valid(true).build();
}
};
/**
* {@link Validator} that ensures that value has 1+ non-whitespace
* characters
*/
public static final Validator NON_BLANK_VALIDATOR = new Validator() {
@Override
public ValidationResult validate(final String subject, final String value, final ValidationContext context) {
return new ValidationResult.Builder().subject(subject).input(value)
.valid(value != null && !value.trim().isEmpty())
.explanation(subject
+ " must contain at least one character that is not white space").build();
}
};
public static final Validator BOOLEAN_VALIDATOR = new Validator() {
@Override
public ValidationResult validate(final String subject, final String value, final ValidationContext context) {
if (context.isExpressionLanguageSupported(subject) && context.isExpressionLanguagePresent(value)) {
return new ValidationResult.Builder().subject(subject).input(value).explanation("Expression Language Present").valid(true).build();
}
final boolean valid = "true".equalsIgnoreCase(value) || "false".equalsIgnoreCase(value);
final String explanation = valid ? null : "Value must be 'true' or 'false'";
return new ValidationResult.Builder().subject(subject).input(value).valid(valid).explanation(explanation).build();
}
};
public static final Validator INTEGER_VALIDATOR = new Validator() {
@Override
public ValidationResult validate(final String subject, final String value, final ValidationContext context) {
if (context.isExpressionLanguageSupported(subject) && context.isExpressionLanguagePresent(value)) {
return new ValidationResult.Builder().subject(subject).input(value).explanation("Expression Language Present").valid(true).build();
}
String reason = null;
try {
Integer.parseInt(value);
} catch (final NumberFormatException e) {
reason = "not a valid integer";
}
return new ValidationResult.Builder().subject(subject).input(value).explanation(reason).valid(reason == null).build();
}
};
public static final Validator LONG_VALIDATOR = new Validator() {
@Override
public ValidationResult validate(final String subject, final String value, final ValidationContext context) {
if (context.isExpressionLanguageSupported(subject) && context.isExpressionLanguagePresent(value)) {
return new ValidationResult.Builder().subject(subject).input(value).explanation("Expression Language Present").valid(true).build();
}
String reason = null;
try {
Long.parseLong(value);
} catch (final NumberFormatException e) {
reason = "not a valid Long";
}
return new ValidationResult.Builder().subject(subject).input(value).explanation(reason).valid(reason == null).build();
}
};
public static final Validator ISO8061_INSTANT_VALIDATOR = new Validator() {
@Override
public ValidationResult validate(final String subject, final String input, final ValidationContext context) {
try {
Instant.parse(input);
return new ValidationResult.Builder().subject(subject).input(input).explanation("Valid ISO8061 Instant Date").valid(true).build();
} catch (final Exception e) {
return new ValidationResult.Builder().subject(subject).input(input).explanation("Not a valid ISO8061 Instant Date, please enter in UTC time").valid(false).build();
}
}
};
public static final Validator NON_NEGATIVE_INTEGER_VALIDATOR = new Validator() {
@Override
public ValidationResult validate(final String subject, final String value, final ValidationContext context) {
if (context.isExpressionLanguageSupported(subject) && context.isExpressionLanguagePresent(value)) {
return new ValidationResult.Builder().subject(subject).input(value).explanation("Expression Language Present").valid(true).build();
}
String reason = null;
try {
final int intVal = Integer.parseInt(value);
if (intVal < 0) {
reason = "value is negative";
}
} catch (final NumberFormatException e) {
reason = "value is not a valid integer";
}
return new ValidationResult.Builder().subject(subject).input(value).explanation(reason).valid(reason == null).build();
}
};
public static final Validator CHARACTER_SET_VALIDATOR = new Validator() {
@Override
public ValidationResult validate(final String subject, final String value, final ValidationContext context) {
if (context.isExpressionLanguageSupported(subject) && context.isExpressionLanguagePresent(value)) {
final ResultType resultType = context.newExpressionLanguageCompiler().getResultType(value);
if (!resultType.equals(ResultType.STRING)) {
return new ValidationResult.Builder()
.subject(subject)
.input(value)
.valid(false)
.explanation("Expected Attribute Query to return type " + ResultType.STRING + " but query returns type " + resultType)
.build();
}
return new ValidationResult.Builder().subject(subject).input(value).explanation("Expression Language Present").valid(true).build();
}
String reason = null;
try {
if (!Charset.isSupported(value)) {
reason = "Character Set is not supported by this JVM.";
}
} catch (final UnsupportedCharsetException uce) {
reason = "Character Set is not supported by this JVM.";
} catch (final IllegalArgumentException iae) {
reason = "Character Set value cannot be null.";
}
return new ValidationResult.Builder().subject(subject).input(value).explanation(reason).valid(reason == null).build();
}
};
/**
* URL Validator that does not allow the Expression Language to be used
*/
public static final Validator URL_VALIDATOR = createURLValidator();
public static final Validator URI_VALIDATOR = new Validator() {
@Override
public ValidationResult validate(final String subject, final String input, final ValidationContext context) {
if (context.isExpressionLanguageSupported(subject) && context.isExpressionLanguagePresent(input)) {
return new ValidationResult.Builder().subject(subject).input(input).explanation("Expression Language Present").valid(true).build();
}
try {
new URI(input);
return new ValidationResult.Builder().subject(subject).input(input).explanation("Valid URI").valid(true).build();
} catch (final Exception e) {
return new ValidationResult.Builder().subject(subject).input(input).explanation("Not a valid URI").valid(false).build();
}
}
};
public static final Validator REGULAR_EXPRESSION_VALIDATOR = createRegexValidator(0, Integer.MAX_VALUE, false);
public static final Validator ATTRIBUTE_EXPRESSION_LANGUAGE_VALIDATOR = new Validator() {
@Override
public ValidationResult validate(final String subject, final String input, final ValidationContext context) {
if (context.isExpressionLanguageSupported(subject) && context.isExpressionLanguagePresent(input)) {
try {
final String result = context.newExpressionLanguageCompiler().validateExpression(input, true);
if (!isEmpty(result)) {
return new ValidationResult.Builder().subject(subject).input(input).valid(false).explanation(result).build();
}
} catch (final Exception e) {
return new ValidationResult.Builder().subject(subject).input(input).valid(false).explanation(e.getMessage()).build();
}
}
return new ValidationResult.Builder().subject(subject).input(input).valid(true).build();
}
};
/**
* @param value to test
* @return true if value is null or empty string; does not trim before
* testing
*/
private static boolean isEmpty(final String value) {
return value == null || value.length() == 0;
}
public static final Validator TIME_PERIOD_VALIDATOR = new Validator() {
@Override
public ValidationResult validate(final String subject, final String input, final ValidationContext context) {
if (context.isExpressionLanguageSupported(subject) && context.isExpressionLanguagePresent(input)) {
return new ValidationResult.Builder().subject(subject).input(input).explanation("Expression Language Present").valid(true).build();
}
if (input == null) {
return new ValidationResult.Builder().subject(subject).input(input).valid(false).explanation("Time Period cannot be null").build();
}
if (Pattern.compile(FormatUtils.TIME_DURATION_REGEX).matcher(input.toLowerCase()).matches()) {
return new ValidationResult.Builder().subject(subject).input(input).valid(true).build();
} else {
return new ValidationResult.Builder()
.subject(subject)
.input(input)
.valid(false)
.explanation("Must be of format <duration> <TimeUnit> where <duration> is a "
+ "non-negative integer and TimeUnit is a supported Time Unit, such "
+ "as: nanos, millis, secs, mins, hrs, days")
.build();
}
}
};
public static final Validator DATA_SIZE_VALIDATOR = new Validator() {
@Override
public ValidationResult validate(final String subject, final String input, final ValidationContext context) {
if (context.isExpressionLanguageSupported(subject) && context.isExpressionLanguagePresent(input)) {
return new ValidationResult.Builder().subject(subject).input(input).explanation("Expression Language Present").valid(true).build();
}
if (input == null) {
return new ValidationResult.Builder()
.subject(subject)
.input(input)
.valid(false)
.explanation("Data Size cannot be null")
.build();
}
if (Pattern.compile(DataUnit.DATA_SIZE_REGEX).matcher(input.toUpperCase()).matches()) {
return new ValidationResult.Builder().subject(subject).input(input).valid(true).build();
} else {
return new ValidationResult.Builder()
.subject(subject).input(input)
.valid(false)
.explanation("Must be of format <Data Size> <Data Unit> where <Data Size>"
+ " is a non-negative integer and <Data Unit> is a supported Data"
+ " Unit, such as: B, KB, MB, GB, TB")
.build();
}
}
};
public static final Validator FILE_EXISTS_VALIDATOR = new FileExistsValidator(true);
//
//
// FACTORY METHODS FOR VALIDATORS
//
//
public static Validator createDirectoryExistsValidator(final boolean allowExpressionLanguage, final boolean createDirectoryIfMissing) {
return new DirectoryExistsValidator(allowExpressionLanguage, createDirectoryIfMissing);
}
private static Validator createURLValidator() {
return new Validator() {
@Override
public ValidationResult validate(final String subject, final String input, final ValidationContext context) {
if (context.isExpressionLanguageSupported(subject) && context.isExpressionLanguagePresent(input)) {
return new ValidationResult.Builder().subject(subject).input(input).explanation("Expression Language Present").valid(true).build();
}
try {
final String evaluatedInput = context.newPropertyValue(input).evaluateAttributeExpressions().getValue();
new URL(evaluatedInput);
return new ValidationResult.Builder().subject(subject).input(input).explanation("Valid URL").valid(true).build();
} catch (final Exception e) {
return new ValidationResult.Builder().subject(subject).input(input).explanation("Not a valid URL").valid(false).build();
}
}
};
}
public static Validator createURLorFileValidator() {
return (subject, input, context) -> {
if (context.isExpressionLanguageSupported(subject) && context.isExpressionLanguagePresent(input)) {
return new ValidationResult.Builder().subject(subject).input(input).explanation("Expression Language Present").valid(true).build();
}
try {
PropertyValue propertyValue = context.newPropertyValue(input);
String evaluatedInput = (propertyValue == null) ? input : propertyValue.evaluateAttributeExpressions().getValue();
boolean validUrl = true;
// First check to see if it is a valid URL
try {
new URL(evaluatedInput);
} catch (MalformedURLException mue) {
validUrl = false;
}
boolean validFile = true;
if (!validUrl) {
// Check to see if it is a file and it exists
final File file = new File(evaluatedInput);
validFile = file.exists();
}
final boolean valid = validUrl || validFile;
final String reason = valid ? "Valid URL or file" : "Not a valid URL or file";
return new ValidationResult.Builder().subject(subject).input(input).explanation(reason).valid(valid).build();
} catch (final Exception e) {
return new ValidationResult.Builder().subject(subject).input(input).explanation("Not a valid URL or file").valid(false).build();
}
};
}
public static Validator createListValidator(boolean trimEntries, boolean excludeEmptyEntries, Validator validator) {
return (subject, input, context) -> {
if (context.isExpressionLanguageSupported(subject) && context.isExpressionLanguagePresent(input)) {
return new ValidationResult.Builder().subject(subject).input(input).explanation("Expression Language Present").valid(true).build();
}
try {
if (input == null) {
return new ValidationResult.Builder().subject(subject).input(null).explanation("List must have at least one non-empty element").valid(false).build();
}
final String[] list = input.split(",");
for (String item : list) {
String itemToValidate = trimEntries ? item.trim() : item;
if (!isEmpty(itemToValidate) || !excludeEmptyEntries) {
ValidationResult result = validator.validate(subject, itemToValidate, context);
if (!result.isValid()) {
return result;
}
}
}
return new ValidationResult.Builder().subject(subject).input(input).explanation("Valid List").valid(true).build();
} catch (final Exception e) {
return new ValidationResult.Builder().subject(subject).input(input).explanation("Not a valid list").valid(false).build();
}
};
}
public static Validator createTimePeriodValidator(final long minTime, final TimeUnit minTimeUnit, final long maxTime, final TimeUnit maxTimeUnit) {
return new TimePeriodValidator(minTime, minTimeUnit, maxTime, maxTimeUnit);
}
public static Validator createAttributeExpressionLanguageValidator(final ResultType expectedResultType) {
return createAttributeExpressionLanguageValidator(expectedResultType, true);
}
public static Validator createDataSizeBoundsValidator(final long minBytesInclusive, final long maxBytesInclusive) {
return new Validator() {
@Override
public ValidationResult validate(final String subject, final String input, final ValidationContext context) {
if (context.isExpressionLanguageSupported(subject) && context.isExpressionLanguagePresent(input)) {
return new ValidationResult.Builder().subject(subject).input(input).explanation("Expression Language Present").valid(true).build();
}
final ValidationResult vr = DATA_SIZE_VALIDATOR.validate(subject, input, context);
if (!vr.isValid()) {
return vr;
}
final long dataSizeBytes = DataUnit.parseDataSize(input, DataUnit.B).longValue();
if (dataSizeBytes < minBytesInclusive) {
return new ValidationResult.Builder().subject(subject).input(input).valid(false).explanation("Cannot be smaller than " + minBytesInclusive + " bytes").build();
}
if (dataSizeBytes > maxBytesInclusive) {
return new ValidationResult.Builder().subject(subject).input(input).valid(false).explanation("Cannot be larger than " + maxBytesInclusive + " bytes").build();
}
return new ValidationResult.Builder().subject(subject).input(input).valid(true).build();
}
};
}
public static Validator createRegexMatchingValidator(final Pattern pattern) {
return new Validator() {
@Override
public ValidationResult validate(final String subject, final String input, final ValidationContext context) {
if (context.isExpressionLanguageSupported(subject) && context.isExpressionLanguagePresent(input)) {
return new ValidationResult.Builder().subject(subject).input(input).explanation("Expression Language Present").valid(true).build();
}
final boolean matches = pattern.matcher(input).matches();
return new ValidationResult.Builder()
.input(input)
.subject(subject)
.valid(matches)
.explanation(matches ? null : "Value does not match regular expression: " + pattern.pattern())
.build();
}
};
}
/**
* Creates a @{link Validator} that ensure that a value is a valid Java
* Regular Expression with at least <code>minCapturingGroups</code>
* capturing groups and at most <code>maxCapturingGroups</code> capturing
* groups. If <code>supportAttributeExpressionLanguage</code> is set to
* <code>true</code>, the value may also include the Expression Language,
* but the result of evaluating the Expression Language will be applied
* before the Regular Expression is performed. In this case, the Expression
* Language will not support FlowFile Attributes but only System/JVM
* Properties
*
* @param minCapturingGroups minimum capturing groups allowed
* @param maxCapturingGroups maximum capturing groups allowed
* @param supportAttributeExpressionLanguage whether or not to support
* expression language
* @return validator
*/
public static Validator createRegexValidator(final int minCapturingGroups, final int maxCapturingGroups, final boolean supportAttributeExpressionLanguage) {
return new Validator() {
@Override
public ValidationResult validate(final String subject, final String value, final ValidationContext context) {
try {
final String substituted;
if (supportAttributeExpressionLanguage) {
try {
substituted = context.newPropertyValue(value).evaluateAttributeExpressions().getValue();
} catch (final Exception e) {
return new ValidationResult.Builder()
.subject(subject)
.input(value)
.valid(false)
.explanation("Failed to evaluate the Attribute Expression Language due to " + e.toString())
.build();
}
} else {
substituted = value;
}
final Pattern pattern = Pattern.compile(substituted);
final int numGroups = pattern.matcher("").groupCount();
if (numGroups < minCapturingGroups || numGroups > maxCapturingGroups) {
return new ValidationResult.Builder()
.subject(subject)
.input(value)
.valid(false)
.explanation("RegEx is required to have between " + minCapturingGroups + " and " + maxCapturingGroups + " Capturing Groups but has " + numGroups)
.build();
}
return new ValidationResult.Builder().subject(subject).input(value).valid(true).build();
} catch (final Exception e) {
return new ValidationResult.Builder()
.subject(subject)
.input(value)
.valid(false)
.explanation("Not a valid Java Regular Expression")
.build();
}
}
};
}
public static Validator createAttributeExpressionLanguageValidator(final ResultType expectedResultType, final boolean allowExtraCharacters) {
return new Validator() {
@Override
public ValidationResult validate(final String subject, final String input, final ValidationContext context) {
final String syntaxError = context.newExpressionLanguageCompiler().validateExpression(input, allowExtraCharacters);
if (syntaxError != null) {
return new ValidationResult.Builder().subject(subject).input(input).valid(false).explanation(syntaxError).build();
}
final ResultType resultType = allowExtraCharacters ? ResultType.STRING : context.newExpressionLanguageCompiler().getResultType(input);
if (!resultType.equals(expectedResultType)) {
return new ValidationResult.Builder()
.subject(subject)
.input(input)
.valid(false)
.explanation("Expected Attribute Query to return type " + expectedResultType + " but query returns type " + resultType)
.build();
}
return new ValidationResult.Builder().subject(subject).input(input).valid(true).build();
}
};
}
public static Validator createLongValidator(final long minimum, final long maximum, final boolean inclusive) {
return new Validator() {
@Override
public ValidationResult validate(final String subject, final String input, final ValidationContext context) {
if (context.isExpressionLanguageSupported(subject) && context.isExpressionLanguagePresent(input)) {
return new ValidationResult.Builder().subject(subject).input(input).explanation("Expression Language Present").valid(true).build();
}
String reason = null;
try {
final long longVal = Long.parseLong(input);
if (longVal < minimum || (!inclusive && longVal == minimum) | longVal > maximum || (!inclusive && longVal == maximum)) {
reason = "Value must be between " + minimum + " and " + maximum + " (" + (inclusive ? "inclusive" : "exclusive") + ")";
}
} catch (final NumberFormatException e) {
reason = "not a valid integer";
}
return new ValidationResult.Builder().subject(subject).input(input).explanation(reason).valid(reason == null).build();
}
};
}
//
//
// SPECIFIC VALIDATOR IMPLEMENTATIONS THAT CANNOT BE ANONYMOUS CLASSES
//
//
static class TimePeriodValidator implements Validator {
private final Pattern pattern;
private final long minNanos;
private final long maxNanos;
private final String minValueEnglish;
private final String maxValueEnglish;
public TimePeriodValidator(final long minValue, final TimeUnit minTimeUnit, final long maxValue, final TimeUnit maxTimeUnit) {
pattern = Pattern.compile(FormatUtils.TIME_DURATION_REGEX);
this.minNanos = TimeUnit.NANOSECONDS.convert(minValue, minTimeUnit);
this.maxNanos = TimeUnit.NANOSECONDS.convert(maxValue, maxTimeUnit);
this.minValueEnglish = minValue + " " + minTimeUnit.toString();
this.maxValueEnglish = maxValue + " " + maxTimeUnit.toString();
}
@Override
public ValidationResult validate(final String subject, final String input, final ValidationContext context) {
if (context.isExpressionLanguageSupported(subject) && context.isExpressionLanguagePresent(input)) {
return new ValidationResult.Builder().subject(subject).input(input).explanation("Expression Language Present").valid(true).build();
}
if (input == null) {
return new ValidationResult.Builder().subject(subject).input(input).valid(false).explanation("Time Period cannot be null").build();
}
final String lowerCase = input.toLowerCase();
final boolean validSyntax = pattern.matcher(lowerCase).matches();
final ValidationResult.Builder builder = new ValidationResult.Builder();
if (validSyntax) {
final long nanos = FormatUtils.getTimeDuration(lowerCase, TimeUnit.NANOSECONDS);
if (nanos < minNanos || nanos > maxNanos) {
builder.subject(subject).input(input).valid(false)
.explanation("Must be in the range of " + minValueEnglish + " to " + maxValueEnglish);
} else {
builder.subject(subject).input(input).valid(true);
}
} else {
builder.subject(subject).input(input).valid(false)
.explanation("Must be of format <duration> <TimeUnit> where <duration> is a non-negative "
+ "integer and TimeUnit is a supported Time Unit, such as: nanos, millis, secs, mins, hrs, days");
}
return builder.build();
}
}
public static class FileExistsValidator implements Validator {
private final boolean allowEL;
public FileExistsValidator(final boolean allowExpressionLanguage) {
this.allowEL = allowExpressionLanguage;
}
@Override
public ValidationResult validate(final String subject, final String value, final ValidationContext context) {
if (context.isExpressionLanguageSupported(subject) && context.isExpressionLanguagePresent(value)) {
return new ValidationResult.Builder().subject(subject).input(value).explanation("Expression Language Present").valid(true).build();
}
final String substituted;
if (allowEL) {
try {
substituted = context.newPropertyValue(value).evaluateAttributeExpressions().getValue();
} catch (final Exception e) {
return new ValidationResult.Builder().subject(subject).input(value).valid(false)
.explanation("Not a valid Expression Language value: " + e.getMessage()).build();
}
} else {
substituted = value;
}
final File file = new File(substituted);
final boolean valid = file.exists();
final String explanation = valid ? null : "File " + file + " does not exist";
return new ValidationResult.Builder().subject(subject).input(value).valid(valid).explanation(explanation).build();
}
}
public static class StringLengthValidator implements Validator {
private final int minimum;
private final int maximum;
public StringLengthValidator(int minimum, int maximum) {
this.minimum = minimum;
this.maximum = maximum;
}
@Override
public ValidationResult validate(final String subject, final String value, final ValidationContext context) {
if (value.length() < minimum || value.length() > maximum) {
return new ValidationResult.Builder()
.subject(subject)
.valid(false)
.input(value)
.explanation(String.format("String length invalid [min: %d, max: %d]", minimum, maximum))
.build();
} else {
return new ValidationResult.Builder()
.valid(true)
.input(value)
.subject(subject)
.build();
}
}
}
public static class DirectoryExistsValidator implements Validator {
private final boolean allowEL;
private final boolean create;
public DirectoryExistsValidator(final boolean allowExpressionLanguage, final boolean create) {
this.allowEL = allowExpressionLanguage;
this.create = create;
}
@Override
public ValidationResult validate(final String subject, final String value, final ValidationContext context) {
if (context.isExpressionLanguageSupported(subject) && context.isExpressionLanguagePresent(value)) {
return new ValidationResult.Builder().subject(subject).input(value).explanation("Expression Language Present").valid(true).build();
}
final String substituted;
if (allowEL) {
try {
substituted = context.newPropertyValue(value).evaluateAttributeExpressions().getValue();
} catch (final Exception e) {
return new ValidationResult.Builder().subject(subject).input(value).valid(false)
.explanation("Not a valid Expression Language value: " + e.getMessage()).build();
}
if (substituted.trim().isEmpty() && !value.trim().isEmpty()) {
// User specified an Expression and nothing more... assume valid.
return new ValidationResult.Builder().subject(subject).input(value).valid(true).build();
}
} else {
substituted = value;
}
String reason = null;
try {
final File file = new File(substituted);
if (!file.exists()) {
if (!create) {
reason = "Directory does not exist";
} else if (!file.mkdirs()) {
reason = "Directory does not exist and could not be created";
}
} else if (!file.isDirectory()) {
reason = "Path does not point to a directory";
}
} catch (final Exception e) {
reason = "Value is not a valid directory name";
}
return new ValidationResult.Builder().subject(subject).input(value).explanation(reason).valid(reason == null).build();
}
}
}