This is an automated email from the ASF dual-hosted git repository.
lukaszlenart pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/struts.git
The following commit(s) were added to refs/heads/main by this push:
new e213ef6c8 WW-5713 refactor(tiles): resolve the legacy OGNL flag from
the container's own ServletContext (#1961)
e213ef6c8 is described below
commit e213ef6c8b831cd460eab71db7ddd8a57f7b8110
Author: Lukasz Lenart <[email protected]>
AuthorDate: Thu Sep 17 07:50:53 2026 +0200
WW-5713 refactor(tiles): resolve the legacy OGNL flag from the container's
own ServletContext (#1961)
* WW-5713 refactor(tiles): resolve the legacy OGNL flag from the
container's own ServletContext
The lazy OGNL: evaluator read struts.tiles.ognl.legacy.enabled from the
ServletContext of whichever Tiles Request reached it first, walking the
request wrappers and catching NotAServletEnvironmentException. The Tiles
container is built for exactly one ApplicationContext, so capture that
context's ServletContext at construction and key the Dispatcher lookup to
it. A non-servlet ApplicationContext cannot carry a Dispatcher and now gets
the disabled evaluator outright.
Removal-version mentions are dropped from the Javadoc, the runtime
messages and the README; forRemoval = true and WW-5714 track the removal.
Co-Authored-By: Claude Opus 5 (1M context) <[email protected]>
* WW-5713 test(tiles): keep a single throwing call inside the assertThrows
lambda
Sonar S5778 on nonServletApplicationContextFailsClosedWithoutLookup.
Co-Authored-By: Claude Opus 5 (1M context) <[email protected]>
---------
Co-authored-by: Claude Opus 5 (1M context) <[email protected]>
---
plugins/tiles/README.md | 8 +--
.../tiles/DisabledOgnlAttributeEvaluator.java | 3 +-
.../struts2/tiles/StrutsTilesContainerFactory.java | 46 +++++++--------
.../org/apache/struts2/tiles/TilesConstants.java | 3 +-
.../apache/tiles/ognl/OGNLAttributeEvaluator.java | 6 +-
.../tiles/StrutsTilesContainerFactoryTest.java | 65 +++++++++++++++++-----
.../tiles/TilesOgnlEvaluatorIntegrationTest.java | 7 ++-
7 files changed, 89 insertions(+), 49 deletions(-)
diff --git a/plugins/tiles/README.md b/plugins/tiles/README.md
index 7c2a98f39..47dff6a38 100644
--- a/plugins/tiles/README.md
+++ b/plugins/tiles/README.md
@@ -16,7 +16,7 @@ Applications that temporarily require the legacy raw
evaluator can set the follo
<constant name="struts.tiles.ognl.legacy.enabled" value="true"/>
```
-The plugin resolves this constant from the current web application's Struts
configuration on the first `OGNL:`
-evaluation and caches the result for that evaluator lifecycle. Enabling the
constant produces a one-time migration
-warning when the legacy evaluator is first used. The compatibility constant is
deprecated in Struts 7.4.0; both it
-and the legacy evaluator are targeted for removal in Struts 8.0.0.
+The plugin resolves this constant from the Struts configuration of the web
application that owns the Tiles
+container, on the first `OGNL:` evaluation, and caches the result for that
evaluator lifecycle. Enabling the constant
+produces a one-time migration warning when the legacy evaluator is first used.
The compatibility constant and the
+legacy evaluator are deprecated for removal.
diff --git
a/plugins/tiles/src/main/java/org/apache/struts2/tiles/DisabledOgnlAttributeEvaluator.java
b/plugins/tiles/src/main/java/org/apache/struts2/tiles/DisabledOgnlAttributeEvaluator.java
index 775f6bdfa..42a7db6e3 100644
---
a/plugins/tiles/src/main/java/org/apache/struts2/tiles/DisabledOgnlAttributeEvaluator.java
+++
b/plugins/tiles/src/main/java/org/apache/struts2/tiles/DisabledOgnlAttributeEvaluator.java
@@ -28,8 +28,7 @@ import org.apache.tiles.request.Request;
final class DisabledOgnlAttributeEvaluator extends AbstractAttributeEvaluator {
static final String DISABLED_MESSAGE = "The Tiles OGNL evaluator is
disabled. Migrate the expression to S2:, "
- + "or temporarily enable struts.tiles.ognl.legacy.enabled. Legacy
Tiles OGNL support will be removed in "
- + "Struts 8.0.0.";
+ + "or temporarily enable struts.tiles.ognl.legacy.enabled; legacy
Tiles OGNL support is deprecated for removal.";
@Override
public Object evaluate(String expression, Request request) {
diff --git
a/plugins/tiles/src/main/java/org/apache/struts2/tiles/StrutsTilesContainerFactory.java
b/plugins/tiles/src/main/java/org/apache/struts2/tiles/StrutsTilesContainerFactory.java
index 636a2cfab..056f754cf 100644
---
a/plugins/tiles/src/main/java/org/apache/struts2/tiles/StrutsTilesContainerFactory.java
+++
b/plugins/tiles/src/main/java/org/apache/struts2/tiles/StrutsTilesContainerFactory.java
@@ -71,7 +71,7 @@ import org.apache.tiles.request.Request;
import org.apache.tiles.request.render.BasicRendererFactory;
import org.apache.tiles.request.render.ChainedDelegateRenderer;
import org.apache.tiles.request.render.Renderer;
-import org.apache.tiles.request.servlet.NotAServletEnvironmentException;
+import org.apache.tiles.request.servlet.ServletApplicationContext;
import org.apache.tiles.request.servlet.ServletUtil;
import java.util.ArrayList;
@@ -99,7 +99,7 @@ public class StrutsTilesContainerFactory extends
BasicTilesContainerFactory {
static final String LEGACY_OGNL_WARNING = "Legacy Tiles OGNL evaluation is
enabled through "
+ "struts.tiles.ognl.legacy.enabled. Migrate expressions to S2: or
ordinary Tiles mechanisms; the "
- + "compatibility flag and legacy evaluator will be removed in Struts
8.0.0.";
+ + "compatibility flag and the legacy evaluator are deprecated for
removal.";
private final Boolean legacyOgnlEnabled;
private final AtomicBoolean legacyOgnlWarningLogged = new AtomicBoolean();
@@ -178,7 +178,7 @@ public class StrutsTilesContainerFactory extends
BasicTilesContainerFactory {
BasicAttributeEvaluatorFactory attributeEvaluatorFactory = new
BasicAttributeEvaluatorFactory(new DirectAttributeEvaluator());
attributeEvaluatorFactory.registerAttributeEvaluator(S2,
createStrutsEvaluator());
attributeEvaluatorFactory.registerAttributeEvaluator(I18N,
createI18NEvaluator());
- attributeEvaluatorFactory.registerAttributeEvaluator(OGNL,
createConfiguredOgnlEvaluator());
+ attributeEvaluatorFactory.registerAttributeEvaluator(OGNL,
createConfiguredOgnlEvaluator(applicationContext));
ELAttributeEvaluator elEvaluator =
createELEvaluator(applicationContext);
if (elEvaluator != null) {
@@ -275,11 +275,14 @@ public class StrutsTilesContainerFactory extends
BasicTilesContainerFactory {
return new I18NAttributeEvaluator();
}
- private AttributeEvaluator createConfiguredOgnlEvaluator() {
- if (legacyOgnlEnabled == null) {
- return new ConfiguredOgnlAttributeEvaluator();
+ private AttributeEvaluator
createConfiguredOgnlEvaluator(ApplicationContext applicationContext) {
+ if (legacyOgnlEnabled != null) {
+ return createOgnlEvaluator(legacyOgnlEnabled);
}
- return createOgnlEvaluator(legacyOgnlEnabled);
+ if (applicationContext instanceof ServletApplicationContext) {
+ return new
ConfiguredOgnlAttributeEvaluator(ServletUtil.getServletContext(applicationContext));
+ }
+ return new DisabledOgnlAttributeEvaluator();
}
private AttributeEvaluator createOgnlEvaluator(boolean enabled) {
@@ -293,20 +296,14 @@ public class StrutsTilesContainerFactory extends
BasicTilesContainerFactory {
}
@SuppressWarnings("removal")
- boolean isLegacyOgnlEnabled(Request request) {
- try {
- ServletContext servletContext =
ServletUtil.getServletRequest(request)
- .getRequest().getServletContext();
- Dispatcher dispatcher = Dispatcher.getInstance(servletContext);
- if (dispatcher == null) {
- return false;
- }
- String configuredValue =
dispatcher.getConfigurationManager().getConfiguration().getContainer().getInstance(
- String.class, TilesConstants.STRUTS_TILES_OGNL_LEGACY_ENABLED);
- return BooleanUtils.toBoolean(configuredValue);
- } catch (NotAServletEnvironmentException ignored) {
+ boolean isLegacyOgnlEnabled(ServletContext servletContext) {
+ Dispatcher dispatcher = Dispatcher.getInstance(servletContext);
+ if (dispatcher == null) {
return false;
}
+ String configuredValue =
dispatcher.getConfigurationManager().getConfiguration().getContainer().getInstance(
+ String.class, TilesConstants.STRUTS_TILES_OGNL_LEGACY_ENABLED);
+ return BooleanUtils.toBoolean(configuredValue);
}
void logLegacyOgnlWarning() {
@@ -334,20 +331,25 @@ public class StrutsTilesContainerFactory extends
BasicTilesContainerFactory {
private final class ConfiguredOgnlAttributeEvaluator extends
AbstractAttributeEvaluator {
+ private final ServletContext servletContext;
private volatile AttributeEvaluator delegate;
+ private ConfiguredOgnlAttributeEvaluator(ServletContext
servletContext) {
+ this.servletContext = servletContext;
+ }
+
@Override
public Object evaluate(String expression, Request request) {
- return getDelegate(request).evaluate(expression, request);
+ return getDelegate().evaluate(expression, request);
}
- private AttributeEvaluator getDelegate(Request request) {
+ private AttributeEvaluator getDelegate() {
AttributeEvaluator result = delegate;
if (result == null) {
synchronized (this) {
result = delegate;
if (result == null) {
- result =
createOgnlEvaluator(isLegacyOgnlEnabled(request));
+ result =
createOgnlEvaluator(isLegacyOgnlEnabled(servletContext));
delegate = result;
}
}
diff --git
a/plugins/tiles/src/main/java/org/apache/struts2/tiles/TilesConstants.java
b/plugins/tiles/src/main/java/org/apache/struts2/tiles/TilesConstants.java
index 44383047d..3812713ed 100644
--- a/plugins/tiles/src/main/java/org/apache/struts2/tiles/TilesConstants.java
+++ b/plugins/tiles/src/main/java/org/apache/struts2/tiles/TilesConstants.java
@@ -26,8 +26,7 @@ public final class TilesConstants {
/**
* Temporarily enables legacy raw Tiles OGNL evaluation.
*
- * @deprecated Migrate Tiles expressions to {@code S2:} or ordinary Tiles
mechanisms. This compatibility
- * constant and the legacy evaluator are targeted for removal in Struts
8.0.0.
+ * @deprecated since 7.4.0, migrate Tiles expressions to {@code S2:} or
ordinary Tiles mechanisms.
*/
@Deprecated(since = "7.4.0", forRemoval = true)
public static final String STRUTS_TILES_OGNL_LEGACY_ENABLED =
"struts.tiles.ognl.legacy.enabled";
diff --git
a/plugins/tiles/src/main/java/org/apache/tiles/ognl/OGNLAttributeEvaluator.java
b/plugins/tiles/src/main/java/org/apache/tiles/ognl/OGNLAttributeEvaluator.java
index 59aaff522..0cd6e21e3 100644
---
a/plugins/tiles/src/main/java/org/apache/tiles/ognl/OGNLAttributeEvaluator.java
+++
b/plugins/tiles/src/main/java/org/apache/tiles/ognl/OGNLAttributeEvaluator.java
@@ -28,9 +28,9 @@ import org.apache.tiles.request.Request;
* Evaluates attribute expressions and expressions with OGNL language.
*
* @since 2.2.0
- * @deprecated This legacy evaluator does not use the Struts OGNL controls
used by {@code S2:} and is disabled by
- * default. Temporary use requires {@code
struts.tiles.ognl.legacy.enabled=true}. Migrate to {@code S2:} or ordinary
- * Tiles mechanisms. This evaluator is targeted for removal in Struts 8.0.0.
+ * @deprecated since 7.4.0, this legacy evaluator does not use the Struts OGNL
controls used by {@code S2:} and is
+ * disabled by default. Temporary use requires {@code
struts.tiles.ognl.legacy.enabled=true}. Migrate to {@code S2:}
+ * or ordinary Tiles mechanisms.
*/
@Deprecated(since = "7.4.0", forRemoval = true)
public class OGNLAttributeEvaluator extends AbstractAttributeEvaluator {
diff --git
a/plugins/tiles/src/test/java/org/apache/struts2/tiles/StrutsTilesContainerFactoryTest.java
b/plugins/tiles/src/test/java/org/apache/struts2/tiles/StrutsTilesContainerFactoryTest.java
index aa92da82d..882fefe04 100644
---
a/plugins/tiles/src/test/java/org/apache/struts2/tiles/StrutsTilesContainerFactoryTest.java
+++
b/plugins/tiles/src/test/java/org/apache/struts2/tiles/StrutsTilesContainerFactoryTest.java
@@ -77,12 +77,14 @@ public class StrutsTilesContainerFactoryTest {
private StrutsTilesContainerFactory factory;
private ApplicationContext applicationContext;
+ private ApplicationContext servletApplicationContext;
private JspFactory originalJspFactory;
@Before
public void setUp() throws Exception {
originalJspFactory = JspFactory.getDefaultFactory();
applicationContext = mock(ApplicationContext.class);
+ servletApplicationContext = new ServletApplicationContext(new
MockServletContext());
factory = new StrutsTilesContainerFactory();
}
@@ -118,7 +120,8 @@ public class StrutsTilesContainerFactoryTest {
// explicitly disables support for EL
JspFactory.setDefaultFactory(null);
- AttributeEvaluatorFactory attributeEvaluatorFactory =
trackingFactory.createAttributeEvaluatorFactory(applicationContext, resolver);
+ AttributeEvaluatorFactory attributeEvaluatorFactory =
trackingFactory.createAttributeEvaluatorFactory(
+ servletApplicationContext, resolver);
assertTrue("The class of the evaluator is not correct",
attributeEvaluatorFactory.getAttributeEvaluator((String) null)
instanceof DirectAttributeEvaluator);
assertTrue("The class of the evaluator is not correct",
@@ -186,7 +189,7 @@ public class StrutsTilesContainerFactoryTest {
trackingFactory.configuredLegacyOgnlEnabled = true;
JspFactory.setDefaultFactory(null);
AttributeEvaluatorFactory evaluators =
trackingFactory.createAttributeEvaluatorFactory(
- applicationContext,
trackingFactory.createLocaleResolver(applicationContext));
+ servletApplicationContext,
trackingFactory.createLocaleResolver(servletApplicationContext));
AttributeEvaluator evaluator =
evaluators.getAttributeEvaluator("OGNL");
assertEquals(0, trackingFactory.configurationResolutions);
@@ -208,7 +211,7 @@ public class StrutsTilesContainerFactoryTest {
trackingFactory.blockConfigurationResolution = true;
JspFactory.setDefaultFactory(null);
AttributeEvaluator evaluator =
trackingFactory.createAttributeEvaluatorFactory(
- applicationContext,
trackingFactory.createLocaleResolver(applicationContext))
+ servletApplicationContext,
trackingFactory.createLocaleResolver(servletApplicationContext))
.getAttributeEvaluator("OGNL");
ExecutorService executor = Executors.newFixedThreadPool(4);
try {
@@ -232,12 +235,48 @@ public class StrutsTilesContainerFactoryTest {
}
@Test
- public void requestScopedConfigurationUsesNormalBooleanParsing() {
+ public void configuredValueUsesNormalBooleanParsing() {
assertResolvedConfiguration("false", false);
assertResolvedConfiguration("TrUe", true);
assertResolvedConfiguration("not-a-boolean", false);
}
+ @Test
+ public void nonServletApplicationContextFailsClosedWithoutLookup() {
+ TrackingFactory trackingFactory = new TrackingFactory();
+ JspFactory.setDefaultFactory(null);
+ AttributeEvaluator evaluator =
trackingFactory.createAttributeEvaluatorFactory(
+ applicationContext,
trackingFactory.createLocaleResolver(applicationContext))
+ .getAttributeEvaluator("OGNL");
+ org.apache.tiles.request.Request request =
mock(org.apache.tiles.request.Request.class);
+
+ EvaluationException exception = assertThrows(EvaluationException.class,
+ () -> evaluator.evaluate("ignored", request));
+
+ assertEquals(DisabledOgnlAttributeEvaluator.DISABLED_MESSAGE,
exception.getMessage());
+ assertEquals(0, trackingFactory.configurationResolutions);
+ assertEquals(0, trackingFactory.rawEvaluatorCreations);
+ }
+
+ @Test
+ public void flagIsReadFromTheOwningWebApplicationNotFromTheRequest() {
+ ApplicationContext owningApplicationContext = new
ServletApplicationContext(
+ createServletContextWithConfiguredValue("false"));
+ ServletContext neighbourServletContext =
createServletContextWithConfiguredValue("true");
+ org.apache.tiles.request.Request neighbourRequest = new ServletRequest(
+ new ServletApplicationContext(neighbourServletContext),
+ new MockHttpServletRequest(neighbourServletContext),
mock(HttpServletResponse.class));
+ JspFactory.setDefaultFactory(null);
+ AttributeEvaluator evaluator = factory.createAttributeEvaluatorFactory(
+ owningApplicationContext,
factory.createLocaleResolver(owningApplicationContext))
+ .getAttributeEvaluator("OGNL");
+
+ EvaluationException exception = assertThrows(EvaluationException.class,
+ () -> evaluator.evaluate("1", neighbourRequest));
+
+ assertEquals(DisabledOgnlAttributeEvaluator.DISABLED_MESSAGE,
exception.getMessage());
+ }
+
@Test
public void customInitializerBooleanConstructorsArePublic() throws
NoSuchMethodException {
assertTrue(Modifier.isPublic(StrutsTilesContainerFactory.class.getConstructor(boolean.class).getModifiers()));
@@ -248,13 +287,15 @@ public class StrutsTilesContainerFactoryTest {
public void noArgInitializerPreservesLazyWebApplicationConfiguration()
throws OgnlException {
PropertyAccessor originalAccessor = getRequestAccessorOrNull();
try {
- StrutsTilesContainerFactory initializedFactory = new
ExposedInitializer().createFactory(applicationContext);
+ ApplicationContext legacyApplicationContext = new
ServletApplicationContext(
+ createServletContextWithConfiguredValue("true"));
+ StrutsTilesContainerFactory initializedFactory = new
ExposedInitializer().createFactory(legacyApplicationContext);
JspFactory.setDefaultFactory(null);
AttributeEvaluator evaluator =
initializedFactory.createAttributeEvaluatorFactory(
- applicationContext,
initializedFactory.createLocaleResolver(applicationContext))
+ legacyApplicationContext,
initializedFactory.createLocaleResolver(legacyApplicationContext))
.getAttributeEvaluator("OGNL");
- assertEquals(1, evaluator.evaluate("1",
createRequestWithConfiguredValue("true")));
+ assertEquals(1, evaluator.evaluate("1",
mock(org.apache.tiles.request.Request.class)));
} finally {
OgnlRuntime.setPropertyAccessor(org.apache.tiles.request.Request.class,
originalAccessor);
}
@@ -315,10 +356,10 @@ public class StrutsTilesContainerFactoryTest {
}
private void assertResolvedConfiguration(String configuredValue, boolean
expected) {
- assertEquals(expected,
factory.isLegacyOgnlEnabled(createRequestWithConfiguredValue(configuredValue)));
+ assertEquals(expected,
factory.isLegacyOgnlEnabled(createServletContextWithConfiguredValue(configuredValue)));
}
- private ServletRequest createRequestWithConfiguredValue(String
configuredValue) {
+ private ServletContext createServletContextWithConfiguredValue(String
configuredValue) {
MockServletContext servletContext = new MockServletContext();
Dispatcher dispatcher = mock(Dispatcher.class);
ConfigurationManager configurationManager =
mock(ConfigurationManager.class);
@@ -330,9 +371,7 @@ public class StrutsTilesContainerFactoryTest {
when(container.getInstance(String.class,
TilesConstants.STRUTS_TILES_OGNL_LEGACY_ENABLED))
.thenReturn(configuredValue);
servletContext.setAttribute(StrutsStatics.SERVLET_DISPATCHER,
dispatcher);
- ServletApplicationContext servletApplicationContext = new
ServletApplicationContext(servletContext);
- return new ServletRequest(servletApplicationContext,
- new MockHttpServletRequest(servletContext),
mock(HttpServletResponse.class));
+ return servletContext;
}
private static class ExposedInitializer extends StrutsTilesInitializer {
@@ -362,7 +401,7 @@ public class StrutsTilesContainerFactoryTest {
}
@Override
- boolean isLegacyOgnlEnabled(org.apache.tiles.request.Request request) {
+ boolean isLegacyOgnlEnabled(ServletContext servletContext) {
configurationResolutions = resolutionCounter.incrementAndGet();
configurationResolutionEntered.countDown();
if (blockConfigurationResolution) {
diff --git
a/plugins/tiles/src/test/java/org/apache/struts2/tiles/TilesOgnlEvaluatorIntegrationTest.java
b/plugins/tiles/src/test/java/org/apache/struts2/tiles/TilesOgnlEvaluatorIntegrationTest.java
index 4373a25e0..0ffc3b7d7 100644
---
a/plugins/tiles/src/test/java/org/apache/struts2/tiles/TilesOgnlEvaluatorIntegrationTest.java
+++
b/plugins/tiles/src/test/java/org/apache/struts2/tiles/TilesOgnlEvaluatorIntegrationTest.java
@@ -44,6 +44,7 @@ import org.junit.Test;
import org.springframework.mock.web.MockHttpServletRequest;
import org.springframework.mock.web.MockServletContext;
+import jakarta.servlet.ServletContext;
import jakarta.servlet.http.HttpServletResponse;
import jakarta.servlet.jsp.JspFactory;
import java.io.PrintWriter;
@@ -188,7 +189,7 @@ public class TilesOgnlEvaluatorIntegrationTest {
}
@Test
- public void
nonServletRequestFailsClosedWithoutExposingEnvironmentFailure() {
+ public void nonServletRequestFailsClosedWithoutExposingExpression() {
TrackingFactory factory = new TrackingFactory();
AttributeEvaluatorFactory evaluators =
createAttributeEvaluatorFactoryWithoutEl(
factory, tilesRequest.getApplicationContext());
@@ -266,9 +267,9 @@ public class TilesOgnlEvaluatorIntegrationTest {
private int legacyWarnings;
@Override
- boolean isLegacyOgnlEnabled(Request request) {
+ boolean isLegacyOgnlEnabled(ServletContext servletContext) {
configurationResolutions++;
- return super.isLegacyOgnlEnabled(request);
+ return super.isLegacyOgnlEnabled(servletContext);
}
@Override