Discussion:
Bean Validation Oddity
Robert DiFalco
2014-03-18 19:23:34 UTC
Permalink
I have an issue with Bean Validation and curious if you agree before I
create an issue.

If I have the following method:

@DELETE
public void delete(
@NotNull
@QueryParam( "emailAddress" )
String emailAddress,
@NotNull
@QueryParam( "password" )
String password ) throws NotFoundException {
userService.delete( emailAddress, password );
}

And if the client sends a null for the first query parameter, the resulting
exception maps to "arg1". Pretty much every where else I use Bean
Validation it is smart enough to use the name of the argument as
"emailAddress".

Is there any way to rectify this? Kind of a drag since even if I parse the
CVE into a nice JSON entity for the 400 response it will say "arg1" instead
of "emailAddress".

Thanks!
Ted M. Young [@jitterted]
2014-03-18 20:12:51 UTC
Permalink
On Tue, Mar 18, 2014 at 12:23 PM, Robert DiFalco
Pretty much every where else I use Bean Validation it is smart enough to
use the name of the argument as "emailAddress".
This is because Java stores (in the class files) the name of fields (the
likely "every where else" to which you refer), but parameters' names are
not saved (though in Java 8 this is possible[1]). That's why the
@QueryParam, and other parameter annotations, require a String so that they
can have a name. The arg1 comes from the standard implementation of bean
validation[2].

An alternative would be to put the email/password into, say, a User object,
which itself would have the validation annotations on it, and that would
provide you with names. Other than that, I'm not sure what else you can do
other than do the null-checking "manually" in your delete method.

[1]
http://docs.oracle.com/javase/tutorial/reflect/member/methodparameterreflection.html
[2] http://beanvalidation.org/proposals/BVAL-241/#naming


;ted
--
http://about.me/tedmyoung
Robert DiFalco
2014-03-19 00:49:33 UTC
Permalink
I wonder if it is possible to have a special implementation of the bean
validation for Jersey that would take the names from QueryParams or
PathParams when they are available. I'll dig around a bit when I get past
my current deadlines.
Post by Ted M. Young [@jitterted]
Pretty much every where else I use Bean Validation it is smart enough to
use the name of the argument as "emailAddress".
This is because Java stores (in the class files) the name of fields (the
likely "every where else" to which you refer), but parameters' names are
not saved (though in Java 8 this is possible[1]). That's why the
@QueryParam, and other parameter annotations, require a String so that they
can have a name. The arg1 comes from the standard implementation of bean
validation[2].
An alternative would be to put the email/password into, say, a User
object, which itself would have the validation annotations on it, and that
would provide you with names. Other than that, I'm not sure what else you
can do other than do the null-checking "manually" in your delete method.
[1]
http://docs.oracle.com/javase/tutorial/reflect/member/methodparameterreflection.html
[2] http://beanvalidation.org/proposals/BVAL-241/#naming
;ted
--
http://about.me/tedmyoung
Ted M. Young [@jitterted]
2014-03-19 03:06:28 UTC
Permalink
I think you'd probably need to use new annotations, since the bean
validation ones don't take the necessary parameter. You might be able to
use custom validation constraints, but I haven't tried that in the context
of JAX-RS.

;ted
Post by Robert DiFalco
I wonder if it is possible to have a special implementation of the bean
validation for Jersey that would take the names from QueryParams or
PathParams when they are available. I'll dig around a bit when I get past
my current deadlines.
On Tue, Mar 18, 2014 at 12:23 PM, Robert DiFalco <
Pretty much every where else I use Bean Validation it is smart enough to
use the name of the argument as "emailAddress".
This is because Java stores (in the class files) the name of fields (the
likely "every where else" to which you refer), but parameters' names are
not saved (though in Java 8 this is possible[1]). That's why the
@QueryParam, and other parameter annotations, require a String so that they
can have a name. The arg1 comes from the standard implementation of bean
validation[2].
An alternative would be to put the email/password into, say, a User
object, which itself would have the validation annotations on it, and that
would provide you with names. Other than that, I'm not sure what else you
can do other than do the null-checking "manually" in your delete method.
[1]
http://docs.oracle.com/javase/tutorial/reflect/member/methodparameterreflection.html
[2] http://beanvalidation.org/proposals/BVAL-241/#naming
;ted
--
http://about.me/tedmyoung
Robert DiFalco
2014-03-19 22:41:42 UTC
Permalink
Alright! Try this out to get validation errors that use the property path
of the QueryParam or PathParam name! How could you not think this is cool?
Just #register this in your ResourceConfig.

