diff --git a/javaxval/src/main/java/com/baeldung/javaxval/constraint/composition/Account.java b/javaxval/src/main/java/com/baeldung/javaxval/constraint/composition/Account.java new file mode 100644 index 0000000000..b47a47ec81 --- /dev/null +++ b/javaxval/src/main/java/com/baeldung/javaxval/constraint/composition/Account.java @@ -0,0 +1,37 @@ +package com.baeldung.javaxval.constraint.composition; + +public class Account { + + @ValidAlphanumeric + private String username; + + @ValidAlphanumericWithSingleViolation + private String password; + + @ValidLengthOrNumericCharacter + private String nickname; + + public String getUsername() { + return username; + } + + public void setUsername(String username) { + this.username = username; + } + + public String getPassword() { + return password; + } + + public void setPassword(String password) { + this.password = password; + } + + public String getNickname() { + return nickname; + } + + public void setNickname(String nickname) { + this.nickname = nickname; + } +} diff --git a/javaxval/src/main/java/com/baeldung/javaxval/constraint/composition/AccountService.java b/javaxval/src/main/java/com/baeldung/javaxval/constraint/composition/AccountService.java new file mode 100644 index 0000000000..f76fed8f04 --- /dev/null +++ b/javaxval/src/main/java/com/baeldung/javaxval/constraint/composition/AccountService.java @@ -0,0 +1,19 @@ +package com.baeldung.javaxval.constraint.composition; + +import org.springframework.stereotype.Component; +import org.springframework.validation.annotation.Validated; + +@Component +@Validated +public class AccountService { + + @AlphanumericReturnValue + public String getAnInvalidAlphanumericValue() { + return "john"; + } + + @AlphanumericReturnValue + public String getValidAlphanumericValue() { + return "johnDoe1234"; + } +} diff --git a/javaxval/src/main/java/com/baeldung/javaxval/constraint/composition/AlphanumericReturnValue.java b/javaxval/src/main/java/com/baeldung/javaxval/constraint/composition/AlphanumericReturnValue.java new file mode 100644 index 0000000000..6e3408712b --- /dev/null +++ b/javaxval/src/main/java/com/baeldung/javaxval/constraint/composition/AlphanumericReturnValue.java @@ -0,0 +1,36 @@ +package com.baeldung.javaxval.constraint.composition; + +import org.hibernate.validator.constraints.Length; + +import javax.validation.Constraint; +import javax.validation.Payload; +import javax.validation.constraints.NotNull; +import javax.validation.constraints.Pattern; +import javax.validation.constraintvalidation.SupportedValidationTarget; +import javax.validation.constraintvalidation.ValidationTarget; + +import java.lang.annotation.Documented; +import java.lang.annotation.Retention; +import java.lang.annotation.Target; + +import static java.lang.annotation.ElementType.*; +import static java.lang.annotation.RetentionPolicy.RUNTIME; + +@NotNull +@Pattern(regexp = ".*\\d.*", message = "must contain at least one numeric character") +@Length(min = 6, max = 32, message = "must have between 6 and 32 characters") +@Target({ METHOD, FIELD, ANNOTATION_TYPE, CONSTRUCTOR, PARAMETER }) +@Retention(RUNTIME) +@Documented +@Constraint(validatedBy = {}) +@SupportedValidationTarget(ValidationTarget.ANNOTATED_ELEMENT) +public @interface AlphanumericReturnValue { + + String message() default "method return value should have a valid length and contain numeric character(s)."; + + Class[] groups() default {}; + + Class[] payload() default {}; + +} + diff --git a/javaxval/src/main/java/com/baeldung/javaxval/constraint/composition/ConstraintCompositionConfig.java b/javaxval/src/main/java/com/baeldung/javaxval/constraint/composition/ConstraintCompositionConfig.java new file mode 100644 index 0000000000..ac0ec81ab2 --- /dev/null +++ b/javaxval/src/main/java/com/baeldung/javaxval/constraint/composition/ConstraintCompositionConfig.java @@ -0,0 +1,21 @@ +package com.baeldung.javaxval.constraint.composition; + +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.ComponentScan; +import org.springframework.context.annotation.Configuration; +import org.springframework.validation.beanvalidation.MethodValidationPostProcessor; + +@Configuration +@ComponentScan({ "com.baeldung.javaxval.constraint.composition" }) +public class ConstraintCompositionConfig { + + @Bean + public MethodValidationPostProcessor methodValidationPostProcessor() { + return new MethodValidationPostProcessor(); + } + + @Bean + public AccountService accountService() { + return new AccountService(); + } +} \ No newline at end of file diff --git a/javaxval/src/main/java/com/baeldung/javaxval/constraint/composition/ValidAlphanumeric.java b/javaxval/src/main/java/com/baeldung/javaxval/constraint/composition/ValidAlphanumeric.java new file mode 100644 index 0000000000..916b4e36a4 --- /dev/null +++ b/javaxval/src/main/java/com/baeldung/javaxval/constraint/composition/ValidAlphanumeric.java @@ -0,0 +1,37 @@ +package com.baeldung.javaxval.constraint.composition; + +import static java.lang.annotation.ElementType.ANNOTATION_TYPE; +import static java.lang.annotation.ElementType.CONSTRUCTOR; +import static java.lang.annotation.ElementType.FIELD; +import static java.lang.annotation.ElementType.METHOD; +import static java.lang.annotation.ElementType.PARAMETER; +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 javax.validation.constraints.NotNull; +import javax.validation.constraints.Pattern; + +import org.hibernate.validator.constraints.Length; + +@NotNull +@Pattern(regexp = ".*\\d.*", message = "must contain at least one numeric character") +@Length(min = 6, max = 32, message = "must have between 6 and 32 characters") +@Target({ METHOD, FIELD, ANNOTATION_TYPE, CONSTRUCTOR, PARAMETER }) +@Retention(RUNTIME) +@Documented +@Constraint(validatedBy = {}) +public @interface ValidAlphanumeric { + + String message() default "field should have a valid length and contain numeric character(s)."; + + Class[] groups() default {}; + + Class[] payload() default {}; + +} + diff --git a/javaxval/src/main/java/com/baeldung/javaxval/constraint/composition/ValidAlphanumericWithSingleViolation.java b/javaxval/src/main/java/com/baeldung/javaxval/constraint/composition/ValidAlphanumericWithSingleViolation.java new file mode 100644 index 0000000000..edc5b6af3e --- /dev/null +++ b/javaxval/src/main/java/com/baeldung/javaxval/constraint/composition/ValidAlphanumericWithSingleViolation.java @@ -0,0 +1,38 @@ +package com.baeldung.javaxval.constraint.composition; + +import static java.lang.annotation.ElementType.ANNOTATION_TYPE; +import static java.lang.annotation.ElementType.CONSTRUCTOR; +import static java.lang.annotation.ElementType.FIELD; +import static java.lang.annotation.ElementType.METHOD; +import static java.lang.annotation.ElementType.PARAMETER; +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 javax.validation.ReportAsSingleViolation; +import javax.validation.constraints.NotNull; +import javax.validation.constraints.Pattern; + +import org.hibernate.validator.constraints.Length; + +@NotNull +@Pattern(regexp = ".*\\d.*", message = "must contain at least one numeric character") +@Length(min = 6, max = 32, message = "must have between 6 and 32 characters") +@Target({ METHOD, FIELD, ANNOTATION_TYPE, CONSTRUCTOR, PARAMETER }) +@Retention(RUNTIME) +@Documented +@Constraint(validatedBy = {}) +@ReportAsSingleViolation +public @interface ValidAlphanumericWithSingleViolation { + + String message() default "field should have a valid length and contain numeric character(s)."; + + Class[] groups() default {}; + + Class[] payload() default {}; + +} \ No newline at end of file diff --git a/javaxval/src/main/java/com/baeldung/javaxval/constraint/composition/ValidLengthOrNumericCharacter.java b/javaxval/src/main/java/com/baeldung/javaxval/constraint/composition/ValidLengthOrNumericCharacter.java new file mode 100644 index 0000000000..444cb4a63a --- /dev/null +++ b/javaxval/src/main/java/com/baeldung/javaxval/constraint/composition/ValidLengthOrNumericCharacter.java @@ -0,0 +1,34 @@ +package com.baeldung.javaxval.constraint.composition; + +import org.hibernate.validator.constraints.CompositionType; +import org.hibernate.validator.constraints.ConstraintComposition; +import org.hibernate.validator.constraints.Length; + +import javax.validation.Constraint; +import javax.validation.Payload; +import javax.validation.constraints.Pattern; + +import java.lang.annotation.Documented; +import java.lang.annotation.Retention; +import java.lang.annotation.Target; + +import static java.lang.annotation.ElementType.*; +import static java.lang.annotation.RetentionPolicy.RUNTIME; + +@Pattern(regexp = ".*\\d.*", message = "must contain at least one numeric character") +@Length(min = 6, max = 32, message = "must have between 6 and 32 characters") +@Target({ METHOD, FIELD, ANNOTATION_TYPE, CONSTRUCTOR, PARAMETER }) +@Retention(RUNTIME) +@Documented +@Constraint(validatedBy = {}) +@ConstraintComposition(CompositionType.OR) +public @interface ValidLengthOrNumericCharacter { + + String message() default "field should have a valid length or contain numeric character(s)."; + + Class[] groups() default {}; + + Class[] payload() default {}; + +} + diff --git a/javaxval/src/test/java/com/baeldung/javaxval/constraint/composition/ConstraintCompositionUnitTest.java b/javaxval/src/test/java/com/baeldung/javaxval/constraint/composition/ConstraintCompositionUnitTest.java new file mode 100644 index 0000000000..6c2b8f801c --- /dev/null +++ b/javaxval/src/test/java/com/baeldung/javaxval/constraint/composition/ConstraintCompositionUnitTest.java @@ -0,0 +1,80 @@ +package com.baeldung.javaxval.constraint.composition; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatThrownBy; + +import java.util.Set; + +import javax.validation.ConstraintViolation; +import javax.validation.ConstraintViolationException; +import javax.validation.Validation; +import javax.validation.Validator; +import javax.validation.ValidatorFactory; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.test.context.ContextConfiguration; +import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; +import org.springframework.test.context.support.AnnotationConfigContextLoader; + +@RunWith(SpringJUnit4ClassRunner.class) +@ContextConfiguration(classes = { ConstraintCompositionConfig.class }, loader = AnnotationConfigContextLoader.class) +public class ConstraintCompositionUnitTest { + + @Autowired + private AccountService accountService; + + private Validator validator; + + @Before + public void setup() { + ValidatorFactory factory = Validation.buildDefaultValidatorFactory(); + validator = factory.getValidator(); + } + + @Test + public void whenUsernameIsInvalid_validationShouldReturnTwoViolations() { + Account account = new Account(); + account.setNickname("valid_nickname123"); + account.setPassword("valid_password123"); + account.setUsername("john"); + + Set> violations = validator.validate(account); + + assertThat(violations).hasSize(2); + } + + @Test + public void whenPasswordIsInvalid_validationShouldReturnSingleViolation() { + Account account = new Account(); + account.setUsername("valid_username123"); + account.setNickname("valid_nickname123"); + account.setPassword("john"); + + Set> violations = validator.validate(account); + + assertThat(violations).hasSize(1); + } + + @Test + public void whenNicknameIsTooShortButContainsNumericCharacter_validationShouldPass() { + Account account = new Account(); + account.setUsername("valid_username123"); + account.setPassword("valid_password123"); + account.setNickname("doe1"); + + Set> violations = validator.validate(account); + + assertThat(violations).isEmpty(); + } + + @Test + public void whenMethodReturnValuesIsInvalid_validationShouldFail() { + assertThatThrownBy(() -> accountService.getAnInvalidAlphanumericValue()).isInstanceOf(ConstraintViolationException.class) + .hasMessageContaining("must contain at least one numeric character") + .hasMessageContaining("must have between 6 and 32 characters"); + } + +} \ No newline at end of file