Skip to content
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

Unable to use Hibernate Validator 4.3.2 if Bean Validation API 1.1 is on the classpath [SPR-15856] #20411

Closed
spring-issuemaster opened this Issue Aug 9, 2017 · 3 comments

Comments

Projects
None yet
2 participants
@spring-issuemaster
Copy link
Collaborator

commented Aug 9, 2017

Ibrahim Ghazal opened SPR-15856 and commented

Trying to create a LocalValidatorFactoryBean bean while both Hibernate Validator 4.3.2 and Bean Validation 1.1 are on the classpath throws the below error:

Aug 09, 2017 1:11:57 PM org.springframework.context.annotation.AnnotationConfigApplicationContext prepareRefresh
INFO: Refreshing org.springframework.context.annotation.AnnotationConfigApplicationContext@5197848c: startup date [Wed Aug 09 13:11:57 AST 2017]; root of context hierarchy
Aug 09, 2017 1:11:57 PM org.hibernate.validator.internal.util.Version <clinit>
INFO: HV000001: Hibernate Validator 4.3.2.Final
Aug 09, 2017 1:11:57 PM org.springframework.context.annotation.AnnotationConfigApplicationContext refresh
WARNING: Exception encountered during context initialization - cancelling refresh attempt: org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'localValidatorFactoryBean' defined in demo.Demo: Invocation of init method failed; nested exception is java.lang.AbstractMethodError: org.hibernate.validator.internal.engine.ConfigurationImpl.getDefaultParameterNameProvider()Ljavax/validation/ParameterNameProvider;
Exception in thread "main" org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'localValidatorFactoryBean' defined in demo.Demo: Invocation of init method failed; nested exception is java.lang.AbstractMethodError: org.hibernate.validator.internal.engine.ConfigurationImpl.getDefaultParameterNameProvider()Ljavax/validation/ParameterNameProvider;
	at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.initializeBean(AbstractAutowireCapableBeanFactory.java:1628)
	at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:555)
	at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:483)
	at org.springframework.beans.factory.support.AbstractBeanFactory$1.getObject(AbstractBeanFactory.java:306)
	at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:230)
	at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:302)
	at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:197)
	at org.springframework.beans.factory.support.DefaultListableBeanFactory.preInstantiateSingletons(DefaultListableBeanFactory.java:761)
	at org.springframework.context.support.AbstractApplicationContext.finishBeanFactoryInitialization(AbstractApplicationContext.java:867)
	at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:543)
	at org.springframework.context.annotation.AnnotationConfigApplicationContext.<init>(AnnotationConfigApplicationContext.java:84)
	at demo.Demo.main(Demo.java:11)
Caused by: java.lang.AbstractMethodError: org.hibernate.validator.internal.engine.ConfigurationImpl.getDefaultParameterNameProvider()Ljavax/validation/ParameterNameProvider;
	at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
	at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
	at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
	at java.lang.reflect.Method.invoke(Method.java:498)
	at org.springframework.util.ReflectionUtils.invokeMethod(ReflectionUtils.java:216)
	at org.springframework.util.ReflectionUtils.invokeMethod(ReflectionUtils.java:201)
	at org.springframework.validation.beanvalidation.LocalValidatorFactoryBean.configureParameterNameProviderIfPossible(LocalValidatorFactoryBean.java:315)
	at org.springframework.validation.beanvalidation.LocalValidatorFactoryBean.afterPropertiesSet(LocalValidatorFactoryBean.java:284)
	at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.invokeInitMethods(AbstractAutowireCapableBeanFactory.java:1687)
	at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.initializeBean(AbstractAutowireCapableBeanFactory.java:1624)
	... 11 more

The issue is that LocalValidatorFactoryBean tries to detect Bean Validation 1.1 using the API interfaces, regardless of what the provider actually implements (See here and here). And since Class.getMethod succeeds, ReflectionUtils.invokeMethod throws an AbstractMethodError, which is an Error and not an Exception, so it doesn't get caught by the catch here.

This could be fixed by explicitly catching AbstractMethodError in addition to Exception. But I think the correct fix is to call getMethod on the provider class instead of the API interfaces.

