This is an automated email from the ASF dual-hosted git repository. lukaszlenart pushed a commit to branch WW-5259-parser in repository https://gitbox.apache.org/repos/asf/struts.git
commit 998991eaa57f4169ddab65d85504ace21c3c2951 Author: Lukasz Lenart <lukaszlen...@apache.org> AuthorDate: Sun Nov 6 18:50:43 2022 +0100 WW-5259 Extracts UrlHelper#parseQueryString into a dedicated bean --- .../StrutsDefaultConfigurationProvider.java | 9 +- .../java/org/apache/struts2/StrutsConstants.java | 3 +- .../struts2/components/ExtraParameterProvider.java | 4 +- .../struts2/components/ServletUrlRenderer.java | 29 ++- .../org/apache/struts2/components/UrlProvider.java | 22 +- .../config/StrutsBeanSelectionProvider.java | 6 +- .../struts2/result/ServletDispatcherResult.java | 14 +- .../struts2/result/ServletRedirectResult.java | 19 +- ...sStringBuilder.java => QueryStringBuilder.java} | 12 +- ...rsStringBuilder.java => QueryStringParser.java} | 6 +- ...gBuilder.java => StrutsQueryStringBuilder.java} | 8 +- .../struts2/url/StrutsQueryStringParser.java | 99 +++++++++ .../java/org/apache/struts2/url/UrlDecoder.java | 4 + .../java/org/apache/struts2/url/UrlEncoder.java | 4 + .../struts2/views/util/DefaultUrlHelper.java | 73 ++---- .../org/apache/struts2/views/util/UrlHelper.java | 18 +- .../org/apache/struts2/default.properties | 13 +- core/src/main/resources/struts-default.xml | 6 +- .../result/ServletActionRedirectResultTest.java | 11 +- .../result/ServletDispatcherResultTest.java | 21 +- .../struts2/result/ServletRedirectResultTest.java | 10 +- ...Test.java => StrutsQueryStringBuilderTest.java} | 14 +- .../struts2/url/StrutsQueryStringParserTest.java | 84 +++++++ .../struts2/views/util/DefaultUrlHelperTest.java | 36 +-- .../main/java/org/apache/struts2/JSPRuntime.java | 13 +- .../org/apache/struts2/EmbeddedJSPResultTest.java | 24 +- .../struts2/json/JSONActionRedirectResultTest.java | 19 +- .../struts2/components/PortletUrlRenderer.java | 77 ++++--- .../result/PortletActionRedirectResult.java | 247 +++++++++++---------- 29 files changed, 530 insertions(+), 375 deletions(-) diff --git a/core/src/main/java/com/opensymphony/xwork2/config/providers/StrutsDefaultConfigurationProvider.java b/core/src/main/java/com/opensymphony/xwork2/config/providers/StrutsDefaultConfigurationProvider.java index d193c4d24..1f49cc11b 100644 --- a/core/src/main/java/com/opensymphony/xwork2/config/providers/StrutsDefaultConfigurationProvider.java +++ b/core/src/main/java/com/opensymphony/xwork2/config/providers/StrutsDefaultConfigurationProvider.java @@ -118,8 +118,10 @@ import org.apache.struts2.conversion.StrutsTypeConverterCreator; import org.apache.struts2.conversion.StrutsTypeConverterHolder; import org.apache.struts2.dispatcher.HttpParameters; import org.apache.struts2.dispatcher.Parameter; -import org.apache.struts2.url.ParametersStringBuilder; -import org.apache.struts2.url.StrutsParametersStringBuilder; +import org.apache.struts2.url.QueryStringBuilder; +import org.apache.struts2.url.QueryStringParser; +import org.apache.struts2.url.StrutsQueryStringBuilder; +import org.apache.struts2.url.StrutsQueryStringParser; import org.apache.struts2.url.StrutsUrlDecoder; import org.apache.struts2.url.StrutsUrlEncoder; import org.apache.struts2.url.UrlDecoder; @@ -236,7 +238,8 @@ public class StrutsDefaultConfigurationProvider implements ConfigurationProvider .factory(ValueSubstitutor.class, EnvsValueSubstitutor.class, Scope.SINGLETON) - .factory(ParametersStringBuilder.class, StrutsParametersStringBuilder.class, Scope.SINGLETON) + .factory(QueryStringBuilder.class, StrutsQueryStringBuilder.class, Scope.SINGLETON) + .factory(QueryStringParser.class, StrutsQueryStringParser.class, Scope.SINGLETON) .factory(UrlEncoder.class, StrutsUrlEncoder.class, Scope.SINGLETON) .factory(UrlDecoder.class, StrutsUrlDecoder.class, Scope.SINGLETON) ; diff --git a/core/src/main/java/org/apache/struts2/StrutsConstants.java b/core/src/main/java/org/apache/struts2/StrutsConstants.java index 7f6955648..b7dec53b2 100644 --- a/core/src/main/java/org/apache/struts2/StrutsConstants.java +++ b/core/src/main/java/org/apache/struts2/StrutsConstants.java @@ -458,7 +458,8 @@ public final class StrutsConstants { /** See {@link org.apache.struts2.components.Date#setDateFormatter(DateFormatter)} */ public static final String STRUTS_DATE_FORMATTER = "struts.date.formatter"; - public static final String STRUTS_URL_PARAMETERS_STRING_BUILDER = "struts.url.parametersStringBuilder"; + public static final String STRUTS_URL_QUERY_STRING_BUILDER = "struts.url.queryStringBuilder"; + public static final String STRUTS_URL_QUERY_STRING_PARSER = "struts.url.queryStringParser"; public static final String STRUTS_URL_ENCODER = "struts.url.encoder"; public static final String STRUTS_URL_DECODER = "struts.url.decoder"; } diff --git a/core/src/main/java/org/apache/struts2/components/ExtraParameterProvider.java b/core/src/main/java/org/apache/struts2/components/ExtraParameterProvider.java index 9734c6b6b..1d3719550 100644 --- a/core/src/main/java/org/apache/struts2/components/ExtraParameterProvider.java +++ b/core/src/main/java/org/apache/struts2/components/ExtraParameterProvider.java @@ -21,5 +21,7 @@ package org.apache.struts2.components; import java.util.Map; public interface ExtraParameterProvider { - public Map getExtraParameters(); + + Map<String, Object> getExtraParameters(); + } diff --git a/core/src/main/java/org/apache/struts2/components/ServletUrlRenderer.java b/core/src/main/java/org/apache/struts2/components/ServletUrlRenderer.java index 18831e9ce..d5281a8a0 100644 --- a/core/src/main/java/org/apache/struts2/components/ServletUrlRenderer.java +++ b/core/src/main/java/org/apache/struts2/components/ServletUrlRenderer.java @@ -29,6 +29,7 @@ import org.apache.logging.log4j.Logger; import org.apache.struts2.StrutsException; import org.apache.struts2.dispatcher.mapper.ActionMapper; import org.apache.struts2.dispatcher.mapper.ActionMapping; +import org.apache.struts2.url.QueryStringParser; import org.apache.struts2.views.util.UrlHelper; import java.io.IOException; @@ -48,6 +49,7 @@ public class ServletUrlRenderer implements UrlRenderer { private ActionMapper actionMapper; private UrlHelper urlHelper; + private QueryStringParser queryStringParser; @Override @Inject @@ -60,6 +62,11 @@ public class ServletUrlRenderer implements UrlRenderer { this.urlHelper = urlHelper; } + @Inject + public void setQueryStringParser(QueryStringParser queryStringParser) { + this.queryStringParser = queryStringParser; + } + /** * {@inheritDoc} */ @@ -152,10 +159,10 @@ public class ServletUrlRenderer implements UrlRenderer { } } - Map actionParams = null; + Map<String, Object> actionParams = null; if (action != null && action.indexOf('?') > 0) { String queryString = action.substring(action.indexOf('?') + 1); - actionParams = urlHelper.parseQueryString(queryString, false); + actionParams = queryStringParser.parse(queryString, false); action = action.substring(0, action.indexOf('?')); } @@ -164,19 +171,19 @@ public class ServletUrlRenderer implements UrlRenderer { String actionMethod = nameMapping.getMethod(); final ActionConfig actionConfig = formComponent.configuration.getRuntimeConfiguration().getActionConfig( - namespace, actionName); + namespace, actionName); if (actionConfig != null) { ActionMapping mapping = new ActionMapping(actionName, namespace, actionMethod, formComponent.parameters); String result = urlHelper.buildUrl(formComponent.actionMapper.getUriFromActionMapping(mapping), - formComponent.request, formComponent.response, actionParams, scheme, formComponent.includeContext, true, false, false); + formComponent.request, formComponent.response, actionParams, scheme, formComponent.includeContext, true, false, false); formComponent.addParameter("action", result); // let's try to get the actual action class and name // this can be used for getting the list of validators formComponent.addParameter("actionName", actionName); try { - Class clazz = formComponent.objectFactory.getClassInstance(actionConfig.getClassName()); + Class<?> clazz = formComponent.objectFactory.getClassInstance(actionConfig.getClassName()); formComponent.addParameter("actionClass", clazz); } catch (ClassNotFoundException e) { // this is OK, we'll just move on @@ -258,7 +265,7 @@ public class ServletUrlRenderer implements UrlRenderer { } if (UrlProvider.NONE.equalsIgnoreCase(includeParams)) { - mergeRequestParameters(urlComponent.getValue(), urlComponent.getParameters(), Collections.<String, Object>emptyMap()); + mergeRequestParameters(urlComponent.getValue(), urlComponent.getParameters(), Collections.emptyMap()); } else if (UrlProvider.ALL.equalsIgnoreCase(includeParams)) { mergeRequestParameters(urlComponent.getValue(), urlComponent.getParameters(), urlComponent.getHttpServletRequest().getParameterMap()); @@ -284,7 +291,7 @@ public class ServletUrlRenderer implements UrlRenderer { private void includeGetParameters(UrlProvider urlComponent) { String query = extractQueryString(urlComponent); - mergeRequestParameters(urlComponent.getValue(), urlComponent.getParameters(), urlHelper.parseQueryString(query, false)); + mergeRequestParameters(urlComponent.getValue(), urlComponent.getParameters(), queryStringParser.parse(query, false)); } private String extractQueryString(UrlProvider urlComponent) { @@ -309,7 +316,7 @@ public class ServletUrlRenderer implements UrlRenderer { * Merge request parameters into current parameters. If a parameter is * already present, than the request parameter in the current request and value attribute * will not override its value. - * + * <p> * The priority is as follows:- * <ul> * <li>parameter from the current request (least priority)</li> @@ -317,8 +324,8 @@ public class ServletUrlRenderer implements UrlRenderer { * <li>parameter from the param tag (most priority)</li> * </ul> * - * @param value the value attribute (URL to be generated by this component) - * @param parameters component parameters + * @param value the value attribute (URL to be generated by this component) + * @param parameters component parameters * @param contextParameters request parameters */ protected void mergeRequestParameters(String value, Map<String, Object> parameters, Map<String, ?> contextParameters) { @@ -332,7 +339,7 @@ public class ServletUrlRenderer implements UrlRenderer { if (StringUtils.contains(value, "?")) { String queryString = value.substring(value.indexOf('?') + 1); - mergedParams = urlHelper.parseQueryString(queryString, false); + mergedParams = queryStringParser.parse(queryString, false); for (Map.Entry<String, ?> entry : contextParameters.entrySet()) { if (!mergedParams.containsKey(entry.getKey())) { mergedParams.put(entry.getKey(), entry.getValue()); diff --git a/core/src/main/java/org/apache/struts2/components/UrlProvider.java b/core/src/main/java/org/apache/struts2/components/UrlProvider.java index abb47c925..8c3b79040 100644 --- a/core/src/main/java/org/apache/struts2/components/UrlProvider.java +++ b/core/src/main/java/org/apache/struts2/components/UrlProvider.java @@ -25,7 +25,7 @@ import javax.servlet.http.HttpServletResponse; import java.util.Map; /** - * Implemntations of this interface can be used to build a URL + * Implementations of this interface can be used to build a URL */ public interface UrlProvider { /** @@ -37,9 +37,9 @@ public interface UrlProvider { * get - include only GET parameters in the URL (default) * all - include both GET and POST parameters in the URL */ - public static final String NONE = "none"; - public static final String GET = "get"; - public static final String ALL = "all"; + String NONE = "none"; + String GET = "get"; + String ALL = "all"; boolean isPutInContext(); @@ -55,7 +55,7 @@ public interface UrlProvider { String getIncludeParams(); - Map getParameters(); + Map<String, Object> getParameters(); HttpServletRequest getHttpServletRequest(); @@ -78,19 +78,19 @@ public interface UrlProvider { boolean isForceAddSchemeHostAndPort(); boolean isEscapeAmp(); - + String getPortletMode(); - + String getWindowState(); - String determineActionURL(String action, String namespace, String method, HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Map parameters, String scheme, boolean includeContext, boolean encode, boolean forceAddSchemeHostAndPort, boolean escapeAmp); - + String determineActionURL(String action, String namespace, String method, HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Map<String, ?> parameters, String scheme, boolean includeContext, boolean encode, boolean forceAddSchemeHostAndPort, boolean escapeAmp); + String determineNamespace(String namespace, ValueStack stack, HttpServletRequest req); String getAnchor(); - + String getPortletUrlType(); - + ValueStack getStack(); void setUrlIncludeParams(String urlIncludeParams); diff --git a/core/src/main/java/org/apache/struts2/config/StrutsBeanSelectionProvider.java b/core/src/main/java/org/apache/struts2/config/StrutsBeanSelectionProvider.java index 15b79f09b..06385f28f 100644 --- a/core/src/main/java/org/apache/struts2/config/StrutsBeanSelectionProvider.java +++ b/core/src/main/java/org/apache/struts2/config/StrutsBeanSelectionProvider.java @@ -66,7 +66,8 @@ import org.apache.struts2.dispatcher.DispatcherErrorHandler; import org.apache.struts2.dispatcher.StaticContentLoader; import org.apache.struts2.dispatcher.mapper.ActionMapper; import org.apache.struts2.dispatcher.multipart.MultiPartRequest; -import org.apache.struts2.url.ParametersStringBuilder; +import org.apache.struts2.url.QueryStringBuilder; +import org.apache.struts2.url.QueryStringParser; import org.apache.struts2.url.UrlDecoder; import org.apache.struts2.url.UrlEncoder; import org.apache.struts2.util.ContentTypeMatcher; @@ -432,7 +433,8 @@ public class StrutsBeanSelectionProvider extends AbstractBeanSelectionProvider { alias(ExpressionCacheFactory.class, StrutsConstants.STRUTS_OGNL_EXPRESSION_CACHE_FACTORY, builder, props, Scope.SINGLETON); alias(BeanInfoCacheFactory.class, StrutsConstants.STRUTS_OGNL_BEANINFO_CACHE_FACTORY, builder, props, Scope.SINGLETON); - alias(ParametersStringBuilder.class, StrutsConstants.STRUTS_URL_PARAMETERS_STRING_BUILDER, builder, props, Scope.SINGLETON); + alias(QueryStringBuilder.class, StrutsConstants.STRUTS_URL_QUERY_STRING_BUILDER, builder, props, Scope.SINGLETON); + alias(QueryStringParser.class, StrutsConstants.STRUTS_URL_QUERY_STRING_PARSER, builder, props, Scope.SINGLETON); alias(UrlEncoder.class, StrutsConstants.STRUTS_URL_ENCODER, builder, props, Scope.SINGLETON); alias(UrlDecoder.class, StrutsConstants.STRUTS_URL_DECODER, builder, props, Scope.SINGLETON); diff --git a/core/src/main/java/org/apache/struts2/result/ServletDispatcherResult.java b/core/src/main/java/org/apache/struts2/result/ServletDispatcherResult.java index f29a771ed..a3bbfaf97 100644 --- a/core/src/main/java/org/apache/struts2/result/ServletDispatcherResult.java +++ b/core/src/main/java/org/apache/struts2/result/ServletDispatcherResult.java @@ -27,7 +27,7 @@ import org.apache.logging.log4j.Logger; import org.apache.struts2.ServletActionContext; import org.apache.struts2.StrutsStatics; import org.apache.struts2.dispatcher.HttpParameters; -import org.apache.struts2.views.util.UrlHelper; +import org.apache.struts2.url.QueryStringParser; import javax.servlet.RequestDispatcher; import javax.servlet.http.HttpServletRequest; @@ -66,7 +66,7 @@ import java.util.Map; * <!-- END SNIPPET: description --> * * <p><b>This result type takes the following parameters:</b></p> - * + * <p> * <!-- START SNIPPET: params --> * * <ul> @@ -76,7 +76,7 @@ import java.util.Map; * <li><b>parse</b> - true by default. If set to false, the location param will not be parsed for Ognl expressions.</li> * * </ul> - * + * <p> * <!-- END SNIPPET: params --> * * <p><b>Example:</b></p> @@ -99,7 +99,7 @@ public class ServletDispatcherResult extends StrutsResultSupport { private static final Logger LOG = LogManager.getLogger(ServletDispatcherResult.class); - private UrlHelper urlHelper; + private QueryStringParser queryStringParser; public ServletDispatcherResult() { super(); @@ -110,8 +110,8 @@ public class ServletDispatcherResult extends StrutsResultSupport { } @Inject - public void setUrlHelper(UrlHelper urlHelper) { - this.urlHelper = urlHelper; + public void setQueryStringParser(QueryStringParser queryStringParser) { + this.queryStringParser = queryStringParser; } /** @@ -140,7 +140,7 @@ public class ServletDispatcherResult extends StrutsResultSupport { if (StringUtils.isNotEmpty(finalLocation) && finalLocation.indexOf('?') > 0) { String queryString = finalLocation.substring(finalLocation.indexOf('?') + 1); HttpParameters parameters = getParameters(invocation); - Map<String, Object> queryParams = urlHelper.parseQueryString(queryString, true); + Map<String, Object> queryParams = queryStringParser.parse(queryString, true); if (queryParams != null && !queryParams.isEmpty()) { parameters = HttpParameters.create(queryParams).withParent(parameters).build(); invocation.getInvocationContext().setParameters(parameters); diff --git a/core/src/main/java/org/apache/struts2/result/ServletRedirectResult.java b/core/src/main/java/org/apache/struts2/result/ServletRedirectResult.java index d59214b23..00d5b5203 100644 --- a/core/src/main/java/org/apache/struts2/result/ServletRedirectResult.java +++ b/core/src/main/java/org/apache/struts2/result/ServletRedirectResult.java @@ -29,7 +29,7 @@ import org.apache.logging.log4j.Logger; import org.apache.struts2.dispatcher.Dispatcher; import org.apache.struts2.dispatcher.mapper.ActionMapper; import org.apache.struts2.dispatcher.mapper.ActionMapping; -import org.apache.struts2.views.util.UrlHelper; +import org.apache.struts2.url.QueryStringBuilder; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; @@ -54,7 +54,7 @@ import static javax.servlet.http.HttpServletResponse.SC_FOUND; * available. This is because actions are built on a single-thread model. The * only way to pass data is through the session or with web parameters * (url?name=value) which can be OGNL expressions. - * + * <p> * <b>This result type takes the following parameters:</b> * * <ul> @@ -65,7 +65,7 @@ import static javax.servlet.http.HttpServletResponse.SC_FOUND; * "hash". You can specify an anchor for a result.</li> * </ul> * This result follows the same rules from {@link StrutsResultSupport}. - * + * <p> * <b>Example:</b> * <pre> * <!-- START SNIPPET: example --> @@ -94,7 +94,7 @@ public class ServletRedirectResult extends StrutsResultSupport implements Reflec protected Map<String, Object> requestParameters = new LinkedHashMap<>(); protected String anchor; - private UrlHelper urlHelper; + private QueryStringBuilder queryStringBuilder; public ServletRedirectResult() { super(); @@ -115,8 +115,8 @@ public class ServletRedirectResult extends StrutsResultSupport implements Reflec } @Inject - public void setUrlHelper(UrlHelper urlHelper) { - this.urlHelper = urlHelper; + public void setQueryStringBuilder(QueryStringBuilder queryStringBuilder) { + this.queryStringBuilder = queryStringBuilder; } public void setStatusCode(int code) { @@ -202,7 +202,7 @@ public class ServletRedirectResult extends StrutsResultSupport implements Reflec } StringBuilder tmpLocation = new StringBuilder(finalLocation); - urlHelper.buildParametersString(requestParameters, tmpLocation, "&"); + queryStringBuilder.build(requestParameters, tmpLocation, "&"); // add the anchor if (anchor != null) { @@ -283,10 +283,7 @@ public class ServletRedirectResult extends StrutsResultSupport implements Reflec LOG.debug("[{}] isn't absolute URI, assuming it's a path", url); return true; } - } catch (IllegalArgumentException e) { - LOG.debug("[{}] isn't a valid URL, assuming it's a path", url, e); - return true; - } catch (MalformedURLException e) { + } catch (IllegalArgumentException | MalformedURLException e) { LOG.debug("[{}] isn't a valid URL, assuming it's a path", url, e); return true; } diff --git a/core/src/main/java/org/apache/struts2/url/ParametersStringBuilder.java b/core/src/main/java/org/apache/struts2/url/QueryStringBuilder.java similarity index 65% copy from core/src/main/java/org/apache/struts2/url/ParametersStringBuilder.java copy to core/src/main/java/org/apache/struts2/url/QueryStringBuilder.java index 651c46ddb..b3dcb3d73 100644 --- a/core/src/main/java/org/apache/struts2/url/ParametersStringBuilder.java +++ b/core/src/main/java/org/apache/struts2/url/QueryStringBuilder.java @@ -21,11 +21,17 @@ package org.apache.struts2.url; import java.util.Map; /** - * A builder used to create a proper query string out of a set of parameters + * A builder used to create a proper Query String out of a set of parameters * @since Struts 6.1.0 */ -public interface ParametersStringBuilder { +public interface QueryStringBuilder { - void buildParametersString(Map<String, Object> params, StringBuilder link, String paramSeparator); + /** + * Builds a Query String with defined separator and appends it to the provided link + * @param params a Map used to build a Query String + * @param link to which the Query String should be added + * @param paramSeparator used to separate parameters in query string + */ + void build(Map<String, Object> params, StringBuilder link, String paramSeparator); } diff --git a/core/src/main/java/org/apache/struts2/url/ParametersStringBuilder.java b/core/src/main/java/org/apache/struts2/url/QueryStringParser.java similarity index 80% rename from core/src/main/java/org/apache/struts2/url/ParametersStringBuilder.java rename to core/src/main/java/org/apache/struts2/url/QueryStringParser.java index 651c46ddb..40549cb15 100644 --- a/core/src/main/java/org/apache/struts2/url/ParametersStringBuilder.java +++ b/core/src/main/java/org/apache/struts2/url/QueryStringParser.java @@ -21,11 +21,11 @@ package org.apache.struts2.url; import java.util.Map; /** - * A builder used to create a proper query string out of a set of parameters + * Used to parse Http Query String into a Map of parameters * @since Struts 6.1.0 */ -public interface ParametersStringBuilder { +public interface QueryStringParser { - void buildParametersString(Map<String, Object> params, StringBuilder link, String paramSeparator); + Map<String, Object> parse(String queryString, boolean forceValueArray); } diff --git a/core/src/main/java/org/apache/struts2/url/StrutsParametersStringBuilder.java b/core/src/main/java/org/apache/struts2/url/StrutsQueryStringBuilder.java similarity index 91% rename from core/src/main/java/org/apache/struts2/url/StrutsParametersStringBuilder.java rename to core/src/main/java/org/apache/struts2/url/StrutsQueryStringBuilder.java index e6e38f451..ea5dca84d 100644 --- a/core/src/main/java/org/apache/struts2/url/StrutsParametersStringBuilder.java +++ b/core/src/main/java/org/apache/struts2/url/StrutsQueryStringBuilder.java @@ -24,19 +24,19 @@ import org.apache.logging.log4j.Logger; import java.util.Map; -public class StrutsParametersStringBuilder implements ParametersStringBuilder { +public class StrutsQueryStringBuilder implements QueryStringBuilder { - private static final Logger LOG = LogManager.getLogger(StrutsParametersStringBuilder.class); + private static final Logger LOG = LogManager.getLogger(StrutsQueryStringBuilder.class); private final UrlEncoder encoder; @Inject - public StrutsParametersStringBuilder(UrlEncoder encoder) { + public StrutsQueryStringBuilder(UrlEncoder encoder) { this.encoder = encoder; } @Override - public void buildParametersString(Map<String, Object> params, StringBuilder link, String paramSeparator) { + public void build(Map<String, Object> params, StringBuilder link, String paramSeparator) { if ((params != null) && (params.size() > 0)) { LOG.debug("Building query string out of: {} parameters", params.size()); StringBuilder queryString = new StringBuilder(); diff --git a/core/src/main/java/org/apache/struts2/url/StrutsQueryStringParser.java b/core/src/main/java/org/apache/struts2/url/StrutsQueryStringParser.java new file mode 100644 index 000000000..e4f2f8505 --- /dev/null +++ b/core/src/main/java/org/apache/struts2/url/StrutsQueryStringParser.java @@ -0,0 +1,99 @@ +/* + * 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.url; + +import com.opensymphony.xwork2.inject.Inject; +import org.apache.commons.lang3.StringUtils; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; +import org.apache.struts2.interceptor.exec.StrutsExecutorProvider; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; + +public class StrutsQueryStringParser implements QueryStringParser { + + private static final Logger LOG = LogManager.getLogger(StrutsExecutorProvider.class); + + private final UrlDecoder decoder; + + @Inject + public StrutsQueryStringParser(UrlDecoder decoder) { + this.decoder = decoder; + } + + @Override + public Map<String, Object> parse(String queryString, boolean forceValueArray) { + if (StringUtils.isEmpty(queryString)) { + LOG.debug("Query String is empty, returning an empty map"); + return Collections.emptyMap(); + } + + Map<String, Object> queryParams = new LinkedHashMap<>(); + String[] params = queryString.split("&"); + for (String param : params) { + if (StringUtils.isBlank(param)) { + LOG.debug("Param [{}] is blank, skipping", param); + continue; + } + + String[] tmpParams = param.split("="); + String paramName = null; + String paramValue = ""; + if (tmpParams.length > 0) { + paramName = tmpParams[0]; + } + if (tmpParams.length > 1) { + paramValue = tmpParams[1]; + } + if (paramName != null) { + extractParam(paramName, paramValue, queryParams, forceValueArray); + } + } + return queryParams; + } + + private void extractParam(String paramName, String paramValue, Map<String, Object> queryParams, boolean forceValueArray) { + String decodedParamName = decoder.decode(paramName, true); + String decodedParamValue = decoder.decode(paramValue, true); + + if (queryParams.containsKey(decodedParamName) || forceValueArray) { + // WW-1619 append new param value to existing value(s) + Object currentParam = queryParams.get(decodedParamName); + if (currentParam instanceof String) { + queryParams.put(decodedParamName, new String[]{(String) currentParam, decodedParamValue}); + } else { + String[] currentParamValues = (String[]) currentParam; + if (currentParamValues != null) { + List<String> paramList = new ArrayList<>(Arrays.asList(currentParamValues)); + paramList.add(decodedParamValue); + queryParams.put(decodedParamName, paramList.toArray(new String[0])); + } else { + queryParams.put(decodedParamName, new String[]{decodedParamValue}); + } + } + } else { + queryParams.put(decodedParamName, decodedParamValue); + } + } +} diff --git a/core/src/main/java/org/apache/struts2/url/UrlDecoder.java b/core/src/main/java/org/apache/struts2/url/UrlDecoder.java index b54c563f5..e88a880ff 100644 --- a/core/src/main/java/org/apache/struts2/url/UrlDecoder.java +++ b/core/src/main/java/org/apache/struts2/url/UrlDecoder.java @@ -18,6 +18,10 @@ */ package org.apache.struts2.url; +/** + * URL Decoder used internally by Struts + * @since Struts 6.1.0 + */ public interface UrlDecoder { /** diff --git a/core/src/main/java/org/apache/struts2/url/UrlEncoder.java b/core/src/main/java/org/apache/struts2/url/UrlEncoder.java index 976645a53..f830435dc 100644 --- a/core/src/main/java/org/apache/struts2/url/UrlEncoder.java +++ b/core/src/main/java/org/apache/struts2/url/UrlEncoder.java @@ -18,6 +18,10 @@ */ package org.apache.struts2.url; +/** + * URL Encoder used internally by Struts + * @since Struts 6.1.0 + */ public interface UrlEncoder { /** diff --git a/core/src/main/java/org/apache/struts2/views/util/DefaultUrlHelper.java b/core/src/main/java/org/apache/struts2/views/util/DefaultUrlHelper.java index 547efef11..bb072c82b 100644 --- a/core/src/main/java/org/apache/struts2/views/util/DefaultUrlHelper.java +++ b/core/src/main/java/org/apache/struts2/views/util/DefaultUrlHelper.java @@ -24,16 +24,13 @@ import org.apache.commons.text.StringEscapeUtils; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; import org.apache.struts2.StrutsConstants; -import org.apache.struts2.url.ParametersStringBuilder; +import org.apache.struts2.url.QueryStringBuilder; +import org.apache.struts2.url.QueryStringParser; import org.apache.struts2.url.UrlDecoder; import org.apache.struts2.url.UrlEncoder; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.LinkedHashMap; -import java.util.List; import java.util.Map; /** @@ -49,7 +46,8 @@ public class DefaultUrlHelper implements UrlHelper { private int httpPort = DEFAULT_HTTP_PORT; private int httpsPort = DEFAULT_HTTPS_PORT; - private ParametersStringBuilder parametersStringBuilder; + private QueryStringBuilder queryStringBuilder; + private QueryStringParser queryStringParser; private UrlEncoder encoder; private UrlDecoder decoder; @@ -74,8 +72,13 @@ public class DefaultUrlHelper implements UrlHelper { } @Inject - public void setParametersStringBuilder(ParametersStringBuilder builder) { - this.parametersStringBuilder = builder; + public void setQueryStringBuilder(QueryStringBuilder builder) { + this.queryStringBuilder = builder; + } + + @Inject + public void setQueryStringParser(QueryStringParser queryStringParser) { + this.queryStringParser = queryStringParser; } public String buildUrl(String action, HttpServletRequest request, HttpServletResponse response, Map<String, Object> params) { @@ -176,9 +179,9 @@ public class DefaultUrlHelper implements UrlHelper { //if the action was not explicitly set grab the params from the request if (escapeAmp) { - parametersStringBuilder.buildParametersString(params, link, AMP); + queryStringBuilder.build(params, link, AMP); } else { - parametersStringBuilder.buildParametersString(params, link, "&"); + queryStringBuilder.build(params, link, "&"); } String result = link.toString(); @@ -208,11 +211,11 @@ public class DefaultUrlHelper implements UrlHelper { * @param params a set of params to assign * @param link a based url * @param paramSeparator separator used - * @deprecated since Struts 6.1.0, use {@link ParametersStringBuilder} instead + * @deprecated since Struts 6.1.0, use {@link QueryStringBuilder} instead */ @Deprecated public void buildParametersString(Map<String, Object> params, StringBuilder link, String paramSeparator) { - parametersStringBuilder.buildParametersString(params, link, paramSeparator); + queryStringBuilder.build(params, link, paramSeparator); } /** @@ -269,47 +272,11 @@ public class DefaultUrlHelper implements UrlHelper { return decoder.decode(input, isQueryString); } + /** + * @deprecated since 6.1.0, use {@link QueryStringParser} directly, use {@link Inject} to inject a proper instance + */ + @Deprecated public Map<String, Object> parseQueryString(String queryString, boolean forceValueArray) { - Map<String, Object> queryParams = new LinkedHashMap<>(); - if (queryString != null) { - String[] params = queryString.split("&"); - for (String param : params) { - if (param.trim().length() > 0) { - String[] tmpParams = param.split("="); - String paramName = null; - String paramValue = ""; - if (tmpParams.length > 0) { - paramName = tmpParams[0]; - } - if (tmpParams.length > 1) { - paramValue = tmpParams[1]; - } - if (paramName != null) { - paramName = decoder.decode(paramName, true); - String translatedParamValue = decoder.decode(paramValue, true); - - if (queryParams.containsKey(paramName) || forceValueArray) { - // WW-1619 append new param value to existing value(s) - Object currentParam = queryParams.get(paramName); - if (currentParam instanceof String) { - queryParams.put(paramName, new String[]{(String) currentParam, translatedParamValue}); - } else { - String[] currentParamValues = (String[]) currentParam; - if (currentParamValues != null) { - List<String> paramList = new ArrayList<>(Arrays.asList(currentParamValues)); - paramList.add(translatedParamValue); - queryParams.put(paramName, paramList.toArray(new String[0])); - } else { - queryParams.put(paramName, new String[]{translatedParamValue}); - } - } - } else { - queryParams.put(paramName, translatedParamValue); - } - } - } - } - } - return queryParams; + return this.queryStringParser.parse(queryString, forceValueArray); } } diff --git a/core/src/main/java/org/apache/struts2/views/util/UrlHelper.java b/core/src/main/java/org/apache/struts2/views/util/UrlHelper.java index b997a58ef..4f82d6848 100644 --- a/core/src/main/java/org/apache/struts2/views/util/UrlHelper.java +++ b/core/src/main/java/org/apache/struts2/views/util/UrlHelper.java @@ -18,6 +18,10 @@ */ package org.apache.struts2.views.util; +import com.opensymphony.xwork2.inject.Inject; +import org.apache.struts2.url.QueryStringBuilder; +import org.apache.struts2.url.QueryStringParser; + import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.util.Map; @@ -30,14 +34,14 @@ public interface UrlHelper { /** * Default HTTP port (80). */ - static final int DEFAULT_HTTP_PORT = 80; + int DEFAULT_HTTP_PORT = 80; /** * Default HTTPS port (443). */ - static final int DEFAULT_HTTPS_PORT = 443; + int DEFAULT_HTTPS_PORT = 443; - static final String AMP = "&"; + String AMP = "&"; String buildUrl(String action, HttpServletRequest request, HttpServletResponse response, Map<String, Object> params); @@ -50,8 +54,16 @@ public interface UrlHelper { String buildUrl(String action, HttpServletRequest request, HttpServletResponse response, Map<String, Object> params, String scheme, boolean includeContext, boolean encodeResult, boolean forceAddSchemeHostAndPort, boolean escapeAmp); + /** + * @deprecated since Struts 6.1.0, use {@link QueryStringBuilder} instead + */ + @Deprecated void buildParametersString(Map<String, Object> params, StringBuilder link, String paramSeparator); + /** + * @deprecated since 6.1.0, use {@link QueryStringParser} directly, use {@link Inject} to inject a proper instance + */ + @Deprecated Map<String, Object> parseQueryString(String queryString, boolean forceValueArray); } diff --git a/core/src/main/resources/org/apache/struts2/default.properties b/core/src/main/resources/org/apache/struts2/default.properties index a57c48bea..5847db3be 100644 --- a/core/src/main/resources/org/apache/struts2/default.properties +++ b/core/src/main/resources/org/apache/struts2/default.properties @@ -279,10 +279,15 @@ struts.ognl.expressionMaxLength=256 ### These formatters are using a slightly different patterns, please check JavaDocs of both and more details is in WW-5016 struts.date.formatter=dateTimeFormatter -### Defines which instance of ParametersStringBuilder to use, Struts provides just one instance: -### - strutsParametersStringBuilder -### The builder is used by UrlHelp to create a proper query string out of provided parameters map -struts.url.parametersStringBuilder=strutsParametersStringBuilder +### Defines which instance of QueryStringBuilder to use, Struts provides just one instance: +### - strutsQueryStringBuilder +### The builder is used by UrlHelp to create a proper Query String out of provided parameters map +struts.url.queryStringBuilder=strutsQueryStringBuilder + +### Defines which instance of QueryStringParser to use, Struts provides just one instance: +### - strutsQueryStringParser +### The parser is used to parse Query String into a map +struts.url.queryStringParser=strutsQueryStringParser ### Defines which instances of encoder and decoder to use, Struts provides one default implementation for each struts.url.encoder=strutsUrlEncoder diff --git a/core/src/main/resources/struts-default.xml b/core/src/main/resources/struts-default.xml index 35b5bf419..fc6e1c54c 100644 --- a/core/src/main/resources/struts-default.xml +++ b/core/src/main/resources/struts-default.xml @@ -313,8 +313,10 @@ <bean type="com.opensymphony.xwork2.ognl.BeanInfoCacheFactory" name="struts" class="com.opensymphony.xwork2.ognl.DefaultOgnlBeanInfoCacheFactory" scope="singleton"/> - <bean type="org.apache.struts2.url.ParametersStringBuilder" name="strutsParametersStringBuilder" - class="org.apache.struts2.url.StrutsParametersStringBuilder" scope="singleton"/> + <bean type="org.apache.struts2.url.QueryStringBuilder" name="strutsQueryStringBuilder" + class="org.apache.struts2.url.StrutsQueryStringBuilder" scope="singleton"/> + <bean type="org.apache.struts2.url.QueryStringParser" name="strutsQueryStringParser" + class="org.apache.struts2.url.StrutsQueryStringParser" scope="singleton"/> <bean type="org.apache.struts2.url.UrlEncoder" name="strutsUrlEncoder" class="org.apache.struts2.url.StrutsUrlEncoder" scope="singleton"/> <bean type="org.apache.struts2.url.UrlDecoder" name="strutsUrlDecoder" diff --git a/core/src/test/java/org/apache/struts2/result/ServletActionRedirectResultTest.java b/core/src/test/java/org/apache/struts2/result/ServletActionRedirectResultTest.java index 0933f8ea7..8fdf1ae30 100644 --- a/core/src/test/java/org/apache/struts2/result/ServletActionRedirectResultTest.java +++ b/core/src/test/java/org/apache/struts2/result/ServletActionRedirectResultTest.java @@ -29,7 +29,8 @@ import org.apache.struts2.ServletActionContext; import org.apache.struts2.StrutsInternalTestCase; import org.apache.struts2.dispatcher.mapper.ActionMapper; import org.apache.struts2.dispatcher.mapper.DefaultActionMapper; -import org.apache.struts2.views.util.DefaultUrlHelper; +import org.apache.struts2.url.StrutsQueryStringBuilder; +import org.apache.struts2.url.StrutsUrlEncoder; import org.easymock.IMocksControl; import org.springframework.mock.web.MockHttpServletRequest; import org.springframework.mock.web.MockHttpServletResponse; @@ -82,7 +83,7 @@ public class ServletActionRedirectResultTest extends StrutsInternalTestCase { result.setEncode(false); result.setPrependServletContext(false); result.setAnchor("fragment"); - result.setUrlHelper(new DefaultUrlHelper()); + result.setQueryStringBuilder(new StrutsQueryStringBuilder(new StrutsUrlEncoder())); IMocksControl control = createControl(); ActionProxy mockActionProxy = control.createMock(ActionProxy.class); @@ -144,7 +145,7 @@ public class ServletActionRedirectResultTest extends StrutsInternalTestCase { result.setEncode(false); result.setPrependServletContext(false); result.setAnchor("fragment"); - result.setUrlHelper(new DefaultUrlHelper()); + result.setQueryStringBuilder(new StrutsQueryStringBuilder(new StrutsUrlEncoder())); IMocksControl control = createControl(); ActionProxy mockActionProxy = control.createMock(ActionProxy.class); @@ -210,7 +211,7 @@ public class ServletActionRedirectResultTest extends StrutsInternalTestCase { result.setEncode(false); result.setPrependServletContext(false); result.setAnchor("fragment"); - result.setUrlHelper(new DefaultUrlHelper()); + result.setQueryStringBuilder(new StrutsQueryStringBuilder(new StrutsUrlEncoder())); IMocksControl control = createControl(); ActionProxy mockActionProxy = control.createMock(ActionProxy.class); @@ -264,7 +265,7 @@ public class ServletActionRedirectResultTest extends StrutsInternalTestCase { result.setEncode(false); result.setPrependServletContext(false); result.setAnchor("fragment"); - result.setUrlHelper(new DefaultUrlHelper()); + result.setQueryStringBuilder(new StrutsQueryStringBuilder(new StrutsUrlEncoder())); IMocksControl control = createControl(); ActionProxy mockActionProxy = control.createMock(ActionProxy.class); diff --git a/core/src/test/java/org/apache/struts2/result/ServletDispatcherResultTest.java b/core/src/test/java/org/apache/struts2/result/ServletDispatcherResultTest.java index 4a90ae443..0f57593d6 100644 --- a/core/src/test/java/org/apache/struts2/result/ServletDispatcherResultTest.java +++ b/core/src/test/java/org/apache/struts2/result/ServletDispatcherResultTest.java @@ -18,29 +18,20 @@ */ package org.apache.struts2.result; -import javax.servlet.RequestDispatcher; -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; - +import com.mockobjects.dynamic.C; +import com.mockobjects.dynamic.Mock; +import com.opensymphony.xwork2.ActionContext; import com.opensymphony.xwork2.mock.MockActionInvocation; -import com.opensymphony.xwork2.util.ValueStack; import com.opensymphony.xwork2.util.ValueStackFactory; -import ognl.Ognl; - import org.apache.struts2.ServletActionContext; import org.apache.struts2.StrutsInternalTestCase; import org.apache.struts2.StrutsStatics; import org.apache.struts2.dispatcher.HttpParameters; -import com.mockobjects.dynamic.C; -import com.mockobjects.dynamic.Mock; -import com.opensymphony.xwork2.ActionContext; -import org.apache.struts2.result.ServletDispatcherResult; - +import javax.servlet.RequestDispatcher; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; -/** - * - */ public class ServletDispatcherResultTest extends StrutsInternalTestCase implements StrutsStatics { public void testInclude() { diff --git a/core/src/test/java/org/apache/struts2/result/ServletRedirectResultTest.java b/core/src/test/java/org/apache/struts2/result/ServletRedirectResultTest.java index 8936783fc..2ee6478f5 100644 --- a/core/src/test/java/org/apache/struts2/result/ServletRedirectResultTest.java +++ b/core/src/test/java/org/apache/struts2/result/ServletRedirectResultTest.java @@ -32,8 +32,8 @@ import com.opensymphony.xwork2.util.ValueStack; import org.apache.struts2.ServletActionContext; import org.apache.struts2.StrutsInternalTestCase; import org.apache.struts2.StrutsStatics; -import org.apache.struts2.dispatcher.mapper.ActionMapper; -import org.apache.struts2.views.util.DefaultUrlHelper; +import org.apache.struts2.url.StrutsQueryStringBuilder; +import org.apache.struts2.url.StrutsUrlEncoder; import org.easymock.IMocksControl; import org.springframework.mock.web.MockHttpServletRequest; import org.springframework.mock.web.MockHttpServletResponse; @@ -240,7 +240,7 @@ public class ServletRedirectResultTest extends StrutsInternalTestCase implements result.setEncode(false); result.setPrependServletContext(false); result.setAnchor("fragment"); - result.setUrlHelper(new DefaultUrlHelper()); + result.setQueryStringBuilder(new StrutsQueryStringBuilder(new StrutsUrlEncoder())); IMocksControl control = createControl(); ActionProxy mockActionProxy = control.createMock(ActionProxy.class); @@ -286,7 +286,7 @@ public class ServletRedirectResultTest extends StrutsInternalTestCase implements result.setParse(true); result.setEncode(false); result.setPrependServletContext(false); - result.setUrlHelper(new DefaultUrlHelper()); + result.setQueryStringBuilder(new StrutsQueryStringBuilder(new StrutsUrlEncoder())); result.setSuppressEmptyParameters(true); IMocksControl control = createControl(); @@ -433,7 +433,7 @@ public class ServletRedirectResultTest extends StrutsInternalTestCase implements } } - public void testPassingNullInvocation() throws Exception{ + public void testPassingNullInvocation() throws Exception { Result result = new ServletRedirectResult(); try { result.execute(null); diff --git a/core/src/test/java/org/apache/struts2/url/StrutsParametersStringBuilderTest.java b/core/src/test/java/org/apache/struts2/url/StrutsQueryStringBuilderTest.java similarity index 88% rename from core/src/test/java/org/apache/struts2/url/StrutsParametersStringBuilderTest.java rename to core/src/test/java/org/apache/struts2/url/StrutsQueryStringBuilderTest.java index 815c5ae16..b30a3b835 100644 --- a/core/src/test/java/org/apache/struts2/url/StrutsParametersStringBuilderTest.java +++ b/core/src/test/java/org/apache/struts2/url/StrutsQueryStringBuilderTest.java @@ -28,9 +28,9 @@ import java.util.Map; import static org.junit.Assert.assertEquals; -public class StrutsParametersStringBuilderTest { +public class StrutsQueryStringBuilderTest { - private ParametersStringBuilder builder; + private QueryStringBuilder builder; @Test public void testBuildParametersStringWithUrlHavingSomeExistingParameters() { @@ -43,7 +43,7 @@ public class StrutsParametersStringBuilderTest { StringBuilder url = new StringBuilder("http://localhost:8080/myContext/myPage.jsp?initParam=initValue"); - builder.buildParametersString(parameters, url, UrlHelper.AMP); + builder.build(parameters, url, UrlHelper.AMP); assertEquals(expectedUrl, url.toString()); } @@ -59,7 +59,7 @@ public class StrutsParametersStringBuilderTest { StringBuilder url = new StringBuilder("http://localhost:8080/myContext/myPage.jsp?initParam=initValue"); - builder.buildParametersString(parameters, url, UrlHelper.AMP); + builder.build(parameters, url, UrlHelper.AMP); assertEquals(expectedUrl, url.toString()); } @@ -71,7 +71,7 @@ public class StrutsParametersStringBuilderTest { parameters.put("param1", new String[]{}); parameters.put("param2", new ArrayList<>()); StringBuilder url = new StringBuilder("https://www.nowhere.com/myworld.html"); - builder.buildParametersString(parameters, url, UrlHelper.AMP); + builder.build(parameters, url, UrlHelper.AMP); assertEquals(expectedUrl, url.toString()); } @@ -87,13 +87,13 @@ public class StrutsParametersStringBuilderTest { } }); StringBuilder url = new StringBuilder("https://www.nowhere.com/myworld.html"); - builder.buildParametersString(parameters, url, "&"); + builder.build(parameters, url, "&"); assertEquals(expectedUrl, url.toString()); } @Before public void setUp() throws Exception { - builder = new StrutsParametersStringBuilder(new StrutsUrlEncoder()); + builder = new StrutsQueryStringBuilder(new StrutsUrlEncoder()); } } diff --git a/core/src/test/java/org/apache/struts2/url/StrutsQueryStringParserTest.java b/core/src/test/java/org/apache/struts2/url/StrutsQueryStringParserTest.java new file mode 100644 index 000000000..6462d9595 --- /dev/null +++ b/core/src/test/java/org/apache/struts2/url/StrutsQueryStringParserTest.java @@ -0,0 +1,84 @@ +/* + * 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.url; + +import org.assertj.core.util.Arrays; +import org.junit.Before; +import org.junit.Test; + +import java.util.Map; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertTrue; + +public class StrutsQueryStringParserTest { + + private QueryStringParser parser; + + @Test + public void testParseQuery() { + Map<String, Object> result = parser.parse("aaa=aaaval&bbb=bbbval&ccc=&%3Ca%22%3E=%3Cval%3E", false); + + assertEquals(result.get("aaa"), "aaaval"); + assertEquals(result.get("bbb"), "bbbval"); + assertEquals(result.get("ccc"), ""); + assertEquals(result.get("<a\">"), "<val>"); + } + + @Test + public void testParseQueryIntoArray() { + Map<String, Object> result = parser.parse("a=1&a=2&a=3", true); + + Object actual = result.get("a"); + assertThat(actual).isInstanceOf(String[].class); + assertThat(Arrays.asList(actual)).containsOnly("1", "2", "3"); + } + + @Test + public void testParseEmptyQuery() { + Map<String, Object> result = parser.parse("", false); + + assertNotNull(result); + assertEquals(result.size(), 0); + } + + @Test + public void testParseNullQuery() { + Map<String, Object> result = parser.parse(null, false); + + assertNotNull(result); + assertEquals(result.size(), 0); + } + + @Test + public void testDecodeSpacesInQueryString() { + Map<String, Object> queryParameters = parser.parse("name=value+with+space", false); + + assertTrue(queryParameters.containsKey("name")); + assertEquals("value with space", queryParameters.get("name")); + } + + @Before + public void setUp() throws Exception { + this.parser = new StrutsQueryStringParser(new StrutsUrlDecoder()); + } + +} diff --git a/core/src/test/java/org/apache/struts2/views/util/DefaultUrlHelperTest.java b/core/src/test/java/org/apache/struts2/views/util/DefaultUrlHelperTest.java index acbee3010..fa1c22322 100644 --- a/core/src/test/java/org/apache/struts2/views/util/DefaultUrlHelperTest.java +++ b/core/src/test/java/org/apache/struts2/views/util/DefaultUrlHelperTest.java @@ -23,7 +23,7 @@ import com.opensymphony.xwork2.ActionContext; import com.opensymphony.xwork2.inject.Container; import com.opensymphony.xwork2.inject.Scope.Strategy; import org.apache.struts2.StrutsInternalTestCase; -import org.apache.struts2.url.StrutsParametersStringBuilder; +import org.apache.struts2.url.StrutsQueryStringBuilder; import org.apache.struts2.url.StrutsUrlDecoder; import org.apache.struts2.url.StrutsUrlEncoder; @@ -339,45 +339,13 @@ public class DefaultUrlHelperTest extends StrutsInternalTestCase { assertEquals(expectedString, urlString); } - - public void testParseQuery() { - Map<String, Object> result = urlHelper.parseQueryString("aaa=aaaval&bbb=bbbval&ccc=&%3Ca%22%3E=%3Cval%3E", false); - - assertEquals(result.get("aaa"), "aaaval"); - assertEquals(result.get("bbb"), "bbbval"); - assertEquals(result.get("ccc"), ""); - assertEquals(result.get("<a\">"), "<val>"); - } - - public void testParseEmptyQuery() { - Map<String, Object> result = urlHelper.parseQueryString("", false); - - assertNotNull(result); - assertEquals(result.size(), 0); - } - - public void testParseNullQuery() { - Map<String, Object> result = urlHelper.parseQueryString(null, false); - - assertNotNull(result); - assertEquals(result.size(), 0); - } - - public void testDecodeSpacesInQueryString() { - Map<String, Object> queryParameters = urlHelper.parseQueryString("name=value+with+space", false); - - assertTrue(queryParameters.containsKey("name")); - assertEquals("value with space", queryParameters.get("name")); - } - - public void setUp() throws Exception { super.setUp(); StubContainer stubContainer = new StubContainer(container); ActionContext.getContext().withContainer(stubContainer); urlHelper = new DefaultUrlHelper(); StrutsUrlEncoder encoder = new StrutsUrlEncoder(); - urlHelper.setParametersStringBuilder(new StrutsParametersStringBuilder(encoder)); + urlHelper.setQueryStringBuilder(new StrutsQueryStringBuilder(encoder)); urlHelper.setEncoder(encoder); urlHelper.setDecoder(new StrutsUrlDecoder()); } diff --git a/plugins/embeddedjsp/src/main/java/org/apache/struts2/JSPRuntime.java b/plugins/embeddedjsp/src/main/java/org/apache/struts2/JSPRuntime.java index 81bbed490..d9425bbb6 100644 --- a/plugins/embeddedjsp/src/main/java/org/apache/struts2/JSPRuntime.java +++ b/plugins/embeddedjsp/src/main/java/org/apache/struts2/JSPRuntime.java @@ -20,8 +20,7 @@ package org.apache.struts2; import com.opensymphony.xwork2.ActionContext; import org.apache.struts2.dispatcher.Parameter; -import org.apache.struts2.views.util.DefaultUrlHelper; -import org.apache.struts2.views.util.UrlHelper; +import org.apache.struts2.url.QueryStringParser; import javax.servlet.Servlet; import javax.servlet.http.HttpServletRequest; @@ -53,11 +52,13 @@ public abstract class JSPRuntime { int i = location.indexOf("?"); if (i > 0) { //extract params from the url and add them to the request - final UrlHelper urlHelperGetInstance = ServletActionContext.getContext().getInstance(UrlHelper.class); - final UrlHelper contextUrlHelper = (urlHelperGetInstance != null ? urlHelperGetInstance : (UrlHelper) ActionContext.getContext().get(StrutsConstants.STRUTS_URL_HELPER)); - final UrlHelper urlHelper = (contextUrlHelper != null ? contextUrlHelper : new DefaultUrlHelper()); + ActionContext actionContext = ServletActionContext.getActionContext(); + if (actionContext == null) { + throw new StrutsException("Running out of action context!"); + } + final QueryStringParser parser = actionContext.getInstance(QueryStringParser.class); String query = location.substring(i + 1); - Map<String, Object> queryParams = urlHelper.parseQueryString(query, true); + Map<String, Object> queryParams = parser.parse(query, true); if (queryParams != null && !queryParams.isEmpty()) { Map<String, Parameter> newParams = new HashMap<>(); for (Map.Entry<String, Object> entry : queryParams.entrySet()) { diff --git a/plugins/embeddedjsp/src/test/java/org/apache/struts2/EmbeddedJSPResultTest.java b/plugins/embeddedjsp/src/test/java/org/apache/struts2/EmbeddedJSPResultTest.java index 6cde3f7d6..4a8eee285 100644 --- a/plugins/embeddedjsp/src/test/java/org/apache/struts2/EmbeddedJSPResultTest.java +++ b/plugins/embeddedjsp/src/test/java/org/apache/struts2/EmbeddedJSPResultTest.java @@ -35,10 +35,9 @@ import junit.framework.TestCase; import org.apache.commons.lang3.StringUtils; import org.apache.struts2.dispatcher.HttpParameters; import org.apache.struts2.jasper.runtime.InstanceHelper; +import org.apache.struts2.url.QueryStringParser; +import org.apache.struts2.url.StrutsQueryStringParser; import org.apache.struts2.url.StrutsUrlDecoder; -import org.apache.struts2.url.StrutsUrlEncoder; -import org.apache.struts2.views.util.DefaultUrlHelper; -import org.apache.struts2.views.util.UrlHelper; import org.apache.tomcat.InstanceManager; import org.easymock.EasyMock; import org.springframework.mock.web.MockHttpServletRequest; @@ -58,6 +57,8 @@ import java.util.Map; import java.util.concurrent.BrokenBarrierException; import java.util.concurrent.CyclicBarrier; +import static org.apache.struts2.ServletActionContext.STRUTS_VALUESTACK_KEY; + public class EmbeddedJSPResultTest extends TestCase { private HttpServletRequest request; @@ -324,18 +325,19 @@ public class EmbeddedJSPResultTest extends TestCase { HttpSession session = EasyMock.createNiceMock(HttpSession.class); EasyMock.replay(session); + //mock value stack + ValueStack valueStack = EasyMock.createNiceMock(ValueStack.class); + EasyMock.expect(valueStack.getActionContext()).andReturn(ActionContext.getContext()).anyTimes(); + EasyMock.replay(valueStack); + EasyMock.expect(request.getSession()).andReturn(session).anyTimes(); EasyMock.expect(request.getParameterMap()).andReturn(params).anyTimes(); EasyMock.expect(request.getParameter("username")).andAnswer(() -> ActionContext.getContext().getParameters().get("username").getValue()); + EasyMock.expect(request.getAttribute(STRUTS_VALUESTACK_KEY)).andReturn(valueStack).anyTimes(); EasyMock.expect(request.getAttribute("something")).andReturn("somethingelse").anyTimes(); EasyMock.replay(request); - //mock value stack - ValueStack valueStack = EasyMock.createNiceMock(ValueStack.class); - EasyMock.expect(valueStack.getActionContext()).andReturn(ActionContext.getContext()).anyTimes(); - EasyMock.replay(valueStack); - //mock converter XWorkConverter converter = new DummyConverter(); @@ -350,10 +352,8 @@ public class EmbeddedJSPResultTest extends TestCase { EasyMock.expect(container.getInstanceNames(FileManager.class)).andReturn(new HashSet<>()).anyTimes(); EasyMock.expect(container.getInstance(FileManager.class)).andReturn(fileManager).anyTimes(); - DefaultUrlHelper urlHelper = new DefaultUrlHelper(); - urlHelper.setDecoder(new StrutsUrlDecoder()); - urlHelper.setEncoder(new StrutsUrlEncoder()); - EasyMock.expect(container.getInstance(UrlHelper.class)).andReturn(urlHelper).anyTimes(); + QueryStringParser queryStringParser = new StrutsQueryStringParser(new StrutsUrlDecoder()); + EasyMock.expect(container.getInstance(QueryStringParser.class)).andReturn(queryStringParser).anyTimes(); FileManagerFactory fileManagerFactory = new DummyFileManagerFactory(); EasyMock.expect(container.getInstance(FileManagerFactory.class)).andReturn(fileManagerFactory).anyTimes(); diff --git a/plugins/json/src/test/java/org/apache/struts2/json/JSONActionRedirectResultTest.java b/plugins/json/src/test/java/org/apache/struts2/json/JSONActionRedirectResultTest.java index 268ee6087..24dc6adf5 100644 --- a/plugins/json/src/test/java/org/apache/struts2/json/JSONActionRedirectResultTest.java +++ b/plugins/json/src/test/java/org/apache/struts2/json/JSONActionRedirectResultTest.java @@ -26,10 +26,9 @@ import com.opensymphony.xwork2.util.ValueStack; import org.apache.struts2.StrutsStatics; import org.apache.struts2.dispatcher.mapper.DefaultActionMapper; import org.apache.struts2.junit.StrutsTestCase; -import org.apache.struts2.url.StrutsParametersStringBuilder; -import org.apache.struts2.url.StrutsUrlDecoder; +import org.apache.struts2.url.QueryStringBuilder; +import org.apache.struts2.url.StrutsQueryStringBuilder; import org.apache.struts2.url.StrutsUrlEncoder; -import org.apache.struts2.views.util.DefaultUrlHelper; import org.springframework.mock.web.MockHttpServletRequest; import org.springframework.mock.web.MockHttpServletResponse; import org.springframework.mock.web.MockServletContext; @@ -37,7 +36,7 @@ import org.springframework.mock.web.MockServletContext; public class JSONActionRedirectResultTest extends StrutsTestCase { private DefaultActionMapper actionMapper; - private DefaultUrlHelper urlHelper; + private QueryStringBuilder queryStringBuilder; MockActionInvocation invocation; MockHttpServletResponse response; @@ -50,7 +49,7 @@ public class JSONActionRedirectResultTest extends StrutsTestCase { JSONActionRedirectResult result = new JSONActionRedirectResult(); result.setActionName("targetAction"); result.setActionMapper(actionMapper); - result.setUrlHelper(urlHelper); + result.setQueryStringBuilder(queryStringBuilder); Object action = new Object(); stack.push(action); @@ -69,7 +68,7 @@ public class JSONActionRedirectResultTest extends StrutsTestCase { JSONActionRedirectResult result = new JSONActionRedirectResult(); result.setActionName("targetAction"); result.setActionMapper(actionMapper); - result.setUrlHelper(urlHelper); + result.setQueryStringBuilder(queryStringBuilder); request.setParameter("struts.enableJSONValidation", "true"); request.setParameter("struts.validateOnly", "false"); @@ -89,7 +88,7 @@ public class JSONActionRedirectResultTest extends StrutsTestCase { JSONActionRedirectResult result = new JSONActionRedirectResult(); result.setActionName("targetAction"); result.setActionMapper(actionMapper); - result.setUrlHelper(urlHelper); + result.setQueryStringBuilder(queryStringBuilder); request.setParameter("struts.enableJSONValidation", "true"); request.setParameter("struts.validateOnly", "true"); @@ -126,10 +125,6 @@ public class JSONActionRedirectResultTest extends StrutsTestCase { this.invocation.setProxy(mockActionProxy); this.actionMapper = new DefaultActionMapper(); - this.urlHelper = new DefaultUrlHelper(); - StrutsUrlEncoder encoder = new StrutsUrlEncoder(); - this.urlHelper.setParametersStringBuilder(new StrutsParametersStringBuilder(encoder)); - this.urlHelper.setEncoder(encoder); - this.urlHelper.setDecoder(new StrutsUrlDecoder()); + this.queryStringBuilder = new StrutsQueryStringBuilder(new StrutsUrlEncoder()); } } diff --git a/plugins/portlet/src/main/java/org/apache/struts2/components/PortletUrlRenderer.java b/plugins/portlet/src/main/java/org/apache/struts2/components/PortletUrlRenderer.java index a286deb3e..754389f83 100644 --- a/plugins/portlet/src/main/java/org/apache/struts2/components/PortletUrlRenderer.java +++ b/plugins/portlet/src/main/java/org/apache/struts2/components/PortletUrlRenderer.java @@ -18,7 +18,6 @@ */ package org.apache.struts2.components; -import com.opensymphony.xwork2.ActionContext; import com.opensymphony.xwork2.ActionInvocation; import com.opensymphony.xwork2.inject.Inject; import org.apache.commons.lang3.StringUtils; @@ -27,6 +26,7 @@ import org.apache.struts2.dispatcher.mapper.ActionMapper; import org.apache.struts2.portlet.context.PortletActionContext; import org.apache.struts2.portlet.util.PortletUrlHelper; import org.apache.struts2.portlet.util.PortletUrlHelperJSR286; +import org.apache.struts2.url.QueryStringParser; import org.apache.struts2.views.util.UrlHelper; import javax.portlet.PortletMode; @@ -66,6 +66,11 @@ public class PortletUrlRenderer implements UrlRenderer { servletRenderer.setUrlHelper(urlHelper); } + @Inject + public void setQueryStringParser(QueryStringParser queryStringParser) { + this.servletRenderer.setQueryStringParser(queryStringParser); + } + /** * {@inheritDoc} */ @@ -75,37 +80,35 @@ public class PortletUrlRenderer implements UrlRenderer { return; } String result; - if (isPortletModeChange(urlComponent,PortletActionContext.getRequest().getPortletMode()) - && StringUtils.isEmpty(urlComponent.getNamespace())) - { - String mode = urlComponent.getPortletMode(); - PortletMode portletMode = new PortletMode(mode); - String action = urlComponent.getAction(); - if (StringUtils.isEmpty(action)) { - action = PortletActionContext.getModeActionMap().get(portletMode).getName(); - } - String modeNamespace = PortletActionContext.getModeNamespaceMap().get(portletMode); - result = portletUrlHelper.buildUrl(action, modeNamespace, urlComponent.getMethod(), - urlComponent.getParameters(), urlComponent.getPortletUrlType(), mode, urlComponent.getWindowState()); + if (isPortletModeChange(urlComponent, PortletActionContext.getRequest().getPortletMode()) + && StringUtils.isEmpty(urlComponent.getNamespace())) { + String mode = urlComponent.getPortletMode(); + PortletMode portletMode = new PortletMode(mode); + String action = urlComponent.getAction(); + if (StringUtils.isEmpty(action)) { + action = PortletActionContext.getModeActionMap().get(portletMode).getName(); + } + String modeNamespace = PortletActionContext.getModeNamespaceMap().get(portletMode); + result = portletUrlHelper.buildUrl(action, modeNamespace, urlComponent.getMethod(), + urlComponent.getParameters(), urlComponent.getPortletUrlType(), mode, urlComponent.getWindowState()); } else { - String namespace = urlComponent.determineNamespace(urlComponent.getNamespace(), urlComponent.getStack(), urlComponent.getHttpServletRequest()); - urlComponent.setNamespace(namespace); - if (onlyActionSpecified(urlComponent)) { - if (StringUtils.isNotEmpty(urlComponent.getAction())) { - String action = urlComponent.findString(urlComponent.getAction()); + String namespace = urlComponent.determineNamespace(urlComponent.getNamespace(), urlComponent.getStack(), urlComponent.getHttpServletRequest()); + urlComponent.setNamespace(namespace); + if (onlyActionSpecified(urlComponent)) { + if (StringUtils.isNotEmpty(urlComponent.getAction())) { + String action = urlComponent.findString(urlComponent.getAction()); result = portletUrlHelper.buildUrl(action, urlComponent.getNamespace(), urlComponent.getMethod(), - urlComponent.getParameters(), urlComponent.getPortletUrlType(), urlComponent.getPortletMode(), urlComponent.getWindowState()); - } - else { + urlComponent.getParameters(), urlComponent.getPortletUrlType(), urlComponent.getPortletMode(), urlComponent.getWindowState()); + } else { result = portletUrlHelper.buildUrl(urlComponent.getAction(), urlComponent.getNamespace(), urlComponent.getMethod(), - urlComponent.getParameters(), urlComponent.getPortletUrlType(), urlComponent.getPortletMode(), urlComponent.getWindowState()); - } - } else if (onlyValueSpecified(urlComponent)) { - result = portletUrlHelper.buildResourceUrl(urlComponent.getValue(), urlComponent.getParameters()); - } else { - result = createDefaultUrl(urlComponent); - } + urlComponent.getParameters(), urlComponent.getPortletUrlType(), urlComponent.getPortletMode(), urlComponent.getWindowState()); + } + } else if (onlyValueSpecified(urlComponent)) { + result = portletUrlHelper.buildResourceUrl(urlComponent.getValue(), urlComponent.getParameters()); + } else { + result = createDefaultUrl(urlComponent); + } } String anchor = urlComponent.getAnchor(); if (StringUtils.isNotEmpty(anchor)) { @@ -128,19 +131,19 @@ public class PortletUrlRenderer implements UrlRenderer { } } - boolean isPortletModeChange(UrlProvider urlComponent,PortletMode currentMode) { - if (StringUtils.isNotEmpty(urlComponent.getPortletMode())) { - PortletMode newPortletMode = new PortletMode(urlComponent.getPortletMode()); - return !(newPortletMode.equals(currentMode)); + boolean isPortletModeChange(UrlProvider urlComponent, PortletMode currentMode) { + if (StringUtils.isNotEmpty(urlComponent.getPortletMode())) { + PortletMode newPortletMode = new PortletMode(urlComponent.getPortletMode()); + return !(newPortletMode.equals(currentMode)); } - return false; - } + return false; + } private String createDefaultUrl(UrlProvider urlComponent) { ActionInvocation ai = urlComponent.getStack().getActionContext().getActionInvocation(); String action = ai.getProxy().getActionName(); return portletUrlHelper.buildUrl(action, urlComponent.getNamespace(), urlComponent.getMethod(), urlComponent.getParameters(), - urlComponent.getPortletUrlType(), urlComponent.getPortletMode(), urlComponent.getWindowState()); + urlComponent.getPortletUrlType(), urlComponent.getPortletMode(), urlComponent.getWindowState()); } private boolean onlyValueSpecified(UrlProvider urlComponent) { @@ -160,7 +163,7 @@ public class PortletUrlRenderer implements UrlRenderer { return; } String namespace = formComponent.determineNamespace(formComponent.namespace, formComponent.getStack(), - formComponent.request); + formComponent.request); String action; if (formComponent.action != null) { action = formComponent.findString(formComponent.action); @@ -177,7 +180,7 @@ public class PortletUrlRenderer implements UrlRenderer { } if (action != null) { String result = portletUrlHelper.buildUrl(action, namespace, null, - formComponent.getParameters(), type, formComponent.portletMode, formComponent.windowState); + formComponent.getParameters(), type, formComponent.portletMode, formComponent.windowState); formComponent.addParameter("action", result); diff --git a/plugins/portlet/src/main/java/org/apache/struts2/portlet/result/PortletActionRedirectResult.java b/plugins/portlet/src/main/java/org/apache/struts2/portlet/result/PortletActionRedirectResult.java index eaef67c0a..9ddd56733 100644 --- a/plugins/portlet/src/main/java/org/apache/struts2/portlet/result/PortletActionRedirectResult.java +++ b/plugins/portlet/src/main/java/org/apache/struts2/portlet/result/PortletActionRedirectResult.java @@ -21,11 +21,11 @@ package org.apache.struts2.portlet.result; import com.opensymphony.xwork2.ActionInvocation; import com.opensymphony.xwork2.config.entities.ResultConfig; import com.opensymphony.xwork2.inject.Inject; -import org.apache.struts2.result.ServletActionRedirectResult; import org.apache.struts2.dispatcher.mapper.ActionMapper; import org.apache.struts2.dispatcher.mapper.ActionMapping; import org.apache.struts2.portlet.PortletConstants; -import org.apache.struts2.views.util.UrlHelper; +import org.apache.struts2.result.ServletActionRedirectResult; +import org.apache.struts2.url.QueryStringBuilder; import javax.portlet.PortletMode; import java.util.Arrays; @@ -34,11 +34,10 @@ import java.util.List; import java.util.Map; /** - * * Portlet modification of the {@link ServletActionRedirectResult}. - * + * <p> * <!-- START SNIPPET: description --> - * + * <p> * This result uses the {@link ActionMapper} provided by the * <code>ActionMapperFactory</code> to instruct the render phase to invoke the * specified action and (optional) namespace. This is better than the @@ -48,31 +47,31 @@ import java.util.Map; * and your application will still work. It is strongly recommended that if you * are redirecting to another action, you use this result rather than the * standard redirect result. - * + * <p> * See examples below for an example of how request parameters could be passed * in. - * + * <p> * <!-- END SNIPPET: description --> - * + * <p> * <b>This result type takes the following parameters:</b> - * + * <p> * <!-- START SNIPPET: params --> - * + * * <ul> - * + * * <li><b>actionName (default)</b> - the name of the action that will be * redirect to</li> - * + * * <li><b>namespace</b> - used to determine which namespace the action is in * that we're redirecting to . If namespace is null, this defaults to the * current namespace</li> - * + * * </ul> - * + * <p> * <!-- END SNIPPET: params --> - * + * <p> * <b>Example:</b> - * + * * <pre> * <!-- START SNIPPET: example --> * <package name="public" extends="struts-default"> @@ -84,19 +83,19 @@ import java.util.Map; * </result> * </action> * </package> - * + * * <package name="secure" extends="struts-default" namespace="/secure"> * <-- Redirect to an action in the same namespace --> * <action name="dashboard" class="..."> * <result>dashboard.jsp</result> * <result name="error" type="redirect-action">error</result> * </action> - * + * * <action name="error" class="..."> * <result>error.jsp</result> * </action> * </package> - * + * * <package name="passingRequestParameters" extends="struts-default" namespace="/passingRequestParameters"> * <-- Pass parameters (reportType, width and height) --> * <!-- @@ -113,106 +112,109 @@ import java.util.Map; * </result> * </action> * </package> - * - * + * + * * <!-- END SNIPPET: example --> * </pre> - * + * * @see ActionMapper */ public class PortletActionRedirectResult extends PortletResult { - private static final long serialVersionUID = -7627388936683562557L; + private static final long serialVersionUID = -7627388936683562557L; - /** The default parameter */ - public static final String DEFAULT_PARAM = "actionName"; + /** + * The default parameter + */ + public static final String DEFAULT_PARAM = "actionName"; - protected String actionName; - protected String namespace; - protected String method; + protected String actionName; + protected String namespace; + protected String method; - private Map<String, Object> requestParameters = new LinkedHashMap<String, Object>(); - private ActionMapper actionMapper; - private UrlHelper urlHelper; + private final Map<String, Object> requestParameters = new LinkedHashMap<String, Object>(); - public PortletActionRedirectResult() { - super(); - } + private ActionMapper actionMapper; + private QueryStringBuilder queryStringBuilder; - public PortletActionRedirectResult(String actionName) { - this(null, actionName, null); - } + public PortletActionRedirectResult() { + super(); + } - public PortletActionRedirectResult(String actionName, String method) { - this(null, actionName, method); - } + public PortletActionRedirectResult(String actionName) { + this(null, actionName, null); + } - public PortletActionRedirectResult(String namespace, String actionName, String method) { - super(null); - this.namespace = namespace; - this.actionName = actionName; - this.method = method; - } + public PortletActionRedirectResult(String actionName, String method) { + this(null, actionName, method); + } - protected List<String> prohibitedResultParam = Arrays.asList(DEFAULT_PARAM, "namespace", "method", "encode", "parse", - "location", "prependServletContext"); + public PortletActionRedirectResult(String namespace, String actionName, String method) { + super(null); + this.namespace = namespace; + this.actionName = actionName; + this.method = method; + } - @Inject - public void setActionMapper(ActionMapper actionMapper) { - this.actionMapper = actionMapper; - } + protected List<String> prohibitedResultParam = Arrays.asList(DEFAULT_PARAM, "namespace", "method", "encode", "parse", + "location", "prependServletContext"); @Inject - public void setUrlHelper(UrlHelper urlHelper) { - this.urlHelper = urlHelper; + public void setActionMapper(ActionMapper actionMapper) { + this.actionMapper = actionMapper; + } + + @Inject + public void setQueryStringBuilder(QueryStringBuilder queryStringBuilder) { + this.queryStringBuilder = queryStringBuilder; } /** - * @see com.opensymphony.xwork2.Result#execute(com.opensymphony.xwork2.ActionInvocation) - */ - public void execute(ActionInvocation invocation) throws Exception { - if (invocation == null) { - throw new IllegalArgumentException("Invocation cannot be null!"); - } + * @see com.opensymphony.xwork2.Result#execute(com.opensymphony.xwork2.ActionInvocation) + */ + public void execute(ActionInvocation invocation) throws Exception { + if (invocation == null) { + throw new IllegalArgumentException("Invocation cannot be null!"); + } - actionName = conditionalParse(actionName, invocation); - parseLocation = false; + actionName = conditionalParse(actionName, invocation); + parseLocation = false; - String portletNamespace = (String)invocation.getInvocationContext().get(PortletConstants.PORTLET_NAMESPACE); - if (portletMode != null) { - Map<PortletMode, String> namespaceMap = getNamespaceMap(invocation); - namespace = namespaceMap.get(portletMode); - } - if (namespace == null) { - namespace = invocation.getProxy().getNamespace(); - } else { - namespace = conditionalParse(namespace, invocation); - } - if (method == null) { - method = ""; - } else { - method = conditionalParse(method, invocation); - } + String portletNamespace = (String) invocation.getInvocationContext().get(PortletConstants.PORTLET_NAMESPACE); + if (portletMode != null) { + Map<PortletMode, String> namespaceMap = getNamespaceMap(invocation); + namespace = namespaceMap.get(portletMode); + } + if (namespace == null) { + namespace = invocation.getProxy().getNamespace(); + } else { + namespace = conditionalParse(namespace, invocation); + } + if (method == null) { + method = ""; + } else { + method = conditionalParse(method, invocation); + } - String resultCode = invocation.getResultCode(); - if (resultCode != null) { - ResultConfig resultConfig = invocation.getProxy().getConfig().getResults().get(resultCode); - Map<String, String> resultConfigParams = resultConfig.getParams(); + String resultCode = invocation.getResultCode(); + if (resultCode != null) { + ResultConfig resultConfig = invocation.getProxy().getConfig().getResults().get(resultCode); + Map<String, String> resultConfigParams = resultConfig.getParams(); for (Map.Entry<String, String> e : resultConfigParams.entrySet()) { if (!prohibitedResultParam.contains(e.getKey())) { requestParameters.put(e.getKey(), e.getValue() == null ? "" : conditionalParse(e.getValue(), invocation)); } } - } + } - StringBuilder tmpLocation = new StringBuilder(actionMapper.getUriFromActionMapping(new ActionMapping(actionName, - (portletNamespace == null ? namespace : portletNamespace + namespace), method, null))); - urlHelper.buildParametersString(requestParameters, tmpLocation, "&"); + ActionMapping actionMapping = new ActionMapping(actionName, (portletNamespace == null ? namespace : portletNamespace + namespace), method, null); + StringBuilder tmpLocation = new StringBuilder(actionMapper.getUriFromActionMapping(actionMapping)); + queryStringBuilder.build(requestParameters, tmpLocation, "&"); - setLocation(tmpLocation.toString()); + setLocation(tmpLocation.toString()); - super.execute(invocation); - } + super.execute(invocation); + } @SuppressWarnings("unchecked") private Map<PortletMode, String> getNamespaceMap(ActionInvocation invocation) { @@ -220,43 +222,42 @@ public class PortletActionRedirectResult extends PortletResult { } /** - * Sets the action name - * - * @param actionName The name - */ - public void setActionName(String actionName) { - this.actionName = actionName; - } + * Sets the action name + * + * @param actionName The name + */ + public void setActionName(String actionName) { + this.actionName = actionName; + } - /** - * Sets the namespace - * - * @param namespace The namespace - */ - public void setNamespace(String namespace) { - this.namespace = namespace; - } + /** + * Sets the namespace + * + * @param namespace The namespace + */ + public void setNamespace(String namespace) { + this.namespace = namespace; + } - /** - * Sets the method - * - * @param method The method - */ - public void setMethod(String method) { - this.method = method; - } + /** + * Sets the method + * + * @param method The method + */ + public void setMethod(String method) { + this.method = method; + } - /** - * Adds a request parameter to be added to the redirect url - * - * @param key The parameter name - * @param value The parameter value - * - * @return the portlet action redirect result - */ - public PortletActionRedirectResult addParameter(String key, Object value) { - requestParameters.put(key, String.valueOf(value)); - return this; - } + /** + * Adds a request parameter to be added to the redirect url + * + * @param key The parameter name + * @param value The parameter value + * @return the portlet action redirect result + */ + public PortletActionRedirectResult addParameter(String key, Object value) { + requestParameters.put(key, String.valueOf(value)); + return this; + } }