| /* |
| * Copyright (c) 2015, 2021 Oracle and/or its affiliates. All rights reserved. |
| * |
| * This program and the accompanying materials are made available under the |
| * terms of the Eclipse Public License v. 2.0 which is available at |
| * http://www.eclipse.org/legal/epl-2.0, |
| * or the Eclipse Distribution License v. 1.0 which is available at |
| * http://www.eclipse.org/org/documents/edl-v10.php. |
| * |
| * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause |
| */ |
| |
| // Contributors: |
| // Marcel Valovy - 2.6 - initial implementation |
| package org.eclipse.persistence.testing.jaxb.beanvalidation; |
| |
| import org.eclipse.persistence.exceptions.BeanValidationException; |
| import org.eclipse.persistence.jaxb.BeanValidationMode; |
| import org.eclipse.persistence.jaxb.ConstraintViolationWrapper; |
| import org.eclipse.persistence.jaxb.JAXBContextFactory; |
| import org.eclipse.persistence.jaxb.JAXBContextProperties; |
| import org.eclipse.persistence.jaxb.JAXBMarshaller; |
| import org.eclipse.persistence.jaxb.JAXBUnmarshaller; |
| import org.eclipse.persistence.jaxb.MarshallerProperties; |
| import org.eclipse.persistence.jaxb.UnmarshallerProperties; |
| import org.eclipse.persistence.testing.jaxb.beanvalidation.dom.Department; |
| import org.eclipse.persistence.testing.jaxb.beanvalidation.dom.Drivers; |
| import org.eclipse.persistence.testing.jaxb.beanvalidation.dom.DrivingLicense; |
| import org.eclipse.persistence.testing.jaxb.beanvalidation.dom.Employee; |
| import org.junit.After; |
| import org.junit.Before; |
| |
| import jakarta.validation.Validation; |
| import jakarta.validation.ValidatorFactory; |
| import jakarta.validation.groups.Default; |
| import jakarta.xml.bind.JAXBContext; |
| import jakarta.xml.bind.JAXBException; |
| import jakarta.xml.bind.Marshaller; |
| import jakarta.xml.bind.PropertyException; |
| import java.io.File; |
| import java.util.AbstractSequentialList; |
| import java.util.ArrayList; |
| import java.util.GregorianCalendar; |
| import java.util.HashMap; |
| import java.util.LinkedList; |
| import java.util.List; |
| import java.util.Set; |
| |
| /** |
| * Test suite for tests that can be run during runtime, i.e. |
| * all that don't relate to XJC or SchemaGen. |
| * |
| * @author Marcel Valovy - marcel.valovy@oracle.com |
| */ |
| public class BeanValidationRuntimeTestCase extends junit.framework.TestCase { |
| |
| private static final String RESOURCES_PATH_RELATIVE = "org/eclipse/persistence/testing/jaxb/beanvalidation/rt/"; |
| private static final String JAVAX_NOT_NULL_MESSAGE = "{jakarta.validation.constraints.NotNull.message}"; |
| private static final String JAVAX_MIN_MESSAGE = "{jakarta.validation.constraints.Min.message}"; |
| private static final String JAVAX_SIZE_MESSAGE = "{jakarta.validation.constraints.Size.message}"; |
| private static final String JAVAX_PATTERN_MESSAGE = "{jakarta.validation.constraints.Pattern.message}"; |
| private static final String JAVAX_FUTURE_MESSAGE = "{jakarta.validation.constraints.Future.message}"; |
| private static final String JAVAX_DIGITS_MESSAGE = "{jakarta.validation.constraints.Digits.message}"; |
| private static final Class[] EMPLOYEE = new Class[]{Employee.class}; |
| private static final boolean DEBUG = false; |
| |
| private static File FILE_VALID; |
| private static File FILE_INVALID; |
| private static File FILE_JSON_VALID; |
| private static File FILE_JSON_INVALID; |
| |
| private boolean toggle = true; // Value is sensitive to the order of methods in testBeanValidation() method. |
| private ValidatorFactory preferredValidatorFactory; |
| private JAXBMarshaller marshallerValidOn; |
| private JAXBMarshaller marshallerValidOff; |
| private JAXBUnmarshaller unmarshallerValidOn; |
| private JAXBUnmarshaller unmarshallerValidOff; |
| private Employee employeeValid = new Employee() |
| .withId(0xCAFEBABE) |
| .withAge(15) |
| .withPersonalName("Richard") |
| .withPhoneNumber("(420)287-4422") |
| .withDepartment(Department.JavaEE) |
| .withDrivingLicense(new DrivingLicense(3326, new GregorianCalendar(2029, 12, 31).getTime())); |
| private Employee employeeInvalid = new Employee() |
| .withAge(15) |
| .withPersonalName("Wo") |
| .withPhoneNumber("287-4422") |
| .withDrivingLicense(new DrivingLicense(1234567, new GregorianCalendar(2010, 5, 20).getTime())); |
| // Order is good just for debug. The CVs themselves aren't ordered. |
| private AbstractSequentialList<String> violationMessages = new LinkedList<String>(){ |
| { |
| add(JAVAX_NOT_NULL_MESSAGE); // id |
| add(JAVAX_MIN_MESSAGE); // age |
| add(JAVAX_SIZE_MESSAGE); // personalName |
| add(JAVAX_PATTERN_MESSAGE); // phoneNumber |
| add(JAVAX_NOT_NULL_MESSAGE); // department |
| add(JAVAX_FUTURE_MESSAGE); // drivingLicense.validThrough |
| add(JAVAX_DIGITS_MESSAGE); // drivingLicense.id |
| }}; |
| private AbstractSequentialList<String> violationMessagesWithoutGroup = new LinkedList<String>(){ |
| { |
| add(JAVAX_NOT_NULL_MESSAGE); // id |
| add(JAVAX_SIZE_MESSAGE); // personalName |
| add(JAVAX_PATTERN_MESSAGE); // phoneNumber |
| add(JAVAX_NOT_NULL_MESSAGE); // department |
| }}; |
| |
| /** |
| * Tests the Bean Validation for MOXy Runtime, with Target groups. |
| * Tested features: |
| * - setting properties (mode, preferred validation factory) through JAXBContext, |
| * marshaller and unmarshaller, and overriding them, |
| * - validation on valid objects before marshalling and after unmarshalling, |
| * - validation on invalid objects before marshalling and after unmarshalling, |
| * - validation with Target groups, |
| * - retrieval of correct error messages when constraint violations happen. |
| * Everything is tested with both XML and JSON marshalling and unmarshalling. |
| */ |
| public void testBeanValidation() throws Exception{ |
| validEmployee(FILE_VALID); |
| invalidEmployee(FILE_INVALID); |
| switchToJson(); |
| validEmployee(FILE_JSON_VALID); |
| invalidEmployee(FILE_JSON_INVALID); |
| |
| clearResources(); |
| } |
| |
| private void validEmployee(File file) throws Exception { |
| |
| toggleDriversGroupOnOff(); |
| |
| marshallerValidOn.marshal(employeeValid, file); |
| assertTrue(marshallerValidOn.getConstraintViolations().isEmpty()); |
| |
| Employee employeeUnm = (Employee) unmarshallerValidOn.unmarshal(file); |
| assertTrue(unmarshallerValidOn.getConstraintViolations().isEmpty()); |
| |
| assertEquals(employeeValid, employeeUnm); |
| } |
| |
| private void invalidEmployee(File fileInvalid) throws Exception { |
| |
| JAXBException exception = null; |
| toggleDriversGroupOnOff(); |
| |
| /* Marshal w/ validation - doesn't pass (we want to check that). */ |
| try { |
| marshallerValidOn.marshal(employeeInvalid, fileInvalid); |
| } catch (JAXBException e) { |
| exception = e; |
| } |
| assertNotNull(exception); |
| assertEquals(String.valueOf(BeanValidationException.CONSTRAINT_VIOLATION), exception.getErrorCode()); |
| if (DEBUG) System.out.println(exception.getMessage()); |
| checkValidationMessages(marshallerValidOn.getConstraintViolations(), violationMessages); |
| |
| /* Marshal w/o validation - creates file for the next part of the test. */ |
| marshallerValidOff.marshal(employeeInvalid, fileInvalid); |
| Set<ConstraintViolationWrapper<Object>> marshalCV = marshallerValidOff.getConstraintViolations(); |
| assertTrue(marshalCV.isEmpty()); |
| |
| /* Unmarshal w/ validation - doesn't pass (we want to check that). */ |
| exception = null; |
| try { unmarshallerValidOn.unmarshal(fileInvalid); } |
| catch (JAXBException e) { exception = e; } |
| assertNotNull(exception); |
| assertEquals(String.valueOf(BeanValidationException.CONSTRAINT_VIOLATION), exception.getErrorCode()); |
| if (DEBUG) System.out.println(exception.getMessage()); |
| checkValidationMessages(unmarshallerValidOn.getConstraintViolations(), violationMessages); |
| |
| /* Unmarshal w/ validation AND no groups - doesn't pass (we want to check that). */ |
| toggleDriversGroupOnOff(); |
| exception = null; |
| |
| try { unmarshallerValidOn.unmarshal(fileInvalid); } |
| catch (JAXBException e) { exception = e; } |
| assertNotNull(exception); |
| assertEquals(String.valueOf(BeanValidationException.CONSTRAINT_VIOLATION), exception.getErrorCode()); |
| if (DEBUG) System.out.println(exception.getMessage()); |
| checkValidationMessages(unmarshallerValidOn.getConstraintViolations(), violationMessagesWithoutGroup); |
| toggleDriversGroupOnOff(); |
| |
| /* Unmarshal w/o validation - testing that invalid objects are correctly unmarshalled when validation is NONE. */ |
| Employee employeeUnm = (Employee) unmarshallerValidOff.unmarshal(fileInvalid); |
| assertTrue(unmarshallerValidOff.getConstraintViolations().isEmpty()); |
| |
| /* Final check that the validation feature did not affect original behavior of JAXB. */ |
| assertEquals(employeeInvalid, employeeUnm); |
| } |
| |
| private void checkValidationMessages(Set<ConstraintViolationWrapper<Object>> constraintViolations, |
| List<String> expectedMessages) { |
| List<String> violationMessages = new ArrayList<>(); |
| for (final ConstraintViolationWrapper cv : constraintViolations) { |
| violationMessages.add(cv.getMessageTemplate()); |
| } |
| |
| assertSame(expectedMessages.size(), violationMessages.size()); |
| assertTrue(violationMessages.containsAll(expectedMessages)); |
| } |
| |
| private void toggleDriversGroupOnOff() throws PropertyException { |
| if (toggle ^= true) { |
| marshallerValidOn.setProperty(MarshallerProperties.BEAN_VALIDATION_GROUPS, new Class[]{Default.class, |
| Drivers.class}); |
| unmarshallerValidOn.setProperty(MarshallerProperties.BEAN_VALIDATION_GROUPS, new Class[] { Default.class, |
| Drivers.class }); |
| } else { |
| marshallerValidOn.setProperty(MarshallerProperties.BEAN_VALIDATION_GROUPS, new Class[0]); |
| unmarshallerValidOn.setProperty(MarshallerProperties.BEAN_VALIDATION_GROUPS, new Class[0]); |
| } |
| } |
| |
| private void switchToJson() throws PropertyException { |
| marshallerValidOn.setProperty(JAXBContextProperties.MEDIA_TYPE, "application/json"); |
| marshallerValidOn.setProperty(JAXBContextProperties.JSON_INCLUDE_ROOT, true); |
| unmarshallerValidOn.setProperty(JAXBContextProperties.MEDIA_TYPE, "application/json"); |
| unmarshallerValidOn.setProperty(JAXBContextProperties.JSON_INCLUDE_ROOT, true); |
| marshallerValidOff.setProperty(JAXBContextProperties.MEDIA_TYPE, "application/json"); |
| marshallerValidOff.setProperty(JAXBContextProperties.JSON_INCLUDE_ROOT, true); |
| unmarshallerValidOff.setProperty(JAXBContextProperties.MEDIA_TYPE, "application/json"); |
| unmarshallerValidOff.setProperty(JAXBContextProperties.JSON_INCLUDE_ROOT, true); |
| } |
| |
| private void clearResources() { |
| assertTrue(FILE_VALID.delete()); |
| assertTrue(FILE_INVALID.delete()); |
| assertTrue(FILE_JSON_VALID.delete()); |
| assertTrue(FILE_JSON_INVALID.delete()); |
| } |
| |
| @Override |
| @Before |
| public void setUp() throws Exception { |
| preferredValidatorFactory = Validation.buildDefaultValidatorFactory(); |
| |
| JAXBContext ctx = JAXBContextFactory.createContext(EMPLOYEE, null); |
| marshallerValidOn = (JAXBMarshaller) ctx.createMarshaller(); |
| marshallerValidOn.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true); |
| marshallerValidOff = (JAXBMarshaller) ctx.createMarshaller(); |
| /* tests setting the property through marshaller */ |
| marshallerValidOff.setProperty(MarshallerProperties.BEAN_VALIDATION_MODE, BeanValidationMode.NONE); |
| marshallerValidOff.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true); |
| |
| JAXBContext ctxValidationOff = JAXBContextFactory.createContext(EMPLOYEE, |
| new HashMap<String, Object>(){{ |
| put(JAXBContextProperties.BEAN_VALIDATION_MODE, BeanValidationMode.NONE); |
| put(JAXBContextProperties.BEAN_VALIDATION_FACTORY, preferredValidatorFactory);}}); |
| unmarshallerValidOn = (JAXBUnmarshaller) ctxValidationOff.createUnmarshaller(); |
| /* tests setting the property through unmarshaller */ |
| unmarshallerValidOn.setProperty(UnmarshallerProperties.BEAN_VALIDATION_MODE, BeanValidationMode.CALLBACK); |
| unmarshallerValidOff = (JAXBUnmarshaller) ctxValidationOff.createUnmarshaller(); |
| |
| String RESOURCES_PATH = Thread.currentThread().getContextClassLoader().getResource(RESOURCES_PATH_RELATIVE).getPath(); |
| FILE_VALID = new File(RESOURCES_PATH + "employee.xml"); |
| FILE_INVALID = new File(RESOURCES_PATH + "employeeInvalid.xml"); |
| FILE_JSON_VALID = new File(RESOURCES_PATH + "employee.json"); |
| FILE_JSON_INVALID = new File(RESOURCES_PATH + "employeeInvalid.json"); |
| } |
| |
| @Override |
| @After |
| public void tearDown() throws Exception { |
| marshallerValidOn = marshallerValidOff = null; |
| unmarshallerValidOn = unmarshallerValidOff = null; |
| employeeValid = employeeInvalid = null; |
| violationMessages = null; |
| preferredValidatorFactory = null; |
| } |
| |
| } |