Anonymous avatar Anonymous committed fb2aba2

Added ability to load multiple instances of the xwork configuration file,
added a few methods from oscore to satisfy Struts

XW-414

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

Comments (0)

Files changed (8)

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

 
 
     private Configuration configuration;
-    private Set includedFileNames = new TreeSet();
+    private Set<String> includedFileNames = new TreeSet<String>();
     private String configFileName;
     private boolean errorIfMissing;
     private Map<String,String> dtdMappings;
 
 
         try {
-            loadConfigurationFile(configFileName, null);
+            loadConfigurationFiles(configFileName, null);
         } catch (ConfigurationException e) {
             throw e;
         } catch (Exception e) {
         return needsReload;
     }
 
-    protected InputStream getInputStream(String fileName) {
-        return FileManager.loadFile(fileName, this.getClass());
-    }
-
     protected void addAction(Element actionElement, PackageConfig packageContext) throws ConfigurationException {
         String name = actionElement.getAttribute("name");
         String className = actionElement.getAttribute("class");
                     // now check if there is a result type now
                     if (!TextUtils.stringSet(resultType)) {
                         // uh-oh, we have a problem
-                        throw new ConfigurationException("No result type specified for result named '" + resultName + "', perhaps the parent package does not specify the result type?");
+                        throw new ConfigurationException("No result type specified for result named '" 
+                                + resultName + "', perhaps the parent package does not specify the result type?", resultElement);
                     }
                 }
 
     //            addPackage(packageElement);
     //        }
     //    }
