mirror of
https://github.com/apache/struts.git
synced 2026-08-11 01:27:14 +00:00
WW-5409 introduce final attribute to package element which make them unextendable
This commit is contained in:
@@ -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;
|
||||
|
||||
+16
-9
@@ -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;
|
||||
|
||||
@@ -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 -->
|
||||
|
||||
+56
-12
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
+37
@@ -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>
|
||||
+37
@@ -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>
|
||||
Reference in New Issue
Block a user