diff --git a/javax-validation-advanced/.gitignore b/javax-validation-advanced/.gitignore new file mode 100644 index 0000000000..8027134ae9 --- /dev/null +++ b/javax-validation-advanced/.gitignore @@ -0,0 +1,6 @@ +.classpath +.project +.settings/ +target/ +bin/ + diff --git a/javax-validation-advanced/README.md b/javax-validation-advanced/README.md new file mode 100644 index 0000000000..8f03107330 --- /dev/null +++ b/javax-validation-advanced/README.md @@ -0,0 +1,5 @@ +## Java Bean Validation Examples + +This module contains articles about Bean Validation. + +### Relevant Articles: diff --git a/javax-validation-advanced/pom.xml b/javax-validation-advanced/pom.xml new file mode 100644 index 0000000000..39da166071 --- /dev/null +++ b/javax-validation-advanced/pom.xml @@ -0,0 +1,34 @@ + + + 4.0.0 + javax-validation-advanced + 0.1-SNAPSHOT + javax-validation-advanced + + + com.baeldung + parent-modules + 1.0.0-SNAPSHOT + + + + + org.springframework.boot + spring-boot-starter-validation + ${spring.boot.version} + + + com.fasterxml.jackson.core + jackson-databind + ${jackson.databind.version} + + + + + 2.7.5 + 2.14.0 + + + \ No newline at end of file diff --git a/javax-validation-advanced/src/main/java/com/baeldung/javaxval/afterdeserialization/BeanDeserializerModifierWithValidation.java b/javax-validation-advanced/src/main/java/com/baeldung/javaxval/afterdeserialization/BeanDeserializerModifierWithValidation.java new file mode 100644 index 0000000000..3e20ebad6b --- /dev/null +++ b/javax-validation-advanced/src/main/java/com/baeldung/javaxval/afterdeserialization/BeanDeserializerModifierWithValidation.java @@ -0,0 +1,20 @@ +package com.baeldung.javaxval.afterdeserialization; + +import com.fasterxml.jackson.databind.BeanDescription; +import com.fasterxml.jackson.databind.DeserializationConfig; +import com.fasterxml.jackson.databind.JsonDeserializer; +import com.fasterxml.jackson.databind.deser.BeanDeserializer; +import com.fasterxml.jackson.databind.deser.BeanDeserializerModifier; + +public class BeanDeserializerModifierWithValidation extends BeanDeserializerModifier { + + @Override + public JsonDeserializer modifyDeserializer(DeserializationConfig config, BeanDescription beanDesc, JsonDeserializer deserializer) { + if (deserializer instanceof BeanDeserializer) { + return new BeanDeserializerWithValidation((BeanDeserializer) deserializer); + } + + return deserializer; + } + +} diff --git a/javax-validation-advanced/src/main/java/com/baeldung/javaxval/afterdeserialization/BeanDeserializerWithValidation.java b/javax-validation-advanced/src/main/java/com/baeldung/javaxval/afterdeserialization/BeanDeserializerWithValidation.java new file mode 100644 index 0000000000..332c83010d --- /dev/null +++ b/javax-validation-advanced/src/main/java/com/baeldung/javaxval/afterdeserialization/BeanDeserializerWithValidation.java @@ -0,0 +1,40 @@ +package com.baeldung.javaxval.afterdeserialization; + +import java.io.IOException; +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 com.fasterxml.jackson.core.JsonParser; +import com.fasterxml.jackson.databind.DeserializationContext; +import com.fasterxml.jackson.databind.deser.BeanDeserializer; +import com.fasterxml.jackson.databind.deser.BeanDeserializerBase; + +public class BeanDeserializerWithValidation extends BeanDeserializer { + + private static final ValidatorFactory factory = Validation.buildDefaultValidatorFactory(); + private static final Validator validator = factory.getValidator(); + + protected BeanDeserializerWithValidation(BeanDeserializerBase src) { + super(src); + } + + @Override + public Object deserialize(JsonParser p, DeserializationContext ctxt) throws IOException { + Object instance = super.deserialize(p, ctxt); + validate(instance); + return instance; + } + + public void validate(T t) { + Set> violations = validator.validate(t); + if (!violations.isEmpty()) { + throw new ConstraintViolationException(violations); + } + } + +} diff --git a/javax-validation-advanced/src/main/java/com/baeldung/javaxval/afterdeserialization/Student.java b/javax-validation-advanced/src/main/java/com/baeldung/javaxval/afterdeserialization/Student.java new file mode 100644 index 0000000000..c1923de265 --- /dev/null +++ b/javax-validation-advanced/src/main/java/com/baeldung/javaxval/afterdeserialization/Student.java @@ -0,0 +1,14 @@ +package com.baeldung.javaxval.afterdeserialization; + +import javax.validation.constraints.Size; + +public class Student { + + @Size(min = 5, max = 10, message = "Student's name must be between 5 and 10 characters") + private String name; + + public String getName() { + return name; + } + +} diff --git a/javax-validation-advanced/src/main/java/com/baeldung/javaxval/afterdeserialization/StudentDeserializerWithValidation.java b/javax-validation-advanced/src/main/java/com/baeldung/javaxval/afterdeserialization/StudentDeserializerWithValidation.java new file mode 100644 index 0000000000..e652a43ccb --- /dev/null +++ b/javax-validation-advanced/src/main/java/com/baeldung/javaxval/afterdeserialization/StudentDeserializerWithValidation.java @@ -0,0 +1,24 @@ +package com.baeldung.javaxval.afterdeserialization; + +import java.io.IOException; +import java.io.InputStream; + +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.module.SimpleModule; + +public class StudentDeserializerWithValidation { + + public static Student readStudent(InputStream inputStream) throws IOException { + ObjectMapper mapper = getObjectMapperWithValidation(); + return mapper.readValue(inputStream, Student.class); + } + + private static ObjectMapper getObjectMapperWithValidation() { + SimpleModule validationModule = new SimpleModule(); + validationModule.setDeserializerModifier(new BeanDeserializerModifierWithValidation()); + ObjectMapper mapper = new ObjectMapper(); + mapper.registerModule(validationModule); + return mapper; + } + +} diff --git a/javax-validation-advanced/src/test/java/com/baeldung/javaxval/StudentDeserializerWithValidationUnitTest.java b/javax-validation-advanced/src/test/java/com/baeldung/javaxval/StudentDeserializerWithValidationUnitTest.java new file mode 100644 index 0000000000..edbe85ecfe --- /dev/null +++ b/javax-validation-advanced/src/test/java/com/baeldung/javaxval/StudentDeserializerWithValidationUnitTest.java @@ -0,0 +1,51 @@ +package com.baeldung.javaxval; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertThrows; + +import java.io.IOException; +import java.io.InputStream; + +import javax.validation.ConstraintViolationException; + +import org.junit.jupiter.api.Test; + +import com.baeldung.javaxval.afterdeserialization.Student; +import com.baeldung.javaxval.afterdeserialization.StudentDeserializerWithValidation; + +public class StudentDeserializerWithValidationUnitTest { + + private final String EXPECTED_ERROR_MESSAGE = "name: Student's name must be between 5 and 10 characters"; + private final String EXPECTED_STUDENT_NAME = "Daniel"; + private final String NAME_TOO_LONG_STUDENT_FILE = "nameTooLongStudent.json"; + private final String NAME_TOO_SHORT_STUDENT_FILE = "nameTooShortStudent.json"; + private final String SUBDIRECTORY = "afterdeserialization/"; + private final String VALID_STUDENT_FILE = "validStudent.json"; + + @Test + void givenValidStudent_WhenReadStudent_ThenReturnStudent() throws IOException { + InputStream inputStream = getInputStream(VALID_STUDENT_FILE); + Student result = StudentDeserializerWithValidation.readStudent(inputStream); + assertEquals(EXPECTED_STUDENT_NAME, result.getName()); + } + + @Test + void givenStudentWithTooShortName_WhenReadStudent_ThenThrows() { + InputStream inputStream = getInputStream(NAME_TOO_SHORT_STUDENT_FILE); + ConstraintViolationException constraintViolationException = assertThrows(ConstraintViolationException.class, () -> StudentDeserializerWithValidation.readStudent(inputStream)); + assertEquals(EXPECTED_ERROR_MESSAGE, constraintViolationException.getMessage()); + } + + @Test + void givenStudentWithTooLongName_WhenReadStudent_ThenThrows() { + InputStream inputStream = getInputStream(NAME_TOO_LONG_STUDENT_FILE); + ConstraintViolationException constraintViolationException = assertThrows(ConstraintViolationException.class, () -> StudentDeserializerWithValidation.readStudent(inputStream)); + assertEquals(EXPECTED_ERROR_MESSAGE, constraintViolationException.getMessage()); + } + + private InputStream getInputStream(String fileName) { + return getClass().getClassLoader() + .getResourceAsStream(SUBDIRECTORY + fileName); + } + +} diff --git a/javax-validation-advanced/src/test/resources/afterdeserialization/nameTooLongStudent.json b/javax-validation-advanced/src/test/resources/afterdeserialization/nameTooLongStudent.json new file mode 100644 index 0000000000..e537ecb25d --- /dev/null +++ b/javax-validation-advanced/src/test/resources/afterdeserialization/nameTooLongStudent.json @@ -0,0 +1,3 @@ +{ + "name": "Constantine" +} \ No newline at end of file diff --git a/javax-validation-advanced/src/test/resources/afterdeserialization/nameTooShortStudent.json b/javax-validation-advanced/src/test/resources/afterdeserialization/nameTooShortStudent.json new file mode 100644 index 0000000000..79ab10cb80 --- /dev/null +++ b/javax-validation-advanced/src/test/resources/afterdeserialization/nameTooShortStudent.json @@ -0,0 +1,3 @@ +{ + "name": "Max" +} \ No newline at end of file diff --git a/javax-validation-advanced/src/test/resources/afterdeserialization/validStudent.json b/javax-validation-advanced/src/test/resources/afterdeserialization/validStudent.json new file mode 100644 index 0000000000..938002ea51 --- /dev/null +++ b/javax-validation-advanced/src/test/resources/afterdeserialization/validStudent.json @@ -0,0 +1,3 @@ +{ + "name": "Daniel" +} \ No newline at end of file diff --git a/javaxval2/.gitignore b/javaxval2/.gitignore new file mode 100644 index 0000000000..8027134ae9 --- /dev/null +++ b/javaxval2/.gitignore @@ -0,0 +1,6 @@ +.classpath +.project +.settings/ +target/ +bin/ + diff --git a/javaxval2/README.md b/javaxval2/README.md new file mode 100644 index 0000000000..8f03107330 --- /dev/null +++ b/javaxval2/README.md @@ -0,0 +1,5 @@ +## Java Bean Validation Examples + +This module contains articles about Bean Validation. + +### Relevant Articles: diff --git a/javaxval2/pom.xml b/javaxval2/pom.xml new file mode 100644 index 0000000000..2e0ed0a281 --- /dev/null +++ b/javaxval2/pom.xml @@ -0,0 +1,34 @@ + + + 4.0.0 + javaxval2 + 0.1-SNAPSHOT + javaxval2 + + + com.baeldung + parent-modules + 1.0.0-SNAPSHOT + + + + + org.springframework.boot + spring-boot-starter-validation + ${spring.boot.version} + + + com.fasterxml.jackson.core + jackson-databind + ${jackson.databind.version} + + + + + 2.7.5 + 2.14.0 + + + \ No newline at end of file diff --git a/javaxval2/src/main/java/com/baeldung/javaxval/afterdeserialization/BeanDeserializerModifierWithValidation.java b/javaxval2/src/main/java/com/baeldung/javaxval/afterdeserialization/BeanDeserializerModifierWithValidation.java new file mode 100644 index 0000000000..3e20ebad6b --- /dev/null +++ b/javaxval2/src/main/java/com/baeldung/javaxval/afterdeserialization/BeanDeserializerModifierWithValidation.java @@ -0,0 +1,20 @@ +package com.baeldung.javaxval.afterdeserialization; + +import com.fasterxml.jackson.databind.BeanDescription; +import com.fasterxml.jackson.databind.DeserializationConfig; +import com.fasterxml.jackson.databind.JsonDeserializer; +import com.fasterxml.jackson.databind.deser.BeanDeserializer; +import com.fasterxml.jackson.databind.deser.BeanDeserializerModifier; + +public class BeanDeserializerModifierWithValidation extends BeanDeserializerModifier { + + @Override + public JsonDeserializer modifyDeserializer(DeserializationConfig config, BeanDescription beanDesc, JsonDeserializer deserializer) { + if (deserializer instanceof BeanDeserializer) { + return new BeanDeserializerWithValidation((BeanDeserializer) deserializer); + } + + return deserializer; + } + +} diff --git a/javaxval2/src/main/java/com/baeldung/javaxval/afterdeserialization/BeanDeserializerWithValidation.java b/javaxval2/src/main/java/com/baeldung/javaxval/afterdeserialization/BeanDeserializerWithValidation.java new file mode 100644 index 0000000000..332c83010d --- /dev/null +++ b/javaxval2/src/main/java/com/baeldung/javaxval/afterdeserialization/BeanDeserializerWithValidation.java @@ -0,0 +1,40 @@ +package com.baeldung.javaxval.afterdeserialization; + +import java.io.IOException; +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 com.fasterxml.jackson.core.JsonParser; +import com.fasterxml.jackson.databind.DeserializationContext; +import com.fasterxml.jackson.databind.deser.BeanDeserializer; +import com.fasterxml.jackson.databind.deser.BeanDeserializerBase; + +public class BeanDeserializerWithValidation extends BeanDeserializer { + + private static final ValidatorFactory factory = Validation.buildDefaultValidatorFactory(); + private static final Validator validator = factory.getValidator(); + + protected BeanDeserializerWithValidation(BeanDeserializerBase src) { + super(src); + } + + @Override + public Object deserialize(JsonParser p, DeserializationContext ctxt) throws IOException { + Object instance = super.deserialize(p, ctxt); + validate(instance); + return instance; + } + + public void validate(T t) { + Set> violations = validator.validate(t); + if (!violations.isEmpty()) { + throw new ConstraintViolationException(violations); + } + } + +} diff --git a/javaxval2/src/main/java/com/baeldung/javaxval/afterdeserialization/Student.java b/javaxval2/src/main/java/com/baeldung/javaxval/afterdeserialization/Student.java new file mode 100644 index 0000000000..c1923de265 --- /dev/null +++ b/javaxval2/src/main/java/com/baeldung/javaxval/afterdeserialization/Student.java @@ -0,0 +1,14 @@ +package com.baeldung.javaxval.afterdeserialization; + +import javax.validation.constraints.Size; + +public class Student { + + @Size(min = 5, max = 10, message = "Student's name must be between 5 and 10 characters") + private String name; + + public String getName() { + return name; + } + +} diff --git a/javaxval2/src/main/java/com/baeldung/javaxval/afterdeserialization/StudentDeserializerWithValidation.java b/javaxval2/src/main/java/com/baeldung/javaxval/afterdeserialization/StudentDeserializerWithValidation.java new file mode 100644 index 0000000000..e652a43ccb --- /dev/null +++ b/javaxval2/src/main/java/com/baeldung/javaxval/afterdeserialization/StudentDeserializerWithValidation.java @@ -0,0 +1,24 @@ +package com.baeldung.javaxval.afterdeserialization; + +import java.io.IOException; +import java.io.InputStream; + +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.module.SimpleModule; + +public class StudentDeserializerWithValidation { + + public static Student readStudent(InputStream inputStream) throws IOException { + ObjectMapper mapper = getObjectMapperWithValidation(); + return mapper.readValue(inputStream, Student.class); + } + + private static ObjectMapper getObjectMapperWithValidation() { + SimpleModule validationModule = new SimpleModule(); + validationModule.setDeserializerModifier(new BeanDeserializerModifierWithValidation()); + ObjectMapper mapper = new ObjectMapper(); + mapper.registerModule(validationModule); + return mapper; + } + +} diff --git a/javaxval2/src/test/java/com/baeldung/javaxval/StudentDeserializerWithValidationUnitTest.java b/javaxval2/src/test/java/com/baeldung/javaxval/StudentDeserializerWithValidationUnitTest.java new file mode 100644 index 0000000000..edbe85ecfe --- /dev/null +++ b/javaxval2/src/test/java/com/baeldung/javaxval/StudentDeserializerWithValidationUnitTest.java @@ -0,0 +1,51 @@ +package com.baeldung.javaxval; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertThrows; + +import java.io.IOException; +import java.io.InputStream; + +import javax.validation.ConstraintViolationException; + +import org.junit.jupiter.api.Test; + +import com.baeldung.javaxval.afterdeserialization.Student; +import com.baeldung.javaxval.afterdeserialization.StudentDeserializerWithValidation; + +public class StudentDeserializerWithValidationUnitTest { + + private final String EXPECTED_ERROR_MESSAGE = "name: Student's name must be between 5 and 10 characters"; + private final String EXPECTED_STUDENT_NAME = "Daniel"; + private final String NAME_TOO_LONG_STUDENT_FILE = "nameTooLongStudent.json"; + private final String NAME_TOO_SHORT_STUDENT_FILE = "nameTooShortStudent.json"; + private final String SUBDIRECTORY = "afterdeserialization/"; + private final String VALID_STUDENT_FILE = "validStudent.json"; + + @Test + void givenValidStudent_WhenReadStudent_ThenReturnStudent() throws IOException { + InputStream inputStream = getInputStream(VALID_STUDENT_FILE); + Student result = StudentDeserializerWithValidation.readStudent(inputStream); + assertEquals(EXPECTED_STUDENT_NAME, result.getName()); + } + + @Test + void givenStudentWithTooShortName_WhenReadStudent_ThenThrows() { + InputStream inputStream = getInputStream(NAME_TOO_SHORT_STUDENT_FILE); + ConstraintViolationException constraintViolationException = assertThrows(ConstraintViolationException.class, () -> StudentDeserializerWithValidation.readStudent(inputStream)); + assertEquals(EXPECTED_ERROR_MESSAGE, constraintViolationException.getMessage()); + } + + @Test + void givenStudentWithTooLongName_WhenReadStudent_ThenThrows() { + InputStream inputStream = getInputStream(NAME_TOO_LONG_STUDENT_FILE); + ConstraintViolationException constraintViolationException = assertThrows(ConstraintViolationException.class, () -> StudentDeserializerWithValidation.readStudent(inputStream)); + assertEquals(EXPECTED_ERROR_MESSAGE, constraintViolationException.getMessage()); + } + + private InputStream getInputStream(String fileName) { + return getClass().getClassLoader() + .getResourceAsStream(SUBDIRECTORY + fileName); + } + +} diff --git a/javaxval2/src/test/resources/afterdeserialization/nameTooLongStudent.json b/javaxval2/src/test/resources/afterdeserialization/nameTooLongStudent.json new file mode 100644 index 0000000000..e537ecb25d --- /dev/null +++ b/javaxval2/src/test/resources/afterdeserialization/nameTooLongStudent.json @@ -0,0 +1,3 @@ +{ + "name": "Constantine" +} \ No newline at end of file diff --git a/javaxval2/src/test/resources/afterdeserialization/nameTooShortStudent.json b/javaxval2/src/test/resources/afterdeserialization/nameTooShortStudent.json new file mode 100644 index 0000000000..79ab10cb80 --- /dev/null +++ b/javaxval2/src/test/resources/afterdeserialization/nameTooShortStudent.json @@ -0,0 +1,3 @@ +{ + "name": "Max" +} \ No newline at end of file diff --git a/javaxval2/src/test/resources/afterdeserialization/validStudent.json b/javaxval2/src/test/resources/afterdeserialization/validStudent.json new file mode 100644 index 0000000000..938002ea51 --- /dev/null +++ b/javaxval2/src/test/resources/afterdeserialization/validStudent.json @@ -0,0 +1,3 @@ +{ + "name": "Daniel" +} \ No newline at end of file diff --git a/pom.xml b/pom.xml index be097f308d..feb9357bda 100644 --- a/pom.xml +++ b/pom.xml @@ -415,6 +415,7 @@ javax-sound javaxval javaxval-2 + javax-validation-advanced jaxb jersey jgit @@ -820,6 +821,7 @@ javax-sound javaxval javaxval-2 + javax-validation-advanced jaxb jersey jgit