Uploaded image for project: 'Spring Framework'
  1. Spring Framework
  2. SPR-15856

Unable to use Hibernate Validator 4.3.2 if Bean Validation API 1.1 is on the classpath

    Details

    • Type: Bug
    • Status: Closed
    • Priority: Minor
    • Resolution: Complete
    • Affects Version/s: 4.3.10
    • Fix Version/s: 4.3.11
    • Component/s: Core
    • Labels:
    • Last commented by a User:
      false

      Description

      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.spring[email protected]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;
      	}
      }
      

        Issue Links

          Activity

          Hide
          juergen.hoeller Juergen Hoeller added a comment - - edited

          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...

          Show
          juergen.hoeller Juergen Hoeller added a comment - - edited 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...
          Hide
          ibrahim.mg Ibrahim Ghazal added a comment -

          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.

          Show
          ibrahim.mg Ibrahim Ghazal added a comment - 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.
          Hide
          juergen.hoeller Juergen Hoeller added a comment -

          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.

          Show
          juergen.hoeller Juergen Hoeller added a comment - 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.

            People

            • Assignee:
              juergen.hoeller Juergen Hoeller
              Reporter:
              ibrahim.mg Ibrahim Ghazal
              Last updater:
              St├ęphane Nicoll
            • Votes:
              0 Vote for this issue
              Watchers:
              2 Start watching this issue

              Dates

              • Created:
                Updated:
                Resolved:
                Days since last comment:
                6 weeks, 3 days ago