Merge pull request #393 from apache/WW-5054-debug-browser

[WW-5054] debug=browser
This commit is contained in:
Lukasz Lenart
2020-02-23 10:48:06 +01:00
committed by GitHub
12 changed files with 525 additions and 372 deletions
@@ -54,7 +54,7 @@ public class OgnlUtil {
private static final Logger LOG = LogManager.getLogger(OgnlUtil.class);
private final ConcurrentMap<String, Object> expressions = new ConcurrentHashMap<>();
private final ConcurrentMap<Class, BeanInfo> beanInfoCache = new ConcurrentHashMap<>();
private final ConcurrentMap<Class<?>, BeanInfo> beanInfoCache = new ConcurrentHashMap<>();
private TypeConverter defaultConverter;
private boolean devMode;
@@ -65,18 +65,23 @@ public class OgnlUtil {
private Set<Pattern> excludedPackageNamePatterns;
private Set<String> excludedPackageNames;
private Set<Class<?>> devModeExcludedClasses;
private Set<Pattern> devModeExcludedPackageNamePatterns;
private Set<String> devModeExcludedPackageNames;
private Container container;
private boolean allowStaticFieldAccess = true;
private boolean allowStaticMethodAccess;
private boolean disallowProxyMemberAccess;
public OgnlUtil() {
excludedClasses = new HashSet<>();
excludedPackageNamePatterns = new HashSet<>();
excludedPackageNames = new HashSet<>();
excludedClasses = Collections.unmodifiableSet(excludedClasses);
excludedPackageNamePatterns = Collections.unmodifiableSet(excludedPackageNamePatterns);
excludedPackageNames = Collections.unmodifiableSet(excludedPackageNames);
excludedClasses = Collections.unmodifiableSet(new HashSet<>());
excludedPackageNamePatterns = Collections.unmodifiableSet(new HashSet<>());
excludedPackageNames = Collections.unmodifiableSet(new HashSet<>());
devModeExcludedClasses = Collections.unmodifiableSet(new HashSet<>());
devModeExcludedPackageNamePatterns = Collections.unmodifiableSet(new HashSet<>());
devModeExcludedPackageNames = Collections.unmodifiableSet(new HashSet<>());
}
@Inject
@@ -111,6 +116,14 @@ public class OgnlUtil {
this.excludedClasses = Collections.unmodifiableSet(excludedClasses);
}
@Inject(value = StrutsConstants.STRUTS_DEV_MODE_EXCLUDED_CLASSES, required = false)
protected void setDevModeExcludedClasses(String commaDelimitedClasses) {
Set<Class<?>> excludedClasses = new HashSet<>();
excludedClasses.addAll(this.devModeExcludedClasses);
excludedClasses.addAll(parseExcludedClasses(commaDelimitedClasses));
this.devModeExcludedClasses = Collections.unmodifiableSet(excludedClasses);
}
private Set<Class<?>> parseExcludedClasses(String commaDelimitedClasses) {
Set<String> classNames = TextParseUtil.commaDelimitedStringToSet(commaDelimitedClasses);
Set<Class<?>> classes = new HashSet<>();
@@ -134,6 +147,14 @@ public class OgnlUtil {
this.excludedPackageNamePatterns = Collections.unmodifiableSet(excludedPackageNamePatterns);
}
@Inject(value = StrutsConstants.STRUTS_DEV_MODE_EXCLUDED_PACKAGE_NAME_PATTERNS, required = false)
protected void setDevModeExcludedPackageNamePatterns(String commaDelimitedPackagePatterns) {
Set<Pattern> excludedPackageNamePatterns = new HashSet<>();
excludedPackageNamePatterns.addAll(this.devModeExcludedPackageNamePatterns);
excludedPackageNamePatterns.addAll(parseExcludedPackageNamePatterns(commaDelimitedPackagePatterns));
this.devModeExcludedPackageNamePatterns = Collections.unmodifiableSet(excludedPackageNamePatterns);
}
private Set<Pattern> parseExcludedPackageNamePatterns(String commaDelimitedPackagePatterns) {
Set<String> packagePatterns = TextParseUtil.commaDelimitedStringToSet(commaDelimitedPackagePatterns);
Set<Pattern> packageNamePatterns = new HashSet<>();
@@ -153,6 +174,14 @@ public class OgnlUtil {
this.excludedPackageNames = Collections.unmodifiableSet(excludedPackageNames);
}
@Inject(value = StrutsConstants.STRUTS_DEV_MODE_EXCLUDED_PACKAGE_NAMES, required = false)
protected void setDevModeExcludedPackageNames(String commaDelimitedPackageNames) {
Set<String> excludedPackageNames = new HashSet<>();
excludedPackageNames.addAll(this.devModeExcludedPackageNames);
excludedPackageNames.addAll(parseExcludedPackageNames(commaDelimitedPackageNames));
this.devModeExcludedPackageNames = Collections.unmodifiableSet(excludedPackageNames);
}
private Set<String> parseExcludedPackageNames(String commaDelimitedPackageNames) {
return TextParseUtil.commaDelimitedStringToSet(commaDelimitedPackageNames);
}
@@ -339,7 +368,7 @@ public class OgnlUtil {
* problems setting the properties
*/
public void setProperties(Map<String, ?> properties, Object o, boolean throwPropertyExceptions) {
Map context = createDefaultContext(o, null);
Map<String, Object> context = createDefaultContext(o);
setProperties(properties, o, context, throwPropertyExceptions);
}
@@ -428,17 +457,15 @@ public class OgnlUtil {
* @throws OgnlException in case of ognl errors
*/
public void setValue(final String name, final Map<String, Object> context, final Object root, final Object value) throws OgnlException {
compileAndExecute(name, context, new OgnlTask<Void>() {
public Void execute(Object tree) throws OgnlException {
if (isEvalExpression(tree, context)) {
throw new OgnlException("Eval expression/chained expressions cannot be used as parameter name");
}
if (isArithmeticExpression(tree, context)) {
throw new OgnlException("Arithmetic expressions cannot be used as parameter name");
}
Ognl.setValue(tree, context, root, value);
return null;
compileAndExecute(name, context, (OgnlTask<Void>) tree -> {
if (isEvalExpression(tree, context)) {
throw new OgnlException("Eval expression/chained expressions cannot be used as parameter name");
}
if (isArithmeticExpression(tree, context)) {
throw new OgnlException("Arithmetic expressions cannot be used as parameter name");
}
Ognl.setValue(tree, context, root, value);
return null;
});
}
@@ -447,7 +474,7 @@ public class OgnlUtil {
SimpleNode node = (SimpleNode) tree;
OgnlContext ognlContext = null;
if (context!=null && context instanceof OgnlContext) {
if (context instanceof OgnlContext) {
ognlContext = (OgnlContext) context;
}
return node.isEvalChain(ognlContext) || node.isSequence(ognlContext);
@@ -460,7 +487,7 @@ public class OgnlUtil {
SimpleNode node = (SimpleNode) tree;
OgnlContext ognlContext = null;
if (context!=null && context instanceof OgnlContext) {
if (context instanceof OgnlContext) {
ognlContext = (OgnlContext) context;
}
return node.isOperation(ognlContext);
@@ -473,7 +500,7 @@ public class OgnlUtil {
SimpleNode node = (SimpleNode) tree;
OgnlContext ognlContext = null;
if (context!=null && context instanceof OgnlContext) {
if (context instanceof OgnlContext) {
ognlContext = (OgnlContext) context;
}
return node.isSimpleMethod(ognlContext) && !node.isChain(ognlContext);
@@ -482,27 +509,15 @@ public class OgnlUtil {
}
public Object getValue(final String name, final Map<String, Object> context, final Object root) throws OgnlException {
return compileAndExecute(name, context, new OgnlTask<Object>() {
public Object execute(Object tree) throws OgnlException {
return Ognl.getValue(tree, context, root);
}
});
return compileAndExecute(name, context, tree -> Ognl.getValue(tree, context, root));
}
public Object callMethod(final String name, final Map<String, Object> context, final Object root) throws OgnlException {
return compileAndExecuteMethod(name, context, new OgnlTask<Object>() {
public Object execute(Object tree) throws OgnlException {
return Ognl.getValue(tree, context, root);
}
});
return compileAndExecuteMethod(name, context, tree -> Ognl.getValue(tree, context, root));
}
public Object getValue(final String name, final Map<String, Object> context, final Object root, final Class resultType) throws OgnlException {
return compileAndExecute(name, context, new OgnlTask<Object>() {
public Object execute(Object tree) throws OgnlException {
return Ognl.getValue(tree, context, root, resultType);
}
});
public Object getValue(final String name, final Map<String, Object> context, final Object root, final Class<?> resultType) throws OgnlException {
return compileAndExecute(name, context, tree -> Ognl.getValue(tree, context, root, resultType));
}
@@ -553,11 +568,7 @@ public class OgnlUtil {
}
public Object compile(String expression, Map<String, Object> context) throws OgnlException {
return compileAndExecute(expression,context,new OgnlTask<Object>() {
public Object execute(Object tree) throws OgnlException {
return tree;
}
});
return compileAndExecute(expression, context, tree -> tree);
}
private void checkEnableEvalExpression(Object tree, Map<String, Object> context) throws OgnlException {
@@ -608,8 +619,8 @@ public class OgnlUtil {
return;
}
final Map contextFrom = createDefaultContext(from, null);
final Map contextTo = createDefaultContext(to, null);
final Map<String, Object> contextFrom = createDefaultContext(from);
final Map<String, Object> contextTo = createDefaultContext(to);
PropertyDescriptor[] fromPds;
PropertyDescriptor[] toPds;
@@ -646,12 +657,10 @@ public class OgnlUtil {
PropertyDescriptor toPd = toPdHash.get(fromPd.getName());
if ((toPd != null) && (toPd.getWriteMethod() != null)) {
try {
compileAndExecute(fromPd.getName(), context, new OgnlTask<Object>() {
public Void execute(Object expr) throws OgnlException {
Object value = Ognl.getValue(expr, contextFrom, from);
Ognl.setValue(expr, contextTo, to, value);
return null;
}
compileAndExecute(fromPd.getName(), context, expr -> {
Object value = Ognl.getValue(expr, contextFrom, from);
Ognl.setValue(expr, contextTo, to, value);
return null;
});
} catch (OgnlException e) {
@@ -700,7 +709,7 @@ public class OgnlUtil {
* @return property descriptors.
* @throws IntrospectionException is thrown if an exception occurs during introspection.
*/
public PropertyDescriptor[] getPropertyDescriptors(Class clazz) throws IntrospectionException {
public PropertyDescriptor[] getPropertyDescriptors(Class<?> clazz) throws IntrospectionException {
BeanInfo beanInfo = getBeanInfo(clazz);
return beanInfo.getPropertyDescriptors();
}
@@ -719,17 +728,13 @@ public class OgnlUtil {
*/
public Map<String, Object> getBeanMap(final Object source) throws IntrospectionException, OgnlException {
Map<String, Object> beanMap = new HashMap<>();
final Map sourceMap = createDefaultContext(source, null);
final Map<String, Object> sourceMap = createDefaultContext(source);
PropertyDescriptor[] propertyDescriptors = getPropertyDescriptors(source);
for (PropertyDescriptor propertyDescriptor : propertyDescriptors) {
final String propertyName = propertyDescriptor.getDisplayName();
Method readMethod = propertyDescriptor.getReadMethod();
if (readMethod != null) {
final Object value = compileAndExecute(propertyName, null, new OgnlTask<Object>() {
public Object execute(Object expr) throws OgnlException {
return Ognl.getValue(expr, sourceMap, source);
}
});
final Object value = compileAndExecute(propertyName, null, expr -> Ognl.getValue(expr, sourceMap, source));
beanMap.put(propertyName, value);
} else {
beanMap.put(propertyName, "There is no read method for " + propertyName);
@@ -757,7 +762,7 @@ public class OgnlUtil {
* @return java bean info.
* @throws IntrospectionException is thrown if an exception occurs during introspection.
*/
public BeanInfo getBeanInfo(Class clazz) throws IntrospectionException {
public BeanInfo getBeanInfo(Class<?> clazz) throws IntrospectionException {
synchronized (beanInfoCache) {
BeanInfo beanInfo = beanInfoCache.get(clazz);
if (beanInfo == null) {
@@ -787,22 +792,30 @@ public class OgnlUtil {
}
}
protected Map createDefaultContext(Object root) {
protected Map<String, Object> createDefaultContext(Object root) {
return createDefaultContext(root, null);
}
protected Map createDefaultContext(Object root, ClassResolver classResolver) {
protected Map<String, Object> createDefaultContext(Object root, ClassResolver classResolver) {
ClassResolver resolver = classResolver;
if (resolver == null) {
resolver = container.getInstance(CompoundRootAccessor.class);
}
SecurityMemberAccess memberAccess = new SecurityMemberAccess(allowStaticMethodAccess, allowStaticFieldAccess);
memberAccess.setExcludedClasses(excludedClasses);
memberAccess.setExcludedPackageNamePatterns(excludedPackageNamePatterns);
memberAccess.setExcludedPackageNames(excludedPackageNames);
memberAccess.setDisallowProxyMemberAccess(disallowProxyMemberAccess);
if (devMode) {
LOG.warn("Working in devMode, using devMode excluded classes and packages!");
memberAccess.setExcludedClasses(devModeExcludedClasses);
memberAccess.setExcludedPackageNamePatterns(devModeExcludedPackageNamePatterns);
memberAccess.setExcludedPackageNames(devModeExcludedPackageNames);
} else {
memberAccess.setExcludedClasses(excludedClasses);
memberAccess.setExcludedPackageNamePatterns(excludedPackageNamePatterns);
memberAccess.setExcludedPackageNames(excludedPackageNames);
}
return Ognl.createDefaultContext(root, memberAccess, resolver, defaultConverter);
}
@@ -319,6 +319,11 @@ public final class StrutsConstants {
public static final String STRUTS_EXCLUDED_PACKAGE_NAME_PATTERNS = "struts.excludedPackageNamePatterns";
public static final String STRUTS_EXCLUDED_PACKAGE_NAMES = "struts.excludedPackageNames";
/** Comma delimited set of excluded classes and package names which cannot be accessed via expressions in devMode */
public static final String STRUTS_DEV_MODE_EXCLUDED_CLASSES = "struts.devMode.excludedClasses";
public static final String STRUTS_DEV_MODE_EXCLUDED_PACKAGE_NAME_PATTERNS = "struts.devMode.excludedPackageNamePatterns";
public static final String STRUTS_DEV_MODE_EXCLUDED_PACKAGE_NAMES = "struts.devMode.excludedPackageNames";
/** Dedicated services to check if passed string is excluded/accepted */
public static final String STRUTS_EXCLUDED_PATTERNS_CHECKER = "struts.excludedPatterns.checker";
public static final String STRUTS_ACCEPTED_PATTERNS_CHECKER = "struts.acceptedPatterns.checker";
@@ -129,6 +129,9 @@ public class ConstantConfig {
private Set<Class<?>> excludedClasses;
private List<Pattern> excludedPackageNamePatterns;
private Set<String> excludedPackageNames;
private Set<Class<?>> devModeExcludedClasses;
private List<Pattern> devModeExcludedPackageNamePatterns;
private Set<String> devModeExcludedPackageNames;
private BeanConfig excludedPatternsChecker;
private BeanConfig acceptedPatternsChecker;
private Set<Pattern> overrideExcludedPatterns;
@@ -149,7 +152,7 @@ public class ConstantConfig {
private String classesToString(Set<Class<?>> classes) {
List<String> list = null;
if (classes != null && !classes.isEmpty()) {
list = new ArrayList<String>();
list = new ArrayList<>();
for (Class<?> c : classes) {
list.add(c.getName());
}
@@ -257,6 +260,9 @@ public class ConstantConfig {
map.put(StrutsConstants.STRUTS_EXCLUDED_CLASSES, classesToString(excludedClasses));
map.put(StrutsConstants.STRUTS_EXCLUDED_PACKAGE_NAME_PATTERNS, StringUtils.join(excludedPackageNamePatterns, ','));
map.put(StrutsConstants.STRUTS_EXCLUDED_PACKAGE_NAMES, StringUtils.join(excludedPackageNames, ','));
map.put(StrutsConstants.STRUTS_DEV_MODE_EXCLUDED_CLASSES, classesToString(devModeExcludedClasses));
map.put(StrutsConstants.STRUTS_DEV_MODE_EXCLUDED_PACKAGE_NAME_PATTERNS, StringUtils.join(devModeExcludedPackageNamePatterns, ','));
map.put(StrutsConstants.STRUTS_DEV_MODE_EXCLUDED_PACKAGE_NAMES, StringUtils.join(devModeExcludedPackageNames, ','));
map.put(StrutsConstants.STRUTS_EXCLUDED_PATTERNS_CHECKER, beanConfToString(excludedPatternsChecker));
map.put(StrutsConstants.STRUTS_ACCEPTED_PATTERNS_CHECKER, beanConfToString(acceptedPatternsChecker));
map.put(StrutsConstants.STRUTS_OVERRIDE_EXCLUDED_PATTERNS, StringUtils.join(overrideExcludedPatterns, ','));
@@ -1209,6 +1215,30 @@ public class ConstantConfig {
this.excludedPackageNames = excludedPackageNames;
}
public Set<Class<?>> getDevModeExcludedClasses() {
return devModeExcludedClasses;
}
public void setDevModeExcludedClasses(Set<Class<?>> devModeExcludedClasses) {
this.devModeExcludedClasses = devModeExcludedClasses;
}
public List<Pattern> getDevModeExcludedPackageNamePatterns() {
return devModeExcludedPackageNamePatterns;
}
public void setDevModeExcludedPackageNamePatterns(List<Pattern> devModeExcludedPackageNamePatterns) {
this.devModeExcludedPackageNamePatterns = devModeExcludedPackageNamePatterns;
}
public Set<String> getDevModeExcludedPackageNames() {
return devModeExcludedPackageNames;
}
public void setDevModeExcludedPackageNames(Set<String> devModeExcludedPackageNames) {
this.devModeExcludedPackageNames = devModeExcludedPackageNames;
}
public BeanConfig getExcludedPatternsChecker() {
return excludedPatternsChecker;
}
@@ -43,7 +43,12 @@ import java.io.PrintWriter;
import java.io.StringWriter;
import java.lang.reflect.Array;
import java.lang.reflect.Method;
import java.util.*;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
/**
* <!-- START SNIPPET: description -->
@@ -65,8 +70,8 @@ import java.util.*;
* the 'xml' mode is inserted at the top of the page.</li>
* <li> <code>command</code> - Tests an OGNL expression and returns the
* string result. Only used by the OGNL console.</li>
* <li><code>browser</code> Shows field values of an object specified in the
* <code>object</code> parameter (#context by default). When the <code>object</code>
* <li><code>browser</code> Shows field values of an object specified in the
* <code>object</code> parameter (action by default). When the <code>object</code>
* parameters is set, the '#' character needs to be escaped to '%23'. Like
* debug=browser&amp;object=%23parameters</li>
* </ul>
@@ -93,9 +98,9 @@ public class DebuggingInterceptor extends AbstractInterceptor {
private final static Logger LOG = LogManager.getLogger(DebuggingInterceptor.class);
private String[] ignorePrefixes = new String[]{"org.apache.struts.",
"com.opensymphony.xwork2.", "xwork."};
"com.opensymphony.xwork2.", "xwork."};
private String[] _ignoreKeys = new String[]{"application", "session",
"parameters", "request"};
"parameters", "request"};
private HashSet<String> ignoreKeys = new HashSet<>(Arrays.asList(_ignoreKeys));
private final static String XML_MODE = "xml";
@@ -111,10 +116,10 @@ public class DebuggingInterceptor extends AbstractInterceptor {
private final static String DECORATE_PARAM = "decorate";
private boolean enableXmlWithConsole = false;
private boolean devMode;
private FreemarkerManager freemarkerManager;
private boolean consoleEnabled = false;
private ReflectionProvider reflectionProvider;
@@ -122,12 +127,12 @@ public class DebuggingInterceptor extends AbstractInterceptor {
public void setDevMode(String mode) {
this.devMode = "true".equals(mode);
}
@Inject
public void setFreemarkerManager(FreemarkerManager mgr) {
this.freemarkerManager = mgr;
}
@Inject
public void setReflectionProvider(ReflectionProvider reflectionProvider) {
this.reflectionProvider = reflectionProvider;
@@ -149,40 +154,40 @@ public class DebuggingInterceptor extends AbstractInterceptor {
ctx.getParameters().remove(DEBUG_PARAM);
if (XML_MODE.equals(type)) {
inv.addPreResultListener(
new PreResultListener() {
public void beforeResult(ActionInvocation inv, String result) {
printContext();
}
});
new PreResultListener() {
public void beforeResult(ActionInvocation inv, String result) {
printContext();
}
});
} else if (CONSOLE_MODE.equals(type)) {
consoleEnabled = true;
inv.addPreResultListener(
new PreResultListener() {
public void beforeResult(ActionInvocation inv, String actionResult) {
String xml = "";
if (enableXmlWithConsole) {
StringWriter writer = new StringWriter();
printContext(new PrettyPrintWriter(writer));
xml = writer.toString();
xml = xml.replaceAll("&", "&amp;");
xml = xml.replaceAll(">", "&gt;");
xml = xml.replaceAll("<", "&lt;");
}
ActionContext.getContext().put("debugXML", xml);
FreemarkerResult result = new FreemarkerResult();
result.setFreemarkerManager(freemarkerManager);
result.setContentType("text/html");
result.setLocation("/org/apache/struts2/interceptor/debugging/console.ftl");
result.setParse(false);
try {
result.execute(inv);
} catch (Exception ex) {
LOG.error("Unable to create debugging console", ex);
}
new PreResultListener() {
public void beforeResult(ActionInvocation inv, String actionResult) {
String xml = "";
if (enableXmlWithConsole) {
StringWriter writer = new StringWriter();
printContext(new PrettyPrintWriter(writer));
xml = writer.toString();
xml = xml.replaceAll("&", "&amp;");
xml = xml.replaceAll(">", "&gt;");
xml = xml.replaceAll("<", "&lt;");
}
});
ActionContext.getContext().put("debugXML", xml);
FreemarkerResult result = new FreemarkerResult();
result.setFreemarkerManager(freemarkerManager);
result.setContentType("text/html");
result.setLocation("/org/apache/struts2/interceptor/debugging/console.ftl");
result.setParse(false);
try {
result.execute(inv);
} catch (Exception ex) {
LOG.error("Unable to create debugging console", ex);
}
}
});
} else if (COMMAND_MODE.equals(type)) {
ValueStack stack = (ValueStack) ctx.getSession().get(SESSION_KEY);
if (stack == null) {
@@ -197,7 +202,7 @@ public class DebuggingInterceptor extends AbstractInterceptor {
res.setContentType("text/plain");
try (PrintWriter writer =
ServletActionContext.getResponse().getWriter()) {
ServletActionContext.getResponse().getWriter()) {
writer.print(stack.findValue(cmd));
} catch (IOException ex) {
ex.printStackTrace();
@@ -209,25 +214,26 @@ public class DebuggingInterceptor extends AbstractInterceptor {
new PreResultListener() {
public void beforeResult(ActionInvocation inv, String actionResult) {
String rootObjectExpression = getParameter(OBJECT_PARAM);
if (rootObjectExpression == null)
rootObjectExpression = "#context";
if (rootObjectExpression == null) {
rootObjectExpression = "action";
}
String decorate = getParameter(DECORATE_PARAM);
ValueStack stack = (ValueStack) ctx.get(ActionContext.VALUE_STACK);
Object rootObject = stack.findValue(rootObjectExpression);
try (StringWriter writer = new StringWriter()) {
ObjectToHTMLWriter htmlWriter = new ObjectToHTMLWriter(writer);
htmlWriter.write(reflectionProvider, rootObject, rootObjectExpression);
String html = writer.toString();
writer.close();
stack.set("debugHtml", html);
//on the first request, response can be decorated
//but we need plain text on the other ones
if ("false".equals(decorate))
ServletActionContext.getRequest().setAttribute("decorator", "none");
FreemarkerResult result = new FreemarkerResult();
result.setFreemarkerManager(freemarkerManager);
result.setContentType("text/html");
@@ -240,7 +246,7 @@ public class DebuggingInterceptor extends AbstractInterceptor {
}
});
}
}
}
if (cont) {
try {
if (actionOnly) {
@@ -280,7 +286,7 @@ public class DebuggingInterceptor extends AbstractInterceptor {
try {
PrettyPrintWriter writer = new PrettyPrintWriter(
ServletActionContext.getResponse().getWriter());
ServletActionContext.getResponse().getWriter());
printContext(writer);
writer.close();
} catch (IOException ex) {
@@ -421,10 +427,10 @@ public class DebuggingInterceptor extends AbstractInterceptor {
private List<Object> filterValueStack(Map requestMap) {
List<Object> filter = new ArrayList<>();
Object valueStack = requestMap.get("struts.valueStack");
if(valueStack != null) {
filter.add(valueStack);
}
return filter;
if (valueStack != null) {
filter.add(valueStack);
}
return filter;
}
}
@@ -20,6 +20,8 @@ package org.apache.struts2.interceptor.debugging;
import com.opensymphony.xwork2.util.reflection.ReflectionException;
import com.opensymphony.xwork2.util.reflection.ReflectionProvider;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import java.beans.IntrospectionException;
import java.io.Writer;
@@ -33,6 +35,9 @@ import java.util.Set;
*
*/
class ObjectToHTMLWriter {
private static final Logger LOG = LogManager.getLogger(ObjectToHTMLWriter.class);
private PrettyPrintWriter prettyWriter;
ObjectToHTMLWriter(Writer writer) {
@@ -40,36 +45,43 @@ class ObjectToHTMLWriter {
this.prettyWriter.setEscape(false);
}
@SuppressWarnings("unchecked")
public void write(ReflectionProvider reflectionProvider, Object root, String expr) throws IntrospectionException,
ReflectionException {
prettyWriter.startNode("table");
prettyWriter.addAttribute("class", "debugTable");
if (root instanceof Map) {
for (Object next : ((Map) root).entrySet()) {
Map.Entry property = (Map.Entry) next;
if (root == null) {
LOG.info("Root is null");
writeProperty("root", null, expr);
} else if (root instanceof Map) {
LOG.info("Root is a Map");
for (Object next : ((Map<?, ?>) root).entrySet()) {
Map.Entry<?, ?> property = (Map.Entry<?, ?>) next;
String key = property.getKey().toString();
Object value = property.getValue();
writeProperty(key, value, expr);
}
} else if (root instanceof List) {
List list = (List) root;
LOG.info("Root is a List");
List<?> list = (List<?>) root;
for (int i = 0; i < list.size(); i++) {
Object element = list.get(i);
writeProperty(String.valueOf(i), element, expr);
}
} else if (root instanceof Set) {
Set set = (Set) root;
LOG.info("Root is a Set");
Set<?> set = (Set<?>) root;
for (Object next : set) {
writeProperty("", next, expr);
}
} else if (root.getClass().isArray()) {
LOG.info("Root is an Array");
Object[] objects = (Object[]) root;
for (int i = 0; i < objects.length; i++) {
writeProperty(String.valueOf(i), objects[i], expr);
}
} else {
LOG.info("Root is {}", root.getClass());
//print properties
Map<String, Object> properties = reflectionProvider.getBeanMap(root);
for (Map.Entry<String, Object> property : properties.entrySet()) {
@@ -99,9 +111,9 @@ class ObjectToHTMLWriter {
//value cell
prettyWriter.startNode("td");
if (value != null) {
LOG.info("Writing property [{}] as [{}]", name, value);
//if is is an empty collection or array, don't write a link
if (isEmptyCollection(value) || isEmptyMap(value) || (value.getClass()
.isArray() && ((Object[]) value).length == 0)) {
if (isEmptyCollection(value) || isEmptyMap(value) || (value.getClass().isArray() && ((Object[]) value).length == 0)) {
prettyWriter.addAttribute("class", "emptyCollection");
prettyWriter.setValue("empty");
} else {
@@ -118,7 +130,7 @@ class ObjectToHTMLWriter {
prettyWriter.startNode("td");
if (value != null) {
prettyWriter.addAttribute("class", "typeColumn");
Class clazz = value.getClass();
Class<?> clazz = value.getClass();
prettyWriter.setValue(clazz.getName());
} else {
prettyWriter.addAttribute("class", "nullValue");
@@ -135,7 +147,7 @@ class ObjectToHTMLWriter {
*/
private boolean isEmptyMap(Object value) {
try {
return value instanceof Map && ((Map) value).isEmpty();
return value instanceof Map && ((Map<?, ?>) value).isEmpty();
} catch (Exception e) {
return true;
}
@@ -146,14 +158,14 @@ class ObjectToHTMLWriter {
*/
private boolean isEmptyCollection(Object value) {
try {
return value instanceof Collection && ((Collection) value).isEmpty();
return value instanceof Collection && ((Collection<?>) value).isEmpty();
} catch (Exception e) {
return true;
}
}
private void writeValue(String name, Object value, String expr) {
Class clazz = value.getClass();
Class<?> clazz = value.getClass();
if (clazz.isPrimitive() || Number.class.isAssignableFrom(clazz) ||
clazz.equals(String.class) || Boolean.class.equals(clazz)) {
prettyWriter.setValue(String.valueOf(value));
@@ -86,6 +86,6 @@
</script>
<body>
${debugHtml}
${debugHtml?no_esc}
</body>
</html>
@@ -48,9 +48,21 @@
sun.misc.Unsafe,
com.opensymphony.xwork2.ActionContext" />
<constant name="struts.devMode.excludedClasses"
value="
java.lang.Object,
java.lang.Runtime,
java.lang.System,
java.lang.Class,
java.lang.ClassLoader,
java.lang.Shutdown,
java.lang.ProcessBuilder,
sun.misc.Unsafe" />
<!-- this must be valid regex, each '.' in package name must be escaped! -->
<!-- it's more flexible but slower than simple string comparison -->
<!-- constant name="struts.excludedPackageNamePatterns" value="^java\.lang\..*,^ognl.*,^(?!javax\.servlet\..+)(javax\..+)" / -->
<!-- constant name="struts.devMode.excludedPackageNamePatterns" value="^java\.lang\..*,^ognl.*,^(?!javax\.servlet\..+)(javax\..+)" / -->
<!-- this is simpler version of the above used with string comparison -->
<constant name="struts.excludedPackageNames"
@@ -75,6 +87,28 @@
com.opensymphony.xwork2.security.,
com.opensymphony.xwork2.util." />
<constant name="struts.devMode.excludedPackageNames"
value="
ognl.,
java.io.,
java.net.,
java.nio.,
javax.,
freemarker.core.,
freemarker.template.,
freemarker.ext.jsp.,
freemarker.ext.rhino.,
sun.misc.,
sun.reflect.,
javassist.,
org.apache.velocity.,
org.objectweb.asm.,
org.springframework.context.,
com.opensymphony.xwork2.inject.,
com.opensymphony.xwork2.ognl.,
com.opensymphony.xwork2.security.,
com.opensymphony.xwork2.util." />
<bean class="com.opensymphony.xwork2.ObjectFactory" name="struts"/>
<bean type="com.opensymphony.xwork2.factory.ResultFactory" name="struts" class="org.apache.struts2.factory.StrutsResultFactory" />
<bean type="com.opensymphony.xwork2.factory.ActionFactory" name="struts" class="com.opensymphony.xwork2.factory.DefaultActionFactory" />
File diff suppressed because it is too large Load Diff
@@ -252,4 +252,9 @@ public class Foo {
public void setAnimalMap(Map<MyNumber, Animal> animalMap) {
this.animalMap = animalMap;
}
@Override
public String toString() {
return "Foo";
}
}
@@ -18,6 +18,13 @@
*/
package org.apache.struts2.config.entities;
import com.opensymphony.xwork2.TestBean;
import com.opensymphony.xwork2.inject.Container;
import org.apache.struts2.StrutsConstants;
import org.junit.Assert;
import org.junit.Test;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashSet;
import java.util.LinkedHashSet;
@@ -25,20 +32,14 @@ import java.util.Locale;
import java.util.Map;
import java.util.Set;
import org.apache.struts2.StrutsConstants;
import org.junit.Assert;
import org.junit.Test;
import com.opensymphony.xwork2.TestBean;
import com.opensymphony.xwork2.inject.Container;
public class ConstantConfigTest {
@Test
public void testBeanConfToString() throws Exception {
public void testBeanConfToString() {
ConstantConfig constantConfig = new ConstantConfig();
String actual = constantConfig.beanConfToString(null);
Assert.assertEquals(null, actual);
Assert.assertNull(actual);
actual = constantConfig.beanConfToString(new BeanConfig(TestBean.class));
Assert.assertEquals(Container.DEFAULT_NAME, actual);
@@ -49,7 +50,7 @@ public class ConstantConfigTest {
}
@Test
public void testGetAllAsStringsMap() throws Exception {
public void testGetAllAsStringsMap() {
ConstantConfig constantConfig = new ConstantConfig();
boolean expectedDevMode = true;
@@ -65,22 +66,32 @@ public class ConstantConfigTest {
Assert.assertEquals(String.valueOf(expectedDevMode), map.get(StrutsConstants.STRUTS_DEVMODE));
Assert.assertEquals(expectedActionExtensions, map.get(StrutsConstants.STRUTS_ACTION_EXTENSION));
Assert.assertEquals(null, map.get(StrutsConstants.STRUTS_I18N_RELOAD));
Assert.assertNull(map.get(StrutsConstants.STRUTS_I18N_RELOAD));
Assert.assertEquals(expectedLanguage, map.get(StrutsConstants.STRUTS_LOCALE));
}
@Test
public void testEmptyClassesToString() throws Exception {
public void testEmptyClassesToString() {
ConstantConfig constantConfig = new ConstantConfig();
constantConfig.setExcludedClasses(new HashSet<Class<?>>());
constantConfig.setExcludedClasses(null);
constantConfig.setExcludedPackageNamePatterns(null);
constantConfig.setExcludedPackageNames(null);
constantConfig.setDevModeExcludedClasses(null);
constantConfig.setDevModeExcludedPackageNamePatterns(null);
constantConfig.setDevModeExcludedPackageNames(null);
Map<String, String> map = constantConfig.getAllAsStringsMap();
Assert.assertEquals(null, map.get(StrutsConstants.STRUTS_EXCLUDED_CLASSES));
Assert.assertNull(map.get(StrutsConstants.STRUTS_EXCLUDED_CLASSES));
Assert.assertNull(map.get(StrutsConstants.STRUTS_EXCLUDED_PACKAGE_NAME_PATTERNS));
Assert.assertNull(map.get(StrutsConstants.STRUTS_EXCLUDED_PACKAGE_NAMES));
Assert.assertNull(map.get(StrutsConstants.STRUTS_DEV_MODE_EXCLUDED_CLASSES));
Assert.assertNull(map.get(StrutsConstants.STRUTS_DEV_MODE_EXCLUDED_PACKAGE_NAME_PATTERNS));
Assert.assertNull(map.get(StrutsConstants.STRUTS_DEV_MODE_EXCLUDED_PACKAGE_NAMES));
}
@Test
public void testClassesToString() throws Exception {
public void testClassesToString() {
ConstantConfig constantConfig = new ConstantConfig();
Set<Class<?>> excludedClasses = new LinkedHashSet<>();
@@ -89,9 +100,13 @@ public class ConstantConfigTest {
excludedClasses.add(System.class);
constantConfig.setExcludedClasses(excludedClasses);
constantConfig.setDevModeExcludedClasses(excludedClasses);
Map<String, String> map = constantConfig.getAllAsStringsMap();
Assert.assertEquals("java.lang.Object,java.lang.Runtime,java.lang.System",
map.get(StrutsConstants.STRUTS_EXCLUDED_CLASSES));
map.get(StrutsConstants.STRUTS_EXCLUDED_CLASSES));
Assert.assertEquals("java.lang.Object,java.lang.Runtime,java.lang.System",
map.get(StrutsConstants.STRUTS_DEV_MODE_EXCLUDED_CLASSES));
}
}
@@ -26,7 +26,7 @@
<td>
<ul>
<#list actionNames as name>
<@s.url var="showConfig" action="showConfig" includeParams="none">
<@s.url var="showConfig" action="showConfig" includeParams="none" escapeAmp="false">
<@s.param name="namespace">${namespace}</@s.param>
<@s.param name="actionName">${name}</@s.param>
</@s.url>
@@ -39,11 +39,11 @@
</table>
<!-- URLTag is faulty -->
<@s.url var="url" action="showConfig" includeParams="none">
<@s.url var="url" action="showConfig" includeParams="none" escapeAmp="false">
<@s.param name="namespace">${namespace}</@s.param>
<@s.param name="actionName">${actionName}</@s.param>
</@s.url>
<#assign url = url + "&amp;detailView=">
<#assign url = url + "&detailView=">
<!-- Set all to false -->
<#assign detailsSelected = false>
<#assign exceptionsSelected = false>