Commits

musachy  committed 79f0672

WW-3076 Add an "order" attribute to the "struts" configuration element

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

  • Participants
  • Parent commits c52ec80

Comments (0)

Files changed (7)

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

         this.errorIfMissing = errorIfMissing;
 
         Map<String, String> mappings = new HashMap<String, String>();
+        mappings.put("-//OpenSymphony Group//XWork 2.1.3//EN", "xwork-2.1.3.dtd");
         mappings.put("-//OpenSymphony Group//XWork 2.1//EN", "xwork-2.1.dtd");
         mappings.put("-//OpenSymphony Group//XWork 2.0//EN", "xwork-2.0.dtd");
         mappings.put("-//OpenSymphony Group//XWork 1.1.1//EN", "xwork-1.1.1.dtd");
 
         } else {
             if (!verifyAction(className, name, location)) {
+                if (LOG.isErrorEnabled())
+                    LOG.error("Unable to verify action [#0] with class [#1], from [#2]", name, className, location.toString());
                 return;
             }
         }
     //    }
     private List<Document> loadConfigurationFiles(String fileName, Element includeElement) {
         List<Document> docs = new ArrayList<Document>();
+        List<Document> finalDocs = new ArrayList<Document>();
         if (!includedFileNames.contains(fileName)) {
             if (LOG.isDebugEnabled()) {
                 LOG.debug("Loading action configurations from: " + fileName);
             includedFileNames.add(fileName);
 
             Iterator<URL> urls = null;
-            Document doc = null;
             InputStream is = null;
 
             IOException ioException = null;
 
                     in.setSystemId(url.toString());
 
-                    doc = DomHelper.parse(in, dtdMappings);
+                    docs.add(DomHelper.parse(in, dtdMappings));
                 } catch (XWorkException e) {
                     if (includeElement != null) {
                         throw new ConfigurationException("Unable to load " + url, e, includeElement);
                         }
                     }
                 }
+            }
+
+            //sort the documents, according to the "order" attribute
+            Collections.sort(docs, new Comparator<Document>() {
+                public int compare(Document doc1, Document doc2) {
+                    return XmlHelper.getLoadOrder(doc1) - XmlHelper.getLoadOrder(doc2);
+                }
+            });
 
+            for (Document doc : docs) {
                 Element rootElement = doc.getDocumentElement();
                 NodeList children = rootElement.getChildNodes();
                 int childSize = children.getLength();
                                 wildcardFinder.setPattern(includeFileName);
                                 Vector<String> wildcardMatches = wildcardFinder.findMatches();
                                 for (String match : wildcardMatches) {
-                                    docs.addAll(loadConfigurationFiles(match, child));
+                                    finalDocs.addAll(loadConfigurationFiles(match, child));
                                 }
                             } else {
-
-                                docs.addAll(loadConfigurationFiles(includeFileName, child));
+                                finalDocs.addAll(loadConfigurationFiles(includeFileName, child));
                             }
                         }
                     }
                 }
-                docs.add(doc);
+                finalDocs.add(doc);
                 loadedFileUrls.add(url.toString());
             }
 
                 LOG.debug("Loaded action configuration from: " + fileName);
             }
         }
-        return docs;
+        return finalDocs;
     }
 
     private void handleWildCardIncludes(String includeFileName, List<Document> docs, Element child) {
         return InterceptorBuilder.constructInterceptorReference(context, refName, refParams, loc, objectFactory);
     }
 
+    List<Document> getDocuments() {
+        return documents;
+    }
 }

File src/java/com/opensymphony/xwork2/config/providers/XmlHelper.java

 import org.w3c.dom.Element;
 import org.w3c.dom.Node;
 import org.w3c.dom.NodeList;
+import org.w3c.dom.Document;
+import org.apache.commons.lang.StringUtils;
 
 import java.util.LinkedHashMap;
 import java.util.Map;
         return paramValue.toString().trim();
     }
 
