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

MethodValidationPostProcessor fails for certain generic method signatures when running against Hibernate Validator 5 [SPR-12237] #22186

Closed
spring-projects-issues opened this issue Sep 22, 2014 · 12 comments
Assignees
Labels
in: core Issues in core modules (aop, beans, core, context, expression) type: bug A general bug
Milestone

Comments

@spring-projects-issues
Copy link
Collaborator

spring-projects-issues commented Sep 22, 2014

Adam McCormick opened SPR-12237 and commented

I am trying to validate service layer method arguments using the MethodValidationPostProcessor. This is failing on, what appears to be, different the method signatures.

With an interface such as:

@Validated
public interface Service<T> {

	public T create(@Valid T item);
	
	public T update(String identifier, @Valid T item);
}

And the following implementation:

@Named
public class ItemService implements Service<Item>{

	@Override
	public Item create(Item item) {
		return item;
	}

	@Override
	public Item update(String identifier, Item item) {
		return item;
	}
}

Validation works fine when calling update(String, Item) but throws the following exception when calling create(Item):

java.lang.IllegalArgumentException: HV000162: The validated type org.commons.test.validation.ItemService does not specify the constructor/method: public abstract java.lang.Object org.commons.test.validation.Service.create(java.lang.Object)

This is also working fine if Service<T> is an abstract class and I just extend it.

Attached is a sample application that demonstrates the problem.


Affects: 4.0.6

Attachments:

Referenced from: commits 7118fcf

3 votes, 8 watchers

@spring-projects-issues
Copy link
Collaborator Author

Juergen Hoeller commented

Could you post the full stacktrace please? This could be an issue with the proxy/implementation mismatch in case of a JDK proxy... Switching to proxyTargetClass=true might help. It could also be an internal problem in Hibernate Validator itself; we'll see.

Juergen

@spring-projects-issues
Copy link
Collaborator Author

Adam McCormick commented

Here's the full stack trace:

java.lang.IllegalArgumentException: HV000162: The validated type org.commons.test.validation.ItemService does not specify the constructor/method: public abstract java.lang.Object org.commons.test.validation.Service.create(java.lang.Object)
	at org.hibernate.validator.internal.engine.ValidatorImpl.validateParametersInContext(ValidatorImpl.java:883)
	at org.hibernate.validator.internal.engine.ValidatorImpl.validateParameters(ValidatorImpl.java:240)
	at org.hibernate.validator.internal.engine.ValidatorImpl.validateParameters(ValidatorImpl.java:198)
	at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
	at sun.reflect.NativeMethodAccessorImpl.invoke(Unknown Source)
	at sun.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source)
	at java.lang.reflect.Method.invoke(Unknown Source)
	at org.springframework.util.ReflectionUtils.invokeMethod(ReflectionUtils.java:209)
	at org.springframework.validation.beanvalidation.MethodValidationInterceptor.invoke(MethodValidationInterceptor.java:117)
	at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179)
	at org.springframework.aop.framework.JdkDynamicAopProxy.invoke(JdkDynamicAopProxy.java:207)
	at com.sun.proxy.$Proxy46.create(Unknown Source)
	at org.commons.test.validation.ItemController.create(ItemController.java:21)
	at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
	at sun.reflect.NativeMethodAccessorImpl.invoke(Unknown Source)
	at sun.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source)
	at java.lang.reflect.Method.invoke(Unknown Source)
	at org.springframework.web.method.support.InvocableHandlerMethod.invoke(InvocableHandlerMethod.java:215)
	at org.springframework.web.method.support.InvocableHandlerMethod.invokeForRequest(InvocableHandlerMethod.java:132)
	at org.springframework.web.servlet.mvc.method.annotation.ServletInvocableHandlerMethod.invokeAndHandle(ServletInvocableHandlerMethod.java:104)
	at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.invokeHandleMethod(RequestMappingHandlerAdapter.java:749)
	at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.handleInternal(RequestMappingHandlerAdapter.java:689)
	at org.springframework.web.servlet.mvc.method.AbstractHandlerMethodAdapter.handle(AbstractHandlerMethodAdapter.java:83)
	at org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:938)
	at org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:870)
	at org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:961)
	at org.springframework.web.servlet.FrameworkServlet.doPost(FrameworkServlet.java:863)
	at javax.servlet.http.HttpServlet.service(HttpServlet.java:646)
	at org.springframework.web.servlet.FrameworkServlet.service(FrameworkServlet.java:837)
	at javax.servlet.http.HttpServlet.service(HttpServlet.java:727)
	at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:303)
	at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:208)
	at org.springframework.web.filter.HiddenHttpMethodFilter.doFilterInternal(HiddenHttpMethodFilter.java:77)
	at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)
	at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:241)
	at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:208)
	at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:220)
	at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:122)
	at org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:501)
	at org.apache.catalina.valves.RemoteIpValve.invoke(RemoteIpValve.java:683)
	at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:171)
	at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:103)
	at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:116)
	at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:408)
	at org.apache.coyote.http11.AbstractHttp11Processor.process(AbstractHttp11Processor.java:1070)
	at org.apache.coyote.AbstractProtocol$AbstractConnectionHandler.process(AbstractProtocol.java:611)
	at org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.doRun(NioEndpoint.java:1736)
	at org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.run(NioEndpoint.java:1695)
	at java.util.concurrent.ThreadPoolExecutor.runWorker(Unknown Source)
	at java.util.concurrent.ThreadPoolExecutor$Worker.run(Unknown Source)
	at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61)
	at java.lang.Thread.run(Unknown Source)

