Commits

Anonymous committed 65bfb32

Starting to apply new Location information to configuration and validation exception messages,
changed validation to use new validating parser, fixed validation test case that had wrong dtd,
converted XWork exceptions to use Java 1.4 Throwable constructors to give better errors.

Issue number: XW-379

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

Comments (0)

Files changed (11)

src/java/com/opensymphony/xwork/DefaultActionInvocation.java

                 return ObjectFactory.getObjectFactory().buildResult(resultConfig, invocationContext.getContextMap());
             } catch (Exception e) {
                 LOG.error("There was an exception while instantiating the result of type " + resultConfig.getClassName(), e);
-                throw e;
+                throw new XworkException(e, resultConfig);
             }
         } else {
             return null;
         try {
             action = ObjectFactory.getObjectFactory().buildAction(proxy.getActionName(), proxy.getNamespace(), proxy.getConfig(), contextMap);
         } catch (InstantiationException e) {
-            throw new XworkException("Unable to intantiate Action!", e);
+            throw new XworkException("Unable to intantiate Action!", e, proxy.getConfig());
         } catch (IllegalAccessException e) {
-            throw new XworkException("Illegal access to constructor, is it public?", e);
+            throw new XworkException("Illegal access to constructor, is it public?", e, proxy.getConfig());
         } catch (Exception e) {
             String gripe = "";
 
             }
 
             gripe += (((" -- " + e.getMessage()) != null) ? e.getMessage() : " [no message in exception]");
-            throw new XworkException(gripe, e);
+            throw new XworkException(gripe, e, proxy.getConfig());
         }
 
         prepareContinuation();

src/java/com/opensymphony/xwork/ObjectFactory.java

             message = "Could not load class " + interceptorClassName + ". Perhaps it exists but certain dependencies are not available?";
         }
 
-        throw new ConfigurationException(message, cause);
+        throw new ConfigurationException(message, cause, interceptorConfig);
     }
 
     /**

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

 import java.io.PrintStream;
 import java.io.PrintWriter;
 
+import com.opensymphony.xwork.util.location.Locatable;
+import com.opensymphony.xwork.util.location.Location;
+import com.opensymphony.xwork.util.location.LocationUtils;
+
 
 /**
  * XworkException
  * @author Jason Carreira
  *         Created Sep 7, 2003 12:15:03 AM
  */