-    private void loadConfigurationFile(String fileName, Element includeElement) {
+    private void loadConfigurationFiles(String fileName, Element includeElement) {
         if (!includedFileNames.contains(fileName)) {
             if (LOG.isDebugEnabled()) {
-                LOG.debug("Loading action configuration from: " + fileName);
+                LOG.debug("Loading action configurations from: " + fileName);
             }
 
             includedFileNames.add(fileName);
 
+            Iterator<URL> urls = null;
             Document doc = null;
             InputStream is = null;
 
+            IOException ioException = null;
             try {
-                is = getInputStream(fileName);
-
-                if (is == null) {
-                    if (errorIfMissing) {
-                        throw new Exception("Could not open file " + fileName);
-                    } else {
-                        LOG.info("Unable to locate configuration file "
-                                +fileName+", skipping");
-                        return;
-                    }
-                }
-
-                InputSource in = new InputSource(is);
-
-                //FIXME: we shouldn't be doing this lookup twice
-                URL url = ClassLoaderUtil.getResource(fileName, getClass());
-                if (url != null) {
-                    in.setSystemId(url.toString());
-                } else {
-                    LOG.info("Successfully located, but unable to determine system id for "+fileName);
-                }
-
-                doc = DomHelper.parse(in, dtdMappings);
-            } catch (XWorkException e) {
-                if (includeElement != null) {
-                    throw new ConfigurationException(e, includeElement);
+                urls = getConfigurationUrls(fileName);
+            } catch (IOException ex) {
+                ioException = ex;
+            }
+            
+            if (urls == null || !urls.hasNext()) {
+                if (errorIfMissing) {
+                    throw new ConfigurationException("Could not open files of the name " + fileName, ioException);
                 } else {
-                    throw new ConfigurationException(e);
+                    LOG.info("Unable to locate configuration files of the name "
+                            +fileName+", skipping");
+                    return;
                 }
-            } catch (Exception e) {
-                final String s = "Caught exception while loading file " + fileName;
-                throw new ConfigurationException(s, e, includeElement);
-            } finally {
-                if (is != null) {
-                    try {
-                        is.close();
-                    } catch (IOException e) {
-                        LOG.error("Unable to close input stream", e);
+            }
+         
+            while (urls.hasNext()) {
+                try {
+                    URL url = urls.next();
+                    is = FileManager.loadFile(url);
+    
+                    InputSource in = new InputSource(is);
+    
+                    in.setSystemId(url.toString());
+    
+                    doc = DomHelper.parse(in, dtdMappings);
+                } catch (XWorkException e) {
+                    if (includeElement != null) {
+                        throw new ConfigurationException(e, includeElement);
+                    } else {
+                        throw new ConfigurationException(e);
+                    }
+                } catch (Exception e) {
+                    final String s = "Caught exception while loading file " + fileName;
+                    throw new ConfigurationException(s, e, includeElement);
+                } finally {
+                    if (is != null) {
+                        try {
+                            is.close();
+                        } catch (IOException e) {
+                            LOG.error("Unable to close input stream", e);
+                        }
                     }
                 }
-            }
-
-            Element rootElement = doc.getDocumentElement();
-            NodeList children = rootElement.getChildNodes();
-            int childSize = children.getLength();
-
-            for (int i = 0; i < childSize; i++) {
-                Node childNode = children.item(i);
-
-                if (childNode instanceof Element) {
-                    Element child = (Element) childNode;
-
-                    final String nodeName = child.getNodeName();
-
-                    if (nodeName.equals("package")) {
-                        addPackage(child);
-                    } else if (nodeName.equals("include")) {
-                        String includeFileName = child.getAttribute("file");
-                        loadConfigurationFile(includeFileName, child);
+    
+                Element rootElement = doc.getDocumentElement();
+                NodeList children = rootElement.getChildNodes();
+                int childSize = children.getLength();
+    
+                for (int i = 0; i < childSize; i++) {
+                    Node childNode = children.item(i);
+    
+                    if (childNode instanceof Element) {
+                        Element child = (Element) childNode;
+    
+                        final String nodeName = child.getNodeName();
+    
+                        if (nodeName.equals("package")) {
+                            addPackage(child);
+                        } else if (nodeName.equals("include")) {
+                            String includeFileName = child.getAttribute("file");
+                            loadConfigurationFiles(includeFileName, child);
+                        }
                     }
                 }
+                
+                loadExtraConfiguration(doc);
             }
-            
-            loadExtraConfiguration(doc);
 
             if (LOG.isDebugEnabled()) {
                 LOG.debug("Loaded action configuration from: " + fileName);
         }
     }
 
+    protected Iterator<URL> getConfigurationUrls(String fileName) throws IOException {
+        return ClassLoaderUtil.getResources(fileName, XmlConfigurationProvider.class, false);
+    }
+
     /**
      * Allows subclasses to load extra information from the document
      * 

src/java/com/opensymphony/xwork2/util/ClassLoaderUtil.java

 
 import java.io.IOException;
 import java.io.InputStream;
+import java.io.NotSerializableException;
 
 import java.net.URL;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Enumeration;
+import java.util.Iterator;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.NoSuchElementException;
 
 
 /**
     //~ Methods ////////////////////////////////////////////////////////////////
 
     /**
+     * Load all resources with a given name, potentially aggregating all results 
+     * from the searched classloaders.  If no results are found, the resource name
+     * is prepended by '/' and tried again.
+     *
+     * This method will try to load the resources using the following methods (in order):
+     * <ul>
+     *  <li>From Thread.currentThread().getContextClassLoader()
+     *  <li>From ClassLoaderUtil.class.getClassLoader()
+     *  <li>callingClass.getClassLoader()
+     * </ul>
+     *
+     * @param resourceName The name of the resources to load
+     * @param callingClass The Class object of the calling object
+     */
+     public static Iterator<URL> getResources(String resourceName, Class callingClass, boolean aggregate) throws IOException {
+         
+         AggregateIterator<URL> iterator = new AggregateIterator<URL>();
+         
+         iterator.addEnumeration(Thread.currentThread().getContextClassLoader().getResources(resourceName));
+
+         if (!iterator.hasNext() || aggregate) {
+             iterator.addEnumeration(ClassLoaderUtil.class.getClassLoader().getResources(resourceName));
+         }
+
+         if (!iterator.hasNext() || aggregate) {
+             ClassLoader cl = callingClass.getClassLoader();
+
+             if (cl != null) {
+                 iterator.addEnumeration(cl.getResources(resourceName));
+             }
+         }
+
+         if (!iterator.hasNext() && (resourceName != null) && (resourceName.charAt(0) != '/')) {
+             return getResources('/' + resourceName, callingClass, aggregate);
+         }
+
+         return iterator;
+     }
+    
+    /**
     * Load a given resource.
     *
     * This method will try to load the resource using the following methods (in order):
     *  <li>callingClass.getClassLoader()
     * </ul>
     *
-    * @param resourceName The name of the resource to load
+    * @param resourceName The name IllegalStateException("Unable to call ")of the resource to load
     * @param callingClass The Class object of the calling object
     */
     public static URL getResource(String resourceName, Class callingClass) {
             }
         }
     }
+    
+    /** Aggregates Enumeration instances into one iterator */
+    protected static class AggregateIterator<E> implements Iterator<E> {
+
+        LinkedList<Enumeration<E>> enums = new LinkedList<Enumeration<E>>();
+        Enumeration<E> cur = null;
+        
+        public AggregateIterator addEnumeration(Enumeration<E> e) {
+            if (e.hasMoreElements()) {
+                if (cur == null) {
+                    cur = e;
+                } else {
+                    enums.add(e);
+                }
+            }
+            return this;
+        }
+        
+        public boolean hasNext() {
+            if (determineCurrentEnumeration() != null) {
+                return cur.hasMoreElements();
+            } 
+            return false;
+        }
+
+        public E next() {
+            if (determineCurrentEnumeration() != null) {
+                return cur.nextElement();
+            } 
+            throw new NoSuchElementException();
+        }
+        
+        private Enumeration<E> determineCurrentEnumeration() {
+            if (cur != null && !cur.hasMoreElements()) {
+                if (enums.size() > 0) {
+                    cur = enums.removeLast();
+                } else {
+                    cur = null;
+                }
+            }
+            return cur;
+        }
+
+        public void remove() {
+            throw new UnsupportedOperationException();
+        }
+    }
 }

src/java/com/opensymphony/xwork2/util/FileManager.java

      */
     public static InputStream loadFile(String fileName, Class clazz) {
         URL fileUrl = ClassLoaderUtil.getResource(fileName, clazz);
-
+        return loadFile(fileUrl);
+    }
+    
+    /**
+     * Loads opens the named file and returns the InputStream
+     *
+     * @param fileName - the name of the file to open
+     * @return an InputStream of the file contents or null
+     * @throws IllegalArgumentException if there is no file with the given file name
+     */
+    public static InputStream loadFile(URL fileUrl) {
         if (fileUrl == null) {
             return null;
         }
-
+        
+        String fileName = fileUrl.toString();
         InputStream is;
 
         try {

src/java/com/opensymphony/xwork2/util/TextUtils.java

  * @version $Revision: 147 $
  */
 public class TextUtils {
-    //~ Static fields/initializers /////////////////////////////////////////////
+
+    public final static String htmlEncode(String s) {
+        return htmlEncode(s, true);
+    }
 
     /**
-     * An array of HTML tags that, in HTML, don't require closing tags. Note that
-     * XHTML doesn't work this way.
+     * Escape html entity characters and high characters (eg "curvy" Word quotes).
+     * Note this method can also be used to encode XML.
+     * @param s the String to escape.
+     * @param encodeSpecialChars if true high characters will be encode other wise not.
+     * @return the escaped string
      */
-    public final static String[] SINGLE_TAGS = {"br", "p", "hr"};
+    public final static String htmlEncode(String s, boolean encodeSpecialChars) {
+        s = noNull(s);
+
+        StringBuffer str = new StringBuffer();
+
+        for (int j = 0; j < s.length(); j++) {
+            char c = s.charAt(j);
+
+            // encode standard ASCII characters into HTML entities where needed
+            if (c < '\200') {
+                switch (c) {
+                case '"':
+                    str.append("&quot;");
+
+                    break;
+
+                case '&':
+                    str.append("&amp;");
+
+                    break;
+
+                case '<':
+                    str.append("&lt;");
 
+                    break;
+
+                case '>':
+                    str.append("&gt;");
+
+                    break;
+
+                default:
+                    str.append(c);
+                }
+            }
+            // encode 'ugly' characters (ie Word "curvy" quotes etc)
+            else if (encodeSpecialChars && (c < '\377')) {
+                String hexChars = "0123456789ABCDEF";
+                int a = c % 16;
+                int b = (c - a) / 16;
+                String hex = "" + hexChars.charAt(b) + hexChars.charAt(a);
+                str.append("&#x" + hex + ";");
+            }
+            //add other characters back in - to handle charactersets
+            //other than ascii
+            else {
+                str.append(c);
+            }
+        }
+
+        return str.toString();
+    }
+    
     /**
      * Join an Iteration of Strings together.
      *

src/test/overview.html

+Duplicate file test

src/test/xwork-default.xml

-<!DOCTYPE xwork PUBLIC
-    "-//OpenSymphony Group//XWork 1.1.1//EN"
-    "http://www.opensymphony.com/xwork/xwork-1.1.1.dtd"
- >
-
-<xwork>
-    <package name="xwork-default">
-        <result-types>
-            <result-type name="chain" class="com.opensymphony.xwork2.ActionChainResult" default="true"/>
-            <result-type name="void" class="com.opensymphony.xwork2.VoidResult"/>
-            <result-type name="mock" class="com.opensymphony.xwork2.mock.MockResult"/>
-        </result-types>
-
-        <interceptors>
-            <interceptor name="timer" class="com.opensymphony.xwork2.interceptor.TimerInterceptor"/>
-            <interceptor name="logger" class="com.opensymphony.xwork2.interceptor.LoggingInterceptor"/>
-            <interceptor name="chain" class="com.opensymphony.xwork2.interceptor.ChainingInterceptor"/>
-            <interceptor name="params" class="com.opensymphony.xwork2.interceptor.ParametersInterceptor"/>
-            <interceptor name="static-params" class="com.opensymphony.xwork2.interceptor.StaticParametersInterceptor"/>
-            <interceptor name="model-driven" class="com.opensymphony.xwork2.interceptor.ModelDrivenInterceptor"/>
-            <interceptor name="validation" class="com.opensymphony.xwork2.validator.ValidationInterceptor"/>
-			<interceptor name="alias" class="com.opensymphony.xwork2.interceptor.AliasInterceptor"/>
-            <interceptor name="test" class="com.opensymphony.xwork2.mock.MockInterceptor">
-                <param name="foo">expectedFoo</param>
-            </interceptor>
-            <interceptor name="reference-resolver" class="com.opensymphony.xwork2.interceptor.ExternalReferencesInterceptor"/>
-
-            <interceptor-stack name="defaultStack">
-            	<interceptor-ref name="reference-resolver"/>
-                <interceptor-ref name="static-params"/>
-                <interceptor-ref name="model-driven"/>
-                <interceptor-ref name="params"/>
-            </interceptor-stack>
-
-            <interceptor-stack name="debugStack">
-                <interceptor-ref name="timer"/>
-                <interceptor-ref name="logger"/>
-            </interceptor-stack>
-        </interceptors>
-    </package>
-</xwork>

src/test/xwork-sample.xml

  <!-- "file:///temp/ross/xwork/src/etc/xwork-1.0.dtd"  -->
 
 <xwork>
-    <include file="xwork-default.xml"/>
+    <include file="xwork-test-default.xml"/>
     <package name="default" extends="xwork-default" externalReferenceResolver="com.opensymphony.xwork2.config.TestExternalReferenceResolver">
 
         <global-results>

src/test/xwork-test-default.xml

+<!DOCTYPE xwork PUBLIC
+    "-//OpenSymphony Group//XWork 1.1.1//EN"
+    "http://www.opensymphony.com/xwork/xwork-1.1.1.dtd"
+ >
+
+<xwork>
+    <package name="xwork-default">
+        <result-types>
+            <result-type name="chain" class="com.opensymphony.xwork2.ActionChainResult" default="true"/>
+            <result-type name="void" class="com.opensymphony.xwork2.VoidResult"/>
+            <result-type name="mock" class="com.opensymphony.xwork2.mock.MockResult"/>
+        </result-types>
+
+        <interceptors>
+            <interceptor name="timer" class="com.opensymphony.xwork2.interceptor.TimerInterceptor"/>
+            <interceptor name="logger" class="com.opensymphony.xwork2.interceptor.LoggingInterceptor"/>
+            <interceptor name="chain" class="com.opensymphony.xwork2.interceptor.ChainingInterceptor"/>
+            <interceptor name="params" class="com.opensymphony.xwork2.interceptor.ParametersInterceptor"/>
+            <interceptor name="static-params" class="com.opensymphony.xwork2.interceptor.StaticParametersInterceptor"/>
+            <interceptor name="model-driven" class="com.opensymphony.xwork2.interceptor.ModelDrivenInterceptor"/>
+            <interceptor name="validation" class="com.opensymphony.xwork2.validator.ValidationInterceptor"/>
+			<interceptor name="alias" class="com.opensymphony.xwork2.interceptor.AliasInterceptor"/>
+            <interceptor name="test" class="com.opensymphony.xwork2.mock.MockInterceptor">
+                <param name="foo">expectedFoo</param>
+            </interceptor>
+            <interceptor name="reference-resolver" class="com.opensymphony.xwork2.interceptor.ExternalReferencesInterceptor"/>
+
+            <interceptor-stack name="defaultStack">
+            	<interceptor-ref name="reference-resolver"/>
+                <interceptor-ref name="static-params"/>
+                <interceptor-ref name="model-driven"/>
+                <interceptor-ref name="params"/>
+            </interceptor-stack>
+
+            <interceptor-stack name="debugStack">
+                <interceptor-ref name="timer"/>
+                <interceptor-ref name="logger"/>
+            </interceptor-stack>
+        </interceptors>
+    </package>
+</xwork>
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.