From 3dbc5c250e42e2ab33a9674eb2049e879a23e519 Mon Sep 17 00:00:00 2001 From: Kusal Kithul-Godage Date: Thu, 28 Dec 2023 05:00:31 +1100 Subject: [PATCH 1/4] WW-5379 Implement alternative mechanism for Velocity directives to obtain ValueStack --- .../views/velocity/StrutsVelocityContext.java | 4 +++ .../components/AbstractDirective.java | 31 +++++++++++-------- 2 files changed, 22 insertions(+), 13 deletions(-) diff --git a/plugins/velocity/src/main/java/org/apache/struts2/views/velocity/StrutsVelocityContext.java b/plugins/velocity/src/main/java/org/apache/struts2/views/velocity/StrutsVelocityContext.java index 4241f6ead4..587acb0138 100644 --- a/plugins/velocity/src/main/java/org/apache/struts2/views/velocity/StrutsVelocityContext.java +++ b/plugins/velocity/src/main/java/org/apache/struts2/views/velocity/StrutsVelocityContext.java @@ -103,4 +103,8 @@ protected Object chainedContextGet(String key) { } return null; } + + public ValueStack getValueStack() { + return stack; + } } diff --git a/plugins/velocity/src/main/java/org/apache/struts2/views/velocity/components/AbstractDirective.java b/plugins/velocity/src/main/java/org/apache/struts2/views/velocity/components/AbstractDirective.java index e955b32aa3..d007ab9da8 100644 --- a/plugins/velocity/src/main/java/org/apache/struts2/views/velocity/components/AbstractDirective.java +++ b/plugins/velocity/src/main/java/org/apache/struts2/views/velocity/components/AbstractDirective.java @@ -18,16 +18,12 @@ */ package org.apache.struts2.views.velocity.components; -import java.io.IOException; -import java.io.Writer; -import java.util.HashMap; -import java.util.Map; - -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; - +import com.opensymphony.xwork2.inject.Container; +import com.opensymphony.xwork2.util.ValueStack; import org.apache.struts2.ServletActionContext; import org.apache.struts2.components.Component; +import org.apache.struts2.views.util.ContextUtil; +import org.apache.struts2.views.velocity.StrutsVelocityContext; import org.apache.velocity.context.InternalContextAdapter; import org.apache.velocity.exception.MethodInvocationException; import org.apache.velocity.exception.ParseErrorException; @@ -35,9 +31,12 @@ import org.apache.velocity.runtime.directive.Directive; import org.apache.velocity.runtime.parser.node.Node; -import com.opensymphony.xwork2.ActionContext; -import com.opensymphony.xwork2.inject.Container; -import com.opensymphony.xwork2.util.ValueStack; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import java.io.IOException; +import java.io.Writer; +import java.util.HashMap; +import java.util.Map; public abstract class AbstractDirective extends Directive { public String getName() { @@ -57,8 +56,14 @@ public int getType() { protected abstract Component getBean(ValueStack stack, HttpServletRequest req, HttpServletResponse res); public boolean render(InternalContextAdapter ctx, Writer writer, Node node) throws IOException, ResourceNotFoundException, ParseErrorException, MethodInvocationException { - // get the bean - ValueStack stack = (ValueStack) ctx.get("stack"); + ValueStack stack; + if (ctx.getInternalUserContext() instanceof StrutsVelocityContext) { + StrutsVelocityContext svc = (StrutsVelocityContext) ctx.getInternalUserContext(); + stack = svc.getValueStack(); + } else { + // Fallback to assuming the ValueStack was put into the Velocity context (as is by default) + stack = (ValueStack) ctx.get(ContextUtil.STACK); + } HttpServletRequest req = (HttpServletRequest) stack.getContext().get(ServletActionContext.HTTP_REQUEST); HttpServletResponse res = (HttpServletResponse) stack.getContext().get(ServletActionContext.HTTP_RESPONSE); Component bean = getBean(stack, req, res); From 3eddd56d2d9af312a2da637d7bf82f6e088e0064 Mon Sep 17 00:00:00 2001 From: Kusal Kithul-Godage Date: Thu, 28 Dec 2023 07:42:23 +1100 Subject: [PATCH 2/4] WW-5379 Add support for internal and chained contexts --- .../components/AbstractDirective.java | 31 ++++++++++++++++--- 1 file changed, 26 insertions(+), 5 deletions(-) diff --git a/plugins/velocity/src/main/java/org/apache/struts2/views/velocity/components/AbstractDirective.java b/plugins/velocity/src/main/java/org/apache/struts2/views/velocity/components/AbstractDirective.java index d007ab9da8..fd984a88da 100644 --- a/plugins/velocity/src/main/java/org/apache/struts2/views/velocity/components/AbstractDirective.java +++ b/plugins/velocity/src/main/java/org/apache/struts2/views/velocity/components/AbstractDirective.java @@ -24,7 +24,10 @@ import org.apache.struts2.components.Component; import org.apache.struts2.views.util.ContextUtil; import org.apache.struts2.views.velocity.StrutsVelocityContext; +import org.apache.velocity.context.AbstractContext; +import org.apache.velocity.context.Context; import org.apache.velocity.context.InternalContextAdapter; +import org.apache.velocity.context.InternalWrapperContext; import org.apache.velocity.exception.MethodInvocationException; import org.apache.velocity.exception.ParseErrorException; import org.apache.velocity.exception.ResourceNotFoundException; @@ -56,11 +59,8 @@ public int getType() { protected abstract Component getBean(ValueStack stack, HttpServletRequest req, HttpServletResponse res); public boolean render(InternalContextAdapter ctx, Writer writer, Node node) throws IOException, ResourceNotFoundException, ParseErrorException, MethodInvocationException { - ValueStack stack; - if (ctx.getInternalUserContext() instanceof StrutsVelocityContext) { - StrutsVelocityContext svc = (StrutsVelocityContext) ctx.getInternalUserContext(); - stack = svc.getValueStack(); - } else { + ValueStack stack = extractValueStack(ctx); + if (stack == null) { // Fallback to assuming the ValueStack was put into the Velocity context (as is by default) stack = (ValueStack) ctx.get(ContextUtil.STACK); } @@ -84,6 +84,27 @@ public boolean render(InternalContextAdapter ctx, Writer writer, Node node) thro return true; } + private ValueStack extractValueStack(Context context) { + do { + if (context instanceof StrutsVelocityContext) { + return ((StrutsVelocityContext) context).getValueStack(); + } + context = extractContext(context); + } while (context != null); + + return null; + } + + private Context extractContext(Context context) { + if (context instanceof InternalWrapperContext) { + return ((InternalWrapperContext) context).getInternalUserContext(); + } + if (context instanceof AbstractContext) { + return ((AbstractContext) context).getChainedContext(); + } + return null; + } + /** *

* Create a Map of properties that the user has passed in. For example: From 450ee91b034d9e338dc01e099cbb0a9673e04d8a Mon Sep 17 00:00:00 2001 From: Kusal Kithul-Godage Date: Thu, 28 Dec 2023 08:07:33 +1100 Subject: [PATCH 3/4] WW-5379 Fix not looking in chained contexts' chained contexts --- .../struts2/views/velocity/StrutsVelocityContext.java | 6 +++--- .../struts2/views/velocity/StrutsVelocityContextTest.java | 4 ++-- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/plugins/velocity/src/main/java/org/apache/struts2/views/velocity/StrutsVelocityContext.java b/plugins/velocity/src/main/java/org/apache/struts2/views/velocity/StrutsVelocityContext.java index 587acb0138..7ab4f24e78 100644 --- a/plugins/velocity/src/main/java/org/apache/struts2/views/velocity/StrutsVelocityContext.java +++ b/plugins/velocity/src/main/java/org/apache/struts2/views/velocity/StrutsVelocityContext.java @@ -77,10 +77,10 @@ public Object internalGet(String key) { } protected List> contextGetterList() { - return Arrays.asList(this::superGet, this::chainedContextGet, this::stackGet); + return Arrays.asList(this::superInternalGet, this::chainedContextGet, this::stackGet); } - protected Object superGet(String key) { + protected Object superInternalGet(String key) { return super.internalGet(key); } @@ -96,7 +96,7 @@ protected Object chainedContextGet(String key) { return null; } for (VelocityContext chainedContext : chainedContexts) { - Object val = chainedContext.internalGet(key); + Object val = chainedContext.get(key); if (val != null) { return val; } diff --git a/plugins/velocity/src/test/java/org/apache/struts2/views/velocity/StrutsVelocityContextTest.java b/plugins/velocity/src/test/java/org/apache/struts2/views/velocity/StrutsVelocityContextTest.java index 6cd38c8aa6..60490c6aff 100644 --- a/plugins/velocity/src/test/java/org/apache/struts2/views/velocity/StrutsVelocityContextTest.java +++ b/plugins/velocity/src/test/java/org/apache/struts2/views/velocity/StrutsVelocityContextTest.java @@ -54,7 +54,7 @@ public void setUp() throws Exception { @Test public void getChainedValue() { - when(chainedContext.internalGet("foo")).thenReturn("bar"); + when(chainedContext.get("foo")).thenReturn("bar"); assertEquals("bar", strutsVelocityContext.internalGet("foo")); } @@ -75,7 +75,7 @@ public void getValuePrecedence() { when(stack.findValue("foo")).thenReturn("qux"); assertEquals("qux", strutsVelocityContext.internalGet("foo")); - when(chainedContext.internalGet("foo")).thenReturn("baz"); + when(chainedContext.get("foo")).thenReturn("baz"); assertEquals("baz", strutsVelocityContext.internalGet("foo")); strutsVelocityContext.put("foo", "bar"); From ee600f99b1838cf79a649c0181cd13ae3bfed63e Mon Sep 17 00:00:00 2001 From: Kusal Kithul-Godage Date: Tue, 2 Jan 2024 21:21:35 +1100 Subject: [PATCH 4/4] WW-5379 Use ValueStackProvider marker interface for Velocity context implementation flexibility --- .../struts2/util/ValueStackProvider.java | 30 +++++++++++++++++++ .../views/velocity/StrutsVelocityContext.java | 4 ++- .../components/AbstractDirective.java | 6 ++-- 3 files changed, 36 insertions(+), 4 deletions(-) create mode 100644 core/src/main/java/org/apache/struts2/util/ValueStackProvider.java diff --git a/core/src/main/java/org/apache/struts2/util/ValueStackProvider.java b/core/src/main/java/org/apache/struts2/util/ValueStackProvider.java new file mode 100644 index 0000000000..2a6d1bf0af --- /dev/null +++ b/core/src/main/java/org/apache/struts2/util/ValueStackProvider.java @@ -0,0 +1,30 @@ +/* + * 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. + */ +package org.apache.struts2.util; + +import com.opensymphony.xwork2.util.ValueStack; + +/** + * @since 6.4.0 + */ +public interface ValueStackProvider { + + ValueStack getValueStack(); + +} diff --git a/plugins/velocity/src/main/java/org/apache/struts2/views/velocity/StrutsVelocityContext.java b/plugins/velocity/src/main/java/org/apache/struts2/views/velocity/StrutsVelocityContext.java index 7ab4f24e78..d18ca6bcf4 100644 --- a/plugins/velocity/src/main/java/org/apache/struts2/views/velocity/StrutsVelocityContext.java +++ b/plugins/velocity/src/main/java/org/apache/struts2/views/velocity/StrutsVelocityContext.java @@ -19,6 +19,7 @@ package org.apache.struts2.views.velocity; import com.opensymphony.xwork2.util.ValueStack; +import org.apache.struts2.util.ValueStackProvider; import org.apache.velocity.VelocityContext; import java.util.ArrayList; @@ -26,7 +27,7 @@ import java.util.List; import java.util.function.Function; -public class StrutsVelocityContext extends VelocityContext { +public class StrutsVelocityContext extends VelocityContext implements ValueStackProvider { private final ValueStack stack; private final List chainedContexts; @@ -104,6 +105,7 @@ protected Object chainedContextGet(String key) { return null; } + @Override public ValueStack getValueStack() { return stack; } diff --git a/plugins/velocity/src/main/java/org/apache/struts2/views/velocity/components/AbstractDirective.java b/plugins/velocity/src/main/java/org/apache/struts2/views/velocity/components/AbstractDirective.java index fd984a88da..539f64bdd9 100644 --- a/plugins/velocity/src/main/java/org/apache/struts2/views/velocity/components/AbstractDirective.java +++ b/plugins/velocity/src/main/java/org/apache/struts2/views/velocity/components/AbstractDirective.java @@ -22,8 +22,8 @@ import com.opensymphony.xwork2.util.ValueStack; import org.apache.struts2.ServletActionContext; import org.apache.struts2.components.Component; +import org.apache.struts2.util.ValueStackProvider; import org.apache.struts2.views.util.ContextUtil; -import org.apache.struts2.views.velocity.StrutsVelocityContext; import org.apache.velocity.context.AbstractContext; import org.apache.velocity.context.Context; import org.apache.velocity.context.InternalContextAdapter; @@ -86,8 +86,8 @@ public boolean render(InternalContextAdapter ctx, Writer writer, Node node) thro private ValueStack extractValueStack(Context context) { do { - if (context instanceof StrutsVelocityContext) { - return ((StrutsVelocityContext) context).getValueStack(); + if (context instanceof ValueStackProvider) { + return ((ValueStackProvider) context).getValueStack(); } context = extractContext(context); } while (context != null);