From dcdec16e48847501a11d1015a08e1b81b70f91cd Mon Sep 17 00:00:00 2001
From: Michal Srb <msrb@redhat.com>
Date: Wed, 15 Jan 2014 09:26:34 +0100
Subject: [PATCH] Port to hibernate validator 5

---
 .../MessageSourceResourceBundleLocator.java        |  2 +-
 .../MethodValidationInterceptor.java               | 89 ++++++++++++++++------
 2 files changed, 66 insertions(+), 25 deletions(-)

diff --git a/spring-context/src/main/java/org/springframework/validation/beanvalidation/MessageSourceResourceBundleLocator.java b/spring-context/src/main/java/org/springframework/validation/beanvalidation/MessageSourceResourceBundleLocator.java
index 83c538e..dc73118 100644
--- a/spring-context/src/main/java/org/springframework/validation/beanvalidation/MessageSourceResourceBundleLocator.java
+++ b/spring-context/src/main/java/org/springframework/validation/beanvalidation/MessageSourceResourceBundleLocator.java
@@ -19,7 +19,7 @@ package org.springframework.validation.beanvalidation;
 import java.util.Locale;
 import java.util.ResourceBundle;
 
-import org.hibernate.validator.resourceloading.ResourceBundleLocator;
+import org.hibernate.validator.spi.resourceloading.ResourceBundleLocator;
 
 import org.springframework.context.MessageSource;
 import org.springframework.context.support.MessageSourceResourceBundle;
diff --git a/spring-context/src/main/java/org/springframework/validation/beanvalidation/MethodValidationInterceptor.java b/spring-context/src/main/java/org/springframework/validation/beanvalidation/MethodValidationInterceptor.java
index 3d4b907..83bb184 100644
--- a/spring-context/src/main/java/org/springframework/validation/beanvalidation/MethodValidationInterceptor.java
+++ b/spring-context/src/main/java/org/springframework/validation/beanvalidation/MethodValidationInterceptor.java
@@ -16,7 +16,11 @@
 
 package org.springframework.validation.beanvalidation;
 
+import java.lang.reflect.Method;
 import java.util.Set;
+
+import javax.validation.ConstraintViolation;
+import javax.validation.ConstraintViolationException;
 import javax.validation.Validation;
 import javax.validation.Validator;
 import javax.validation.ValidatorFactory;
@@ -24,11 +28,8 @@ import javax.validation.ValidatorFactory;
 import org.aopalliance.intercept.MethodInterceptor;
 import org.aopalliance.intercept.MethodInvocation;
 import org.hibernate.validator.HibernateValidator;
-import org.hibernate.validator.method.MethodConstraintViolation;
-import org.hibernate.validator.method.MethodConstraintViolationException;
-import org.hibernate.validator.method.MethodValidator;
-
 import org.springframework.core.annotation.AnnotationUtils;
