Skip to content

Instantly share code, notes, and snippets.

@bmchild
Last active December 18, 2015 05:09
Show Gist options
  • Save bmchild/5730620 to your computer and use it in GitHub Desktop.
Save bmchild/5730620 to your computer and use it in GitHub Desktop.
JSR303 @ScriptAssert with field error
/**
*
*/
package com.bmchild.validation.constraints;
import static java.lang.annotation.ElementType.TYPE;
import static java.lang.annotation.RetentionPolicy.RUNTIME;
import java.lang.annotation.Documented;
import java.lang.annotation.Retention;
import java.lang.annotation.Target;
import javax.validation.Constraint;
import javax.validation.Payload;
import org.hibernate.validator.constraints.ScriptAssert;
/**
* Uses {@link ScriptAssert} logic to validate the class and allows field error definitions
*
* @see ScriptAssert
* @author bchild
*
*/
@Target({ TYPE })
@Retention(RUNTIME)
@Constraint(validatedBy = {ScriptAssertFieldErrorValidator.class })
@Documented
public @interface ScriptAssertFieldError {
String message() default "{org.hibernate.validator.constraints.ScriptAssert.message}";
Class<?>[] groups() default { };
Class<? extends Payload>[] payload() default { };
/**
* @return The name of the script language used by this constraint as
* expected by the JSR 223 {@link javax.script.ScriptEngineManager}. A
* {@link javax.validation.ConstraintDeclarationException} will be thrown upon script
* evaluation, if no engine for the given language could be found.
*/
String lang();
/**
* @return The script to be executed. The script must return
* <code>Boolean.TRUE</code>, if the annotated element could
* successfully be validated, otherwise <code>Boolean.FALSE</code>.
* Returning null or any type other than Boolean will cause a
* {@link javax.validation.ConstraintDeclarationException} upon validation. Any
* exception occurring during script evaluation will be wrapped into
* a ConstraintDeclarationException, too. Within the script, the
* validated object can be accessed from the {@link javax.script.ScriptContext
* script context} using the name specified in the
* <code>alias</code> attribute.
*/
String script();
/**
* @return The name, under which the annotated element shall be registered
* within the script context. Defaults to "_this".
*/
String alias() default "_this";
String fieldName();
/**
* Defines several {@code @ScriptAssert} annotations on the same element.
*/
@Target({ TYPE })
@Retention(RUNTIME)
@Documented
public @interface List {
ScriptAssertFieldError[] value();
}
}
/**
*
*/
package com.crengland.validation.constraints;
import static org.hibernate.validator.internal.util.logging.Messages.MESSAGES;
import javax.script.ScriptException;
import javax.validation.ConstraintDeclarationException;
import javax.validation.ConstraintValidator;
import javax.validation.ConstraintValidatorContext;
import org.apache.commons.lang.StringUtils;
import org.hibernate.validator.internal.constraintvalidators.ScriptAssertValidator;
import org.hibernate.validator.internal.util.Contracts;
import org.hibernate.validator.internal.util.scriptengine.ScriptEvaluator;
import org.hibernate.validator.internal.util.scriptengine.ScriptEvaluatorFactory;
import com.bmchild.common.validation.ConstraintValidatorUtils;
/**
* Validator for {@link ScriptAssertFieldError}
* @see ScriptAssertValidator
* @author bchild
*
*/
public class ScriptAssertFieldErrorValidator implements ConstraintValidator<ScriptAssertFieldError, Object> {
private static final String TRUE_OR_FALSE_EXCEPTION = "script must return true or false";
private String script;
private String languageName;
private String alias;
private String fieldName;
private String errorMessage;
@Override
public void initialize(ScriptAssertFieldError constraintAnnotation) {
validateParameters( constraintAnnotation );
this.script = constraintAnnotation.script();
this.languageName = constraintAnnotation.lang();
this.alias = constraintAnnotation.alias();
this.fieldName = constraintAnnotation.fieldName();
this.errorMessage = constraintAnnotation.message();
}
/* (non-Javadoc)
* @see javax.validation.ConstraintValidator#isValid(java.lang.Object, javax.validation.ConstraintValidatorContext)
*/
@Override
public boolean isValid(Object value, ConstraintValidatorContext context) {
Object evaluationResult;
ScriptEvaluator scriptEvaluator;
try {
ScriptEvaluatorFactory evaluatorFactory = ScriptEvaluatorFactory.getInstance();
scriptEvaluator = evaluatorFactory.getScriptEvaluatorByLanguageName( languageName );
}
catch ( ScriptException e ) {
throw new ConstraintDeclarationException( e );
}
try {
evaluationResult = scriptEvaluator.evaluate( script, value, alias );
}
catch ( ScriptException e ) {
throw new ConstraintDeclarationException("An error occurred during script execution", e);
}
if ( evaluationResult == null ) {
throw new ConstraintDeclarationException(TRUE_OR_FALSE_EXCEPTION);
}
if ( !( evaluationResult instanceof Boolean ) ) {
throw new ConstraintDeclarationException(TRUE_OR_FALSE_EXCEPTION);
}
Boolean result = Boolean.TRUE.equals( evaluationResult );
if(!result && StringUtils.isNotEmpty(fieldName)) {
ConstraintValidatorUtils.addConstraintViolation(context, errorMessage, fieldName);
}
return result;
}
private void validateParameters(ScriptAssertFieldError constraintAnnotation) {
Contracts.assertNotEmpty( constraintAnnotation.script(), MESSAGES.parameterMustNotBeEmpty( "script" ) );
Contracts.assertNotEmpty( constraintAnnotation.lang(), MESSAGES.parameterMustNotBeEmpty( "lang" ) );
Contracts.assertNotEmpty( constraintAnnotation.alias(), MESSAGES.parameterMustNotBeEmpty( "alias" ) );
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment