Commits

Anonymous committed 719fb79

Enable per-method validations for annotations
Issue Number: XW-470
Submitted by: David Rupp

git-svn-id: http://svn.opensymphony.com/svn/xwork/branches/2.0@1359e221344d-f017-0410-9bd5-d282ab1896d7

Comments (0)

Files changed (5)

src/java/com/opensymphony/xwork2/validator/ActionValidatorManager.java

  */
 public interface ActionValidatorManager {
 
+
+    /**
+     * Returns a list of validators for the given class, context, and method. This is the primary
+     * lookup method for validators.
+     *
+     * @param clazz the class to lookup.
+     * @param context the context of the action class - can be <tt>null</tt>.
+     * @param method the name of the method being invoked on the action - can be <tt>null</tt>.
+     * @return a list of all validators for the given class and context.
+     */
+    List<Validator> getValidators(Class clazz, String context, String method);
+
     /**
      * Returns a list of validators for the given class and context. This is the primary
      * lookup method for validators.
      * @param context the context of the action class - can be <tt>null</tt>.
      * @return a list of all validators for the given class and context.
      */
-    List getValidators(Class clazz, String context);
+    List<Validator> getValidators(Class clazz, String context);
 
     /**
      * Validates the given object using action and its context.
      * @throws ValidationException if an error happens when validating the action.
      */
     void validate(Object object, String context, ValidatorContext validatorContext) throws ValidationException;
+
+    /**
+     * Validates the given object using an action, its context, and the name of the method being invoked on the action.
+     *
+     * @param object the action to validate.
+     * @param context the action's context.
+     * @param method the name of the method being invoked on the action - can be <tt>null</tt>.
+     * @throws ValidationException if an error happens when validating the action.
+     */
+    void validate(Object object, String context, String method) throws ValidationException;
+
+    /**
+     * Validates an action give its context and a validation context.
+     *
+     * @param object the action to validate.
+     * @param context the action's context.
+     * @param validatorContext
+     * @param method the name of the method being invoked on the action - can be <tt>null</tt>.
+     * @throws ValidationException if an error happens when validating the action.
+     */
+    void validate(Object object, String context, ValidatorContext validatorContext, String method) throws ValidationException;
 }

src/java/com/opensymphony/xwork2/validator/AnnotationActionValidatorManager.java

 import java.io.IOException;
 
 import com.opensymphony.xwork2.util.FileManager;