public class ValidationConfigurationContextResolver implements
ContextResolver<ValidationConfig> {
@Override
public ValidationConfig getContext( final Class<?> type ) {
final ValidationConfig config = new ValidationConfig();
config.parameterNameProvider( new
RestAnnotationParameterNameProvider() );
return config;
}

static class RestAnnotationParameterNameProvider extends
DefaultParameterNameProvider {

@Override
public List<String> getParameterNames( Method method ) {
try {
Annotation[][] annotationsByParam =
method.getParameterAnnotations();

List<String> names = new ArrayList<>(
annotationsByParam.length );
for ( Annotation[] annotations : annotationsByParam ) {
String name = getParamName( annotations );
if ( name == null )
name = "arg" + ( names.size() + 1 );

names.add( name );
}

return names;
}
catch ( Exception e ) {
e.printStackTrace();
return super.getParameterNames( method );
}
}

private static String getParamName( Annotation[] annotations ) {
for ( Annotation annotation : annotations ) {
if ( annotation.annotationType() == QueryParam.class ) {
return QueryParam.class.cast( annotation ).value();
}
else if ( annotation.annotationType() == PathParam.class ) {
return PathParam.class.cast( annotation ).value();
}
}

return null;
}
}
}
Post by Ted M. Young [@jitterted]
I think you'd probably need to use new annotations, since the bean
validation ones don't take the necessary parameter. You might be able to
use custom validation constraints, but I haven't tried that in the context
of JAX-RS.
;ted
Post by Robert DiFalco
I wonder if it is possible to have a special implementation of the bean
validation for Jersey that would take the names from QueryParams or
PathParams when they are available. I'll dig around a bit when I get past
my current deadlines.
On Tue, Mar 18, 2014 at 12:23 PM, Robert DiFalco <
Pretty much every where else I use Bean Validation it is smart enough
to use the name of the argument as "emailAddress".
This is because Java stores (in the class files) the name of fields (the
likely "every where else" to which you refer), but parameters' names are
not saved (though in Java 8 this is possible[1]). That's why the
@QueryParam, and other parameter annotations, require a String so that they
can have a name. The arg1 comes from the standard implementation of bean
validation[2].
An alternative would be to put the email/password into, say, a User
object, which itself would have the validation annotations on it, and that
would provide you with names. Other than that, I'm not sure what else you
can do other than do the null-checking "manually" in your delete method.
[1]
http://docs.oracle.com/javase/tutorial/reflect/member/methodparameterreflection.html
[2] http://beanvalidation.org/proposals/BVAL-241/#naming
;ted
--
http://about.me/tedmyoung
Ted M. Young [@jitterted]
2014-03-21 22:40:05 UTC
Permalink
That looks like a great pull-request candidate for Jersey...