(The reason I can't simply remove Bean Validation 1.1 from the classpath is that Spring Boot includes it even if <hibernate-validator.version>4.3.2.Final</hibernate-validator.version> is set in pom.xml. We also faced the same issue when deploying to a Java EE container that implements Bean Validation 1.1.)

This is only an issue for Spring 4.x. Spring 5 will make Bean Validation 1.1 mandatory.

To reproduce:
pom.xml

<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
	<modelVersion>4.0.0</modelVersion>
	<groupId>com.example</groupId>
	<artifactId>demo</artifactId>
	<version>0.0.1-SNAPSHOT</version>

	<properties>
		<maven.compiler.source>1.8</maven.compiler.source>
		<maven.compiler.target>1.8</maven.compiler.target>
	</properties>

	<dependencies>
		<dependency>
			<groupId>org.springframework</groupId>
			<artifactId>spring-context</artifactId>
			<version>4.3.10.RELEASE</version>
		</dependency>
		<dependency>
			<groupId>javax.validation</groupId>
			<artifactId>validation-api</artifactId>
			<version>1.1.0.Final</version>
		</dependency>
		<dependency>
			<groupId>org.hibernate</groupId>
			<artifactId>hibernate-validator</artifactId>
			<version>4.3.2.Final</version>
		</dependency>
	</dependencies>
</project>

src/main/java/demo/Demo.java

package demo;

import javax.validation.Validator;

import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.validation.beanvalidation.LocalValidatorFactoryBean;

public class Demo {
	public static void main(String[] args) {
		AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(Demo.class);
		Validator validator = context.getBean(Validator.class);
		System.out.println(validator);

		context.close();
	}

	@Bean
	public LocalValidatorFactoryBean localValidatorFactoryBean() {
		LocalValidatorFactoryBean localValidatorFactoryBean = new LocalValidatorFactoryBean();

		return localValidatorFactoryBean;
	}
}

Affects: 4.3.10

Issue Links:

  • #20362 Follow-up: AbstractMethodError when calling validated method of MethodValidationPostProcessor is using a @Lazy validator
  • #20470 Error on type argument constraint validation failure

Referenced from: commits 0d0399a

@spring-issuemaster

This comment has been minimized.

Copy link
Collaborator Author

commented Aug 9, 2017

Juergen Hoeller commented

We are catching AbstractMethodError in other places for support in such a scenario, so I wouldn't mind doing it here as well. Checking for the existence of a method on a provider class may cause issues in a restricted environment: potentially even with a JVM security manager but in particular with OSGi and Jigsaw.

Just wondering: Why are you still using Hibernate Validator 4.3.2? Any specific reason why you're not simply upgrading to a Bean Validation 1.1 provider at this point? We should be able to address this in Spring Framework 4.3.11 in any case but just out of leniency, not because of this being a first-class scenario...

@spring-issuemaster

This comment has been minimized.

Copy link
Collaborator Author

commented Aug 9, 2017

Ibrahim Ghazal commented

We are upgrading, which is how I found this bug. I ran an application (currently running on WebSphere 8 with Java EE 6) on WebSphere Liberty with javaee-7.0 enabled and got this exception. I also found a pom.xml dependencyManagement hack in the project to force inclusion of BV 1.0 and make HV 4.3.2 run with Spring Boot in non-JEE environments.

We really only need this fixed to smooth our transition. Having the exact same WAR run on both the old and new environments makes things easier. Otherwise we'd need one build with BV1.1/HV5 and another build with BV1.0/HV4.

@spring-issuemaster

This comment has been minimized.

Copy link
Collaborator Author

commented Aug 9, 2017

Juergen Hoeller commented

Alright, I see. There's nothing wrong with allowing such a smoother upgrade path through a lenient fallback in 4.3.11.

I assume the problem would also disappear if you bundled the Bean Validation API within your WAR and deployed to WebSphere in parent-last class loader mode? With the API being picked up locally next to Hibernate Validator itself, there wouldn't be a mismatch to begin with.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
You can’t perform that action at this time.