Skip to content

Explore JSON validation #949

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 7 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -52,9 +52,9 @@
import org.hibernate.validator.cdi.internal.ValidatorFactoryBean;
import org.hibernate.validator.cdi.internal.interceptor.ValidationEnabledAnnotatedType;
import org.hibernate.validator.cdi.internal.interceptor.ValidationInterceptor;
import org.hibernate.validator.cdi.internal.util.GetterPropertyMatcherHelper;
import org.hibernate.validator.internal.util.Contracts;
import org.hibernate.validator.internal.util.ExecutableHelper;
import org.hibernate.validator.internal.util.ReflectionHelper;
import org.hibernate.validator.internal.util.TypeResolutionHelper;
import org.hibernate.validator.internal.util.logging.Log;
import org.hibernate.validator.internal.util.logging.LoggerFactory;
Expand Down Expand Up @@ -99,6 +99,7 @@ public class ValidationExtension implements Extension {
*/
private final Validator validator;
private final ValidatorFactory validatorFactory;
private final GetterPropertyMatcherHelper getterPropertyMatcherHelper;
private final Set<ExecutableType> globalExecutableTypes;
private final boolean isExecutableValidationEnabled;

Expand All @@ -119,6 +120,7 @@ public ValidationExtension() {
isExecutableValidationEnabled = bootstrap.isExecutableValidationEnabled();
validatorFactory = config.buildValidatorFactory();
validator = validatorFactory.getValidator();
getterPropertyMatcherHelper = GetterPropertyMatcherHelper.forValidationFactory( validatorFactory );

executableHelper = new ExecutableHelper( new TypeResolutionHelper() );
}
Expand Down Expand Up @@ -259,7 +261,7 @@ private <T> void determineConstrainedMethods(AnnotatedType<T> type, BeanDescript
for ( AnnotatedMethod<? super T> annotatedMethod : type.getMethods() ) {
Method method = annotatedMethod.getJavaMember();

boolean isGetter = ReflectionHelper.isGetterMethod( method );
boolean isGetter = getterPropertyMatcherHelper.isProperty( method );

// obtain @ValidateOnExecution from the top-most method in the hierarchy
Method methodForExecutableTypeRetrieval = replaceWithOverriddenOrInterfaceMethod( method, overriddenAndImplementedMethods );
Expand Down Expand Up @@ -312,7 +314,7 @@ private boolean isNonGetterConstrained(Method method, BeanDescriptor beanDescrip
}

private boolean isGetterConstrained(Method method, BeanDescriptor beanDescriptor) {
String propertyName = ReflectionHelper.getPropertyName( method );
String propertyName = getterPropertyMatcherHelper.getPropertyName( method );
PropertyDescriptor propertyDescriptor = beanDescriptor.getConstraintsForProperty( propertyName );
return propertyDescriptor != null && propertyDescriptor.findConstraints()
.declaredOn( ElementType.METHOD )
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
/*
* Hibernate Validator, declare and validate application constraints
*
* License: Apache License, Version 2.0
* See the license.txt file in the root directory or <http://www.apache.org/licenses/LICENSE-2.0>.
*/
package org.hibernate.validator.cdi.internal.util;

import java.lang.reflect.Method;
import java.lang.reflect.Type;

import javax.validation.ValidatorFactory;

import org.hibernate.validator.HibernateValidatorFactory;
import org.hibernate.validator.internal.properties.DefaultGetterPropertyMatcher;
import org.hibernate.validator.spi.properties.ConstrainableExecutable;
import org.hibernate.validator.spi.properties.GetterPropertyMatcher;

/**
* A wrapper around {@link GetterPropertyMatcher}.
*
* @author Marko Bekhta
*/
public class GetterPropertyMatcherHelper {

private final GetterPropertyMatcher getterPropertyMatcher;

private GetterPropertyMatcherHelper(GetterPropertyMatcher getterPropertyMatcher) {
this.getterPropertyMatcher = getterPropertyMatcher;
}

public boolean isProperty(Method method) {
return getterPropertyMatcher.isProperty( new ConstrainableMethod( method ) );
}

public String getPropertyName(Method method) {
return getterPropertyMatcher.getPropertyName( new ConstrainableMethod( method ) );
}

public static GetterPropertyMatcherHelper forValidationFactory(ValidatorFactory factory) {
GetterPropertyMatcher getterPropertyMatcher;
if ( factory instanceof HibernateValidatorFactory ) {
getterPropertyMatcher = factory.unwrap( HibernateValidatorFactory.class ).getGetterPropertyMatcher();
}
else {
getterPropertyMatcher = new DefaultGetterPropertyMatcher();
}
return new GetterPropertyMatcherHelper( getterPropertyMatcher );
}

private static class ConstrainableMethod implements ConstrainableExecutable {

private final Method method;

private ConstrainableMethod(Method method) {
this.method = method;
}

@Override public Class<?> getReturnType() {
return method.getReturnType();
}

@Override public String getName() {
return method.getName();
}

@Override public Type[] getParameterTypes() {
return method.getParameterTypes();
}

public Method getMethod() {
return method;
}
}
}
11 changes: 10 additions & 1 deletion engine/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -103,7 +103,11 @@
<artifactId>money-api</artifactId>
<optional>true</optional>
</dependency>

<dependency>
<groupId>javax.json</groupId>
<artifactId>javax.json-api</artifactId>
<optional>true</optional>
</dependency>
<!--
Test dependencies
-->
Expand Down Expand Up @@ -147,6 +151,11 @@
<artifactId>moneta</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.glassfish</groupId>
<artifactId>javax.json</artifactId>
<scope>test</scope>
</dependency>
</dependencies>

<build>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
/*
* Hibernate Validator, declare and validate application constraints
*
* License: Apache License, Version 2.0
* See the license.txt file in the root directory or <http://www.apache.org/licenses/LICENSE-2.0>.
*/
package org.hibernate.validator;

import java.util.Set;

import javax.json.JsonObject;
import javax.validation.ConstraintViolation;

/**
* An interface for validating objects like JSON or Map.
*
* @author Marko Bekhta
*/
@Incubating
public interface HibernateFreeFormValidator {

Set<ConstraintViolation<JsonObject>> validateJson(JsonObject json, Class<?> typeToValidate, Class<?>... groups);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What is typeToValidate? Also the scope of the interface is a bit blurry to me. The class comment describes it as a generic on, but then the method signature is tied to JSON. Can we make it truly generic?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In this first experiment, Marko did use a class as a way to define the JSON type, thus this parameter here and the beanClass attribute you mention a bit later.

We already decided we don't want that so it will be changed.

}
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,10 @@
import javax.validation.valueextraction.ValueExtractor;

import org.hibernate.validator.cfg.ConstraintMapping;
import org.hibernate.validator.cfg.json.JsonConstraintMapping;
import org.hibernate.validator.constraints.ParameterScriptAssert;
import org.hibernate.validator.constraints.ScriptAssert;
import org.hibernate.validator.spi.properties.GetterPropertyMatcher;
import org.hibernate.validator.spi.resourceloading.ResourceBundleLocator;
import org.hibernate.validator.spi.scripting.ScriptEvaluator;
import org.hibernate.validator.spi.scripting.ScriptEvaluatorFactory;
Expand Down Expand Up @@ -107,6 +109,15 @@ public interface HibernateValidatorConfiguration extends Configuration<Hibernate
@Incubating
String TEMPORAL_VALIDATION_TOLERANCE = "hibernate.validator.temporal_validation_tolerance";

/**
* Property for configuring the getter property matcher, allowing to set which rules will be applied
* to determine if a method is a valid JavaBean getter.
*
* @since 6.1.0
*/
@Incubating
String GETTER_PROPERTY_MATCHER_CLASSNAME = "hibernate.validator.getter_property_matcher";

/**
* <p>
* Returns the {@link ResourceBundleLocator} used by the
Expand Down Expand Up @@ -311,4 +322,21 @@ public interface HibernateValidatorConfiguration extends Configuration<Hibernate
*/
@Incubating
HibernateValidatorConfiguration constraintValidatorPayload(Object constraintValidatorPayload);

/**
* Allows to set a getter property matcher which defines rules of what a JavaBean getter is.
*
* @param getterPropertyMatcher the {@link GetterPropertyMatcher} to be used
*
* @return {@code this} following the chaining method pattern
*
* @since 6.1.0
*/
@Incubating
HibernateValidatorConfiguration getterPropertyMatcher(GetterPropertyMatcher getterPropertyMatcher);

@Incubating
HibernateValidatorConfiguration addJsonMapping(JsonConstraintMapping mapping);

JsonConstraintMapping createJsonConstraintMapping();
}
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@

import org.hibernate.validator.constraints.ParameterScriptAssert;
import org.hibernate.validator.constraints.ScriptAssert;
import org.hibernate.validator.spi.properties.GetterPropertyMatcher;
import org.hibernate.validator.spi.scripting.ScriptEvaluator;
import org.hibernate.validator.spi.scripting.ScriptEvaluatorFactory;

Expand Down Expand Up @@ -47,6 +48,16 @@ public interface HibernateValidatorFactory extends ValidatorFactory {
@Incubating
Duration getTemporalValidationTolerance();

/**
* Returns the getter property matcher which defines what rules of what a JavaBeans getter is.
*
* @return the getter property matcher of current {@link ValidatorFactory}
*
* @since 6.1.0
*/
@Incubating
GetterPropertyMatcher getGetterPropertyMatcher();

/**
* Returns a context for validator configuration via options from the
* Bean Validation API as well as specific ones from Hibernate Validator.
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
/*
* Hibernate Validator, declare and validate application constraints
*
* License: Apache License, Version 2.0
* See the license.txt file in the root directory or <http://www.apache.org/licenses/LICENSE-2.0>.
*/
package org.hibernate.validator.cfg.json;

/**
* Facet of a constraint mapping creational context which allows to mark the underlying
* element as to be validated in a cascaded way.
*
* @author Gunnar Morling
* @author Kevin Pollet &lt;kevin.pollet@serli.com&gt; (C) 2011 SERLI
*/
public interface Cascadable<C extends Cascadable<C>> {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why is it that all these interfaces are copied for JSON?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So the issue is that when declaring something as cascading, we need to define the new JSON type that will be used.

Maybe we could try to use some more generics in the DSL and have this difference managed this way (e.g. having a generics for the Cascadable DSL and inject it where it makes sense).

Not sure it's doable nor very future proof.


/**
* Marks the current element (property, parameter etc.) as cascadable.
*
* @return The current creational context following the method chaining pattern.
*/
C valid();

/**
* Adds a group conversion for this cascadable element. Several conversions may be configured for one element.
*
* @param from the source group of the conversion to be configured
*
* @return a creational context allow to set the target group of the conversion
*/
GroupConversionTargetContext<C> convertGroup(Class<?> from);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
/*
* Hibernate Validator, declare and validate application constraints
*
* License: Apache License, Version 2.0
* See the license.txt file in the root directory or <http://www.apache.org/licenses/LICENSE-2.0>.
*/
package org.hibernate.validator.cfg.json;

import org.hibernate.validator.cfg.ConstraintDef;

/**
* Facet of a constraint mapping creational context which allows to place
* constraints on the underlying element.
*
* @author Gunnar Morling
* @author Kevin Pollet &lt;kevin.pollet@serli.com&gt; (C) 2011 SERLI
*/
public interface Constrainable<C extends Constrainable<C>> {
/**
* Adds a new constraint.
*
* @param definition The constraint to add.
*
* @return The current creational context following the method chaining pattern.
*/
C constraint(ConstraintDef<?, ?> definition);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
/*
* Hibernate Validator, declare and validate application constraints
*
* License: Apache License, Version 2.0
* See the license.txt file in the root directory or <http://www.apache.org/licenses/LICENSE-2.0>.
*/
package org.hibernate.validator.cfg.json;

/**
* Creational context which allows to set the target group of a group conversion configured via
* {@link Cascadable#convertGroup(Class)}.
*
* @author Gunnar Morling
*/
public interface GroupConversionTargetContext<C> {

/**
* Sets the target group of the conversion to be configured.
*
* @param to the target group of the conversion
*
* @return The current creational context following the method chaining pattern.
*/
C to(Class<?> to);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
/*
* Hibernate Validator, declare and validate application constraints
*
* License: Apache License, Version 2.0
* See the license.txt file in the root directory or <http://www.apache.org/licenses/LICENSE-2.0>.
*/
package org.hibernate.validator.cfg.json;

/**
* Represents a constraint mapping configured via the programmatic API.
*
* @author Hardy Ferentschik
* @author Gunnar Morling
* @author Kevin Pollet &lt;kevin.pollet@serli.com&gt; (C) 2011 SERLI
* @author Yoann Rodiere
* @author Marko Bekhta
*/
public interface JsonConstraintMapping {

/**
* Starts defining constraints on the specified bean class. Each bean class may only be configured once within all
* constraint mappings used for configuring one validator factory.
*
* @param <C> The type to be configured.
* @param beanClass The bean class on which to define constraints. All constraints defined after calling this method
* are added to the bean of the type {@code beanClass} until the next call of {@code type} or {@code annotation}.
*
* @return Instance allowing for defining constraints on the specified class.
*/
<C> TypeConstraintMappingContext<C> type(Class<C> beanClass);

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
/*
* Hibernate Validator, declare and validate application constraints
*
* License: Apache License, Version 2.0
* See the license.txt file in the root directory or <http://www.apache.org/licenses/LICENSE-2.0>.
*/
package org.hibernate.validator.cfg.json;

/**
* Constraint mapping creational context representing a property of a bean. Allows
* to place constraints on the property, mark the property as cascadable and to
* navigate to other constraint targets.
*
* @author Gunnar Morling
* @author Kevin Pollet &lt;kevin.pollet@serli.com&gt; (C) 2011 SERLI
* @author Marko Bekhta
*/
public interface PropertyConstraintMappingContext extends Constrainable<PropertyConstraintMappingContext>,
PropertyTarget,
Cascadable<PropertyConstraintMappingContext> {

}
Loading