This is an automated email from the ASF dual-hosted git repository. lukaszlenart pushed a commit to branch WW-2934-skip-validators-on-conversion-error in repository https://gitbox.apache.org/repos/asf/struts.git
commit 5ba95dcd8245fc62f6e25c6f1ff0388744e914bc Author: Lukasz Lenart <[email protected]> AuthorDate: Thu Jul 23 21:44:08 2026 +0200 WW-2934 test(core): cover annotation manager + document conversion-error skip exemption Co-Authored-By: Claude Opus 4.8 <[email protected]> --- .../validator/DefaultActionValidatorManager.java | 7 +++++ .../org/apache/struts2/default.properties | 1 + .../DefaultActionValidatorManagerTest.java | 33 ++++++++++++++++++++++ 3 files changed, 41 insertions(+) diff --git a/core/src/main/java/org/apache/struts2/validator/DefaultActionValidatorManager.java b/core/src/main/java/org/apache/struts2/validator/DefaultActionValidatorManager.java index f5c40a1fb..9e1c5a7a6 100644 --- a/core/src/main/java/org/apache/struts2/validator/DefaultActionValidatorManager.java +++ b/core/src/main/java/org/apache/struts2/validator/DefaultActionValidatorManager.java @@ -99,6 +99,13 @@ public class DefaultActionValidatorManager implements ActionValidatorManager { this.reloadingConfigs = Boolean.parseBoolean(reloadingConfigs); } + /** + * When set to {@code true}, a field's remaining validators are skipped once that field has a + * type-conversion error. The field's own {@link ConversionErrorFieldValidator} still runs (so its + * message is still shown), and action-level validators are unaffected. Defaults to {@code false}. + * + * @param skipValidatorsOnConversionError whether to skip a field's remaining validators when it has a conversion error. + */ @Inject(value = StrutsConstants.STRUTS_VALIDATORS_SKIP_VALIDATORS_ON_CONVERSION_ERROR, required = false) public void setSkipValidatorsOnConversionError(String skipValidatorsOnConversionError) { this.skipValidatorsOnConversionError = Boolean.parseBoolean(skipValidatorsOnConversionError); diff --git a/core/src/main/resources/org/apache/struts2/default.properties b/core/src/main/resources/org/apache/struts2/default.properties index 0dcb5ab80..76a1ff9a2 100644 --- a/core/src/main/resources/org/apache/struts2/default.properties +++ b/core/src/main/resources/org/apache/struts2/default.properties @@ -148,6 +148,7 @@ struts.devMode = false ### When set to true, a field's remaining validators are skipped once that field ### has a type conversion error, avoiding a duplicate error (WW-2934). +### The field's own conversion validator still runs, so its message is still shown. ### valid values are: true, false (false is the default) struts.validators.skipValidatorsOnConversionError = false diff --git a/core/src/test/java/org/apache/struts2/validator/DefaultActionValidatorManagerTest.java b/core/src/test/java/org/apache/struts2/validator/DefaultActionValidatorManagerTest.java index f2ad1550f..e868bb669 100644 --- a/core/src/test/java/org/apache/struts2/validator/DefaultActionValidatorManagerTest.java +++ b/core/src/test/java/org/apache/struts2/validator/DefaultActionValidatorManagerTest.java @@ -19,11 +19,14 @@ package org.apache.struts2.validator; import org.apache.struts2.ActionContext; +import org.apache.struts2.ActionInvocation; +import org.apache.struts2.ActionProxy; import org.apache.struts2.FileManagerFactory; import org.apache.struts2.SimpleAction; import org.apache.struts2.TestBean; import org.apache.struts2.ValidationOrderAction; import org.apache.struts2.XWorkTestCase; +import org.apache.struts2.config.entities.ActionConfig; import org.apache.struts2.conversion.impl.ConversionData; import org.apache.struts2.interceptor.ValidationAware; import org.apache.struts2.test.DataAware2; @@ -39,6 +42,7 @@ import org.apache.struts2.validator.validators.RequiredStringValidator; import org.apache.struts2.validator.validators.ShortRangeFieldValidator; import org.apache.struts2.StrutsException; import org.assertj.core.api.Assertions; +import org.easymock.EasyMock; import org.xml.sax.SAXParseException; import java.util.ArrayList; @@ -432,4 +436,33 @@ public class DefaultActionValidatorManagerTest extends XWorkTestCase { assertTrue(action.getActionErrors().contains("Action level always fails")); } + public void testConversionError_skipFiresForAnnotationManager() throws Exception { + // AnnotationActionValidatorManager.buildValidatorKey() needs an ActionInvocation/ActionProxy + // on the ActionContext to resolve the package name/config for the validator cache key. + ActionConfig config = new ActionConfig.Builder("packageName", "name", "").build(); + ActionInvocation invocation = EasyMock.createNiceMock(ActionInvocation.class); + ActionProxy proxy = EasyMock.createNiceMock(ActionProxy.class); + EasyMock.expect(invocation.getProxy()).andReturn(proxy).anyTimes(); + EasyMock.expect(proxy.getMethod()).andReturn("execute").anyTimes(); + EasyMock.expect(proxy.getConfig()).andReturn(config).anyTimes(); + EasyMock.replay(invocation); + EasyMock.replay(proxy); + ActionContext.getContext().withActionInvocation(invocation); + + AnnotationActionValidatorManager annMgr = container.inject(AnnotationActionValidatorManager.class); + annMgr.setSkipValidatorsOnConversionError("true"); + + ConversionErrorSkipAction action = new ConversionErrorSkipAction(); + ActionContext.getContext().getConversionErrors() + .put("age", new ConversionData(new String[]{"one"}, Integer.class)); + + annMgr.validate(action, null); + + List<String> ageErrors = action.getFieldErrors().get("age"); + assertNotNull(ageErrors); + // required is skipped; the conversion validator itself still runs + assertEquals(1, ageErrors.size()); + assertEquals("Age must be a valid number", ageErrors.get(0)); + } + }
