Anonymous avatar Anonymous committed 82b9da8

- Cleaned up exceptions with invalid configuration files
- Made XworkException show stack trace of nested throwable
- Made VisitorFieldValidator use message resource bundles for the objects being validated

git-svn-id: http://svn.opensymphony.com/svn/xwork/trunk@43 e221344d-f017-0410-9bd5-d282ab1896d7

Comments (0)

Files changed (10)

src/java/com/opensymphony/xwork/XworkException.java

  */
 package com.opensymphony.xwork;
 
+import java.io.PrintStream;
+import java.io.PrintWriter;
+
 
 /**
  * XworkException
     //~ Constructors ///////////////////////////////////////////////////////////
 
     /**
-     * Constructs a <code>XworkException</code> with no detail  message.
-     */
+ * Constructs a <code>XworkException</code> with no detail  message.
+ */
     public XworkException() {
     }
 
     /**
-     * Constructs a <code>XworkException</code> with the specified
-     * detail message.
-     *
-     * @param   s   the detail message.
-     */
+ * Constructs a <code>XworkException</code> with the specified
+ * detail message.
+ *
+ * @param   s   the detail message.
+ */
     public XworkException(String s) {
         super(s);
     }
 
     /**
-     * Constructs a <code>XworkException</code> with no detail  message.
-     */
+ * Constructs a <code>XworkException</code> with no detail  message.
+ */
     public XworkException(Throwable cause) {
         this.throwable = cause;
     }
 
     /**
-     * Constructs a <code>XworkException</code> with the specified
-     * detail message.
-     *
-     * @param   s   the detail message.
-     */
+ * Constructs a <code>XworkException</code> with the specified
+ * detail message.
+ *
+ * @param   s   the detail message.
+ */
     public XworkException(String s, Throwable cause) {
         super(s);
         this.throwable = cause;
     }
 
     /**
-     * Returns a short description of this throwable object.
-     * If this <code>Throwable</code> object was
-     * {@link #Throwable(String) created} with an error message string,
-     * then the result is the concatenation of three strings:
-     * <ul>
-     * <li>The name of the actual class of this object
-     * <li>": " (a colon and a space)
-     * <li>The result of the {@link #getMessage} method for this object
-     * </ul>
-     * If this <code>Throwable</code> object was {@link #Throwable() created}
-     * with no error message string, then the name of the actual class of
-     * this object is returned.
-     *
-     * @return  a string representation of this <code>Throwable</code>.
-     */
+ * Prints this <code>Throwable</code> and its backtrace to the
+ * specified print stream.
+ *
+ * @param s <code>PrintStream</code> to use for output
+ */
+    public void printStackTrace(PrintStream s) {
+        super.printStackTrace(s);
+
+        if (throwable != null) {
+            s.println("with nested exception " + throwable);
+            throwable.printStackTrace(s);
+        }
+    }
+
+    /**
+ * Prints this <code>Throwable</code> and its backtrace to the specified
+ * print writer.
+ *
+ * @param s <code>PrintWriter</code> to use for output
+ * @since   JDK1.1
+ */
+    public void printStackTrace(PrintWriter s) {
+        super.printStackTrace(s);
+
+        if (throwable != null) {
+            s.println("with nested exception " + throwable);
+            throwable.printStackTrace(s);
+        }
+    }
+
+    /**
+ * Returns a short description of this throwable object.
+ * If this <code>Throwable</code> object was
+ * {@link #Throwable(String) created} with an error message string,
+ * then the result is the concatenation of three strings:
+ * <ul>
+ * <li>The name of the actual class of this object
+ * <li>": " (a colon and a space)
+ * <li>The result of the {@link #getMessage} method for this object
+ * </ul>
+ * If this <code>Throwable</code> object was {@link #Throwable() created}
+ * with no error message string, then the name of the actual class of
+ * this object is returned.
+ *
+ * @return  a string representation of this <code>Throwable</code>.
+ */
     public String toString() {
         if (throwable == null) {
             return super.toString();

src/java/com/opensymphony/xwork/config/impl/DefaultConfiguration.java

             reload();
         } catch (ConfigurationException e) {
             String s = "Caught ConfigurationException while initializing ConfigurationProvider.";
-            LOG.error(s, e);
-            throw new RuntimeException(s);
+            LOG.fatal(s);
+            throw e;
         }
     }
 
     }
 
     /**
-    * This methodName builds the internal runtime configuration used by Xwork for finding and configuring Actions from the
-    * programmatic configuration data structures. All of the old runtime configuration will be discarded and rebuilt.
-    */
+* This methodName builds the internal runtime configuration used by Xwork for finding and configuring Actions from the
+* programmatic configuration data structures. All of the old runtime configuration will be discarded and rebuilt.
+*/
     public synchronized void buildRuntimeConfiguration() throws ConfigurationException {
         Map namespaceActionConfigs = new HashMap();
 
     }
 
     /**
-    * Allows the configuration to clean up any resources used
-    */
+* Allows the configuration to clean up any resources used
+*/
     public void destroy() {
     }
 
     /**
-    * Calls the ConfigurationProviderFactory.getConfig() to tell it to reload the configuration and then calls
-    * buildRuntimeConfiguration().
-    * @throws ConfigurationException
-    */
+* Calls the ConfigurationProviderFactory.getConfig() to tell it to reload the configuration and then calls
+* buildRuntimeConfiguration().
+* @throws ConfigurationException
+*/
     public synchronized void reload() throws ConfigurationException {
         packageContexts.clear();
 
     }
 
     /**
-    * Builds the full runtime actionconfig with all of the defaults and inheritance
-    * @param packageContext the PackageConfig which holds the base config we're building from
-    * @param baseConfig the ActionConfig which holds only the configuration specific to itself, without the defaults
-    * and inheritance
-    * @return a full ActionConfig for runtime configuration with all of the inherited and default params
-    */
+* Builds the full runtime actionconfig with all of the defaults and inheritance
+* @param packageContext the PackageConfig which holds the base config we're building from
+* @param baseConfig the ActionConfig which holds only the configuration specific to itself, without the defaults
+* and inheritance
+* @return a full ActionConfig for runtime configuration with all of the inherited and default params
+*/
     private ActionConfig buildFullActionConfig(PackageConfig packageContext, ActionConfig baseConfig) throws ConfigurationException {
         Map params = new HashMap(baseConfig.getParams());
         Map results = new HashMap(packageContext.getAllGlobalResults());
         }
 
         /**
-        * Gets the configuration information for an action name, or returns null if the
-        * name is not recognized.
-        *
-        * @param name the name of the action
-        * @param namespace the namespace for the action or null for the empty namespace, ""
-        * @return the configuration information for action requested
-        */
+* Gets the configuration information for an action name, or returns null if the
+* name is not recognized.
+*
+* @param name the name of the action
+* @param namespace the namespace for the action or null for the empty namespace, ""
+* @return the configuration information for action requested
+*/
         public synchronized ActionConfig getActionConfig(String namespace, String name) {
             ActionConfig config = null;
             Map actions = (Map) namespaceActionConfigs.get((namespace == null) ? "" : namespace);
         }
 
         /**
-        * Gets the configuration settings for every action.
-        *
-        * @return a Map of namespace - > Map of ActionConfig objects, with the key being the action name
-        */
+* Gets the configuration settings for every action.
+*
+* @return a Map of namespace - > Map of ActionConfig objects, with the key being the action name
+*/
         public synchronized Map getActionConfigs() {
             return namespaceActionConfigs;
         }

