From commits-return-18366-archive-asf-public=cust-asf.ponee.io@struts.apache.org Sat Apr 20 05:33:51 2019 Return-Path: X-Original-To: archive-asf-public@cust-asf.ponee.io Delivered-To: archive-asf-public@cust-asf.ponee.io Received: from mail.apache.org (hermes.apache.org [207.244.88.153]) by mx-eu-01.ponee.io (Postfix) with SMTP id 79C3E180629 for ; Sat, 20 Apr 2019 07:33:50 +0200 (CEST) Received: (qmail 7468 invoked by uid 500); 20 Apr 2019 05:33:49 -0000 Mailing-List: contact commits-help@struts.apache.org; run by ezmlm Precedence: bulk List-Help: List-Unsubscribe: List-Post: List-Id: Reply-To: dev@struts.apache.org Delivered-To: mailing list commits@struts.apache.org Received: (qmail 7459 invoked by uid 99); 20 Apr 2019 05:33:49 -0000 Received: from ec2-52-202-80-70.compute-1.amazonaws.com (HELO gitbox.apache.org) (52.202.80.70) by apache.org (qpsmtpd/0.29) with ESMTP; Sat, 20 Apr 2019 05:33:49 +0000 Received: by gitbox.apache.org (ASF Mail Server at gitbox.apache.org, from userid 33) id 8CA1F87A14; Sat, 20 Apr 2019 05:33:48 +0000 (UTC) Date: Sat, 20 Apr 2019 05:33:48 +0000 To: "commits@struts.apache.org" Subject: [struts] branch master updated: Proposed fix for WW-5029 for the 2.5.x branch (#347) MIME-Version: 1.0 Content-Type: text/plain; charset=utf-8 Content-Transfer-Encoding: 8bit Message-ID: <155573842831.26845.7296437940500272263@gitbox.apache.org> From: yasserzamani@apache.org X-Git-Host: gitbox.apache.org X-Git-Repo: struts X-Git-Refname: refs/heads/master X-Git-Reftype: branch X-Git-Oldrev: f0776aeac6297c849b14921debec4c9cb5653c37 X-Git-Newrev: 47a8a21da276ca1f2521685f7445330be84c66ee X-Git-Rev: 47a8a21da276ca1f2521685f7445330be84c66ee X-Git-NotificationType: ref_changed_plus_diff X-Git-Multimail-Version: 1.5.dev Auto-Submitted: auto-generated This is an automated email from the ASF dual-hosted git repository. yasserzamani pushed a commit to branch master in repository https://gitbox.apache.org/repos/asf/struts.git The following commit(s) were added to refs/heads/master by this push: new 47a8a21 Proposed fix for WW-5029 for the 2.5.x branch (#347) 47a8a21 is described below commit 47a8a21da276ca1f2521685f7445330be84c66ee Author: JCgH4164838Gh792C124B5 <43964333+jcgh4164838gh792c124b5@users.noreply.github.com> AuthorDate: Sat Apr 20 09:54:36 2019 +0430 Proposed fix for WW-5029 for the 2.5.x branch (#347) * Proposed fix for WW-5029 for the 2.5.x branch: - NOTE: If the PR is accepted please credit Maxime Clement for this change as they found the issue, identified the probable cause/related details and opened the JIRA. - Updated XWorkConfigurationProvider buildAllowedMethods(), loadGlobalAllowedMethods() so that they now handle situations when a SAX parser produces multiple elements to represent the tag body value. - No changes to unit tests. * Update commit to fix weakness identified by Maxime Clement: - Implementation should now properly concatenate the node children values together (as a single unified string) in both buildAllowedMethods(), loadGlobalAllowedMethods() - before generating the method Set to be added. - Made some eligible variables final. * Update commit to provide new unit tests: - Added unit tests to confirm the fixes for buildAllowedMethods(), loadGlobalAllowedMethods() - Added Mock DOM classes sufficient for these tests. - Added unit tests to cover buildResults() and loadGlobalResults(). (cherry picked from commit fb38a91) --- .../config/providers/XmlConfigurationProvider.java | 41 +- .../providers/XmlConfigurationProviderTest.java | 802 +++++++++++++++++++++ 2 files changed, 831 insertions(+), 12 deletions(-) diff --git a/core/src/main/java/com/opensymphony/xwork2/config/providers/XmlConfigurationProvider.java b/core/src/main/java/com/opensymphony/xwork2/config/providers/XmlConfigurationProvider.java index 3b72352..9cd21fb 100644 --- a/core/src/main/java/com/opensymphony/xwork2/config/providers/XmlConfigurationProvider.java +++ b/core/src/main/java/com/opensymphony/xwork2/config/providers/XmlConfigurationProvider.java @@ -874,15 +874,22 @@ public class XmlConfigurationProvider implements ConfigurationProvider { if (allowedMethodsEls.getLength() > 0) { // user defined 'allowed-methods' so used them whatever Strict DMI was enabled or not allowedMethods = new HashSet<>(packageContext.getGlobalAllowedMethods()); - - if (allowedMethodsEls.getLength() > 0) { - Node n = allowedMethodsEls.item(0).getFirstChild(); - if (n != null) { - String s = n.getNodeValue().trim(); - if (s.length() > 0) { - allowedMethods.addAll(TextParseUtil.commaDelimitedStringToSet(s)); + // Fix for WW-5029 (concatenate all possible text node children) + final Node allowedMethodsNode = allowedMethodsEls.item(0); + if (allowedMethodsNode != null) { + final NodeList allowedMethodsChildren = allowedMethodsNode.getChildNodes(); + final StringBuilder allowedMethodsSB = new StringBuilder(); + for (int i = 0; i < allowedMethodsChildren.getLength(); i++) { + Node allowedMethodsChildNode = allowedMethodsChildren.item(i); + String childNodeValue = (allowedMethodsChildNode != null ? allowedMethodsChildNode.getNodeValue() : ""); + childNodeValue = (childNodeValue != null ? childNodeValue.trim() : ""); + if (childNodeValue.length() > 0) { + allowedMethodsSB.append(childNodeValue); } } + if (allowedMethodsSB.length() > 0) { + allowedMethods.addAll(TextParseUtil.commaDelimitedStringToSet(allowedMethodsSB.toString())); + } } } else if (packageContext.isStrictMethodInvocation()) { // user enabled Strict DMI but didn't defined action specific 'allowed-methods' so we use 'global-allowed-methods' only @@ -937,11 +944,21 @@ public class XmlConfigurationProvider implements ConfigurationProvider { if (globalAllowedMethodsElms.getLength() > 0) { Set globalAllowedMethods = new HashSet<>(); - Node n = globalAllowedMethodsElms.item(0).getFirstChild(); - if (n != null) { - String s = n.getNodeValue().trim(); - if (s.length() > 0) { - globalAllowedMethods = TextParseUtil.commaDelimitedStringToSet(s); + // Fix for WW-5029 (concatenate all possible text node children) + Node globaAllowedMethodsNode = globalAllowedMethodsElms.item(0); + if (globaAllowedMethodsNode != null) { + NodeList globaAllowedMethodsChildren = globaAllowedMethodsNode.getChildNodes(); + final StringBuilder globalAllowedMethodsSB = new StringBuilder(); + for (int i = 0; i < globaAllowedMethodsChildren.getLength(); i++) { + Node globalAllowedMethodsChildNode = globaAllowedMethodsChildren.item(i); + String childNodeValue = (globalAllowedMethodsChildNode != null ? globalAllowedMethodsChildNode.getNodeValue() : ""); + childNodeValue = (childNodeValue != null ? childNodeValue.trim() : ""); + if (childNodeValue.length() > 0) { + globalAllowedMethodsSB.append(childNodeValue); + } + } + if (globalAllowedMethodsSB.length() > 0) { + globalAllowedMethods.addAll(TextParseUtil.commaDelimitedStringToSet(globalAllowedMethodsSB.toString())); } } packageContext.addGlobalAllowedMethods(globalAllowedMethods); diff --git a/core/src/test/java/com/opensymphony/xwork2/config/providers/XmlConfigurationProviderTest.java b/core/src/test/java/com/opensymphony/xwork2/config/providers/XmlConfigurationProviderTest.java index faa6b93..6aefed8 100644 --- a/core/src/test/java/com/opensymphony/xwork2/config/providers/XmlConfigurationProviderTest.java +++ b/core/src/test/java/com/opensymphony/xwork2/config/providers/XmlConfigurationProviderTest.java @@ -23,8 +23,11 @@ import com.opensymphony.xwork2.ObjectFactory; import com.opensymphony.xwork2.config.ConfigurationProvider; import com.opensymphony.xwork2.config.RuntimeConfiguration; import com.opensymphony.xwork2.config.entities.PackageConfig; +import com.opensymphony.xwork2.config.entities.ResultConfig; +import com.opensymphony.xwork2.config.entities.ResultTypeConfig; import com.opensymphony.xwork2.config.impl.MockConfiguration; import com.opensymphony.xwork2.util.ClassLoaderUtil; +import com.sun.org.apache.xerces.internal.dom.ElementImpl; import org.w3c.dom.Document; import java.io.File; @@ -35,9 +38,20 @@ import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.Paths; import java.util.ArrayList; +import java.util.HashMap; import java.util.Iterator; import java.util.List; +import java.util.Map; import java.util.Set; +import org.apache.struts2.result.ServletDispatcherResult; +import org.w3c.dom.Attr; +import org.w3c.dom.DOMException; +import org.w3c.dom.Element; +import org.w3c.dom.NamedNodeMap; +import org.w3c.dom.Node; +import org.w3c.dom.NodeList; +import org.w3c.dom.TypeInfo; +import org.w3c.dom.UserDataHandler; public class XmlConfigurationProviderTest extends ConfigurationTestBase { @@ -294,4 +308,792 @@ public class XmlConfigurationProviderTest extends ConfigurationTestBase { assertTrue(loadedFileNames.contains("xwork-test-beans.xml")); assertTrue(loadedFileNames.contains("xwork-test-default.xml")); } + + /** + * Test buildAllowedMethods() to ensure consistent results for processing + * in XML configuration elements. + * + * @throws Exception + */ + public void testBuildAllowedMethods() throws Exception { + // Test introduced with WW-5029 fix. + // Set up test using two mock DOM Elements: + // 1) A mock "action" Element with a single "allowed-methods" child Element that contains a single + // TEXT_NODE Node. This simulates a typical result from a SAX parser parsing the allowed-methods + // tag body. + // 2) A mock "action" Element with a single "allowed-methods" child Element that contains multiple + // TEXT_NODE Nodes. This simulates an unusal result from a SAX parser parsing the allowed-methods + // tag body (as reported with WW-5029). + final String fakeBodyString = "allowedMethod1,allowedMethod2,allowedMethod3"; + PackageConfig.Builder testPackageConfigBuilder = new PackageConfig.Builder("allowedMethodsPackage"); + List singleStringList = new ArrayList(1); + List multipleStringList = new ArrayList(4); + singleStringList.add(fakeBodyString); + multipleStringList.add("allowedMethod1,"); + multipleStringList.add("allowed"); + multipleStringList.add("Method2,"); + multipleStringList.add("allowedMethod3"); + NodeList mockNodeListSingleChild = new MockNodeList(singleStringList); + NodeList mockNodeListMultipleChild = new MockNodeList(multipleStringList); + Element mockSingleChildAllowedMethodsElement = new MockElement("allowed-methods", fakeBodyString, + "allowed-methods", fakeBodyString, Node.TEXT_NODE, mockNodeListSingleChild, null); + Element mockMultipleChildAllowedMethodsElement = new MockElement("allowed-methods", fakeBodyString, + "allowed-methods", fakeBodyString, Node.TEXT_NODE, mockNodeListMultipleChild, null); + MockNodeList mockActionElementChildrenSingle = new MockNodeList(); + mockActionElementChildrenSingle.addToNodeList(mockSingleChildAllowedMethodsElement); + MockNodeList mockActionElementChildrenMultiple = new MockNodeList(); + mockActionElementChildrenMultiple.addToNodeList(mockMultipleChildAllowedMethodsElement); + Element mockActionElementSingle = new MockElement("action", "fakeBody", "action", "fakeValue", + Node.TEXT_NODE, mockActionElementChildrenSingle, null); + Element mockActionElementMultiple = new MockElement("action", "fakeBody", "action", "fakeValue", + Node.TEXT_NODE, mockActionElementChildrenMultiple, null); + // Attempt the method using both types of Elements (single child and multiple child) and confirm + // the result is the same for both. Also confirm the results are as expected. + XmlConfigurationProvider prov = new XmlConfigurationProvider("com/opensymphony/xwork2/config/providers/xwork- test.xml", false); + Set singleChildResult = prov.buildAllowedMethods(mockActionElementSingle, testPackageConfigBuilder); + Set multipleChildResult = prov.buildAllowedMethods(mockActionElementMultiple, testPackageConfigBuilder); + assertNotNull("singleChildResult is null ?", singleChildResult); + assertNotNull("multipleChildResult is null ?", multipleChildResult); + assertEquals("singleChildResult not equal to multipleChildResult ?", singleChildResult, multipleChildResult); + // Since both Sets are equal, only need to test one to confirm contents are correct + assertEquals("result Sets not of length 3 ?", 3, multipleChildResult.size()); + assertTrue("allowedMethod1 not present ?", multipleChildResult.contains("allowedMethod1")); + assertTrue("allowedMethod2 not present ?", multipleChildResult.contains("allowedMethod2")); + assertTrue("allowedMethod3 not present ?", multipleChildResult.contains("allowedMethod3")); + assertFalse("noSuchMethod is present ?", multipleChildResult.contains("noSuchMethod")); + } + + /** + * Test loadGlobalAllowedMethods() to ensure consistent results for processing + * in XML configuration elements. + * + * @throws Exception + */ + public void testLoadGlobalAllowedMethods() throws Exception { + // Test introduced with WW-5029 fix. + // Set up test using two mock DOM Elements: + // 1) A mock "package" Element with a single "global-allowed-methods" child Element that contains a single + // TEXT_NODE Node. This simulates a typical result from a SAX parser parsing the global-allowed-methods + // tag body. + // 2) A mock "package" Element with a single "global-allowed-methods" child Element that contains multiple + // TEXT_NODE Nodes. This simulates an unusal result from a SAX parser parsing the global-allowed-methods + // tag body (as reported with WW-5029). + final String fakeBodyString = "allowedMethod1,allowedMethod2,allowedMethod3"; + PackageConfig.Builder testPackageConfigBuilder = new PackageConfig.Builder("globalAllowedMethodsPackage"); + List singleStringList = new ArrayList(1); + List multipleStringList = new ArrayList(4); + singleStringList.add(fakeBodyString); + multipleStringList.add("allowedMethod4,"); + multipleStringList.add("allowed"); + multipleStringList.add("Method5,"); + multipleStringList.add("allowedMethod6"); + NodeList mockNodeListSingleChild = new MockNodeList(singleStringList); + NodeList mockNodeListMultipleChild = new MockNodeList(multipleStringList); + Element mockSingleChildAllowedMethodsElement = new MockElement("global-allowed-methods", fakeBodyString, + "global-allowed-methods", fakeBodyString, Node.TEXT_NODE, mockNodeListSingleChild, null); + Element mockMultipleChildAllowedMethodsElement = new MockElement("global-allowed-methods", fakeBodyString, + "global-allowed-methods", fakeBodyString, Node.TEXT_NODE, mockNodeListMultipleChild, null); + MockNodeList mockPackageElementChildrenSingle = new MockNodeList(); + mockPackageElementChildrenSingle.addToNodeList(mockSingleChildAllowedMethodsElement); + MockNodeList mockPackageElementChildrenMultiple = new MockNodeList(); + mockPackageElementChildrenMultiple.addToNodeList(mockMultipleChildAllowedMethodsElement); + Element mockPackageElementSingle = new MockElement("package", "fakeBody", "package", "fakeValue", + Node.TEXT_NODE, mockPackageElementChildrenSingle, null); + Element mockPackageElementMultiple = new MockElement("package", "fakeBody", "package", "fakeValue", + Node.TEXT_NODE, mockPackageElementChildrenMultiple, null); + // Attempt the method using the single child Element and confirm the result is as expected. + XmlConfigurationProvider prov = new XmlConfigurationProvider("com/opensymphony/xwork2/config/providers/xwork- test.xml", false); + prov.loadGlobalAllowedMethods(testPackageConfigBuilder, mockPackageElementSingle); + Set currentGlobalResult = testPackageConfigBuilder.getGlobalAllowedMethods(); + assertNotNull("currentGlobalResult is null ?", currentGlobalResult); + assertEquals("currentGlobalResult Sets not of length 3 ?", 3, currentGlobalResult.size()); + assertTrue("allowedMethod1 not present ?", currentGlobalResult.contains("allowedMethod1")); + assertTrue("allowedMethod2 not present ?", currentGlobalResult.contains("allowedMethod2")); + assertTrue("allowedMethod3 not present ?", currentGlobalResult.contains("allowedMethod3")); + assertFalse("noSuchMethod is present ?", currentGlobalResult.contains("noSuchMethod")); + // Attempt the method using the multiple child Element and confirm the result is as expected. + prov.loadGlobalAllowedMethods(testPackageConfigBuilder, mockPackageElementMultiple); + currentGlobalResult = testPackageConfigBuilder.getGlobalAllowedMethods(); + assertNotNull("currentGlobalResult is null ?", currentGlobalResult); + assertEquals("currentGlobalResult Sets not of length 6 ?", 6, currentGlobalResult.size()); + assertTrue("allowedMethod4 not present ?", currentGlobalResult.contains("allowedMethod4")); + assertTrue("allowedMethod5 not present ?", currentGlobalResult.contains("allowedMethod5")); + assertTrue("allowedMethod6 not present ?", currentGlobalResult.contains("allowedMethod6")); + assertFalse("noSuchMethod is present ?", currentGlobalResult.contains("snoSUchMethod")); + // Confirm the previously added elements are still present + assertTrue("allowedMethod1 not present ?", currentGlobalResult.contains("allowedMethod1")); + assertTrue("allowedMethod2 not present ?", currentGlobalResult.contains("allowedMethod2")); + assertTrue("allowedMethod3 not present ?", currentGlobalResult.contains("allowedMethod3")); + } + + /** + * Test buildResults() to ensure consistent results for processing + * in XML configuration elements. + * + * @throws Exception + */ + public void testBuildResults() throws Exception { + // Set up test using two mock DOM Elements: + // 1) A mock "action" Element with a two "result" child Elements that each contains a single + // TEXT_NODE Node. This simulates a typical result from a SAX parser parsing the result + // tag body. + // 2) A mock "action" Element with two "result" child Elements that each contain multiple + // TEXT_NODE Nodes. This simulates an unusal result from a SAX parser parsing the result + // tag body. + final String fakeBodyString = "/SomePath/SomePath/SomePath/SomeJSP.jsp"; + final String fakeBodyString2 = "/SomePath2/SomePath2/SomePath2/SomeJSP2.jsp"; + final String resultParam = "nonNullDefaultParam"; + PackageConfig.Builder testPackageConfigBuilder = new PackageConfig.Builder("resultsPackage"); + ResultTypeConfig.Builder resultTypeConfigBuilder = new ResultTypeConfig.Builder("dispatcher", (String) ServletDispatcherResult.class.getName()); + resultTypeConfigBuilder.defaultResultParam(resultParam); + ResultTypeConfig resultTypeConfig = resultTypeConfigBuilder.build(); + testPackageConfigBuilder.addResultTypeConfig(resultTypeConfig); + List singleStringList = new ArrayList(1); + List singleStringList2 = new ArrayList(1); + List multipleStringList = new ArrayList(4); + List multipleStringList2 = new ArrayList(4); + singleStringList.add(fakeBodyString); + singleStringList2.add(fakeBodyString2); + multipleStringList.add("/SomePath"); + multipleStringList.add("/SomePath/"); + multipleStringList.add("SomePath/"); + multipleStringList.add("SomeJSP.jsp"); + multipleStringList2.add("/SomePath2"); + multipleStringList2.add("/SomePath2/"); + multipleStringList2.add("SomePath2/"); + multipleStringList2.add("SomeJSP2.jsp"); + NodeList mockNodeListSingleChild = new MockNodeList(singleStringList); + NodeList mockNodeListSingleChild2 = new MockNodeList(singleStringList2); + NodeList mockNodeListMultipleChild = new MockNodeList(multipleStringList); + NodeList mockNodeListMultipleChild2 = new MockNodeList(multipleStringList2); + Element mockSingleChildResultElement = new MockElement("result", fakeBodyString, + "result", fakeBodyString, Node.TEXT_NODE, mockNodeListSingleChild, null); + mockSingleChildResultElement.setAttribute("name", "input"); + mockSingleChildResultElement.setAttribute("type", "dispatcher"); + Element mockSingleChildResultElement2 = new MockElement("result", fakeBodyString2, + "result", fakeBodyString2, Node.TEXT_NODE, mockNodeListSingleChild2, null); + mockSingleChildResultElement2.setAttribute("name", "success"); + mockSingleChildResultElement2.setAttribute("type", "dispatcher"); + Element mockMultipleChildAllowedMethodsElement = new MockElement("result", fakeBodyString, + "result", fakeBodyString, Node.TEXT_NODE, mockNodeListMultipleChild, null); + mockMultipleChildAllowedMethodsElement.setAttribute("name", "input"); + mockMultipleChildAllowedMethodsElement.setAttribute("type", "dispatcher"); + Element mockMultipleChildAllowedMethodsElement2 = new MockElement("result", fakeBodyString2, + "result", fakeBodyString2, Node.TEXT_NODE, mockNodeListMultipleChild2, null); + mockMultipleChildAllowedMethodsElement2.setAttribute("name", "success"); + mockMultipleChildAllowedMethodsElement2.setAttribute("type", "dispatcher"); + MockNodeList mockActionElementChildrenSingle = new MockNodeList(); + mockActionElementChildrenSingle.addToNodeList(mockSingleChildResultElement); + mockActionElementChildrenSingle.addToNodeList(mockSingleChildResultElement2); + MockNodeList mockActionElementChildrenMultiple = new MockNodeList(); + mockActionElementChildrenMultiple.addToNodeList(mockMultipleChildAllowedMethodsElement); + mockActionElementChildrenMultiple.addToNodeList(mockMultipleChildAllowedMethodsElement2); + Element mockActionElementSingle = new MockElement("action", "fakeBody", "action", "fakeValue", + Node.TEXT_NODE, mockActionElementChildrenSingle, null); + Element mockActionElementMultiple = new MockElement("action", "fakeBody", "action", "fakeValue", + Node.TEXT_NODE, mockActionElementChildrenMultiple, null); + // Attempt the method using both types of Elements (single child and multiple child) and confirm + // the result is the same for both. Also confirm the results are as expected. + XmlConfigurationProvider prov = new XmlConfigurationProvider("com/opensymphony/xwork2/config/providers/xwork- test.xml", false); + Map singleChildResult = prov.buildResults(mockActionElementSingle, testPackageConfigBuilder); + Map multipleChildResult = prov.buildResults(mockActionElementMultiple, testPackageConfigBuilder); + assertNotNull("singleChildResult is null ?", singleChildResult); + assertNotNull("multipleChildResult is null ?", multipleChildResult); + assertEquals("singleChildResult not equal to multipleChildResult ?", singleChildResult, multipleChildResult); + // Since both Maps are equal, only need to test one to confirm the contents of ONE are correct.contents are correct + // The multipleChildResult (constructed from the multiple child TEXT_NODES) will be checked for correctness. + assertEquals("result Maps not of size 2 ?", 2, multipleChildResult.size()); + ResultConfig inputResult = multipleChildResult.get("input"); + ResultConfig successResult = multipleChildResult.get("success"); + assertNotNull("inputResult (multipleChildResult) is null ?", inputResult); + assertNotNull("successResult (multipleChildResult) is null ?", successResult); + Map inputResultParams = inputResult.getParams(); + Map successResultParams = successResult.getParams(); + assertNotNull("inputResultParams (multipleChildResult) is null ?", inputResultParams); + assertNotNull("successResultParams (multipleChildResult) is null ?", successResultParams); + assertEquals("inputResult (multipleChildResult) resultParam value not equal to fakeBodyString ?", + fakeBodyString, inputResultParams.get(resultParam)); + assertEquals("successResult (multipleChildResult) resultParam value not equal to fakeBodyString2 ?", + fakeBodyString2, successResultParams.get(resultParam)); + } + + /** + * Test loadGlobalResults() to ensure consistent results for processing + * in XML configuration elements. + * + * @throws Exception + */ + public void testLoadGlobalResults() throws Exception { + // Set up test using two mock DOM Elements: + // 1) A mock "package" Element containing a mock "global-results" Element with a two "result" + // child Elements that each contains a single TEXT_NODE Node. This simulates a typical result + // from a SAX parser parsing the result tag body. + // 2) A mock "package" Element containing a mock "global-results" Element with two "result" + // child Elements that each contain multiple TEXT_NODE Nodes. This simulates an unusal result + // from a SAX parser parsing the result tag body. + final String fakeBodyString = "/SomePath/SomePath/SomePath/SomeJSP.jsp"; + final String fakeBodyString2 = "/SomePath2/SomePath2/SomePath2/SomeJSP2.jsp"; + final String resultParam = "nonNullDefaultParam"; + PackageConfig.Builder testPackageConfigBuilder = new PackageConfig.Builder("resultsPackage"); + ResultTypeConfig.Builder resultTypeConfigBuilder = new ResultTypeConfig.Builder("dispatcher", (String) ServletDispatcherResult.class.getName()); + resultTypeConfigBuilder.defaultResultParam(resultParam); + ResultTypeConfig resultTypeConfig = resultTypeConfigBuilder.build(); + testPackageConfigBuilder.addResultTypeConfig(resultTypeConfig); + List singleStringList = new ArrayList(1); + List singleStringList2 = new ArrayList(1); + List multipleStringList = new ArrayList(4); + List multipleStringList2 = new ArrayList(4); + singleStringList.add(fakeBodyString); + singleStringList2.add(fakeBodyString2); + multipleStringList.add("/SomePath"); + multipleStringList.add("/SomePath/"); + multipleStringList.add("SomePath/"); + multipleStringList.add("SomeJSP.jsp"); + multipleStringList2.add("/SomePath2"); + multipleStringList2.add("/SomePath2/"); + multipleStringList2.add("SomePath2/"); + multipleStringList2.add("SomeJSP2.jsp"); + NodeList mockNodeListSingleChild = new MockNodeList(singleStringList); + NodeList mockNodeListSingleChild2 = new MockNodeList(singleStringList2); + NodeList mockNodeListMultipleChild = new MockNodeList(multipleStringList); + NodeList mockNodeListMultipleChild2 = new MockNodeList(multipleStringList2); + Element mockSingleChildResultElement = new MockElement("result", fakeBodyString, + "result", fakeBodyString, Node.TEXT_NODE, mockNodeListSingleChild, null); + mockSingleChildResultElement.setAttribute("name", "input"); + mockSingleChildResultElement.setAttribute("type", "dispatcher"); + Element mockSingleChildResultElement2 = new MockElement("result", fakeBodyString2, + "result", fakeBodyString2, Node.TEXT_NODE, mockNodeListSingleChild2, null); + mockSingleChildResultElement2.setAttribute("name", "success"); + mockSingleChildResultElement2.setAttribute("type", "dispatcher"); + Element mockMultipleChildAllowedMethodsElement = new MockElement("result", fakeBodyString, + "result", fakeBodyString, Node.TEXT_NODE, mockNodeListMultipleChild, null); + mockMultipleChildAllowedMethodsElement.setAttribute("name", "input2"); + mockMultipleChildAllowedMethodsElement.setAttribute("type", "dispatcher"); + Element mockMultipleChildAllowedMethodsElement2 = new MockElement("result", fakeBodyString, + "result", fakeBodyString2, Node.TEXT_NODE, mockNodeListMultipleChild2, null); + mockMultipleChildAllowedMethodsElement2.setAttribute("name", "success2"); + mockMultipleChildAllowedMethodsElement2.setAttribute("type", "dispatcher"); + MockNodeList mockGlobalResultsElementChildrenSingle = new MockNodeList(); + mockGlobalResultsElementChildrenSingle.addToNodeList(mockSingleChildResultElement); + mockGlobalResultsElementChildrenSingle.addToNodeList(mockSingleChildResultElement2); + MockNodeList mockGlobalResultsElementChildrenMultiple = new MockNodeList(); + mockGlobalResultsElementChildrenMultiple.addToNodeList(mockMultipleChildAllowedMethodsElement); + mockGlobalResultsElementChildrenMultiple.addToNodeList(mockMultipleChildAllowedMethodsElement2); + Element mockGlobalResultsElementSingle = new MockElement("global-results", "fakeBody", "global-results", "fakeValue", + Node.TEXT_NODE, mockGlobalResultsElementChildrenSingle, null); + Element mockGlobalResultsGlobalResultsElementMultiple = new MockElement("global-results", "fakeBody", "global-results", "fakeValue", + Node.TEXT_NODE, mockGlobalResultsElementChildrenMultiple, null); + MockNodeList mockPackageElementChildrenSingle = new MockNodeList(); + mockPackageElementChildrenSingle.addToNodeList(mockGlobalResultsElementSingle); + MockNodeList mockPackageElementChildrenMultiple = new MockNodeList(); + mockPackageElementChildrenMultiple.addToNodeList(mockGlobalResultsGlobalResultsElementMultiple); + Element mockPackageElementSingle = new MockElement("package", "fakeBody", "package", "fakeValue", + Node.TEXT_NODE, mockPackageElementChildrenSingle, null); + Element mockPackageElementMultiple = new MockElement("package", "fakeBody", "package", "fakeValue", + Node.TEXT_NODE, mockPackageElementChildrenMultiple, null); + // Attempt the global laod method using single child Elements first, and confirm the results are as expected. + XmlConfigurationProvider prov = new XmlConfigurationProvider("com/opensymphony/xwork2/config/providers/xwork- test.xml", false); + prov.loadGlobalResults(testPackageConfigBuilder, mockPackageElementSingle); + PackageConfig testPackageConfig = testPackageConfigBuilder.build(); + Map currentGlobalResults = testPackageConfig.getAllGlobalResults(); + assertNotNull("currentGlobalResults is null ?", currentGlobalResults); + assertEquals("currentGlobalResults size not 2 ?", 2, currentGlobalResults.size()); + ResultConfig inputResult = currentGlobalResults.get("input"); + ResultConfig successResult = currentGlobalResults.get("success"); + assertNotNull("inputResult (currentGlobalResults - single) is null ?", inputResult); + assertNotNull("successResult (currentGlobalResults - single) is null ?", successResult); + Map inputResultParams = inputResult.getParams(); + Map successResultParams = successResult.getParams(); + assertNotNull("inputResultParams (currentGlobalResults - single) is null ?", inputResultParams); + assertNotNull("successResultParams (currentGlobalResults - single) is null ?", successResultParams); + assertEquals("inputResult (currentGlobalResults - single) resultParam value not equal to fakeBodyString ?", + fakeBodyString, inputResultParams.get(resultParam)); + assertEquals("successResult (currentGlobalResults - single) resultParam value not equal to fakeBodyString2 ?", + fakeBodyString2, successResultParams.get(resultParam)); + // Attempt the global laod method using mutliple child Elements next, and confirm the results are as expected. + prov.loadGlobalResults(testPackageConfigBuilder, mockPackageElementMultiple); + testPackageConfig = testPackageConfigBuilder.build(); + currentGlobalResults = testPackageConfig.getAllGlobalResults(); + assertNotNull("currentGlobalResults is null ?", currentGlobalResults); + assertEquals("currentGlobalResults size not 4 ?", 4, currentGlobalResults.size()); + ResultConfig inputResult2 = currentGlobalResults.get("input2"); + ResultConfig successResult2 = currentGlobalResults.get("success2"); + assertNotNull("inputResult2 (currentGlobalResults - multiple) is null ?", inputResult2); + assertNotNull("successResult2 (currentGlobalResults - multiple) is null ?", successResult2); + Map inputResultParams2 = inputResult2.getParams(); + Map successResultParams2 = successResult2.getParams(); + assertNotNull("inputResultParams2 (currentGlobalResults - multiple) is null ?", inputResultParams2); + assertNotNull("successResultParams2 (currentGlobalResults - multiple) is null ?", successResultParams2); + assertEquals("inputResult2 (currentGlobalResults - multiple) resultParam value not equal to fakeBodyString ?", + fakeBodyString, inputResultParams2.get(resultParam)); + assertEquals("successResult2 (currentGlobalResults - multiple) resultParam value not equal to fakeBodyString2 ?", + fakeBodyString2, successResultParams2.get(resultParam)); + // Confirm the previous global results are still present + inputResult = currentGlobalResults.get("input"); + successResult = currentGlobalResults.get("success"); + assertNotNull("inputResult (currentGlobalResults - single) is null ?", inputResult); + assertNotNull("successResult (currentGlobalResults - single) is null ?", successResult); + inputResultParams = inputResult.getParams(); + successResultParams = successResult.getParams(); + assertNotNull("inputResultParams (currentGlobalResults - single) is null ?", inputResultParams); + assertNotNull("successResultParams (currentGlobalResults - single) is null ?", successResultParams); + assertEquals("inputResult (currentGlobalResults - single) resultParam value not equal to fakeBodyString ?", + fakeBodyString, inputResultParams.get(resultParam)); + assertEquals("successResult (currentGlobalResults - single) resultParam value not equal to fakeBodyString2 ?", + fakeBodyString2, successResultParams.get(resultParam)); + inputResult = currentGlobalResults.get("input"); + successResult = currentGlobalResults.get("success"); + assertNotNull("inputResult (currentGlobalResults - single) is null ?", inputResult); + assertNotNull("successResult (currentGlobalResults - single) is null ?", successResult); + inputResultParams = inputResult.getParams(); + successResultParams = successResult.getParams(); + assertNotNull("inputResultParams (currentGlobalResults - single) is null ?", inputResultParams); + assertNotNull("successResultParams (currentGlobalResults - single) is null ?", successResultParams); + assertEquals("inputResult (currentGlobalResults - single) resultParam value not equal to fakeBodyString ?", + fakeBodyString, inputResultParams.get(resultParam)); + assertEquals("successResult (currentGlobalResults - single) resultParam value not equal to fakeBodyString2 ?", + fakeBodyString2, successResultParams.get(resultParam)); + } + + /** + * Mock NodeList. + * + * Provides minimal functionality to permit limited mock DOM testing. + */ + protected class MockNodeList implements org.w3c.dom.NodeList { + List nodeList; + + public MockNodeList() { + this.nodeList = new ArrayList(0); + } + + /** + * Produces TEXT_NODE Nodes based on the input List of Strings. Node names + * follow a simple pattern "nodeX" where X is the index. + * + * @param stringList + */ + public MockNodeList(List stringList) { + if (stringList != null) { + final int nodeListLength = stringList.size(); + this.nodeList = new ArrayList(nodeListLength); + for (int index = 0; index < nodeListLength; index++) { + this.nodeList.add(new MockNode("node" + index, stringList.get(index), Node.TEXT_NODE, null, null)); + } + } else { + this.nodeList = new ArrayList(0); + } + } + + public MockNodeList(NodeList nodeList) { + if (nodeList != null) { + final int nodeListLength = nodeList.getLength(); + this.nodeList = new ArrayList(nodeListLength); + for (int index = 0; index < nodeListLength; index++) { + this.nodeList.add(nodeList.item(index)); + } + } else { + this.nodeList = new ArrayList(0); + } + } + + public void addToNodeList(List nodeList) { + if (nodeList != null) { + final int nodeListLength = nodeList.size(); + for (int index = 0; index < nodeListLength; index++) { + this.nodeList.add(nodeList.get(index)); + } + } else { + this.nodeList = new ArrayList(0); + } + } + + public void addToNodeList(Node node) { + nodeList.add(node); + } + + public void setParentForListNodes(Node parentNode) { + if (nodeList != null) { + final int nodeListLength = nodeList.size(); + for (int index = 0; index < nodeListLength; index++) { + Node node = nodeList.get(index); + if (node instanceof MockNode) { + MockNode mockNode = (MockNode) node; + mockNode.setParentNode(parentNode); + } + } + } + } + + @Override + public Node item(int index) { + return (nodeList != null ? nodeList.get(index) : null); + } + + @Override + public int getLength() { + return (nodeList != null ? nodeList.size() : 0); + } + } + + /** + * MockNode + * + * Provides minimal functionality to permit limited mock DOM testing. + */ + protected class MockNode implements org.w3c.dom.Node { + final private String nodeName; + private String nodeValue; + final private short nodeType; + private Node parentNode; + final private NodeList childNodes; + + public MockNode(String nodeName, String nodeValue, short nodeType, NodeList childNodes, Node parentNode) { + this.nodeName = nodeName; + this.nodeValue = nodeValue; + this.nodeType = nodeType; + this.childNodes = childNodes; + this.parentNode = parentNode; + + if (childNodes instanceof MockNodeList) { + MockNodeList mockNodeList = (MockNodeList) childNodes; + mockNodeList.setParentForListNodes(this); + } + } + + @Override + public String getNodeName() { + return nodeName; + } + + @Override + public String getNodeValue() throws DOMException { + return nodeValue; + } + + @Override + public void setNodeValue(String nodeValue) throws DOMException { + this.nodeValue = nodeValue; + } + + @Override + public short getNodeType() { + return nodeType; + } + + public void setParentNode(Node parentNode) { + this.parentNode = parentNode; + } + + @Override + public Node getParentNode() { + return parentNode; + } + + @Override + public NodeList getChildNodes() { + return childNodes; + } + + @Override + public Node getFirstChild() { + if (childNodes != null) { + return childNodes.item(0); + } else { + return null; + } + } + + @Override + public Node getLastChild() { + if (childNodes != null) { + return childNodes.item(childNodes.getLength()); + } else { + return null; + } + } + + @Override + public Node getPreviousSibling() { + throw new UnsupportedOperationException("No mock support."); + } + + @Override + public Node getNextSibling() { + throw new UnsupportedOperationException("No mock support."); + } + + @Override + public NamedNodeMap getAttributes() { + throw new UnsupportedOperationException("No mock support."); + } + + @Override + public Document getOwnerDocument() { + throw new UnsupportedOperationException("No mock support."); + } + + @Override + public Node insertBefore(Node newChild, Node refChild) throws DOMException { + throw new UnsupportedOperationException("No mock support."); + } + + @Override + public Node replaceChild(Node newChild, Node oldChild) throws DOMException { + throw new UnsupportedOperationException("No mock support."); + } + + @Override + public Node removeChild(Node oldChild) throws DOMException { + throw new UnsupportedOperationException("No mock support."); + } + + @Override + public Node appendChild(Node newChild) throws DOMException { + throw new UnsupportedOperationException("No mock support."); + } + + @Override + public boolean hasChildNodes() { + return (childNodes != null ? childNodes.getLength() > 0 : false); + } + + @Override + public Node cloneNode(boolean deep) { + throw new UnsupportedOperationException("No mock support."); + } + + @Override + public void normalize() { + throw new UnsupportedOperationException("No mock support."); + } + + @Override + public boolean isSupported(String feature, String version) { + throw new UnsupportedOperationException("No mock support."); + } + + @Override + public String getNamespaceURI() { + throw new UnsupportedOperationException("No mock support."); + } + + @Override + public String getPrefix() { + throw new UnsupportedOperationException("No mock support."); + } + + @Override + public void setPrefix(String prefix) throws DOMException { + throw new UnsupportedOperationException("No mock support."); + } + + @Override + public String getLocalName() { + throw new UnsupportedOperationException("No mock support."); + } + + @Override + public boolean hasAttributes() { + throw new UnsupportedOperationException("No mock support."); + } + + @Override + public String getBaseURI() { + throw new UnsupportedOperationException("No mock support."); + } + + @Override + public short compareDocumentPosition(Node other) throws DOMException { + throw new UnsupportedOperationException("No mock support."); + } + + @Override + public String getTextContent() throws DOMException { + return nodeValue; + } + + @Override + public void setTextContent(String textContent) throws DOMException { + setNodeValue(textContent); + } + + @Override + public boolean isSameNode(Node other) { + throw new UnsupportedOperationException("No mock support."); + } + + @Override + public String lookupPrefix(String namespaceURI) { + throw new UnsupportedOperationException("No mock support."); + } + + @Override + public boolean isDefaultNamespace(String namespaceURI) { + throw new UnsupportedOperationException("No mock support."); + } + + @Override + public String lookupNamespaceURI(String prefix) { + throw new UnsupportedOperationException("No mock support."); + } + + @Override + public boolean isEqualNode(Node arg) { + throw new UnsupportedOperationException("No mock support."); + } + + @Override + public Object getFeature(String feature, String version) { + throw new UnsupportedOperationException("No mock support."); + } + + @Override + public Object setUserData(String key, Object data, UserDataHandler handler) { + throw new UnsupportedOperationException("No mock support."); + } + + @Override + public Object getUserData(String key) { + throw new UnsupportedOperationException("No mock support."); + } + } + + /** + * Mock Element. + * + * Provides minimal functionality to permit limited mock DOM testing. + */ + protected class MockElement extends MockNode implements org.w3c.dom.Element { + final private String tagName; + final private String tagBody; + final private Map attributes; + + public MockElement(String tagName, String tagBody, + String nodeName, String nodeValue, short nodeType, NodeList childNodes, Node parentNode) { + super(nodeName, nodeValue, nodeType, childNodes, parentNode); + this.tagName = nodeName; + this.tagBody = nodeValue; + attributes = new HashMap<>(); + } + + @Override + public String getTagName() { + return tagName; + } + + @Override + public String getAttribute(String name) { + return attributes.get(name); + } + + @Override + public void setAttribute(String name, String value) throws DOMException { + attributes.put(name, value); + } + + @Override + public void removeAttribute(String name) throws DOMException { + attributes.remove(name); + } + + @Override + public Attr getAttributeNode(String name) { + throw new UnsupportedOperationException("No mock support."); + } + + @Override + public Attr setAttributeNode(Attr newAttr) throws DOMException { + throw new UnsupportedOperationException("No mock support."); + } + + @Override + public Attr removeAttributeNode(Attr oldAttr) throws DOMException { + throw new UnsupportedOperationException("No mock support."); + } + + @Override + public NodeList getElementsByTagName(String name) { + if (name != null) { + final NodeList tempChildren = getChildNodes(); + final MockNodeList result = new MockNodeList(); + if (tempChildren != null) { + final int nodeListLength = tempChildren.getLength(); + for (int index = 0; index < nodeListLength; index++) { + Node node = tempChildren.item(index); + if (node instanceof Element) { + Element element = (Element) node; + if (name.equals(element.getTagName())) { + result.addToNodeList(element); + } + } + } + } + return result; + } else { + return new MockNodeList((NodeList) null); + } + } + + @Override + public String getAttributeNS(String namespaceURI, String localName) throws DOMException { + throw new UnsupportedOperationException("No mock support."); + } + + @Override + public void setAttributeNS(String namespaceURI, String qualifiedName, String value) throws DOMException { + throw new UnsupportedOperationException("No mock support."); + } + + @Override + public void removeAttributeNS(String namespaceURI, String localName) throws DOMException { + throw new UnsupportedOperationException("No mock support."); + } + + @Override + public Attr getAttributeNodeNS(String namespaceURI, String localName) throws DOMException { + return null; // Sufficient for Result processing + } + + @Override + public Attr setAttributeNodeNS(Attr newAttr) throws DOMException { + throw new UnsupportedOperationException("No mock support."); + } + + @Override + public NodeList getElementsByTagNameNS(String namespaceURI, String localName) throws DOMException { + throw new UnsupportedOperationException("No mock support."); + } + + @Override + public boolean hasAttribute(String name) { + throw new UnsupportedOperationException("No mock support."); + } + + @Override + public boolean hasAttributeNS(String namespaceURI, String localName) throws DOMException { + throw new UnsupportedOperationException("No mock support."); + } + + @Override + public TypeInfo getSchemaTypeInfo() { + throw new UnsupportedOperationException("No mock support."); + } + + @Override + public void setIdAttribute(String name, boolean isId) throws DOMException { + throw new UnsupportedOperationException("No mock support."); + } + + @Override + public void setIdAttributeNS(String namespaceURI, String localName, boolean isId) throws DOMException { + throw new UnsupportedOperationException("No mock support."); + } + + @Override + public void setIdAttributeNode(Attr idAttr, boolean isId) throws DOMException { + throw new UnsupportedOperationException("No mock support."); + } + } + }