Commits

Anonymous committed 7c2c5d3

Packages in includes can't see packages declared ahead of include
o added callback method for failing package configurations to reiterate over to find the parent package configuration
o added unit test
o updated wrong proxyinvoke test configuration (found by this refactoring)

Issue Number: XW-493, XW-486

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

Comments (0)

Files changed (9)

src/java/com/opensymphony/xwork2/config/entities/PackageConfig.java

     private String namespace = "";
     private boolean isAbstract = false;
 
+    private boolean needsRefresh;
+    
+
 
     public PackageConfig() {
     }
         resultTypeConfigs.put(config.getName(), config);
     }
 
+    public boolean isNeedsRefresh() {
+        return needsRefresh;
+    }
+
+    public void setNeedsRefresh(boolean needsRefresh) {
+        this.needsRefresh = needsRefresh;
+    }
+
     public boolean equals(Object o) {
         if (this == o) {
             return true;

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

     }
 
     public void loadPackages() throws ConfigurationException {
+        List<ReloadPackage> reloads = new ArrayList<ReloadPackage>();
         for (Document doc : documents) {
             Element rootElement = doc.getDocumentElement();
             NodeList children = rootElement.getChildNodes();
                     final String nodeName = child.getNodeName();
 
                     if (nodeName.equals("package")) {
-                        addPackage(child);
-                    } 
+                        PackageConfig cfg = addPackage(child);
+                        if (cfg.isNeedsRefresh()) {
+                            reloads.add(new ReloadPackage(doc, child));
+                        }
+                    }
                 }
             }
             loadExtraConfiguration(doc);
         }
+
+        if (reloads.size() > 0) {
+            reloadRequiredPackages(reloads);
+        }
+
+        reloads.clear();
         documents.clear();
         configuration = null;
     }
 
+    private void reloadRequiredPackages(List<ReloadPackage> reloads) {
+        List<ReloadPackage> result = new ArrayList<ReloadPackage>();
+        if ( reloads.size() > 0) {
+            for (ReloadPackage pkg : reloads) {
+                Document doc = pkg.getDoc();
+                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")) {
+                            PackageConfig cfg = addPackage(child);
+                            if (cfg.isNeedsRefresh()) {
+                                result.add(new ReloadPackage(doc, child));
+                            }
+                        }
+                    }
+                }
+                loadExtraConfiguration(doc);
+            }
+        }
+        if ( result.size() > 0 && reloads.size() > result.size() && result.size() != reloads.size()) {
+            reloadRequiredPackages(result);
+        }
+    }
+
+    private class ReloadPackage {
+        Element child;
+        Document doc;
+
+        public ReloadPackage(Document doc, Element child) {
+            this.child = child;
+            this.doc = doc;
+        }
+
+        public Element getChild() {
+            return child;
+        }
+
+        public Document getDoc() {
+            return doc;
+        }
+    }
+
     /**
      * Tells whether the ConfigurationProvider should reload its configuration. This method should only be called
      * if ConfigurationManager.isReloadingConfigs() is true.
     /**
      * Create a PackageConfig from an XML element representing it.
      */
