WW-5409 introduce final attribute to package element which make them unextendable

This commit is contained in:
Jeffery Xie
2024-04-11 12:44:53 +10:00
parent c6d13f1016
commit bf5f29132b
7 changed files with 318 additions and 21 deletions
@@ -47,6 +47,7 @@ public class PackageConfig extends Located implements Comparable<PackageConfig>,
protected String name;
protected String namespace = "";
protected boolean isAbstract = false;
protected boolean isFinal = false; // a final package is unextendable
protected boolean needsRefresh;
protected boolean strictMethodInvocation = true;
@@ -69,6 +70,7 @@ public class PackageConfig extends Located implements Comparable<PackageConfig>,
this.name = orig.name;
this.namespace = orig.namespace;
this.isAbstract = orig.isAbstract;
this.isFinal = orig.isFinal;
this.needsRefresh = orig.needsRefresh;
this.actionConfigs = new LinkedHashMap<>(orig.actionConfigs);
this.globalResultConfigs = new LinkedHashMap<>(orig.globalResultConfigs);
@@ -85,6 +87,10 @@ public class PackageConfig extends Located implements Comparable<PackageConfig>,
return isAbstract;
}
public boolean isFinal() {
return isFinal;
}
public Map<String, ActionConfig> getActionConfigs() {
return actionConfigs;
}
@@ -360,6 +366,7 @@ public class PackageConfig extends Located implements Comparable<PackageConfig>,
PackageConfig that = (PackageConfig) o;
if (isAbstract != that.isAbstract) return false;
if (isFinal != that.isFinal) return false;
if (needsRefresh != that.needsRefresh) return false;
if (strictMethodInvocation != that.strictMethodInvocation) return false;
if (actionConfigs != null ? !actionConfigs.equals(that.actionConfigs) : that.actionConfigs != null)
@@ -404,6 +411,7 @@ public class PackageConfig extends Located implements Comparable<PackageConfig>,
result = 31 * result + name.hashCode();
result = 31 * result + (namespace != null ? namespace.hashCode() : 0);
result = 31 * result + (isAbstract ? 1 : 0);
result = 31 * result + (isFinal ? 1 : 0);
result = 31 * result + (needsRefresh ? 1 : 0);
result = 31 * result + (strictMethodInvocation ? 1 : 0);
return result;
@@ -453,6 +461,11 @@ public class PackageConfig extends Located implements Comparable<PackageConfig>,
return this;
}
public Builder isFinal(boolean isFinal) {
target.isFinal = isFinal;
return this;
}
public Builder defaultInterceptorRef(String name) {
target.defaultInterceptorRef = name;
return this;
@@ -603,8 +603,8 @@ public abstract class XmlDocConfigurationProvider implements ConfigurationProvid
*/
protected PackageConfig.Builder buildPackageContext(Element packageElement) {
String parent = packageElement.getAttribute("extends");
String abstractVal = packageElement.getAttribute("abstract");
boolean isAbstract = parseBoolean(abstractVal);
boolean isAbstract = parseBoolean(packageElement.getAttribute("abstract"));
boolean isFinal = parseBoolean(packageElement.getAttribute("final"));
String name = defaultString(packageElement.getAttribute("name"));
String namespace = defaultString(packageElement.getAttribute("namespace"));
@@ -617,6 +617,7 @@ public abstract class XmlDocConfigurationProvider implements ConfigurationProvid
PackageConfig.Builder cfg = new PackageConfig.Builder(name)
.namespace(namespace)
.isAbstract(isAbstract)
.isFinal(isFinal)
.strictMethodInvocation(strictDMI)
.location(DomHelper.getLocationObject(packageElement));
@@ -627,17 +628,23 @@ public abstract class XmlDocConfigurationProvider implements ConfigurationProvid
// has parents, let's look it up
List<PackageConfig> parents = new ArrayList<>();
for (String parentPackageName : ConfigurationUtil.buildParentListFromString(parent)) {
if (configuration.getPackageConfigNames().contains(parentPackageName)) {
parents.add(configuration.getPackageConfig(parentPackageName));
} else if (declaredPackages.containsKey(parentPackageName)) {
if (configuration.getPackageConfig(parentPackageName) == null) {
addPackage(declaredPackages.get(parentPackageName));
boolean isParentPackageConfigDefined = false;
if (configuration.getPackageConfigNames().contains(parentPackageName)) { // parent package already added to configuration
isParentPackageConfigDefined = true;
} else if (declaredPackages.containsKey(parentPackageName)) { // parent package declared but yet added to configuration
addPackage(declaredPackages.get(parentPackageName));
isParentPackageConfigDefined = true;
}
if (isParentPackageConfigDefined) {
PackageConfig parentPackageConfig = configuration.getPackageConfig(parentPackageName);
if (parentPackageConfig.isFinal()) {
throw new ConfigurationException("Parent package is final and unextendable: " + parentPackageName);
}
parents.add(configuration.getPackageConfig(parentPackageName));
parents.add(parentPackageConfig);
} else {
throw new ConfigurationException("Parent package is not defined: " + parentPackageName);
}
}
if (parents.isEmpty()) {
@@ -54,6 +54,7 @@ public class StrutsXmlConfigurationProvider extends XmlConfigurationProvider {
put("-//Apache Software Foundation//DTD Struts Configuration 2.3//EN", "struts-2.3.dtd");
put("-//Apache Software Foundation//DTD Struts Configuration 2.5//EN", "struts-2.5.dtd");
put("-//Apache Software Foundation//DTD Struts Configuration 6.0//EN", "struts-6.0.dtd");
put("-//Apache Software Foundation//DTD Struts Configuration 6.4.0//EN", "struts-6.4.0.dtd");
}});
private File baseDir = null;
private final String filename;
+158
View File
@@ -0,0 +1,158 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
-->
<!-- START SNIPPET: strutsDtd -->
<!--
Struts configuration DTD.
Use the following DOCTYPE
<!DOCTYPE struts PUBLIC
"-//Apache Software Foundation//DTD Struts Configuration 6.4.0//EN"
"https://struts.apache.org/dtds/struts-6.4.0.dtd">
-->
<!ELEMENT struts ((package|include|bean|constant)*,bean-selection?, unknown-handler-stack?)>
<!ATTLIST struts
order CDATA #IMPLIED
>
<!ELEMENT package (result-types?, interceptors?, default-interceptor-ref?, default-action-ref?, default-class-ref?, global-results?, global-allowed-methods?, global-exception-mappings?, action*)>
<!ATTLIST package
name CDATA #REQUIRED
extends CDATA #IMPLIED
namespace CDATA #IMPLIED
abstract CDATA #IMPLIED
final CDATA #IMPLIED
strict-method-invocation (true|false) "true"
>
<!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-allowed-methods (#PCDATA)>
<!ELEMENT global-exception-mappings (exception-mapping+)>
<!ELEMENT action ((param|result|interceptor-ref|exception-mapping)*,allowed-methods?)>
<!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 allowed-methods (#PCDATA)>
<!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 bean-selection (#PCDATA)>
<!ATTLIST bean-selection
name CDATA #IMPLIED
class 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: strutsDtd -->
@@ -36,10 +36,9 @@ import java.util.List;
public class XmlConfigurationProviderPackagesTest extends ConfigurationTestBase {
public void testBadInheritance() throws ConfigurationException {
final String filename = "com/opensymphony/xwork2/config/providers/xwork-test-bad-inheritance.xml";
ConfigurationProvider provider = null;
try {
provider = buildConfigurationProvider(filename);
provider = buildConfigurationProvider(getXmlConfigFilePath("xwork-test-bad-inheritance.xml"));
fail("Should have thrown a ConfigurationException");
provider.init(configuration);
provider.loadPackages();
@@ -49,8 +48,7 @@ public class XmlConfigurationProviderPackagesTest extends ConfigurationTestBase
}
public void testBasicPackages() throws ConfigurationException {
final String filename = "com/opensymphony/xwork2/config/providers/xwork-test-basic-packages.xml";
ConfigurationProvider provider = buildConfigurationProvider(filename);
ConfigurationProvider provider = buildConfigurationProvider(getXmlConfigFilePath("xwork-test-basic-packages.xml"));
provider.init(configuration);
provider.loadPackages();
@@ -70,8 +68,7 @@ public class XmlConfigurationProviderPackagesTest extends ConfigurationTestBase
}
public void testDefaultPackage() throws ConfigurationException {
final String filename = "com/opensymphony/xwork2/config/providers/xwork-test-default-package.xml";
ConfigurationProvider provider = buildConfigurationProvider(filename);
ConfigurationProvider provider = buildConfigurationProvider(getXmlConfigFilePath("xwork-test-default-package.xml"));
provider.init(configuration);
provider.loadPackages();
@@ -84,8 +81,7 @@ public class XmlConfigurationProviderPackagesTest extends ConfigurationTestBase
}
public void testPackageInheritance() throws ConfigurationException {
final String filename = "com/opensymphony/xwork2/config/providers/xwork-test-package-inheritance.xml";
ConfigurationProvider provider = buildConfigurationProvider(filename);
ConfigurationProvider provider = buildConfigurationProvider(getXmlConfigFilePath("xwork-test-package-inheritance.xml"));
provider.init(configuration);
provider.loadPackages();
@@ -111,7 +107,7 @@ public class XmlConfigurationProviderPackagesTest extends ConfigurationTestBase
assertTrue(multipleParents.contains(defaultPackage));
assertTrue(multipleParents.contains(abstractPackage));
assertTrue(multipleParents.contains(singlePackage));
PackageConfig parentBelow = configuration.getPackageConfig("testParentBelow");
assertEquals(1, parentBelow.getParents().size());
List<PackageConfig> parentBelowParents = parentBelow.getParents();
@@ -129,7 +125,7 @@ public class XmlConfigurationProviderPackagesTest extends ConfigurationTestBase
assertNull(runtimeConfiguration.getActionConfig("/single", "abstract"));
assertNotNull(runtimeConfiguration.getActionConfig("/single", "single"));
assertNull(runtimeConfiguration.getActionConfig("/single", "multiple"));
assertNotNull(runtimeConfiguration.getActionConfig("/parentBelow", "default"));
assertNotNull(runtimeConfiguration.getActionConfig("/parentBelow", "abstract"));
assertNotNull(runtimeConfiguration.getActionConfig("/parentBelow", "single"));
@@ -138,13 +134,57 @@ public class XmlConfigurationProviderPackagesTest extends ConfigurationTestBase
}
public void testPackageWithFinalAttributeLoads() throws ConfigurationException {
ConfigurationProvider provider = buildConfigurationProvider(getXmlConfigFilePath("xwork-test-package-final.xml"));
provider.init(configuration);
provider.loadPackages();
// test expectations
assertEquals(3, configuration.getPackageConfigs().size());
PackageConfig defaultPackage = configuration.getPackageConfig("default");
assertNotNull(defaultPackage);
assertEquals("default", defaultPackage.getName());
// final package extends default
PackageConfig finalPackage = configuration.getPackageConfig("finalPackage");
assertNotNull(finalPackage);
assertEquals("finalPackage", finalPackage.getName());
assertEquals(1, finalPackage.getParents().size());
assertEquals(defaultPackage, finalPackage.getParents().get(0));
// normal package extends default
PackageConfig normalPackage = configuration.getPackageConfig("normalPackage");
assertNotNull(normalPackage);
assertEquals("normalPackage", normalPackage.getName());
assertEquals(1, normalPackage.getParents().size());
assertEquals(defaultPackage, normalPackage.getParents().get(0));
configurationManager.addContainerProvider(provider);
configurationManager.reload();
RuntimeConfiguration runtimeConfiguration = configurationManager.getConfiguration().getRuntimeConfiguration();
assertNotNull(runtimeConfiguration.getActionConfig("/final", "default"));
assertNotNull(runtimeConfiguration.getActionConfig("/final", "actionFinal"));
assertNotNull(runtimeConfiguration.getActionConfig("/normal", "default"));
assertNotNull(runtimeConfiguration.getActionConfig("/normal", "actionNormal"));
}
public void testExtendsFinalPackageThrowsConfigurationException() throws ConfigurationException {
try {
buildConfigurationProvider(getXmlConfigFilePath("xwork-test-package-extends-final.xml"));
} catch (ConfigurationException e) {
assertEquals("Parent package is final and unextendable: parentLevelTwo", e.getMessage());
}
}
public void testDefaultClassRef() throws ConfigurationException {
final String filename = "com/opensymphony/xwork2/config/providers/xwork-test-defaultclassref-package.xml";
final String hasDefaultClassRefPkgName = "hasDefaultClassRef";
final String noDefaultClassRefPkgName = "noDefaultClassRef";
final String testDefaultClassRef = "com.opensymphony.xwork2.ActionSupport";
ConfigurationProvider provider = buildConfigurationProvider(filename);
ConfigurationProvider provider = buildConfigurationProvider(getXmlConfigFilePath("xwork-test-defaultclassref-package.xml"));
provider.init(configuration);
// setup our expectations
@@ -157,4 +197,8 @@ public class XmlConfigurationProviderPackagesTest extends ConfigurationTestBase
assertEquals(expectedDefaultClassRefPackage, configuration.getPackageConfig(hasDefaultClassRefPkgName));
assertEquals(expectedNoDefaultClassRefPackage, configuration.getPackageConfig(noDefaultClassRefPkgName));
}
private String getXmlConfigFilePath(String fileName) {
return "com/opensymphony/xwork2/config/providers/" + fileName;
}
}
@@ -0,0 +1,37 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
-->
<!DOCTYPE struts PUBLIC
"-//Apache Software Foundation//DTD Struts Configuration 6.4.0//EN"
"struts-6.4.0.dtd">
<struts>
<package name="default" namespace="/default">
<action name="default" class="com.opensymphony.xwork2.ActionSupport"/>
</package>
<package name="parentLevelTwo" namespace="/parent2" final="true">
<action name="levelTwo" class="com.opensymphony.xwork2.ActionSupport"/>
</package>
<package name="child" namespace="/child" extends="default,parentLevelTwo">
<action name="single" class="com.opensymphony.xwork2.ActionSupport"/>
</package>
</struts>
@@ -0,0 +1,37 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
-->
<!DOCTYPE struts PUBLIC
"-//Apache Software Foundation//DTD Struts Configuration 6.4.0//EN"
"struts-6.4.0.dtd">
<struts>
<package name="default" namespace="/default">
<action name="default" class="com.opensymphony.xwork2.ActionSupport"/>
</package>
<package name="finalPackage" namespace="/final" extends="default" final="true">
<action name="actionFinal" class="com.opensymphony.xwork2.ActionSupport"/>
</package>
<package name="normalPackage" namespace="/normal" extends="default">
<action name="actionNormal" class="com.opensymphony.xwork2.ActionSupport"/>
</package>
</struts>