@spring-projects-issues
Copy link
Collaborator Author

Nishant Niranjan commented

I was facing similar issue , while I used hibernate-validator-annotation-processor-5.0.1.Final.jar and
hibernate-validator-5.0.1.Final.jar with validation-api-1.1.0.Final.jar. I was using jdk1.8.0_11 version of java.
I resolved this issue by changing the version hibernate-validator-annotation-processor-5.0.1.Final.jar and
hibernate-validator-5.0.1.Final.jar with hibernate-validator-annotation-processor-5.0.0.CR2.jar and
hibernate-validator-5.0.0.CR2.jar respectively.

@spring-projects-issues
Copy link
Collaborator Author

Eugeniu Rabii commented

I hate to be mean, but it's one year since this has been opened. There are no updates? I can re-produce this one with latest spring/hibernate.

@spring-projects-issues
Copy link
Collaborator Author

Juergen Hoeller commented

This seems to be an issue in Hibernate Validator itself since different HV versions apparently exhibit different behavior here. If we can do something about it from Spring's side, in our MethodValidationPostProcessor implementation, then we'll certainly consider it. However, it seems like this is worth reporting to the HV team as well.

Juergen

@spring-projects-issues
Copy link
Collaborator Author

Eugeniu Rabii commented

I did report : https://hibernate.atlassian.net/browse/HV-1011 and they gave one of their samples as a response which I can't get to re-produce the error.

On the other had, it is easy to reproduce on spring side. Again, I don't want to be mean - but that seems like your issue.

A small example that proves this:

import java.util.Optional;

import javax.validation.constraints.NotNull;

public interface IJobApi<ID, T> {
    Optional<Job<ID, T>> findJob(@NotNull ID jobId);
}
import java.util.Optional;
import java.util.UUID;

import org.springframework.validation.annotation.Validated;

@Validated
public class JobApi implements IJobApi<UUID, EntityKey> {

    @Override
    public Optional<Job<UUID, EntityKey>> findJob(UUID jobId) {
        return null;
    }
}
@Configuration
public class MyApplicationContext {

    @Bean
    public IJobApi<UUID, EntityKey> jobApi() {
        return new JobApi();
    }

    @Bean
    public LocalValidatorFactoryBean localValidatorFactoryBean() {
        return new LocalValidatorFactoryBean();
    }

