Программное создание валидатора значений примитивных типов
Мне необходимо проверять значения различных примитивных типов и их коллекций на соответствие динамически задаваемым ограничениям и правилам. Необходимо это для проверки/мониторинга возвращаемых значений парка CNC-машин, которые могут иметь различные диапазоны допустимых значений в зависимости от машины/заказа/времени/сотрудника/фазы луны и т.д.. Также проверки используются для определения превышения пороговых значений критических показателей с последующей генерацией событий.
К сожалению, вариант с сохранением значений в БД не допустим по непреодолимым обстоятельствам и поэтому, на данный момент, рассматриваются следующие варианты...
Вариант 1: свой "велосипед"
Писать свой "велосипед" и надеятся, что квалификации и опыта хватит на разработку хорошей библиотеки.
Вариант 2: имплементация Bean Validation API
Как первый вариант, с тем лишь различием, что конструироваться "велосипед" будет по определённым правилам, что поможет избежать квадратных колёс, но использование/надёжность/обслуживание всё равно будет под вопросом.
Вариант 3: Hibernate Validator
Использовать Bean Validation API (Hibernate Validator), где имеется возможность динамического создания правил. Пример использования ниже:
<!-- Maven dependencies -->
<dependency>
<groupId>org.hibernate.validator</groupId>
<artifactId>hibernate-validator</artifactId>
<version>6.0.13.Final</version>
</dependency>
<dependency>
<groupId>javax.validation</groupId>
<artifactId>validation-api</artifactId>
<version>2.0.1.Final</version>
</dependency>
import org.hibernate.validator.HibernateValidator;
import org.hibernate.validator.cfg.ConstraintMapping;
import org.hibernate.validator.internal.cfg.context.DefaultConstraintMapping;
import org.hibernate.validator.messageinterpolation.ParameterMessageInterpolator;
import javax.validation.ConstraintViolation;
import javax.validation.Validation;
import javax.validation.Validator;
import javax.validation.ValidatorFactory;
import javax.validation.valueextraction.ExtractedValue;
import javax.validation.valueextraction.ValueExtractor;
import org.hibernate.validator.cfg.defs.*;
public class App
{
public static void main( final String[] args )
{
try ( ValidatorFactory factory = Validation
.byProvider( HibernateValidator.class )
.configure()
.addMapping( buildPrimitiveConstraintMapping() )
.addValueExtractor( new IntegerValueExtractor() )
.addValueExtractor( new StringValueExtractor() )
.messageInterpolator( new ParameterMessageInterpolator() )
.buildValidatorFactory() )
{
Validator validator = factory.getValidator();
validator.validate( 7 )
.stream()
.map( ConstraintViolation::getMessage )
.forEach( System.out::println );
validator.validate( "" )
.stream()
.map( ConstraintViolation::getMessage )
.forEach( System.out::println );
}
catch ( Exception e )
{
e.printStackTrace();
}
}
private static ConstraintMapping buildPrimitiveConstraintMapping()
{
ConstraintMapping mapping = new DefaultConstraintMapping();
mapping
.type( Integer.class )
.constraint( new NotNullDef() )
.constraint( new MinDef().value( 18 ) )
.constraint( new MaxDef().value( 50 ) );
mapping
.type( String.class )
.constraint( new NotNullDef() )
.constraint( new NotBlankDef() );
return mapping;
}
// Value extractor for Integer
public static class IntegerValueExtractor
implements
ValueExtractor<@ExtractedValue(type = Integer.class) Integer>
{
@Override
public void extractValues(
Integer originalValue,
ValueReceiver receiver )
{
receiver.value( null, originalValue );
}
}
// Value extractor for String
public static class StringValueExtractor
implements
ValueExtractor<@ExtractedValue(type = String.class) String>
{
@Override
public void extractValues(
String originalValue,
ValueReceiver receiver )
{
receiver.value( null, originalValue );
}
}
}
Как принято решать такие проблемы "по фэн-шую"? Не является ли выбранная библиотека избыточной для решения поставленной задачи/проблемы?
Ответы (1 шт):
Не знаю правильно-ли или нет, но решил так:
try (ValidatorFactory factory = Validation.byProvider( HibernateValidator.class )
.configure()
.addMapping( buildPrimitiveConstraintMapping() )
.addValueExtractor( new IntegerValueExtractor() )
.addValueExtractor( new StringValueExtractor() )
.messageInterpolator( new CustomMessageInterpolator() )
.buildValidatorFactory())
{
Validator validator = factory.getValidator();
validator
.validate( 7 ).stream()
.map( ConstraintViolation::getMessage )
.forEach( System.out::println );
validator
.validate( "" ).stream()
.map( ConstraintViolation::getMessage )
.forEach( System.out::println );
}
catch( Exception e )
{
e.printStackTrace();
}
private static ConstraintMapping buildPrimitiveConstraintMapping()
{
ConstraintMapping mapping = new DefaultConstraintMapping();
mapping
.type( Integer.class )
.constraint( new NotNullDef() )
.constraint( new MinDef().value( 18 ) )
.constraint( new MaxDef().value( 50 )
.message( "The value {validatedValue} must not be greater than {value}" ) );
mapping
.type( String.class )
.constraint( new NotNullDef() )
.constraint( new NotBlankDef().message( "Value can't be null" ) );
return mapping;
}
public static class IntegerValueExtractor implements ValueExtractor<@ExtractedValue(type = Integer.class) Integer>
{
@Override public void extractValues( Integer originalValue, ValueReceiver receiver )
{
receiver.value( null, originalValue );
}
}
public static class StringValueExtractor implements ValueExtractor<@ExtractedValue(type = String.class) String>
{
@Override public void extractValues( String originalValue, ValueReceiver receiver )
{
receiver.value( null, originalValue );
}
}
Класс CustomMessageInterpolator непосредственно не относится к поставленной задаче и можно использовать и далее ParameterMessageInterpolator, как показано в коде вопроса, но в этом случае будет невозможно использовать {validatedValue}
в сообщениях об ошибках (скорее всего из-за того, что не используются многие зависимости HibernateValidator'а):
import java.util.Locale;
import java.lang.invoke.MethodHandles;
import org.hibernate.validator.internal.engine.messageinterpolation.InterpolationTerm;
import org.hibernate.validator.internal.engine.messageinterpolation.ParameterTermResolver;
import org.hibernate.validator.internal.util.logging.Log;
import org.hibernate.validator.internal.util.logging.LoggerFactory;
import org.hibernate.validator.messageinterpolation.AbstractMessageInterpolator;
public class CustomMessageInterpolator extends AbstractMessageInterpolator
{
private static final Log LOG = LoggerFactory.make( MethodHandles.lookup() );
@Override public String interpolate( Context context, Locale locale, String term )
{
if ( "{validatedValue}".equals( term ) )
{
return String.valueOf( context.getValidatedValue() );
}
else if ( InterpolationTerm.isElExpression( term ) )
{
LOG.warnElIsUnsupported( term );
return term;
}
ParameterTermResolver parameterTermResolver = new ParameterTermResolver();
return parameterTermResolver.interpolate( context, term );
}
}