src/java/com/opensymphony/xwork/validator/DelegatingValidatorContext.java

 
     //~ Constructors ///////////////////////////////////////////////////////////
 
-    public DelegatingValidatorContext(Object object) {
-        if (object instanceof ValidationAware) {
-            validationAware = (ValidationAware) object;
-        } else {
-            validationAware = new LoggingValidationAware(object);
-        }
+    public DelegatingValidatorContext(ValidationAware validationAware, LocaleAware localeAware) {
+        this.localeAware = localeAware;
+        this.validationAware = validationAware;
+    }
 
-        if (object instanceof LocaleAware) {
-            localeAware = (LocaleAware) object;
-        } else {
-            localeAware = new LocaleAwareSupport(object.getClass());
-        }
+    public DelegatingValidatorContext(Object object) {
+        this(makeValidationAware(object), makeLocaleAware(object));
     }
 
     //~ Methods ////////////////////////////////////////////////////////////////
         return validationAware.hasFieldErrors();
     }
 
+    protected static LocaleAware makeLocaleAware(Object object) {
+        if (object instanceof LocaleAware) {
+            return (LocaleAware) object;
+        } else {
+            return new LocaleAwareSupport(object.getClass());
+        }
+    }
+
+    protected static ValidationAware makeValidationAware(Object object) {
+        if (object instanceof ValidationAware) {
+            return (ValidationAware) object;
+        } else {
+            return new LoggingValidationAware(object);
+        }
+    }
+
+    protected void setLocaleAware(LocaleAware localeAware) {
+        this.localeAware = localeAware;
+    }
+
+    protected LocaleAware getLocaleAware() {
+        return localeAware;
+    }
+
+    protected void setValidationAware(ValidationAware validationAware) {
+        this.validationAware = validationAware;
+    }
+
+    protected ValidationAware getValidationAware() {
+        return validationAware;
+    }
+
     //~ Inner Classes //////////////////////////////////////////////////////////
 
