I am trying to add validation to my model objects (which double as my form backing beans) using javax.validation annotations.
My model looks like this:
public Class TestObject {
private String myProp;
private InnerObject innerObject;
//getters and setters omitted
}
public Class InnerObject {
private BigDecimal myValue;
#NotNull
public BigDecimal getMyValue();
}
In my controller I have the method call like this:
public View calculate(#ModelAttribute("testObject") #Valid TestObject testObject, BindingResult result)
I also have the <mvc:annotation-driven/> in my spring-servlet.xml file.
Everytime I run the form with a null value it tells me there are 0 binding result errors.
I am using Java 1.6 with Hibernate-Validator-4.2.0 and Validation-API-1.0.0 on my classpath.
Can anyone help me and let me know what I am doing wrong? Been playing around with this for a while and can't get it to work.
Thanks
You are not referencing your InnerObject class. Your controller is taking a TestObject, but the field "innerObject" of the class TestObject is of type String.
Ok, getting a little further with this. I noticed that I was still using a custom validator in my controller like this:
binder.setValidator(new CustomValidator());
So I removed it and then I added #Valid above the getter on my inner object like:
#Valid
public InnerObject getInnerObject();
Now I can see that there are binding errors in my code. I have a new problem though. I have #NotNull on a BigDecimal property and I get the following error
org.springframework.web.util.NestedServletException: Request processing failed; nested exception is javax.validation.UnexpectedTypeException: No validator could be found for type: java.math.BigDecimal
From the documentation it looks like BigDecimal is supported. So not sure what is going on. Think I am getting closer though.
Ok to resolve the BigDecimal error I changed:
private BigDecimal myField;
to
private BigDecimal myFeild = BigDecimal.Zero;
It works properly now.
The only downside is that since these are my form backing objects a zero is displayed in the field initially instead of a blank.
I am not sure if there is any way around this?
Related
I am developing a service in Java and with spring Framework, that uses javax.validation annotations in the methods, like this example:
public void method(
#NotNull #Positive Integer val1,
#NotNull #PastOrPresent Date startDate,
#NotNull #PastOrPresent Date endDate);
When I write the annotation #Validated in the class, it work fine, however I have some problems with Circular dependencies in some classes StackOverFlow and the annotation #Validated produces UnsatisfiedDependencyException errors. (I will not be able to change to #Lazy, but in spite of I have to validate with javax.validation annotation).
Therefor, I want to validate the method programmatic, however I have not found the way to do it for methods yet, I Only was able to from class (when I use Dto class), with the method validate of Validated class.
Does someone can tell me how to do it.
Regards.
Updated 11/09/2019:
I have found a way to do it from the follows method:
private <T> void validate(T currentObject, Method currentMethod, Object... parameterValues) {
Set<ConstraintViolation<T>> errors=null;
ExecutableValidator executableValidator = validator.forExecutables();
errors = executableValidator.validateParameters(currentObject, currentMethod, parameterValues);
System.out.println((errors != null && !errors.isEmpty())?"There are exceptions":"There are not exceptions");
}
If someone can do it with reflections and easiest, I will thanks you.
when you use the method, you can insert in the first and second argument: this and (new Object(){}).getClass().getEnclosingMethod(), respectively.
I have the following situation
public class MyCustomForm {
private MyCustomType a;
private MyCustomType b;
}
#RestController
public class AController {
#RequestMapping(...)
public void myMethod(#RequestBody MyCustomForm form){
...
}
}
I want to send in a POST request the necessary data to fill the form. The problem is that MyCustomType is a complex data type and cannot be deserialized from JSON.
The first thing I tried was to write a PropertyEditor so that Spring will know how the make the deserialization from a string. This solution works if I use anything else beside #RequestBody (it works with #PathVariable for example).
I made some research and the reason why #RequestBody is not working is because this annotation generates a proxy which uses its own deserialization rules. Those rules do not interfere with custom PropertyEditors.
The next thing I tried was to use a custom Converter. This solution still didn't solved the issue.
Any other ideas?
I understood that the newest version of jackson (version 2) will know about the custom Converters or PropertyEditors but updating my jackson mapper is not really a solution in my case.
You can use #JsonDeserialize for your MyCustomType classes like
public class MyCustomForm {
#JsonDeserialize(using = MyCustomTypeDeserializer.class)
private MyCustomType a;
#JsonDeserialize(using = MyCustomTypeDeserializer.class)
private MyCustomType b;
}
Some references:
https://fasterxml.github.io/jackson-databind/javadoc/2.3.0/com/fasterxml/jackson/databind/annotation/JsonDeserialize.html
http://www.davismol.net/2015/06/05/jackson-using-jsonserialize-or-jsondeserialize-annotation-to-register-a-custom-serializer-or-deserializer/
http://www.baeldung.com/jackson-custom-serialization
I'm using the latest snapshot spring-data-rest-webmvc:2.0.0-SNAPSHOT.
When Jackson tries to serialize PersistentEntityResource to JSON, it goes into an infinite loop on the "persistentEntity" property. I understand the property is supposed to be ignored, but it's not.
public class PersistentEntityResource<T> extends Resource<T> {
#JsonIgnore private final PersistentEntity<?, ?> entity;
...
public PersistentEntity<?, ?> getPersistentEntity() {
return entity;
}
}
Maybe Jackson sees the getter that doesn't match the name of the field and decides to serialize? Or do I have some configuration wrong?
This seems to be an error reported here:
https://jira.springsource.org/browse/DATAREST-117?page=com.atlassian.jira.plugin.system.issuetabpanels:all-tabpanel
I solved the problem by making sure that I don't have another bean in the spring context called "objectMapper". It was a default Jackson ObjectMapper that was overwriting the custom one configured in RepositoryRestMvcConfiguration.
I'm using spring's PreAuthorize annotation as follows:
#PreAuthorize("hasRole('role')");
However, I already have 'role' defined as a static String on another class. If I try to use this value:
#PreAuthorize("hasRole(OtherClass.ROLE)");
I get an error:
org.springframework.expression.spel.SpelEvaluationException: EL1008E:(pos 14): Field or property 'OtherClass' cannot be found on object of type 'org.springframework.security.access.expression.method.MethodSecurityExpressionRoot'
Is there a way to access static variables like this with a PreAuthorize annotation?
Try the following which uses Spring Expression Language to evaluate the type:
#PreAuthorize("hasRole(T(fully.qualified.OtherClass).ROLE)");
Be sure to specify the fully qualified class name.
Documentation
You can also create a bean container with roles, like:
#Component("R")
public final class RoleContainer {
public static final String ROLE_A = "ROLE_A";
}
then on controller you can use:
#PreAuthorize("hasRole(#R.ROLE_A)")
To make it possible to write expressions without package names:
<sec:global-method-security>
<sec:expression-handler ref="methodSecurityExpressionHandler"/>
</sec:global-method-security>
<bean id="methodSecurityExpressionHandler" class="my.example.DefaultMethodSecurityExpressionHandler"/>
Then extend the DefaultMethodSecurityExpressionHandler:
public class DefaultMethodSecurityExpressionHandler extends org.springframework.security.access.expression.method.DefaultMethodSecurityExpressionHandler {
#Override
public StandardEvaluationContext createEvaluationContextInternal(final Authentication auth, final MethodInvocation mi) {
StandardEvaluationContext standardEvaluationContext = super.createEvaluationContextInternal(auth, mi);
((StandardTypeLocator) standardEvaluationContext.getTypeLocator()).registerImport("my.example");
return standardEvaluationContext;
}
}
Now create my.example.Roles.java :
public class Roles {
public static final String ROLE_UNAUTHENTICATED = "ROLE_UNAUTHENTICATED";
public static final String ROLE_AUTHENTICATED = "ROLE_AUTHENTICATED";
}
And refer to it without package name in annotations:
#PreAuthorize("hasRole(T(Roles).ROLE_AUTHENTICATED)")
instead of:
#PreAuthorize("hasRole(T(my.example.Roles).ROLE_AUTHENTICATED)")
Makes it more readable imho. Also roles are now typed. Write:
#PreAuthorize("hasRole(T(Roles).ROLE_AUTHENTICATEDDDD)")
and you will get startup errors that wouldn't have been there if you wrote:
#PreAuthorize("hasRole('ROLE_AUTHENTICATEDDDD')")
Try something like this:
#PreAuthorize("hasRole(T(com.company.enumpackage.OtherClass).ROLE.name())");
If your OtherClass enum is declared as public static, then you need to use $ sign:
#PreAuthorize("hasRole(T(com.company.ParentTopLevelClass$OtherClass).ROLE.name())");
name() to prevent futer problems if toString() will be overriden later
The accepted answer from Kevin Bowersox works, but I didn't like having the T(fully.qualified.path) stuff so I kept looking. I started by creating a custom security method using the answer from James Watkins here:
How to create custom methods for use in spring security expression language annotations
However, instead of a String, I used my enums.Permissions class as the parameter type:
#Component
public class MySecurityService {
public boolean hasPermission(enums.Permissions permission) {
...do some work here...
return true;
}
}
Now the neat part is that when I call the hasPermission from an annotation, I don't have to have to type the whole path, but I do have to enclose it in single quotes:
#PreAuthorize("#mySecurityService.hasPermission('SOME_ROLE_NAME')")
Because the hasPermission method expects an Enum, it will automatically find the Enum value with that name. If it doesn't find it you'll get an exception:
org.springframework.expression.spel.SpelEvaluationException: Type conversion problem, cannot convert from java.lang.String to enums.Permissions
You can rename hasPermission to hasRole, in which case the only trade off is that you are trading T(fully.qualified.path) for #mySecurityService and extra single quotes.
Not sure if it is any better, but there it is. Since none of this is going to verify the values at compile time anyways, my next step is to make an annotation processor.
I also have to give credit to krosenvold for pointing out that spring can automatically convert to an enum:
https://stackoverflow.com/a/516899/618881
Currently I am encountering the following problem with Solr 4.1 in combination with Lucene 4.1.
I have a Solr-Bean like this:
import org.apache.solr.client.solrj.beans.Field;
public final class Bean {
#Field
private int someInt;
#Field
private String someString;
//Fields like this are just being updated for easier use of the bean
private List<Integer> someStringSplitIntoIntegers
//setters, rest is the same:
public void setSomeInt(int someInt) {
this.someInt = someInt;
}
//getters straightforward...
}
Now I want to add the bean to a Solr database via HttpSolrServer.addBean(bean) and I am getting the following exception:
Exception in thread "main" org.apache.solr.client.solrj.beans.BindingException: Invalid setter method. Must have one and only one parameter
at org.apache.solr.client.solrj.beans.DocumentObjectBinder$DocField.storeType(DocumentObjectBinder.java:202)
at org.apache.solr.client.solrj.beans.DocumentObjectBinder$DocField.<init>(DocumentObjectBinder.java:150)
at org.apache.solr.client.solrj.beans.DocumentObjectBinder.collectInfo(DocumentObjectBinder.java:119)
at org.apache.solr.client.solrj.beans.DocumentObjectBinder.getDocFields(DocumentObjectBinder.java:99)
at org.apache.solr.client.solrj.beans.DocumentObjectBinder.toSolrInputDocument(DocumentObjectBinder.java:73)
at org.apache.solr.client.solrj.SolrServer.addBean(SolrServer.java:136)
at org.apache.solr.client.solrj.SolrServer.addBean(SolrServer.java:125)
(...my call...)
But I don't get why. I have the class for more than one argument per setter several times but it is ok (it's even generated by Eclipse).
And now the question:
What am I missing? What could the reason for this be? Does a missing entry in the schema.xml cause these errors (what I doubt...)?
Thanks in advance.
Martin Braun
One can use a Field annotation directly on the setter method:
#Field("cat")
public void setCategory(String[] c){
this.categories = c;
}
make sure that you have an int field in the solr schema.xml called someInt
Cheers
Already fixed on my own.
From the comments:
Ok. Seems to be fixed. I used #Field on the getters as well which caused errors. I already tried removing it but then another error arised. So I changed it back. Furthermore, Solr Beans are not conform with boolean isBoolean() methods (which caused the new error). – Martin Braun just now edit