Control: tag -1 ftbfs upstream fixed-upstream patch

The cleanest way to fix this would be upgrading Spring to 5.0 or later, which drops the build-dependency on hibernate-validator 4.x and strictly requires validation-api 1.1 and hibernate-validator 5.x.

In case that's not possible in the near term, I attach here a patch consisting of changes backported from upstream to remove the code depending on hibernate-validator 4.x.

I did successfully run the unit tests of o.s.validation.beanvalidation.* with my patch applied, but it needed so much hacking that I don't entirely trust the result. I have not yet tested an actual application using the bean validation feature from the installed package, and I won't open an MR until I've done that.
>From db567eaf8d24df19d5667b0477db42ad46246c86 Mon Sep 17 00:00:00 2001
From: Ryan Tandy <r...@nardis.ca>
Date: Mon, 18 Nov 2019 21:50:19 +0000
Subject: [PATCH] Fix FTBFS with Hibernate Validator 5.x

---
 debian/changelog                              |   7 +
 debian/control                                |   4 +-
 debian/maven.rules                            |   2 +-
 .../0054-disable-hibernate-validator-4.patch  | 315 ++++++++++++++++++
 debian/patches/series                         |   1 +
 5 files changed, 326 insertions(+), 3 deletions(-)
 create mode 100644 debian/patches/0054-disable-hibernate-validator-4.patch

