mirror of
https://github.com/apache/struts.git
synced 2026-08-05 22:56:59 +00:00
Merge pull request #1187 from apache/fix/WW-5517-debug
WW-5517 Fixes <s:debug/> to be compatible with allowlist capability
This commit is contained in:
@@ -18,7 +18,10 @@
|
||||
*/
|
||||
package org.apache.struts2.components;
|
||||
|
||||
import org.apache.commons.lang3.ClassUtils;
|
||||
import org.apache.struts2.inject.Inject;
|
||||
import org.apache.struts2.ognl.ThreadAllowlist;
|
||||
import org.apache.struts2.util.CompoundRoot;
|
||||
import org.apache.struts2.util.ValueStack;
|
||||
import org.apache.struts2.util.reflection.ReflectionProvider;
|
||||
import jakarta.servlet.http.HttpServletRequest;
|
||||
@@ -40,6 +43,7 @@ public class Debug extends UIBean {
|
||||
|
||||
protected ReflectionProvider reflectionProvider;
|
||||
|
||||
private ThreadAllowlist threadAllowlist;
|
||||
|
||||
public Debug(ValueStack stack, HttpServletRequest request, HttpServletResponse response) {
|
||||
super(stack, request, response);
|
||||
@@ -50,6 +54,11 @@ public class Debug extends UIBean {
|
||||
this.reflectionProvider = prov;
|
||||
}
|
||||
|
||||
@Inject
|
||||
public void setThreadAllowlist(ThreadAllowlist threadAllowlist) {
|
||||
this.threadAllowlist = threadAllowlist;
|
||||
}
|
||||
|
||||
protected String getDefaultTemplate() {
|
||||
return TEMPLATE;
|
||||
}
|
||||
@@ -59,16 +68,19 @@ public class Debug extends UIBean {
|
||||
|
||||
if (showDebug()) {
|
||||
ValueStack stack = getStack();
|
||||
Iterator iter = stack.getRoot().iterator();
|
||||
List stackValues = new ArrayList(stack.getRoot().size());
|
||||
allowList(stack.getRoot());
|
||||
|
||||
Iterator<Object> iter = stack.getRoot().iterator();
|
||||
List<Object> stackValues = new ArrayList<>(stack.getRoot().size());
|
||||
while (iter.hasNext()) {
|
||||
Object o = iter.next();
|
||||
Map values;
|
||||
Map<String, Object> values;
|
||||
try {
|
||||
values = reflectionProvider.getBeanMap(o);
|
||||
} catch (Exception e) {
|
||||
throw new StrutsException("Caught an exception while getting the property values of " + o, e);
|
||||
}
|
||||
allowListClass(o);
|
||||
stackValues.add(new DebugMapEntry(o.getClass().getName(), values));
|
||||
}
|
||||
|
||||
@@ -77,6 +89,16 @@ public class Debug extends UIBean {
|
||||
return result;
|
||||
}
|
||||
|
||||
private void allowList(CompoundRoot root) {
|
||||
root.forEach(this::allowListClass);
|
||||
}
|
||||
|
||||
private void allowListClass(Object o) {
|
||||
threadAllowlist.allowClass(o.getClass());
|
||||
ClassUtils.getAllSuperclasses(o.getClass()).forEach(threadAllowlist::allowClass);
|
||||
ClassUtils.getAllInterfaces(o.getClass()).forEach(threadAllowlist::allowClass);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean end(Writer writer, String body) {
|
||||
if (showDebug()) {
|
||||
@@ -91,17 +113,17 @@ public class Debug extends UIBean {
|
||||
return (devMode || Boolean.TRUE == PrepareOperations.getDevModeOverride());
|
||||
}
|
||||
|
||||
private static class DebugMapEntry implements Map.Entry {
|
||||
private final Object key;
|
||||
private static class DebugMapEntry implements Map.Entry<String, Object> {
|
||||
private final String key;
|
||||
private Object value;
|
||||
|
||||
DebugMapEntry(Object key, Object value) {
|
||||
DebugMapEntry(String key, Object value) {
|
||||
this.key = key;
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getKey() {
|
||||
public String getKey() {
|
||||
return key;
|
||||
}
|
||||
|
||||
|
||||
+55
-53
@@ -18,11 +18,14 @@
|
||||
*/
|
||||
package org.apache.struts2.interceptor;
|
||||
|
||||
import org.apache.logging.log4j.Level;
|
||||
import org.apache.logging.log4j.LogManager;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
import org.apache.struts2.ActionInvocation;
|
||||
import org.apache.struts2.config.entities.ExceptionMappingConfig;
|
||||
import org.apache.struts2.dispatcher.HttpParameters;
|
||||
import org.apache.struts2.inject.Inject;
|
||||
import org.apache.struts2.ognl.ThreadAllowlist;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
@@ -42,11 +45,11 @@ import java.util.Map;
|
||||
* you make this interceptor the first interceptor on the stack, ensuring that it has full access to catch any
|
||||
* exception, even those caused by other interceptors.
|
||||
* </p>
|
||||
*
|
||||
* <p>
|
||||
* <!-- END SNIPPET: description -->
|
||||
*
|
||||
* <p><u>Interceptor parameters:</u></p>
|
||||
*
|
||||
* <p>
|
||||
* <!-- START SNIPPET: parameters -->
|
||||
*
|
||||
* <ul>
|
||||
@@ -64,11 +67,11 @@ import java.util.Map;
|
||||
* The parameters above enables us to log all thrown exceptions with stacktace in our own logfile,
|
||||
* and present a friendly webpage (with no stacktrace) to the end user.
|
||||
* </p>
|
||||
*
|
||||
* <p>
|
||||
* <!-- END SNIPPET: parameters -->
|
||||
*
|
||||
* <p><u>Extending the interceptor:</u></p>
|
||||
*
|
||||
* <p>
|
||||
* <!-- START SNIPPET: extending -->
|
||||
* <p>
|
||||
* If you want to add custom handling for publishing the Exception, you may override
|
||||
@@ -158,11 +161,17 @@ public class ExceptionMappingInterceptor extends AbstractInterceptor {
|
||||
|
||||
private static final Logger LOG = LogManager.getLogger(ExceptionMappingInterceptor.class);
|
||||
|
||||
private transient ThreadAllowlist threadAllowlist;
|
||||
|
||||
protected Logger categoryLogger;
|
||||
protected boolean logEnabled = false;
|
||||
protected String logCategory;
|
||||
protected String logLevel;
|
||||
|
||||
@Inject
|
||||
public void setThreadAllowlist(ThreadAllowlist threadAllowlist) {
|
||||
this.threadAllowlist = threadAllowlist;
|
||||
}
|
||||
|
||||
public boolean isLogEnabled() {
|
||||
return logEnabled;
|
||||
@@ -173,20 +182,20 @@ public class ExceptionMappingInterceptor extends AbstractInterceptor {
|
||||
}
|
||||
|
||||
public String getLogCategory() {
|
||||
return logCategory;
|
||||
}
|
||||
return logCategory;
|
||||
}
|
||||
|
||||
public void setLogCategory(String logCatgory) {
|
||||
this.logCategory = logCatgory;
|
||||
}
|
||||
public void setLogCategory(String logCategory) {
|
||||
this.logCategory = logCategory;
|
||||
}
|
||||
|
||||
public String getLogLevel() {
|
||||
return logLevel;
|
||||
}
|
||||
public String getLogLevel() {
|
||||
return logLevel;
|
||||
}
|
||||
|
||||
public void setLogLevel(String logLevel) {
|
||||
this.logLevel = logLevel;
|
||||
}
|
||||
public void setLogLevel(String logLevel) {
|
||||
this.logLevel = logLevel;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String intercept(ActionInvocation invocation) throws Exception {
|
||||
@@ -200,13 +209,16 @@ public class ExceptionMappingInterceptor extends AbstractInterceptor {
|
||||
}
|
||||
List<ExceptionMappingConfig> exceptionMappings = invocation.getProxy().getConfig().getExceptionMappings();
|
||||
ExceptionMappingConfig mappingConfig = this.findMappingFromExceptions(exceptionMappings, e);
|
||||
if (mappingConfig != null && mappingConfig.getResult()!=null) {
|
||||
if (mappingConfig != null && mappingConfig.getResult() != null) {
|
||||
Map<String, String> mappingParams = mappingConfig.getParams();
|
||||
// create a mutable HashMap since some interceptors will remove parameters, and parameterMap is immutable
|
||||
HttpParameters parameters = HttpParameters.create(mappingParams).build();
|
||||
invocation.getInvocationContext().withParameters(parameters);
|
||||
result = mappingConfig.getResult();
|
||||
publishException(invocation, new ExceptionHolder(e));
|
||||
ExceptionHolder holder = new ExceptionHolder(e);
|
||||
threadAllowlist.allowClass(holder.getClass());
|
||||
threadAllowlist.allowClass(e.getClass());
|
||||
publishException(invocation, holder);
|
||||
} else {
|
||||
throw e;
|
||||
}
|
||||
@@ -221,55 +233,45 @@ public class ExceptionMappingInterceptor extends AbstractInterceptor {
|
||||
* @param e the exception to log.
|
||||
*/
|
||||
protected void handleLogging(Exception e) {
|
||||
if (logCategory != null) {
|
||||
if (categoryLogger == null) {
|
||||
// init category logger
|
||||
categoryLogger = LogManager.getLogger(logCategory);
|
||||
}
|
||||
doLog(categoryLogger, e);
|
||||
} else {
|
||||
doLog(LOG, e);
|
||||
}
|
||||
if (logCategory != null) {
|
||||
if (categoryLogger == null) {
|
||||
// init category logger
|
||||
categoryLogger = LogManager.getLogger(logCategory);
|
||||
}
|
||||
doLog(categoryLogger, e);
|
||||
} else {
|
||||
doLog(LOG, e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Performs the actual logging.
|
||||
*
|
||||
* @param logger the provided logger to use.
|
||||
* @param e the exception to log.
|
||||
* @param logger the provided logger to use.
|
||||
* @param e the exception to log.
|
||||
*/
|
||||
protected void doLog(Logger logger, Exception e) {
|
||||
if (logLevel == null) {
|
||||
logger.debug(e.getMessage(), e);
|
||||
return;
|
||||
}
|
||||
if (logLevel == null) {
|
||||
logger.debug(e.getMessage(), e);
|
||||
return;
|
||||
}
|
||||
|
||||
if ("trace".equalsIgnoreCase(logLevel)) {
|
||||
logger.trace(e.getMessage(), e);
|
||||
} else if ("debug".equalsIgnoreCase(logLevel)) {
|
||||
logger.debug(e.getMessage(), e);
|
||||
} else if ("info".equalsIgnoreCase(logLevel)) {
|
||||
logger.info(e.getMessage(), e);
|
||||
} else if ("warn".equalsIgnoreCase(logLevel)) {
|
||||
logger.warn(e.getMessage(), e);
|
||||
} else if ("error".equalsIgnoreCase(logLevel)) {
|
||||
logger.error(e.getMessage(), e);
|
||||
} else if ("fatal".equalsIgnoreCase(logLevel)) {
|
||||
logger.fatal(e.getMessage(), e);
|
||||
} else {
|
||||
throw new IllegalArgumentException("LogLevel [" + logLevel + "] is not supported");
|
||||
}
|
||||
Level level = Level.getLevel(logLevel);
|
||||
if (level == null) {
|
||||
throw new IllegalArgumentException("LogLevel [" + logLevel + "] is not supported");
|
||||
}
|
||||
logger.log(level, e.getMessage(), e);
|
||||
}
|
||||
|
||||
/**
|
||||
* Try to find appropriate {@link ExceptionMappingConfig} based on provided Throwable
|
||||
*
|
||||
* @param exceptionMappings list of defined exception mappings
|
||||
* @param t caught exception
|
||||
* @param t caught exception
|
||||
* @return appropriate mapping or null
|
||||
*/
|
||||
protected ExceptionMappingConfig findMappingFromExceptions(List<ExceptionMappingConfig> exceptionMappings, Throwable t) {
|
||||
ExceptionMappingConfig config = null;
|
||||
ExceptionMappingConfig config = null;
|
||||
// Check for specific exception mappings.
|
||||
if (exceptionMappings != null) {
|
||||
int deepest = Integer.MAX_VALUE;
|
||||
@@ -288,15 +290,15 @@ public class ExceptionMappingInterceptor extends AbstractInterceptor {
|
||||
* Return the depth to the superclass matching. 0 means ex matches exactly. Returns -1 if there's no match.
|
||||
* Otherwise, returns depth. Lowest depth wins.
|
||||
*
|
||||
* @param exceptionMapping the mapping classname
|
||||
* @param t the cause
|
||||
* @param exceptionMapping the mapping classname
|
||||
* @param t the cause
|
||||
* @return the depth, if not found -1 is returned.
|
||||
*/
|
||||
public int getDepth(String exceptionMapping, Throwable t) {
|
||||
return getDepth(exceptionMapping, t.getClass(), 0);
|
||||
}
|
||||
|
||||
private int getDepth(String exceptionMapping, Class exceptionClass, int depth) {
|
||||
private int getDepth(String exceptionMapping, Class<?> exceptionClass, int depth) {
|
||||
if (exceptionClass.getName().contains(exceptionMapping)) {
|
||||
// Found it!
|
||||
return depth;
|
||||
@@ -312,7 +314,7 @@ public class ExceptionMappingInterceptor extends AbstractInterceptor {
|
||||
* Default implementation to handle ExceptionHolder publishing. Pushes given ExceptionHolder on the stack.
|
||||
* Subclasses may override this to customize publishing.
|
||||
*
|
||||
* @param invocation The invocation to publish Exception for.
|
||||
* @param invocation The invocation to publish Exception for.
|
||||
* @param exceptionHolder The exceptionHolder wrapping the Exception to publish.
|
||||
*/
|
||||
protected void publishException(ActionInvocation invocation, ExceptionHolder exceptionHolder) {
|
||||
|
||||
+34
-19
@@ -18,21 +18,23 @@
|
||||
*/
|
||||
package org.apache.struts2.interceptor.debugging;
|
||||
|
||||
import org.apache.struts2.ActionContext;
|
||||
import org.apache.struts2.ActionInvocation;
|
||||
import org.apache.struts2.inject.Inject;
|
||||
import org.apache.struts2.interceptor.AbstractInterceptor;
|
||||
import org.apache.struts2.util.ValueStack;
|
||||
import org.apache.struts2.util.reflection.ReflectionProvider;
|
||||
import jakarta.servlet.http.HttpServletResponse;
|
||||
import org.apache.commons.lang3.ClassUtils;
|
||||
import org.apache.logging.log4j.LogManager;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
import org.apache.struts2.ActionContext;
|
||||
import org.apache.struts2.ActionInvocation;
|
||||
import org.apache.struts2.ServletActionContext;
|
||||
import org.apache.struts2.StrutsConstants;
|
||||
import org.apache.struts2.dispatcher.DispatcherConstants;
|
||||
import org.apache.struts2.dispatcher.Parameter;
|
||||
import org.apache.struts2.dispatcher.PrepareOperations;
|
||||
import org.apache.struts2.dispatcher.RequestMap;
|
||||
import org.apache.struts2.inject.Inject;
|
||||
import org.apache.struts2.interceptor.AbstractInterceptor;
|
||||
import org.apache.struts2.ognl.ThreadAllowlist;
|
||||
import org.apache.struts2.util.ValueStack;
|
||||
import org.apache.struts2.util.reflection.ReflectionProvider;
|
||||
import org.apache.struts2.views.freemarker.FreemarkerManager;
|
||||
import org.apache.struts2.views.freemarker.FreemarkerResult;
|
||||
|
||||
@@ -101,10 +103,10 @@ public class DebuggingInterceptor extends AbstractInterceptor {
|
||||
|
||||
private final String[] ignorePrefixes = new String[]{"org.apache.struts.", "org.apache.struts2.", "xwork."};
|
||||
private final Set<String> ignoreKeys = Set.of(
|
||||
DispatcherConstants.APPLICATION,
|
||||
DispatcherConstants.SESSION,
|
||||
DispatcherConstants.PARAMETERS,
|
||||
DispatcherConstants.REQUEST
|
||||
DispatcherConstants.APPLICATION,
|
||||
DispatcherConstants.SESSION,
|
||||
DispatcherConstants.PARAMETERS,
|
||||
DispatcherConstants.REQUEST
|
||||
);
|
||||
|
||||
private final static String XML_MODE = "xml";
|
||||
@@ -126,6 +128,7 @@ public class DebuggingInterceptor extends AbstractInterceptor {
|
||||
|
||||
private boolean consoleEnabled = false;
|
||||
private ReflectionProvider reflectionProvider;
|
||||
private transient ThreadAllowlist threadAllowlist;
|
||||
|
||||
@Inject(StrutsConstants.STRUTS_DEVMODE)
|
||||
public void setDevMode(String mode) {
|
||||
@@ -142,6 +145,11 @@ public class DebuggingInterceptor extends AbstractInterceptor {
|
||||
this.reflectionProvider = reflectionProvider;
|
||||
}
|
||||
|
||||
@Inject
|
||||
public void setThreadAllowlist(ThreadAllowlist threadAllowlist) {
|
||||
this.threadAllowlist = threadAllowlist;
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
*
|
||||
@@ -200,7 +208,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) {
|
||||
LOG.warn("Interceptor in: {} mode has failed!", COMMAND_MODE, ex);
|
||||
@@ -217,6 +225,7 @@ public class DebuggingInterceptor extends AbstractInterceptor {
|
||||
String decorate = getParameter(DECORATE_PARAM);
|
||||
ValueStack stack = ctx.getValueStack();
|
||||
Object rootObject = stack.findValue(rootObjectExpression);
|
||||
allowListClass(rootObject);
|
||||
|
||||
try (StringWriter writer = new StringWriter()) {
|
||||
ObjectToHTMLWriter htmlWriter = new ObjectToHTMLWriter(writer);
|
||||
@@ -228,8 +237,9 @@ public class DebuggingInterceptor extends AbstractInterceptor {
|
||||
|
||||
//on the first request, response can be decorated
|
||||
//but we need plain text on the other ones
|
||||
if ("false".equals(decorate))
|
||||
if ("false".equals(decorate)) {
|
||||
ServletActionContext.getRequest().setAttribute("decorator", "none");
|
||||
}
|
||||
|
||||
FreemarkerResult result = new FreemarkerResult();
|
||||
result.setFreemarkerManager(freemarkerManager);
|
||||
@@ -239,7 +249,6 @@ public class DebuggingInterceptor extends AbstractInterceptor {
|
||||
} catch (Exception ex) {
|
||||
LOG.error("Unable to create debugging console", ex);
|
||||
}
|
||||
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -262,6 +271,14 @@ public class DebuggingInterceptor extends AbstractInterceptor {
|
||||
}
|
||||
}
|
||||
|
||||
private void allowListClass(Object o) {
|
||||
if (o != null) {
|
||||
threadAllowlist.allowClass(o.getClass());
|
||||
ClassUtils.getAllSuperclasses(o.getClass()).forEach(threadAllowlist::allowClass);
|
||||
ClassUtils.getAllInterfaces(o.getClass()).forEach(threadAllowlist::allowClass);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a single string from the request parameters
|
||||
*
|
||||
@@ -277,12 +294,11 @@ public class DebuggingInterceptor extends AbstractInterceptor {
|
||||
* Prints the current context to the response in XML format.
|
||||
*/
|
||||
protected void printContext() {
|
||||
HttpServletResponse res = ServletActionContext.getResponse();
|
||||
res.setContentType("text/xml");
|
||||
HttpServletResponse response = ActionContext.getContext().getServletResponse();
|
||||
response.setContentType("text/xml");
|
||||
|
||||
try {
|
||||
PrettyPrintWriter writer = new PrettyPrintWriter(
|
||||
ServletActionContext.getResponse().getWriter());
|
||||
PrettyPrintWriter writer = new PrettyPrintWriter(response.getWriter());
|
||||
printContext(writer);
|
||||
writer.close();
|
||||
} catch (IOException ex) {
|
||||
@@ -311,6 +327,7 @@ public class DebuggingInterceptor extends AbstractInterceptor {
|
||||
}
|
||||
}
|
||||
if (print) {
|
||||
allowListClass(ctxMap.get(key));
|
||||
serializeIt(ctxMap.get(key), key, writer, new ArrayList<>());
|
||||
}
|
||||
}
|
||||
@@ -426,5 +443,3 @@ public class DebuggingInterceptor extends AbstractInterceptor {
|
||||
return filter;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -25,8 +25,6 @@ import org.apache.commons.lang3.BooleanUtils;
|
||||
import org.apache.logging.log4j.LogManager;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
import org.apache.struts2.StrutsConstants;
|
||||
import org.apache.struts2.ognl.ProviderAllowlist;
|
||||
import org.apache.struts2.ognl.ThreadAllowlist;
|
||||
|
||||
import java.lang.reflect.AccessibleObject;
|
||||
import java.lang.reflect.Constructor;
|
||||
|
||||
@@ -19,8 +19,8 @@
|
||||
*/
|
||||
-->
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<@s.style>
|
||||
<html lang="en">
|
||||
<style>
|
||||
.debugTable {
|
||||
border-style: solid;
|
||||
border-width: 1px;
|
||||
@@ -50,40 +50,40 @@
|
||||
.emptyCollection {
|
||||
background-color: #EEEEEE;
|
||||
}
|
||||
</@s.style>
|
||||
</style>
|
||||
|
||||
<@s.script>
|
||||
<script>
|
||||
function expand(src, path) {
|
||||
var baseUrl = location.href;
|
||||
var i = baseUrl.indexOf("&object=");
|
||||
let baseUrl = location.href;
|
||||
const i = baseUrl.indexOf('&object=');
|
||||
baseUrl = (i > 0 ? baseUrl.substring(0, i) : baseUrl) + "&object=" + path;
|
||||
if (baseUrl.indexOf("decorate") < 0) {
|
||||
baseUrl += "&decorate=false";
|
||||
}
|
||||
|
||||
var request = new XMLHttpRequest();
|
||||
const request = new XMLHttpRequest();
|
||||
request.open('GET', baseUrl, true);
|
||||
request.onreadystatechange = function() {
|
||||
if (this.readyState === 4) {
|
||||
if (this.status >= 200 && this.status < 400) {
|
||||
var div = document.createElement("div");
|
||||
const div = document.createElement('div');
|
||||
console.log(this.responseText);
|
||||
div.innerHTML = this.responseText;
|
||||
src.parentNode.appendChild(div);
|
||||
|
||||
src.innerHTML = "Collapse";
|
||||
var oldonclick = src.onclick;
|
||||
const oldOnclick = src.onclick;
|
||||
src.onclick = function() {
|
||||
src.innerHTML = "Expand";
|
||||
src.parentNode.removeChild(div);
|
||||
src.onclick = oldonclick;
|
||||
src.onclick = oldOnclick;
|
||||
};
|
||||
}
|
||||
}
|
||||
};
|
||||
request.send();
|
||||
}
|
||||
</@s.script>
|
||||
</script>
|
||||
|
||||
<body>
|
||||
${debugHtml?no_esc}
|
||||
|
||||
@@ -23,21 +23,25 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<link rel="stylesheet" type="text/css" href="webconsole.css"/>
|
||||
<script src="webconsole.js"></script>
|
||||
<title>OGNL Console</title>
|
||||
<link rel="stylesheet" type="text/css" href="webconsole.css"/>
|
||||
<script src="webconsole.js"></script>
|
||||
<title>OGNL Console</title>
|
||||
</head>
|
||||
<body>
|
||||
<div id="shell">
|
||||
<div class="wc-results" id="wc-result">
|
||||
Welcome to the OGNL console!
|
||||
<br/>
|
||||
:->
|
||||
</div>
|
||||
<form onsubmit="return false" id="wc-form">
|
||||
<input type="hidden" name="debug" value="command"/>
|
||||
<input name="expression" onkeyup="keyEvent(event)" class="wc-command" id="wc-command" type="text"/>
|
||||
</form>
|
||||
<div class="wc-results" id="wc-result">
|
||||
Welcome to the OGNL console!
|
||||
<br/>
|
||||
:->
|
||||
</div>
|
||||
<form onsubmit="return false" id="wc-form">
|
||||
<input type="hidden" name="debug" value="command"/>
|
||||
<input name="expression" class="wc-command" id="wc-command" type="text"/>
|
||||
<script>
|
||||
const input = document.getElementById("wc-command")
|
||||
input.addEventListener("keyup", keyEvent);
|
||||
</script>
|
||||
</form>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
@@ -18,8 +18,6 @@
|
||||
*/
|
||||
package org.apache.struts2;
|
||||
|
||||
import org.apache.struts2.ActionProxyFactory;
|
||||
import org.apache.struts2.XWorkJUnit4TestCase;
|
||||
import org.apache.struts2.dispatcher.Dispatcher;
|
||||
import org.apache.struts2.util.StrutsTestCaseHelper;
|
||||
import org.apache.struts2.views.jsp.StrutsMockServletContext;
|
||||
|
||||
+31
-29
@@ -19,16 +19,17 @@
|
||||
package org.apache.struts2.interceptor;
|
||||
|
||||
import com.mockobjects.dynamic.Mock;
|
||||
import org.apache.struts2.action.Action;
|
||||
import org.apache.struts2.ActionContext;
|
||||
import org.apache.struts2.ActionInvocation;
|
||||
import org.apache.struts2.ActionProxy;
|
||||
import org.apache.struts2.StrutsException;
|
||||
import org.apache.struts2.XWorkTestCase;
|
||||
import org.apache.struts2.action.Action;
|
||||
import org.apache.struts2.config.entities.ActionConfig;
|
||||
import org.apache.struts2.config.entities.ExceptionMappingConfig;
|
||||
import org.apache.struts2.ognl.ThreadAllowlist;
|
||||
import org.apache.struts2.util.ValueStack;
|
||||
import org.apache.struts2.validator.ValidationException;
|
||||
import org.apache.struts2.StrutsException;
|
||||
|
||||
/**
|
||||
* Unit test for ExceptionMappingInterceptor.
|
||||
@@ -52,7 +53,7 @@ public class ExceptionMappingInterceptorTest extends XWorkTestCase {
|
||||
String result = interceptor.intercept(invocation);
|
||||
assertNotNull(stack.findValue("exception"));
|
||||
assertEquals(stack.findValue("exception"), exception);
|
||||
assertEquals(result, "spooky");
|
||||
assertEquals("spooky", result);
|
||||
ExceptionHolder holder = (ExceptionHolder) stack.getRoot().get(0); // is on top of the root
|
||||
assertNotNull(holder.getExceptionStack()); // to invoke the method for unit test
|
||||
}
|
||||
@@ -67,7 +68,7 @@ public class ExceptionMappingInterceptorTest extends XWorkTestCase {
|
||||
String result = interceptor.intercept(invocation);
|
||||
assertNotNull(stack.findValue("exception"));
|
||||
assertEquals(stack.findValue("exception"), exception);
|
||||
assertEquals(result, "throwable");
|
||||
assertEquals("throwable", result);
|
||||
}
|
||||
|
||||
public void testNoThrownException() throws Exception {
|
||||
@@ -77,7 +78,7 @@ public class ExceptionMappingInterceptorTest extends XWorkTestCase {
|
||||
mockInvocation.expectAndReturn("invoke", Action.SUCCESS);
|
||||
mockInvocation.matchAndReturn("getAction", action.proxy());
|
||||
String result = interceptor.intercept(invocation);
|
||||
assertEquals(result, Action.SUCCESS);
|
||||
assertEquals(Action.SUCCESS, result);
|
||||
assertNull(stack.findValue("exception"));
|
||||
}
|
||||
|
||||
@@ -106,7 +107,7 @@ public class ExceptionMappingInterceptorTest extends XWorkTestCase {
|
||||
mockInvocation.matchAndReturn("getAction", action.proxy());
|
||||
|
||||
try {
|
||||
interceptor.setLogEnabled(true);
|
||||
interceptor.setLogEnabled(true);
|
||||
interceptor.intercept(invocation);
|
||||
fail("Should not have reached this point.");
|
||||
} catch (Exception e) {
|
||||
@@ -123,8 +124,8 @@ public class ExceptionMappingInterceptorTest extends XWorkTestCase {
|
||||
mockInvocation.matchAndReturn("getAction", action.proxy());
|
||||
|
||||
try {
|
||||
interceptor.setLogEnabled(true);
|
||||
interceptor.setLogCategory("showcase.unhandled");
|
||||
interceptor.setLogEnabled(true);
|
||||
interceptor.setLogCategory("showcase.unhandled");
|
||||
interceptor.intercept(invocation);
|
||||
fail("Should not have reached this point.");
|
||||
} catch (Exception e) {
|
||||
@@ -141,9 +142,9 @@ public class ExceptionMappingInterceptorTest extends XWorkTestCase {
|
||||
mockInvocation.matchAndReturn("getAction", action.proxy());
|
||||
|
||||
try {
|
||||
interceptor.setLogEnabled(true);
|
||||
interceptor.setLogCategory("showcase.unhandled");
|
||||
interceptor.setLogLevel("fatal");
|
||||
interceptor.setLogEnabled(true);
|
||||
interceptor.setLogCategory("showcase.unhandled");
|
||||
interceptor.setLogLevel("fatal");
|
||||
interceptor.intercept(invocation);
|
||||
fail("Should not have reached this point.");
|
||||
} catch (Exception e) {
|
||||
@@ -164,9 +165,9 @@ public class ExceptionMappingInterceptorTest extends XWorkTestCase {
|
||||
mockInvocation.matchAndReturn("getAction", action.proxy());
|
||||
|
||||
try {
|
||||
interceptor.setLogEnabled(true);
|
||||
interceptor.setLogCategory("showcase.unhandled");
|
||||
interceptor.setLogLevel("error");
|
||||
interceptor.setLogEnabled(true);
|
||||
interceptor.setLogCategory("showcase.unhandled");
|
||||
interceptor.setLogLevel("error");
|
||||
interceptor.intercept(invocation);
|
||||
fail("Should not have reached this point.");
|
||||
} catch (Exception e) {
|
||||
@@ -183,9 +184,9 @@ public class ExceptionMappingInterceptorTest extends XWorkTestCase {
|
||||
mockInvocation.matchAndReturn("getAction", action.proxy());
|
||||
|
||||
try {
|
||||
interceptor.setLogEnabled(true);
|
||||
interceptor.setLogCategory("showcase.unhandled");
|
||||
interceptor.setLogLevel("warn");
|
||||
interceptor.setLogEnabled(true);
|
||||
interceptor.setLogCategory("showcase.unhandled");
|
||||
interceptor.setLogLevel("warn");
|
||||
interceptor.intercept(invocation);
|
||||
fail("Should not have reached this point.");
|
||||
} catch (Exception e) {
|
||||
@@ -202,9 +203,9 @@ public class ExceptionMappingInterceptorTest extends XWorkTestCase {
|
||||
mockInvocation.matchAndReturn("getAction", action.proxy());
|
||||
|
||||
try {
|
||||
interceptor.setLogEnabled(true);
|
||||
interceptor.setLogCategory("showcase.unhandled");
|
||||
interceptor.setLogLevel("info");
|
||||
interceptor.setLogEnabled(true);
|
||||
interceptor.setLogCategory("showcase.unhandled");
|
||||
interceptor.setLogLevel("info");
|
||||
interceptor.intercept(invocation);
|
||||
fail("Should not have reached this point.");
|
||||
} catch (Exception e) {
|
||||
@@ -221,9 +222,9 @@ public class ExceptionMappingInterceptorTest extends XWorkTestCase {
|
||||
mockInvocation.matchAndReturn("getAction", action.proxy());
|
||||
|
||||
try {
|
||||
interceptor.setLogEnabled(true);
|
||||
interceptor.setLogCategory("showcase.unhandled");
|
||||
interceptor.setLogLevel("debug");
|
||||
interceptor.setLogEnabled(true);
|
||||
interceptor.setLogCategory("showcase.unhandled");
|
||||
interceptor.setLogLevel("debug");
|
||||
interceptor.intercept(invocation);
|
||||
fail("Should not have reached this point.");
|
||||
} catch (Exception e) {
|
||||
@@ -240,9 +241,9 @@ public class ExceptionMappingInterceptorTest extends XWorkTestCase {
|
||||
mockInvocation.matchAndReturn("getAction", action.proxy());
|
||||
|
||||
try {
|
||||
interceptor.setLogEnabled(true);
|
||||
interceptor.setLogCategory("showcase.unhandled");
|
||||
interceptor.setLogLevel("trace");
|
||||
interceptor.setLogEnabled(true);
|
||||
interceptor.setLogCategory("showcase.unhandled");
|
||||
interceptor.setLogLevel("trace");
|
||||
interceptor.intercept(invocation);
|
||||
fail("Should not have reached this point.");
|
||||
} catch (Exception e) {
|
||||
@@ -259,12 +260,12 @@ public class ExceptionMappingInterceptorTest extends XWorkTestCase {
|
||||
mockInvocation.matchAndReturn("getAction", action.proxy());
|
||||
|
||||
try {
|
||||
interceptor.setLogEnabled(true);
|
||||
interceptor.setLogLevel("xxx");
|
||||
interceptor.setLogEnabled(true);
|
||||
interceptor.setLogLevel("xxx");
|
||||
interceptor.intercept(invocation);
|
||||
fail("Should not have reached this point.");
|
||||
} catch (IllegalArgumentException e) {
|
||||
// success
|
||||
// success
|
||||
}
|
||||
}
|
||||
|
||||
@@ -296,6 +297,7 @@ public class ExceptionMappingInterceptorTest extends XWorkTestCase {
|
||||
mockInvocation.expectAndReturn("getStack", stack);
|
||||
mockInvocation.expectAndReturn("getInvocationContext", ActionContext.of().bind());
|
||||
interceptor = new ExceptionMappingInterceptor();
|
||||
interceptor.setThreadAllowlist(new ThreadAllowlist());
|
||||
interceptor.init();
|
||||
}
|
||||
|
||||
|
||||
+569
@@ -0,0 +1,569 @@
|
||||
/*
|
||||
* 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.interceptor.debugging;
|
||||
|
||||
import org.apache.struts2.ActionContext;
|
||||
import org.apache.struts2.StrutsJUnit4InternalTestCase;
|
||||
import org.apache.struts2.TestAction;
|
||||
import org.apache.struts2.dispatcher.DispatcherConstants;
|
||||
import org.apache.struts2.dispatcher.HttpParameters;
|
||||
import org.apache.struts2.dispatcher.RequestMap;
|
||||
import org.apache.struts2.dispatcher.SessionMap;
|
||||
import org.apache.struts2.mock.MockActionInvocation;
|
||||
import org.apache.struts2.ognl.ThreadAllowlist;
|
||||
import org.apache.struts2.util.ValueStack;
|
||||
import org.assertj.core.api.InstanceOfAssertFactories;
|
||||
import org.assertj.core.util.Maps;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.springframework.mock.web.MockHttpServletRequest;
|
||||
import org.springframework.mock.web.MockHttpServletResponse;
|
||||
import org.springframework.mock.web.MockHttpSession;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import static org.assertj.core.api.AssertionsForClassTypes.assertThat;
|
||||
|
||||
public class DebuggingInterceptorTest extends StrutsJUnit4InternalTestCase {
|
||||
|
||||
private DebuggingInterceptor interceptor;
|
||||
private MockActionInvocation invocation;
|
||||
private MockHttpServletRequest request;
|
||||
private MockHttpServletResponse response;
|
||||
private ActionContext context;
|
||||
private TestAction action;
|
||||
|
||||
@Test
|
||||
public void noDevMode() throws Exception {
|
||||
interceptor.intercept(invocation);
|
||||
assertThat(invocation.getResultCode()).isEqualTo("mock");
|
||||
assertThat(response.getContentAsString()).isEmpty();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void debugXml() throws Exception {
|
||||
interceptor.setDevMode("true");
|
||||
context.withParameters(HttpParameters.create(Maps.newHashMap("debug", "xml")).build());
|
||||
|
||||
interceptor.intercept(invocation);
|
||||
|
||||
assertThat(response.getContentAsString()).isEqualToIgnoringWhitespace("""
|
||||
<debug>
|
||||
<parameters/>
|
||||
<context/>
|
||||
<request/>
|
||||
<session/>
|
||||
<valueStack>
|
||||
<value>
|
||||
<action>
|
||||
<actionErrors/>
|
||||
<actionMessages/>
|
||||
<class>class org.apache.struts2.TestAction</class>
|
||||
<fieldErrors/>
|
||||
<locale>
|
||||
<ISO3Country>USA</ISO3Country>
|
||||
<ISO3Language>eng</ISO3Language>
|
||||
<class>class java.util.Locale</class>
|
||||
<country>US</country>
|
||||
<displayCountry>United States</displayCountry>
|
||||
<displayLanguage>English</displayLanguage>
|
||||
<displayName>English (United States)</displayName>
|
||||
<displayScript></displayScript>
|
||||
<displayVariant></displayVariant>
|
||||
<extensionKeys/>
|
||||
<language>en</language>
|
||||
<script></script>
|
||||
<unicodeLocaleAttributes/>
|
||||
<unicodeLocaleKeys/>
|
||||
<variant></variant>
|
||||
</locale>
|
||||
<status>
|
||||
<class>class org.apache.struts2.SomeEnum</class>
|
||||
<declaringClass>class org.apache.struts2.SomeEnum</declaringClass>
|
||||
<displayName>completed</displayName>
|
||||
<name>COMPLETED</name>
|
||||
</status>
|
||||
<statusList>
|
||||
<value>
|
||||
<class>class org.apache.struts2.SomeEnum</class>
|
||||
<declaringClass>class org.apache.struts2.SomeEnum</declaringClass>
|
||||
<displayName>init</displayName>
|
||||
<name>INIT</name>
|
||||
</value>
|
||||
<value>
|
||||
<class>class org.apache.struts2.SomeEnum</class>
|
||||
<declaringClass>class org.apache.struts2.SomeEnum</declaringClass>
|
||||
<displayName>completed</displayName>
|
||||
<name>COMPLETED</name>
|
||||
</value>
|
||||
</statusList>
|
||||
<texts>
|
||||
<baseBundleName>org.apache.struts2.TestAction</baseBundleName>
|
||||
<class>class java.util.PropertyResourceBundle</class>
|
||||
<keys>
|
||||
<class>class sun.util.ResourceBundleEnumeration</class>
|
||||
</keys>
|
||||
<locale>
|
||||
<ISO3Country></ISO3Country>
|
||||
<ISO3Language></ISO3Language>
|
||||
<class>class java.util.Locale</class>
|
||||
<country></country>
|
||||
<displayCountry></displayCountry>
|
||||
<displayLanguage></displayLanguage>
|
||||
<displayName></displayName>
|
||||
<displayScript></displayScript>
|
||||
<displayVariant></displayVariant>
|
||||
<extensionKeys/>
|
||||
<language></language>
|
||||
<script></script>
|
||||
<unicodeLocaleAttributes/>
|
||||
<unicodeLocaleKeys/>
|
||||
<variant></variant>
|
||||
</locale>
|
||||
</texts>
|
||||
</action>
|
||||
<org.apache.struts2.util.OgnlValueStack.MAP_IDENTIFIER_KEY></org.apache.struts2.util.OgnlValueStack.MAP_IDENTIFIER_KEY>
|
||||
</value>
|
||||
<value>
|
||||
<class>class org.apache.struts2.text.DefaultTextProvider</class>
|
||||
</value>
|
||||
</valueStack>
|
||||
</debug>
|
||||
""");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void debugXmlWithConsole() throws Exception {
|
||||
interceptor.setDevMode("true");
|
||||
context.withParameters(HttpParameters.create(Maps.newHashMap("debug", "console")).build());
|
||||
interceptor.setEnableXmlWithConsole(true);
|
||||
|
||||
interceptor.intercept(invocation);
|
||||
|
||||
assertThat(response.getContentAsString()).isEqualToIgnoringWhitespace("""
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<script>
|
||||
var baseUrl = "/static";
|
||||
window.open(baseUrl+"/webconsole.html", 'OGNL Console','width=500,height=450,status=no,toolbar=no,menubar=no');
|
||||
</script>
|
||||
</head>
|
||||
<body>
|
||||
<pre>
|
||||
&lt;debug&gt;
|
||||
&lt;parameters/&gt;
|
||||
&lt;context/&gt;
|
||||
&lt;request/&gt;
|
||||
&lt;session/&gt;
|
||||
&lt;valueStack&gt;
|
||||
&lt;value&gt;
|
||||
&lt;action&gt;
|
||||
&lt;actionErrors/&gt;
|
||||
&lt;actionMessages/&gt;
|
||||
&lt;class&gt;class org.apache.struts2.TestAction&lt;/class&gt;
|
||||
&lt;fieldErrors/&gt;
|
||||
&lt;locale&gt;
|
||||
&lt;ISO3Country&gt;USA&lt;/ISO3Country&gt;
|
||||
&lt;ISO3Language&gt;eng&lt;/ISO3Language&gt;
|
||||
&lt;class&gt;class java.util.Locale&lt;/class&gt;
|
||||
&lt;country&gt;US&lt;/country&gt;
|
||||
&lt;displayCountry&gt;United States&lt;/displayCountry&gt;
|
||||
&lt;displayLanguage&gt;English&lt;/displayLanguage&gt;
|
||||
&lt;displayName&gt;English (United States)&lt;/displayName&gt;
|
||||
&lt;displayScript&gt;&lt;/displayScript&gt;
|
||||
&lt;displayVariant&gt;&lt;/displayVariant&gt;
|
||||
&lt;extensionKeys/&gt;
|
||||
&lt;language&gt;en&lt;/language&gt;
|
||||
&lt;script&gt;&lt;/script&gt;
|
||||
&lt;unicodeLocaleAttributes/&gt;
|
||||
&lt;unicodeLocaleKeys/&gt;
|
||||
&lt;variant&gt;&lt;/variant&gt;
|
||||
&lt;/locale&gt;
|
||||
&lt;status&gt;
|
||||
&lt;class&gt;class org.apache.struts2.SomeEnum&lt;/class&gt;
|
||||
&lt;declaringClass&gt;class org.apache.struts2.SomeEnum&lt;/declaringClass&gt;
|
||||
&lt;displayName&gt;completed&lt;/displayName&gt;
|
||||
&lt;name&gt;COMPLETED&lt;/name&gt;
|
||||
&lt;/status&gt;
|
||||
&lt;statusList&gt;
|
||||
&lt;value&gt;
|
||||
&lt;class&gt;class org.apache.struts2.SomeEnum&lt;/class&gt;
|
||||
&lt;declaringClass&gt;class org.apache.struts2.SomeEnum&lt;/declaringClass&gt;
|
||||
&lt;displayName&gt;init&lt;/displayName&gt;
|
||||
&lt;name&gt;INIT&lt;/name&gt;
|
||||
&lt;/value&gt;
|
||||
&lt;value&gt;
|
||||
&lt;class&gt;class org.apache.struts2.SomeEnum&lt;/class&gt;
|
||||
&lt;declaringClass&gt;class org.apache.struts2.SomeEnum&lt;/declaringClass&gt;
|
||||
&lt;displayName&gt;completed&lt;/displayName&gt;
|
||||
&lt;name&gt;COMPLETED&lt;/name&gt;
|
||||
&lt;/value&gt;
|
||||
&lt;/statusList&gt;
|
||||
&lt;texts&gt;
|
||||
&lt;baseBundleName&gt;org.apache.struts2.TestAction&lt;/baseBundleName&gt;
|
||||
&lt;class&gt;class java.util.PropertyResourceBundle&lt;/class&gt;
|
||||
&lt;keys&gt;
|
||||
&lt;class&gt;class sun.util.ResourceBundleEnumeration&lt;/class&gt;
|
||||
&lt;/keys&gt;
|
||||
&lt;locale&gt;
|
||||
&lt;ISO3Country&gt;&lt;/ISO3Country&gt;
|
||||
&lt;ISO3Language&gt;&lt;/ISO3Language&gt;
|
||||
&lt;class&gt;class java.util.Locale&lt;/class&gt;
|
||||
&lt;country&gt;&lt;/country&gt;
|
||||
&lt;displayCountry&gt;&lt;/displayCountry&gt;
|
||||
&lt;displayLanguage&gt;&lt;/displayLanguage&gt;
|
||||
&lt;displayName&gt;&lt;/displayName&gt;
|
||||
&lt;displayScript&gt;&lt;/displayScript&gt;
|
||||
&lt;displayVariant&gt;&lt;/displayVariant&gt;
|
||||
&lt;extensionKeys/&gt;
|
||||
&lt;language&gt;&lt;/language&gt;
|
||||
&lt;script&gt;&lt;/script&gt;
|
||||
&lt;unicodeLocaleAttributes/&gt;
|
||||
&lt;unicodeLocaleKeys/&gt;
|
||||
&lt;variant&gt;&lt;/variant&gt;
|
||||
&lt;/locale&gt;
|
||||
&lt;/texts&gt;
|
||||
&lt;/action&gt;
|
||||
&lt;org.apache.struts2.util.OgnlValueStack.MAP_IDENTIFIER_KEY&gt;&lt;/org.apache.struts2.util.OgnlValueStack.MAP_IDENTIFIER_KEY&gt;
|
||||
&lt;/value&gt;
|
||||
&lt;value&gt;
|
||||
&lt;class&gt;class org.apache.struts2.text.DefaultTextProvider&lt;/class&gt;
|
||||
&lt;/value&gt;
|
||||
&lt;/valueStack&gt;
|
||||
&lt;/debug&gt;
|
||||
</pre>
|
||||
</body>
|
||||
</html>
|
||||
""");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void debugConsole() throws Exception {
|
||||
interceptor.setDevMode("true");
|
||||
context.withParameters(HttpParameters.create(Maps.newHashMap("debug", "console")).build());
|
||||
|
||||
interceptor.intercept(invocation);
|
||||
|
||||
assertThat(response.getContentAsString()).isEqualToIgnoringWhitespace("""
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<script>
|
||||
var baseUrl = "/static";
|
||||
window.open(baseUrl+"/webconsole.html", 'OGNL Console','width=500,height=450,status=no,toolbar=no,menubar=no');
|
||||
</script>
|
||||
</head>
|
||||
<body>
|
||||
<pre>
|
||||
|
||||
</pre>
|
||||
</body>
|
||||
</html>
|
||||
""");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void debugCommand() throws Exception {
|
||||
interceptor.setDevMode("true");
|
||||
Map<String, Object> params = new HashMap<>() {{
|
||||
put("debug", "command");
|
||||
put("expression", "1+1");
|
||||
}};
|
||||
context.withParameters(HttpParameters.create(params).build());
|
||||
|
||||
interceptor.intercept(invocation);
|
||||
|
||||
assertThat(response.getContentAsString()).isEqualToIgnoringWhitespace("2");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void debugBrowser() throws Exception {
|
||||
interceptor.setDevMode("true");
|
||||
context.withParameters(HttpParameters.create(Maps.newHashMap("debug", "browser")).build());
|
||||
|
||||
interceptor.intercept(invocation);
|
||||
invocation.invoke();
|
||||
|
||||
assertThat(response.getContentAsString()).isEqualToIgnoringWhitespace("""
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<style>
|
||||
.debugTable {
|
||||
border-style: solid;
|
||||
border-width: 1px;
|
||||
}
|
||||
|
||||
.debugTable td {
|
||||
border-style: solid;
|
||||
border-width: 1px;
|
||||
}
|
||||
|
||||
.nameColumn {
|
||||
background-color:#CCDDFF;
|
||||
}
|
||||
|
||||
.valueColumn {
|
||||
background-color: #CCFFCC;
|
||||
}
|
||||
|
||||
.nullValue {
|
||||
background-color: #FF0000;
|
||||
}
|
||||
|
||||
.typeColumn {
|
||||
background-color: white;
|
||||
}
|
||||
|
||||
.emptyCollection {
|
||||
background-color: #EEEEEE;
|
||||
}
|
||||
</style>
|
||||
|
||||
<script>
|
||||
function expand(src, path) {
|
||||
let baseUrl = location.href;
|
||||
const i = baseUrl.indexOf('&object=');
|
||||
baseUrl = (i > 0 ? baseUrl.substring(0, i) : baseUrl) + "&object=" + path;
|
||||
if (baseUrl.indexOf("decorate") < 0) {
|
||||
baseUrl += "&decorate=false";
|
||||
}
|
||||
|
||||
const request = new XMLHttpRequest();
|
||||
request.open('GET', baseUrl, true);
|
||||
request.onreadystatechange = function() {
|
||||
if (this.readyState === 4) {
|
||||
if (this.status >= 200 && this.status < 400) {
|
||||
const div = document.createElement('div');
|
||||
console.log(this.responseText);
|
||||
div.innerHTML = this.responseText;
|
||||
src.parentNode.appendChild(div);
|
||||
|
||||
src.innerHTML = "Collapse";
|
||||
const oldOnclick = src.onclick;
|
||||
src.onclick = function() {
|
||||
src.innerHTML = "Expand";
|
||||
src.parentNode.removeChild(div);
|
||||
src.onclick = oldOnclick;
|
||||
};
|
||||
}
|
||||
}
|
||||
};
|
||||
request.send();
|
||||
}
|
||||
</script>
|
||||
|
||||
<body>
|
||||
<table class="debugTable">
|
||||
<tr>
|
||||
<td class="nameColumn">container</td>
|
||||
<td class="valueColumn">There is no read method for container</td>
|
||||
<td class="typeColumn">java.lang.String</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="nameColumn">foo</td>
|
||||
<td class="nullValue">null</td>
|
||||
<td class="nullValue">unknown</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="nameColumn">intList</td>
|
||||
<td class="nullValue">null</td>
|
||||
<td class="nullValue">unknown</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="nameColumn">locale</td>
|
||||
<td class="valueColumn">
|
||||
<a onclick="expand(this, 'action["locale"]')" href="javascript://nop/">Expand</a>
|
||||
</td>
|
||||
<td class="typeColumn">java.util.Locale</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="nameColumn">result</td>
|
||||
<td class="nullValue">null</td>
|
||||
<td class="nullValue">unknown</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="nameColumn">collection2</td>
|
||||
<td class="nullValue">null</td>
|
||||
<td class="nullValue">unknown</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="nameColumn">someBool</td>
|
||||
<td class="nullValue">null</td>
|
||||
<td class="nullValue">unknown</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="nameColumn">array</td>
|
||||
<td class="nullValue">null</td>
|
||||
<td class="nullValue">unknown</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="nameColumn">fooInt</td>
|
||||
<td class="nullValue">null</td>
|
||||
<td class="nullValue">unknown</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="nameColumn">id</td>
|
||||
<td class="nullValue">null</td>
|
||||
<td class="nullValue">unknown</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="nameColumn">map</td>
|
||||
<td class="nullValue">null</td>
|
||||
<td class="nullValue">unknown</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="nameColumn">actionErrors</td>
|
||||
<td class="emptyCollection">empty</td>
|
||||
<td class="typeColumn">java.util.LinkedList</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="nameColumn">objectArray</td>
|
||||
<td class="nullValue">null</td>
|
||||
<td class="nullValue">unknown</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="nameColumn">fieldErrors</td>
|
||||
<td class="emptyCollection">empty</td>
|
||||
<td class="typeColumn">java.util.LinkedHashMap</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="nameColumn">collection</td>
|
||||
<td class="nullValue">null</td>
|
||||
<td class="nullValue">unknown</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="nameColumn">floatNumber</td>
|
||||
<td class="nullValue">null</td>
|
||||
<td class="nullValue">unknown</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="nameColumn">list</td>
|
||||
<td class="nullValue">null</td>
|
||||
<td class="nullValue">unknown</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="nameColumn">enumList</td>
|
||||
<td class="nullValue">null</td>
|
||||
<td class="nullValue">unknown</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="nameColumn">actionMessages</td>
|
||||
<td class="emptyCollection">empty</td>
|
||||
<td class="typeColumn">java.util.LinkedList</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="nameColumn">statusList</td>
|
||||
<td class="valueColumn">
|
||||
<a onclick="expand(this, 'action["statusList"]')" href="javascript://nop/">Expand</a>
|
||||
</td>
|
||||
<td class="typeColumn">java.util.Arrays$ArrayList</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="nameColumn">texts</td>
|
||||
<td class="valueColumn">
|
||||
<a onclick="expand(this, 'action["texts"]')" href="javascript://nop/">Expand</a>
|
||||
</td>
|
||||
<td class="typeColumn">java.util.PropertyResourceBundle</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="nameColumn">list3</td>
|
||||
<td class="nullValue">null</td>
|
||||
<td class="nullValue">unknown</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="nameColumn">list2</td>
|
||||
<td class="nullValue">null</td>
|
||||
<td class="nullValue">unknown</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="nameColumn">user</td>
|
||||
<td class="nullValue">null</td>
|
||||
<td class="nullValue">unknown</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="nameColumn">status</td>
|
||||
<td class="valueColumn">
|
||||
<a onclick="expand(this, 'action["status"]')" href="javascript://nop/">Expand</a>
|
||||
</td>
|
||||
<td class="typeColumn">org.apache.struts2.SomeEnum</td>
|
||||
</tr>
|
||||
</table>
|
||||
</body>
|
||||
</html>
|
||||
""");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void allowlist() throws Exception {
|
||||
interceptor.setDevMode("true");
|
||||
context.withParameters(HttpParameters.create(Maps.newHashMap("debug", "browser")).build());
|
||||
|
||||
assertThat(container.getInstance(ThreadAllowlist.class))
|
||||
.extracting(ThreadAllowlist::getAllowlist).asInstanceOf(InstanceOfAssertFactories.SET)
|
||||
.isEmpty();
|
||||
|
||||
interceptor.intercept(invocation);
|
||||
invocation.invoke();
|
||||
|
||||
assertThat(container.getInstance(ThreadAllowlist.class))
|
||||
.extracting(ThreadAllowlist::getAllowlist).asInstanceOf(InstanceOfAssertFactories.SET)
|
||||
.contains(
|
||||
org.apache.struts2.interceptor.ValidationAware.class,
|
||||
org.apache.struts2.Validateable.class,
|
||||
org.apache.struts2.action.Action.class,
|
||||
org.apache.struts2.text.TextProvider.class,
|
||||
org.apache.struts2.ActionSupport.class,
|
||||
org.apache.struts2.locale.LocaleProvider.class,
|
||||
org.apache.struts2.TestAction.class
|
||||
);
|
||||
}
|
||||
|
||||
@Before
|
||||
public void before() {
|
||||
request = new MockHttpServletRequest();
|
||||
request.setSession(new MockHttpSession());
|
||||
response = new MockHttpServletResponse();
|
||||
|
||||
ValueStack valueStack = dispatcher.getValueStackFactory().createValueStack();
|
||||
|
||||
context = valueStack.getActionContext()
|
||||
.withServletContext(servletContext)
|
||||
.withServletRequest(request)
|
||||
.withServletResponse(response)
|
||||
.withSession(new SessionMap(request))
|
||||
.with(DispatcherConstants.REQUEST, new RequestMap(request));
|
||||
|
||||
interceptor = container.inject(DebuggingInterceptor.class);
|
||||
interceptor.init();
|
||||
|
||||
invocation = new MockActionInvocation();
|
||||
invocation.setResultCode("mock");
|
||||
invocation.setInvocationContext(context);
|
||||
action = new TestAction();
|
||||
invocation.setAction(action);
|
||||
invocation.setStack(valueStack);
|
||||
|
||||
valueStack.set("action", invocation.getAction());
|
||||
|
||||
context = context.withActionInvocation(invocation).bind();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -18,13 +18,12 @@
|
||||
*/
|
||||
package org.apache.struts2.views.jsp.ui;
|
||||
|
||||
import org.apache.struts2.config.ConfigurationException;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.apache.struts2.StrutsConstants;
|
||||
import org.apache.struts2.dispatcher.PrepareOperations;
|
||||
import org.apache.struts2.views.jsp.AbstractUITagTest;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Collections;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
@@ -59,7 +58,7 @@ public class DebugTagTest extends AbstractUITagTest {
|
||||
freshTag.setPageContext(pageContext);
|
||||
// DebugTag has no additional state, so it compares as equal with the default tag clear state as well.
|
||||
assertTrue("Tag state after doEndTag() under default tag clear state is inequal to new Tag with pageContext/parent set. " +
|
||||
"May indicate that clearTagStateForTagPoolingServers() calls are not working properly.",
|
||||
"May indicate that clearTagStateForTagPoolingServers() calls are not working properly.",
|
||||
strutsBodyTagsAreReflectionEqual(tag, freshTag));
|
||||
}
|
||||
|
||||
@@ -82,7 +81,7 @@ public class DebugTagTest extends AbstractUITagTest {
|
||||
freshTag.setPerformClearTagStateForTagPoolingServers(true);
|
||||
freshTag.setPageContext(pageContext);
|
||||
assertTrue("Tag state after doEndTag() and explicit tag state clearing is inequal to new Tag with pageContext/parent set. " +
|
||||
"May indicate that clearTagStateForTagPoolingServers() calls are not working properly.",
|
||||
"May indicate that clearTagStateForTagPoolingServers() calls are not working properly.",
|
||||
strutsBodyTagsAreReflectionEqual(tag, freshTag));
|
||||
}
|
||||
|
||||
@@ -98,7 +97,7 @@ public class DebugTagTest extends AbstractUITagTest {
|
||||
freshTag.setPageContext(pageContext);
|
||||
// DebugTag has no additional state, so it compares as equal with the default tag clear state as well.
|
||||
assertTrue("Tag state after doEndTag() under default tag clear state is inequal to new Tag with pageContext/parent set. " +
|
||||
"May indicate that clearTagStateForTagPoolingServers() calls are not working properly.",
|
||||
"May indicate that clearTagStateForTagPoolingServers() calls are not working properly.",
|
||||
strutsBodyTagsAreReflectionEqual(tag, freshTag));
|
||||
}
|
||||
|
||||
@@ -116,7 +115,7 @@ public class DebugTagTest extends AbstractUITagTest {
|
||||
freshTag.setPerformClearTagStateForTagPoolingServers(true);
|
||||
freshTag.setPageContext(pageContext);
|
||||
assertTrue("Tag state after doEndTag() and explicit tag state clearing is inequal to new Tag with pageContext/parent set. " +
|
||||
"May indicate that clearTagStateForTagPoolingServers() calls are not working properly.",
|
||||
"May indicate that clearTagStateForTagPoolingServers() calls are not working properly.",
|
||||
strutsBodyTagsAreReflectionEqual(tag, freshTag));
|
||||
}
|
||||
|
||||
@@ -135,7 +134,7 @@ public class DebugTagTest extends AbstractUITagTest {
|
||||
freshTag.setPageContext(pageContext);
|
||||
// DebugTag has no additional state, so it compares as equal with the default tag clear state as well.
|
||||
assertTrue("Tag state after doEndTag() under default tag clear state is inequal to new Tag with pageContext/parent set. " +
|
||||
"May indicate that clearTagStateForTagPoolingServers() calls are not working properly.",
|
||||
"May indicate that clearTagStateForTagPoolingServers() calls are not working properly.",
|
||||
strutsBodyTagsAreReflectionEqual(tag, freshTag));
|
||||
|
||||
PrepareOperations.clearDevModeOverride(); // Clear DevMode override. Avoid ThreadLocal side-effects if test thread re-used.
|
||||
@@ -158,7 +157,7 @@ public class DebugTagTest extends AbstractUITagTest {
|
||||
freshTag.setPerformClearTagStateForTagPoolingServers(true);
|
||||
freshTag.setPageContext(pageContext);
|
||||
assertTrue("Tag state after doEndTag() and explicit tag state clearing is inequal to new Tag with pageContext/parent set. " +
|
||||
"May indicate that clearTagStateForTagPoolingServers() calls are not working properly.",
|
||||
"May indicate that clearTagStateForTagPoolingServers() calls are not working properly.",
|
||||
strutsBodyTagsAreReflectionEqual(tag, freshTag));
|
||||
|
||||
PrepareOperations.clearDevModeOverride(); // Clear DevMode override. Avoid ThreadLocal side-effects if test thread re-used.
|
||||
@@ -177,7 +176,7 @@ public class DebugTagTest extends AbstractUITagTest {
|
||||
freshTag.setPageContext(pageContext);
|
||||
// DebugTag has no additional state, so it compares as equal with the default tag clear state as well.
|
||||
assertTrue("Tag state after doEndTag() under default tag clear state is inequal to new Tag with pageContext/parent set. " +
|
||||
"May indicate that clearTagStateForTagPoolingServers() calls are not working properly.",
|
||||
"May indicate that clearTagStateForTagPoolingServers() calls are not working properly.",
|
||||
strutsBodyTagsAreReflectionEqual(tag, freshTag));
|
||||
|
||||
PrepareOperations.clearDevModeOverride(); // Clear DevMode override. Avoid ThreadLocal side-effects if test thread re-used.
|
||||
@@ -198,16 +197,14 @@ public class DebugTagTest extends AbstractUITagTest {
|
||||
freshTag.setPerformClearTagStateForTagPoolingServers(true);
|
||||
freshTag.setPageContext(pageContext);
|
||||
assertTrue("Tag state after doEndTag() and explicit tag state clearing is inequal to new Tag with pageContext/parent set. " +
|
||||
"May indicate that clearTagStateForTagPoolingServers() calls are not working properly.",
|
||||
"May indicate that clearTagStateForTagPoolingServers() calls are not working properly.",
|
||||
strutsBodyTagsAreReflectionEqual(tag, freshTag));
|
||||
|
||||
PrepareOperations.clearDevModeOverride(); // Clear DevMode override. Avoid ThreadLocal side-effects if test thread re-used.
|
||||
}
|
||||
|
||||
private void setDevMode(final boolean devMode) {
|
||||
setStrutsConstant(new HashMap<String, String>() {{
|
||||
put(StrutsConstants.STRUTS_DEVMODE, Boolean.toString(devMode));
|
||||
}});
|
||||
setStrutsConstant(Collections.singletonMap(StrutsConstants.STRUTS_DEVMODE, Boolean.toString(devMode)));
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
Reference in New Issue
Block a user