How can #Validated work on common method? - spring-boot

In Springboot project, when I try to add #Validated on controller method, it worked.But now I want to add it on a common method, then failed.
Try to add #Validated on controller method, it worked
public class TaskValidator {
private static final Logger logger = LogManager.getLogger("TaskValidatorLogger");
public void validateTest(#Validated Test test) {
logger.info("Validate: {}", test.getName());
}
public static void main(String[] args) {
new TaskValidator().validateTest(new Test());
}
}
#Data
public class Test {
#NotNull(message = "name can not be null")
private String name;
}
It should throw an MethodArgumentNotValidException but not.

Spring MVC has the ability to automatically validate #Controller
inputs. In previous versions it was up to the developer to manually
invoke validation logic.
In the controller methods, springboot automatically binds any validators to the model and invoke it when the data is bound to the object.
But in your case , you are trying to validate an object in which case , springboot might not be automatically binding your validator to your model and call the validator.So, in that case, you will need to manually bind the object to the validator.
or you can manually invoke the validator on a bean like :
#AutoWired
Validator validator;
...
validator.validate(book);

Related

Is it possible to use custom Spring validator to validate method input just like input of controller method in MVC?

I have custom Spring validator (not constraint-validator!)
#Component
public class MyValidator implements org.springframework.validation.Validator{
//my fine implementation
}
I can use that validator in controller like
#RestController
public class MyController{
#Autowired private MyValidator myValidator;
#InitBnder
public void binder(WebDataBinder binder){
binder.addValidator(myValidator);
}
#PostMapping
public void whatever(#RequestBody #Valid MyValidatedObject){ // not sure is #Valid needed here
.....
}
}
This works fine on controller level due to manual registration of validator to web binder. Now, I want to use the same validator on service method input, not on contorller level. If I do
#Service
#Validated
class MyService{
public void someMethod(#Valid MyValidatedObject input);
}
Validator is not fired. Other annotatons like #Min or #Max works here (throwing an exception as MyValidatedObject is not valid subject for those - but it shows that validation is about to be triggered). So it seams that only ConstrainValidator alike validators works here out of the box. How can I make Spring to use my custom validator to validate method input??
PS obviously I can inject validator and invoke it directly, but that is not what I want.

Spring custom validator class with MultipartFile class support does not get called/fired

Sorry i couldn't find a better way to construct the question.
I am trying to validate on server side an array of multipartfile received with other information (e.g email, phone number etc) from an Ajax multipart/form-data post request.
I created 2 custom validator classes by implementing Spring Validator interface, UserFilesValidator to check that all received files are image files and are not above 2MB
and UserInfosValidator to check other user information e.g. phone number validate, email avalibility etc.
I registered the custom validator classes using #InitBinder.
Now when request is made, UserInfosValidator gets called and works fine but UserFilesValidator doesn't get called
Here is my code
Controller class
#Controller
public class UserController{
private final UserFilesValidator userFilesValidator;
private final UserInfosValidator userInfosValidator;
#Autowired
public UserController(UserFilesValidator userFilesValidator, UserInfosValidator userInfosValidator){
this.userFilesValidator = userFilesValidator;
this.userInfosValidator = userInfosValidator;
}
#InitBinder("userfiles")
public void formDataBinder1(WebDataBinder binder){
binder.addValidators(userFilesValidator);
}
#InitBinder("userinfos")
public void formDataBinder2(WebDataBinder binder){
binder.addValidators(userInfosValidator);
}
#RequestMapping(value = "/add", method = RequestMethod.POST, produces = MediaType.APPLICATION_JSON_VALUE, consumes = MediaType.MULTIPART_FORM_DATA_VALUE)
#ResponseBody
public ResponseEntity<UserEntity> addUser(#RequestParam("userfiles") #Valid MultipartFile[] files,
#RequestPart("userinfos") #Valid UserEntity user){
//codes ...
}
}
Validator class
#Component
public class UserFilesValidator implements Validator {
#Override
public boolean supports(Class<?> clazz) {
return MultipartFile[].class.isAssignableFrom(clazz);
}
#Override
public void validate(Object target, Errors errors) {
MultipartFile[] files = (MultipartFile[]) target;
//validation codes ...
}
}
I excluded UserInfosValidator class since it works fine.
I also did not included global exception handler class (that's #ControllerAdvice annotated class)
since errors has to be registered in UserFilesValidator class which is not even getting called.
So please how can i get this to work? I know i can validate it in addUser method but I don't want to have validation codes in controller class
request handler method.

CDI SessionScoped POJO inside an in-container JUnit test

I'm testing a web application using JUnit. The buisness layer of this application is writed in EJB stateless classes.
So I do "in container" tests with JUnit and Glassfish-embedded.
All works fine so far, EJBs are injected using lookup functions.
Here are a simple test case :
public class SupportTest {
private static EJBContainer container;
private static MyEJB myEjb;
#BeforeClass
public static void setUpServices() throws NamingException {
Map<String, Object> properties = new HashMap<String, Object>();
properties.put(EJBContainer.MODULES, new File("target/classes"));
container = EJBContainer.createEJBContainer(properties);
myEjb = (MyEJB) container.getContext().lookup("java:global/classes/MyEJB");
}
#Test
public void test() {
myEjb.doSomething("user_login");
}
}
Now I have a SessionScoped POJO (CDI) which keep information such as user login and so on.
This Pojo is injected inside a static class. Like this :
public class MyStaticClass {
public static boolean verifyLogin(String login) {
MySessionPojo mySessionPojo = CDI.current().select(MySessionPojo.class).get();
return mySessionPojo.getLogin().equals(login);
}
}
This static class is used in EJB to secure the buisness code, like this :
#Stateless
public class MyEJB {
public void doSomething(String login) {
if(MyStaticClass.verifyLogin(login)){
//do something
}
}
}
Inside a normal Glassfish 4.1 server, the injection of the POJO inside the static class works fine.
Inside the Glassfish-embedded, the POJO injection fails with this message :
WELD-001303: No active contexts for scope type javax.enterprise.context.SessionScoped
I assume this is because there is no Http Session bound to it.
Is there a way to simulate/create à SessionContext programmatically?
Thanks.
Ok, I finally find a workaround. I use the framework JMockit to replace the static class by a mock class, with fake methods which always return TRUE. (I had already tested Mockito and PowerMock, but both didn't work).

#Autowired in static classes

This is an Spring MVC project with Hibernate.
I'm, trying to make a Logger class that, is responsible for inputting logs into database.
Other classes just call proper methods with some attributes and this class should do all magic.
By nature it should be a class with static methods, but that causes problems with autowiring dao object.
public class StatisticLogger {
#Autowired
static Dao dao;
public static void AddLoginEvent(LogStatisticBean user){
//TODO code it god damn it
}
public static void AddDocumentEvent(LogStatisticBean user, Document document, DocumentActionFlags actionPerformed){
//TODO code it god damn it
}
public static void addErrorLog(Exception e, String page, HashMap<String, Object> parameters){
ExceptionLogBean elb=new ExceptionLogBean();
elb.setStuntDescription(e);
elb.setSourcePage(page);
elb.setParameters(parameters);
if(dao!=null){ //BUT DAO IS NULL
dao.saveOrUpdateEntity(elb);
}
}
How to make it right? What should I do not to make dao object null?
I know that I could pass it as a method parameter, but that isn't very good.
I'm guessing that autowired can't work on static objects, because they are created to early to autowiring mechanism isn't created yet.
You can't #Autowired a static field. But there is a tricky skill to deal with this:
#Component
public class StatisticLogger {
private static Dao dao;
#Autowired
private Dao dao0;
#PostConstruct
private void initStaticDao () {
dao = this.dao0;
}
}
In one word, #Autowired a instance field, and assign the value to the static filed when your object is constructed. BTW, the StatisticLogger object must be managed by Spring as well.
Classical autowiring probably won't work, because a static class is not a Bean and hence can't be managed by Spring. There are ways around this, for example by using the factory-method aproach in XML, or by loading the beans from a Spring context in a static initializer block, but what I'd suggest is to change your design:
Don't use static methods, use services that you inject where you need them. If you use Spring, you might as well use it correctly. Dependency Injection is an Object Oriented technique, and it only makes sense if you actually embrace OOP.
I know this is an old question but just wanted to share what I did,
the solution by #Weibo Li is ok but the problem it raises Sonar Critical alert about assigning non static variable to a static variable
the way i resolved it with no sonar alerts is the following
I change the StatisticLogger to singlton class (no longer static)
like this
public class StatisticLogger {
private static StatisticLogger instance = null;
private Dao dao;
public static StatisticLogger getInstance() {
if (instance == null) {
instance = new StatisticLogger();
}
return instance;
}
protected StatisticLogger() {
}
public void setDao(Dao dao) {
this.dao = dao;
}
public void AddLoginEvent(LogStatisticBean user){
//TODO code it god damn it
}
public void AddDocumentEvent(LogStatisticBean user, Document document, DocumentActionFlags actionPerformed){
//TODO code it god damn it
}
public void addErrorLog(Exception e, String page, HashMap<String, Object> parameters){
ExceptionLogBean elb=new ExceptionLogBean();
elb.setStuntDescription(e);
elb.setSourcePage(page);
elb.setParameters(parameters);
if(dao!=null){
dao.saveOrUpdateEntity(elb);
}
}
I created a service(or Component) that autowire the service that i want and set it in the singlton class
This is safe since in spring it will initialize all the managed beans before doing anything else and that mean the PostConstruct method below is always called before anything can access the StatisticLogger
something like this
#Component
public class DaoSetterService {
#Autowired
private Dao dao0;
#PostConstruct
private void setDaoValue () {
StatisticLogger.getInstance().setDao(dao0);
}
}
Instead of using StatisticLogger as static class I just use it as StatisticLogger.getInstance() and i can access all the methods inside it
You can pass the DAO to StatisticLogger from where you call it.
public static void AddLoginEvent(LogStatisticBean user, DAO dao){
dao.callMethod();
}
It might be too late to put an answer to this question, especially when a question is already having an accepted answer. But it might help others in case they face the same issue.
inside the StatisticLogger class create an instance of the Dao service.
public static Dao daoService = new Dao();
then, auto-wire the service instance through the constructor of the StatisticLogger class.
#Autowired
public functionName(Dao daoService0) {
this.daoService = daoService0;
}
//use this service as usual in static class
daoService.fun();
I think this is the simplest solution for the problem.

Using both JSR-303 and Traditional Bean Validation?

Is it possible to use both JSR-303 bean validation and traditional validation (a single validator class for the type) in Spring? If so, what configuration is required to set this up?
I have tried the instructions on the reference.
#InitBinder
protected void initBinder(WebDataBinder binder) {
binder.setValidator(new DualEntryValidator());
}
#RequestMapping(value="/dualEntry.htm", method = RequestMethod.POST)
public ModelAndView handlePost(#Valid DualEntryForm form, BindingResult result) {
ModelAndView modelAndView = new ModelAndView("dualEntry", getCommonModel());
if (!result.hasErrors()){
//do logic
return modelAndView;
}else {
modelAndView.addObject("dualEntryForm", form);
return modelAndView;
}
}
I can get this to use my custom Validator or the JSR-303 validation, but not both. If I have the initBinder present as in the example it uses the custom Validator. If I remove it the JSR-303 bean validation is used. How can I use both?
I've done that following the instructions here:
http://blog.jteam.nl/2009/08/04/bean-validation-integrating-jsr-303-with-spring/
See the "Enjoy both worlds" section. Shortly, you explicitly run a JSR303 validation from a Spring validator, "joining" the results of JSR303 validations based on annotations and your custom validation logic.
I realise this is quite old, but I got this to work with minimal disturbance to my code
Change binder.setValidator(new DualEntryValidator());
to
#InitBinder
protected void initBinder(WebDataBinder binder) {
binder.addValidators(new DualEntryValidator());
}
With setValidator() you're replacing the JSR-303 validator with your one. With addValidator(), the JSR-303 validator is called and so is yours.
You need to make sure that your validator does not overlap with your JSR-303 #NotNull, #Min, #Max, etc. annotations otherwise you'll get duplicate error messages added.
Spring provides three handle for bean validation.
1.abstract class AbstractPropertyValidationAnnotationHandler
2.abstract class AbstractMethodValidationAnnotationHandler
3.abstract class ClassValidationAnnotationHandler
In this example i am implementing custom annotation CustomAnnotationHandle
#Target({ElementType.METHOD, ElementType.TYPE})
#Retention(RetentionPolicy.RUNTIME)
#Documented
Class CustomAnnotationHandle extends Annotation{
public abstract String value();
}
To implement custom annotation for property validation we need to extend AbstractPropertyValidationAnnotationHandler Class.
AbstractPropertyValidationAnnotationHandler provides createValidationRule abstract method
protected abstract AbstractValidationRule createValidationRule(Annotation annotation, Class class1, String s);
So,the extended class must provide implementation of
protected abstract AbstractValidationRule createValidationRule(Annotation annotation, Class class1, String s)
public class CustomPropertyAnnotationHandler extends AbstractPropertyValidationAnnotationHandler
{
public CustomPropertyAnnotationHandler()
{
super(new Class[] {
XXX.XXX.PackageLevle.CustomAnnotationHandle // as it takes array of custom annotation ,so we can pass more than one
// overwriting abstract method
protected AbstractValidationRule createValidationRule(Annotation annotation, Class class1, String s){
CustomAnnotationHandle value = (CustomAnnotationHandle)annotation;
return TestValidationRule(value.getValue());
// as you can see it return AbstractValidationRule.So, we need a class to give our bean specific validation rule.In our case it is
//TestValidationRule
}
}
}
public class TestValidationRule extends AbstractValidationRule
{
public TestValidationRule (String valuetest)
{
super();
this.valuetest = valuetest;
}
Private String valuetest;
}
Spring provides AnnotationBeanValidationConfigurationLoader Class.This class is used for spring own annotation for bean validation.
DefaultValidationAnnotationHandlerRegistry class is used as defaultHandlerRegistry.But if we need to provide our own annotaion then we
need to extend AnnotationBeanValidationConfigurationLoader and set our specific handleregistry via method
setHandlerRegistry(new CustomPropertyAnnotationHandler());
Class DefaultValidationAnnotationHandlerRegistry is used to register spring own annotation for bean validation.It register bean by
calling registerPropertyHandler method of SimpleValidationAnnotationHandlerRegistry class.So for our custom annotation we need to
register CustomPropertyAnnotationHandler by calling registerPropertyHandler method of SimpleValidationAnnotationHandlerRegistry class
public class OurBeanSpecificValidationLoader extends AnnotationBeanValidationConfigurationLoader
{
public OurBeanSpecificValidationLoader ()
{
super();
setHandlerRegistry(new OurSpecificAnnotationHandleRegistery ());
}
}
public class OurSpecificAnnotationHandleRegistery extends DefaultValidationAnnotationHandlerRegistry
{
public OurSpecificAnnotationHandleRegistery ()
{
registerPropertyHandler(new CustomPropertyAnnotationHandler() );
}
}
so you have your custom annotation for bean valiation.E.g
#CustomAnnotationHandle(value = "test")
private Object test;

Resources