+import org.springframework.util.ReflectionUtils;
 import org.springframework.validation.annotation.Validated;
 
 /**
@@ -45,25 +46,48 @@ import org.springframework.validation.annotation.Validated;
  * at the type level of the containing target class, applying to all public service methods
  * of that class. By default, JSR-303 will validate against its default group only.
  *
- * <p>As of Spring 3.1, this functionality requires Hibernate Validator 4.2 or higher.
- * Once Bean Validation 1.1 becomes available, this class will autodetect a compliant
- * provider and automatically use the standard method validation support there.
+ * <p>As of Spring 4.0, this functionality requires either a Bean Validation 1.1 provider
+ * (such as Hibernate Validator 5.0) or the Bean Validation 1.0 API with Hibernate Validator
+ * 4.2 or 4.3. The actual provider will be autodetected and automatically adapted.
  *
  * @author Juergen Hoeller
  * @since 3.1
  * @see MethodValidationPostProcessor
+ * @see javax.validation.executable.ExecutableValidator
  * @see org.hibernate.validator.method.MethodValidator
  */
 public class MethodValidationInterceptor implements MethodInterceptor {
 
-	private final MethodValidator validator;
+	private static Method forExecutablesMethod;
+
+	private static Method validateParametersMethod;
+
+	private static Method validateReturnValueMethod;
+
+	static {
+		try {
+			forExecutablesMethod = Validator.class.getMethod("forExecutables");
+			Class<?> executableValidatorClass = forExecutablesMethod.getReturnType();
+			validateParametersMethod = executableValidatorClass.getMethod(
+					"validateParameters", Object.class, Method.class, Object[].class, Class[].class);
+			validateReturnValueMethod = executableValidatorClass.getMethod(
+					"validateReturnValue", Object.class, Method.class, Object.class, Class[].class);
+		}
+		catch (Exception ex) {
+			// Bean Validation 1.1 ExecutableValidator API not available
+		}
+	}
+
+
+	private final Validator validator;
 
 
 	/**
 	 * Create a new MethodValidationInterceptor using a default JSR-303 validator underneath.
 	 */
 	public MethodValidationInterceptor() {
-		this(Validation.byProvider(HibernateValidator.class).configure().buildValidatorFactory());
+		this(forExecutablesMethod != null ? Validation.buildDefaultValidatorFactory() :
+				HibernateValidatorDelegate.buildValidatorFactory());
 	}
 
 	/**
@@ -79,24 +103,32 @@ public class MethodValidationInterceptor implements MethodInterceptor {
 	 * @param validator the JSR-303 Validator to use
 	 */
 	public MethodValidationInterceptor(Validator validator) {
-		this.validator = validator.unwrap(MethodValidator.class);
+		this.validator = validator;
 	}
 
 
+	@SuppressWarnings("unchecked")
 	public Object invoke(MethodInvocation invocation) throws Throwable {
-		Class[] groups = determineValidationGroups(invocation);
-		Set<MethodConstraintViolation<Object>> result = this.validator.validateAllParameters(
-				invocation.getThis(), invocation.getMethod(), invocation.getArguments(), groups);
-		if (!result.isEmpty()) {
-			throw new MethodConstraintViolationException(result);
-		}
-		Object returnValue = invocation.proceed();
-		result = this.validator.validateReturnValue(
-				invocation.getThis(), invocation.getMethod(), returnValue, groups);
-		if (!result.isEmpty()) {
-			throw new MethodConstraintViolationException(result);
+		Class<?>[] groups = determineValidationGroups(invocation);
+		if (forExecutablesMethod != null) {
+			Object executableValidator = ReflectionUtils.invokeMethod(forExecutablesMethod, this.validator);
+			Set<ConstraintViolation<?>> result = (Set<ConstraintViolation<?>>)
+					ReflectionUtils.invokeMethod(validateParametersMethod, executableValidator,
+							invocation.getThis(), invocation.getMethod(), invocation.getArguments(), groups);
+			if (!result.isEmpty()) {
+				throw new ConstraintViolationException(result);
+			}
+			Object returnValue = invocation.proceed();
+			result = (Set<ConstraintViolation<?>>)
+					ReflectionUtils.invokeMethod(validateReturnValueMethod, executableValidator,
+							invocation.getThis(), invocation.getMethod(), returnValue, groups);
+			if (!result.isEmpty()) {
+				throw new ConstraintViolationException(result);
+			}
+			return returnValue;
 		}
-		return returnValue;
+		
+		return invocation.proceed();
 	}
 
 	/**
@@ -106,9 +138,18 @@ public class MethodValidationInterceptor implements MethodInterceptor {
 	 * @param invocation the current MethodInvocation
 	 * @return the applicable validation groups as a Class array
 	 */
-	protected Class[] determineValidationGroups(MethodInvocation invocation) {
+	protected Class<?>[] determineValidationGroups(MethodInvocation invocation) {
 		Validated valid = AnnotationUtils.findAnnotation(invocation.getThis().getClass(), Validated.class);
-		return (valid != null ? valid.value() : new Class[0]);
+		return (valid != null ? valid.value() : new Class<?>[0]);
 	}
 
+    /**
+     * Inner class to avoid a hard-coded Hibernate Validator 4.2/4.3 dependency.
+     */
+    private static class HibernateValidatorDelegate {
+
+        public static ValidatorFactory buildValidatorFactory() {
+            return Validation.byProvider(HibernateValidator.class).configure().buildValidatorFactory();
+        }
+    }
 }
-- 
1.8.3.1

