Spring 3 - Annotation based Bean Validation - java

I am building a REST API. Its made up of a Resource ( #Controller ) which returns a response 204 even when one of the mandatory field is not present.
I am using Spring 3.1, validation-api (1.1.0.Final) & Hibernate-validator(4.3.0). Not sure if Hibernate-validator plays any role here.
<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.0.Final</version>
<exclusions>
<exclusion>
<groupId>javax.validation</groupId>
<artifactId>validation-api</artifactId>
</exclusion>
</exclusions>
</dependency>
I have a spring controller #Controller and a Bean with #Component
#POST
#Consumes(value = {MediaType.APPLICATION_JSON})
#Produces(value = {MediaType.APPLICATION_JSON})
#RequestMapping(method = RequestMethod.POST)
public Response addUserData(#Valid #RequestBody UserData userDataRequest) throws ResourceException {
...
}
My UserData bean has
#Component
public class UserData {
#NotNull(message = "user ID should not be null")
#Min(value = 1, message = "user ID should not be empty")
#Max(value = 20, message = "user ID should have length of more than 20")
#Pattern(regexp="[A-Z][a-z]+", message = "Only Alphanumeric characters allowed")
private String userId;
}
My validations are not getting executed. When I dont pass "userId", there is no error thrown. What am I missing here ?

You must have the following about the infrastructure
#EnableWebMvc
#Configuration
public class WebMvcConfig extends WebMvcConfigurerAdapter {
...
#Autowired
private ValidatorConfig validatorConfig;
...
#Override
public Validator getValidator() {
return validatorConfig.localValidatorFactoryBean();
}
...
}
Where validatorConfig comes from
#Configuration
public class ValidatorConfig {
#Autowired
private ReloadableResourceBundleMessageSource rrbms;
#Bean
public LocalValidatorFactoryBean localValidatorFactoryBean(){
LocalValidatorFactoryBean localValidatorFactoryBean = new LocalValidatorFactoryBean();
localValidatorFactoryBean.setValidationMessageSource(rrbms);
return localValidatorFactoryBean;
}
}
And finally (I suggest you consider put the error messages in a .properties file, such as validation.properties how shown below)
#Configuration
public class MessageSourceConfig {
#Bean(name="messageSource")
public ReloadableResourceBundleMessageSource reloadableResourceBundleMessageSource() {
ReloadableResourceBundleMessageSource resource = new ReloadableResourceBundleMessageSource();
resource.setBasenames("classpath:/com/manuel/jordan/somethinga",
"classpath:/com/manuel/jordan/validation/validation");
resource.setDefaultEncoding("UTF-8");
return resource;
}
}
Some considerations or suggestions:
change #Valid to #Validated (see the API for the #Validated)
Remove the #Component for UserData (that represents a kind of entity right?). Remember that for that class each instance is unique and any bean managed by Spring is Singleton.
put the error messages in a .properties file
from where come the #POST, #Consumes and #Produces annotations?. They are not in the Spring API
Addition 01 about your comment:
Yes, you must use #EnableWebMVC. It indicates to Spring create some special beans internally for the web environment. See the #EnableWebMVC API. Is very important that annotation. Even for Rest I use that annotation.
About the Rest annotations, use the Spring annotations. Such as #RequestMapping and new 'variations' such as #GetMapping, #PostMapping etc.. That annotations contain the produces and consumes attributes. I have never seen your approach about mixing two sets of annotations from Rest.
Addition 02
The WebMvcConfigurerAdapter class represents the XML configuration file about all the Spring MVC infrastructure
Therefore for XML
#EnableWebMvc is equivalent <mvc:annotation-driven/>
About validation it should be: <mvc:annotation-driven validator="beanNameValidator" /> where the validator attribute according with the .xsd says:
Attribute : validator The bean name of the Validator that is to be
used to validate Controller model objects. This attribute is not
required, and only needs to be specified explicitly if a custom
Validator needs to be configured. If not specified, JSR-303
validation will be installed if a JSR-303 provider is present on the
classpath.
beanNameValidator according with my #Bean seems should be localValidatorFactoryBean

I ultimately ended up using Jersey Bean Validation, instead of Spring. This is because rest of my code is using Jersey anyways. To make this work I just imported Jersey Bean Validation jar and added a small change to web.xml. Validations are now working.
Thank you #Manual Jordan. I will upvote your answer, since it gave me the right clues.
<!-- jersey beanValidation -->
<init-param>
<param-name>jersey.config.beanValidation.enableOutputValidationErrorEntity.server</param-name>
<param-value>true</param-value>
</init-param>

Maybe passing to your "addUserData" method, a bindingResult Object, so you can test for and retrieve validation errors.
here is an example of how to use it : Validation form input

Related

How PropertiesLoaderUtils.loadProperties mapped to Configuration POJO using Spring at Runtime?

I have a service which is which consumes many external services. I am creating properties file for each of them, these are quite few predefined properties such as twil.props, tweet.props, hubpot.props etc
So as to get those properites at runtime I am using PropertiesLoaderUtils like below:
Resource resource = new ClassPathResource("/"+apiname +".properties");
Properties props = PropertiesLoaderUtils.loadProperties(resource);
I would like to get these properties into POJO just like ConfigurationProperties, I have designed following POJO for that purpose:
public class APIConfig {
private Integer paginationPerPage;
private String paginationKeyword;
private String paginationStyle;
private String countParamKeyword;
private String countKey;
private String offsetKey;
}
I will maintain properties file in such a way so that these can be easily mapped to Config POJO:
Properties for twil.properties
api.paginationPerPage=10
api.paginationKeyword=limit
api.paginationStyle=offset
api.countParamKeyword=count
api.countKey=count
api.offsetKey=offset
So can I get this directly into given POJO by utilizing any of Spring Boot / Spring utility, config etc?
As it is noticed at comments, the only right solution here which includes zero downtime, namely #RefreshScope.
Use spring cloud config dependency
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-config</artifactId>
</dependency>
Add to your code
#Configuration
class SomeConfiguration {
#Bean
#RefreshScope
#ConfigurationProperties("twil.props")
APIConfig twilConfig() {
return new ApiConfig()
}
#Bean
#RefreshScope
#ConfigurationProperties("tweet.props")
APIConfig tweetConfig() {
return new ApiConfig()
}
}
Usage: #Autowire APIConfig tweetConfig; to any bean
Call /refresh endpoint to refresh beans by new values from property sources
Please make a solution uniform with Spring ecosystem.
If you want to have dynamically to be dependent on, for example, #PathVariable:
private Map<String, AppConfig> sourceToConfig;
#GetMapping("/{source}/foo")
public void baz(#PathVariable source) {
AppConfig revelantConfig = sourceToConfig.get(source);
...
}
Spring provides the opportunity to automatically collect a map of beans where bean key is a bean name.
Rename bean methods above from twilConfig to twil() and call you endpoint like: /twil/foo (twilConfig is a bad path for an endpoint)

Scan new manually added controllers,, when xml based spring mvc has already been loaded

I am working on a project where we are using spring mvc as a web framework.
It has an xml-based configuration and starts up first.
But also I have plugins which I can add to my project manually while working with it. Each plugin describes an API with all his #Controller-s and Models.
I managed to register these API-s in my Spring configuration
(
AnnotationConfigWebApplicationContext ctx=new AnnotationConfigWebApplicationContext();
ctx.register(classNames);
ctx.refresh();
),
but how can I kind of "wake up" my spring and say please scan all these controllers.
I have an ExceptionHandler for all my API-s that's why I need to scan them all to connect these Controllers with the handler.
I have tried this, but it did not work.
AnnotationConfigWebApplicationContext ctx=new AnnotationConfigWebApplicationContext();
ctx.scan(packageName);
ctx.refresh();
I get no error during the execution.
Use #RestController annotation for your controller class which was defined all APIs
#RestController
public class exampleController {
}
Use #Component annotation for other component classes
#Component
public class Validations {
}
As well as you can use #Service annotation for business layer which is defined business logic and use #Repository annotation for the class which access database.
To enable above mentioned annotations, you need add below mentioned dependancy If you are using "maven" to build the project. otherwise add all jar files to the project related with below dependancy
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
If you need to handle exceptions you need create below mentioned class with #ControllerAdvice annotation. You can handle specific method for the each exception. (Example: NullPointerException)
#ControllerAdvice
public class ExceptionHandler extends RuntimeException {
#Autowired
ResponceBuilder responseBuilder;
#JsonIgnore
private HttpStatus status = HttpStatus.OK;
public ExceptionHandler() {
super();
}
#org.springframework.web.bind.annotation.ExceptionHandler(Example.class)
public ResponseEntity<Response>
NullPointerException(NullPointerException e) {
log.info("Invalid Data: {}",e.getErrorMessage());
return new ResponseEntity<>(responseBuilder.build(e.getErrorCode(),
e.getErrorMessage()), status);
}

How to call a Spring managed object from a POJO?

I am running a web-app, which has one exposed class ( available to other POJO classes) and that has one autowired private member.
Spring managed class
public class EPSQueueSender {
#Autowired
private AmqpTemplate epsMessageTemplate;
public void dosomething(...){
epsMessageTemplate.convertAndSend(...); // Here epsMessageTemplate is null if instance of EPSQueueSender taken from other POJO
}
}
POJO class
public class Test{
EPSQueueSender sender = new EPSQueueSender();
sender.dosomething(....); // gives null exception on epsMessageTemplate
}
Spring code ( running as WebApp) and POJO class code( different Jar) are on same JVM. The POJO is not able to get initialized autowired object. However it is initialized if I use it in webApp project.
Can someone please give some suggestion how can I overcome this problem?
Last thing I would like to try is to hit webserver as http request from POJO.
beans can be pojo or xml many examples might help. You already have #autowired but you did not create the #bean method itself that belongs in a class annotated with #Configuration
Your problem could be overcome using #Configurable feature of spring. For it you have configure in xml with a code like belove
<context:annotation-config/>
<context:spring-configured/>
<context:load-time-weaver/>
in Java Congiguration like below:
#Configuration
#EnableAspectJAutoProxy
#EnableSpringConfigured
#EnableLoadTimeWeaving
public class ConfigApplicationContext {
}
with this configuration you can benefit of the load-waving aspect technique that througth the build-in Spring bean AnnotationBeanConfigureAspect you can inject Spring bean in a pojo that is annotated with #Configurable. you colud be have a code like below:
#Configurable
public class Test{
#Autowired
private EPSQueueSender sender;
public void method(){
sender.dosomething(....); // gives null exception on epsMessageTemplate
}
}
of course, since that you are using a load-wave technique you have configure an agent that will perform the istruments. the configuration is very simple and you have add a line like below in the start of the jvm or tomcat:
java -javaagent:path of the jar with the agent/spring-instrument.jar
remember of course of insert the aop and spring aop maven dependency:
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aop</artifactId>
<version>yourVersion</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aspects</artifactId>4
<version>yourVersion</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-instrument</artifactId>
<version>yourVersion</version>
</dependency>
I hope that this can help you

can #FeignClient extend - and #RestController implement - a common, fully-annotated Interface?

I want a Feign client to consume a Spring Boot controller, and I want the contract between them to be specified in a common Interface to the degree possible.
The interface with method would look something like this:
#RequestMapping
public interface RuleManager {
#RequestMapping(value = "/addRule", method = RequestMethod.POST, consumes = {"application/json"}, produces = {"application/json"})
#ResponseBody Rule addRule(#RequestBody Rule rule);
}
The Feign client would look like:
#FeignClient(url = "http://localhost:8080")
public interface RuleManagerClient extends RuleManager { }
and the Spring boot controller:
#RestController
public class RuleManagerService implements RuleManager {
#Override
#Transactional
public Rule addRule(#RequestBody Rule rule) {
return rule;
}
}
It's nice that I don't have to specify #RequestMapping in two places, but unfortunately it seems I do have to specify #RequestBody twice. When #RequestBody is omitted from either the controller or the shared interface, the Rule object is instantiated but with all members set to null.
Is there a way around this ? Perhaps this is addressed in a newer version ? My dependencies include:
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-feign</artifactId>
<exclusions>
<exclusion>
<groupId>com.netflix.feign</groupId>
<artifactId>feign-core</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>com.netflix.feign</groupId>
<artifactId>feign-core</artifactId>
<version>8.14.3</version>
</dependency>
I discovered this technique required at least feign-core 8.6 here:
https://jmnarloch.wordpress.com/2015/08/19/spring-cloud-designing-feign-client/
Thanks for any help.
Apparently this does work--#RequestBody need only appear in the shared Interface. The problem was that I had the following property set in application.properties for the controller but not for the client:
spring.jackson.property-naming-strategy=CAMEL_CASE_TO_LOWER_CASE_WITH_UNDERSCORES
That's why the object was instantiated on the server side but with all members null--effectively, the wrong properties were sent across the wire, for example "ruleName" instead of the expected "rule_name".

How to disable Hibernate validation in a Spring Boot project

I have a spring boot project that has a CrudRepository, an Entity and a Controller. I am basically trying to persist an entity based on the data passed to the Controller.
To do this, I am using spring-boot-starter-jpa. My Entity is annotated with JSR-303 annotations, which are checked in the controller before the data gets passed to the CrudRepository for persistence.
Controller method:
#RequestMapping(value = "users", method = { RequestMethod.POST })
public SuccessfulResponse<User> addUser(#Valid #RequestBody User user, BindingResult validation) {
if (validation.hasErrors()) {
throw new ValidationException(validation);
}
User saved = this.users.save(user);
return new SuccessfulResponse<User>(saved);
}
Entity:
#Entity /* JPA */
public class User {
#Id /* JPA */
#Column(name="email_address", nullable=false, length=255) /* JPA */
#UserUnique
private String emailAddress;
}
The cause of my issues is the UserUnique annotation. Its validator looks like this:
public class UserUniqueValidator implements ConstraintValidator<UserUnique, String> {
private UserRepository users;
#Autowired
public UserUniqueValidator(UserRepository users) {
this.users = users;
}
#Override
public void initialize(UserUnique annotation) {}
#Override
public boolean isValid(String value, ConstraintValidatorContext context) {
return users.findOne(value) == null;
}
}
What seems to be happening is, the validation is getting run twice. Once in the controller via the #Valid annotation, and once when Hibernate tries to persist the object. However, when Hibernate tries to persist the object, it throws:
javax.validation.ValidationException: HV000064: Unable to instantiate ConstraintValidator: class test.UserUniqueValidator`
This seems to be because its not spring-aware and cant inject the dependency into the constructor. So, what I want to do is disable Hibernate validation completely (as its redundant and already happening in the controller).
There seems to be a property called javax.persistence.validation.mode which you can set to none. However, I cant for the life of me figure out where to set it in a code-based configuration.
I realise there are questions like JSR-303 dependency injection and Hibernate but these are all using xml config and manually configuring parts of the persistence layer.
What I want to do is "post-configure" the required parts of the persistence layer that Spring Boot creates for me because if I define my own then I am no longer leveraging Spring Boot's auto configuration. Can anyone help me determine if 1) this is possible and 2) which parts do I need to configure and how?
Thanks!
As [M. Deinum] mentioned in a comment on my original post, the solution is to set:
spring.jpa.properties.javax.persistence.validation.mode=none
In the application.properties file.
Additionally, this behaviour is described here (its easy to miss because no example is provided).
#Erin Drummond's Answer is for database entity validation (individual records)
But if someone ran into a problem with schema validation below property works well.
# Hibernate ddl auto (create, create-drop, validate, update, none)
spring.jpa.hibernate.ddl-auto=none
Actually, in spring boot 2.x. it is:
spring.jpa.hibernate.ddl-auto: none
for spring boot unless you add validation dependency the spring validation would not kick in.
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-validation</artifactId>
</dependency>

Categories