I have a java 8 functional interface that accepts a list of validators that are applied on an object and returns the validation result. The validation results are accumulated in the reduce phase. The code as follows:
public interface LogicalTableValidator extends Function<LogicalTable, ValidationResult> {
static LogicalTableValidator addAll(LogicalTableValidator... validators) {
// Need to break out of this validator stream, based on the criticality of a particular validation error
return logicalTable -> Arrays.stream(validators).map(v -> v.apply(logicalTable))
.reduce(new ValidationResult(logicalTable.getUid()), (validationResult, currentResult) -> {
validationResult.addValidationMessages(currentResult.getValidationMessages());
return validationResult;
});
}
}
This validation logic gets called from here
LogicalTableValidator logicalTableValidators = LogicalTableValidator.addAll(getValidators());
List<ValidationResult> ltValidationResults = logicalTables.stream()
.parallel()
.map(logicalTableValidators)
.collect(Collectors.toList());
The problem I am facing is that, I am not able to break from the validation logic conditionally. This will be the case when I am applying the validators on the logicalObject, if the validation fails with a critical error, I dont need to run rest of the validators. Instead I need to stop the validation process right there.
A work around would be not to use lambda expression for validation and use the following code instead.
return new LogicalTableValidator() {
@Override
public ValidationResult apply(LogicalTable t) {
ValidationResult result = new ValidationResult(t.getUid());
for (LogicalTableValidator validator : validators) {
ValidationResult currentResult = validator.apply(t);
List<ValidationMessage> messages = currentResult.getValidationMessages();
Boolean exit = false;
for (ValidationMessage message : messages) {
if(StringUtils.equalsIgnoreCase(message.getSeverity(), "1")) {
exit = true;
break;
}
}
result.addValidationMessages(currentResult.getValidationMessages());
if (exit) break;
}
return result;
}
};
It seems, not using lambda expression in functional interface, defeats the purpose of using functional interface, but I couldn't figure out a way to conditionally break out of the validation loop. Is there any alternative I can use? Should this code be structured in a different way?
You can try something like below. In peek
it collect ValidationMessages
. In filter
and findFirst
it stop after first error message. It is replacement for takeWhile
that was mentioned in comments, you can also check this.
public interface LogicalTableValidator extends Function<LogicalTable, ValidationResult> {
static LogicalTableValidator addAll(LogicalTableValidator... validators) {
logicalTable -> {
ValidationResult result = new ValidationResult(logicalTable.getUid());
Arrays.stream(validators).map(v -> v.apply(logicalTable))
.peek(currentResult -> result.addValidationMessages(currentResult.getValidationMessages()))
.filter(currentResult -> currentResult.getValidationMessages().stream()
.filter(message -> StringUtils.equalsIgnoreCase(message.getSeverity(), "1"))
.count() > 0)
.findFirst()
.orElse(null);
return result;
}
}
}