This is an automated email from the ASF dual-hosted git repository. lukaszlenart pushed a commit to branch WW-3476-scoped-constants in repository https://gitbox.apache.org/repos/asf/struts.git
commit 06d6e3c67a4b901f9b427c2d420418358983fd1b Author: Lukasz Lenart <[email protected]> AuthorDate: Sat Oct 10 17:11:12 2026 +0200 WW-3476 feat(config): add struts-7.5.dtd with <scoped-constant> Co-Authored-By: Claude Opus 5.5 <[email protected]> --- .../config/StrutsXmlConfigurationProvider.java | 3 +- .../providers/XmlDocConfigurationProvider.java | 17 +++ core/src/main/resources/struts-7.5.dtd | 164 +++++++++++++++++++++ .../XmlConfigurationProviderPackagesTest.java | 31 ++++ .../xwork-test-scoped-constants-duplicate.xml | 30 ++++ .../xwork-test-scoped-constants-old-dtd.xml | 29 ++++ .../providers/xwork-test-scoped-constants.xml | 34 +++++ 7 files changed, 307 insertions(+), 1 deletion(-) diff --git a/core/src/main/java/org/apache/struts2/config/StrutsXmlConfigurationProvider.java b/core/src/main/java/org/apache/struts2/config/StrutsXmlConfigurationProvider.java index 12a57a3a8..6b0a9dce9 100644 --- a/core/src/main/java/org/apache/struts2/config/StrutsXmlConfigurationProvider.java +++ b/core/src/main/java/org/apache/struts2/config/StrutsXmlConfigurationProvider.java @@ -51,7 +51,8 @@ public class StrutsXmlConfigurationProvider extends XmlConfigurationProvider { "-//Apache Software Foundation//DTD Struts Configuration 2.3//EN", "struts-2.3.dtd", "-//Apache Software Foundation//DTD Struts Configuration 2.5//EN", "struts-2.5.dtd", "-//Apache Software Foundation//DTD Struts Configuration 6.0//EN", "struts-6.0.dtd", - "-//Apache Software Foundation//DTD Struts Configuration 6.5//EN", "struts-6.5.dtd"); + "-//Apache Software Foundation//DTD Struts Configuration 6.5//EN", "struts-6.5.dtd", + "-//Apache Software Foundation//DTD Struts Configuration 7.5//EN", "struts-7.5.dtd"); private File baseDir = null; private final String filename; diff --git a/core/src/main/java/org/apache/struts2/config/providers/XmlDocConfigurationProvider.java b/core/src/main/java/org/apache/struts2/config/providers/XmlDocConfigurationProvider.java index da44b71f6..b6c606e66 100644 --- a/core/src/main/java/org/apache/struts2/config/providers/XmlDocConfigurationProvider.java +++ b/core/src/main/java/org/apache/struts2/config/providers/XmlDocConfigurationProvider.java @@ -424,6 +424,8 @@ public abstract class XmlDocConfigurationProvider implements ConfigurationProvid LOG.debug("Loaded {}", newPackage); + loadScopedConstants(newPackage, packageElement); + // add result types (and default result) to this package addResultTypes(newPackage, packageElement); @@ -839,6 +841,21 @@ public abstract class XmlDocConfigurationProvider implements ConfigurationProvid } } + /** + * @since 7.5.0 + */ + protected void loadScopedConstants(PackageConfig.Builder packageContext, Element packageElement) { + Set<String> declared = new HashSet<>(); + iterateChildrenByTagName(packageElement, "scoped-constant", constantElement -> { + String name = constantElement.getAttribute("name"); + if (!declared.add(name)) { + throw new ConfigurationException(format("Package [%s] declares scoped constant [%s] more than once", + packageContext.getName(), name), constantElement); + } + packageContext.addScopedConstant(name, constantElement.getAttribute("value")); + }); + } + /** * Load all the global results for this package from the XML element. * diff --git a/core/src/main/resources/struts-7.5.dtd b/core/src/main/resources/struts-7.5.dtd new file mode 100644 index 000000000..307f5f0e0 --- /dev/null +++ b/core/src/main/resources/struts-7.5.dtd @@ -0,0 +1,164 @@ +<?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 7.5//EN" + "https://struts.apache.org/dtds/struts-7.5.dtd"> +--> + +<!ELEMENT struts ((package|include|bean|constant)*,bean-selection?, unknown-handler-stack?)> +<!ATTLIST struts + order CDATA #IMPLIED +> + +<!ELEMENT package (scoped-constant*, 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 scoped-constant EMPTY> +<!ATTLIST scoped-constant + name CDATA #REQUIRED + value CDATA #REQUIRED +> + +<!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 --> + diff --git a/core/src/test/java/org/apache/struts2/config/providers/XmlConfigurationProviderPackagesTest.java b/core/src/test/java/org/apache/struts2/config/providers/XmlConfigurationProviderPackagesTest.java index 0c4bd9714..25350d5c1 100644 --- a/core/src/test/java/org/apache/struts2/config/providers/XmlConfigurationProviderPackagesTest.java +++ b/core/src/test/java/org/apache/struts2/config/providers/XmlConfigurationProviderPackagesTest.java @@ -24,7 +24,9 @@ import org.apache.struts2.config.RuntimeConfiguration; import org.apache.struts2.config.entities.PackageConfig; import java.util.List; +import java.util.Map; +import static org.assertj.core.api.Assertions.assertThat; /** * Created by IntelliJ IDEA. @@ -198,6 +200,35 @@ public class XmlConfigurationProviderPackagesTest extends ConfigurationTestBase assertEquals(expectedNoDefaultClassRefPackage, configuration.getPackageConfig(noDefaultClassRefPkgName)); } + public void testScopedConstantsLoadAndInherit() throws ConfigurationException { + buildConfigurationProvider(getXmlConfigFilePath("xwork-test-scoped-constants.xml")); + + PackageConfig parent = configuration.getPackageConfig("parent"); + PackageConfig child = configuration.getPackageConfig("child"); + + assertEquals(Map.of("first", "parent-first", "second", "parent-second"), parent.getScopedConstants()); + assertEquals(Map.of("second", "child-second"), child.getScopedConstants()); + assertEquals(Map.of("first", "parent-first", "second", "child-second"), child.getAllScopedConstants()); + } + + public void testDuplicateScopedConstantInOnePackageFails() { + try { + buildConfigurationProvider(getXmlConfigFilePath("xwork-test-scoped-constants-duplicate.xml")); + fail("Should have thrown a ConfigurationException"); + } catch (ConfigurationException e) { + assertThat(e).hasStackTraceContaining("Package [dup] declares scoped constant [first] more than once"); + } + } + + public void testScopedConstantIsRejectedUnderOlderDtd() { + try { + buildConfigurationProvider(getXmlConfigFilePath("xwork-test-scoped-constants-old-dtd.xml")); + fail("Should have thrown a ConfigurationException"); + } catch (ConfigurationException e) { + assertThat(e).hasStackTraceContaining("\"scoped-constant\" must be declared"); + } + } + private String getXmlConfigFilePath(String fileName) { return "org/apache/struts2/config/providers/" + fileName; } diff --git a/core/src/test/resources/org/apache/struts2/config/providers/xwork-test-scoped-constants-duplicate.xml b/core/src/test/resources/org/apache/struts2/config/providers/xwork-test-scoped-constants-duplicate.xml new file mode 100644 index 000000000..92b9ec116 --- /dev/null +++ b/core/src/test/resources/org/apache/struts2/config/providers/xwork-test-scoped-constants-duplicate.xml @@ -0,0 +1,30 @@ +<?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 7.5//EN" + "struts-7.5.dtd"> +<struts> + <package name="dup" namespace="/dup"> + <scoped-constant name="first" value="one"/> + <scoped-constant name="first" value="two"/> + </package> +</struts> diff --git a/core/src/test/resources/org/apache/struts2/config/providers/xwork-test-scoped-constants-old-dtd.xml b/core/src/test/resources/org/apache/struts2/config/providers/xwork-test-scoped-constants-old-dtd.xml new file mode 100644 index 000000000..35817e260 --- /dev/null +++ b/core/src/test/resources/org/apache/struts2/config/providers/xwork-test-scoped-constants-old-dtd.xml @@ -0,0 +1,29 @@ +<?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.5//EN" + "struts-6.5.dtd"> +<struts> + <package name="old" namespace="/old"> + <scoped-constant name="first" value="one"/> + </package> +</struts> diff --git a/core/src/test/resources/org/apache/struts2/config/providers/xwork-test-scoped-constants.xml b/core/src/test/resources/org/apache/struts2/config/providers/xwork-test-scoped-constants.xml new file mode 100644 index 000000000..ef0797de8 --- /dev/null +++ b/core/src/test/resources/org/apache/struts2/config/providers/xwork-test-scoped-constants.xml @@ -0,0 +1,34 @@ +<?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 7.5//EN" + "struts-7.5.dtd"> +<struts> + <package name="parent" namespace="/parent"> + <scoped-constant name="first" value="parent-first"/> + <scoped-constant name="second" value="parent-second"/> + </package> + + <package name="child" namespace="/child" extends="parent"> + <scoped-constant name="second" value="child-second"/> + </package> +</struts>