+import com.opensymphony.xwork2.ObjectFactory;
 
 /**
  * <code>AnnotationActionValidatorManager</code>
      * @return a list of all validators for the given class and context.
      */
     public synchronized List<Validator> getValidators(Class clazz, String context) {
+        return getValidators(clazz, context, null);
+    }
+
+    /**
+     * Returns a list of validators for the given class, context, and method. This is the primary
+     * lookup method for validators.
+     *
+     * @param clazz   the class to lookup.
+     * @param context the context of the action class - can be <tt>null</tt>.
+     * @param method  the name of the method being invoked on the action - can be <tt>null</tt>.
+     * @return a list of all validators for the given class and context.
+     */
+    public synchronized List<Validator> getValidators(Class clazz, String context, String method) {
         final String validatorKey = buildValidatorKey(clazz, context);
 
         if (validatorCache.containsKey(validatorKey)) {
         // create clean instances of the validators for the caller's use
         ArrayList<Validator> validators = new ArrayList<Validator>(cfgs.size());
         for (ValidatorConfig cfg : cfgs) {
-            Validator validator = ValidatorFactory.getValidator(cfg);
-            validator.setValidatorType(cfg.getType());
-            validators.add(validator);
+            if (method == null || method.equals(cfg.getParams().get("methodName"))) {
+                Validator validator = ValidatorFactory.getValidator(cfg, ObjectFactory.getObjectFactory());
+                validator.setValidatorType(cfg.getType());
+                validators.add(validator);
+            }
         }
 
         return validators;
      * @throws ValidationException if an error happens when validating the action.
      */
     public void validate(Object object, String context) throws ValidationException {
+        validate(object, context, (String) null);
+    }
+
+    /**
+     * Validates the given object using action, its context, and the name of the method being invoked on the action.
+     *
+     * @param object  the action to validate.
+     * @param context the action's context.
+     * @param method  the name of the method being invoked on the action.
+     * @throws ValidationException if an error happens when validating the action.
+     */
+    public void validate(Object object, String context, String method) throws ValidationException {
         ValidatorContext validatorContext = new DelegatingValidatorContext(object);
-        validate(object, context, validatorContext);
+        validate(object, context, validatorContext, method);
     }
 
     /**
      * @throws ValidationException if an error happens when validating the action.
      */
     public void validate(Object object, String context, ValidatorContext validatorContext) throws ValidationException {
-        List<Validator> validators = getValidators(object.getClass(), context);
+        validate(object, context, validatorContext, null);
+    }
+
+    /**
+     * Validates an action give its context, a validation context, and the name of the method being invoked on the action.
+     *
+     * @param object           the action to validate.
+     * @param context          the action's context.
+     * @param validatorContext
+     * @param method           the name of the method being invoked on the action.
+     * @throws ValidationException if an error happens when validating the action.
+     */
+    public void validate(Object object, String context, ValidatorContext validatorContext, String method) throws ValidationException {
+        List<Validator> validators = getValidators(object.getClass(), context, method);
         Set<String> shortcircuitedFields = null;
 
         for (final Validator validator: validators) {
                 validator.setValidatorContext(validatorContext);
 
                 if (LOG.isDebugEnabled()) {
-                    LOG.debug("Running validator: " + validator + " for object " + object);
+                    LOG.debug("Running validator: " + validator + " for object " + object + " and method " + method);
                 }
 
                 FieldValidator fValidator = null;
         }
 
         // look for validators for implemented interfaces
-        Class[] interfaces = clazz.getInterfaces();
-
-        for (Class anInterface1 : interfaces) {
+        for (Class anInterface1 : clazz.getInterfaces()) {
             if (checked.contains(anInterface1.getName())) {
                 continue;
             }

src/java/com/opensymphony/xwork2/validator/AnnotationValidationConfigurationBuilder.java

         List<ValidatorConfig> result = new ArrayList<ValidatorConfig>();
 
         String fieldName = null;
+        String methodName = null;
 
         Annotation[] annotations = null;
 
         if (o instanceof Method) {
             Method method = (Method) o;
             fieldName = resolvePropertyName(method);
+            methodName = method.getName();
 
             annotations = method.getAnnotations();
         }
 
                 }
             }
+            
+            if (methodName != null) {
+                for (ValidatorConfig vc : result) {
+                    vc.getParams().put("methodName", methodName);
+                }
+            }
         }
         return result;
     }

src/java/com/opensymphony/xwork2/validator/DefaultActionValidatorManager.java

 package com.opensymphony.xwork2.validator;
 
 import com.opensymphony.xwork2.util.FileManager;
+import com.opensymphony.xwork2.ObjectFactory;
 
 import org.apache.commons.logging.Log;
 import org.apache.commons.logging.LogFactory;
     /** The file suffix for any validation file. */
     protected static final String VALIDATION_CONFIG_SUFFIX = "-validation.xml";
 
-    private static final Map validatorCache = Collections.synchronizedMap(new HashMap());
-    private static final Map validatorFileCache = Collections.synchronizedMap(new HashMap());
+    private static final Map<String, List<ValidatorConfig>> validatorCache = Collections.synchronizedMap(new HashMap<String, List<ValidatorConfig>>());
+    private static final Map<String, List<ValidatorConfig>> validatorFileCache = Collections.synchronizedMap(new HashMap<String, List<ValidatorConfig>>());
+
     private static final Log LOG = LogFactory.getLog(DefaultActionValidatorManager.class);
 
     /**
      * @param context the context of the action class - can be <tt>null</tt>.
      * @return a list of all validators for the given class and context.
      */
-    public synchronized List getValidators(Class clazz, String context) {
+    public synchronized List<Validator> getValidators(Class clazz, String context) {
+        return getValidators(clazz, context, null);
+    }
+
+    /**
+     * Returns a list of validators for the given class, context, and method name. This is the primary
+     * lookup method for validators.
+     *
+     * @param clazz the class to lookup.
+     * @param context the context of the action class - can be <tt>null</tt>.
+     * @param method the name of the method being invoked on the action - can be <tt>null</tt>.
+     * @return a list of all validators for the given class and context.
+     */
+    public synchronized List<Validator> getValidators(Class clazz, String context, String method) {
         final String validatorKey = buildValidatorKey(clazz, context);
 
         if (validatorCache.containsKey(validatorKey)) {
         }
 
         // get the set of validator configs
-        List cfgs = (List) validatorCache.get(validatorKey);
+        List<ValidatorConfig> cfgs = validatorCache.get(validatorKey);
 
         // create clean instances of the validators for the caller's use
-        ArrayList validators = new ArrayList(cfgs.size());
-        for (Iterator iterator = cfgs.iterator(); iterator.hasNext(); ) {
-            ValidatorConfig cfg = (ValidatorConfig) iterator.next();
-            Validator validator = ValidatorFactory.getValidator(cfg);
-            validator.setValidatorType(cfg.getType());
-            validators.add(validator);
-        }
+        ArrayList<Validator> validators = new ArrayList<Validator>(cfgs.size());
+        for (ValidatorConfig cfg : cfgs) {
+            if (method == null || method.equals(cfg.getParams().get("methodName"))) {
+                Validator validator = ValidatorFactory.getValidator(cfg, ObjectFactory.getObjectFactory());
+                validator.setValidatorType(cfg.getType());
+                validators.add(validator);
+            }
+        }        
 
         return validators;
     }
      * @throws ValidationException if an error happens when validating the action.
      */
     public void validate(Object object, String context) throws ValidationException {
+        validate(object, context, (String) null);
+    }
+
+    /**
+     * Validates the given object using action, its context, and the name of the method being invoked on the action.
+     *
+     * @param object the action to validate.
+     * @param context the action's context.
+     * @param method the name of the method being invoked on the action.
+     * @throws ValidationException if an error happens when validating the action.
+     */
+    public void validate(Object object, String context, String method) throws ValidationException {
         ValidatorContext validatorContext = new DelegatingValidatorContext(object);
-        validate(object, context, validatorContext);
+        validate(object, context, validatorContext, method);
     }
 
     /**
      * @throws ValidationException if an error happens when validating the action.
      */
     public void validate(Object object, String context, ValidatorContext validatorContext) throws ValidationException {
-        List validators = getValidators(object.getClass(), context);
-        Set shortcircuitedFields = null;
+        validate(object, context, validatorContext, null);
+    }
 
-        for (Iterator iterator = validators.iterator(); iterator.hasNext();) {
-            final Validator validator = (Validator) iterator.next();
+    /**
+     * Validates an action give its context, a validation context, and the name of the method being invoked on the action.
+     *
+     * @param object the action to validate.
+     * @param context the action's context.
+     * @param validatorContext
+     * @param method the name of the method being invoked on the action.
+     * @throws ValidationException if an error happens when validating the action.
+     */
+    public void validate(Object object, String context, ValidatorContext validatorContext, String method) throws ValidationException {
+        List<Validator> validators = getValidators(object.getClass(), context, method);
+        Set<String> shortcircuitedFields = null;
+
+        for (final Validator validator : validators) {
             try {
                 validator.setValidatorContext(validatorContext);
 
                 if (LOG.isDebugEnabled()) {
-                    LOG.debug("Running validator: " + validator + " for object " + object);
+                    LOG.debug("Running validator: " + validator + " for object " + object + " and method " + method);
                 }
 
                 FieldValidator fValidator = null;
 
                 if (validator instanceof ShortCircuitableValidator && ((ShortCircuitableValidator) validator).isShortCircuit()) {
                     // get number of existing errors
-                    List errs = null;
+                    List<String> errs = null;
 
                     if (fValidator != null) {
                         if (validatorContext.hasFieldErrors()) {
-                            Collection fieldErrors = (Collection) validatorContext.getFieldErrors().get(fullFieldName);
+                            Collection<String> fieldErrors = (Collection<String>) validatorContext.getFieldErrors().get(fullFieldName);
 
                             if (fieldErrors != null) {
-                                errs = new ArrayList(fieldErrors);
+                                errs = new ArrayList<String>(fieldErrors);
                             }
                         }
                     } else if (validatorContext.hasActionErrors()) {
-                        Collection actionErrors = validatorContext.getActionErrors();
+                        Collection<String> actionErrors = validatorContext.getActionErrors();
 
                         if (actionErrors != null) {
-                            errs = new ArrayList(actionErrors);
+                            errs = new ArrayList<String>(actionErrors);
                         }
                     }
 
 
                     if (fValidator != null) {
                         if (validatorContext.hasFieldErrors()) {
-                            Collection errCol = (Collection) validatorContext.getFieldErrors().get(fullFieldName);
+                            Collection<String> errCol = (Collection<String>) validatorContext.getFieldErrors().get(fullFieldName);
 
                             if ((errCol != null) && !errCol.equals(errs)) {
                                 if (LOG.isDebugEnabled()) {
                                 }
 
                                 if (shortcircuitedFields == null) {
-                                    shortcircuitedFields = new TreeSet();
+                                    shortcircuitedFields = new TreeSet<String>();
                                 }
 
                                 shortcircuitedFields.add(fullFieldName);
                             }
                         }
                     } else if (validatorContext.hasActionErrors()) {
-                        Collection errCol = validatorContext.getActionErrors();
+                        Collection<String> errCol = validatorContext.getActionErrors();
 
                         if ((errCol != null) && !errCol.equals(errs)) {
                             if (LOG.isDebugEnabled()) {
         return sb.toString();
     }
 
-    private List buildAliasValidatorConfigs(Class aClass, String context, boolean checkFile) {
+    private List<ValidatorConfig> buildAliasValidatorConfigs(Class aClass, String context, boolean checkFile) {
         String fileName = aClass.getName().replace('.', '/') + "-" + context + VALIDATION_CONFIG_SUFFIX;
 
         return loadFile(fileName, aClass, checkFile);
     }
 
-    private List buildClassValidatorConfigs(Class aClass, boolean checkFile) {
+    private List<ValidatorConfig> buildClassValidatorConfigs(Class aClass, boolean checkFile) {
         String fileName = aClass.getName().replace('.', '/') + VALIDATION_CONFIG_SUFFIX;
 
         return loadFile(fileName, aClass, checkFile);
      * @param checked the set of previously checked class-contexts, null if none have been checked
      * @return a list of validator configs for the given class and context.
      */
-    private List buildValidatorConfigs(Class clazz, String context, boolean checkFile, Set checked) {
-        List validatorConfigs = new ArrayList();
+    private List<ValidatorConfig> buildValidatorConfigs(Class clazz, String context, boolean checkFile, Set checked) {
+        List<ValidatorConfig> validatorConfigs = new ArrayList<ValidatorConfig>();
 
         if (checked == null) {
             checked = new TreeSet();
         }
 
         if (clazz.isInterface()) {
-            Class[] interfaces = clazz.getInterfaces();
-
-            for (int x = 0; x < interfaces.length; x++) {
-                validatorConfigs.addAll(buildValidatorConfigs(interfaces[x], context, checkFile, checked));
+            for (Class anInterface : clazz.getInterfaces()) {
+                validatorConfigs.addAll(buildValidatorConfigs(anInterface, context, checkFile, checked));
             }
         } else {
             if (!clazz.equals(Object.class)) {
         }
 
         // look for validators for implemented interfaces
-        Class[] interfaces = clazz.getInterfaces();
-
-        for (int x = 0; x < interfaces.length; x++) {
-            if (checked.contains(interfaces[x].getName())) {
+        for (Class anInterface1 : clazz.getInterfaces()) {
+            if (checked.contains(anInterface1.getName())) {
                 continue;
             }
 
-            validatorConfigs.addAll(buildClassValidatorConfigs(interfaces[x], checkFile));
+            validatorConfigs.addAll(buildClassValidatorConfigs(anInterface1, checkFile));
 
             if (context != null) {
-                validatorConfigs.addAll(buildAliasValidatorConfigs(interfaces[x], context, checkFile));
+                validatorConfigs.addAll(buildAliasValidatorConfigs(anInterface1, context, checkFile));
             }
 
-            checked.add(interfaces[x].getName());
+            checked.add(anInterface1.getName());
         }
 
         validatorConfigs.addAll(buildClassValidatorConfigs(clazz, checkFile));
         return validatorConfigs;
     }
 
-    private List loadFile(String fileName, Class clazz, boolean checkFile) {
-        List retList = Collections.EMPTY_LIST;
+    private List<ValidatorConfig> loadFile(String fileName, Class clazz, boolean checkFile) {
+        List<ValidatorConfig> retList = Collections.emptyList();
 
         if ((checkFile && FileManager.fileNeedsReloading(fileName)) || !validatorFileCache.containsKey(fileName)) {
             InputStream is = null;
                 is = FileManager.loadFile(fileName, clazz);
 
                 if (is != null) {
-                    retList = new ArrayList(ValidatorFileParser.parseActionValidatorConfigs(is, fileName));
+                    retList = new ArrayList<ValidatorConfig>(ValidatorFileParser.parseActionValidatorConfigs(is, fileName));
                 }
             } finally {
                 if (is != null) {
 
             validatorFileCache.put(fileName, retList);
         } else {
-            retList = (List) validatorFileCache.get(fileName);
+            retList = validatorFileCache.get(fileName);
         }
 
         return retList;

src/java/com/opensymphony/xwork2/validator/ValidationInterceptor.java

  *     &lt;result name="success"&gt;good_result.ftl&lt;/result&gt;
  * &lt;/action&gt;
  * 
- * 
+ * &lt;-- in the following case only annotated methods of the action class will
+ *        be validated --&gt;
+ * &lt;action name="someAction" class="com.examples.SomeAction"&gt;
+ *     &lt;interceptor-ref name="params"/&gt;
+ *     &lt;interceptor-ref name="validation"&gt;
+ *         &lt;param name="validateAnnotatedMethodOnly"&gt;true&lt;/param&gt;
+ *     &lt;/interceptor-ref&gt;
+ *     &lt;interceptor-ref name="workflow"/&gt;
+ *     &lt;result name="success"&gt;good_result.ftl&lt;/result&gt;
+ * &lt;/action&gt;
+ *
+ *
  * <!-- END SNIPPET: example -->
  * </pre>
  *
  * @version $Date$ $Id$
  */
 public class ValidationInterceptor extends MethodFilterInterceptor {
+
+    private boolean validateAnnotatedMethodOnly;
+
+
+    public boolean isValidateAnnotatedMethodOnly() {
+        return validateAnnotatedMethodOnly;
+    }
+
+    public void setValidateAnnotatedMethodOnly(boolean validateAnnotatedMethodOnly) {
+        this.validateAnnotatedMethodOnly = validateAnnotatedMethodOnly;
+    }
+
     /**
      * Gets the current action and its context and calls {@link DefaultActionValidatorManager#validate(Object, String)}.
      *
     protected void doBeforeInvocation(ActionInvocation invocation) throws Exception {
         Object action = invocation.getAction();
         String context = invocation.getProxy().getActionName();
+        String method = invocation.getProxy().getMethod();
 
         if (log.isDebugEnabled()) {
             log.debug("Validating "
-                    + invocation.getProxy().getNamespace() + "/" + invocation.getProxy().getActionName() + " with method "+invocation.getProxy().getMethod()+".");
+                    + invocation.getProxy().getNamespace() + "/" + invocation.getProxy().getActionName() + " with method "+ method +".");
+
+        }
+
+        if (validateAnnotatedMethodOnly) {
+            ActionValidatorManagerFactory.getInstance().validate(action, context, method);
+        } else {
+            ActionValidatorManagerFactory.getInstance().validate(action, context);
         }
 
-        ActionValidatorManagerFactory.getInstance().validate(action, context);
     }
 
     protected String doIntercept(ActionInvocation invocation) throws Exception {