diff --git a/debian/changelog b/debian/changelog
index addf891d..a58e294d 100644
--- a/debian/changelog
+++ b/debian/changelog
@@ -1,3 +1,10 @@
+libspring-java (4.3.22-4.1) UNRELEASED; urgency=medium
+
+  * Fix FTBFS with libhibernate-validator-java 5.x: Upgrade to Bean Validation
+    1.1 and remove Hibernate Validator 4.x support. (Closes: #941685)
+
+ -- Ryan Tandy <r...@nardis.ca>  Mon, 18 Nov 2019 19:48:22 +0000
+
 libspring-java (4.3.22-4) unstable; urgency=medium
 
   * Team upload.
diff --git a/debian/control b/debian/control
index b7d5a178..ba1cdd91 100644
--- a/debian/control
+++ b/debian/control
@@ -43,7 +43,7 @@ Build-Depends-Indep: bsh,
                      libgeronimo-jcache-1.0-spec-java,
                      libgeronimo-jms-1.1-spec-java,
                      libgeronimo-jta-1.2-spec-java,
-                     libgeronimo-validation-1.0-spec-java,
+                     libgeronimo-validation-1.1-spec-java,
                      libhessian-java,
                      libhibernate-validator-java (>= 4.3.2),
                      libhibernate3-java (>= 3.5),
@@ -156,7 +156,7 @@ Recommends: libasm-java (>= 5.0),
             libcglib-java (>= 3.1),
             libgeronimo-ejb-3.2-spec-java,
             libgeronimo-jms-1.1-spec-java,
-            libgeronimo-validation-1.0-spec-java,
+            libgeronimo-validation-1.1-spec-java,
             libjodatime-java,
             libspring-instrument-java (= ${source:Version}),
             libtomcat9-java
diff --git a/debian/maven.rules b/debian/maven.rules
index c5658212..00a46097 100644
--- a/debian/maven.rules
+++ b/debian/maven.rules
@@ -10,7 +10,7 @@ javax.portlet portlet-api * * * *
 s/javax.resource/org.apache.geronimo.specs/ s/connector-api/geronimo-j2ee-connector_1.5_spec/ * s/.*/debian/ * *
 s/javax.servlet.jsp.jstl/org.apache.taglibs/ s/javax.servlet.jsp.jstl-api/taglibs-standard-spec/ * s/.*/debian/ * *
 s/javax.transaction/org.apache.geronimo.specs/ s/javax.transaction-api/geronimo-jta_1.2_spec/ * s/.*/debian/ * *
-s/javax.validation/org.apache.geronimo.specs/ s/validation-api/geronimo-validation_1.0_spec/ * s/.*/debian/ * *
+s/javax.validation/org.apache.geronimo.specs/ s/validation-api/geronimo-validation_1.1_spec/ * s/.*/debian/ * *
 junit junit * s/.*/4.x/ * *
 log4j log4j * s/.*/1.2.x/ * *
 org.apache.tomcat s/catalina/tomcat-catalina/ * s/.*/9.x/ * *
diff --git a/debian/patches/0054-disable-hibernate-validator-4.patch b/debian/patches/0054-disable-hibernate-validator-4.patch
new file mode 100644
index 00000000..59927894
--- /dev/null
+++ b/debian/patches/0054-disable-hibernate-validator-4.patch
@@ -0,0 +1,315 @@
+Description: Fix compilation failure with Hibernate Validator 5.x
+ Backport parts of upstream commit 54004e0d78dd92c62429999b77befbc9d9fcdb68
+ to remove Hibernate Validator 4.x dependency and use Bean Validation 1.1
+ directly.
+Author: Ryan Tandy <r...@nardis.ca>
+Forwarded: not-needed
+Bug-Debian: https://bugs.debian.org/941685
+--- a/spring-context/src/main/java/org/springframework/validation/beanvalidation/LocalValidatorFactoryBean.java
++++ b/spring-context/src/main/java/org/springframework/validation/beanvalidation/LocalValidatorFactoryBean.java
+@@ -24,11 +24,13 @@
+ import java.lang.reflect.Proxy;
+ import java.util.Arrays;
+ import java.util.HashMap;
++import java.util.List;
+ import java.util.Map;
+ import java.util.Properties;
+ import javax.validation.Configuration;
+ import javax.validation.ConstraintValidatorFactory;
+ import javax.validation.MessageInterpolator;
++import javax.validation.ParameterNameProvider;
+ import javax.validation.TraversableResolver;
+ import javax.validation.Validation;
+ import javax.validation.ValidationException;
+@@ -67,15 +69,8 @@
+  * you will almost always use the default Validator anyway. This can also be injected directly
+  * into any target dependency of type {@link org.springframework.validation.Validator}!
+  *
+- * <p><b>As of Spring 4.0, this class supports Bean Validation 1.0 and 1.1, with special support
+- * for Hibernate Validator 4.3 and 5.x</b> (see {@link #setValidationMessageSource}).
+- *
+- * <p>Note that Bean Validation 1.1's {@code #forExecutables} method isn't supported: We do not
+- * expect that method to be called by application code; consider {@link MethodValidationInterceptor}
+- * instead. If you really need programmatic {@code #forExecutables} access, inject this class as
+- * a {@link ValidatorFactory} and call {@link #getValidator()} on it, then {@code #forExecutables}
+- * on the returned native {@link Validator} reference instead of directly on this class.
+- * Alternatively, call {@code #unwrap(Validator.class)} which will also provide the native object.
++ * <p><b>As of Spring 5.0, this class requires Bean Validation 1.1, with special support
++ * for Hibernate Validator 5.x</b> (see {@link #setValidationMessageSource}).
+  *
+  * <p>This class is also being used by Spring's MVC configuration namespace, in case of the
+  * {@code javax.validation} API being present but no explicit Validator having been configured.
+@@ -90,10 +85,6 @@
+ public class LocalValidatorFactoryBean extends SpringValidatorAdapter
+ 		implements ValidatorFactory, ApplicationContextAware, InitializingBean, DisposableBean {
+ 
+-	// Bean Validation 1.1 close() method available?
+-	private static final Method closeMethod = ClassUtils.getMethodIfAvailable(ValidatorFactory.class, "close");
+-
+-
+ 	@SuppressWarnings("rawtypes")
+ 	private Class providerClass;
+ 
+@@ -312,55 +303,23 @@
+ 	}
+ 
+ 	private void configureParameterNameProviderIfPossible(Configuration<?> configuration) {
+-		try {
+-			Class<?> parameterNameProviderClass =
+-					ClassUtils.forName("javax.validation.ParameterNameProvider", getClass().getClassLoader());
+-			Method parameterNameProviderMethod =
+-					Configuration.class.getMethod("parameterNameProvider", parameterNameProviderClass);
+-			final Object defaultProvider = ReflectionUtils.invokeMethod(
+-					Configuration.class.getMethod("getDefaultParameterNameProvider"), configuration);
+-			final ParameterNameDiscoverer discoverer = this.parameterNameDiscoverer;
+-			Object parameterNameProvider = Proxy.newProxyInstance(getClass().getClassLoader(),
+-					new Class<?>[] {parameterNameProviderClass}, new InvocationHandler() {
+-				@Override
+-				public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
+-					if (method.getName().equals("getParameterNames")) {
+-						String[] result = null;
+-						if (args[0] instanceof Constructor) {
+-							result = discoverer.getParameterNames((Constructor<?>) args[0]);
+-						}
+-						else if (args[0] instanceof Method) {
+-							result = discoverer.getParameterNames((Method) args[0]);
+-						}
+-						if (result != null) {
+-							return Arrays.asList(result);
+-						}
+-						else {
+-							try {
+-								return method.invoke(defaultProvider, args);
+-							}
+-							catch (InvocationTargetException ex) {
+-								throw ex.getTargetException();
+-							}
+-						}
+-					}
+-					else {
+-						// toString, equals, hashCode
+-						try {
+-							return method.invoke(this, args);
+-						}
+-						catch (InvocationTargetException ex) {
+-							throw ex.getTargetException();
+-						}
+-					}
+-				}
+-			});
+-			ReflectionUtils.invokeMethod(parameterNameProviderMethod, configuration, parameterNameProvider);
+-
+-		}
+-		catch (Throwable ex) {
+-			// Bean Validation 1.1 API not available - simply not applying the ParameterNameDiscoverer
+-		}
++		// TODO: inner class
++		final ParameterNameDiscoverer discoverer = this.parameterNameDiscoverer;
++		final ParameterNameProvider defaultProvider = configuration.getDefaultParameterNameProvider();
++		configuration.parameterNameProvider(new ParameterNameProvider() {
++			@Override
++			public List<String> getParameterNames(Constructor<?> constructor) {
++				String[] paramNames = discoverer.getParameterNames(constructor);
++				return (paramNames != null ? Arrays.asList(paramNames) :
++						defaultProvider.getParameterNames(constructor));
++			}
++			@Override
++			public List<String> getParameterNames(Method method) {
++				String[] paramNames = discoverer.getParameterNames(method);
++				return (paramNames != null ? Arrays.asList(paramNames) :
++						defaultProvider.getParameterNames(method));
++			}
++		});
+ 	}
+ 
+ 	/**
+@@ -428,9 +387,14 @@
+ 	}
+ 
+ 
++	@Override
++	public ParameterNameProvider getParameterNameProvider() {
++		return this.validatorFactory.getParameterNameProvider();
++	}
++
+ 	public void close() {
+-		if (closeMethod != null && this.validatorFactory != null) {
+-			ReflectionUtils.invokeMethod(closeMethod, this.validatorFactory);
++		if (this.validatorFactory != null) {
++			this.validatorFactory.close();
+ 		}
+ 	}
+ 
+--- a/spring-context/src/main/java/org/springframework/validation/beanvalidation/MethodValidationInterceptor.java
++++ b/spring-context/src/main/java/org/springframework/validation/beanvalidation/MethodValidationInterceptor.java
+@@ -26,7 +26,6 @@
+ 
+ import org.aopalliance.intercept.MethodInterceptor;
+ import org.aopalliance.intercept.MethodInvocation;
+-import org.hibernate.validator.HibernateValidator;
+ 
+ import org.springframework.beans.factory.FactoryBean;
+ import org.springframework.beans.factory.SmartFactoryBean;
+@@ -50,9 +49,8 @@
+  * 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 4.0, this functionality requires either a Bean Validation 1.1 provider
+- * (such as Hibernate Validator 5.x) or the Bean Validation 1.0 API with Hibernate Validator
+- * 4.3. The actual provider will be autodetected and automatically adapted.
++ * <p>As of Spring 5.0, this functionality requires a Bean Validation 1.1 provider
++ * (such as Hibernate Validator 5.x).
+  *
+  * @author Juergen Hoeller
+  * @since 3.1
+@@ -90,8 +88,7 @@
+ 	 * Create a new MethodValidationInterceptor using a default JSR-303 validator underneath.
+ 	 */
+ 	public MethodValidationInterceptor() {
+-		this(forExecutablesMethod != null ? Validation.buildDefaultValidatorFactory() :
+-				HibernateValidatorDelegate.buildValidatorFactory());
++		this(Validation.buildDefaultValidatorFactory());
+ 	}
+ 
+ 	/**
+@@ -121,52 +118,36 @@
+ 
+ 		Class<?>[] groups = determineValidationGroups(invocation);
+ 
+-		if (forExecutablesMethod != null) {
+-			// Standard Bean Validation 1.1 API
+-			Object execVal;
+-			try {
+-				execVal = ReflectionUtils.invokeMethod(forExecutablesMethod, this.validator);
+-			}
+-			catch (AbstractMethodError err) {
+-				// Probably an adapter (maybe a lazy-init proxy) without BV 1.1 support
+-				Validator nativeValidator = this.validator.unwrap(Validator.class);
+-				execVal = ReflectionUtils.invokeMethod(forExecutablesMethod, nativeValidator);
+-				// If successful, store native Validator for further use
+-				this.validator = nativeValidator;
+-			}
+-
+-			Method methodToValidate = invocation.getMethod();
+-			Set<ConstraintViolation<?>> result;
+-
+-			try {
+-				result = (Set<ConstraintViolation<?>>) ReflectionUtils.invokeMethod(validateParametersMethod,
+-						execVal, invocation.getThis(), methodToValidate, invocation.getArguments(), groups);
+-			}
+-			catch (IllegalArgumentException ex) {
+-				// Probably a generic type mismatch between interface and impl as reported in SPR-12237 / HV-1011
+-				// Let's try to find the bridged method on the implementation class...
+-				methodToValidate = BridgeMethodResolver.findBridgedMethod(
+-						ClassUtils.getMostSpecificMethod(invocation.getMethod(), invocation.getThis().getClass()));
+-				result = (Set<ConstraintViolation<?>>) ReflectionUtils.invokeMethod(validateParametersMethod,
+-						execVal, invocation.getThis(), methodToValidate, invocation.getArguments(), groups);
+-			}
+-			if (!result.isEmpty()) {
+-				throw new ConstraintViolationException(result);
+-			}
+-
+-			Object returnValue = invocation.proceed();
+-			result = (Set<ConstraintViolation<?>>) ReflectionUtils.invokeMethod(validateReturnValueMethod,
+-					execVal, invocation.getThis(), methodToValidate, returnValue, groups);
+-			if (!result.isEmpty()) {
+-				throw new ConstraintViolationException(result);
+-			}
+-			return returnValue;
+-		}
+-
+-		else {
+-			// Hibernate Validator 4.3's native API
+-			return HibernateValidatorDelegate.invokeWithinValidation(invocation, this.validator, groups);
++		// Standard Bean Validation 1.1 API
++		Object execVal = ReflectionUtils.invokeMethod(forExecutablesMethod, this.validator);
++		Method methodToValidate = invocation.getMethod();
++		Set<ConstraintViolation<?>> result;
++
++		try {
++			result = (Set<ConstraintViolation<?>>) ReflectionUtils.invokeMethod(validateParametersMethod,
++					execVal, invocation.getThis(), methodToValidate, invocation.getArguments(), groups);
++		}
++		catch (IllegalArgumentException ex) {
++			// Probably a generic type mismatch between interface and impl as reported in SPR-12237 / HV-1011
++			// Let's try to find the bridged method on the implementation class...
++			methodToValidate = BridgeMethodResolver.findBridgedMethod(
++					ClassUtils.getMostSpecificMethod(invocation.getMethod(), invocation.getThis().getClass()));
++			result = (Set<ConstraintViolation<?>>) ReflectionUtils.invokeMethod(validateParametersMethod,
++					execVal, invocation.getThis(), methodToValidate, invocation.getArguments(), groups);
+ 		}
++		if (!result.isEmpty()) {
++			throw new ConstraintViolationException(result);
++		}
++
++		Object returnValue = invocation.proceed();
++
++		result = (Set<ConstraintViolation<?>>) ReflectionUtils.invokeMethod(validateReturnValueMethod,
++				execVal, invocation.getThis(), methodToValidate, returnValue, groups);
++		if (!result.isEmpty()) {
++			throw new ConstraintViolationException(result);
++		}
++
++		return returnValue;
+ 	}
+ 
+ 	private boolean isFactoryBeanMetadataMethod(Method method) {
+@@ -205,36 +186,4 @@
+ 		return (validatedAnn != null ? validatedAnn.value() : new Class<?>[0]);
+ 	}
+ 
+-
+-	/**
+-	 * Inner class to avoid a hard-coded Hibernate Validator 4.3 dependency.
+-	 */
+-	private static class HibernateValidatorDelegate {
+-
+-		public static ValidatorFactory buildValidatorFactory() {
+-			return Validation.byProvider(HibernateValidator.class).configure().buildValidatorFactory();
+-		}
+-
+-		@SuppressWarnings("deprecation")
+-		public static Object invokeWithinValidation(MethodInvocation invocation, Validator validator, Class<?>[] groups)
+-				throws Throwable {
+-
+-			org.hibernate.validator.method.MethodValidator methodValidator =
+-					validator.unwrap(org.hibernate.validator.method.MethodValidator.class);
+-			Set<org.hibernate.validator.method.MethodConstraintViolation<Object>> result =
+-					methodValidator.validateAllParameters(
+-							invocation.getThis(), invocation.getMethod(), invocation.getArguments(), groups);
+-			if (!result.isEmpty()) {
+-				throw new org.hibernate.validator.method.MethodConstraintViolationException(result);
+-			}
+-			Object returnValue = invocation.proceed();
+-			result = methodValidator.validateReturnValue(
+-					invocation.getThis(), invocation.getMethod(), returnValue, groups);
+-			if (!result.isEmpty()) {
+-				throw new org.hibernate.validator.method.MethodConstraintViolationException(result);
+-			}
+-			return returnValue;
+-		}
+-	}
+-
+ }
+--- a/spring-context/src/main/java/org/springframework/validation/beanvalidation/SpringValidatorAdapter.java
++++ b/spring-context/src/main/java/org/springframework/validation/beanvalidation/SpringValidatorAdapter.java
+@@ -27,6 +27,7 @@
+ import javax.validation.ConstraintViolation;
+ import javax.validation.ValidationException;
+ import javax.validation.Validator;
++import javax.validation.executable.ExecutableValidator;
+ import javax.validation.metadata.BeanDescriptor;
+ import javax.validation.metadata.ConstraintDescriptor;
+ 
+@@ -320,6 +321,11 @@
+ 		}
+ 	}
+ 
++	@Override
++	public ExecutableValidator forExecutables() {
++		return this.targetValidator.forExecutables();
++	}
++
+ 
+ 	/**
+ 	 * Wrapper for a String attribute which can be resolved via a {@code MessageSource},
diff --git a/debian/patches/series b/debian/patches/series
index c1b2e38f..cd82afb3 100644
--- a/debian/patches/series
+++ b/debian/patches/series
@@ -21,3 +21,4 @@
 0051-reproducible-build-source-date.patch
 0052-no-jasperreports.patch
 0053-ignore-reactor.patch
+0054-disable-hibernate-validator-4.patch
-- 
2.24.0

Reply via email to