com.github.umeshawasthi.struts2.jsr303.validation.interceptor.JSR303ValidationInterceptor.java Source code

Java tutorial

Introduction

Here is the source code for com.github.umeshawasthi.struts2.jsr303.validation.interceptor.JSR303ValidationInterceptor.java

Source

/*******************************************************************************
 *
 *  Struts2-JSR303-beanValidation-Plugin - An Open Source Struts2 Plugin to hook Bean Validator.
 *  =================================================================================================================
 *
 *  Copyright (C) 2013 by Umesh Awasthi
 *  https://github.com/umeshawasthi/jsr303-validator-plugin
 *
 * **********************************************************************************************************************
 *
 *  Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with
 *  the License. You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 *  Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on
 *  an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the
 *  specific language governing permissions and limitations under the License.
 *
 * **********************************************************************************************************************/

package com.github.umeshawasthi.struts2.jsr303.validation.interceptor;

import java.lang.reflect.Method;
import java.util.Collection;
import java.util.Set;

import javax.validation.ConstraintViolation;
import javax.validation.Validator;

import org.apache.commons.lang3.StringUtils;
import org.apache.struts2.interceptor.validation.SkipValidation;

import com.github.umeshawasthi.struts2.jsr303.validation.constant.ValidatorConstants;
import com.opensymphony.xwork2.ActionInvocation;
import com.opensymphony.xwork2.ActionProxy;
import com.opensymphony.xwork2.ModelDriven;
import com.opensymphony.xwork2.inject.Inject;
import com.opensymphony.xwork2.interceptor.MethodFilterInterceptor;
import com.opensymphony.xwork2.util.AnnotationUtils;
import com.opensymphony.xwork2.util.ValueStack;
import com.opensymphony.xwork2.util.logging.Logger;
import com.opensymphony.xwork2.util.logging.LoggerFactory;
import com.opensymphony.xwork2.validator.DelegatingValidatorContext;
import com.opensymphony.xwork2.validator.ValidatorContext;

/**
 * <p>
 * JSR 303 bean Validation interceeptor.This Intercepter do not itself provide any Bean validation functionality but
 * works as a bridge between JSR303 Bean validation specification and Struts2 validation mechanism.
 * </p>
 * <p>
 * Intercepter will create a Validation Factory based on the provider class and will validate requested method or Action
 * class. Hibernate bean validator will be used as a default validator in case of no provider class will be supplied to
 * the intercepter.
 * </p>
 * 
 * @author Umesh Awasthi
 */
public class JSR303ValidationInterceptor extends MethodFilterInterceptor {

    private static final Logger LOG = LoggerFactory.getLogger(JSR303ValidationInterceptor.class);
    private static final long serialVersionUID = 1L;
    protected JSR303ValidationManager jsr303ValidationManager;

    @Inject()
    public void setJsr303ValidationManager(JSR303ValidationManager jsr303ValidationManager) {
        this.jsr303ValidationManager = jsr303ValidationManager;
    }

    @SuppressWarnings("nls")
    @Override
    protected String doIntercept(ActionInvocation invocation) throws Exception {
        Validator validator = this.jsr303ValidationManager.getValidator();
        if (validator == null) {

            if (LOG.isDebugEnabled()) {
                LOG.debug("There is no Bean Validator configured in class path. Skipping Bean validation..");
            }
            return invocation.invoke();

        }

        if (LOG.isDebugEnabled()) {
            LOG.debug("Starting bean validation using " + validator.getClass());
        }

        Object action = invocation.getAction();
        ActionProxy actionProxy = invocation.getProxy();
        ValueStack valueStack = invocation.getStack();
        String methodName = actionProxy.getMethod();
        String context = actionProxy.getConfig().getName();

        if (LOG.isDebugEnabled()) {
            LOG.debug("Validating [#0/#1] with method [#2]", invocation.getProxy().getNamespace(),
                    invocation.getProxy().getActionName(), methodName);
        }

        Collection<Method> annotatedMethods = AnnotationUtils.getAnnotatedMethods(action.getClass(),
                SkipValidation.class);

        if (!annotatedMethods.contains(getActionMethod(action.getClass(), methodName))) {
            // performing jsr303 bean validation
            performBeanValidation(action, valueStack, methodName, context, validator);
        }

        return invocation.invoke();

    }

    @SuppressWarnings("nls")
    protected void performBeanValidation(Object action, ValueStack valueStack, String methodName, String context,
            Validator validator) throws NoSuchMethodException {

        LOG.debug("Initiating bean valdation..");

        Set<ConstraintViolation<Object>> constraintViolations = null;

        if (action instanceof ModelDriven) {
            LOG.debug("Performing validation on model..");
            constraintViolations = validator.validate(((ModelDriven) action).getModel());
        } else {
            LOG.debug("Performing validation on action..");
            constraintViolations = validator.validate(action);
        }

        addBeanValidationErros(constraintViolations, action, valueStack, null, validator);

    }

    @SuppressWarnings("nls")
    private void addBeanValidationErros(Set<ConstraintViolation<Object>> constraintViolations, Object action,
            ValueStack valueStack, String parentFieldname, Validator validator) {
        if (constraintViolations != null) {
            ValidatorContext validatorContext = new DelegatingValidatorContext(action);
            for (ConstraintViolation<Object> constraintViolation : constraintViolations) {
                String key = constraintViolation.getMessage();
                String message = key;
                try {
                    message = validatorContext.getText(key);
                } catch (Exception e) {
                    LOG.error("Error while trying to fetch message", e);
                }

                if (isActionError(constraintViolation)) {
                    if (LOG.isDebugEnabled()) {
                        LOG.debug("Adding action error '#0'", message);
                    }
                    validatorContext.addActionError(message);
                } else {

                    ValidationError validationError = buildBeanValidationError(constraintViolation, message,
                            action);
                    String fieldName = validationError.getFieldName();
                    if (action instanceof ModelDriven
                            && fieldName.startsWith(ValidatorConstants.MODELDRIVEN_PREFIX)) {
                        fieldName = fieldName.replace("model.", ValidatorConstants.EMPTY_SPACE);
                    }
                    if (parentFieldname != null) {
                        fieldName = parentFieldname + ValidatorConstants.FIELD_SEPERATOR + fieldName;
                    }
                    if (LOG.isDebugEnabled()) {
                        LOG.debug("Adding field error [#0] with message '#1'", fieldName,
                                validationError.getMessage());
                    }
                    validatorContext.addFieldError(fieldName, validationError.getMessage());

                }
            }
        }
    }

    protected ValidationError buildBeanValidationError(ConstraintViolation<Object> violation, String message,
            Object action) {

        if (violation.getPropertyPath().iterator().next().getName() != null) {
            String fieldName = violation.getPropertyPath().toString();
            String finalMessage = StringUtils.removeStart(message, fieldName + ValidatorConstants.FIELD_SEPERATOR);
            return new ValidationError(fieldName, finalMessage);
        }

        return null;

    }

    /**
     * Decide if a violation should be added to the fieldErrors or actionErrors
     */
    protected boolean isActionError(ConstraintViolation<Object> violation) {

        if (violation.getLeafBean() == violation.getInvalidValue()) {
            return true;
        }
        return false;

        /* if(violation.getPropertyPath().iterator().next().getName() == null){
          return true;
        }
        return false;*/
    }

    /**
     * This is copied from DefaultActionInvocation
     */
    protected Method getActionMethod(Class actionClass, String methodName) throws NoSuchMethodException {
        Method method;

        try {
            method = actionClass.getMethod(methodName, new Class[0]);
        } catch (NoSuchMethodException e) {
            // hmm -- OK, try doXxx instead
            try {
                String altMethodName = "do" + methodName.substring(0, 1).toUpperCase() + methodName.substring(1);
                method = actionClass.getMethod(altMethodName, new Class[0]);
            } catch (NoSuchMethodException e1) {
                // throw the original one
                throw e;
            }
        }

        return method;
    }

    /**
     * Inner class for validation error
     * Nice concept taken from  Oval plugin. 
     */

    class ValidationError {
        private String fieldName;
        private String message;

        ValidationError(String fieldName, String message) {
            this.fieldName = fieldName;
            this.message = message;
        }

        public String getFieldName() {
            return this.fieldName;
        }

        public String getMessage() {
            return this.message;
        }
    }

}