RESTFul validation using Spring Part 2

Core program: We will create annotation interface Validators where application developer can add validators. ValidatorAspect class validate method is called whenever Validators annotation is encountered at controller layer. ValidatorAspect class will have logic to dynamically invoke custom validator.

Interface: Validators

package com.validation;
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Inherited;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;


@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Inherited
@Documented
public @interface Validators {

     /**
       * The validators that should be run before the service method is
       * invoked.
       */
       String[] validators() default {};
}

Validation Interface:– Custom annotation will load Spring validators as bean and can be fetched within Beanfactory. Note interface is annotated with @Component, hence any class annotated with @Validation will act as Spring bean. As alternate approach you can use @Component directly over custom validation class.

package com.validation;
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Inherited;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import org.springframework.stereotype.Component;


@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Inherited
@Documented
@Component
public @interface Validation {
       String value();
}

 ValidationAspect Class: Class is responsible to dynamically invoke custom validation defined at controller layer.

package com.validation;
import java.lang.annotation.Annotation;
import java.lang.reflect.AnnotatedElement;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.List;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.validation.DirectFieldBindingResult;
import org.springframework.validation.FieldError;
import org.springframework.validation.Validator;
import com.cloudsdp.core.exception.CloudSdpMessageException;
import com.cloudsdp.core.exception.Errors;


public class ValidationAspect {
 @Autowired
 private BeanFactory beanFactory;
    /**
     * Pointcut for running business validations.
     *
     * @param call the execution stack join point
     * @throws Throwable on failure
     */
    public void validate(JoinPoint call) throws Throwable {
        MethodSignature methodSignature = (MethodSignature) call.getSignature();
        methodSignature.getDeclaringType();
        Class classObject = call.getTarget().getClass();
        Method method = classObject.getMethod(methodSignature.getName(),

                                    methodSignature.getParameterTypes());
        String [] validatorNames = readAnnotation(method);
        if ((validatorNames != null) && (validatorNames.length > 0)) {
            Validator[] validators = new Validator[validatorNames.length];
            for (int i = 0; i < validators.length; i++) {
             validators[i] = (Validator) beanFactory.getBean(validatorNames[i]);
                // Re-factor for reuse
                Object arg = findArgument(validators[i], call);
                DirectFieldBindingResult result = new DirectFieldBindingResult(arg,

                                                arg.getClass().getName());
                validators[i].validate(arg, result);
                if(result.hasErrors()){
                       CloudSdpMessageException ex = new CloudSdpMessageException();
                       ex.setFormattedErrors(formatErrors(result));
                       throw ex;
                }
            }
        }
    }
  /**
    * Format result if it contains errors.
    */

    private List formatErrors(DirectFieldBindingResult result) {
        List<Errors> errors =  null;
        if(result.hasErrors()){
             errors = new ArrayList<Errors>();
             if(result.hasFieldErrors()){
             for(FieldError fieldError:result.getFieldErrors()){
                 Errors error = new Errors();
                 error.setObjectName(fieldError.getObjectName());
                 error.setFieldName(fieldError.getField());
                 error.setErrorMessage(fieldError.getCode());
                 errors.add(error);
             }
        }
    }
    return errors;
   }

  /**
    * Read annotation to get validation class detail.
    */
   private String[] readAnnotation(AnnotatedElement element)
   {
        String[] validatorNames = null;
        try
        {  
              Annotation[] classAnnotations = element.getAnnotations();
              for(Annotation annotation : classAnnotations)
              {
                  if (annotation instanceof Validators)
                  {
                      Validators validator = (Validators)annotation;
                      validatorNames = validator.validators();
                  }
              }
         }
         catch (Exception exception)
         {
              exception.printStackTrace();
         }
         return validatorNames;
    }
    /**
     * Called to process each service method argument passed.
     *
     * @param validator the candidate validator
     * @param call the join point being executed
     * @return the argument that needs to be validated
     */
    private Object findArgument(Validator validator, JoinPoint call) {
        Object[] args = call.getArgs();
        if (args.length == 0) {
            return null;
        }
        for (Object arg : args) {
            if (arg != null && validator.supports(arg.getClass())) {
                return arg;
            }
        }
        return null;
    }
}

Custom Validation Class:

package com.myapplication.validator;
import javax.inject.Inject;
import org.springframework.core.env.Environment;
import org.springframework.validation.Errors;
import org.springframework.validation.ValidationUtils;
import org.springframework.validation.Validator;
import com.campaignify.entity.User;
import com.cloudsdp.core.validation.Validation;


@Validation(value= "myapplication.registeration.user")
public class UserManagementValidator implements Validator {
       @Inject
       private Environment environment;
       @Override
       public boolean supports(Class clazz) {
              return User.class.isAssignableFrom(clazz);
       }
       @Override
       public void validate(Object target, Errors errors) {
              // Validation logic
              ValidationUtils.rejectIfEmpty(errors, "firstName", environment.getProperty("firstName.required"));
              ValidationUtils.rejectIfEmpty(errors, "lastName", environment.getProperty("lastName.required"));
              ValidationUtils.rejectIfEmpty(errors, "companyName", environment.getProperty("companyName.required"));
      }
}

Next

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

%d bloggers like this: