Merge pull request #735 from apache/WW-5336-deprecate-ognltool

This commit is contained in:
Kusal Kithul-Godage
2023-08-22 22:54:28 +10:00
committed by GitHub
9 changed files with 201 additions and 150 deletions
@@ -18,14 +18,16 @@
*/
package org.apache.struts2.util;
import com.opensymphony.xwork2.ActionContext;
import com.opensymphony.xwork2.ObjectFactory;
import com.opensymphony.xwork2.ognl.OgnlUtil;
import com.opensymphony.xwork2.util.ClassLoaderUtil;
import com.opensymphony.xwork2.util.TextParseUtil;
import com.opensymphony.xwork2.util.ValueStack;
import ognl.OgnlException;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.apache.struts2.StrutsException;
import org.apache.struts2.views.jsp.ui.OgnlTool;
import org.apache.struts2.views.util.UrlHelper;
import javax.servlet.RequestDispatcher;
@@ -39,7 +41,16 @@ import java.io.PrintWriter;
import java.io.StringWriter;
import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;
import java.util.*;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import static java.text.MessageFormat.format;
import static java.util.Collections.emptyList;
import static java.util.Collections.singletonList;
/**
* Struts base utility class, for use in Velocity and Freemarker templates
@@ -50,32 +61,30 @@ public class StrutsUtil {
protected HttpServletRequest request;
protected HttpServletResponse response;
protected Map<String, Class> classes = new Hashtable<>();
protected OgnlTool ognl;
protected Map<String, Class<?>> classes = new HashMap<>();
protected OgnlUtil ognl;
protected ValueStack stack;
private UrlHelper urlHelper;
private ObjectFactory objectFactory;
private final UrlHelper urlHelper;
private final ObjectFactory objectFactory;
public StrutsUtil(ValueStack stack, HttpServletRequest request, HttpServletResponse response) {
this.stack = stack;
this.request = request;
this.response = response;
this.ognl = stack.getActionContext().getContainer().getInstance(OgnlTool.class);
this.ognl = stack.getActionContext().getContainer().getInstance(OgnlUtil.class);
this.urlHelper = stack.getActionContext().getContainer().getInstance(UrlHelper.class);
this.objectFactory = stack.getActionContext().getContainer().getInstance(ObjectFactory.class);
}
public Object bean(Object aName) throws Exception {
String name = aName.toString();
Class c = classes.get(name);
if (c == null) {
c = ClassLoaderUtil.loadClass(name, StrutsUtil.class);
classes.put(name, c);
public Object bean(Object name) throws Exception {
String className = name.toString();
Class<?> clazz = classes.get(className);
if (clazz == null) {
clazz = ClassLoaderUtil.loadClass(className, StrutsUtil.class);
classes.put(className, clazz);
}
return objectFactory.buildBean(c, stack.getContext());
return objectFactory.buildBean(clazz, stack.getContext());
}
public boolean isTrue(String expression) {
@@ -88,30 +97,20 @@ public class StrutsUtil {
}
public String include(Object aName) throws Exception {
try {
RequestDispatcher dispatcher = request.getRequestDispatcher(aName.toString());
if (dispatcher == null) {
throw new IllegalArgumentException("Cannot find included file " + aName);
}
ResponseWrapper responseWrapper = new ResponseWrapper(response);
dispatcher.include(request, responseWrapper);
return responseWrapper.getData();
}
catch (Exception e) {
LOG.debug("Cannot include {}", aName, e);
throw e;
RequestDispatcher dispatcher = request.getRequestDispatcher(aName.toString());
if (dispatcher == null) {
throw new IllegalArgumentException("Cannot find included file " + aName);
}
ResponseWrapper responseWrapper = new ResponseWrapper(response);
dispatcher.include(request, responseWrapper);
return responseWrapper.getData();
}
public String urlEncode(String s) {
try {
return URLEncoder.encode(s, "UTF-8");
} catch (UnsupportedEncodingException e) {
LOG.debug("Cannot encode URL [{}]", s, e);
LOG.debug(format("Cannot encode URL [{0}]", s), e);
return s;
}
}
@@ -124,6 +123,17 @@ public class StrutsUtil {
return stack.findValue(expression, Class.forName(className));
}
public Object findValue(String expr, Object context) {
try {
return ognl.getValue(expr, ActionContext.getContext().getContextMap(), context);
} catch (OgnlException e) {
if (e.getReason() instanceof SecurityException) {
LOG.error(format("Could not evaluate this expression due to security constraints: [{0}]", expr), e);
}
return null;
}
}
public String getText(String text) {
return (String) stack.findValue("getText('" + text.replace('\'', '"') + "')");
}
@@ -132,7 +142,7 @@ public class StrutsUtil {
* @return the url ContextPath. An empty string if one does not exist.
*/
public String getContext() {
return (request == null)? "" : request.getContextPath();
return request == null ? "" : request.getContextPath();
}
public String translateVariables(String expression) {
@@ -156,71 +166,64 @@ public class StrutsUtil {
* to use as the value of the ListEntry
* @return a List of ListEntry
*/
public List makeSelectList(String selectedList, String list, String listKey, String listValue) {
List selectList = new ArrayList();
Collection selectedItems = null;
Object i = stack.findValue(selectedList);
if (i != null) {
if (i.getClass().isArray()) {
selectedItems = Arrays.asList((Object[]) i);
} else if (i instanceof Collection) {
selectedItems = (Collection) i;
} else {
// treat it is a single item
selectedItems = new ArrayList();
selectedItems.add(i);
}
}
public List<ListEntry> makeSelectList(String selectedList, String list, String listKey, String listValue) {
List<ListEntry> selectList = new ArrayList<>();
Collection items = (Collection) stack.findValue(list);
if (items == null) {
return selectList;
}
if (items != null) {
for (Object element : items) {
Object key;
if ((listKey == null) || (listKey.length() == 0)) {
key = element;
} else {
key = ognl.findValue(listKey, element);
}
Object value = null;
if ((listValue == null) || (listValue.length() == 0)) {
value = element;
} else {
value = ognl.findValue(listValue, element);
}
boolean isSelected = false;
if ((value != null) && (selectedItems != null) && selectedItems.contains(value)) {
isSelected = true;
}
selectList.add(new ListEntry(key, value, isSelected));
}
Collection selectedItems = getSelectedItems(selectedList);
for (Object element : items) {
Object key = computeKey(listKey, element);
Object value = computeValue(listValue, element);
boolean isSelected = value != null && selectedItems.contains(value);
selectList.add(new ListEntry(key, value, isSelected));
}
return selectList;
}
private Collection getSelectedItems(String selectedListName) {
Object i = stack.findValue(selectedListName);
if (i == null) {
return emptyList();
}
if (i.getClass().isArray()) {
return Arrays.asList((Object[]) i);
} else if (i instanceof Collection) {
return (Collection) i;
}
return singletonList(i);
}
private Object computeKey(String listKey, Object element) {
if (listKey == null || listKey.isEmpty()) {
return element;
}
return findValue(listKey, element);
}
private Object computeValue(String listValue, Object element) {
if (listValue == null || listValue.isEmpty()) {
return element;
}
return findValue(listValue, element);
}
public int toInt(long aLong) {
return (int) aLong;
}
public long toLong(int anInt) {
return (long) anInt;
return anInt;
}
public long toLong(String aLong) {
if (aLong == null) {
if (aLong == null || aLong.isEmpty()) {
return 0;
}
return Long.parseLong(aLong);
}
@@ -233,14 +236,7 @@ public class StrutsUtil {
}
public String toStringSafe(Object obj) {
try {
if (obj != null) {
return String.valueOf(obj);
}
return "";
} catch (Exception e) {
return "Exception thrown: " + e;
}
return obj == null ? "" : obj.toString();
}
static class ResponseWrapper extends HttpServletResponseWrapper {
@@ -257,7 +253,6 @@ public class StrutsUtil {
public String getData() {
writer.flush();
return strout.toString();
}
@@ -24,16 +24,24 @@ import com.opensymphony.xwork2.inject.Container;
import com.opensymphony.xwork2.inject.Inject;
import com.opensymphony.xwork2.util.ClassLoaderUtil;
import com.opensymphony.xwork2.util.ValueStack;
import freemarker.cache.*;
import freemarker.cache.ClassTemplateLoader;
import freemarker.cache.FileTemplateLoader;
import freemarker.cache.MultiTemplateLoader;
import freemarker.cache.TemplateLoader;
import freemarker.cache.WebappTemplateLoader;
import freemarker.core.HTMLOutputFormat;
import freemarker.core.OutputFormat;
import freemarker.core.TemplateClassResolver;
import freemarker.ext.jsp.TaglibFactory;
import freemarker.ext.servlet.HttpRequestHashModel;
import freemarker.ext.servlet.HttpRequestParametersHashModel;
import freemarker.ext.servlet.HttpSessionHashModel;
import freemarker.ext.servlet.ServletContextHashModel;
import freemarker.template.*;
import freemarker.template.Configuration;
import freemarker.template.ObjectWrapper;
import freemarker.template.TemplateException;
import freemarker.template.TemplateExceptionHandler;
import freemarker.template.TemplateModel;
import freemarker.template.Version;
import freemarker.template.utility.StringUtil;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
@@ -51,7 +59,13 @@ import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.text.SimpleDateFormat;
import java.util.*;
import java.util.Calendar;
import java.util.Collections;
import java.util.GregorianCalendar;
import java.util.HashMap;
import java.util.Map;
import java.util.Properties;
import java.util.Set;
/**
* <p>
@@ -176,27 +190,27 @@ public class FreemarkerManager {
public void setEncoding(String encoding) {
this.encoding = encoding;
}
@Inject(StrutsConstants.STRUTS_FREEMARKER_WRAPPER_ALT_MAP)
public void setWrapperAltMap(String val) {
altMapWrapper = "true".equals(val);
}
@Inject(StrutsConstants.STRUTS_FREEMARKER_BEANWRAPPER_CACHE)
public void setCacheBeanWrapper(String val) {
cacheBeanWrapper = "true".equals(val);
}
@Inject(StrutsConstants.STRUTS_FREEMARKER_MRU_MAX_STRONG_SIZE)
public void setMruMaxStrongSize(String size) {
mruMaxStrongSize = Integer.parseInt(size);
}
@Inject(value = StrutsConstants.STRUTS_FREEMARKER_TEMPLATES_CACHE_UPDATE_DELAY, required = false)
public void setTemplateUpdateDelay(String delay) {
templateUpdateDelay = delay;
}
@Inject
public void setContainer(Container container) {
Map<String, TagLibraryModelProvider> map = new HashMap<>();
@@ -281,8 +295,8 @@ public class FreemarkerManager {
loadSettings(servletContext);
}
/**
* Sets the Freemarker Configuration's template loader with the FreemarkerThemeTemplateLoader
/**
* Sets the Freemarker Configuration's template loader with the FreemarkerThemeTemplateLoader
* at the top.
*
* @param templateLoader the template loader
@@ -293,7 +307,7 @@ public class FreemarkerManager {
themeTemplateLoader.init(templateLoader);
config.setTemplateLoader(themeTemplateLoader);
}
/**
* Create the instance of the freemarker Configuration object.
* <p>
@@ -543,7 +557,7 @@ public class FreemarkerManager {
protected void populateContext(ScopesHashModel model, ValueStack stack, Object action, HttpServletRequest request, HttpServletResponse response) {
// put the same objects into the context that the velocity result uses
Map standard = ContextUtil.getStandardContext(stack, request, response);
Map<String, Object> standard = ContextUtil.getStandardContext(stack, request, response);
model.putAll(standard);
// support for JSP exception pages, exposing the servlet or JSP exception
@@ -19,16 +19,16 @@
package org.apache.struts2.views.jsp.ui;
import com.opensymphony.xwork2.ActionContext;
import ognl.OgnlException;
import com.opensymphony.xwork2.inject.Inject;
import com.opensymphony.xwork2.ognl.OgnlUtil;
import ognl.OgnlException;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
/**
* FIXME: remove?
* @deprecated since 6.3.0. Use {@link org.apache.struts2.util.StrutsUtil} instead.
*/
@Deprecated
public class OgnlTool {
private static final Logger LOG = LogManager.getLogger(OgnlTool.class);
@@ -43,6 +43,10 @@ public class OgnlTool {
this.ognlUtil = ognlUtil;
}
/**
* @deprecated since 6.3.0. Use {@link org.apache.struts2.util.StrutsUtil#findValue(String, Object)} instead.
*/
@Deprecated
public Object findValue(String expr, Object context) {
try {
return ognlUtil.getValue(expr, ActionContext.getContext().getContextMap(), context);
@@ -21,7 +21,6 @@ package org.apache.struts2.views.util;
import com.opensymphony.xwork2.ActionInvocation;
import com.opensymphony.xwork2.util.ValueStack;
import org.apache.struts2.util.StrutsUtil;
import org.apache.struts2.views.jsp.ui.OgnlTool;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
@@ -48,8 +47,9 @@ public class ContextUtil {
map.put(SESSION, req.getSession(false));
map.put(BASE, req.getContextPath());
map.put(STACK, stack);
map.put(OGNL, stack.getActionContext().getContainer().getInstance(OgnlTool.class));
map.put(STRUTS, new StrutsUtil(stack, req, res));
StrutsUtil util = new StrutsUtil(stack, req, res);
map.put(STRUTS, util);
map.put(OGNL, util); // Deprecated since 6.3.0
ActionInvocation invocation = stack.getActionContext().getActionInvocation();
if (invocation != null) {
+1
View File
@@ -198,6 +198,7 @@
<bean type="ognl.MethodAccessor" name="com.opensymphony.xwork2.util.CompoundRoot"
class="com.opensymphony.xwork2.ognl.accessor.CompoundRootAccessor"/>
<!-- Deprecated since 6.3.0, inject OgnlUtil directly instead, or use StrutsUtil -->
<bean class="org.apache.struts2.views.jsp.ui.OgnlTool"/>
<bean type="org.apache.struts2.dispatcher.StaticContentLoader"
+1 -1
View File
@@ -33,5 +33,5 @@
#end
#macro(property $object $property)
$!{ognl.findValue($property, $object)}
$!{struts.findValue($property, $object)}
#end
@@ -29,9 +29,12 @@ import org.springframework.mock.web.MockRequestDispatcher;
import javax.servlet.RequestDispatcher;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.LinkedHashMap;
import java.util.List;
import static org.assertj.core.api.Assertions.assertThat;
/**
* Test case for StrutsUtil.
*
@@ -145,28 +148,63 @@ public class StrutsUtilTest extends StrutsInternalTestCase {
}
public void testMakeSelectListMethod() {
String[] selectedList = new String[] { "Car", "Airplane", "Bus" };
List list = new ArrayList();
list.add("Lorry");
list.add("Car");
list.add("Helicopter");
public void testMakeSelectList() {
String[] selectedList = new String[]{"Car", "Airplane", "Bus"}; // Array
List<String> list = Arrays.asList("Lorry", "Car", "Helicopter");
stack.getContext().put("mySelectedList", selectedList);
stack.getContext().put("myList", list);
List listMade = strutsUtil.makeSelectList("#mySelectedList", "#myList", null, null);
List<ListEntry> listMade = strutsUtil.makeSelectList("#mySelectedList", "#myList", null, null);
assertEquals(listMade.size(), 3);
assertEquals(((ListEntry)listMade.get(0)).getKey(), "Lorry");
assertEquals(((ListEntry)listMade.get(0)).getValue(), "Lorry");
assertFalse(((ListEntry) listMade.get(0)).getIsSelected());
assertEquals(((ListEntry)listMade.get(1)).getKey(), "Car");
assertEquals(((ListEntry)listMade.get(1)).getValue(), "Car");
assertTrue(((ListEntry) listMade.get(1)).getIsSelected());
assertEquals(((ListEntry)listMade.get(2)).getKey(), "Helicopter");
assertEquals(((ListEntry)listMade.get(2)).getValue(), "Helicopter");
assertFalse(((ListEntry) listMade.get(2)).getIsSelected());
LinkedHashMap<String, Boolean> expectedItems = new LinkedHashMap<>();
expectedItems.put("Lorry", false);
expectedItems.put("Car", true);
expectedItems.put("Helicopter", false);
makeSelectListCommonAssertions(listMade, expectedItems);
}
public void testMakeSelectListCollection() {
List<String> selectedList = Arrays.asList("Airplane", "Helicopter", "Bus"); // Collection
List<String> list = Arrays.asList("Lorry", "Car", "Helicopter");
stack.getContext().put("mySelectedList", selectedList);
stack.getContext().put("myList", list);
List<ListEntry> listMade = strutsUtil.makeSelectList("#mySelectedList", "#myList", null, null);
LinkedHashMap<String, Boolean> expectedItems = new LinkedHashMap<>();
expectedItems.put("Lorry", false);
expectedItems.put("Car", false);
expectedItems.put("Helicopter", true);
makeSelectListCommonAssertions(listMade, expectedItems);
}
public void testMakeSelectListSingleton() {
String selectedItem = "Lorry"; // Singleton
List<String> list = Arrays.asList("Lorry", "Car", "Helicopter");
stack.getContext().put("mySelectedList", selectedItem);
stack.getContext().put("myList", list);
List<ListEntry> listMade = strutsUtil.makeSelectList("#mySelectedList", "#myList", null, null);
LinkedHashMap<String, Boolean> expectedItems = new LinkedHashMap<>();
expectedItems.put("Lorry", true);
expectedItems.put("Car", false);
expectedItems.put("Helicopter", false);
makeSelectListCommonAssertions(listMade, expectedItems);
}
private void makeSelectListCommonAssertions(List<ListEntry> listMade, LinkedHashMap<String, Boolean> expectedItems) {
assertThat(listMade).extracting("key").containsExactly(expectedItems.keySet().toArray());
assertThat(listMade).extracting("value").containsExactly(expectedItems.keySet().toArray());
assertThat(listMade).extracting("isSelected").containsExactly(expectedItems.values().toArray());
}
public void testMakeSelectListNonExistent() {
List<ListEntry> listMade = strutsUtil.makeSelectList("#mySelectedList", "#nonexistent", null, null);
assertThat(listMade).isEmpty();
}
public void testToInt() {
@@ -178,12 +216,22 @@ public class StrutsUtilTest extends StrutsInternalTestCase {
assertEquals(strutsUtil.toLong(11), 11L);
}
public void testStringToLong() {
assertEquals(11L, strutsUtil.toLong("11"));
assertEquals(0L, strutsUtil.toLong(null));
assertEquals(0L, strutsUtil.toLong(""));
}
public void testToString() {
assertEquals(strutsUtil.toString(1), "1");
assertEquals(strutsUtil.toString(11L), "11");
}
public void testToStringSafe() {
assertEquals("1", strutsUtil.toStringSafe(1));
assertEquals("", strutsUtil.toStringSafe(null));
}
public void testTranslateVariables() {
stack.push(new Object() {
public String getFoo() {
@@ -233,7 +281,7 @@ public class StrutsUtilTest extends StrutsInternalTestCase {
// === internal class to assist in testing
static class InternalMockHttpServletRequest extends MockHttpServletRequest {
protected static class InternalMockHttpServletRequest extends MockHttpServletRequest {
InternalMockRequestDispatcher dispatcher = null;
public RequestDispatcher getRequestDispatcher(String path) {
dispatcher = new InternalMockRequestDispatcher(path);
@@ -245,8 +293,8 @@ public class StrutsUtilTest extends StrutsInternalTestCase {
}
}
static class InternalMockRequestDispatcher extends MockRequestDispatcher {
private String url;
protected static class InternalMockRequestDispatcher extends MockRequestDispatcher {
private final String url;
boolean included = false;
public InternalMockRequestDispatcher(String url) {
super(url);
@@ -29,7 +29,6 @@ import org.apache.struts2.ServletActionContext;
import org.apache.struts2.StrutsConstants;
import org.apache.struts2.StrutsException;
import org.apache.struts2.views.TagLibraryDirectiveProvider;
import org.apache.struts2.views.jsp.ui.OgnlTool;
import org.apache.struts2.views.util.ContextUtil;
import org.apache.velocity.VelocityContext;
import org.apache.velocity.app.Velocity;
@@ -56,6 +55,7 @@ import java.util.Properties;
import static java.lang.String.format;
import static java.util.stream.Collectors.joining;
import static java.util.stream.Collectors.toList;
import static org.apache.struts2.views.util.ContextUtil.OGNL;
import static org.apache.struts2.views.util.ContextUtil.STRUTS;
/**
@@ -101,27 +101,15 @@ public class VelocityManager {
}
/**
* @return a reference to the VelocityEngine used by <b>all</b> struts velocity thingies with the exception of
* directly accessed *.vm pages
* @return a reference to the VelocityEngine used by <strong>all</strong> Struts Velocity results except directly
* accessed *.vm pages (unless otherwise configured)
*/
public VelocityEngine getVelocityEngine() {
return velocityEngine;
}
/**
* <p>
* This method is responsible for creating the standard VelocityContext used by all WW2 velocity views. The
* following context parameters are defined:
* </p>
*
* <ul>
* <li><strong>request</strong> - the current HttpServletRequest</li>
* <li><strong>response</strong> - the current HttpServletResponse</li>
* <li><strong>stack</strong> - the current {@link ValueStack}</li>
* <li><strong>ognl</strong> - an {@link OgnlTool}</li>
* <li><strong>struts</strong> - an instance of {@link org.apache.struts2.util.StrutsUtil}</li>
* <li><strong>action</strong> - the current Struts action</li>
* </ul>
* This method is responsible for creating the standard VelocityContext used by all Struts Velocity views.
*
* @param stack the current {@link ValueStack}
* @param req the current HttpServletRequest
@@ -141,7 +129,9 @@ public class VelocityManager {
List<VelocityContext> chainedContexts = prepareChainedContexts(req, res, stack.getContext());
Context context = new StrutsVelocityContext(chainedContexts, stack);
ContextUtil.getStandardContext(stack, req, res).forEach(context::put);
context.put(STRUTS, new VelocityStrutsUtil(velocityEngine, context, stack, req, res));
VelocityStrutsUtil util = new VelocityStrutsUtil(velocityEngine, context, stack, req, res);
context.put(STRUTS, util);
context.put(OGNL, util); // Deprecated since 6.3.0
return context;
}
@@ -22,7 +22,6 @@ import com.opensymphony.xwork2.ActionContext;
import com.opensymphony.xwork2.util.ValueStack;
import org.apache.struts2.ServletActionContext;
import org.apache.struts2.junit.StrutsJUnit4TestCase;
import org.apache.struts2.views.jsp.ui.OgnlTool;
import org.apache.velocity.context.Context;
import org.apache.velocity.tools.ToolContext;
import org.junit.After;
@@ -95,7 +94,7 @@ public class VelocityManagerTest extends StrutsJUnit4TestCase {
assertNotNull(context);
assertThat(context.get("struts")).isInstanceOf(VelocityStrutsUtil.class);
assertThat(context.get("ognl")).isInstanceOf(OgnlTool.class);
assertThat(context.get("ognl")).isInstanceOf(VelocityStrutsUtil.class); // Deprecated since 6.3.0
assertThat(context.get("stack")).isInstanceOf(ValueStack.class);
assertThat(context.get("request")).isInstanceOf(HttpServletRequest.class);
assertThat(context.get("response")).isInstanceOf(HttpServletResponse.class);