Generic method is not validated
Description
Environment
Attachments
- 21 Mar 2015, 02:57 PM
Activity
Gunnar MorlingApril 23, 2015 at 7:52 AM
Hey; thanks for that thorough analysis!
There may be a solution which doesn't depend on the specifics of proxies. The problem to me appears to be that in ExecutableMetadata
we do not make sure that any (generic) parameter types are actually the ones of the specific bean "owning" that executable meta-data, but it could be the parameter types from an overridden super-type method. This depends on the order in which the executables from the hierarchy are added to the meta-data builder: we keep the parameter types from the first executable added.
So it may happen that the ExecutableMetadata
for StringImpl_WeldProxy#genericArg()
wrongly has the parameter types from GenericInterface#genericArg()
(if that's the first type visited). Then the meta-data look-up using the parameter types of the actual method would fail (we'd look up using "genericArg[String]", whereas the meta-data is stored under "genericArg[Object]").
The solution would be to make sure we always keep the parameter types from the most-specific executable. I have pushed a fix for this here. That way the meta-data is correct and it shouldn't matter whether the validated bean is a proxy or not.
Apart from that it's another question whether the proxy class should be presented as "root bean class". It's not nice, but then that's what actually is happening. So I'm inclined to leave that part as is.
Hardy FerentschikApril 22, 2015 at 7:37 PM
Ok, here comes the second part of the puzzle. The first part is the problem in ValidationExtension
. Here the problem is that the original method must be used in determineConstrainedMethod
when passed to isNonGetterConstrained
, even if the original method was overwritten. This is necessary to ensure that the metdatata look-up via BeanDescriptor#getConstraintsForMethod
will work. This is indeed convoluted in itself. If, however, the original method is used for the metadata lookup, the right interceptor bindings get created.
Now the second problem is ValidationInterceptor
. Note, that the ValidatorFactory
and hence the metadata cache is not shared. ValidationExtension
bootstraps its on Validator
just in order to determine which classes need interceptor bindings. Once ValidationInterceptor#validateMethodInvocation
is called we are dealing with a different ValidatorFactory
which is now the mananged CDI bean created by ValidationExtension
. So initially the metadata cache is all empty and need to be re-created. This is of course not a problem per se. The problem is that the invocation instance provided by InvocationContext.getTarget
is a Weld proxy. Later on in ValidationContext#forValidateParameters
this proxy is used to set the rootBean
and rootBeanClass
. So the metadata created is really for the proxy class, not for StringImpl
. This seems to work for most cases, but not for the generic use-case as in the test case. My guess is that the override checks with classmate fail in this case and hence the metadata for the proxy is different from the metadata of StringImpl
itself (in fact the metadata of the proxy does not contain metadata for genericArg(String)
.
Really, one should generate the metadata for the proxied class, not for the proxy itself (this would also reduce a lot of unnecessary metadata creation and storage). Adding a check for a Weld proxy to ValidationContext#forValidateParameters
which will set the rootBeanClass
to the proxied class will make the test pass. The rub here is that this check is Weld specific. It would not work for any other proxied classes. It also needs to be checked in the engine module which cannot have a hard reference to CDI/Weld, so all proxy detection and unwrapping needs to happen via reflection.
The question of dealing with proxies has come up before (I think in the context of cascaded validation and dealing with ORM association proxies). To offer the possibility to support other types of proxies one would need a customizable proxy detection contract which would be used whenever a class needs to inferred from an instance to be validated.
Thoughts?
BTW, we already have some Weld proxy specific code when it comes to dealing with class hierarchies. Maybe in a first step we make it work for Weld proxies and extend this to a generic contract later!?
Hardy FerentschikApril 22, 2015 at 3:12 PM
I would say we need to do the meta-data look-up using the original method, i.e. the one prior to replacing it with the super-type method. It's all a bit convoluted...
Right, that's what I thought and did initially. This "fixes" the lookup in ValidationExtension
, but then things fall apart a bit later in the ValidationInterceptor
:
java.lang.IllegalArgumentException: HV000162: The validated type org.hibernate.validator.integration.wildfly.generictype.StringImpl$Proxy$_$$_WeldSubclass does not specify the constructor/method: public void org.hibernate.validator.integration.wildfly.generictype.StringImpl.genericArg(java.lang.String)
And that's where things get even more convoluted. The metadata has an element for genericArg(java.lang.Object)
, but not for genericArg(java.lang.String)
. Not sure yet what's happening here.
Gunnar MorlingApril 22, 2015 at 2:33 PM
@Hardy Ferentschik, took a quick look, I would say we need to do the meta-data look-up using the original method, i.e. the one prior to replacing it with the super-type method. It's all a bit convoluted...
Hardy FerentschikMarch 23, 2015 at 11:33 AM
Thanks @Manuel Amoabeng for the great test case. I was able to almost directly plug it into the Validator test suite and you are right, it should work. There is indeed a bug which lies in the way how we look up meta-data in this case. We will take a closer look at the issue and how to resolve it asap.
Details
Assignee
Hardy FerentschikHardy FerentschikReporter
Manuel AmoabengManuel AmoabengBug Testcase Reminder (view)
Bug reports should generally be accompanied by a test case!
Bug Testcase Reminder (edit)
Bug reports should generally be accompanied by a test case!
Participants
Gunnar MorlingHardy FerentschikManuel AmoabengFix versions
Priority
Major
Details
Details
Assignee
Reporter
Bug Testcase Reminder (view)
Bug reports should generally be accompanied by a test case!
Bug Testcase Reminder (edit)
Bug reports should generally be accompanied by a test case!
I am not sure if this should work - at least I would expect it to:
Method validation is not triggered for a method declared in a bean's interface hierarchy with a generic argument annotated
@NotNull
.The attached test case demonstrates the issue (requires maven and a local wildfly instance).