-    private class LoggingValidationAware implements ValidationAware {
+    private static class LoggingValidationAware implements ValidationAware {
         private Log log;
 
         public LoggingValidationAware(Object obj) {

src/java/com/opensymphony/xwork/validator/validators/ExpressionValidator.java

     public void validate(Object object) throws ValidationException {
         Boolean answer = Boolean.FALSE;
         OgnlValueStack stack = ActionContext.getContext().getValueStack();
-        Object obj = stack.findValue(expression);
+        Object obj = null;
+
+        try {
+            obj = stack.findValue(expression);
+        } catch (Exception e) {
+            log.warn("Caught exception while evaluating expression " + expression, e);
+        }
 
         if ((obj != null) && (obj instanceof Boolean)) {
             answer = (Boolean) obj;

src/java/com/opensymphony/xwork/validator/validators/ValidatorSupport.java

     public String getMessage(Object object) {
         String message;
 
+        OgnlValueStack stack = ActionContext.getContext().getValueStack();
+        stack.push(this);
+
         if (messageKey != null) {
             message = validatorContext.getText(messageKey, defaultMessage);
         } else {
             message = defaultMessage;
+            message = TextParseUtil.translateVariables(message, stack);
         }
 
-        OgnlValueStack stack = ActionContext.getContext().getValueStack();
-        stack.push(this);
-        message = TextParseUtil.translateVariables(message, stack);
         stack.pop();
 
         return message;

src/java/com/opensymphony/xwork/validator/validators/VisitorFieldValidator.java

 package com.opensymphony.xwork.validator.validators;
 
 import com.opensymphony.xwork.ActionContext;
+import com.opensymphony.xwork.util.OgnlValueStack;
 import com.opensymphony.xwork.validator.ActionValidatorManager;
 import com.opensymphony.xwork.validator.DelegatingValidatorContext;
 import com.opensymphony.xwork.validator.ValidationException;
 
             for (Iterator iterator = coll.iterator(); iterator.hasNext();) {
                 Object o = iterator.next();
-                ValidatorContext validatorContext = new AppendingValidatorContext(getValidatorContext(), fieldName, getMessage(o));
-                ActionValidatorManager.validate(o, visitorContext, validatorContext);
+                validateObject(fieldName, o, visitorContext);
             }
         } else if (value instanceof Object[]) {
             Object[] array = (Object[]) value;
 
             for (int i = 0; i < array.length; i++) {
                 Object o = array[i];
-                ValidatorContext validatorContext = new AppendingValidatorContext(getValidatorContext(), fieldName, getMessage(o));
-                ActionValidatorManager.validate(o, visitorContext, validatorContext);
+                validateObject(fieldName, o, visitorContext);
             }
         } else {
-            ValidatorContext validatorContext = new AppendingValidatorContext(getValidatorContext(), fieldName, getMessage(value));
-            ActionValidatorManager.validate(value, visitorContext, validatorContext);
+            validateObject(fieldName, value, visitorContext);
         }
     }
 
+    private void validateObject(String fieldName, Object o, String visitorContext) throws ValidationException {
+        OgnlValueStack stack = ActionContext.getContext().getValueStack();
+        stack.push(o);
+
+        ValidatorContext validatorContext = new AppendingValidatorContext(getValidatorContext(), o, fieldName, getMessage(o));
+        ActionValidatorManager.validate(o, visitorContext, validatorContext);
+        stack.pop();
+    }
+
     //~ Inner Classes //////////////////////////////////////////////////////////
 
     private class AppendingValidatorContext extends DelegatingValidatorContext {
+        Object o;
         String field;
         String message;
 
-        public AppendingValidatorContext(Object object, String field, String message) {
-            super(object);
+        public AppendingValidatorContext(ValidatorContext parent, Object object, String field, String message) {
+            super(parent, makeLocaleAware(object));
+
+            //            super(parent);
             this.field = field;
             this.message = message;
         }

src/test/com/opensymphony/xwork/TestBean-beanMessageBundle-validation.xml

+<!DOCTYPE validators PUBLIC "-//OpenSymphony Group//XWork Validator 1.0//EN" "http://www.opensymphony.com/xwork/xwork-validator-1.0.dtd">
+<validators>
+    <field name="count">
+        <field-validator type="int">
+            <param name="min">1</param>
+            <param name="max">100</param>
+            <message key="invalid.count">Invalid Count!</message>
+        </field-validator>
+    </field>
+</validators>

src/test/com/opensymphony/xwork/TestBean.properties

+invalid.count=Count must be between ${min} and ${max}, current value is ${count}.

src/test/com/opensymphony/xwork/VisitorValidatorTestAction-beanMessageBundle-validation.xml

+<!DOCTYPE validators PUBLIC "-//OpenSymphony Group//XWork Validator 1.0//EN" "http://www.opensymphony.com/xwork/xwork-validator-1.0.dtd">
+<validators>
+    <field name="bean">
+        <field-validator type="visitor">
+            <message>bean: </message>
+        </field-validator>
+    </field>
+</validators>

src/test/com/opensymphony/xwork/validator/VisitorFieldValidatorTest.java

 import com.opensymphony.xwork.ActionContext;
 import com.opensymphony.xwork.TestBean;
 import com.opensymphony.xwork.VisitorValidatorTestAction;
+import com.opensymphony.xwork.util.OgnlValueStack;
 
 import junit.framework.TestCase;
 
         Calendar cal = new GregorianCalendar(1900, 01, 01);
         bean.setBirth(cal.getTime());
         bean.setCount(-1);
+
+        OgnlValueStack stack = new OgnlValueStack();
+        ActionContext.setContext(new ActionContext(stack.getContext()));
     }
 
     public void testArrayValidation() throws Exception {
         TestBean[] beanArray = action.getTestBeanArray();
         TestBean testBean = beanArray[0];
         testBean.setName("foo");
-        ActionValidatorManager.validate(action, "validateArray");
+        validate("validateArray");
 
         assertTrue(action.hasFieldErrors());
 
         assertEquals(4, errors.size());
     }
 
+    public void testBeanMessagesUseBeanResourceBundle() throws Exception {
+        validate("beanMessageBundle");
+        assertTrue(action.hasFieldErrors());
+
+        Map fieldErrors = action.getFieldErrors();
+        assertTrue(fieldErrors.containsKey("bean.count"));
+
+        List beanCountMessages = (List) fieldErrors.get("bean.count");
+        assertEquals(1, beanCountMessages.size());
+
+        String beanCountMessage = (String) beanCountMessages.get(0);
+        assertEquals("bean: Count must be between 1 and 100, current value is -1.", beanCountMessage);
+    }
+
     public void testCollectionValidation() throws Exception {
         List testBeanList = action.getTestBeanList();
         TestBean testBean = (TestBean) testBeanList.get(0);
         testBean.setName("foo");
-        ActionValidatorManager.validate(action, "validateList");
+        validate("validateList");
 
         assertTrue(action.hasFieldErrors());
 
     }
 
     public void testContextIsOverriddenByContextParamInValidationXML() throws Exception {
-        ActionValidatorManager.validate(action, "visitorValidationAlias");
+        validate("visitorValidationAlias");
         assertTrue(action.hasFieldErrors());
 
         Map fieldErrors = action.getFieldErrors();
     }
 
     public void testContextIsPropagated() throws Exception {
-        ActionContext.getContext().setName("visitorValidation");
-        ActionValidatorManager.validate(action, "visitorValidation");
+        validate("visitorValidation");
         assertTrue(action.hasFieldErrors());
 
         Map fieldErrors = action.getFieldErrors();
         //the error from the action should be there too
         assertTrue(fieldErrors.containsKey("context"));
     }
+
+    protected void tearDown() throws Exception {
+        super.tearDown();
+        ActionContext.setContext(null);
+    }
+
+    private void validate(String context) throws ValidationException {
+        ActionContext actionContext = ActionContext.getContext();
+        actionContext.setName(context);
+        ActionValidatorManager.validate(action, context);
+    }
 }
Tip: Filter by directory path e.g. /media app.js to search for public/media/app.js.
Tip: Use camelCasing e.g. ProjME to search for ProjectModifiedEvent.java.
Tip: Filter by extension type e.g. /repo .js to search for all .js files in the /repo directory.
Tip: Separate your search with spaces e.g. /ssh pom.xml to search for src/ssh/pom.xml.
Tip: Use ↑ and ↓ arrow keys to navigate and return to view the file.
Tip: You can also navigate files with Ctrl+j (next) and Ctrl+k (previous) and view the file with Ctrl+o.
Tip: You can also navigate files with Alt+j (next) and Alt+k (previous) and view the file with Alt+o.