-public class XworkException extends RuntimeException {
+public class XworkException extends RuntimeException implements Locatable {
 
-    Throwable throwable;
+    private Location location;
 
 
     /**
      * @param s the detail message.
      */
     public XworkException(String s) {
-        super(s);
+        this(s, null, null);
+    }
+    
+    /**
+     * Constructs a <code>XworkException</code> with the specified
+     * detail message and location.
+     *
+     * @param s the detail message.
+     */
+    public XworkException(String s, Object target) {
+        this(s, (Throwable) null, target);
     }
 
     /**
      * Constructs a <code>XworkException</code> with no detail  message.
      */
     public XworkException(Throwable cause) {
-        this.throwable = cause;
+        this(null, cause, null);
+    }
+    
+    /**
+     * Constructs a <code>XworkException</code> with no detail  message.
+     */
+    public XworkException(Throwable cause, Object target) {
+        this(null, cause, target);
     }
 
     /**
      * @param s the detail message.
      */
     public XworkException(String s, Throwable cause) {
-        super(s);
-        this.throwable = cause;
-    }
-
-
-    public Throwable getThrowable() {
-        return throwable;
+        this(s, cause, null);
     }
-
-    /**
-     * Prints this <code>Throwable</code> and its backtrace to the
-     * specified print stream.
+    
+    
+     /**
+     * Constructs a <code>XworkException</code> with the specified
+     * detail message.
      *
-     * @param s <code>PrintStream</code> to use for output
+     * @param s the detail message.
      */
-    public void printStackTrace(PrintStream s) {
-        super.printStackTrace(s);
-
-        if (throwable != null) {
-            s.println("with nested exception " + throwable);
-            throwable.printStackTrace(s);
+    public XworkException(String s, Throwable cause, Object target) {
+        super(s, cause);
+        
+        this.location = LocationUtils.getLocation(target);
+        if (this.location == Location.UNKNOWN) {
+            this.location = LocationUtils.getLocation(cause);
         }
     }
 
-    /**
-     * 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);
-        }
+    public Throwable getThrowable() {
+        return getCause();
+    }
+    
+    public Location getLocation() {
+        return this.location;
     }
 
     /**
      * @return a string representation of this <code>Throwable</code>.
      */
     public String toString() {
-        if (throwable == null) {
-            return super.toString();
+        String msg = getMessage();
+        if (msg == null && getCause() != null) {
+            msg = getCause().getMessage();
         }
-
-        return super.toString() + "\n    with nested exception \n" + throwable.toString();
+        return msg + " - " + location.toString();
     }
 }

src/java/com/opensymphony/xwork/config/ConfigurationException.java

 package com.opensymphony.xwork.config;
 
 import com.opensymphony.xwork.XworkException;
+import com.opensymphony.xwork.util.location.Location;
 
 
 /**
     public ConfigurationException(String s) {
         super(s);
     }
+    
+    /**
+     * Constructs a <code>ConfigurationException</code> with the specified
+     * detail message.
+     *
+     * @param s the detail message.
+     */
+    public ConfigurationException(String s, Object target) {
+        super(s, target);
+    }
 
     /**
      * Constructs a <code>ConfigurationException</code> with no detail  message.
     public ConfigurationException(Throwable cause) {
         super(cause);
     }
+    
+    /**
+     * Constructs a <code>ConfigurationException</code> with no detail  message.
+     */
+    public ConfigurationException(Throwable cause, Object target) {
+        super(cause, target);
+    }
 
     /**
      * Constructs a <code>ConfigurationException</code> with the specified
     public ConfigurationException(String s, Throwable cause) {
         super(s, cause);
     }
+    
+    /**
+     * Constructs a <code>ConfigurationException</code> with the specified
+     * detail message.
+     *
+     * @param s the detail message.
+     */
+    public ConfigurationException(String s, Throwable cause, Object target) {
+        super(s, cause, target);
+    }
 }

src/java/com/opensymphony/xwork/config/entities/ActionConfig.java

     }
 
     public String toString() {
-        return "{ActionConfig " + className + ((methodName != null) ? ("." + methodName + "()") : "") + "}";
+        StringBuffer sb = new StringBuffer();
+        sb.append("{ActionConfig ");
+        sb.append(className);
+        if (methodName != null) {
+            sb.append(".").append(methodName).append("()");
+        } 
+        sb.append(" - ").append(location);
+        sb.append("}");
+        return sb.toString();
     }
 }

src/java/com/opensymphony/xwork/config/providers/XmlConfigurationProvider.java

 
 
         try {
-            loadConfigurationFile(configFileName);
+            loadConfigurationFile(configFileName, null);
         } catch (Exception e) {
             LOG.fatal("Could not load XWork configuration file, failing", e);
             throw new ConfigurationException("Error loading configuration file " + configFileName, e);
         try {
             results = buildResults(actionElement, packageContext);
         } catch (ConfigurationException e) {
-            throw new ConfigurationException("Error building results for action " + name + " in namespace " + packageContext.getNamespace(), e);
+            throw new ConfigurationException("Error building results for action " + name + " in namespace " + packageContext.getNamespace(), e, actionElement);
         }
 
         List interceptorList = buildInterceptorList(actionElement, packageContext);
                 //TODO this should be localized
                 String msg = "Could not find External Reference Resolver: " + externalReferenceResolver + ". " + e.getMessage();
                 LOG.error(msg);
-                throw new ConfigurationException(msg, e);
+                throw new ConfigurationException(msg, e, packageElement);
             } catch (Exception e) {
                 //TODO this should be localized
                 String msg = "Could not create External Reference Resolver: " + externalReferenceResolver + ". " + e.getMessage();
                 LOG.error(msg);
-                throw new ConfigurationException(msg, e);
+                throw new ConfigurationException(msg, e, packageElement);
             }
         }
 
                 ResultTypeConfig config = (ResultTypeConfig) packageContext.getAllResultTypeConfigs().get(resultType);
 
                 if (config == null) {
-                    throw new ConfigurationException("There is no result type defined for type '" + resultType + "' mapped with name '" + resultName + "'");
+                    throw new ConfigurationException("There is no result type defined for type '" + resultType + "' mapped with name '" + resultName + "'", resultElement);
                 }
 
                 Class resultClass = config.getClazz();
     //            addPackage(packageElement);
     //        }
     //    }