    @Bean
    public MethodValidationPostProcessor methodValidationPostProcessor() {
        return new MethodValidationPostProcessor();
    }
}
public class EntityKey {}
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(MyApplicationContext.class);
        IJobApi<UUID, EntityKey> bean = context.getBean(IJobApi.class);
        bean.findJob(null);
        context.close();

The root cause of the problem seems to be here:

at org.springframework.util.ReflectionUtils.invokeMethod(ReflectionUtils.java:211)
	at org.springframework.validation.beanvalidation.MethodValidationInterceptor.invoke(MethodValidationInterceptor.java:117)

I am not that versatile to understand what is going on actually; but seems like some information is lost/un-matched because of some proxing.

@spring-projects-issues
Copy link
Collaborator Author

Juergen Hoeller commented

The problem with the test case reported to HV is that it was obtaining the concrete declared method on the implementation class; this will obviously work. However, when coming in through a generic bridge method on the interface (which may happen with an interface-based proxy), HV seems to be stuck. Anyway, we'll try to resolve the bridge method on Spring's side...

Juergen

@spring-projects-issues
Copy link
Collaborator Author

Juergen Hoeller commented

As far as I was able to reproduce this, it only happens against Hibernate Validator 5 (more specifically, 5.2.1, using the standard BV 1.1 API for method validation) but not against Hibernate Validator 4 (i.e. 4.3.1, using HV's native method validation API).

It can be enforced by performing the method call on a raw declaration of the service interface, calling the T (Object) variant of the method and therefore going through the generated bridge method on the implementation class.

Juergen

@spring-projects-issues
Copy link
Collaborator Author

Juergen Hoeller commented

In other words, the test case in HV-1011 should fail once the method is retrieved like this:

JobApi.class.getDeclaredMethod( "findJob", Object.class );

@spring-projects-issues
Copy link
Collaborator Author

Juergen Hoeller commented

I'm afraid there is no clean way to resolve this on Spring's side if the validation metadata lives in a generic interface. We have to pass a variant of that interface method into the validation engine, otherwise that engine may not find the validation metadata. We are more flexible there on Java 8 where annotations are being preserved across bridge methods, while it's almost bound to fail on Java 6 or 7 in some scenarios, also depending on the BV provider etc.

In the end, Hibernate Validator should accept a given bridge method in such an interface scenario, as outlined above. For the time being, I've put a workaround in place where, in case of the regular attempt failing, we resolve the bridge method on the implementation class and call the validation engine once more with this resolved method. We cannot do this by default since it's quite a guess across Bean Validation providers and JVMs, but since it does make those tests pass with Hibernate Validator 5.2.1 on Java 8, it's worth that workaround at least.

So in summary, from my perspective, HV-1011 should get addressed on Hibernate Validator's side, but we do have a workaround in place as of Spring 4.2.1 which hopefully addresses your case as well. Please give the upcoming 4.2.1.BUILD-SNAPSHOT a try and let me know...

Juergen

@spring-projects-issues
Copy link
Collaborator Author

Eugeniu Rabii commented

@Juergen thank you for the fix (haven't tested it just yet), one minor note is that the guys over at hibernate fixed this one in their own repo - so you might actually revert whatever you did in Spring - not sure... Here is the link:

hibernate/hibernate-validator#425

@spring-projects-issues
Copy link
Collaborator Author

Juergen Hoeller commented

Thanks for the heads-up, Eugeniu Rabii. The workaround doesn't hurt since it's just a fallback for a case that otherwise would fail immediately... So I guess I'll leave it in place for the time being, not least of it all for working with older HV 5.x versions (and 5.2.2 isn't out yet anyway).

Juergen

@spring-projects-issues spring-projects-issues added type: bug A general bug in: core Issues in core modules (aop, beans, core, context, expression) labels Jan 11, 2019
@spring-projects-issues spring-projects-issues added this to the 4.2.1 milestone Jan 11, 2019
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
in: core Issues in core modules (aop, beans, core, context, expression) type: bug A general bug
Projects
None yet
Development

No branches or pull requests

2 participants