mirror of
https://github.com/apache/struts.git
synced 2026-08-31 19:35:40 +00:00
Merge branch 'master' into WW-5381-method-accessor
This commit is contained in:
@@ -57,7 +57,7 @@ jobs:
|
||||
publish_results: true
|
||||
|
||||
- name: "Upload artifact"
|
||||
uses: actions/upload-artifact@a8a3f3ad30e3422c9c7b888a15615d19a852ae32 # 3.1.3
|
||||
uses: actions/upload-artifact@c7d193f32edcb7bfad88892161225aeda64e9392 # 4.0.0
|
||||
with:
|
||||
name: SARIF file
|
||||
path: results.sarif
|
||||
|
||||
@@ -72,7 +72,7 @@
|
||||
<s:script src="%{jqueryJs}"/>
|
||||
<s:url var="bootstrapJs" value='/js/bootstrap.min.js' encode='false' includeParams='none'/>
|
||||
<s:script src="%{bootstrapJs}"/>
|
||||
<s:script type="text/javascript">
|
||||
<s:script>
|
||||
$(function () {
|
||||
var alerts = $('ul.alert').wrap('<div />');
|
||||
alerts.prepend('<a class="close" data-dismiss="alert" href="#">×</a>');
|
||||
|
||||
@@ -72,7 +72,7 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<s:script type="text/javascript">
|
||||
<s:script>
|
||||
/********************************************************************
|
||||
* JS just used on this page.
|
||||
* Usually this would be placed in a JS file
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
#set ($stylebase = "https://struts.apache.org/css")
|
||||
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
|
||||
<!--
|
||||
<!--
|
||||
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
|
||||
@@ -16,7 +16,7 @@ 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.
|
||||
under the License.
|
||||
-->
|
||||
<html>
|
||||
<head>
|
||||
@@ -43,12 +43,12 @@ under the License.
|
||||
<script src='http://struts.apache.org/highlighter/js/$hscript' type='text/javascript'></script>
|
||||
#end
|
||||
|
||||
<script type="text/javascript">
|
||||
<script>
|
||||
SyntaxHighlighter.defaults['toolbar'] = false;
|
||||
SyntaxHighlighter.all();
|
||||
</script>
|
||||
#end
|
||||
<script type="text/javascript" language="javascript">
|
||||
<script>
|
||||
var hide = null;
|
||||
var show = null;
|
||||
var children = null;
|
||||
|
||||
@@ -22,13 +22,13 @@
|
||||
<head>
|
||||
<title>OSGi Console</title>
|
||||
|
||||
<link rel="stylesheet" type="text/css" href="<@s.url value="${parameters.staticContentPath}/css/shell.css" />" />
|
||||
<link rel="stylesheet" type="text/css" href="<@s.url value="${parameters.staticContentPath}/css/main.css" />" />
|
||||
<link rel="stylesheet" type="text/css" href="<@s.url value="${parameters.staticContentPath}/css/redmond/jquery-ui-1.12.1.redmond.css" />" />
|
||||
<@s.link rel="stylesheet" type="text/css" href="${base}${parameters.staticContentPath}/css/shell.css" />
|
||||
<@s.link rel="stylesheet" type="text/css" href="${base}${parameters.staticContentPath}/css/main.css" />
|
||||
<@s.link rel="stylesheet" type="text/css" href="${base}${parameters.staticContentPath}/css/redmond/jquery-ui-1.12.1.redmond.css" />
|
||||
|
||||
<script src="<@s.url value="${parameters.staticContentPath}/js/shell.js" />"></script>
|
||||
<script src="<@s.url value="${parameters.staticContentPath}/js/jquery-1.12.4.min.js" />"></script>
|
||||
<script src="<@s.url value="${parameters.staticContentPath}/js/jquery-ui-1.12.1.min.js" />"></script>
|
||||
<@s.script src="${base}${parameters.staticContentPath}/js/shell.js" />
|
||||
<@s.script src="${base}${parameters.staticContentPath}/js/jquery-1.12.4.min.js" />
|
||||
<@s.script src="${base}${parameters.staticContentPath}/js/jquery-ui-1.12.1.min.js" />
|
||||
</head>
|
||||
<body>
|
||||
<div class="menu">
|
||||
|
||||
@@ -22,17 +22,17 @@
|
||||
<head>
|
||||
<title>${bundle.symbolicName!}</title>
|
||||
|
||||
<link rel="stylesheet" type="text/css" href="<@s.url value="${parameters.staticContentPath}/css/main.css" />" />
|
||||
<link rel="stylesheet" type="text/css" href="<@s.url value="${parameters.staticContentPath}/css/redmond/jquery-ui-1.12.1.redmond.css" />" />
|
||||
<@s.link rel="stylesheet" type="text/css" href="${base}${parameters.staticContentPath}/css/main.css" />
|
||||
<@s.link rel="stylesheet" type="text/css" href="${base}${parameters.staticContentPath}/css/redmond/jquery-ui-1.12.1.redmond.css" />
|
||||
|
||||
<script src="<@s.url value="${parameters.staticContentPath}/js/jquery-1.12.4.min.js" />"></script>
|
||||
<script src="<@s.url value="${parameters.staticContentPath}/js/jquery-ui-1.12.1.min.js" />"></script>
|
||||
<@s.script src="${base}${parameters.staticContentPath}/js/jquery-1.12.4.min.js"/>
|
||||
<@s.script src="${base}${parameters.staticContentPath}/js/jquery-ui-1.12.1.min.js" />
|
||||
|
||||
<script type="text/javascript">
|
||||
<@s.script>
|
||||
$(function() {
|
||||
$("#tabs").tabs();
|
||||
});
|
||||
</script>
|
||||
</@s.script>
|
||||
|
||||
</head>
|
||||
<body>
|
||||
@@ -72,7 +72,7 @@
|
||||
<span class="ui-icon ui-icon-transferthick-e-w"></span>
|
||||
OSGi Shell
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<@s.actionerror />
|
||||
|
||||
@@ -22,11 +22,11 @@
|
||||
<head>
|
||||
<title>OSGi Bundles</title>
|
||||
|
||||
<link rel="stylesheet" type="text/css" href="<@s.url value="${parameters.staticContentPath}/css/main.css" />" />
|
||||
<link rel="stylesheet" type="text/css" href="<@s.url value="${parameters.staticContentPath}/css/redmond/jquery-ui-1.12.1.redmond.css" />" />
|
||||
<@s.link rel="stylesheet" type="text/css" href="${base}${parameters.staticContentPath}/css/main.css" />
|
||||
<@s.link rel="stylesheet" type="text/css" href="${base}${parameters.staticContentPath}/css/redmond/jquery-ui-1.12.1.redmond.css" />
|
||||
|
||||
<script src="<@s.url value="${parameters.staticContentPath}/js/jquery-1.12.4.min.js" />"></script>
|
||||
<script src="<@s.url value="${parameters.staticContentPath}/js/jquery-ui-1.12.1.min.js" />"></script>
|
||||
<@s.script src="${base}${parameters.staticContentPath}/js/jquery-1.12.4.min.js" />
|
||||
<@s.script src="${base}${parameters.staticContentPath}/js/jquery-ui-1.12.1.min.js" />
|
||||
</head>
|
||||
<body>
|
||||
|
||||
@@ -42,7 +42,7 @@
|
||||
<span class="ui-icon ui-icon-transferthick-e-w"></span>
|
||||
OSGi Shell
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<@s.actionerror />
|
||||
|
||||
@@ -33,5 +33,5 @@ package com.opensymphony.xwork2.interceptor;
|
||||
* @deprecated since Struts 6.2.0, use {@link org.apache.struts2.action.NoParameters}
|
||||
*/
|
||||
@Deprecated
|
||||
public interface NoParameters {
|
||||
public interface NoParameters extends org.apache.struts2.action.NoParameters {
|
||||
}
|
||||
|
||||
+3
-1
@@ -26,7 +26,6 @@ import org.apache.logging.log4j.Logger;
|
||||
import org.apache.struts2.dispatcher.HttpParameters;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.HashSet;
|
||||
import java.util.Map;
|
||||
import java.util.TreeMap;
|
||||
|
||||
@@ -98,7 +97,10 @@ import java.util.TreeMap;
|
||||
* </pre>
|
||||
*
|
||||
* @author Gabe
|
||||
*
|
||||
* @deprecated since 6.4.0, use {@link org.apache.struts2.interceptor.parameter.ParametersInterceptor}.
|
||||
*/
|
||||
@Deprecated
|
||||
public class ParameterFilterInterceptor extends AbstractInterceptor {
|
||||
|
||||
private static final Logger LOG = LogManager.getLogger(ParameterFilterInterceptor.class);
|
||||
|
||||
@@ -19,19 +19,8 @@
|
||||
package com.opensymphony.xwork2.interceptor;
|
||||
|
||||
/**
|
||||
* This interface is implemented by actions that want to declare acceptable parameters. Works in conjunction with {@link
|
||||
* ParametersInterceptor}. For example, actions may want to create a white list of parameters they will accept or a
|
||||
* blacklist of parameters they will reject to prevent clients from setting other unexpected (and possibly dangerous)
|
||||
* parameters.
|
||||
* @deprecated since 6.4.0, use {@link org.apache.struts2.action.ParameterNameAware}.
|
||||
*/
|
||||
public interface ParameterNameAware {
|
||||
|
||||
/**
|
||||
* Tests if the the action will accept the parameter with the given name.
|
||||
*
|
||||
* @param parameterName the parameter name
|
||||
* @return <tt>true</tt> if accepted, <tt>false</tt> otherwise
|
||||
*/
|
||||
boolean acceptableParameterName(String parameterName);
|
||||
|
||||
@Deprecated
|
||||
public interface ParameterNameAware extends org.apache.struts2.action.ParameterNameAware {
|
||||
}
|
||||
|
||||
@@ -19,19 +19,8 @@
|
||||
package com.opensymphony.xwork2.interceptor;
|
||||
|
||||
/**
|
||||
* This interface is implemented by actions that want to declare acceptable parameter value. Works in conjunction with {@link
|
||||
* ParametersInterceptor}. For example, actions may want to create a white list of parameter values they will accept or a
|
||||
* blacklist of parameter values they will reject to prevent clients from setting other unexpected (and possibly dangerous)
|
||||
* parameter values.
|
||||
* @deprecated since 6.4.0, use {@link org.apache.struts2.action.ParameterValueAware}.
|
||||
*/
|
||||
public interface ParameterValueAware {
|
||||
|
||||
/**
|
||||
* Tests if the the action will accept the parameter with the given value.
|
||||
*
|
||||
* @param parameterValue the parameter value
|
||||
* @return <tt>true</tt> if accepted, <tt>false</tt> otherwise
|
||||
*/
|
||||
boolean acceptableParameterValue(String parameterValue);
|
||||
|
||||
@Deprecated
|
||||
public interface ParameterValueAware extends org.apache.struts2.action.ParameterValueAware {
|
||||
}
|
||||
|
||||
@@ -18,537 +18,8 @@
|
||||
*/
|
||||
package com.opensymphony.xwork2.interceptor;
|
||||
|
||||
import com.opensymphony.xwork2.ActionContext;
|
||||
import com.opensymphony.xwork2.ActionInvocation;
|
||||
import com.opensymphony.xwork2.TextProvider;
|
||||
import com.opensymphony.xwork2.inject.Inject;
|
||||
import com.opensymphony.xwork2.security.AcceptedPatternsChecker;
|
||||
import com.opensymphony.xwork2.security.ExcludedPatternsChecker;
|
||||
import com.opensymphony.xwork2.util.ClearableValueStack;
|
||||
import com.opensymphony.xwork2.util.MemberAccessValueStack;
|
||||
import com.opensymphony.xwork2.util.TextParseUtil;
|
||||
import com.opensymphony.xwork2.util.ValueStack;
|
||||
import com.opensymphony.xwork2.util.ValueStackFactory;
|
||||
import com.opensymphony.xwork2.util.reflection.ReflectionContextState;
|
||||
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.dispatcher.HttpParameters;
|
||||
import org.apache.struts2.dispatcher.Parameter;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.Comparator;
|
||||
import java.util.HashSet;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.TreeMap;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
/**
|
||||
* This interceptor sets all parameters on the value stack.
|
||||
* @deprecated since 6.4.0, use {@link org.apache.struts2.interceptor.parameter.ParametersInterceptor}.
|
||||
*/
|
||||
public class ParametersInterceptor extends MethodFilterInterceptor {
|
||||
|
||||
private static final Logger LOG = LogManager.getLogger(ParametersInterceptor.class);
|
||||
|
||||
protected static final int PARAM_NAME_MAX_LENGTH = 100;
|
||||
|
||||
private static final Pattern DMI_IGNORED_PATTERN = Pattern.compile("^(action|method):.*", Pattern.CASE_INSENSITIVE);
|
||||
|
||||
private int paramNameMaxLength = PARAM_NAME_MAX_LENGTH;
|
||||
private boolean devMode = false;
|
||||
private boolean dmiEnabled = false;
|
||||
|
||||
protected boolean ordered = false;
|
||||
|
||||
private ValueStackFactory valueStackFactory;
|
||||
private ExcludedPatternsChecker excludedPatterns;
|
||||
private AcceptedPatternsChecker acceptedPatterns;
|
||||
private Set<Pattern> excludedValuePatterns = null;
|
||||
private Set<Pattern> acceptedValuePatterns = null;
|
||||
|
||||
@Inject
|
||||
public void setValueStackFactory(ValueStackFactory valueStackFactory) {
|
||||
this.valueStackFactory = valueStackFactory;
|
||||
}
|
||||
|
||||
@Inject(StrutsConstants.STRUTS_DEVMODE)
|
||||
public void setDevMode(String mode) {
|
||||
this.devMode = BooleanUtils.toBoolean(mode);
|
||||
}
|
||||
|
||||
@Inject
|
||||
public void setExcludedPatterns(ExcludedPatternsChecker excludedPatterns) {
|
||||
this.excludedPatterns = excludedPatterns;
|
||||
}
|
||||
|
||||
@Inject
|
||||
public void setAcceptedPatterns(AcceptedPatternsChecker acceptedPatterns) {
|
||||
this.acceptedPatterns = acceptedPatterns;
|
||||
}
|
||||
|
||||
@Inject(value = StrutsConstants.STRUTS_ENABLE_DYNAMIC_METHOD_INVOCATION, required = false)
|
||||
protected void setDynamicMethodInvocation(String dmiEnabled) {
|
||||
this.dmiEnabled = Boolean.parseBoolean(dmiEnabled);
|
||||
}
|
||||
|
||||
/**
|
||||
* If the param name exceeds the configured maximum length it will not be
|
||||
* accepted.
|
||||
*
|
||||
* @param paramNameMaxLength Maximum length of param names
|
||||
*/
|
||||
public void setParamNameMaxLength(int paramNameMaxLength) {
|
||||
this.paramNameMaxLength = paramNameMaxLength;
|
||||
}
|
||||
|
||||
static private int countOGNLCharacters(String s) {
|
||||
int count = 0;
|
||||
for (int i = s.length() - 1; i >= 0; i--) {
|
||||
char c = s.charAt(i);
|
||||
if (c == '.' || c == '[') count++;
|
||||
}
|
||||
return count;
|
||||
}
|
||||
|
||||
/**
|
||||
* Compares based on number of '.' and '[' characters (fewer is higher)
|
||||
*/
|
||||
static final Comparator<String> rbCollator = (s1, s2) -> {
|
||||
int l1 = countOGNLCharacters(s1);
|
||||
int l2 = countOGNLCharacters(s2);
|
||||
return l1 < l2 ? -1 : (l2 < l1 ? 1 : s1.compareTo(s2));
|
||||
};
|
||||
|
||||
@Override
|
||||
public String doIntercept(ActionInvocation invocation) throws Exception {
|
||||
Object action = invocation.getAction();
|
||||
if (!(action instanceof NoParameters)) {
|
||||
ActionContext ac = invocation.getInvocationContext();
|
||||
HttpParameters parameters = retrieveParameters(ac);
|
||||
|
||||
if (LOG.isDebugEnabled()) {
|
||||
LOG.debug("Setting params {}", getParameterLogMap(parameters));
|
||||
}
|
||||
|
||||
if (parameters != null) {
|
||||
Map<String, Object> contextMap = ac.getContextMap();
|
||||
try {
|
||||
ReflectionContextState.setCreatingNullObjects(contextMap, true);
|
||||
ReflectionContextState.setDenyMethodExecution(contextMap, true);
|
||||
ReflectionContextState.setReportingConversionErrors(contextMap, true);
|
||||
|
||||
ValueStack stack = ac.getValueStack();
|
||||
setParameters(action, stack, parameters);
|
||||
} finally {
|
||||
ReflectionContextState.setCreatingNullObjects(contextMap, false);
|
||||
ReflectionContextState.setDenyMethodExecution(contextMap, false);
|
||||
ReflectionContextState.setReportingConversionErrors(contextMap, false);
|
||||
}
|
||||
}
|
||||
}
|
||||
return invocation.invoke();
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the parameter map to apply from wherever appropriate
|
||||
*
|
||||
* @param ac The action context
|
||||
* @return The parameter map to apply
|
||||
*/
|
||||
protected HttpParameters retrieveParameters(ActionContext ac) {
|
||||
return ac.getParameters();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Adds the parameters into context's ParameterMap
|
||||
*
|
||||
* @param ac The action context
|
||||
* @param newParams The parameter map to apply
|
||||
* <p>
|
||||
* In this class this is a no-op, since the parameters were fetched from the same location.
|
||||
* In subclasses both retrieveParameters() and addParametersToContext() should be overridden.
|
||||
* </p>
|
||||
*/
|
||||
protected void addParametersToContext(ActionContext ac, Map<String, ?> newParams) {
|
||||
}
|
||||
|
||||
protected void setParameters(final Object action, ValueStack stack, HttpParameters parameters) {
|
||||
HttpParameters params;
|
||||
Map<String, Parameter> acceptableParameters;
|
||||
if (ordered) {
|
||||
params = HttpParameters.create().withComparator(getOrderedComparator()).withParent(parameters).build();
|
||||
acceptableParameters = new TreeMap<>(getOrderedComparator());
|
||||
} else {
|
||||
params = HttpParameters.create().withParent(parameters).build();
|
||||
acceptableParameters = new TreeMap<>();
|
||||
}
|
||||
|
||||
for (Map.Entry<String, Parameter> entry : params.entrySet()) {
|
||||
String parameterName = entry.getKey();
|
||||
boolean isAcceptableParameter = isAcceptableParameter(parameterName, action);
|
||||
isAcceptableParameter &= isAcceptableParameterValue(entry.getValue(), action);
|
||||
|
||||
if (isAcceptableParameter) {
|
||||
acceptableParameters.put(parameterName, entry.getValue());
|
||||
}
|
||||
}
|
||||
|
||||
ValueStack newStack = valueStackFactory.createValueStack(stack);
|
||||
boolean clearableStack = newStack instanceof ClearableValueStack;
|
||||
if (clearableStack) {
|
||||
//if the stack's context can be cleared, do that to prevent OGNL
|
||||
//from having access to objects in the stack, see XW-641
|
||||
((ClearableValueStack) newStack).clearContextValues();
|
||||
Map<String, Object> context = newStack.getContext();
|
||||
ReflectionContextState.setCreatingNullObjects(context, true);
|
||||
ReflectionContextState.setDenyMethodExecution(context, true);
|
||||
ReflectionContextState.setReportingConversionErrors(context, true);
|
||||
|
||||
//keep locale from original context
|
||||
newStack.getActionContext().withLocale(stack.getActionContext().getLocale()).withValueStack(stack);
|
||||
}
|
||||
|
||||
boolean memberAccessStack = newStack instanceof MemberAccessValueStack;
|
||||
if (memberAccessStack) {
|
||||
//block or allow access to properties
|
||||
//see WW-2761 for more details
|
||||
MemberAccessValueStack accessValueStack = (MemberAccessValueStack) newStack;
|
||||
accessValueStack.useAcceptProperties(acceptedPatterns.getAcceptedPatterns());
|
||||
accessValueStack.useExcludeProperties(excludedPatterns.getExcludedPatterns());
|
||||
}
|
||||
|
||||
for (Map.Entry<String, Parameter> entry : acceptableParameters.entrySet()) {
|
||||
String name = entry.getKey();
|
||||
Parameter value = entry.getValue();
|
||||
try {
|
||||
newStack.setParameter(name, value.getObject());
|
||||
} catch (RuntimeException e) {
|
||||
if (devMode) {
|
||||
notifyDeveloperParameterException(action, name, e.getMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (clearableStack) {
|
||||
stack.getActionContext().withConversionErrors(newStack.getActionContext().getConversionErrors());
|
||||
}
|
||||
|
||||
addParametersToContext(ActionContext.getContext(), acceptableParameters);
|
||||
}
|
||||
|
||||
protected void notifyDeveloperParameterException(Object action, String property, String message) {
|
||||
String developerNotification = "Unexpected Exception caught setting '" + property + "' on '" + action.getClass() + ": " + message;
|
||||
if (action instanceof TextProvider) {
|
||||
TextProvider tp = (TextProvider) action;
|
||||
developerNotification = tp.getText("devmode.notification",
|
||||
"Developer Notification:\n{0}",
|
||||
new String[]{developerNotification}
|
||||
);
|
||||
}
|
||||
|
||||
LOG.error(developerNotification);
|
||||
|
||||
if (action instanceof ValidationAware) {
|
||||
// see https://issues.apache.org/jira/browse/WW-4066
|
||||
Collection<String> messages = ((ValidationAware) action).getActionMessages();
|
||||
messages.add(message);
|
||||
((ValidationAware) action).setActionMessages(messages);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if name of parameter can be accepted or thrown away
|
||||
*
|
||||
* @param name parameter name
|
||||
* @param action current action
|
||||
* @return true if parameter is accepted
|
||||
*/
|
||||
protected boolean isAcceptableParameter(String name, Object action) {
|
||||
ParameterNameAware parameterNameAware = (action instanceof ParameterNameAware) ? (ParameterNameAware) action : null;
|
||||
return acceptableName(name) && (parameterNameAware == null || parameterNameAware.acceptableParameterName(name));
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if parameter value can be accepted or thrown away
|
||||
*
|
||||
* @param param the parameter
|
||||
* @param action current action
|
||||
* @return true if parameter is accepted
|
||||
*/
|
||||
protected boolean isAcceptableParameterValue(Parameter param, Object action) {
|
||||
ParameterValueAware parameterValueAware = (action instanceof ParameterValueAware) ? (ParameterValueAware) action : null;
|
||||
boolean acceptableParamValue = (parameterValueAware == null || parameterValueAware.acceptableParameterValue(param.getValue()));
|
||||
if (hasParamValuesToExclude() || hasParamValuesToAccept()) {
|
||||
// Additional validations to process
|
||||
acceptableParamValue &= acceptableValue(param.getName(), param.getValue());
|
||||
}
|
||||
return acceptableParamValue;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets an instance of the comparator to use for the ordered sorting. Override this
|
||||
* method to customize the ordering of the parameters as they are set to the
|
||||
* action.
|
||||
*
|
||||
* @return A comparator to sort the parameters
|
||||
*/
|
||||
protected Comparator<String> getOrderedComparator() {
|
||||
return rbCollator;
|
||||
}
|
||||
|
||||
protected String getParameterLogMap(HttpParameters parameters) {
|
||||
if (parameters == null) {
|
||||
return "NONE";
|
||||
}
|
||||
|
||||
StringBuilder logEntry = new StringBuilder();
|
||||
for (Map.Entry<String, Parameter> entry : parameters.entrySet()) {
|
||||
logEntry.append(entry.getKey());
|
||||
logEntry.append(" => ");
|
||||
logEntry.append(entry.getValue().getValue());
|
||||
logEntry.append(" ");
|
||||
}
|
||||
|
||||
return logEntry.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* Validates the name passed is:
|
||||
* * Within the max length of a parameter name
|
||||
* * Is not excluded
|
||||
* * Is accepted
|
||||
*
|
||||
* @param name - Name to check
|
||||
* @return true if accepted
|
||||
*/
|
||||
protected boolean acceptableName(String name) {
|
||||
if (isIgnoredDMI(name)) {
|
||||
LOG.trace("DMI is enabled, ignoring DMI method: {}", name);
|
||||
return false;
|
||||
}
|
||||
boolean accepted = isWithinLengthLimit(name) && !isExcluded(name) && isAccepted(name);
|
||||
if (devMode && accepted) { // notify only when in devMode
|
||||
LOG.debug("Parameter [{}] was accepted and will be appended to action!", name);
|
||||
}
|
||||
return accepted;
|
||||
}
|
||||
|
||||
private boolean isIgnoredDMI(String name) {
|
||||
if (dmiEnabled) {
|
||||
return DMI_IGNORED_PATTERN.matcher(name).matches();
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Validates:
|
||||
* * Value is null/blank
|
||||
* * Value is not excluded
|
||||
* * Value is accepted
|
||||
*
|
||||
* @param name - Param name (for logging)
|
||||
* @param value - value to check
|
||||
* @return true if accepted
|
||||
*/
|
||||
protected boolean acceptableValue(String name, String value) {
|
||||
boolean accepted = (value == null || value.isEmpty() || (!isParamValueExcluded(value) && isParamValueAccepted(value)));
|
||||
if (!accepted) {
|
||||
String message = "Value [{}] of parameter [{}] was not accepted and will be dropped!";
|
||||
if (devMode) {
|
||||
LOG.warn(message, value, name);
|
||||
} else {
|
||||
LOG.debug(message, value, name);
|
||||
}
|
||||
}
|
||||
return accepted;
|
||||
}
|
||||
|
||||
protected boolean isWithinLengthLimit(String name) {
|
||||
boolean matchLength = name.length() <= paramNameMaxLength;
|
||||
if (!matchLength) {
|
||||
if (devMode) { // warn only when in devMode
|
||||
LOG.warn("Parameter [{}] is too long, allowed length is [{}]. Use Interceptor Parameter Overriding " +
|
||||
"to override the limit, see more at\n" +
|
||||
"https://struts.apache.org/core-developers/interceptors.html#interceptor-parameter-overriding",
|
||||
name, paramNameMaxLength);
|
||||
} else {
|
||||
LOG.warn("Parameter [{}] is too long, allowed length is [{}]", name, paramNameMaxLength);
|
||||
}
|
||||
}
|
||||
return matchLength;
|
||||
}
|
||||
|
||||
protected boolean isAccepted(String paramName) {
|
||||
AcceptedPatternsChecker.IsAccepted result = acceptedPatterns.isAccepted(paramName);
|
||||
if (result.isAccepted()) {
|
||||
return true;
|
||||
} else if (devMode) { // warn only when in devMode
|
||||
LOG.warn("Parameter [{}] didn't match accepted pattern [{}]! See Accepted / Excluded patterns at\n" +
|
||||
"https://struts.apache.org/security/#accepted--excluded-patterns",
|
||||
paramName, result.getAcceptedPattern());
|
||||
} else {
|
||||
LOG.debug("Parameter [{}] didn't match accepted pattern [{}]!", paramName, result.getAcceptedPattern());
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
protected boolean isExcluded(String paramName) {
|
||||
ExcludedPatternsChecker.IsExcluded result = excludedPatterns.isExcluded(paramName);
|
||||
if (result.isExcluded()) {
|
||||
if (devMode) { // warn only when in devMode
|
||||
LOG.warn("Parameter [{}] matches excluded pattern [{}]! See Accepted / Excluded patterns at\n" +
|
||||
"https://struts.apache.org/security/#accepted--excluded-patterns",
|
||||
paramName, result.getExcludedPattern());
|
||||
} else {
|
||||
LOG.debug("Parameter [{}] matches excluded pattern [{}]!", paramName, result.getExcludedPattern());
|
||||
}
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
protected boolean isParamValueExcluded(String value) {
|
||||
if (!hasParamValuesToExclude()) {
|
||||
LOG.debug("'excludedValuePatterns' not defined so anything is allowed");
|
||||
return false;
|
||||
}
|
||||
for (Pattern excludedValuePattern : excludedValuePatterns) {
|
||||
if (excludedValuePattern.matcher(value).matches()) {
|
||||
if (devMode) {
|
||||
LOG.warn("Parameter value [{}] matches excluded pattern [{}]! See Accepting/Excluding parameter values at\n" +
|
||||
"https://struts.apache.org/core-developers/parameters-interceptor#excluding-parameter-values",
|
||||
value, excludedValuePatterns);
|
||||
} else {
|
||||
LOG.debug("Parameter value [{}] matches excluded pattern [{}]", value, excludedValuePattern);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
protected boolean isParamValueAccepted(String value) {
|
||||
if (!hasParamValuesToAccept()) {
|
||||
LOG.debug("'acceptedValuePatterns' not defined so anything is allowed");
|
||||
return true;
|
||||
}
|
||||
for (Pattern acceptedValuePattern : acceptedValuePatterns) {
|
||||
if (acceptedValuePattern.matcher(value).matches()) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
if (devMode) {
|
||||
LOG.warn("Parameter value [{}] didn't match accepted pattern [{}]! See Accepting/Excluding parameter values at\n" +
|
||||
"https://struts.apache.org/core-developers/parameters-interceptor#excluding-parameter-values",
|
||||
value, acceptedValuePatterns);
|
||||
} else {
|
||||
LOG.debug("Parameter value [{}] was not accepted!", value);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private boolean hasParamValuesToExclude() {
|
||||
return excludedValuePatterns != null && excludedValuePatterns.size() > 0;
|
||||
}
|
||||
|
||||
private boolean hasParamValuesToAccept() {
|
||||
return acceptedValuePatterns != null && acceptedValuePatterns.size() > 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Whether to order the parameters or not
|
||||
*
|
||||
* @return True to order
|
||||
*/
|
||||
public boolean isOrdered() {
|
||||
return ordered;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set whether to order the parameters by object depth or not
|
||||
*
|
||||
* @param ordered True to order them
|
||||
*/
|
||||
public void setOrdered(boolean ordered) {
|
||||
this.ordered = ordered;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets a comma-delimited list of regular expressions to match
|
||||
* parameters that are allowed in the parameter map (aka whitelist).
|
||||
* <p>
|
||||
* Don't change the default unless you know what you are doing in terms
|
||||
* of security implications.
|
||||
* </p>
|
||||
*
|
||||
* @param commaDelim A comma-delimited list of regular expressions
|
||||
*/
|
||||
public void setAcceptParamNames(String commaDelim) {
|
||||
acceptedPatterns.setAcceptedPatterns(commaDelim);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets a comma-delimited list of regular expressions to match
|
||||
* parameters that should be removed from the parameter map.
|
||||
*
|
||||
* @param commaDelim A comma-delimited list of regular expressions
|
||||
*/
|
||||
public void setExcludeParams(String commaDelim) {
|
||||
excludedPatterns.setExcludedPatterns(commaDelim);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets a comma-delimited list of regular expressions to match
|
||||
* values of parameters that should be accepted and included in the parameter map.
|
||||
*
|
||||
* @param commaDelimitedPatterns A comma-delimited set of regular expressions
|
||||
*/
|
||||
public void setAcceptedValuePatterns(String commaDelimitedPatterns) {
|
||||
Set<String> patterns = TextParseUtil.commaDelimitedStringToSet(commaDelimitedPatterns);
|
||||
if (acceptedValuePatterns == null) {
|
||||
// Limit unwanted log entries (for 1st call, acceptedValuePatterns null)
|
||||
LOG.debug("Sets accepted value patterns to [{}], note this may impact the safety of your application!", patterns);
|
||||
} else {
|
||||
LOG.warn("Replacing accepted patterns [{}] with [{}], be aware that this may impact safety of your application!",
|
||||
acceptedValuePatterns, patterns);
|
||||
}
|
||||
acceptedValuePatterns = new HashSet<>(patterns.size());
|
||||
try {
|
||||
for (String pattern : patterns) {
|
||||
acceptedValuePatterns.add(Pattern.compile(pattern, Pattern.CASE_INSENSITIVE));
|
||||
}
|
||||
} finally {
|
||||
acceptedValuePatterns = Collections.unmodifiableSet(acceptedValuePatterns);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets a comma-delimited list of regular expressions to match
|
||||
* values of parameters that should be removed from the parameter map.
|
||||
*
|
||||
* @param commaDelimitedPatterns A comma-delimited set of regular expressions
|
||||
*/
|
||||
public void setExcludedValuePatterns(String commaDelimitedPatterns) {
|
||||
Set<String> patterns = TextParseUtil.commaDelimitedStringToSet(commaDelimitedPatterns);
|
||||
if (excludedValuePatterns == null) {
|
||||
// Limit unwanted log entries (for 1st call, excludedValuePatterns null)
|
||||
LOG.debug("Setting excluded value patterns to [{}]", patterns);
|
||||
} else {
|
||||
LOG.warn("Replacing excluded value patterns [{}] with [{}], be aware that this may impact safety of your application!",
|
||||
excludedValuePatterns, patterns);
|
||||
}
|
||||
excludedValuePatterns = new HashSet<>(patterns.size());
|
||||
try {
|
||||
for (String pattern : patterns) {
|
||||
excludedValuePatterns.add(Pattern.compile(pattern, Pattern.CASE_INSENSITIVE));
|
||||
}
|
||||
} finally {
|
||||
excludedValuePatterns = Collections.unmodifiableSet(excludedValuePatterns);
|
||||
}
|
||||
}
|
||||
public class ParametersInterceptor extends org.apache.struts2.interceptor.parameter.ParametersInterceptor {
|
||||
}
|
||||
|
||||
@@ -60,17 +60,18 @@ public class SecurityMemberAccess implements MemberAccess {
|
||||
private static final Logger LOG = LogManager.getLogger(SecurityMemberAccess.class);
|
||||
|
||||
private static final Set<String> ALLOWLIST_REQUIRED_PACKAGES = unmodifiableSet(new HashSet<>(Arrays.asList(
|
||||
"com.opensymphony.xwork2.validator.validators",
|
||||
"org.apache.struts2.components",
|
||||
"org.apache.struts2.views.jsp",
|
||||
"com.opensymphony.xwork2.validator.validators"
|
||||
"org.apache.struts2.views.jsp"
|
||||
)));
|
||||
|
||||
private static final Set<Class<?>> ALLOWLIST_REQUIRED_CLASSES = unmodifiableSet(new HashSet<>(Arrays.asList(
|
||||
java.lang.Enum.class,
|
||||
java.lang.String.class,
|
||||
java.util.Date.class,
|
||||
java.util.HashMap.class,
|
||||
java.util.Map.class,
|
||||
java.util.Map.Entry.class,
|
||||
java.util.HashMap.class
|
||||
java.util.Map.Entry.class
|
||||
)));
|
||||
|
||||
private final ProviderAllowlist providerAllowlist;
|
||||
|
||||
@@ -23,7 +23,6 @@ package org.apache.struts2.action;
|
||||
* them automatically. This may be useful if one is using the action tag and want to supply
|
||||
* the parameters to the action manually using the param tag. It may also be useful if one for
|
||||
* security reasons wants to make sure that parameters cannot be set by malicious users.
|
||||
*
|
||||
*/
|
||||
public interface NoParameters extends com.opensymphony.xwork2.interceptor.NoParameters {
|
||||
public interface NoParameters {
|
||||
}
|
||||
|
||||
@@ -0,0 +1,39 @@
|
||||
/*
|
||||
* 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.action;
|
||||
|
||||
import org.apache.struts2.interceptor.parameter.ParametersInterceptor;
|
||||
|
||||
/**
|
||||
* This interface is implemented by actions that want to declare acceptable parameters. Works in conjunction with {@link
|
||||
* ParametersInterceptor}. For example, actions may want to create a white list of parameters they will accept or a
|
||||
* blacklist of parameters they will reject to prevent clients from setting other unexpected (and possibly dangerous)
|
||||
* parameters.
|
||||
*/
|
||||
public interface ParameterNameAware {
|
||||
|
||||
/**
|
||||
* Tests if the action will accept the parameter with the given name.
|
||||
*
|
||||
* @param parameterName the parameter name
|
||||
* @return <code>true</code> if accepted, <code>false</code> otherwise
|
||||
*/
|
||||
boolean acceptableParameterName(String parameterName);
|
||||
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
/*
|
||||
* 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.action;
|
||||
|
||||
import org.apache.struts2.interceptor.parameter.ParametersInterceptor;
|
||||
|
||||
/**
|
||||
* This interface is implemented by actions that want to declare acceptable parameter values. Works in conjunction with
|
||||
* {@link ParametersInterceptor}. For example, actions may want to create a white list of parameter values they will
|
||||
* accept or a blacklist of parameter values they will reject to prevent clients from setting other unexpected (and
|
||||
* possibly dangerous) parameter values.
|
||||
*/
|
||||
public interface ParameterValueAware {
|
||||
|
||||
/**
|
||||
* Tests if the action will accept the parameter with the given value.
|
||||
*
|
||||
* @param parameterValue the parameter value
|
||||
* @return <code>true</code> if accepted, <code>false</code> otherwise
|
||||
*/
|
||||
boolean acceptableParameterValue(String parameterValue);
|
||||
|
||||
}
|
||||
+3
-3
@@ -19,10 +19,10 @@
|
||||
package org.apache.struts2.interceptor;
|
||||
|
||||
import com.opensymphony.xwork2.ActionContext;
|
||||
import com.opensymphony.xwork2.interceptor.ParametersInterceptor;
|
||||
import org.apache.struts2.ServletActionContext;
|
||||
import org.apache.struts2.action.ParameterNameAware;
|
||||
import org.apache.struts2.dispatcher.HttpParameters;
|
||||
import org.apache.struts2.dispatcher.mapper.ActionMapping;
|
||||
import org.apache.struts2.interceptor.parameter.ParametersInterceptor;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
@@ -53,7 +53,7 @@ import java.util.Map;
|
||||
* <!-- START SNIPPET: extending -->
|
||||
*
|
||||
* <p>
|
||||
* The best way to add behavior to this interceptor is to utilize the {@link com.opensymphony.xwork2.interceptor.ParameterNameAware} interface in your
|
||||
* The best way to add behavior to this interceptor is to utilize the {@link ParameterNameAware} interface in your
|
||||
* actions. However, if you wish to apply a global rule that isn't implemented in your action, then you could extend
|
||||
* this interceptor and override the {@link #acceptableName(String)} method.
|
||||
* </p>
|
||||
|
||||
+561
@@ -0,0 +1,561 @@
|
||||
/*
|
||||
* 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.parameter;
|
||||
|
||||
import com.opensymphony.xwork2.ActionContext;
|
||||
import com.opensymphony.xwork2.ActionInvocation;
|
||||
import com.opensymphony.xwork2.TextProvider;
|
||||
import com.opensymphony.xwork2.inject.Inject;
|
||||
import com.opensymphony.xwork2.interceptor.MethodFilterInterceptor;
|
||||
import com.opensymphony.xwork2.interceptor.ValidationAware;
|
||||
import com.opensymphony.xwork2.security.AcceptedPatternsChecker;
|
||||
import com.opensymphony.xwork2.security.ExcludedPatternsChecker;
|
||||
import com.opensymphony.xwork2.util.ClearableValueStack;
|
||||
import com.opensymphony.xwork2.util.MemberAccessValueStack;
|
||||
import com.opensymphony.xwork2.util.TextParseUtil;
|
||||
import com.opensymphony.xwork2.util.ValueStack;
|
||||
import com.opensymphony.xwork2.util.ValueStackFactory;
|
||||
import com.opensymphony.xwork2.util.reflection.ReflectionContextState;
|
||||
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.action.NoParameters;
|
||||
import org.apache.struts2.action.ParameterNameAware;
|
||||
import org.apache.struts2.action.ParameterValueAware;
|
||||
import org.apache.struts2.dispatcher.HttpParameters;
|
||||
import org.apache.struts2.dispatcher.Parameter;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.Comparator;
|
||||
import java.util.HashSet;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.TreeMap;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
import static org.apache.commons.lang3.StringUtils.normalizeSpace;
|
||||
|
||||
/**
|
||||
* This interceptor sets all parameters on the value stack.
|
||||
*/
|
||||
public class ParametersInterceptor extends MethodFilterInterceptor {
|
||||
|
||||
private static final Logger LOG = LogManager.getLogger(ParametersInterceptor.class);
|
||||
|
||||
protected static final int PARAM_NAME_MAX_LENGTH = 100;
|
||||
|
||||
private static final Pattern DMI_IGNORED_PATTERN = Pattern.compile("^(action|method):.*", Pattern.CASE_INSENSITIVE);
|
||||
|
||||
private int paramNameMaxLength = PARAM_NAME_MAX_LENGTH;
|
||||
private boolean devMode = false;
|
||||
private boolean dmiEnabled = false;
|
||||
|
||||
protected boolean ordered = false;
|
||||
|
||||
private ValueStackFactory valueStackFactory;
|
||||
private ExcludedPatternsChecker excludedPatterns;
|
||||
private AcceptedPatternsChecker acceptedPatterns;
|
||||
private Set<Pattern> excludedValuePatterns = null;
|
||||
private Set<Pattern> acceptedValuePatterns = null;
|
||||
|
||||
@Inject
|
||||
public void setValueStackFactory(ValueStackFactory valueStackFactory) {
|
||||
this.valueStackFactory = valueStackFactory;
|
||||
}
|
||||
|
||||
@Inject(StrutsConstants.STRUTS_DEVMODE)
|
||||
public void setDevMode(String mode) {
|
||||
this.devMode = BooleanUtils.toBoolean(mode);
|
||||
}
|
||||
|
||||
@Inject
|
||||
public void setExcludedPatterns(ExcludedPatternsChecker excludedPatterns) {
|
||||
this.excludedPatterns = excludedPatterns;
|
||||
}
|
||||
|
||||
@Inject
|
||||
public void setAcceptedPatterns(AcceptedPatternsChecker acceptedPatterns) {
|
||||
this.acceptedPatterns = acceptedPatterns;
|
||||
}
|
||||
|
||||
@Inject(value = StrutsConstants.STRUTS_ENABLE_DYNAMIC_METHOD_INVOCATION, required = false)
|
||||
protected void setDynamicMethodInvocation(String dmiEnabled) {
|
||||
this.dmiEnabled = Boolean.parseBoolean(dmiEnabled);
|
||||
}
|
||||
|
||||
/**
|
||||
* If the param name exceeds the configured maximum length it will not be
|
||||
* accepted.
|
||||
*
|
||||
* @param paramNameMaxLength Maximum length of param names
|
||||
*/
|
||||
public void setParamNameMaxLength(int paramNameMaxLength) {
|
||||
this.paramNameMaxLength = paramNameMaxLength;
|
||||
}
|
||||
|
||||
static private int countOGNLCharacters(String s) {
|
||||
int count = 0;
|
||||
for (int i = s.length() - 1; i >= 0; i--) {
|
||||
char c = s.charAt(i);
|
||||
if (c == '.' || c == '[') count++;
|
||||
}
|
||||
return count;
|
||||
}
|
||||
|
||||
/**
|
||||
* Compares based on number of '.' and '[' characters (fewer is higher)
|
||||
*/
|
||||
static final Comparator<String> rbCollator = (s1, s2) -> {
|
||||
int l1 = countOGNLCharacters(s1);
|
||||
int l2 = countOGNLCharacters(s2);
|
||||
return l1 < l2 ? -1 : (l2 < l1 ? 1 : s1.compareTo(s2));
|
||||
};
|
||||
|
||||
@Override
|
||||
public String doIntercept(ActionInvocation invocation) throws Exception {
|
||||
Object action = invocation.getAction();
|
||||
if (!(action instanceof NoParameters)) {
|
||||
ActionContext ac = invocation.getInvocationContext();
|
||||
HttpParameters parameters = retrieveParameters(ac);
|
||||
|
||||
if (LOG.isDebugEnabled()) {
|
||||
LOG.debug("Setting params {}", normalizeSpace(getParameterLogMap(parameters)));
|
||||
}
|
||||
|
||||
if (parameters != null) {
|
||||
Map<String, Object> contextMap = ac.getContextMap();
|
||||
try {
|
||||
ReflectionContextState.setCreatingNullObjects(contextMap, true);
|
||||
ReflectionContextState.setDenyMethodExecution(contextMap, true);
|
||||
ReflectionContextState.setReportingConversionErrors(contextMap, true);
|
||||
|
||||
ValueStack stack = ac.getValueStack();
|
||||
setParameters(action, stack, parameters);
|
||||
} finally {
|
||||
ReflectionContextState.setCreatingNullObjects(contextMap, false);
|
||||
ReflectionContextState.setDenyMethodExecution(contextMap, false);
|
||||
ReflectionContextState.setReportingConversionErrors(contextMap, false);
|
||||
}
|
||||
}
|
||||
}
|
||||
return invocation.invoke();
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the parameter map to apply from wherever appropriate
|
||||
*
|
||||
* @param ac The action context
|
||||
* @return The parameter map to apply
|
||||
*/
|
||||
protected HttpParameters retrieveParameters(ActionContext ac) {
|
||||
return ac.getParameters();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Adds the parameters into context's ParameterMap
|
||||
*
|
||||
* @param ac The action context
|
||||
* @param newParams The parameter map to apply
|
||||
* <p>
|
||||
* In this class this is a no-op, since the parameters were fetched from the same location.
|
||||
* In subclasses both retrieveParameters() and addParametersToContext() should be overridden.
|
||||
* </p>
|
||||
*/
|
||||
protected void addParametersToContext(ActionContext ac, Map<String, ?> newParams) {
|
||||
}
|
||||
|
||||
protected void setParameters(final Object action, ValueStack stack, HttpParameters parameters) {
|
||||
HttpParameters params;
|
||||
Map<String, Parameter> acceptableParameters;
|
||||
if (ordered) {
|
||||
params = HttpParameters.create().withComparator(getOrderedComparator()).withParent(parameters).build();
|
||||
acceptableParameters = new TreeMap<>(getOrderedComparator());
|
||||
} else {
|
||||
params = HttpParameters.create().withParent(parameters).build();
|
||||
acceptableParameters = new TreeMap<>();
|
||||
}
|
||||
|
||||
for (Map.Entry<String, Parameter> entry : params.entrySet()) {
|
||||
String parameterName = entry.getKey();
|
||||
boolean isAcceptableParameter = isAcceptableParameter(parameterName, action);
|
||||
isAcceptableParameter &= isAcceptableParameterValue(entry.getValue(), action);
|
||||
|
||||
if (isAcceptableParameter) {
|
||||
acceptableParameters.put(parameterName, entry.getValue());
|
||||
}
|
||||
}
|
||||
|
||||
ValueStack newStack = valueStackFactory.createValueStack(stack);
|
||||
boolean clearableStack = newStack instanceof ClearableValueStack;
|
||||
if (clearableStack) {
|
||||
//if the stack's context can be cleared, do that to prevent OGNL
|
||||
//from having access to objects in the stack, see XW-641
|
||||
((ClearableValueStack) newStack).clearContextValues();
|
||||
Map<String, Object> context = newStack.getContext();
|
||||
ReflectionContextState.setCreatingNullObjects(context, true);
|
||||
ReflectionContextState.setDenyMethodExecution(context, true);
|
||||
ReflectionContextState.setReportingConversionErrors(context, true);
|
||||
|
||||
//keep locale from original context
|
||||
newStack.getActionContext().withLocale(stack.getActionContext().getLocale()).withValueStack(stack);
|
||||
}
|
||||
|
||||
boolean memberAccessStack = newStack instanceof MemberAccessValueStack;
|
||||
if (memberAccessStack) {
|
||||
//block or allow access to properties
|
||||
//see WW-2761 for more details
|
||||
MemberAccessValueStack accessValueStack = (MemberAccessValueStack) newStack;
|
||||
accessValueStack.useAcceptProperties(acceptedPatterns.getAcceptedPatterns());
|
||||
accessValueStack.useExcludeProperties(excludedPatterns.getExcludedPatterns());
|
||||
}
|
||||
|
||||
for (Map.Entry<String, Parameter> entry : acceptableParameters.entrySet()) {
|
||||
String name = entry.getKey();
|
||||
Parameter value = entry.getValue();
|
||||
try {
|
||||
newStack.setParameter(name, value.getObject());
|
||||
} catch (RuntimeException e) {
|
||||
if (devMode) {
|
||||
notifyDeveloperParameterException(action, name, e.getMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (clearableStack) {
|
||||
stack.getActionContext().withConversionErrors(newStack.getActionContext().getConversionErrors());
|
||||
}
|
||||
|
||||
addParametersToContext(ActionContext.getContext(), acceptableParameters);
|
||||
}
|
||||
|
||||
protected void notifyDeveloperParameterException(Object action, String property, String message) {
|
||||
String developerNotification = "Unexpected Exception caught setting '" + property + "' on '" + action.getClass() + ": " + message;
|
||||
if (action instanceof TextProvider) {
|
||||
TextProvider tp = (TextProvider) action;
|
||||
developerNotification = tp.getText("devmode.notification",
|
||||
"Developer Notification:\n{0}",
|
||||
new String[]{developerNotification}
|
||||
);
|
||||
}
|
||||
|
||||
LOG.error(developerNotification);
|
||||
|
||||
if (action instanceof ValidationAware) {
|
||||
// see https://issues.apache.org/jira/browse/WW-4066
|
||||
Collection<String> messages = ((ValidationAware) action).getActionMessages();
|
||||
messages.add(message);
|
||||
((ValidationAware) action).setActionMessages(messages);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if name of parameter can be accepted or thrown away
|
||||
*
|
||||
* @param name parameter name
|
||||
* @param action current action
|
||||
* @return true if parameter is accepted
|
||||
*/
|
||||
protected boolean isAcceptableParameter(String name, Object action) {
|
||||
ParameterNameAware parameterNameAware = (action instanceof ParameterNameAware) ? (ParameterNameAware) action : null;
|
||||
return acceptableName(name) && (parameterNameAware == null || parameterNameAware.acceptableParameterName(name));
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if parameter value can be accepted or thrown away
|
||||
*
|
||||
* @param param the parameter
|
||||
* @param action current action
|
||||
* @return true if parameter is accepted
|
||||
*/
|
||||
protected boolean isAcceptableParameterValue(Parameter param, Object action) {
|
||||
ParameterValueAware parameterValueAware = (action instanceof ParameterValueAware) ? (ParameterValueAware) action : null;
|
||||
boolean acceptableParamValue = (parameterValueAware == null || parameterValueAware.acceptableParameterValue(param.getValue()));
|
||||
if (hasParamValuesToExclude() || hasParamValuesToAccept()) {
|
||||
// Additional validations to process
|
||||
acceptableParamValue &= acceptableValue(param.getName(), param.getValue());
|
||||
}
|
||||
return acceptableParamValue;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets an instance of the comparator to use for the ordered sorting. Override this
|
||||
* method to customize the ordering of the parameters as they are set to the
|
||||
* action.
|
||||
*
|
||||
* @return A comparator to sort the parameters
|
||||
*/
|
||||
protected Comparator<String> getOrderedComparator() {
|
||||
return rbCollator;
|
||||
}
|
||||
|
||||
protected String getParameterLogMap(HttpParameters parameters) {
|
||||
if (parameters == null) {
|
||||
return "NONE";
|
||||
}
|
||||
|
||||
StringBuilder logEntry = new StringBuilder();
|
||||
for (Map.Entry<String, Parameter> entry : parameters.entrySet()) {
|
||||
logEntry.append(entry.getKey());
|
||||
logEntry.append(" => ");
|
||||
logEntry.append(entry.getValue().getValue());
|
||||
logEntry.append(" ");
|
||||
}
|
||||
|
||||
return logEntry.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* Validates the name passed is:
|
||||
* * Within the max length of a parameter name
|
||||
* * Is not excluded
|
||||
* * Is accepted
|
||||
*
|
||||
* @param name - Name to check
|
||||
* @return true if accepted
|
||||
*/
|
||||
protected boolean acceptableName(String name) {
|
||||
if (isIgnoredDMI(name)) {
|
||||
LOG.trace("DMI is enabled, ignoring DMI method: {}", name);
|
||||
return false;
|
||||
}
|
||||
boolean accepted = isWithinLengthLimit(name) && !isExcluded(name) && isAccepted(name);
|
||||
if (devMode && accepted) { // notify only when in devMode
|
||||
LOG.debug("Parameter [{}] was accepted and will be appended to action!", name);
|
||||
}
|
||||
return accepted;
|
||||
}
|
||||
|
||||
private boolean isIgnoredDMI(String name) {
|
||||
if (dmiEnabled) {
|
||||
return DMI_IGNORED_PATTERN.matcher(name).matches();
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Validates:
|
||||
* * Value is null/blank
|
||||
* * Value is not excluded
|
||||
* * Value is accepted
|
||||
*
|
||||
* @param name - Param name (for logging)
|
||||
* @param value - value to check
|
||||
* @return true if accepted
|
||||
*/
|
||||
protected boolean acceptableValue(String name, String value) {
|
||||
boolean accepted = (value == null || value.isEmpty() || (!isParamValueExcluded(value) && isParamValueAccepted(value)));
|
||||
if (!accepted) {
|
||||
String message = "Value [{}] of parameter [{}] was not accepted and will be dropped!";
|
||||
if (devMode) {
|
||||
LOG.warn(message, normalizeSpace(value), normalizeSpace(name));
|
||||
} else {
|
||||
LOG.debug(message, normalizeSpace(value), normalizeSpace(name));
|
||||
}
|
||||
}
|
||||
return accepted;
|
||||
}
|
||||
|
||||
protected boolean isWithinLengthLimit(String name) {
|
||||
boolean matchLength = name.length() <= paramNameMaxLength;
|
||||
if (!matchLength) {
|
||||
if (devMode) { // warn only when in devMode
|
||||
LOG.warn("Parameter [{}] is too long, allowed length is [{}]. Use Interceptor Parameter Overriding " +
|
||||
"to override the limit, see more at\n" +
|
||||
"https://struts.apache.org/core-developers/interceptors.html#interceptor-parameter-overriding",
|
||||
name, paramNameMaxLength);
|
||||
} else {
|
||||
LOG.warn("Parameter [{}] is too long, allowed length is [{}]", name, paramNameMaxLength);
|
||||
}
|
||||
}
|
||||
return matchLength;
|
||||
}
|
||||
|
||||
protected boolean isAccepted(String paramName) {
|
||||
AcceptedPatternsChecker.IsAccepted result = acceptedPatterns.isAccepted(paramName);
|
||||
if (result.isAccepted()) {
|
||||
return true;
|
||||
} else if (devMode) { // warn only when in devMode
|
||||
LOG.warn("Parameter [{}] didn't match accepted pattern [{}]! See Accepted / Excluded patterns at\n" +
|
||||
"https://struts.apache.org/security/#accepted--excluded-patterns",
|
||||
paramName, result.getAcceptedPattern());
|
||||
} else {
|
||||
LOG.debug("Parameter [{}] didn't match accepted pattern [{}]!", paramName, result.getAcceptedPattern());
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
protected boolean isExcluded(String paramName) {
|
||||
ExcludedPatternsChecker.IsExcluded result = excludedPatterns.isExcluded(paramName);
|
||||
if (result.isExcluded()) {
|
||||
if (devMode) { // warn only when in devMode
|
||||
LOG.warn("Parameter [{}] matches excluded pattern [{}]! See Accepted / Excluded patterns at\n" +
|
||||
"https://struts.apache.org/security/#accepted--excluded-patterns",
|
||||
paramName, result.getExcludedPattern());
|
||||
} else {
|
||||
LOG.debug("Parameter [{}] matches excluded pattern [{}]!", paramName, result.getExcludedPattern());
|
||||
}
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
protected boolean isParamValueExcluded(String value) {
|
||||
if (!hasParamValuesToExclude()) {
|
||||
LOG.debug("'excludedValuePatterns' not defined so anything is allowed");
|
||||
return false;
|
||||
}
|
||||
for (Pattern excludedValuePattern : excludedValuePatterns) {
|
||||
if (excludedValuePattern.matcher(value).matches()) {
|
||||
if (devMode) {
|
||||
LOG.warn("Parameter value [{}] matches excluded pattern [{}]! See Accepting/Excluding parameter values at\n" +
|
||||
"https://struts.apache.org/core-developers/parameters-interceptor#excluding-parameter-values",
|
||||
value, excludedValuePatterns);
|
||||
} else {
|
||||
LOG.debug("Parameter value [{}] matches excluded pattern [{}]", value, excludedValuePattern);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
protected boolean isParamValueAccepted(String value) {
|
||||
if (!hasParamValuesToAccept()) {
|
||||
LOG.debug("'acceptedValuePatterns' not defined so anything is allowed");
|
||||
return true;
|
||||
}
|
||||
for (Pattern acceptedValuePattern : acceptedValuePatterns) {
|
||||
if (acceptedValuePattern.matcher(value).matches()) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
if (devMode) {
|
||||
LOG.warn("Parameter value [{}] didn't match accepted pattern [{}]! See Accepting/Excluding parameter values at\n" +
|
||||
"https://struts.apache.org/core-developers/parameters-interceptor#excluding-parameter-values",
|
||||
value, acceptedValuePatterns);
|
||||
} else {
|
||||
LOG.debug("Parameter value [{}] was not accepted!", value);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private boolean hasParamValuesToExclude() {
|
||||
return excludedValuePatterns != null && excludedValuePatterns.size() > 0;
|
||||
}
|
||||
|
||||
private boolean hasParamValuesToAccept() {
|
||||
return acceptedValuePatterns != null && acceptedValuePatterns.size() > 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Whether to order the parameters or not
|
||||
*
|
||||
* @return True to order
|
||||
*/
|
||||
public boolean isOrdered() {
|
||||
return ordered;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set whether to order the parameters by object depth or not
|
||||
*
|
||||
* @param ordered True to order them
|
||||
*/
|
||||
public void setOrdered(boolean ordered) {
|
||||
this.ordered = ordered;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets a comma-delimited list of regular expressions to match
|
||||
* parameters that are allowed in the parameter map (aka whitelist).
|
||||
* <p>
|
||||
* Don't change the default unless you know what you are doing in terms
|
||||
* of security implications.
|
||||
* </p>
|
||||
*
|
||||
* @param commaDelim A comma-delimited list of regular expressions
|
||||
*/
|
||||
public void setAcceptParamNames(String commaDelim) {
|
||||
acceptedPatterns.setAcceptedPatterns(commaDelim);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets a comma-delimited list of regular expressions to match
|
||||
* parameters that should be removed from the parameter map.
|
||||
*
|
||||
* @param commaDelim A comma-delimited list of regular expressions
|
||||
*/
|
||||
public void setExcludeParams(String commaDelim) {
|
||||
excludedPatterns.setExcludedPatterns(commaDelim);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets a comma-delimited list of regular expressions to match
|
||||
* values of parameters that should be accepted and included in the parameter map.
|
||||
*
|
||||
* @param commaDelimitedPatterns A comma-delimited set of regular expressions
|
||||
*/
|
||||
public void setAcceptedValuePatterns(String commaDelimitedPatterns) {
|
||||
Set<String> patterns = TextParseUtil.commaDelimitedStringToSet(commaDelimitedPatterns);
|
||||
if (acceptedValuePatterns == null) {
|
||||
// Limit unwanted log entries (for 1st call, acceptedValuePatterns null)
|
||||
LOG.debug("Sets accepted value patterns to [{}], note this may impact the safety of your application!", patterns);
|
||||
} else {
|
||||
LOG.warn("Replacing accepted patterns [{}] with [{}], be aware that this may impact safety of your application!",
|
||||
acceptedValuePatterns, patterns);
|
||||
}
|
||||
acceptedValuePatterns = new HashSet<>(patterns.size());
|
||||
try {
|
||||
for (String pattern : patterns) {
|
||||
acceptedValuePatterns.add(Pattern.compile(pattern, Pattern.CASE_INSENSITIVE));
|
||||
}
|
||||
} finally {
|
||||
acceptedValuePatterns = Collections.unmodifiableSet(acceptedValuePatterns);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets a comma-delimited list of regular expressions to match
|
||||
* values of parameters that should be removed from the parameter map.
|
||||
*
|
||||
* @param commaDelimitedPatterns A comma-delimited set of regular expressions
|
||||
*/
|
||||
public void setExcludedValuePatterns(String commaDelimitedPatterns) {
|
||||
Set<String> patterns = TextParseUtil.commaDelimitedStringToSet(commaDelimitedPatterns);
|
||||
if (excludedValuePatterns == null) {
|
||||
// Limit unwanted log entries (for 1st call, excludedValuePatterns null)
|
||||
LOG.debug("Setting excluded value patterns to [{}]", patterns);
|
||||
} else {
|
||||
LOG.warn("Replacing excluded value patterns [{}] with [{}], be aware that this may impact safety of your application!",
|
||||
excludedValuePatterns, patterns);
|
||||
}
|
||||
excludedValuePatterns = new HashSet<>(patterns.size());
|
||||
try {
|
||||
for (String pattern : patterns) {
|
||||
excludedValuePatterns.add(Pattern.compile(pattern, Pattern.CASE_INSENSITIVE));
|
||||
}
|
||||
} finally {
|
||||
excludedValuePatterns = Collections.unmodifiableSet(excludedValuePatterns);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing,
|
||||
* software distributed under the License is distributed on an
|
||||
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
||||
* KIND, either express or implied. See the License for the
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
package org.apache.struts2.util;
|
||||
|
||||
import com.opensymphony.xwork2.util.ValueStack;
|
||||
|
||||
/**
|
||||
* @since 6.4.0
|
||||
*/
|
||||
public interface ValueStackProvider {
|
||||
|
||||
ValueStack getValueStack();
|
||||
|
||||
}
|
||||
@@ -20,39 +20,39 @@
|
||||
-->
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<style>
|
||||
<@s.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>
|
||||
</@s.style>
|
||||
|
||||
<script>
|
||||
<@s.script>
|
||||
function expand(src, path) {
|
||||
var baseUrl = location.href;
|
||||
var i = baseUrl.indexOf("&object=");
|
||||
@@ -83,7 +83,7 @@
|
||||
};
|
||||
request.send();
|
||||
}
|
||||
</script>
|
||||
</@s.script>
|
||||
|
||||
<body>
|
||||
${debugHtml?no_esc}
|
||||
|
||||
@@ -21,7 +21,7 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<@s.script type="text/javascript">
|
||||
<@s.script>
|
||||
var baseUrl = "<@s.url value="/static" includeParams="none"/>";
|
||||
window.open(baseUrl+"/webconsole.html", 'OGNL Console','width=500,height=450,status=no,toolbar=no,menubar=no');
|
||||
</@s.script>
|
||||
|
||||
@@ -177,6 +177,16 @@
|
||||
<bean type="com.opensymphony.xwork2.ognl.accessor.RootAccessor" name="struts"
|
||||
class="com.opensymphony.xwork2.ognl.accessor.CompoundRootAccessor"/>
|
||||
|
||||
<!-- DEPRECATED since 6.4.0 - The following 3 beans are retained for backwards compatibility
|
||||
with custom beans that may expect them to be present. Inject the DEFAULT bean of type
|
||||
'com.opensymphony.xwork2.ognl.accessor.RootAccessor' above instead. -->
|
||||
<bean type="ognl.ClassResolver" name="com.opensymphony.xwork2.util.CompoundRoot"
|
||||
class="com.opensymphony.xwork2.ognl.accessor.CompoundRootAccessor"/>
|
||||
<bean type="ognl.PropertyAccessor" name="com.opensymphony.xwork2.util.CompoundRoot"
|
||||
class="com.opensymphony.xwork2.ognl.accessor.CompoundRootAccessor"/>
|
||||
<bean type="ognl.MethodAccessor" name="com.opensymphony.xwork2.util.CompoundRoot"
|
||||
class="com.opensymphony.xwork2.ognl.accessor.CompoundRootAccessor"/>
|
||||
|
||||
<bean type="ognl.PropertyAccessor" name="java.lang.Object"
|
||||
class="com.opensymphony.xwork2.ognl.accessor.ObjectAccessor"/>
|
||||
<bean type="ognl.PropertyAccessor" name="java.util.Iterator"
|
||||
|
||||
@@ -19,7 +19,7 @@
|
||||
*/
|
||||
-->
|
||||
<#if parameters.validate!false == true>
|
||||
<script type="text/javascript" src="${base}${parameters.staticContentPath}/css_xhtml/validation.js"></script>
|
||||
<@s.script src="${base}${parameters.staticContentPath}/css_xhtml/validation.js"/>
|
||||
<#if parameters.onsubmit??>
|
||||
${tag.addParameter('onsubmit', "${parameters.onsubmit}; return validateForm_${parameters.escapedId}();")}
|
||||
<#else>
|
||||
|
||||
@@ -18,7 +18,7 @@
|
||||
* under the License.
|
||||
*/
|
||||
-->
|
||||
<@s.script type="text/javascript">
|
||||
<@s.script>
|
||||
function autoPopulate_${parameters.escapedId}(targetElement) {
|
||||
<#if parameters.headerKey?? && parameters.headerValue??>
|
||||
if (targetElement.options[targetElement.selectedIndex].value == '${parameters.headerKey?js_string}') {
|
||||
|
||||
@@ -114,7 +114,7 @@
|
||||
</#if>
|
||||
/><#rt/>
|
||||
</#if>
|
||||
<@s.script type="text/javascript">
|
||||
<@s.script>
|
||||
<#assign itemCount = startCount/>
|
||||
var ${parameters.escapedId}Group = new Array(${parameters.listSize?number?c} + ${startCount});
|
||||
for (var i = 0; i < (${parameters.listSize?number?c} + ${startCount}); i++) {
|
||||
|
||||
@@ -24,6 +24,6 @@
|
||||
--><#t/>
|
||||
<#if (parameters.hasTooltip!false)><#t/>
|
||||
<#lt/><!-- javascript that is needed for tooltips -->
|
||||
<#lt/><@s.script type="text/javascript" src="${base}${parameters.staticContentPath}/domTT.js" />
|
||||
<#lt/><@s.script src="${base}${parameters.staticContentPath}/domTT.js" />
|
||||
<#lt/><@s.link rel="stylesheet" type="text/css" href="${base}${parameters.staticContentPath}/domTT.css" />
|
||||
</#if><#t/>
|
||||
|
||||
@@ -21,7 +21,7 @@
|
||||
</form>
|
||||
|
||||
<#if (parameters.customOnsubmitEnabled??)>
|
||||
<@s.script type="text/javascript">
|
||||
<@s.script>
|
||||
<#--
|
||||
Enable auto-select of optiontransferselect tag's entries upon containing form's
|
||||
submission.
|
||||
|
||||
@@ -18,4 +18,4 @@
|
||||
* under the License.
|
||||
*/
|
||||
-->
|
||||
<@s.script src="${base}${parameters.staticContentPath}/utils.js" type="text/javascript" />
|
||||
<@s.script src="${base}${parameters.staticContentPath}/utils.js" />
|
||||
|
||||
@@ -19,7 +19,7 @@
|
||||
*/
|
||||
-->
|
||||
<#if !stack.findValue("#inputtransferselect_js_included")??><#t/>
|
||||
<@s.script type="text/javascript" src="${base}${parameters.staticContentPath}/inputtransferselect.js"/>
|
||||
<@s.script src="${base}${parameters.staticContentPath}/inputtransferselect.js"/>
|
||||
<#assign temporaryVariable = stack.setValue("#inputtransferselect_js_included", "true") /><#t/>
|
||||
</#if><#t/>
|
||||
<table>
|
||||
|
||||
@@ -19,7 +19,7 @@
|
||||
*/
|
||||
-->
|
||||
<#if !stack.findValue("#optiontransferselect_js_included")??><#t/>
|
||||
<@s.script type="text/javascript" src="${base}${parameters.staticContentPath}/optiontransferselect.js" />
|
||||
<@s.script src="${base}${parameters.staticContentPath}/optiontransferselect.js" />
|
||||
<#assign temporaryVariable = stack.setValue("#optiontransferselect_js_included", "true") /><#t/>
|
||||
</#if><#t/>
|
||||
<table>
|
||||
|
||||
@@ -19,7 +19,7 @@
|
||||
*/
|
||||
-->
|
||||
<#if !stack.findValue("#optiontransferselect_js_included")??><#t/>
|
||||
<@s.script type="text/javascript" src="${base}${parameters.staticContentPath}/optiontransferselect.js" /><#t/>
|
||||
<@s.script src="${base}${parameters.staticContentPath}/optiontransferselect.js" /><#t/>
|
||||
<#assign temporaryVariable = stack.setValue("#optiontransferselect_js_included", "true") /><#t/>
|
||||
</#if><#t/>
|
||||
<table>
|
||||
|
||||
@@ -32,7 +32,7 @@ Only the following validators are supported:
|
||||
END SNIPPET: supported-validators
|
||||
-->
|
||||
<#if ((parameters.validate!false == true) && (parameters.performValidation!false == true))>
|
||||
<@s.script type="text/javascript">
|
||||
<@s.script>
|
||||
function validateForm_${parameters.escapedId}() {
|
||||
<#--
|
||||
In case of multiselect fields return only the first value.
|
||||
|
||||
@@ -22,7 +22,7 @@
|
||||
<#include "/${parameters.templateDir}/simple/form-close.ftl" />
|
||||
<#include "/${parameters.templateDir}/${parameters.expandTheme}/form-close-validate.ftl" />
|
||||
<#if parameters.focusElement??>
|
||||
<@s.script type="text/javascript">
|
||||
<@s.script>
|
||||
StrutsUtils.addOnLoad(function() {
|
||||
var element = document.getElementById("${parameters.focusElement?js_string}");
|
||||
if(element) {
|
||||
|
||||
@@ -19,7 +19,7 @@
|
||||
*/
|
||||
-->
|
||||
<#if parameters.validate!false == true>
|
||||
<@s.script type="text/javascript" src="${base}${parameters.staticContentPath}/xhtml/validation.js" />
|
||||
<@s.script src="${base}${parameters.staticContentPath}/xhtml/validation.js" />
|
||||
<#if parameters.onsubmit??>
|
||||
${tag.addParameter('onsubmit', "${parameters.onsubmit}; return validateForm_${parameters.escapedId}();")}
|
||||
<#else>
|
||||
|
||||
@@ -49,7 +49,7 @@ under the License.
|
||||
color: #666;
|
||||
}
|
||||
</style>
|
||||
<script type="text/javascript" language="javascript">
|
||||
<script>
|
||||
var hide = null;
|
||||
var show = null;
|
||||
var children = null;
|
||||
|
||||
+8
-8
@@ -16,7 +16,7 @@
|
||||
* specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*/
|
||||
package com.opensymphony.xwork2.interceptor;
|
||||
package org.apache.struts2.interceptor.parameter;
|
||||
|
||||
import com.opensymphony.xwork2.Action;
|
||||
import com.opensymphony.xwork2.ActionContext;
|
||||
@@ -32,6 +32,7 @@ import com.opensymphony.xwork2.config.providers.MockConfigurationProvider;
|
||||
import com.opensymphony.xwork2.config.providers.StrutsDefaultConfigurationProvider;
|
||||
import com.opensymphony.xwork2.config.providers.XmlConfigurationProvider;
|
||||
import com.opensymphony.xwork2.conversion.impl.XWorkConverter;
|
||||
import com.opensymphony.xwork2.interceptor.ValidationAware;
|
||||
import com.opensymphony.xwork2.mock.MockActionInvocation;
|
||||
import com.opensymphony.xwork2.ognl.OgnlValueStack;
|
||||
import com.opensymphony.xwork2.ognl.OgnlValueStackFactory;
|
||||
@@ -41,6 +42,9 @@ import com.opensymphony.xwork2.util.ValueStack;
|
||||
import com.opensymphony.xwork2.util.ValueStackFactory;
|
||||
import com.opensymphony.xwork2.util.reflection.ReflectionContextState;
|
||||
import ognl.OgnlContext;
|
||||
import org.apache.struts2.action.NoParameters;
|
||||
import org.apache.struts2.action.ParameterNameAware;
|
||||
import org.apache.struts2.action.ParameterValueAware;
|
||||
import org.apache.struts2.config.StrutsXmlConfigurationProvider;
|
||||
import org.apache.struts2.dispatcher.HttpParameters;
|
||||
import org.junit.Assert;
|
||||
@@ -74,11 +78,7 @@ public class ParametersInterceptorTest extends XWorkTestCase {
|
||||
put("barKey", "barValue");
|
||||
}
|
||||
};
|
||||
Object a = new ParameterNameAware() {
|
||||
public boolean acceptableParameterName(String parameterName) {
|
||||
return expected.containsKey(parameterName);
|
||||
}
|
||||
};
|
||||
ParameterNameAware a = expected::containsKey;
|
||||
final Map<String, Object> parameters = new HashMap<String, Object>() {
|
||||
{
|
||||
put("fooKey", "fooValue");
|
||||
@@ -231,10 +231,10 @@ public class ParametersInterceptorTest extends XWorkTestCase {
|
||||
|
||||
public void testModelDrivenParameters() throws Exception {
|
||||
Map<String, Object> params = new HashMap<>();
|
||||
final String fooVal = "com.opensymphony.xwork2.interceptor.ParametersInterceptorTest.foo";
|
||||
final String fooVal = "org.apache.struts2.interceptor.parameter.ParametersInterceptorTest.foo";
|
||||
params.put("foo", fooVal);
|
||||
|
||||
final String nameVal = "com.opensymphony.xwork2.interceptor.ParametersInterceptorTest.name";
|
||||
final String nameVal = "org.apache.struts2.interceptor.parameter.ParametersInterceptorTest.name";
|
||||
params.put("name", nameVal);
|
||||
params.put("count", "15");
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
<tr>
|
||||
<td class="tdLabel"><label for="cb" class="label">mylabel:</label></td>
|
||||
<td class="tdInput">
|
||||
<script type="text/javascript" nonce="r4nd0m">
|
||||
<script nonce="r4nd0m">
|
||||
function autoPopulate_cb(targetElement) {
|
||||
targetElement.form.elements['foo'].value=targetElement.options[targetElement.selectedIndex].value;
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
<tr>
|
||||
<td class="tdLabel"><label for="myFavouriteFruit" class="label">My Favourite Fruit:</label></td>
|
||||
<td class="tdInput">
|
||||
<script type="text/javascript">
|
||||
<script>
|
||||
function autoPopulate_myFavouriteFruit(targetElement) {
|
||||
if (targetElement.options[targetElement.selectedIndex].value == '-1') {
|
||||
return;
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
<tr>
|
||||
<td class="tdLabel"><label for="myFavouriteFruit" class="label">My Favourite Fruit:</label></td>
|
||||
<td class="tdInput">
|
||||
<script type="text/javascript">
|
||||
<script>
|
||||
function autoPopulate_myFavouriteFruit(targetElement) {
|
||||
if (targetElement.options[targetElement.selectedIndex].value == '-1') {
|
||||
return;
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
<tr>
|
||||
<td class="tdLabel"><label for="cb['"."']=bc(){};//" class="label">mylabel:</label></td>
|
||||
<td class="tdInput">
|
||||
<script type="text/javascript">
|
||||
<script>
|
||||
function autoPopulate_cb__________bc_______(targetElement) {
|
||||
targetElement.form.elements['foo'].value=targetElement.options[targetElement.selectedIndex].value;
|
||||
}
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
<br/>
|
||||
<select name="region" id="region" class="c2" style="s2">
|
||||
</select>
|
||||
<script type="text/javascript" nonce="r4nd0m">
|
||||
<script nonce="r4nd0m">
|
||||
var fooGroup = new Array(2 + 0);
|
||||
for (var i = 0; i < (2 + 0); i++) {
|
||||
fooGroup[i] = [];
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
<br/>
|
||||
<select name="region" id="region">
|
||||
</select>
|
||||
<script type="text/javascript">
|
||||
<script>
|
||||
var fooGroup = new Array(2 + 0);
|
||||
for (var i = 0; i < (2 + 0); i++) {
|
||||
fooGroup[i] = [];
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
<br/>
|
||||
<select name="region" id="region">
|
||||
</select>
|
||||
<script type="text/javascript">
|
||||
<script>
|
||||
var foo_barGroup = new Array(2 + 0);
|
||||
for (var i = 0; i < (2 + 0); i++) {
|
||||
foo_barGroup[i] = [];
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
<br/>
|
||||
<select name="region" id="region" class="c2" style="s2">
|
||||
</select>
|
||||
<script type="text/javascript">
|
||||
<script>
|
||||
var fooGroup = new Array(2 + 0);
|
||||
for (var i = 0; i < (2 + 0); i++) {
|
||||
fooGroup[i] = [];
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
</select>
|
||||
<br />
|
||||
<select name="region" disabled="disabled" id="region" title="someTitle" select-name="secondName"></select>
|
||||
<script type="text/javascript">
|
||||
<script>
|
||||
var someIdGroup = newArray(2 + 0);
|
||||
for (var i = 0; i < (2 + 0); i++) {
|
||||
someIdGroup[i] = [];
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
<br/>
|
||||
<select name="region" id="region" onclick="testMe()" ondblclick="testMe()" onmousedown="testMe()" onmouseup="testMe()" onmouseover="testMe()" onmousemove="testMe()" onmouseout="testMe()" onfocus="testMe()" onblur="testMe()" onkeypress="testMe()" onkeydown="testMe()" onselect="testMe()" onchange="testMe()">
|
||||
</select>
|
||||
<script type="text/javascript" >
|
||||
<script>
|
||||
var fooGroup = new Array(2 + 0);
|
||||
for (var i = 0; i < (2 + 0); i++) {
|
||||
fooGroup[i] = [];
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
<script type="text/javascript" src="/static/xhtml/validation.js"></script>
|
||||
<script src="/static/xhtml/validation.js"></script>
|
||||
<form id="testAction" name="myForm" onsubmit="submitMe(); return validateForm_testAction();" action="/testAction.action" method="post" enctype="myEncType" title="mytitle" accept-charset="UTF-8" onreset="clearErrorMessages(this);clearErrorLabels(this);">
|
||||
<table class="wwFormTable"> <tr>
|
||||
<td class="tdLabel"></td>
|
||||
<td class="tdInput"> <script type="text/javascript" src="/static/optiontransferselect.js"></script>
|
||||
<td class="tdInput"> <script src="/static/optiontransferselect.js"></script>
|
||||
<table>
|
||||
<tr><td>
|
||||
<select name="myUpDownSelectTag" size="5" id="testAction_myUpDownSelectTag" multiple="multiple">
|
||||
@@ -21,7 +21,7 @@
|
||||
</form>
|
||||
|
||||
|
||||
<script type="text/javascript">
|
||||
<script>
|
||||
var containingForm = document.getElementById("testAction");
|
||||
StrutsUtils.addEventListener(containingForm, "submit",
|
||||
function(evt) {
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
</table>
|
||||
</form>
|
||||
|
||||
<script type="text/javascript">
|
||||
<script>
|
||||
StrutsUtils.addOnLoad(function() {
|
||||
var element = document.getElementById("felement");
|
||||
if(element) {
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
<script type="text/javascript" src="/static/xhtml/validation.js"></script>
|
||||
<script src="/static/xhtml/validation.js"></script>
|
||||
<form id="doubleValidationAction" name="myForm" onsubmit="submitMe(); return validateForm_doubleValidationAction();" action="/doubleValidationAction.action" method="post" enctype="myEncType" title="mytitle" accept-charset="UTF-8" onreset="clearErrorMessages(this);clearErrorLabels(this);">
|
||||
<table class="wwFormTable"> <tr>
|
||||
<td class="tdLabel"></td>
|
||||
<td class="tdInput"> <script type="text/javascript" src="/static/optiontransferselect.js"></script>
|
||||
<td class="tdInput"> <script src="/static/optiontransferselect.js"></script>
|
||||
<table>
|
||||
<tr><td>
|
||||
<select name="myUpDownSelectTag" size="5" id="doubleValidationAction_myUpDownSelectTag" multiple="multiple">
|
||||
@@ -21,7 +21,7 @@
|
||||
</form>
|
||||
|
||||
|
||||
<script type="text/javascript">
|
||||
<script>
|
||||
var containingForm = document.getElementById("doubleValidationAction");
|
||||
StrutsUtils.addEventListener(containingForm, "submit",
|
||||
function(evt) {
|
||||
@@ -30,7 +30,7 @@
|
||||
}, true);
|
||||
</script>
|
||||
|
||||
<script type="text/javascript">
|
||||
<script>
|
||||
|
||||
function validateForm_doubleValidationAction() {
|
||||
var getFieldValue = function(field) {
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
<script type="text/javascript" src="/static/xhtml/validation.js"></script>
|
||||
<script src="/static/xhtml/validation.js"></script>
|
||||
<form id="doubleValidationAction" name="myForm" onsubmit="submitMe(); return validateForm_doubleValidationAction();" action="/doubleValidationAction.action" method="post" enctype="myEncType" title="mytitle" accept-charset="UTF-8" onreset="clearErrorMessages(this);clearErrorLabels(this);">
|
||||
<table class="wwFormTable"> <tr>
|
||||
<td class="tdLabel"></td>
|
||||
<td class="tdInput"> <script type="text/javascript" src="/static/optiontransferselect.js"></script>
|
||||
<td class="tdInput"> <script src="/static/optiontransferselect.js"></script>
|
||||
<table>
|
||||
<tr><td>
|
||||
<select name="myUpDownSelectTag" size="5" id="doubleValidationAction_myUpDownSelectTag" multiple="multiple">
|
||||
@@ -21,7 +21,7 @@
|
||||
</form>
|
||||
|
||||
|
||||
<script type="text/javascript">
|
||||
<script>
|
||||
var containingForm = document.getElementById("doubleValidationAction");
|
||||
StrutsUtils.addEventListener(containingForm, "submit",
|
||||
function(evt) {
|
||||
@@ -30,7 +30,7 @@
|
||||
}, true);
|
||||
</script>
|
||||
|
||||
<script type="text/javascript">
|
||||
<script>
|
||||
function validateForm_doubleValidationAction() {
|
||||
var getFieldValue = function(field) {
|
||||
var type = field.type ? field.type : field[0].type;
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
<script type="text/javascript" src="/static/xhtml/validation.js"></script>
|
||||
<script src="/static/xhtml/validation.js"></script>
|
||||
<form id="doubleValidationAction" name="myForm" onsubmit="submitMe(); return validateForm_doubleValidationAction();" action="/doubleValidationAction.action" method="post" enctype="myEncType" title="mytitle" accept-charset="UTF-8" onreset="clearErrorMessages(this);clearErrorLabels(this);">
|
||||
<table class="wwFormTable"> <tr>
|
||||
<td class="tdLabel"></td>
|
||||
<td class="tdInput"> <script type="text/javascript" src="/static/optiontransferselect.js"></script>
|
||||
<td class="tdInput"> <script src="/static/optiontransferselect.js"></script>
|
||||
<table>
|
||||
<tr><td>
|
||||
<select name="myUpDownSelectTag" size="5" id="doubleValidationAction_myUpDownSelectTag" multiple="multiple">
|
||||
@@ -21,7 +21,7 @@
|
||||
</form>
|
||||
|
||||
|
||||
<script type="text/javascript">
|
||||
<script>
|
||||
var containingForm = document.getElementById("doubleValidationAction");
|
||||
StrutsUtils.addEventListener(containingForm, "submit",
|
||||
function(evt) {
|
||||
@@ -30,7 +30,7 @@
|
||||
}, true);
|
||||
</script>
|
||||
|
||||
<script type="text/javascript">
|
||||
<script>
|
||||
function validateForm_doubleValidationAction() {
|
||||
var getFieldValue = function(field) {
|
||||
var type = field.type ? field.type : field[0].type;
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
<form id="myAction" name="myForm" onsubmit="submitMe()" action="myAction" method="post" enctype="myEncType" title="mytitle">
|
||||
<table class="wwFormTable"> <tr>
|
||||
<td class="tdLabel"></td>
|
||||
<td class="tdInput"> <script type="text/javascript" src="/static/optiontransferselect.js"></script>
|
||||
<td class="tdInput"> <script src="/static/optiontransferselect.js"></script>
|
||||
<table>
|
||||
<tr><td>
|
||||
<select name="myUpDownSelectTag" size="5" id="myAction_myUpDownSelectTag" multiple="multiple">
|
||||
@@ -20,9 +20,9 @@
|
||||
</form>
|
||||
|
||||
|
||||
<script type="text/javascript">
|
||||
<script>
|
||||
var containingForm = document.getElementById("myAction");
|
||||
StrutsUtils.addEventListener(containingForm, "submit",
|
||||
StrutsUtils.addEventListener(containingForm, "submit",
|
||||
function(evt) {
|
||||
var updownselectObj = document.getElementById("myAction_myUpDownSelectTag");
|
||||
selectAllOptionsExceptSome(updownselectObj, "key", "");
|
||||
|
||||
@@ -1,2 +1,2 @@
|
||||
<link rel="stylesheet" type="text/css" href="/static/xhtml/styles.css" nonce="r4nd0m"/>
|
||||
<script type="text/javascript" src="/static/utils.js" nonce="r4nd0m"></script>
|
||||
<script src="/static/utils.js" nonce="r4nd0m"></script>
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
<tr>
|
||||
<td class="tdLabel"></td>
|
||||
<td class="tdInput">
|
||||
<script type="text/javascript" src="/static/inputtransferselect.js" nonce="r4nd0m"></script>
|
||||
<script src="/static/inputtransferselect.js" nonce="r4nd0m"></script>
|
||||
<table>
|
||||
<tr><td><input type="text" name="collection_input" id="collection_input"/></td>
|
||||
<td class="tdTransferSelect"><inputtype="button"value="->"onclick="addOption(document.getElementById('collection_input'),document.getElementById('collection'))"/>
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
<tr>
|
||||
<td class="tdLabel"></td>
|
||||
<td class="tdInput">
|
||||
<script type="text/javascript" src="/static/inputtransferselect.js"></script>
|
||||
<script src="/static/inputtransferselect.js"></script>
|
||||
<table>
|
||||
<tr>
|
||||
<td><input type="text" name="collection_input" id="collection_input" collection-name="inputName" /></td>
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
<tr>
|
||||
<td class="tdLabel"></td>
|
||||
<tr>
|
||||
<td class="tdLabel"></td>
|
||||
<td class="tdInput">
|
||||
<script type="text/javascript" src="/static/optiontransferselect.js" nonce="r4nd0m"></script>
|
||||
<script src="/static/optiontransferselect.js" nonce="r4nd0m"></script>
|
||||
<table>
|
||||
<tr>
|
||||
<td>
|
||||
@@ -24,15 +24,15 @@
|
||||
<input type="button" class="buttonCssClass" style="buttonCssStyle"
|
||||
value="All Left" onclick="moveAllOptions(document.getElementById('doubleId'), document.getElementById('id'), false, 'Double Header Key', '');" /><br/><br/>
|
||||
<input type="button" class="buttonCssClass" style="buttonCssStyle"
|
||||
value="All Right" onclick="moveAllOptions(document.getElementById('id'), document.getElementById('doubleId'), false, 'Header Key', '');" /><br/><br/>
|
||||
value="All Right" onclick="moveAllOptions(document.getElementById('id'), document.getElementById('doubleId'), false, 'Header Key', '');" /><br/><br/>
|
||||
<input type="button" class="buttonCssClass" style="buttonCssStyle"
|
||||
value="Select All" onclick="selectAllOptionsExceptSome(document.getElementById('id'), 'key', 'Header Key');selectAllOptionsExceptSome(document.getElementById('doubleId'), 'key', 'Double Header Key');" /><br/><br/>
|
||||
</td>
|
||||
</td>
|
||||
<td>
|
||||
<label for="rightTitle">Title Right</label><br/>
|
||||
<select
|
||||
<select
|
||||
name="list2"
|
||||
size="20"
|
||||
size="20"
|
||||
multiple="multiple"
|
||||
id="doubleId"
|
||||
class="c2"
|
||||
@@ -49,5 +49,5 @@
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</td>
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
<tr>
|
||||
<tr>
|
||||
<td class="tdLabel"></td>
|
||||
<td class="tdInput">
|
||||
<script type="text/javascript" src="/static/optiontransferselect.js"></script>
|
||||
<script src="/static/optiontransferselect.js"></script>
|
||||
<table>
|
||||
<tr>
|
||||
<td>
|
||||
@@ -25,14 +25,14 @@
|
||||
<input type="button" class="buttonCssClass" style="buttonCssStyle"
|
||||
value="All Left" onclick="moveAllOptions(document.getElementById('doubleId'), document.getElementById('id'), false, 'Double Header Key', '');" /><br/><br/>
|
||||
<input type="button" class="buttonCssClass" style="buttonCssStyle"
|
||||
value="All Right" onclick="moveAllOptions(document.getElementById('id'), document.getElementById('doubleId'), false, 'Header Key', '');" /><br/><br/>
|
||||
value="All Right" onclick="moveAllOptions(document.getElementById('id'), document.getElementById('doubleId'), false, 'Header Key', '');" /><br/><br/>
|
||||
<input type="button" class="buttonCssClass" style="buttonCssStyle"
|
||||
value="Select All" onclick="selectAllOptionsExceptSome(document.getElementById('id'), 'key', 'Header Key');selectAllOptionsExceptSome(document.getElementById('doubleId'), 'key', 'Double Header Key');" /><br/><br/>
|
||||
</td> <td>
|
||||
<label for="rightTitle">Title Right</label><br/>
|
||||
<select
|
||||
<select
|
||||
name="list2"
|
||||
size="20"
|
||||
size="20"
|
||||
multiple="multiple"
|
||||
id="doubleId"
|
||||
>
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
<tr>
|
||||
<tr>
|
||||
<td class="tdLabel"></td>
|
||||
<td class="tdInput">
|
||||
<script type="text/javascript" src="/static/optiontransferselect.js"></script>
|
||||
<script src="/static/optiontransferselect.js"></script>
|
||||
<table>
|
||||
<tr>
|
||||
<td>
|
||||
@@ -24,15 +24,15 @@
|
||||
<input type="button" class="buttonCssClass" style="buttonCssStyle"
|
||||
value="All Left" onclick="moveAllOptions(document.getElementById('doubleId'), document.getElementById('id'), false, '');" /><br/><br/>
|
||||
<input type="button" class="buttonCssClass" style="buttonCssStyle"
|
||||
value="All Right" onclick="moveAllOptions(document.getElementById('id'), document.getElementById('doubleId'), false, '');" /><br/><br/>
|
||||
value="All Right" onclick="moveAllOptions(document.getElementById('id'), document.getElementById('doubleId'), false, '');" /><br/><br/>
|
||||
<input type="button" class="buttonCssClass" style="buttonCssStyle"
|
||||
value="Select All" onclick="selectAllOptions(document.getElementById('id'));selectAllOptions(document.getElementById('doubleId'));" /><br/><br/>
|
||||
</td>
|
||||
<td>
|
||||
<label for="rightTitle">Title Right</label><br/>
|
||||
<select
|
||||
<select
|
||||
name="list2"
|
||||
size="20"
|
||||
size="20"
|
||||
multiple="multiple"
|
||||
id="doubleId"
|
||||
>
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
<tr>
|
||||
<tr>
|
||||
<td class="tdLabel"></td>
|
||||
<td class="tdInput">
|
||||
<script type="text/javascript" src="/static/optiontransferselect.js"></script>
|
||||
<script src="/static/optiontransferselect.js"></script>
|
||||
<table>
|
||||
<tr>
|
||||
<td>
|
||||
@@ -25,15 +25,15 @@
|
||||
<input type="button" class="buttonCssClass" style="buttonCssStyle"
|
||||
value="All Left" onclick="moveAllOptions(document.getElementById('doubleId'), document.getElementById('id'), false, '');" /><br/><br/>
|
||||
<input type="button" class="buttonCssClass" style="buttonCssStyle"
|
||||
value="All Right" onclick="moveAllOptions(document.getElementById('id'), document.getElementById('doubleId'), false, 'Header Key', '');" /><br/><br/>
|
||||
value="All Right" onclick="moveAllOptions(document.getElementById('id'), document.getElementById('doubleId'), false, 'Header Key', '');" /><br/><br/>
|
||||
<input type="button" class="buttonCssClass" style="buttonCssStyle"
|
||||
value="Select All" onclick="selectAllOptionsExceptSome(document.getElementById('id'), 'key', 'Header Key');selectAllOptions(document.getElementById('doubleId'));" /><br/><br/>
|
||||
</td>
|
||||
<td>
|
||||
<label for="rightTitle">Title Right</label><br/>
|
||||
<select
|
||||
<select
|
||||
name="list2"
|
||||
size="20"
|
||||
size="20"
|
||||
multiple="multiple"
|
||||
id="doubleId"
|
||||
>
|
||||
@@ -45,7 +45,7 @@
|
||||
<input type="hidden" id="__multiselect_doubleId" name="__multiselect_list2" value=""/>
|
||||
<input type="button" onclick="moveOptionDown(document.getElementById('doubleId'), 'key', '');" value="v" />
|
||||
<input type="button" onclick="moveOptionUp(document.getElementById('doubleId'), 'key', '');" value="^" />
|
||||
|
||||
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
<tr>
|
||||
<td class="tdLabel"></td>
|
||||
<tr>
|
||||
<td class="tdLabel"></td>
|
||||
<td class="tdInput">
|
||||
<script type="text/javascript" src="/static/optiontransferselect.js"></script>
|
||||
<script src="/static/optiontransferselect.js"></script>
|
||||
<table>
|
||||
<tr>
|
||||
<td>
|
||||
@@ -24,15 +24,15 @@
|
||||
<input type="button" class="buttonCssClass" style="buttonCssStyle"
|
||||
value="All Left" onclick="moveAllOptions(document.getElementById('doubleId'), document.getElementById('id'), false, 'Double Header Key', '');" /><br/><br/>
|
||||
<input type="button" class="buttonCssClass" style="buttonCssStyle"
|
||||
value="All Right" onclick="moveAllOptions(document.getElementById('id'), document.getElementById('doubleId'), false, 'Header Key', '');" /><br/><br/>
|
||||
value="All Right" onclick="moveAllOptions(document.getElementById('id'), document.getElementById('doubleId'), false, 'Header Key', '');" /><br/><br/>
|
||||
<input type="button" class="buttonCssClass" style="buttonCssStyle"
|
||||
value="Select All" onclick="selectAllOptionsExceptSome(document.getElementById('id'), 'key', 'Header Key');selectAllOptionsExceptSome(document.getElementById('doubleId'), 'key', 'Double Header Key');" /><br/><br/>
|
||||
</td>
|
||||
<td>
|
||||
<label for="rightTitle">Title Right</label><br/>
|
||||
<select
|
||||
<select
|
||||
name="list2"
|
||||
size="20"
|
||||
size="20"
|
||||
multiple="multiple"
|
||||
id="doubleId"
|
||||
>
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
<tr>
|
||||
<tr>
|
||||
<td class="tdLabel"></td>
|
||||
<td class="tdInput">
|
||||
<script type="text/javascript" src="/static/optiontransferselect.js"></script>
|
||||
<script src="/static/optiontransferselect.js"></script>
|
||||
<table>
|
||||
<tr>
|
||||
<td>
|
||||
@@ -25,15 +25,15 @@
|
||||
<input type="button" class="buttonCssClass" style="buttonCssStyle"
|
||||
value="All Left" onclick="moveAllOptions(document.getElementById('doubleId'), document.getElementById('id'), false, 'Double Header Key', '');" /><br/><br/>
|
||||
<input type="button" class="buttonCssClass" style="buttonCssStyle"
|
||||
value="All Right" onclick="moveAllOptions(document.getElementById('id'), document.getElementById('doubleId'), false, 'Header Key', '');" /><br/><br/>
|
||||
value="All Right" onclick="moveAllOptions(document.getElementById('id'), document.getElementById('doubleId'), false, 'Header Key', '');" /><br/><br/>
|
||||
<input type="button" class="buttonCssClass" style="buttonCssStyle"
|
||||
value="Select All" onclick="selectAllOptionsExceptSome(document.getElementById('id'), 'key', 'Header Key');selectAllOptionsExceptSome(document.getElementById('doubleId'), 'key', 'Double Header Key');" /><br/><br/>
|
||||
</td>
|
||||
<td>
|
||||
<label for="rightTitle">Title Right</label><br/>
|
||||
<select
|
||||
<select
|
||||
name="list2"
|
||||
size="20"
|
||||
size="20"
|
||||
multiple="multiple"
|
||||
id="doubleId"
|
||||
>
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
<tr>
|
||||
<tr>
|
||||
<td class="tdLabel"></td>
|
||||
<td class="tdInput">
|
||||
<script type="text/javascript" src="/static/optiontransferselect.js"></script>
|
||||
<script src="/static/optiontransferselect.js"></script>
|
||||
<table>
|
||||
<tr>
|
||||
<td>
|
||||
@@ -22,12 +22,12 @@
|
||||
value="Left" onclick="moveSelectedOptions(document.getElementById('doubleId'), document.getElementById('id'), false, 'Double Header Key', '');alert('Moving Left');" /><br/><br/>
|
||||
<input type="button" class="buttonCssClass" style="buttonCssStyle"
|
||||
value="Right" onclick="moveSelectedOptions(document.getElementById('id'), document.getElementById('doubleId'), false, 'Header Key', '');alert('Moving Right');" /><br/><br/>
|
||||
</td>
|
||||
</td>
|
||||
<td>
|
||||
<label for="rightTitle">Title Right</label><br/>
|
||||
<select
|
||||
<select
|
||||
name="list2"
|
||||
size="20"
|
||||
size="20"
|
||||
multiple="multiple"
|
||||
id="doubleId"
|
||||
>
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
<tr>
|
||||
<td class="tdLabel"></td>
|
||||
<td class="tdInput">
|
||||
<script type="text/javascript" src="/static/optiontransferselect.js"></script>
|
||||
<script src="/static/optiontransferselect.js"></script>
|
||||
<table>
|
||||
<tr>
|
||||
<td>
|
||||
|
||||
@@ -15,7 +15,7 @@
|
||||
</table>
|
||||
</form>
|
||||
<!--javascript that is needed for tooltips-->
|
||||
<script type="text/javascript" src="/static/domTT.js"></script>
|
||||
<script src="/static/domTT.js"></script>
|
||||
<link rel="stylesheet" type="text/css" href="/static/domTT.css"/>
|
||||
|
||||
|
||||
|
||||
@@ -15,5 +15,5 @@
|
||||
</table>
|
||||
</form>
|
||||
<!--javascript that is needed for tooltips-->
|
||||
<script type="text/javascript" src="/static/domTT.js"></script>
|
||||
<script src="/static/domTT.js"></script>
|
||||
<link rel="stylesheet" type="text/css" href="/static/domTT.css"/>
|
||||
|
||||
@@ -16,6 +16,6 @@
|
||||
</form>
|
||||
|
||||
<!--javascript that is needed for tooltips-->
|
||||
<script type="text/javascript" src="/static/domTT.js"></script>
|
||||
<script src="/static/domTT.js"></script>
|
||||
<link rel="stylesheet" type="text/css" href="/static/domTT.css"/>
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<tr> <td class="tdLabel"></td> <td class="tdInput">
|
||||
<script type="text/javascript" src="/static/optiontransferselect.js"></script>
|
||||
<script src="/static/optiontransferselect.js"></script>
|
||||
<table>
|
||||
<tr><td>
|
||||
<select name="myName" size="5" id="myId" multiple="multiple">
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
<tr> <td class="tdLabel"></td> <td class="tdInput">
|
||||
<script type="text/javascript" src="/static/optiontransferselect.js"></script>
|
||||
<script src="/static/optiontransferselect.js"></script>
|
||||
<table>
|
||||
<tr><td>
|
||||
<select name="myName" size="5" id="myId" multiple="multiple">
|
||||
<option value=""></option>
|
||||
<option value=""></option>
|
||||
<option value="england">England</option>
|
||||
<option value="america" selected="selected">America</option>
|
||||
<option value="australia">Australia</option>
|
||||
@@ -12,9 +12,9 @@
|
||||
<input type="hidden" id="__multiselect_myId" name="__multiselect_myName" value=""/>
|
||||
</td></tr>
|
||||
<tr><td>
|
||||
<input type="button" value="Move Up" onclick="moveOptionUp(document.getElementById('myId'), 'key', '');" />
|
||||
<input type="button" value="v" onclick="moveOptionDown(document.getElementById('myId'), 'key', '');" />
|
||||
<input type="button" value="*" onclick="selectAllOptions(document.getElementById('myId'), 'key', '');" />
|
||||
<input type="button" value="Move Up" onclick="moveOptionUp(document.getElementById('myId'), 'key', '');" />
|
||||
<input type="button" value="v" onclick="moveOptionDown(document.getElementById('myId'), 'key', '');" />
|
||||
<input type="button" value="*" onclick="selectAllOptions(document.getElementById('myId'), 'key', '');" />
|
||||
</td></tr>
|
||||
</table>
|
||||
</td> </tr>
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<tr> <td class="tdLabel"></td> <td class="tdInput">
|
||||
<script type="text/javascript" src="/static/optiontransferselect.js"></script>
|
||||
<script src="/static/optiontransferselect.js"></script>
|
||||
<table>
|
||||
<tr><td>
|
||||
<select name="myName" size="5" id="myId" multiple="multiple">
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<tr> <td class="tdLabel"></td> <td class="tdInput">
|
||||
<script type="text/javascript" src="/static/optiontransferselect.js" nonce="r4nd0m"></script>
|
||||
<script src="/static/optiontransferselect.js" nonce="r4nd0m"></script>
|
||||
<table>
|
||||
<tr><td>
|
||||
<select name="myName" size="5" id="myId" multiple="multiple">
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<tr> <td class="tdLabel"></td> <td class="tdInput">
|
||||
<script type="text/javascript" src="/static/optiontransferselect.js"></script>
|
||||
<script src="/static/optiontransferselect.js"></script>
|
||||
<table>
|
||||
<tr><td>
|
||||
<select name="myName" size="5" id="myId" multiple="multiple">
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<tr> <td class="tdLabel"></td> <td class="tdInput">
|
||||
<script type="text/javascript" src="/static/optiontransferselect.js"></script>
|
||||
<script src="/static/optiontransferselect.js"></script>
|
||||
<table>
|
||||
<tr><td>
|
||||
<select name="myName" size="5" id="myId" multiple="multiple">
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<tr> <td class="tdLabel"></td> <td class="tdInput">
|
||||
<script type="text/javascript" src="/static/optiontransferselect.js"></script>
|
||||
<script src="/static/optiontransferselect.js"></script>
|
||||
<table>
|
||||
<tr><td>
|
||||
<select name="myName" size="5" id="myId" multiple="multiple">
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<tr> <td class="tdLabel"></td> <td class="tdInput">
|
||||
<script type="text/javascript" src="/static/optiontransferselect.js"></script>
|
||||
<script src="/static/optiontransferselect.js"></script>
|
||||
<table>
|
||||
<tr><td>
|
||||
<select name="myName" size="5" id="myId" multiple="multiple">
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<tr> <td class="tdLabel"></td> <td class="tdInput">
|
||||
<script type="text/javascript" src="/static/optiontransferselect.js"></script>
|
||||
<script src="/static/optiontransferselect.js"></script>
|
||||
<table>
|
||||
<tr><td>
|
||||
<select name="myName" size="5" id="myId" multiple="multiple">
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<tr> <td class="tdLabel"></td> <td class="tdInput">
|
||||
<script type="text/javascript" src="/static/optiontransferselect.js"></script>
|
||||
<script src="/static/optiontransferselect.js"></script>
|
||||
<table>
|
||||
<tr><td>
|
||||
<select name="myName" size="5" id="myId" multiple="multiple">
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<tr> <td class="tdLabel"></td> <td class="tdInput">
|
||||
<script type="text/javascript" src="/static/optiontransferselect.js"></script>
|
||||
<script src="/static/optiontransferselect.js"></script>
|
||||
<table>
|
||||
<tr><td>
|
||||
<select name="myName" size="5" id="myId" multiple="multiple">
|
||||
|
||||
+109
-81
@@ -24,14 +24,23 @@ import com.opensymphony.xwork2.FileManagerFactory;
|
||||
import com.opensymphony.xwork2.ObjectFactory;
|
||||
import com.opensymphony.xwork2.config.Configuration;
|
||||
import com.opensymphony.xwork2.config.ConfigurationException;
|
||||
import com.opensymphony.xwork2.config.entities.*;
|
||||
import com.opensymphony.xwork2.config.entities.ActionConfig;
|
||||
import com.opensymphony.xwork2.config.entities.ExceptionMappingConfig;
|
||||
import com.opensymphony.xwork2.config.entities.InterceptorMapping;
|
||||
import com.opensymphony.xwork2.config.entities.PackageConfig;
|
||||
import com.opensymphony.xwork2.config.entities.ResultConfig;
|
||||
import com.opensymphony.xwork2.inject.Container;
|
||||
import com.opensymphony.xwork2.inject.Inject;
|
||||
import com.opensymphony.xwork2.util.AnnotationUtils;
|
||||
import com.opensymphony.xwork2.util.TextParseUtil;
|
||||
import com.opensymphony.xwork2.util.WildcardHelper;
|
||||
import com.opensymphony.xwork2.util.classloader.ReloadingClassLoader;
|
||||
import com.opensymphony.xwork2.util.finder.*;
|
||||
import com.opensymphony.xwork2.util.finder.ClassFinder;
|
||||
import com.opensymphony.xwork2.util.finder.ClassFinderFactory;
|
||||
import com.opensymphony.xwork2.util.finder.ClassLoaderInterface;
|
||||
import com.opensymphony.xwork2.util.finder.ClassLoaderInterfaceDelegate;
|
||||
import com.opensymphony.xwork2.util.finder.Test;
|
||||
import com.opensymphony.xwork2.util.finder.UrlSet;
|
||||
import org.apache.commons.lang3.BooleanUtils;
|
||||
import org.apache.commons.lang3.ObjectUtils;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
@@ -39,26 +48,50 @@ import org.apache.logging.log4j.LogManager;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
import org.apache.struts2.StrutsConstants;
|
||||
import org.apache.struts2.StrutsException;
|
||||
import org.apache.struts2.convention.annotation.*;
|
||||
import org.apache.struts2.convention.annotation.Action;
|
||||
import org.apache.struts2.convention.annotation.Actions;
|
||||
import org.apache.struts2.convention.annotation.AllowedMethods;
|
||||
import org.apache.struts2.convention.annotation.DefaultInterceptorRef;
|
||||
import org.apache.struts2.convention.annotation.ExceptionMapping;
|
||||
import org.apache.struts2.convention.annotation.ExceptionMappings;
|
||||
import org.apache.struts2.convention.annotation.Namespace;
|
||||
import org.apache.struts2.convention.annotation.Namespaces;
|
||||
import org.apache.struts2.convention.annotation.ParentPackage;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.lang.reflect.Method;
|
||||
import java.lang.reflect.Modifier;
|
||||
import java.net.URL;
|
||||
import java.util.*;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* This class implements the ActionConfigBuilder interface.
|
||||
* </p>
|
||||
*/
|
||||
public class PackageBasedActionConfigBuilder implements ActionConfigBuilder {
|
||||
|
||||
private static final Logger LOG = LogManager.getLogger(PackageBasedActionConfigBuilder.class);
|
||||
|
||||
private static final String DEFAULT_ACTION_SUFFIX = "Action";
|
||||
private static final String DEFAULT_METHOD = "execute";
|
||||
|
||||
private static final boolean EXTRACT_BASE_INTERFACES = true;
|
||||
|
||||
/**
|
||||
* Pattern to match the whole path with sub-path as on JDK9+ getClassLoader().getResources("")
|
||||
* can return also a sub-path like "!/META-INF/versions/..."
|
||||
*/
|
||||
private static final String EXCLUDE_ALL_JARS_PATTERN = ".*?\\.jar(!/|/)?(.*)?";
|
||||
private static final String DEFAULT_SPLIT_PATTERN = "\\s*,\\s*";
|
||||
|
||||
private final Configuration configuration;
|
||||
private final ActionNameBuilder actionNameBuilder;
|
||||
private final ResultMapBuilder resultMapBuilder;
|
||||
@@ -66,6 +99,9 @@ public class PackageBasedActionConfigBuilder implements ActionConfigBuilder {
|
||||
private final ObjectFactory objectFactory;
|
||||
private final String defaultParentPackage;
|
||||
private final boolean redirectToSlash;
|
||||
private final Set<String> loadedFileUrls = new HashSet<>();
|
||||
private final boolean enableSmiInheritance;
|
||||
|
||||
private String[] actionPackages;
|
||||
private String[] excludePackages;
|
||||
private String[] packageLocators;
|
||||
@@ -73,20 +109,17 @@ public class PackageBasedActionConfigBuilder implements ActionConfigBuilder {
|
||||
private String packageLocatorsBasePackage;
|
||||
private boolean disableActionScanning = false;
|
||||
private boolean disablePackageLocatorsScanning = false;
|
||||
private Set<String> actionSuffix = Collections.singleton("Action");
|
||||
private Set<String> actionSuffix = Collections.singleton(DEFAULT_ACTION_SUFFIX);
|
||||
private boolean checkImplementsAction = true;
|
||||
private boolean mapAllMatches = false;
|
||||
private Set<String> loadedFileUrls = new HashSet<>();
|
||||
|
||||
private boolean devMode;
|
||||
private ReloadingClassLoader reloadingClassLoader;
|
||||
private boolean reload;
|
||||
private Set<String> fileProtocols;
|
||||
private Set<String> fileProtocols = Collections.emptySet();
|
||||
private boolean alwaysMapExecute;
|
||||
private boolean excludeParentClassLoader;
|
||||
private boolean slashesInActionNames;
|
||||
private boolean enableSmiInheritance;
|
||||
|
||||
private static final String DEFAULT_METHOD = "execute";
|
||||
private boolean eagerLoading = false;
|
||||
|
||||
private FileManager fileManager;
|
||||
@@ -95,16 +128,17 @@ public class PackageBasedActionConfigBuilder implements ActionConfigBuilder {
|
||||
/**
|
||||
* Constructs actions based on a list of packages.
|
||||
*
|
||||
* @param configuration The XWork configuration that the new package configs and action configs
|
||||
* are added to.
|
||||
* @param container Xwork Container
|
||||
* @param objectFactory The ObjectFactory used to create the actions and such.
|
||||
* @param redirectToSlash A boolean parameter that controls whether or not this will create an
|
||||
* action for indexes. If this is set to true, index actions are not created because
|
||||
* the unknown handler will redirect from /foo to /foo/. The only action that is created
|
||||
* is to the empty action in the namespace (e.g. the namespace /foo and the action "").
|
||||
* @param enableSmiInheritance A boolean parameter which determines if a newly created package config inherits the SMI value of its parent package config
|
||||
* @param defaultParentPackage The default parent package for all the configuration.
|
||||
* @param configuration The XWork configuration that the new package configs and action configs
|
||||
* are added to.
|
||||
* @param container Xwork Container
|
||||
* @param objectFactory The ObjectFactory used to create the actions and such.
|
||||
* @param redirectToSlash A boolean parameter that controls whether or not this will create an
|
||||
* action for indexes. If this is set to true, index actions are not created because
|
||||
* the unknown handler will redirect from /foo to /foo/. The only action that is created
|
||||
* is to the empty action in the namespace (e.g. the namespace /foo and the action "").
|
||||
* @param enableSmiInheritance A boolean parameter, which determines if a newly created package config inherits
|
||||
* the SMI value of its parent package config
|
||||
* @param defaultParentPackage The default parent package for all the configuration.
|
||||
*/
|
||||
@Inject
|
||||
public PackageBasedActionConfigBuilder(Configuration configuration, Container container, ObjectFactory objectFactory,
|
||||
@@ -135,7 +169,7 @@ public class PackageBasedActionConfigBuilder implements ActionConfigBuilder {
|
||||
|
||||
/**
|
||||
* @param reload Reload configuration when classes change. Defaults to "false" and should not be used
|
||||
* in production.
|
||||
* in production.
|
||||
*/
|
||||
@Inject(ConventionConstants.CONVENTION_CLASSES_RELOAD)
|
||||
public void setReload(String reload) {
|
||||
@@ -157,8 +191,8 @@ public class PackageBasedActionConfigBuilder implements ActionConfigBuilder {
|
||||
}
|
||||
|
||||
/**
|
||||
* @param alwaysMapExecute If this constant is true, and there is an "execute" method(not annotated), a mapping will be added
|
||||
* pointing to it, even if there are other mapping in the class
|
||||
* @param alwaysMapExecute If this constant is true, and there is an "execute" method(not annotated), a mapping will be added
|
||||
* pointing to it, even if there are other mapping in the class
|
||||
*/
|
||||
@Inject(ConventionConstants.CONVENTION_ACTION_ALWAYS_MAP_EXECUTE)
|
||||
public void setAlwaysMapExecute(String alwaysMapExecute) {
|
||||
@@ -167,6 +201,7 @@ public class PackageBasedActionConfigBuilder implements ActionConfigBuilder {
|
||||
|
||||
/**
|
||||
* File URLs whose protocol are in these list will be processed as jars containing classes
|
||||
*
|
||||
* @param fileProtocols Comma separated list of file protocols that will be considered as jar files and scanned
|
||||
*/
|
||||
@Inject(ConventionConstants.CONVENTION_ACTION_FILE_PROTOCOLS)
|
||||
@@ -190,7 +225,7 @@ public class PackageBasedActionConfigBuilder implements ActionConfigBuilder {
|
||||
@Inject(value = ConventionConstants.CONVENTION_ACTION_INCLUDE_JARS, required = false)
|
||||
public void setIncludeJars(String includeJars) {
|
||||
if (StringUtils.isNotEmpty(includeJars)) {
|
||||
this.includeJars = includeJars.split("\\s*[,]\\s*");
|
||||
this.includeJars = includeJars.split(DEFAULT_SPLIT_PATTERN);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -209,13 +244,13 @@ public class PackageBasedActionConfigBuilder implements ActionConfigBuilder {
|
||||
@Inject(value = ConventionConstants.CONVENTION_ACTION_PACKAGES, required = false)
|
||||
public void setActionPackages(String actionPackages) {
|
||||
if (StringUtils.isNotBlank(actionPackages)) {
|
||||
this.actionPackages = actionPackages.split("\\s*[,]\\s*");
|
||||
this.actionPackages = actionPackages.split(DEFAULT_SPLIT_PATTERN);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param checkImplementsAction (Optional) Map classes that implement com.opensymphony.xwork2.Action
|
||||
* as actions
|
||||
* as actions
|
||||
*/
|
||||
@Inject(value = ConventionConstants.CONVENTION_ACTION_CHECK_IMPLEMENTS_ACTION, required = false)
|
||||
public void setCheckImplementsAction(String checkImplementsAction) {
|
||||
@@ -240,7 +275,7 @@ public class PackageBasedActionConfigBuilder implements ActionConfigBuilder {
|
||||
@Inject(value = ConventionConstants.CONVENTION_EXCLUDE_PACKAGES, required = false)
|
||||
public void setExcludePackages(String excludePackages) {
|
||||
if (StringUtils.isNotBlank(excludePackages)) {
|
||||
this.excludePackages = excludePackages.split("\\s*[,]\\s*");
|
||||
this.excludePackages = excludePackages.split(DEFAULT_SPLIT_PATTERN);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -249,7 +284,7 @@ public class PackageBasedActionConfigBuilder implements ActionConfigBuilder {
|
||||
*/
|
||||
@Inject(value = ConventionConstants.CONVENTION_PACKAGE_LOCATORS, required = false)
|
||||
public void setPackageLocators(String packageLocators) {
|
||||
this.packageLocators = packageLocators.split("\\s*[,]\\s*");
|
||||
this.packageLocators = packageLocators.split(DEFAULT_SPLIT_PATTERN);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -273,7 +308,7 @@ public class PackageBasedActionConfigBuilder implements ActionConfigBuilder {
|
||||
|
||||
/**
|
||||
* @param eagerLoading (Optional) If set, found action classes will be instantiated by the ObjectFactory to accelerate future use
|
||||
* setting it up can clash with Spring managed beans
|
||||
* setting it up can clash with Spring managed beans
|
||||
*/
|
||||
@Inject(value = ConventionConstants.CONVENTION_ACTION_EAGER_LOADING, required = false)
|
||||
public void setEagerLoading(String eagerLoading) {
|
||||
@@ -293,7 +328,7 @@ public class PackageBasedActionConfigBuilder implements ActionConfigBuilder {
|
||||
protected void initReloadClassLoader() {
|
||||
//when the configuration is reloaded, a new classloader will be setup
|
||||
if (isReloadEnabled() && reloadingClassLoader == null)
|
||||
reloadingClassLoader = new ReloadingClassLoader(getClassLoader());
|
||||
reloadingClassLoader = new ReloadingClassLoader(getClassLoader());
|
||||
}
|
||||
|
||||
protected ClassLoader getClassLoader() {
|
||||
@@ -324,17 +359,17 @@ public class PackageBasedActionConfigBuilder implements ActionConfigBuilder {
|
||||
if (LOG.isTraceEnabled()) {
|
||||
LOG.trace("Loading action configurations");
|
||||
if (actionPackages != null) {
|
||||
LOG.trace("Actions being loaded from action packages: {}", actionPackages);
|
||||
LOG.trace("Actions being loaded from action packages: {}", (Object[]) actionPackages);
|
||||
}
|
||||
if (packageLocators != null) {
|
||||
LOG.trace("Actions being loaded using package locator's: {}", packageLocators);
|
||||
LOG.trace("Actions being loaded using package locator's: {}", (Object[]) packageLocators);
|
||||
}
|
||||
if (excludePackages != null) {
|
||||
LOG.trace("Excluding actions from packages: {}", excludePackages);
|
||||
LOG.trace("Excluding actions from packages: {}", (Object[]) excludePackages);
|
||||
}
|
||||
}
|
||||
|
||||
Set<Class> classes = findActions();
|
||||
Set<Class<?>> classes = findActions();
|
||||
buildConfiguration(classes);
|
||||
}
|
||||
}
|
||||
@@ -342,8 +377,7 @@ public class PackageBasedActionConfigBuilder implements ActionConfigBuilder {
|
||||
protected ClassLoaderInterface getClassLoaderInterface() {
|
||||
if (isReloadEnabled()) {
|
||||
return new ClassLoaderInterfaceDelegate(this.reloadingClassLoader);
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
/*
|
||||
if there is a ClassLoaderInterface in the context, use it, otherwise
|
||||
default to the default ClassLoaderInterface (a wrapper around the current
|
||||
@@ -365,9 +399,8 @@ public class PackageBasedActionConfigBuilder implements ActionConfigBuilder {
|
||||
return devMode && reload;
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
protected Set<Class> findActions() {
|
||||
Set<Class> classes = new HashSet<>();
|
||||
protected Set<Class<?>> findActions() {
|
||||
Set<Class<?>> classes = new HashSet<>();
|
||||
try {
|
||||
if (actionPackages != null || (packageLocators != null && !disablePackageLocatorsScanning)) {
|
||||
|
||||
@@ -438,30 +471,26 @@ public class PackageBasedActionConfigBuilder implements ActionConfigBuilder {
|
||||
}
|
||||
|
||||
//try to find classes dirs inside war files
|
||||
urlSet = urlSet.includeClassesUrl(classLoaderInterface, new UrlSet.FileProtocolNormalizer() {
|
||||
public URL normalizeToFileProtocol(URL url) {
|
||||
return fileManager.normalizeToFileProtocol(url);
|
||||
}
|
||||
});
|
||||
|
||||
urlSet = urlSet.includeClassesUrl(classLoaderInterface, url -> fileManager.normalizeToFileProtocol(url));
|
||||
|
||||
urlSet = urlSet.excludeJavaExtDirs()
|
||||
.excludeJavaEndorsedDirs()
|
||||
.excludeUserExtensionsDir();
|
||||
.excludeJavaEndorsedDirs()
|
||||
.excludeUserExtensionsDir();
|
||||
|
||||
try {
|
||||
urlSet = urlSet.excludeJavaHome();
|
||||
urlSet = urlSet.excludeJavaHome();
|
||||
} catch (NullPointerException e) {
|
||||
// This happens in GAE since the sandbox contains no java.home directory
|
||||
LOG.warn("Could not exclude JAVA_HOME, is this a sandbox jvm?");
|
||||
// This happens in GAE since the sandbox contains no java.home directory
|
||||
LOG.warn("Could not exclude JAVA_HOME, is this a sandbox jvm?");
|
||||
}
|
||||
urlSet = urlSet.excludePaths(System.getProperty("sun.boot.class.path", ""));
|
||||
urlSet = urlSet.exclude(".*/JavaVM.framework/.*");
|
||||
|
||||
if (includeJars == null) {
|
||||
urlSet = urlSet.exclude(".*?\\.jar(!/|/)?");
|
||||
LOG.debug("\"{}\" is not defined, excluding all JAR files!", ConventionConstants.CONVENTION_ACTION_INCLUDE_JARS);
|
||||
urlSet = urlSet.exclude(EXCLUDE_ALL_JARS_PATTERN);
|
||||
} else {
|
||||
if(LOG.isDebugEnabled()) {
|
||||
if (LOG.isDebugEnabled()) {
|
||||
LOG.debug("jar urls regexes were specified: {}", Arrays.asList(includeJars));
|
||||
}
|
||||
List<URL> rawIncludedUrls = urlSet.getUrls();
|
||||
@@ -509,7 +538,7 @@ public class PackageBasedActionConfigBuilder implements ActionConfigBuilder {
|
||||
*
|
||||
* @param className the name of the class to test
|
||||
* @return true if the specified class should be included in the
|
||||
* package-based action scan
|
||||
* package-based action scan
|
||||
*/
|
||||
protected boolean includeClassNameInActionScan(String className) {
|
||||
String classPackageName = StringUtils.substringBeforeLast(className, ".");
|
||||
@@ -523,15 +552,15 @@ public class PackageBasedActionConfigBuilder implements ActionConfigBuilder {
|
||||
* @return false if class package is on the {@link #excludePackages} list
|
||||
*/
|
||||
protected boolean checkExcludePackages(String classPackageName) {
|
||||
if(excludePackages != null && excludePackages.length > 0) {
|
||||
if (excludePackages != null && excludePackages.length > 0) {
|
||||
WildcardHelper wildcardHelper = new WildcardHelper();
|
||||
|
||||
//we really don't care about the results, just the boolean
|
||||
Map<String, String> matchMap = new HashMap<>();
|
||||
|
||||
for(String packageExclude : excludePackages) {
|
||||
for (String packageExclude : excludePackages) {
|
||||
int[] packagePattern = wildcardHelper.compilePattern(packageExclude);
|
||||
if(wildcardHelper.match(matchMap, classPackageName, packagePattern)) {
|
||||
if (wildcardHelper.match(matchMap, classPackageName, packagePattern)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -564,9 +593,9 @@ public class PackageBasedActionConfigBuilder implements ActionConfigBuilder {
|
||||
* @return true if class package is on the {@link #packageLocators} list
|
||||
*/
|
||||
protected boolean checkPackageLocators(String classPackageName) {
|
||||
if (packageLocators != null && !disablePackageLocatorsScanning && classPackageName.length() > 0
|
||||
if (packageLocators != null && !disablePackageLocatorsScanning && !classPackageName.isEmpty()
|
||||
&& (packageLocatorsBasePackage == null || classPackageName
|
||||
.startsWith(packageLocatorsBasePackage))) {
|
||||
.startsWith(packageLocatorsBasePackage))) {
|
||||
for (String packageLocator : packageLocators) {
|
||||
String[] splitted = classPackageName.split("\\.");
|
||||
|
||||
@@ -586,14 +615,10 @@ public class PackageBasedActionConfigBuilder implements ActionConfigBuilder {
|
||||
* is to return the result of {@link #includeClassNameInActionScan(String)}.
|
||||
*
|
||||
* @return a {@link Test} object that returns true if the specified class
|
||||
* name should be included in the package scan
|
||||
* name should be included in the package scan
|
||||
*/
|
||||
protected Test<String> getClassPackageTest() {
|
||||
return new Test<String>() {
|
||||
public boolean test(String className) {
|
||||
return includeClassNameInActionScan(className);
|
||||
}
|
||||
};
|
||||
return this::includeClassNameInActionScan;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -604,7 +629,7 @@ public class PackageBasedActionConfigBuilder implements ActionConfigBuilder {
|
||||
* super-classes of the specified class.
|
||||
*
|
||||
* @return a {@link Test} object that returns true if the specified class
|
||||
* should be included in the package scan
|
||||
* should be included in the package scan
|
||||
*/
|
||||
protected Test<ClassFinder.ClassInfo> getActionClassTest() {
|
||||
return new Test<ClassFinder.ClassInfo>() {
|
||||
@@ -638,8 +663,7 @@ public class PackageBasedActionConfigBuilder implements ActionConfigBuilder {
|
||||
};
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
protected void buildConfiguration(Set<Class> classes) {
|
||||
protected void buildConfiguration(Set<Class<?>> classes) {
|
||||
Map<String, PackageConfig.Builder> packageConfigs = new HashMap<>();
|
||||
|
||||
for (Class<?> actionClass : classes) {
|
||||
@@ -754,7 +778,7 @@ public class PackageBasedActionConfigBuilder implements ActionConfigBuilder {
|
||||
|
||||
private Set<String> getAllowedMethods(Class<?> actionClass) {
|
||||
List<AllowedMethods> annotations = AnnotationUtils.findAnnotations(actionClass, AllowedMethods.class);
|
||||
if (annotations == null || annotations.isEmpty()) {
|
||||
if (annotations.isEmpty()) {
|
||||
return Collections.emptySet();
|
||||
} else {
|
||||
Set<String> methods = new HashSet<>();
|
||||
@@ -767,6 +791,7 @@ public class PackageBasedActionConfigBuilder implements ActionConfigBuilder {
|
||||
|
||||
/**
|
||||
* Interfaces, enums, annotations, and abstract classes cannot be instantiated.
|
||||
*
|
||||
* @param actionClass class to check
|
||||
* @return returns true if the class cannot be instantiated or should be ignored
|
||||
*/
|
||||
@@ -885,7 +910,7 @@ public class PackageBasedActionConfigBuilder implements ActionConfigBuilder {
|
||||
} else {
|
||||
Action ann = method.getAnnotation(Action.class);
|
||||
if (ann != null) {
|
||||
map.put(method.getName(), Arrays.asList(ann));
|
||||
map.put(method.getName(), Collections.singletonList(ann));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -894,7 +919,8 @@ public class PackageBasedActionConfigBuilder implements ActionConfigBuilder {
|
||||
}
|
||||
|
||||
/**
|
||||
* Builds a list of actions from an @Actions annotation, and check that they are not all empty
|
||||
* Builds a list of actions from an @Actions annotation, and check that they are not all empty
|
||||
*
|
||||
* @param actionsAnnotation Actions annotation
|
||||
* @return a list of Actions
|
||||
*/
|
||||
@@ -926,12 +952,12 @@ public class PackageBasedActionConfigBuilder implements ActionConfigBuilder {
|
||||
*/
|
||||
protected void createActionConfig(PackageConfig.Builder pkgCfg, Class<?> actionClass, String actionName,
|
||||
String actionMethod, Action annotation, Set<String> allowedMethods) {
|
||||
String className = actionClass.getName();
|
||||
String className = actionClass.getName();
|
||||
if (annotation != null) {
|
||||
actionName = annotation.value().equals(Action.DEFAULT_VALUE) ? actionName : annotation.value();
|
||||
actionName = StringUtils.contains(actionName, "/") && !slashesInActionNames ? StringUtils.substringAfterLast(actionName, "/") : actionName;
|
||||
if(!Action.DEFAULT_VALUE.equals(annotation.className())){
|
||||
className = annotation.className();
|
||||
if (!Action.DEFAULT_VALUE.equals(annotation.className())) {
|
||||
className = annotation.className();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -988,8 +1014,10 @@ public class PackageBasedActionConfigBuilder implements ActionConfigBuilder {
|
||||
//watch class file
|
||||
if (isReloadEnabled()) {
|
||||
URL classFile = actionClass.getResource(actionClass.getSimpleName() + ".class");
|
||||
fileManager.monitorFile(classFile);
|
||||
loadedFileUrls.add(classFile.toString());
|
||||
if (classFile != null) {
|
||||
fileManager.monitorFile(classFile);
|
||||
loadedFileUrls.add(classFile.toString());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -998,7 +1026,7 @@ public class PackageBasedActionConfigBuilder implements ActionConfigBuilder {
|
||||
|
||||
for (ExceptionMapping exceptionMapping : exceptions) {
|
||||
LOG.trace("Mapping exception [{}] to result [{}] for action [{}]", exceptionMapping.exception(),
|
||||
exceptionMapping.result(), actionName);
|
||||
exceptionMapping.result(), actionName);
|
||||
ExceptionMappingConfig.Builder builder = new ExceptionMappingConfig.Builder(null, exceptionMapping
|
||||
.exception(), exceptionMapping.result());
|
||||
builder.addParams(StringTools.createParameterMap(exceptionMapping.params()));
|
||||
@@ -1009,8 +1037,8 @@ public class PackageBasedActionConfigBuilder implements ActionConfigBuilder {
|
||||
}
|
||||
|
||||
protected PackageConfig.Builder getPackageConfig(final Map<String, PackageConfig.Builder> packageConfigs,
|
||||
String actionNamespace, final String actionPackage, final Class<?> actionClass,
|
||||
Action action) {
|
||||
String actionNamespace, final String actionPackage, final Class<?> actionClass,
|
||||
Action action) {
|
||||
if (action != null && !action.value().equals(Action.DEFAULT_VALUE)) {
|
||||
LOG.trace("Using non-default action namespace from the Action annotation of [{}]", action.value());
|
||||
String actionName = action.value();
|
||||
@@ -1067,7 +1095,7 @@ public class PackageBasedActionConfigBuilder implements ActionConfigBuilder {
|
||||
|
||||
/**
|
||||
* Determine all the index handling actions and results based on this logic:
|
||||
*
|
||||
* <p>
|
||||
* 1. Loop over all the namespaces such as /foo and see if it has an action named index
|
||||
* 2. If an action doesn't exists in the parent namespace of the same name, create an action
|
||||
* in the parent namespace of the same name as the namespace that points to the index
|
||||
|
||||
+10
-4
@@ -19,6 +19,7 @@
|
||||
package org.apache.struts2.views.velocity;
|
||||
|
||||
import com.opensymphony.xwork2.util.ValueStack;
|
||||
import org.apache.struts2.util.ValueStackProvider;
|
||||
import org.apache.velocity.VelocityContext;
|
||||
|
||||
import java.util.ArrayList;
|
||||
@@ -26,7 +27,7 @@ import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.function.Function;
|
||||
|
||||
public class StrutsVelocityContext extends VelocityContext {
|
||||
public class StrutsVelocityContext extends VelocityContext implements ValueStackProvider {
|
||||
|
||||
private final ValueStack stack;
|
||||
private final List<VelocityContext> chainedContexts;
|
||||
@@ -77,10 +78,10 @@ public class StrutsVelocityContext extends VelocityContext {
|
||||
}
|
||||
|
||||
protected List<Function<String, Object>> contextGetterList() {
|
||||
return Arrays.asList(this::superGet, this::chainedContextGet, this::stackGet);
|
||||
return Arrays.asList(this::superInternalGet, this::chainedContextGet, this::stackGet);
|
||||
}
|
||||
|
||||
protected Object superGet(String key) {
|
||||
protected Object superInternalGet(String key) {
|
||||
return super.internalGet(key);
|
||||
}
|
||||
|
||||
@@ -96,11 +97,16 @@ public class StrutsVelocityContext extends VelocityContext {
|
||||
return null;
|
||||
}
|
||||
for (VelocityContext chainedContext : chainedContexts) {
|
||||
Object val = chainedContext.internalGet(key);
|
||||
Object val = chainedContext.get(key);
|
||||
if (val != null) {
|
||||
return val;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ValueStack getValueStack() {
|
||||
return stack;
|
||||
}
|
||||
}
|
||||
|
||||
+39
-13
@@ -18,26 +18,28 @@
|
||||
*/
|
||||
package org.apache.struts2.views.velocity.components;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.Writer;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
|
||||
import com.opensymphony.xwork2.inject.Container;
|
||||
import com.opensymphony.xwork2.util.ValueStack;
|
||||
import org.apache.struts2.ServletActionContext;
|
||||
import org.apache.struts2.components.Component;
|
||||
import org.apache.struts2.util.ValueStackProvider;
|
||||
import org.apache.struts2.views.util.ContextUtil;
|
||||
import org.apache.velocity.context.AbstractContext;
|
||||
import org.apache.velocity.context.Context;
|
||||
import org.apache.velocity.context.InternalContextAdapter;
|
||||
import org.apache.velocity.context.InternalWrapperContext;
|
||||
import org.apache.velocity.exception.MethodInvocationException;
|
||||
import org.apache.velocity.exception.ParseErrorException;
|
||||
import org.apache.velocity.exception.ResourceNotFoundException;
|
||||
import org.apache.velocity.runtime.directive.Directive;
|
||||
import org.apache.velocity.runtime.parser.node.Node;
|
||||
|
||||
import com.opensymphony.xwork2.ActionContext;
|
||||
import com.opensymphony.xwork2.inject.Container;
|
||||
import com.opensymphony.xwork2.util.ValueStack;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
import java.io.IOException;
|
||||
import java.io.Writer;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
public abstract class AbstractDirective extends Directive {
|
||||
public String getName() {
|
||||
@@ -57,8 +59,11 @@ public abstract class AbstractDirective extends Directive {
|
||||
protected abstract Component getBean(ValueStack stack, HttpServletRequest req, HttpServletResponse res);
|
||||
|
||||
public boolean render(InternalContextAdapter ctx, Writer writer, Node node) throws IOException, ResourceNotFoundException, ParseErrorException, MethodInvocationException {
|
||||
// get the bean
|
||||
ValueStack stack = (ValueStack) ctx.get("stack");
|
||||
ValueStack stack = extractValueStack(ctx);
|
||||
if (stack == null) {
|
||||
// Fallback to assuming the ValueStack was put into the Velocity context (as is by default)
|
||||
stack = (ValueStack) ctx.get(ContextUtil.STACK);
|
||||
}
|
||||
HttpServletRequest req = (HttpServletRequest) stack.getContext().get(ServletActionContext.HTTP_REQUEST);
|
||||
HttpServletResponse res = (HttpServletResponse) stack.getContext().get(ServletActionContext.HTTP_RESPONSE);
|
||||
Component bean = getBean(stack, req, res);
|
||||
@@ -79,6 +84,27 @@ public abstract class AbstractDirective extends Directive {
|
||||
return true;
|
||||
}
|
||||
|
||||
private ValueStack extractValueStack(Context context) {
|
||||
do {
|
||||
if (context instanceof ValueStackProvider) {
|
||||
return ((ValueStackProvider) context).getValueStack();
|
||||
}
|
||||
context = extractContext(context);
|
||||
} while (context != null);
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private Context extractContext(Context context) {
|
||||
if (context instanceof InternalWrapperContext) {
|
||||
return ((InternalWrapperContext) context).getInternalUserContext();
|
||||
}
|
||||
if (context instanceof AbstractContext) {
|
||||
return ((AbstractContext) context).getChainedContext();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* Create a Map of properties that the user has passed in. For example:
|
||||
|
||||
+2
-2
@@ -54,7 +54,7 @@ public class StrutsVelocityContextTest {
|
||||
|
||||
@Test
|
||||
public void getChainedValue() {
|
||||
when(chainedContext.internalGet("foo")).thenReturn("bar");
|
||||
when(chainedContext.get("foo")).thenReturn("bar");
|
||||
assertEquals("bar", strutsVelocityContext.internalGet("foo"));
|
||||
}
|
||||
|
||||
@@ -75,7 +75,7 @@ public class StrutsVelocityContextTest {
|
||||
when(stack.findValue("foo")).thenReturn("qux");
|
||||
assertEquals("qux", strutsVelocityContext.internalGet("foo"));
|
||||
|
||||
when(chainedContext.internalGet("foo")).thenReturn("baz");
|
||||
when(chainedContext.get("foo")).thenReturn("baz");
|
||||
assertEquals("baz", strutsVelocityContext.internalGet("foo"));
|
||||
|
||||
strutsVelocityContext.put("foo", "bar");
|
||||
|
||||
Reference in New Issue
Block a user