-    private void loadConfigurationFile(String fileName) {
+    private void loadConfigurationFile(String fileName, Element includeElement) {
         if (!includedFileNames.contains(fileName)) {
             if (LOG.isDebugEnabled()) {
                 LOG.debug("Loading xwork configuration from: " + fileName);
 
                 InputSource in = new InputSource(is);
                 in.setSystemId(fileName);
+                Map dtdMappings = new HashMap();
+                dtdMappings.put("-//OpenSymphony Group//XWork 1.1.1//EN", "xwork-1.1.1.dtd");
+                dtdMappings.put("-//OpenSymphony Group//XWork 1.1//EN", "xwork-1.1.dtd");
+                dtdMappings.put("-//OpenSymphony Group//XWork 1.0//EN", "xwork-1.0.dtd");
                 
-                doc = DomHelper.parse(in);
+                doc = DomHelper.parse(in, dtdMappings);
             } catch (Exception e) {
                 final String s = "Caught exception while loading file " + fileName;
                 LOG.error(s, e);
-                throw new ConfigurationException(s, e);
+                throw new ConfigurationException(s, e, includeElement);
             } finally {
                 if (is != null) {
                     try {
                         addPackage(child);
                     } else if (nodeName.equals("include")) {
                         String includeFileName = child.getAttribute("file");
-                        loadConfigurationFile(includeFileName);
+                        loadConfigurationFile(includeFileName, child);
                     }
                 }
             }

src/java/com/opensymphony/xwork/util/DomHelper.java

 package com.opensymphony.xwork.util;
 
 import java.io.IOException;
+import java.util.Map;
 
 import com.opensymphony.util.ClassLoaderUtil;
 
         return LocationAttributes.getLocation(element);
     }
 
+    
+    /**
+     * Creates a W3C Document that remembers the location of each element in
+     * the source file. The location of element nodes can then be retrieved
+     * using the {@link #getLocation(Element)} method.
+     *
+     * @param inputSource the inputSource to read the document from
+     */
+    public static Document parse(InputSource inputSource) 
+        throws SAXException, SAXNotSupportedException, IOException {
+        return parse(inputSource, null);
+    }
+    
+    
     /**
      * Creates a W3C Document that remembers the location of each element in
      * the source file. The location of element nodes can then be retrieved
      *
      * @param inputSource the inputSource to read the document from
      */
-    public static Document parse(InputSource inputSource)
+    public static Document parse(InputSource inputSource, Map dtdMappings)
             throws SAXException, SAXNotSupportedException, IOException {
                 
         SAXParserFactory factory = SAXParserFactory.newInstance();
         // Enhance the sax stream with location information
         ContentHandler locationHandler = new LocationAttributes.Pipe(builder);
         
-        parser.parse(inputSource, new StartHandler(locationHandler));
+        try {
+            parser.parse(inputSource, new StartHandler(locationHandler, dtdMappings));
+        } catch (Exception ex) {
+            throw new XworkException(ex);
+        }
         
         return builder.getDocument();
     }
     public static class StartHandler extends DefaultHandler {
         
         private ContentHandler nextHandler;
+        private Map dtdMappings;
         
         /**
          * Create a filter that is chained to another handler.
          * @param next the next handler in the chain.
          */
-        public StartHandler(ContentHandler next) {
+        public StartHandler(ContentHandler next, Map dtdMappings) {
             nextHandler = next;
+            this.dtdMappings = dtdMappings;
         }
 
         public void setDocumentLocator(Locator locator) {
         }
         
         public InputSource resolveEntity(String publicId, String systemId) {
-            if ("-//OpenSymphony Group//XWork 1.1.1//EN".equals(publicId)) {
-                return new InputSource(ClassLoaderUtil.getResourceAsStream("xwork-1.1.1.dtd", DomHelper.class));
-            }
-            else if ("-//OpenSymphony Group//XWork 1.1//EN".equals(publicId)) {
-                return new InputSource(ClassLoaderUtil.getResourceAsStream("xwork-1.1.dtd", DomHelper.class));
+            if (dtdMappings != null && dtdMappings.containsKey(publicId)) {
+                String val = dtdMappings.get(publicId).toString();
+                return new InputSource(ClassLoaderUtil.getResourceAsStream(val, DomHelper.class));
             }
-            else if ("-//OpenSymphony Group//XWork 1.0//EN".equals(publicId)) {
-                return new InputSource(ClassLoaderUtil.getResourceAsStream("xwork-1.0.dtd", DomHelper.class));
-            }
-    
             return null;
         }
         
         }
 
         public void error(SAXParseException exception) throws SAXException {
-            LOG.error(exception.getMessage() + " at (" + exception.getLineNumber() + ":" + exception.getColumnNumber() + ")");
+            LOG.error(exception.getMessage() + " at (" + exception.getPublicId() + ":" + 
+                exception.getLineNumber() + ":" + exception.getColumnNumber() + ")");
             throw exception;
         }
 
         public void fatalError(SAXParseException exception) throws SAXException {
-            LOG.fatal(exception.getMessage() + " at (" + exception.getLineNumber() + ":" + exception.getColumnNumber() + ")");
+            LOG.fatal(exception.getMessage() + " at (" + exception.getPublicId() + ":" + 
+                exception.getLineNumber() + ":" + exception.getColumnNumber() + ")");
             throw exception;
         }
     }

src/java/com/opensymphony/xwork/util/location/LocationUtils.java

 import org.xml.sax.Locator;
 import org.xml.sax.SAXParseException;
 
+import org.w3c.dom.Element;
+
 /**
  * Location-related utility methods.
  */
                 return Location.UNKNOWN;
             }
         }
+        
+        if (obj instanceof Element) {
+            return LocationAttributes.getLocation((Element)obj);
+        }
 
         List currentFinders = finders; // Keep the current list
         int size = currentFinders.size();

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

 package com.opensymphony.xwork.validator;
 
 import com.opensymphony.xwork.ObjectFactory;
+import com.opensymphony.xwork.util.DomHelper;
 import org.apache.commons.logging.Log;
 import org.apache.commons.logging.LogFactory;
 import org.w3c.dom.*;
         Document doc = null;
 
         try {
-            DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
-            dbf.setValidating(true);
-            dbf.setNamespaceAware(true);
-
-            DocumentBuilder builder = dbf.newDocumentBuilder();
-            builder.setEntityResolver(new EntityResolver() {
-                public InputSource resolveEntity(String publicId, String systemId) {
-                    ClassLoader loader = Thread.currentThread().getContextClassLoader();
-
-                    if ("-//OpenSymphony Group//XWork Validator 1.0//EN".equals(publicId)) {
-                        return new InputSource(loader.getResourceAsStream("xwork-validator-1.0.dtd"));
-                    } else if ("-//OpenSymphony Group//XWork Validator 1.0.2//EN".equals(publicId)) {
-                        return new InputSource(loader.getResourceAsStream("xwork-validator-1.0.2.dtd"));
-                    }
-
-                    return null;
-                }
-            });
-            builder.setErrorHandler(new ErrorHandler() {
-                public void warning(SAXParseException exception) {
-                    log.warn(exception.getMessage() + " at (" + exception.getLineNumber() + ":" + exception.getColumnNumber() + " of '" + resourceName + "')");
-                }
-
-                public void error(SAXParseException exception) {
-                    log.error(exception.getMessage() + " at (" + exception.getLineNumber() + ":" + exception.getColumnNumber() + " of '" + resourceName + "')");
-                }
-
-                public void fatalError(SAXParseException exception) {
-                    log.fatal(exception.getMessage() + " at (" + exception.getLineNumber() + ":" + exception.getColumnNumber() + " of '" + resourceName + "')");
-                }
-            });
-            doc = builder.parse(is);
+            InputSource in = new InputSource(is);
+            in.setSystemId(resourceName);
+                
+            Map dtdMappings = new HashMap();
+            dtdMappings.put("-//OpenSymphony Group//XWork Validator 1.0//EN", "xwork-validator-1.0.dtd");
+            dtdMappings.put("-//OpenSymphony Group//XWork Validator 1.0.2//EN", "xwork-validator-1.0.2.dtd");
+            
+            doc = DomHelper.parse(in, dtdMappings);
         } catch (Exception e) {
             log.fatal("Caught exception while attempting to load validation configuration file '" + resourceName + "'.", e);
         }

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">
+<!DOCTYPE validators PUBLIC "-//OpenSymphony Group//XWork Validator 1.0.2//EN" "http://www.opensymphony.com/xwork/xwork-validator-1.0.2.dtd">
 <validators>
     <field name="count">
         <field-validator type="int" short-circuit="true">

src/test/com/opensymphony/xwork/config/entities/ActionConfigTest.java

+/*
+ * Copyright (c) 2002-2003 by OpenSymphony
+ * All rights reserved.
+ */
+package com.opensymphony.xwork.config.entities;
+
+import com.opensymphony.xwork.util.location.LocationImpl;
+import com.opensymphony.xwork.XWorkTestCase;
+
+import java.util.HashMap;
+
+
+/**
+ * ActionConfigTest
+ */
+public class ActionConfigTest extends XWorkTestCase {
+
+    public void testToString() {
+        ActionConfig cfg = new ActionConfig();
+        cfg.setClassName("foo.Bar");
+        cfg.setMethodName("execute");
+        cfg.setLocation(new LocationImpl(null, "foo/xwork.xml", 10, 12));
+        
+        assertTrue("Wrong toString(): "+cfg.toString(), 
+            "{ActionConfig foo.Bar.execute() - foo/xwork.xml:10:12}".equals(cfg.toString()));
+    }
+    
+    public void testToStringWithNoMethod() {
+        ActionConfig cfg = new ActionConfig();
+        cfg.setClassName("foo.Bar");
+        cfg.setLocation(new LocationImpl(null, "foo/xwork.xml", 10, 12));
+        
+        assertTrue("Wrong toString(): "+cfg.toString(), 
+            "{ActionConfig foo.Bar - foo/xwork.xml:10:12}".equals(cfg.toString()));
+    }
+}