Skip to content

wnameless/spelscriptassert

Folders and files

NameName
Last commit message
Last commit date

Latest commit

Β 

History

50 Commits
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 

Repository files navigation

Maven Central codecov

SpELScriptAssert

Spring Expression Language(SpEL) Jakarta validator.

Purpose

Apply SpEL to Hibernate @ScriptAssert by creating a new validation annotation @SpELScriptAssert.

Demo - basic usage

Using Spring Expression Language(SpEL) to validate a Java bean.

Bean

@SpELScriptAssert( //
    script = "@mathComponent.multiply(#add(a, b), c) == output", //
    performIf = "a != null && b != null && c != null", //
    helpers = {MathHelper.class}, //
    reportOn = "output", //
    message = "{validation.MixBean.output}")
public class MixBean {

  public Integer output = 10;
  public Integer a = 1;
  public Integer b = 2;
  public Integer c = 3;

}

Validation message:

output != (a + b) * c

Message properties:

validation.MixBean.output={reportOn} != (a + b) * c

Spring Component:

@Component
public class MathComponent {
  public int multiply(int a, int b) { return a * b; }
}

Helper class:

public class MathHelper {
  public static int add(int a, int b) { return a + b; }
}

Demo - advanced usage

target evaluated value can be reused in script and message attributes by calling #target
#{...} blocks in message attribute are all SpEL expressions which will be evaluated eventually

Bean

@SpELScriptAssert( //
    target = "a * b * c", //
    script = "#target >= 10", //
    message = "#{#target} {validation.AdvancedBean.msg} #{T(java.lang.Math).abs(#target - 10)}")
public class AdvancedBean {

  public Integer a = 1;
  public Integer b = 2;
  public Integer c = 3;

}

Validation message:

6 is NOT greater than or equal to Ten before adding 4

Message properties:

validation.AdvancedBean.msg=is NOT greater than or equal to Ten before adding

Maven Repo

<dependency>
	<groupId>com.github.wnameless.spring</groupId>
	<artifactId>spelscriptassert</artifactId>
	<version>${newestVersion}</version>
	<!-- Newest version shows in the maven-central badge above -->
</dependency>

Quick Start - configure ValidationMessages resource bundle

Spring Environment - Inject MessageSource into the Jakarta Validator.

@Configuration
public class SpELScriptAssertConfig {
  @Bean
  LocalValidatorFactoryBean validatorFactoryBean(MessageSource messageSource) {
    var bean = new LocalValidatorFactoryBean();
    bean.setValidationMessageSource(messageSource);
    return bean;
  }
}

Standalone mode

// Jakarta Validator looks up `ValidationMessages.properties` file under classpath by default

Feature List

Name Description Since
SpEL code blocks in message template Them message attribute of @SpELScriptAssert now accepts #{spel_expr} code blocks. v1.1.0
target An optional attribute can hold an evaluation result for further use. v1.1.0
Optional Spring Environment @SpELScriptAssert can be used standalone or with Spring Environment. The only difference is that @springComponent syntax of SpEL expression won't work in standalone mode. v1.0.0
Java Module supported The module-info.java included. v1.0.0
performIf A condition expression for determining whether a validation is performed or NOT. v1.0.0
helpers Register static methods from given Helper Classes. Methods can be called by #helperMethod syntax in SpEL expression. v1.0.0
reportOn Same as reportOn in Hibernate validation @ScriptAssert annotation. v1.0.0
Smart Evaluation Strategies Smart boolean convertion for evaluation values. v1.0.0

πŸ” SpEL code blocks(#{...}) in message template

@SpELScriptAssert( //
    script = "str.empty", //
    message = "str is NOT empty, str is '#{str}' with length of #{str.length}")
public class CodeBlockBean {
  public String str = "SpELScriptAssert";
}

Validation message:

str is NOT empty, str is 'SpELScriptAssert' with length of 16

πŸ” Utilize target attribute

The target attribute can hold an evaluation value for further uses in script and message contents. It can not only impove the readability but also save computation time if the evaluation of the expression in target attribute is highly time comsuming.

@SpELScriptAssert( //
    target = "bi1.multiply(bi2)", //
    script = "#target.toString.length < 10", //
    reportOn = "bi1 * bi2", //
    message = "{reportOn} = #{#target} and the digits are more than 10")
public class TargetBean {
  public BigInteger bi1 = new BigInteger("123456789");
  public BigInteger bi2 = new BigInteger("987654321");
}

Validation message:

bi1 * bi2 = 121932631112635269 and the digits are more than 10

πŸ” Smart Evaluation Strategies

SpELScriptAssert expects all evaluation scripts (both script and performIf) which always return Boolean values, however it also accepts other returning value types and treats them as Boolean values.

1. Non-zero number is True, zero is False.

@SpELScriptAssert(script = "a", reportOn = "a", message = "{reportOn} is false")
@SpELScriptAssert(script = "b", reportOn = "b", message = "{reportOn} is false")
public class NumBean {
  public int a = 0;
  public long b = 1L;
}

Validation message:

a is false

2. Non-empty Array or Collection is True, otherwise False.

@SpELScriptAssert(script = "a", reportOn = "a", message = "{reportOn} is false")
@SpELScriptAssert(script = "b", reportOn = "b", message = "{reportOn} is false")
@SpELScriptAssert(script = "c", performIf = "d", reportOn = "c", message = "{reportOn} is false")
public class ArrayCollectionBean {
  public int[] a = {};
  public String[] b = {"I"};
  public List<?> c = List.of();
  public Map<?, ?> d = Map.of("II", "III");
}

Validation message:

a is false
c is false

3. Non-blank CharSequence is True, otherwise False.

@SpELScriptAssert(script = "a", reportOn = "a", message = "{reportOn} is false")
@SpELScriptAssert(script = "b", reportOn = "b", message = "{reportOn} is false")
public class CharSequenceBean {
  public String a = "   ";
  public CharSequence b = "I";
}

Validation message:

a is false

4. Non-empty Optional is True, otherwise False. However Optional<Boolean> can only be dertermined by its own returning value.

@SpELScriptAssert(script = "a", reportOn = "a", message = "{reportOn} is false")
@SpELScriptAssert(script = "b", reportOn = "b", message = "{reportOn} is false")
@SpELScriptAssert(script = "c", reportOn = "c", message = "{reportOn} is false")
public class OptionalBean {
  public Optional<String> a = Optional.empty();
  public Optional<String> b = Optional.of("I");
  public Optional<Boolean> c = Optional.of(false);
}

Validation message:

a is false
c is false

5. Default behavior: Non-null is True, otherwise False.

@SpELScriptAssert(script = "a", reportOn = "a", message = "{reportOn} is false")
@SpELScriptAssert(script = "b", reportOn = "b", message = "{reportOn} is false")
public class NullBean {
  public BigInteger a = null;
  public BigDecimal b = BigDecimal.valueOf(123L);
}

Validation message:

a is false