;ted
Post by Robert DiFalco
Alright! Try this out to get validation errors that use the property path
of the QueryParam or PathParam name! How could you not think this is cool?
Just #register this in your ResourceConfig.
public class ValidationConfigurationContextResolver implements
ContextResolver<ValidationConfig> {
@Override
public ValidationConfig getContext( final Class<?> type ) {
final ValidationConfig config = new ValidationConfig();
config.parameterNameProvider( new
RestAnnotationParameterNameProvider() );
return config;
}
static class RestAnnotationParameterNameProvider extends
DefaultParameterNameProvider {
@Override
public List<String> getParameterNames( Method method ) {
try {
Annotation[][] annotationsByParam =
method.getParameterAnnotations();
List<String> names = new ArrayList<>(
annotationsByParam.length );
for ( Annotation[] annotations : annotationsByParam ) {
String name = getParamName( annotations );
if ( name == null )
name = "arg" + ( names.size() + 1 );
names.add( name );
}
return names;
}
catch ( Exception e ) {
e.printStackTrace();
return super.getParameterNames( method );
}
}
private static String getParamName( Annotation[] annotations ) {
for ( Annotation annotation : annotations ) {
if ( annotation.annotationType() == QueryParam.class ) {
return QueryParam.class.cast( annotation ).value();
}
else if ( annotation.annotationType() == PathParam.class ) {
return PathParam.class.cast( annotation ).value();
}
}
return null;
}
}
}
Post by Ted M. Young [@jitterted]
I think you'd probably need to use new annotations, since the bean
validation ones don't take the necessary parameter. You might be able to
use custom validation constraints, but I haven't tried that in the context
of JAX-RS.
;ted
Post by Robert DiFalco
I wonder if it is possible to have a special implementation of the bean
validation for Jersey that would take the names from QueryParams or
PathParams when they are available. I'll dig around a bit when I get past
my current deadlines.
On Tue, Mar 18, 2014 at 12:23 PM, Robert DiFalco <
Pretty much every where else I use Bean Validation it is smart enough
to use the name of the argument as "emailAddress".
This is because Java stores (in the class files) the name of fields
(the likely "every where else" to which you refer), but parameters' names
are not saved (though in Java 8 this is possible[1]). That's why the
@QueryParam, and other parameter annotations, require a String so that they
can have a name. The arg1 comes from the standard implementation of bean
validation[2].
An alternative would be to put the email/password into, say, a User
object, which itself would have the validation annotations on it, and that
would provide you with names. Other than that, I'm not sure what else you
can do other than do the null-checking "manually" in your delete method.
[1]
http://docs.oracle.com/javase/tutorial/reflect/member/methodparameterreflection.html
[2] http://beanvalidation.org/proposals/BVAL-241/#naming
;ted
--
http://about.me/tedmyoung
Trolly Rogers
2014-03-22 01:05:46 UTC
Permalink
Yeah, I agree. Nice work Robert!
Post by Ted M. Young [@jitterted]
That looks like a great pull-request candidate for Jersey...
;ted
Post by Robert DiFalco
Alright! Try this out to get validation errors that use the property path
of the QueryParam or PathParam name! How could you not think this is cool?
Just #register this in your ResourceConfig.
public class ValidationConfigurationContextResolver implements
ContextResolver<ValidationConfig> {
@Override
public ValidationConfig getContext( final Class<?> type ) {
final ValidationConfig config = new ValidationConfig();
config.parameterNameProvider( new
RestAnnotationParameterNameProvider() );
return config;
}
static class RestAnnotationParameterNameProvider extends
DefaultParameterNameProvider {
@Override
public List<String> getParameterNames( Method method ) {
try {
Annotation[][] annotationsByParam =
method.getParameterAnnotations();
List<String> names = new ArrayList<>(
annotationsByParam.length );
for ( Annotation[] annotations : annotationsByParam ) {
String name = getParamName( annotations );
if ( name == null )
name = "arg" + ( names.size() + 1 );
names.add( name );
}
return names;
}
catch ( Exception e ) {
e.printStackTrace();
return super.getParameterNames( method );
}
}
private static String getParamName( Annotation[] annotations ) {
for ( Annotation annotation : annotations ) {
if ( annotation.annotationType() == QueryParam.class ) {
return QueryParam.class.cast( annotation ).value();
}
else if ( annotation.annotationType() == PathParam.class ) {
return PathParam.class.cast( annotation ).value();
}
}
return null;
}
}
}
Post by Ted M. Young [@jitterted]
I think you'd probably need to use new annotations, since the bean
validation ones don't take the necessary parameter. You might be able to
use custom validation constraints, but I haven't tried that in the context
of JAX-RS.
;ted
On Tue, Mar 18, 2014 at 5:49 PM, Robert DiFalco <
Post by Robert DiFalco
I wonder if it is possible to have a special implementation of the bean
validation for Jersey that would take the names from QueryParams or
PathParams when they are available. I'll dig around a bit when I get past
my current deadlines.
On Tue, Mar 18, 2014 at 12:23 PM, Robert DiFalco <
Pretty much every where else I use Bean Validation it is smart enough
to use the name of the argument as "emailAddress".
This is because Java stores (in the class files) the name of fields
(the likely "every where else" to which you refer), but parameters' names
are not saved (though in Java 8 this is possible[1]). That's why the
@QueryParam, and other parameter annotations, require a String so that they
can have a name. The arg1 comes from the standard implementation of bean
validation[2].
An alternative would be to put the email/password into, say, a User
object, which itself would have the validation annotations on it, and that
would provide you with names. Other than that, I'm not sure what else you
can do other than do the null-checking "manually" in your delete method.
[1]
http://docs.oracle.com/javase/tutorial/reflect/member/methodparameterreflection.html
[2] http://beanvalidation.org/proposals/BVAL-241/#naming
;ted
--
http://about.me/tedmyoung
Loading...