blob: 25ba97f0e86c33ca08e0009bd4223e6f6d4dbe67 [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2015, 2016 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 v1.0 and Eclipse Distribution License v. 1.0
* which accompanies this distribution.
* The Eclipse Public License is available at http://www.eclipse.org/legal/epl-v10.html
* and the Eclipse Distribution License is available at
* http://www.eclipse.org/org/documents/edl-v10.php.
*
* 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.BeanValidationHelper;
import org.eclipse.persistence.jaxb.ConstraintViolationWrapper;
import org.eclipse.persistence.jaxb.JAXBContext;
import org.eclipse.persistence.jaxb.JAXBContextFactory;
import org.eclipse.persistence.jaxb.JAXBContextProperties;
import org.eclipse.persistence.jaxb.JAXBMarshaller;
import org.eclipse.persistence.testing.jaxb.beanvalidation.dom.Employee;
import org.eclipse.persistence.testing.jaxb.beanvalidation.special.ConstructorAnnotatedEmployee;
import org.eclipse.persistence.testing.jaxb.beanvalidation.special.CustomAnnotatedEmployee;
import org.eclipse.persistence.testing.jaxb.beanvalidation.special.InheritanceAnnotatedEmployee;
import org.eclipse.persistence.testing.jaxb.beanvalidation.special.MethodAnnotatedEmployee;
import org.eclipse.persistence.testing.jaxb.beanvalidation.special.NonConstrainedClass;
import javax.validation.ConstraintValidatorFactory;
import javax.validation.ConstraintViolation;
import javax.validation.MessageInterpolator;
import javax.validation.ParameterNameProvider;
import javax.validation.Path;
import javax.validation.TraversableResolver;
import javax.validation.Validator;
import javax.validation.ValidatorContext;
import javax.validation.ValidatorFactory;
import javax.validation.executable.ExecutableValidator;
import javax.validation.metadata.BeanDescriptor;
import javax.validation.metadata.ConstraintDescriptor;
import javax.xml.bind.SchemaOutputResolver;
import javax.xml.transform.Result;
import javax.xml.transform.stream.StreamResult;
import java.io.File;
import java.io.IOException;
import java.io.StringWriter;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;
import static org.eclipse.persistence.testing.jaxb.beanvalidation.ContentComparator.equalsXML;
/**
* Test case storing non-standard tests, i.e. those that didn't fit neither in
* {@link org.eclipse.persistence.testing.jaxb.beanvalidation.BeanValidationRuntimeTestCase} nor in
* {@link org.eclipse.persistence.testing.jaxb.beanvalidation.BeanValidationBindingsTestCase}.
*
* @author Marcel Valovy - marcel.valovy@oracle.com
*/
public class BeanValidationSpecialtiesTestCase extends junit.framework.TestCase {
private static final String NOT_NULL_MESSAGE = "{javax.validation.constraints.NotNull.message}";
private static final String CUSTOM_ANNOTATION_MESSAGE = "{org.eclipse.persistence.moxy.CustomAnnotation.message}";
private static final String GENERATOR_SCHEMA_WITH_FACETS =
"org/eclipse/persistence/testing/jaxb/beanvalidation/generator/schema_with_facets.xsd";
private static final String GENERATOR_SCHEMA_SUFFIX = "schema.xsd";
private static String GENERATOR_SCHEMA;
public void testGenerator() throws Exception {
try {
Map<String, Object> props = new HashMap<>();
props.put(JAXBContextProperties.BEAN_VALIDATION_FACETS, true);
javax.xml.bind.JAXBContext jaxbContext = JAXBContextFactory.createContext(new Class[] {Employee.class}, props);
String generatorSchemaWithFacetsPath = Thread.currentThread().getContextClassLoader().getResource(GENERATOR_SCHEMA_WITH_FACETS).getPath();
GENERATOR_SCHEMA = generatorSchemaWithFacetsPath.substring(0, generatorSchemaWithFacetsPath.lastIndexOf('/') + 1) + GENERATOR_SCHEMA_SUFFIX;
SchemaOutputResolver sor = new MySchemaOutputResolver();
jaxbContext.generateSchema(sor);
assertTrue(equalsXML(new File(generatorSchemaWithFacetsPath), new File(GENERATOR_SCHEMA)));
} finally {
if(GENERATOR_SCHEMA!=null) {
File generatorSchema = new File(GENERATOR_SCHEMA);
if(generatorSchema.exists()) {
assertTrue("Generated schema '" + GENERATOR_SCHEMA + "' was not deleted properly. DO that manually.", generatorSchema.delete());
}
}
}
}
/**
* Tests that we do not skip validation on classes that do not have any bean validation annotations but have a
* custom validation annotation.
*/
public void testCustomAnnotations() throws Exception {
JAXBMarshaller marshaller = (JAXBMarshaller) JAXBContextFactory.createContext(new
Class[]{CustomAnnotatedEmployee.class}, null).createMarshaller();
CustomAnnotatedEmployee employee = new CustomAnnotatedEmployee().withId(0xCAFEBABE);
try {
marshaller.marshal(employee, new StringWriter());
assertFalse("Constraints-breaking class escaped validation -> fail.", true);
} catch (BeanValidationException ignored) {
}
Set<ConstraintViolationWrapper<Object>> violations = marshaller.getConstraintViolations();
assertFalse("Some constraints were not validated, even though they should have been.", violations.isEmpty());
// For all, i.e. one constraintViolations.
for (ConstraintViolationWrapper cv : violations) {
assertEquals(CUSTOM_ANNOTATION_MESSAGE, cv.getMessage());
}
}
/**
* Tests that we do not skip validation on classes that do not have any bean validation annotations on fields but
* have some on methods.
*/
public void testMethodAnnotations() throws Exception {
JAXBMarshaller marshaller = (JAXBMarshaller) JAXBContextFactory.createContext(new
Class[]{MethodAnnotatedEmployee.class}, null).createMarshaller();
MethodAnnotatedEmployee employee = new MethodAnnotatedEmployee().withId(null);
try {
marshaller.marshal(employee, new StringWriter());
assertFalse("Constraints-breaking class escaped validation -> fail.", true);
} catch (BeanValidationException ignored) {
}
Set<ConstraintViolationWrapper<Object>> violations = marshaller.getConstraintViolations();
assertFalse(violations.isEmpty());
// For all, i.e. one constraintViolations.
for (ConstraintViolationWrapper cv : violations) {
assertEquals(NOT_NULL_MESSAGE, cv.getMessageTemplate());
}
}
/**
* Tests that we detect inherited constraints.
*/
public void testInheritedAnnotations() throws Exception {
JAXBMarshaller marshaller = (JAXBMarshaller) JAXBContextFactory.createContext(new
Class[]{InheritanceAnnotatedEmployee.class}, null).createMarshaller();
InheritanceAnnotatedEmployee employee = (InheritanceAnnotatedEmployee) new InheritanceAnnotatedEmployee()
.withId(null);
try {
marshaller.marshal(employee, new StringWriter());
assertFalse("Constraints-breaking class escaped validation -> fail.", true);
} catch (BeanValidationException ignored) {
}
Set<ConstraintViolationWrapper<Object>> violations = marshaller.getConstraintViolations();
assertFalse(violations.isEmpty());
// For all, i.e. one constraintViolations.
for (ConstraintViolationWrapper cv : violations) {
assertEquals(NOT_NULL_MESSAGE, cv.getMessageTemplate());
}
}
/**
* Tests that we do not skip validation on classes that do not have any bean validation annotations on fields or
* methods but have some on constructors.
*/
public void testConstructorAnnotations() throws Exception {
JAXBContext context = (JAXBContext)JAXBContextFactory.createContext(new Class[]{ConstructorAnnotatedEmployee.class}, null);
JAXBMarshaller marshaller = context.createMarshaller();
ConstructorAnnotatedEmployee employee = new ConstructorAnnotatedEmployee(null);
try {
marshaller.marshal(employee, new StringWriter());
} catch (BeanValidationException ignored) {
}
// Ok, HV is not picking up constraints on constructor. But that does not mean anything. Our job is to ensure
// that we correctly identify that the class is constrained and pass the object to the underlying BV impl.
BeanValidationHelper beanValidationHelper = context.getBeanValidationHelper();
assertTrue(beanValidationHelper.getConstraintsMap().containsKey(ConstructorAnnotatedEmployee.class));
// This will not detect the constraints violation on constructor (on HV 5.1), although it should.
// Set<? extends ConstraintViolation<?>> violations = marshaller.getConstraintViolations();
//
// assertFalse(violations.isEmpty());
//
// // For all, i.e. one constraintViolations.
// for (ConstraintViolation constraintViolation : violations) {
// assertEquals(NOT_NULL_MESSAGE, constraintViolation.getMessageTemplate());
// }
}
/**
* Tests {@link org.eclipse.persistence.jaxb.JAXBContextProperties#BEAN_VALIDATION_NO_OPTIMISATION} property.
*/
public void testNoOptimisationOption() throws Exception {
JAXBMarshaller marshaller = (JAXBMarshaller) JAXBContextFactory.createContext(
new Class[]{ NonConstrainedClass.class },
new HashMap<String, Object>(){{
put(JAXBContextProperties.BEAN_VALIDATION_NO_OPTIMISATION, true);
put(JAXBContextProperties.BEAN_VALIDATION_FACTORY, new CustomValidatorFactory());
}})
.createMarshaller();
try {
marshaller.marshal( new NonConstrainedClass(), new StringWriter());
} catch (BeanValidationException ignored) {
}
Set<ConstraintViolationWrapper<Object>> violations = marshaller.getConstraintViolations();
assertFalse(violations.isEmpty());
}
private static class MySchemaOutputResolver extends SchemaOutputResolver {
@Override
public Result createOutput(String uri, String suggestedFileName) throws IOException {
File file = new File(GENERATOR_SCHEMA);
StreamResult result = new StreamResult(file);
result.setSystemId(file.toURI().toURL().toString());
return result;
}
}
/**
* Validator factory which returns {@code false} from method {@code validate()} in provided {@code Validator}.
*/
private class CustomValidatorFactory implements ValidatorFactory {
@Override
public Validator getValidator() {
return new Validator() {
@Override
public <T> Set<ConstraintViolation<T>> validate(T t, Class<?>... classes) {
return new HashSet<ConstraintViolation<T>>(){
{ this.add(new ConstraintViolation<T>() {
@Override
public String getMessage() {
return "";
}
@Override
public String getMessageTemplate() {
return null;
}
@Override
public T getRootBean() {
//noinspection unchecked
return (T) new Object();
}
@Override
public Class<T> getRootBeanClass() {
return null;
}
@Override
public Object getLeafBean() {
return null;
}
@Override
public Object[] getExecutableParameters() {
return new Object[0];
}
@Override
public Object getExecutableReturnValue() {
return null;
}
@Override
public Path getPropertyPath() {
return new Path() {
@Override
public Iterator<Node> iterator() {
return null;
}
};
}
@Override
public Object getInvalidValue() {
return null;
}
@Override
public ConstraintDescriptor<?> getConstraintDescriptor() {
return null;
}
@Override
public <U> U unwrap(Class<U> type) {
return null;
}
}); }
};
}
@Override
public <T> Set<ConstraintViolation<T>> validateProperty(T t, String s, Class<?>... classes) {
return null;
}
@Override
public <T> Set<ConstraintViolation<T>> validateValue(Class<T> tClass, String s, Object o, Class<?>... classes) {
return null;
}
@Override
public BeanDescriptor getConstraintsForClass(Class<?> aClass) {
return null;
}
@Override
public <T> T unwrap(Class<T> tClass) {
return null;
}
@Override
public ExecutableValidator forExecutables() {
return null;
}
};
}
@Override
public ValidatorContext usingContext() {
return null;
}
@Override
public MessageInterpolator getMessageInterpolator() {
return null;
}
@Override
public TraversableResolver getTraversableResolver() {
return null;
}
@Override
public ConstraintValidatorFactory getConstraintValidatorFactory() {
return null;
}
@Override
public ParameterNameProvider getParameterNameProvider() {
return null;
}
@Override
public <T> T unwrap(Class<T> tClass) {
return null;
}
@Override
public void close() {
}
}
}