+    /**
+     * Return the value of the "order" attribute from the root element
+     */
+     public static int getLoadOrder(Document doc) {
+        Element rootElement = doc.getDocumentElement();
+        String number = rootElement.getAttribute("order");
+        if (StringUtils.isNotBlank(number)) {
+            try {
+                return Integer.parseInt(number);
+            } catch (NumberFormatException e) {
+                return Integer.MAX_VALUE;
+            }
+        } else {
+            //no order specified
+            return Integer.MAX_VALUE;
+        }
+    }
 }

File src/java/xwork-2.1.3.dtd

+<?xml version="1.0" encoding="UTF-8"?>
+
+<!-- START SNIPPET: xworkDtd -->
+
+<!--
+   XWork configuration DTD.
+   Use the following DOCTYPE
+
+   <!DOCTYPE xwork PUBLIC
+	"-//OpenSymphony Group//XWork 2.1.3//EN"
+	"http://www.opensymphony.com/xwork/xwork-2.1.3.dtd">
+-->
+
+<!ELEMENT xwork ((package|include|bean|constant)*, unknown-handler-stack?)>
+<!ATTLIST xwork
+    order CDATA #IMPLIED
+>
+
+<!ELEMENT package (result-types?, interceptors?, default-interceptor-ref?, default-action-ref?, default-class-ref?, global-results?, global-exception-mappings?, action*)>
+<!ATTLIST package
+    name CDATA #REQUIRED
+    extends CDATA #IMPLIED
+    namespace CDATA #IMPLIED
+    abstract CDATA #IMPLIED
+>
+
+<!ELEMENT result-types (result-type+)>
+
+<!ELEMENT result-type (param*)>
+<!ATTLIST result-type
+    name CDATA #REQUIRED
+    class CDATA #REQUIRED
+    default (true|false) "false"
+>
+
+<!ELEMENT interceptors (interceptor|interceptor-stack)+>
+
+<!ELEMENT interceptor (param*)>
+<!ATTLIST interceptor
+    name CDATA #REQUIRED
+    class CDATA #REQUIRED
+>
+
+<!ELEMENT interceptor-stack (interceptor-ref*)>
+<!ATTLIST interceptor-stack
+    name CDATA #REQUIRED
+>
+
+<!ELEMENT interceptor-ref (param*)>
+<!ATTLIST interceptor-ref
+    name CDATA #REQUIRED
+>
+
+<!ELEMENT default-interceptor-ref (#PCDATA)>
+<!ATTLIST default-interceptor-ref
+    name CDATA #REQUIRED
+>
+
+<!ELEMENT default-action-ref (#PCDATA)>
+<!ATTLIST default-action-ref
+    name CDATA #REQUIRED
+>
+
+<!ELEMENT default-class-ref (#PCDATA)>
+<!ATTLIST default-class-ref
+   class CDATA #REQUIRED
+>
+
+<!ELEMENT global-results (result+)>
+
+<!ELEMENT global-exception-mappings (exception-mapping+)>
+
+<!ELEMENT action (param|result|interceptor-ref|exception-mapping)*>
+<!ATTLIST action
+    name CDATA #REQUIRED
+    class CDATA #IMPLIED
+    method CDATA #IMPLIED
+    converter CDATA #IMPLIED
+>
+
+<!ELEMENT param (#PCDATA)>
+<!ATTLIST param
+    name CDATA #REQUIRED
+>
+
+<!ELEMENT result (#PCDATA|param)*>
+<!ATTLIST result
+    name CDATA #IMPLIED
+    type CDATA #IMPLIED
+>
+
+<!ELEMENT exception-mapping (#PCDATA|param)*>
+<!ATTLIST exception-mapping
+    name CDATA #IMPLIED
+    exception CDATA #REQUIRED
+    result CDATA #REQUIRED
+>
+
+<!ELEMENT include (#PCDATA)>
+<!ATTLIST include
+    file CDATA #REQUIRED
+>
+
+<!ELEMENT bean (#PCDATA)>
+<!ATTLIST bean
+    type CDATA #IMPLIED
+    name CDATA #IMPLIED
+    class CDATA #REQUIRED
+    scope CDATA #IMPLIED
+    static CDATA #IMPLIED
+    optional CDATA #IMPLIED
+>
+
+<!ELEMENT constant (#PCDATA)>
+<!ATTLIST constant
+    name CDATA #REQUIRED
+    value CDATA #REQUIRED
+>
+
+<!ELEMENT unknown-handler-stack (unknown-handler-ref*)>
+<!ELEMENT unknown-handler-ref (#PCDATA)>
+<!ATTLIST unknown-handler-ref
+    name CDATA #REQUIRED
+>
+
+<!-- END SNIPPET: xworkDtd -->
+

File src/test/com/opensymphony/xwork2/config/providers/XmlConfigurationProviderTest.java

 
 import com.opensymphony.xwork2.config.ConfigurationProvider;
 import com.opensymphony.xwork2.config.RuntimeConfiguration;
+import com.opensymphony.xwork2.config.impl.MockConfiguration;
 import com.opensymphony.xwork2.config.entities.PackageConfig;
 import com.opensymphony.xwork2.util.ClassLoaderUtil;
 import com.opensymphony.xwork2.util.FileManager;
+import com.opensymphony.xwork2.ObjectFactory;
 
 import java.io.File;
+import java.io.IOException;
 import java.net.URI;
+import java.net.URL;
+import java.util.Iterator;
+import java.util.List;
+import java.util.ArrayList;
+
+import org.w3c.dom.Document;
 
 
 public class XmlConfigurationProviderTest extends ConfigurationTestBase {
 
+    public void testLoadOrder() throws Exception {
+        configuration = new MockConfiguration();
+        ((MockConfiguration)configuration).selfRegister();
+        container = configuration.getContainer();
+
+        XmlConfigurationProvider prov = new XmlConfigurationProvider("xwork-test-load-order.xml", true) {
+            @Override
+            protected Iterator<URL> getConfigurationUrls(String fileName) throws IOException {
+                List<URL> urls = new ArrayList<URL>();
+                urls.add(ClassLoaderUtil.getResource("com/opensymphony/xwork2/config/providers/loadorder1/xwork-test-load-order.xml", XmlConfigurationProvider.class));
+                urls.add(ClassLoaderUtil.getResource("com/opensymphony/xwork2/config/providers/loadorder2/xwork-test-load-order.xml", XmlConfigurationProvider.class));
+                urls.add(ClassLoaderUtil.getResource("com/opensymphony/xwork2/config/providers/loadorder3/xwork-test-load-order.xml", XmlConfigurationProvider.class));
+                return urls.iterator();
+            }
+        };
+        prov.setObjectFactory(container.getInstance(ObjectFactory.class));
+        prov.init(configuration);
+        List<Document> docs = prov.getDocuments();
+        assertEquals(3, docs.size());
+
+        assertEquals(1, XmlHelper.getLoadOrder(docs.get(0)));
+        assertEquals(2, XmlHelper.getLoadOrder(docs.get(1)));
+        assertEquals(3, XmlHelper.getLoadOrder(docs.get(2)));
+    }
+
     public void testNeedsReload() throws Exception {
         FileManager.setReloadingConfigs(true);
         final String filename = "com/opensymphony/xwork2/config/providers/xwork-test-actions.xml";
 
         assertTrue(!provider.needsReload());
     }
-
+         
 }

File src/test/com/opensymphony/xwork2/config/providers/loadorder1/xwork-test-load-order.xml

+<!DOCTYPE xwork PUBLIC
+    "-//OpenSymphony Group//XWork 2.1.3//EN"
+    "http://www.opensymphony.com/xwork/xwork-2.1.3.dtd"
+ >
+
+<xwork order="2">
+</xwork>

File src/test/com/opensymphony/xwork2/config/providers/loadorder2/xwork-test-load-order.xml

+<!DOCTYPE xwork PUBLIC
+    "-//OpenSymphony Group//XWork 2.1.3//EN"
+    "http://www.opensymphony.com/xwork/xwork-2.1.3.dtd"
+ >
+
+<xwork order="3">
+</xwork>

File src/test/com/opensymphony/xwork2/config/providers/loadorder3/xwork-test-load-order.xml

+<!DOCTYPE xwork PUBLIC
+    "-//OpenSymphony Group//XWork 2.1.3//EN"
+    "http://www.opensymphony.com/xwork/xwork-2.1.3.dtd"
+ >
+
+<xwork order="1">
+</xwork>