-    protected void addPackage(Element packageElement) throws ConfigurationException {
+    protected PackageConfig addPackage(Element packageElement) throws ConfigurationException {
         PackageConfig newPackage = buildPackageContext(packageElement);
 
+        if (newPackage.isNeedsRefresh()) {
+            return newPackage;
+        }
+        
         if (LOG.isDebugEnabled()) {
             LOG.debug("Loaded " + newPackage);
         }
         loadDefaultActionRef(newPackage, packageElement);
 
         configuration.addPackageConfig(newPackage.getName(), newPackage);
+        return newPackage;
     }
 
     protected void addResultTypes(PackageConfig packageContext, Element element) {
                 LOG.error("Unable to find parent packages " + parent);
 
                 cfg = new PackageConfig(name, namespace, isAbstract);
+                cfg.setNeedsRefresh(true);
             } else {
                 cfg = new PackageConfig(name, namespace, isAbstract, parents);
             }
                 Element rootElement = doc.getDocumentElement();
                 NodeList children = rootElement.getChildNodes();
                 int childSize = children.getLength();
-    
+
                 for (int i = 0; i < childSize; i++) {
                     Node childNode = children.item(i);
     
     
                         final String nodeName = child.getNodeName();
     
-                        //if (nodeName.equals("package")) {
-                        //    addPackage(child);
-                        //} else 
                         if (nodeName.equals("include")) {
                             String includeFileName = child.getAttribute("file");
                             docs.addAll(loadConfigurationFiles(includeFileName, child));

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

 package com.opensymphony.xwork2.config.providers;
 
 import java.io.File;
+import java.util.List;
 
 import com.opensymphony.xwork2.config.ConfigurationProvider;
+import com.opensymphony.xwork2.config.RuntimeConfiguration;
+import com.opensymphony.xwork2.config.entities.PackageConfig;
 import com.opensymphony.xwork2.util.FileManager;
 
 
         
         assertTrue(provider.needsReload());
     }
+
+    public void testInheritence() throws Exception {
+        final String filename = "com/opensymphony/xwork2/config/providers/xwork-include-parent.xml";
+        ConfigurationProvider provider = buildConfigurationProvider(filename);
+
+        provider.init(configuration);
+        provider.loadPackages();
+
+        // test expectations
+        assertEquals(6, configuration.getPackageConfigs().size());
+
+        
+        PackageConfig defaultPackage = configuration.getPackageConfig("default");
+        assertNotNull(defaultPackage);
+        assertEquals("default", defaultPackage.getName());
+
+
+        PackageConfig namespace1 = configuration.getPackageConfig("namespace1");
+        assertNotNull(namespace1);
+        assertEquals("namespace1", namespace1.getName());
+        assertEquals(defaultPackage, namespace1.getParents().get(0));
+
+        PackageConfig namespace2 = configuration.getPackageConfig("namespace2");
+        assertNotNull(namespace2);
+        assertEquals("namespace2", namespace2.getName());
+        assertEquals(1, namespace2.getParents().size());
+        assertEquals(namespace1, namespace2.getParents().get(0));
+
+        
+        PackageConfig namespace4 = configuration.getPackageConfig("namespace4");
+        assertNotNull(namespace4);
+        assertEquals("namespace4", namespace4.getName());
+        assertEquals(1, namespace4.getParents().size());
+        assertEquals(namespace1, namespace4.getParents().get(0));
+
+        
+        PackageConfig namespace5 = configuration.getPackageConfig("namespace5");
+        assertNotNull(namespace5);
+        assertEquals("namespace5", namespace5.getName());
+        assertEquals(1, namespace5.getParents().size());
+        assertEquals(namespace4, namespace5.getParents().get(0));
+
+    }
 }

src/test/com/opensymphony/xwork2/config/providers/xwork-include-after-package-2.xml

+<!DOCTYPE xwork PUBLIC
+    "-//OpenSymphony Group//XWork 2.0//EN"
+    "http://www.opensymphony.com/xwork/xwork-2.0.dtd"
+ >
+
+<xwork>
+    <package name="namespace5" extends="namespace4" namespace="/namespace5">
+        <action name="action5" class="com.opensymphony.xwork2.SimpleAction">
+            <result name="success" type="chain"/>
+            <interceptor-ref name="params"/>
+        </action>
+    </package>
+
+</xwork>

src/test/com/opensymphony/xwork2/config/providers/xwork-include-after-package.xml

+<!DOCTYPE xwork PUBLIC
+    "-//OpenSymphony Group//XWork 2.0//EN"
+    "http://www.opensymphony.com/xwork/xwork-2.0.dtd"
+ >
+
+<xwork>
+    <package name="namespace4" extends="namespace1" namespace="/namespace4">
+        <interceptors>
+            <interceptor name="static-params" class="com.opensymphony.xwork2.interceptor.StaticParametersInterceptor"/>
+        </interceptors>
+
+        <action name="action4" class="com.opensymphony.xwork2.SimpleAction">
+            <result name="success" type="chain"/>
+            <interceptor-ref name="params"/>
+        </action>
+    </package>
+
+
+    <include file="com/opensymphony/xwork2/config/providers/xwork-include-after-package-2.xml" />
+</xwork>

src/test/com/opensymphony/xwork2/config/providers/xwork-include-before-package-2.xml

+<!DOCTYPE xwork PUBLIC
+    "-//OpenSymphony Group//XWork 2.0//EN"
+    "http://www.opensymphony.com/xwork/xwork-2.0.dtd"
+ >
+
+<xwork>
+    <package name="namespace2" extends="namespace1" namespace="/namespace2">
+        <action name="action2" class="com.opensymphony.xwork2.SimpleAction">
+            <result name="success" type="chain"/>
+            <interceptor-ref name="params"/>
+        </action>
+    </package>
+
+</xwork>

src/test/com/opensymphony/xwork2/config/providers/xwork-include-before-package.xml

+<!DOCTYPE xwork PUBLIC
+    "-//OpenSymphony Group//XWork 2.0//EN"
+    "http://www.opensymphony.com/xwork/xwork-2.0.dtd"
+ >
+
+<xwork>
+	<include file="xwork-test-beans.xml" />
+
+    <package name="namespace1" extends="default" namespace="/namespace1">
+        <interceptors>
+            <interceptor name="static-params" class="com.opensymphony.xwork2.interceptor.StaticParametersInterceptor"/>
+        </interceptors>
+
+        <action name="action1" class="com.opensymphony.xwork2.SimpleAction">
+            <result name="success" type="chain"/>
+            <interceptor-ref name="params"/>
+        </action>
+    </package>
+
+    <include file="com/opensymphony/xwork2/config/providers/xwork-include-before-package-2.xml" />
+
+</xwork>

src/test/com/opensymphony/xwork2/config/providers/xwork-include-parent.xml

+<!DOCTYPE xwork PUBLIC
+    "-//OpenSymphony Group//XWork 2.0//EN"
+    "http://www.opensymphony.com/xwork/xwork-2.0.dtd"
+ >
+
+<xwork>
+	<include file="xwork-test-default.xml" />
+
+    <package name="default">
+        <result-types>
+            <result-type name="chain" class="com.opensymphony.xwork2.ActionChainResult" default="true"/>
+        </result-types>
+        <interceptors>
+            <interceptor name="params" class="com.opensymphony.xwork2.interceptor.ParametersInterceptor"/>
+        </interceptors>
+    </package>
+
+    <include file="com/opensymphony/xwork2/config/providers/xwork-include-before-package.xml" />
+
+
+    <include file="com/opensymphony/xwork2/config/providers/xwork-include-after-package.xml" />
+</xwork>

src/test/xwork-proxyinvoke.xml

     
  	<bean type="com.opensymphony.xwork2.ActionProxyFactory" name="default" class="com.opensymphony.xwork2.DefaultActionProxyFactory"/>
 	<constant name="devMode" value="false" />
- 
+
+    <package name="xwork-test-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-stack name="defaultStack">
+                <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>
+    
     <package name="default" extends="xwork-test-default">
 
         <action name="ProxyInvocation" method="show" class="com.opensymphony.xwork2.ProxyInvocationAction"/>
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.