diff --git a/core/src/main/java/org/apache/struts2/StrutsConstants.java b/core/src/main/java/org/apache/struts2/StrutsConstants.java index d20858b73..c72089442 100644 --- a/core/src/main/java/org/apache/struts2/StrutsConstants.java +++ b/core/src/main/java/org/apache/struts2/StrutsConstants.java @@ -223,4 +223,8 @@ public final class StrutsConstants { /** Enables caching of parsed OGNL expressions **/ public static final String STRUTS_ENABLE_OGNL_EXPRESSION_CACHE = "struts.ognl.enableExpressionCache"; + + /** The{@link org.apache.struts2.views.util.UrlHelper} implementation class **/ + public static final String STRUTS_URL_HELPER = "struts.view.urlHelper"; + } diff --git a/core/src/main/java/org/apache/struts2/components/Component.java b/core/src/main/java/org/apache/struts2/components/Component.java index f0ee2d6a8..e7bdc3672 100644 --- a/core/src/main/java/org/apache/struts2/components/Component.java +++ b/core/src/main/java/org/apache/struts2/components/Component.java @@ -21,19 +21,11 @@ package org.apache.struts2.components; -import java.io.IOException; -import java.io.PrintWriter; -import java.io.Writer; -import java.util.Iterator; -import java.util.LinkedHashMap; -import java.util.Map; -import java.util.Stack; - -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; - -import org.apache.struts2.StrutsException; +import com.opensymphony.xwork2.inject.Inject; +import com.opensymphony.xwork2.util.TextParseUtil; +import com.opensymphony.xwork2.util.ValueStack; import org.apache.struts2.StrutsConstants; +import org.apache.struts2.StrutsException; import org.apache.struts2.dispatcher.mapper.ActionMapper; import org.apache.struts2.dispatcher.mapper.ActionMapping; import org.apache.struts2.util.FastByteArrayOutputStream; @@ -41,9 +33,14 @@ import org.apache.struts2.views.jsp.TagUtils; import org.apache.struts2.views.util.ContextUtil; import org.apache.struts2.views.util.UrlHelper; -import com.opensymphony.xwork2.inject.Inject; -import com.opensymphony.xwork2.util.ValueStack; -import com.opensymphony.xwork2.util.TextParseUtil; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import java.io.IOException; +import java.io.PrintWriter; +import java.io.Writer; +import java.util.LinkedHashMap; +import java.util.Map; +import java.util.Stack; /** * Base class to extend for UI components. @@ -52,12 +49,14 @@ import com.opensymphony.xwork2.util.TextParseUtil; * */ public class Component { + public static final String COMPONENT_STACK = "__component_stack"; protected ValueStack stack; protected Map parameters; protected ActionMapper actionMapper; protected boolean throwExceptionOnELFailure; + private UrlHelper urlHelper; /** * Constructor. @@ -91,7 +90,11 @@ public class Component { public void setThrowExceptionsOnELFailure(String throwException) { this.throwExceptionOnELFailure = "true".equals(throwException); } - + + @Inject + public void setUrlHelper(UrlHelper urlHelper) { + this.urlHelper = urlHelper; + } /** * Gets the OGNL value stack assoicated with this component. * @return the OGNL value stack assoicated with this component. @@ -407,7 +410,7 @@ public class Component { String finalNamespace = determineNamespace(namespace, getStack(), req); ActionMapping mapping = new ActionMapping(finalAction, finalNamespace, finalMethod, parameters); String uri = actionMapper.getUriFromActionMapping(mapping); - return UrlHelper.buildUrl(uri, req, res, parameters, scheme, includeContext, encodeResult, forceAddSchemeHostAndPort, escapeAmp); + return urlHelper.buildUrl(uri, req, res, parameters, scheme, includeContext, encodeResult, forceAddSchemeHostAndPort, escapeAmp); } /** @@ -445,8 +448,8 @@ public class Component { stack.push(parameters); stack.push(this); try { - for (Iterator iterator = params.entrySet().iterator(); iterator.hasNext();) { - Map.Entry entry = (Map.Entry) iterator.next(); + for (Object o : params.entrySet()) { + Map.Entry entry = (Map.Entry) o; String key = (String) entry.getKey(); stack.setValue(key, entry.getValue()); } 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 c518172c7..149e9f696 100644 --- a/core/src/main/java/org/apache/struts2/components/ServletUrlRenderer.java +++ b/core/src/main/java/org/apache/struts2/components/ServletUrlRenderer.java @@ -27,16 +27,15 @@ import com.opensymphony.xwork2.config.entities.ActionConfig; import com.opensymphony.xwork2.inject.Inject; import com.opensymphony.xwork2.util.logging.Logger; import com.opensymphony.xwork2.util.logging.LoggerFactory; +import org.apache.commons.lang3.StringUtils; import org.apache.struts2.StrutsException; import org.apache.struts2.dispatcher.mapper.ActionMapper; import org.apache.struts2.dispatcher.mapper.ActionMapping; import org.apache.struts2.views.util.UrlHelper; -import org.apache.commons.lang3.StringUtils; import java.io.IOException; import java.io.Writer; import java.util.Collections; -import java.util.Iterator; import java.util.LinkedHashMap; import java.util.Map; @@ -51,12 +50,17 @@ public class ServletUrlRenderer implements UrlRenderer { private static final Logger LOG = LoggerFactory.getLogger(ServletUrlRenderer.class); private ActionMapper actionMapper; + private UrlHelper urlHelper; @Inject public void setActionMapper(ActionMapper mapper) { this.actionMapper = mapper; } + @Inject + public void setUrlHelper(UrlHelper urlHelper) { + this.urlHelper = urlHelper; + } /** * {@inheritDoc} @@ -87,7 +91,7 @@ public class ServletUrlRenderer implements UrlRenderer { if (_value != null && _value.indexOf("?") > 0) { _value = _value.substring(0, _value.indexOf("?")); } - result = UrlHelper.buildUrl(_value, urlComponent.getHttpServletRequest(), urlComponent.getHttpServletResponse(), urlComponent.getParameters(), scheme, urlComponent.isIncludeContext(), urlComponent.isEncode(), urlComponent.isForceAddSchemeHostAndPort(), urlComponent.isEscapeAmp()); + result = urlHelper.buildUrl(_value, urlComponent.getHttpServletRequest(), urlComponent.getHttpServletResponse(), urlComponent.getParameters(), scheme, urlComponent.isIncludeContext(), urlComponent.isEncode(), urlComponent.isForceAddSchemeHostAndPort(), urlComponent.isEscapeAmp()); } String anchor = urlComponent.getAnchor(); if (StringUtils.isNotEmpty(anchor)) { @@ -121,8 +125,7 @@ public class ServletUrlRenderer implements UrlRenderer { * {@inheritDoc} */ public void renderFormUrl(Form formComponent) { - String namespace = formComponent.determineNamespace(formComponent.namespace, formComponent.getStack(), - formComponent.request); + String namespace = formComponent.determineNamespace(formComponent.namespace, formComponent.getStack(), formComponent.request); String action; if(formComponent.action != null) { @@ -151,8 +154,8 @@ public class ServletUrlRenderer implements UrlRenderer { if (actionConfig != null) { ActionMapping mapping = new ActionMapping(actionName, namespace, actionMethod, formComponent.parameters); - String result = UrlHelper.buildUrl(formComponent.actionMapper.getUriFromActionMapping(mapping), - formComponent.request, formComponent.response, null, null, formComponent.includeContext, true); + String result = urlHelper.buildUrl(formComponent.actionMapper.getUriFromActionMapping(mapping), + formComponent.request, formComponent.response, null, null, formComponent.includeContext, true); formComponent.addParameter("action", result); // let's try to get the actual action class and name @@ -187,7 +190,7 @@ public class ServletUrlRenderer implements UrlRenderer { LOG.warn("No configuration found for the specified action: '" + actionName + "' in namespace: '" + namespace + "'. Form action defaulting to 'action' attribute's literal value."); } - String result = UrlHelper.buildUrl(action, formComponent.request, formComponent.response, null, null, formComponent.includeContext, true); + String result = urlHelper.buildUrl(action, formComponent.request, formComponent.response, null, null, formComponent.includeContext, true); formComponent.addParameter("action", result); // namespace: cut out anything between the start and the last / @@ -236,7 +239,7 @@ public class ServletUrlRenderer implements UrlRenderer { } if (UrlProvider.NONE.equalsIgnoreCase(includeParams)) { - mergeRequestParameters(urlComponent.getValue(), urlComponent.getParameters(), Collections.EMPTY_MAP); + mergeRequestParameters(urlComponent.getValue(), urlComponent.getParameters(), Collections.emptyMap()); } else if (UrlProvider.ALL.equalsIgnoreCase(includeParams)) { mergeRequestParameters(urlComponent.getValue(), urlComponent.getParameters(), urlComponent.getHttpServletRequest().getParameterMap()); @@ -267,7 +270,7 @@ public class ServletUrlRenderer implements UrlRenderer { } private void includeGetParameters(UrlProvider urlComponent) { String query = extractQueryString(urlComponent); - mergeRequestParameters(urlComponent.getValue(), urlComponent.getParameters(), UrlHelper.parseQueryString(query)); + mergeRequestParameters(urlComponent.getValue(), urlComponent.getParameters(), urlHelper.parseQueryString(query)); } private String extractQueryString(UrlProvider urlComponent) { @@ -304,9 +307,9 @@ public class ServletUrlRenderer implements UrlRenderer { * @param parameters component parameters * @param contextParameters request parameters */ - protected void mergeRequestParameters(String value, Map parameters, Map contextParameters){ + protected void mergeRequestParameters(String value, Map parameters, Map contextParameters){ - Map mergedParams = new LinkedHashMap(contextParameters); + Map mergedParams = new LinkedHashMap(contextParameters); // Merge contextParameters (from current request) with parameters specified in value attribute // eg. value="someAction.action?id=someId&venue=someVenue" @@ -315,30 +318,23 @@ public class ServletUrlRenderer implements UrlRenderer { if (value != null && value.trim().length() > 0 && value.indexOf("?") > 0) { String queryString = value.substring(value.indexOf("?")+1); - mergedParams = UrlHelper.parseQueryString(queryString); - for (Iterator iterator = contextParameters.entrySet().iterator(); iterator.hasNext();) { - Map.Entry entry = (Map.Entry) iterator.next(); - Object key = entry.getKey(); - - if (!mergedParams.containsKey(key)) { - mergedParams.put(key, entry.getValue()); + mergedParams = urlHelper.parseQueryString(queryString); + for (Map.Entry entry : contextParameters.entrySet()) { + if (!mergedParams.containsKey(entry.getKey())) { + mergedParams.put(entry.getKey(), entry.getValue()); } } } - // Merge parameters specified in value attribute // eg. value="someAction.action?id=someId&venue=someVenue" // with parameters specified though param tag // eg. // where parameters specified through param tag takes priority. - for (Iterator iterator = mergedParams.entrySet().iterator(); iterator.hasNext();) { - Map.Entry entry = (Map.Entry) iterator.next(); - Object key = entry.getKey(); - - if (!parameters.containsKey(key)) { - parameters.put(key, entry.getValue()); + for (Map.Entry entry : mergedParams.entrySet()) { + if (!parameters.containsKey(entry.getKey())) { + parameters.put(entry.getKey(), entry.getValue()); } } } diff --git a/core/src/main/java/org/apache/struts2/config/BeanSelectionProvider.java b/core/src/main/java/org/apache/struts2/config/BeanSelectionProvider.java index 6366c0cd2..d7c40661b 100644 --- a/core/src/main/java/org/apache/struts2/config/BeanSelectionProvider.java +++ b/core/src/main/java/org/apache/struts2/config/BeanSelectionProvider.java @@ -21,17 +21,6 @@ package org.apache.struts2.config; -import java.util.Properties; -import java.util.StringTokenizer; - -import org.apache.struts2.StrutsConstants; -import org.apache.struts2.components.UrlRenderer; -import org.apache.struts2.dispatcher.StaticContentLoader; -import org.apache.struts2.dispatcher.mapper.ActionMapper; -import org.apache.struts2.dispatcher.multipart.MultiPartRequest; -import org.apache.struts2.views.freemarker.FreemarkerManager; -import org.apache.struts2.views.velocity.VelocityManager; - import com.opensymphony.xwork2.ActionProxyFactory; import com.opensymphony.xwork2.ObjectFactory; import com.opensymphony.xwork2.TextProvider; @@ -46,13 +35,27 @@ import com.opensymphony.xwork2.inject.ContainerBuilder; import com.opensymphony.xwork2.inject.Context; import com.opensymphony.xwork2.inject.Factory; import com.opensymphony.xwork2.inject.Scope; -import com.opensymphony.xwork2.util.*; +import com.opensymphony.xwork2.util.ClassLoaderUtil; +import com.opensymphony.xwork2.util.LocalizedTextUtil; +import com.opensymphony.xwork2.util.PatternMatcher; +import com.opensymphony.xwork2.util.ValueStackFactory; import com.opensymphony.xwork2.util.location.LocatableProperties; import com.opensymphony.xwork2.util.logging.Logger; import com.opensymphony.xwork2.util.logging.LoggerFactory; import com.opensymphony.xwork2.util.reflection.ReflectionContextFactory; import com.opensymphony.xwork2.util.reflection.ReflectionProvider; import com.opensymphony.xwork2.validator.ActionValidatorManager; +import org.apache.struts2.StrutsConstants; +import org.apache.struts2.components.UrlRenderer; +import org.apache.struts2.dispatcher.StaticContentLoader; +import org.apache.struts2.dispatcher.mapper.ActionMapper; +import org.apache.struts2.dispatcher.multipart.MultiPartRequest; +import org.apache.struts2.views.freemarker.FreemarkerManager; +import org.apache.struts2.views.util.UrlHelper; +import org.apache.struts2.views.velocity.VelocityManager; + +import java.util.Properties; +import java.util.StringTokenizer; /** * Selects the implementations of key framework extension points, using the loaded @@ -178,7 +181,9 @@ import com.opensymphony.xwork2.validator.ActionValidatorManager; * */ public class BeanSelectionProvider implements ConfigurationProvider { + public static final String DEFAULT_BEAN_NAME = "struts"; + private static final Logger LOG = LoggerFactory.getLogger(BeanSelectionProvider.class); public void destroy() { @@ -191,7 +196,6 @@ public class BeanSelectionProvider implements ConfigurationProvider { public void init(Configuration configuration) throws ConfigurationException { // NO-OP - } public boolean needsReload() { @@ -216,6 +220,7 @@ public class BeanSelectionProvider implements ConfigurationProvider { alias(PatternMatcher.class, StrutsConstants.STRUTS_PATTERNMATCHER, builder, props); alias(StaticContentLoader.class, StrutsConstants.STRUTS_STATIC_CONTENT_LOADER, builder, props); alias(UnknownHandlerManager.class, StrutsConstants.STRUTS_UNKNOWN_HANDLER_MANAGER, builder, props); + alias(UrlHelper.class, StrutsConstants.STRUTS_URL_HELPER, builder, props); if ("true".equalsIgnoreCase(props.getProperty(StrutsConstants.STRUTS_DEVMODE))) { props.setProperty(StrutsConstants.STRUTS_I18N_RELOAD, "true"); @@ -271,7 +276,7 @@ public class BeanSelectionProvider implements ConfigurationProvider { String foundName = props.getProperty(key, DEFAULT_BEAN_NAME); if (builder.contains(type, foundName)) { if (LOG.isInfoEnabled()) { - LOG.info("Choosing bean ("+foundName+") for "+type); + LOG.info("Choosing bean (" + foundName + ") for " + type); } builder.alias(type, foundName, Container.DEFAULT_NAME); } else { @@ -305,8 +310,10 @@ public class BeanSelectionProvider implements ConfigurationProvider { } static class ObjectFactoryDelegateFactory implements Factory { + String name; Class type; + ObjectFactoryDelegateFactory(String name, Class type) { this.name = name; this.type = type; @@ -320,5 +327,7 @@ public class BeanSelectionProvider implements ConfigurationProvider { throw new ConfigurationException("Unable to load bean "+type.getName()+" ("+name+")"); } } + } + } diff --git a/core/src/main/java/org/apache/struts2/dispatcher/ServletActionRedirectResult.java b/core/src/main/java/org/apache/struts2/dispatcher/ServletActionRedirectResult.java index 174acd77e..b41c06ea4 100644 --- a/core/src/main/java/org/apache/struts2/dispatcher/ServletActionRedirectResult.java +++ b/core/src/main/java/org/apache/struts2/dispatcher/ServletActionRedirectResult.java @@ -21,20 +21,17 @@ package org.apache.struts2.dispatcher; -import java.util.Arrays; -import java.util.List; - +import com.opensymphony.xwork2.ActionInvocation; +import com.opensymphony.xwork2.util.reflection.ReflectionExceptionHandler; import org.apache.struts2.dispatcher.mapper.ActionMapper; import org.apache.struts2.dispatcher.mapper.ActionMapping; -import com.opensymphony.xwork2.ActionInvocation; -import com.opensymphony.xwork2.util.logging.Logger; -import com.opensymphony.xwork2.util.logging.LoggerFactory; -import com.opensymphony.xwork2.util.reflection.ReflectionExceptionHandler; +import java.util.Arrays; +import java.util.List; /** * - * + * * This result uses the {@link ActionMapper} provided by the * {@link ActionMapperFactory} to redirect the browser to a URL that invokes the * specified action and (optional) namespace. This is better than the @@ -44,40 +41,40 @@ import com.opensymphony.xwork2.util.reflection.ReflectionExceptionHandler; * 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. - * + * * See examples below for an example of how request parameters could be passed * in. - * + * * - * + * * This result type takes the following parameters: - * + * * - * + * *
    - * + * *
  • actionName (default) - The name of the action that will be * redirected to.
  • - * + * *
  • namespace - Used to determine which namespace the action is in * that we're redirecting to. If namespace is null, the default will be the * current namespace.
  • - * + * *
  • suppressEmptyParameters - Optional boolean (defaults to false) that * can prevent parameters with no values from being included in the redirect * URL.
  • * - *
  • parse - Boolean, true by default. If set to false, the actionName + *
  • parse - Boolean, true by default. If set to false, the actionName * param will not be parsed for Ognl expressions.
  • * - *
  • anchor - Optional. Also known as "fragment" or colloquially as + *
  • anchor - Optional. Also known as "fragment" or colloquially as * "hash". You can specify an anchor for a result.
  • *
- * + * * - * + * * Example: - * + * *
  * 
  * <package name="public" extends="struts-default">
@@ -89,19 +86,19 @@ import com.opensymphony.xwork2.util.reflection.ReflectionExceptionHandler;
  *         </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="redirectAction">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) -->
  *    <!--
@@ -121,18 +118,18 @@ import com.opensymphony.xwork2.util.reflection.ReflectionExceptionHandler;
  *       </result>
  *    </action>
  * </package>
- * 
- * 
+ *
+ *
  * 
  * 
- * + * * @see ActionMapper */ public class ServletActionRedirectResult extends ServletRedirectResult implements ReflectionExceptionHandler { private static final long serialVersionUID = -9042425229314584066L; - /** The default parameter */ + /* The default parameter */ public static final String DEFAULT_PARAM = "actionName"; protected String actionName; @@ -165,68 +162,55 @@ public class ServletActionRedirectResult extends ServletRedirectResult implement /** * @see com.opensymphony.xwork2.Result#execute(com.opensymphony.xwork2.ActionInvocation) */ - public void execute(ActionInvocation invocation) throws Exception - { + public void execute(ActionInvocation invocation) throws Exception { actionName = conditionalParse(actionName, invocation); - if (namespace == null) - { + if (namespace == null) { namespace = invocation.getProxy().getNamespace(); - } - else - { + } else { namespace = conditionalParse(namespace, invocation); } - if (method == null) - { + if (method == null) { method = ""; - } - else - { + } else { method = conditionalParse(method, invocation); } - StringBuilder tmpLocation = new StringBuilder(actionMapper.getUriFromActionMapping(new ActionMapping(actionName, namespace, method, null))); + String tmpLocation = actionMapper.getUriFromActionMapping(new ActionMapping(actionName, namespace, method, null)); - setLocation(tmpLocation.toString()); + setLocation(tmpLocation); super.execute(invocation); } /** * Sets the action name - * - * @param actionName - * The name + * + * @param actionName The name */ - public void setActionName(String actionName) - { + public void setActionName(String actionName) { this.actionName = actionName; } /** * Sets the namespace - * - * @param namespace - * The namespace + * + * @param namespace The namespace */ - public void setNamespace(String namespace) - { + public void setNamespace(String namespace) { this.namespace = namespace; } /** * Sets the method - * - * @param method - * The method + * + * @param method The method */ - public void setMethod(String method) - { + public void setMethod(String method) { this.method = method; } - protected List getProhibitedResultParams() - { + protected List getProhibitedResultParams() { return Arrays.asList(DEFAULT_PARAM, "namespace", "method", "encode", "parse", "location", "prependServletContext", "suppressEmptyParameters", "anchor"); } + } diff --git a/core/src/main/java/org/apache/struts2/dispatcher/ServletDispatcherResult.java b/core/src/main/java/org/apache/struts2/dispatcher/ServletDispatcherResult.java index 8463e290e..5d5164b3d 100644 --- a/core/src/main/java/org/apache/struts2/dispatcher/ServletDispatcherResult.java +++ b/core/src/main/java/org/apache/struts2/dispatcher/ServletDispatcherResult.java @@ -22,8 +22,10 @@ package org.apache.struts2.dispatcher; import com.opensymphony.xwork2.ActionInvocation; +import com.opensymphony.xwork2.inject.Inject; import com.opensymphony.xwork2.util.logging.Logger; import com.opensymphony.xwork2.util.logging.LoggerFactory; +import org.apache.commons.lang.StringUtils; import org.apache.commons.lang3.ObjectUtils; import org.apache.struts2.ServletActionContext; import org.apache.struts2.StrutsStatics; @@ -95,6 +97,8 @@ public class ServletDispatcherResult extends StrutsResultSupport { private static final Logger LOG = LoggerFactory.getLogger(ServletDispatcherResult.class); + private UrlHelper urlHelper; + public ServletDispatcherResult() { super(); } @@ -103,6 +107,11 @@ public class ServletDispatcherResult extends StrutsResultSupport { super(location); } + @Inject + public void setUrlHelper(UrlHelper urlHelper) { + this.urlHelper = urlHelper; + } + /** * Dispatches to the given location. Does its forward via a RequestDispatcher. If the * dispatch fails a 404 error will be sent back in the http response. @@ -128,11 +137,10 @@ public class ServletDispatcherResult extends StrutsResultSupport { //add parameters passed on the location to #parameters // see WW-2120 - if (invocation != null && finalLocation != null && finalLocation.length() > 0 - && finalLocation.indexOf("?") > 0) { + if (StringUtils.isNotEmpty(finalLocation) && finalLocation.indexOf("?") > 0) { String queryString = finalLocation.substring(finalLocation.indexOf("?") + 1); - Map parameters = (Map) invocation.getInvocationContext().getContextMap().get("parameters"); - Map queryParams = UrlHelper.parseQueryString(queryString, true); + Map parameters = getParameters(invocation); + Map queryParams = urlHelper.parseQueryString(queryString, true); if (queryParams != null && !queryParams.isEmpty()) parameters.putAll(queryParams); } @@ -140,7 +148,6 @@ public class ServletDispatcherResult extends StrutsResultSupport { // if the view doesn't exist, let's do a 404 if (dispatcher == null) { response.sendError(404, "result '" + finalLocation + "' not found"); - return; } @@ -160,4 +167,10 @@ public class ServletDispatcherResult extends StrutsResultSupport { } } } + + @SuppressWarnings("unchecked") + private Map getParameters(ActionInvocation invocation) { + return (Map) invocation.getInvocationContext().getContextMap().get("parameters"); + } + } diff --git a/core/src/main/java/org/apache/struts2/dispatcher/ServletRedirectResult.java b/core/src/main/java/org/apache/struts2/dispatcher/ServletRedirectResult.java index ee20e5c5f..a843a5bb9 100644 --- a/core/src/main/java/org/apache/struts2/dispatcher/ServletRedirectResult.java +++ b/core/src/main/java/org/apache/struts2/dispatcher/ServletRedirectResult.java @@ -100,17 +100,14 @@ public class ServletRedirectResult extends StrutsResultSupport implements Reflec private static final Logger LOG = LoggerFactory.getLogger(ServletRedirectResult.class); protected boolean prependServletContext = true; - protected ActionMapper actionMapper; - protected int statusCode = SC_FOUND; - protected boolean suppressEmptyParameters = false; - - protected Map requestParameters = new LinkedHashMap(); - + protected Map requestParameters = new LinkedHashMap(); protected String anchor; + private UrlHelper urlHelper; + public ServletRedirectResult() { super(); } @@ -125,13 +122,16 @@ public class ServletRedirectResult extends StrutsResultSupport implements Reflec } @Inject - public void setActionMapper(ActionMapper mapper) - { + public void setActionMapper(ActionMapper mapper) { this.actionMapper = mapper; } - public void setStatusCode(int code) - { + @Inject + public void setUrlHelper(UrlHelper urlHelper) { + this.urlHelper = urlHelper; + } + + public void setStatusCode(int code) { this.statusCode = code; } @@ -140,8 +140,7 @@ public class ServletRedirectResult extends StrutsResultSupport implements Reflec * * @param anchor */ - public void setAnchor(String anchor) - { + public void setAnchor(String anchor) { this.anchor = anchor; } @@ -149,22 +148,16 @@ public class ServletRedirectResult extends StrutsResultSupport implements Reflec * Sets whether or not to prepend the servlet context path to the redirected * URL. * - * @param prependServletContext - * true to prepend the location with the servlet context - * path, false otherwise. + * @param prependServletContext true to prepend the location with the servlet context path, false otherwise. */ - public void setPrependServletContext(boolean prependServletContext) - { + public void setPrependServletContext(boolean prependServletContext) { this.prependServletContext = prependServletContext; } - public void execute(ActionInvocation invocation) throws Exception - { - if (anchor != null) - { + public void execute(ActionInvocation invocation) throws Exception { + if (anchor != null) { anchor = conditionalParse(anchor, invocation); } - super.execute(invocation); } @@ -172,58 +165,43 @@ public class ServletRedirectResult extends StrutsResultSupport implements Reflec * Redirects to the location specified by calling * {@link HttpServletResponse#sendRedirect(String)}. * - * @param finalLocation - * the location to redirect to. - * @param invocation - * an encapsulation of the action execution state. - * @throws Exception - * if an error occurs when redirecting. + * @param finalLocation the location to redirect to. + * @param invocation an encapsulation of the action execution state. + * @throws Exception if an error occurs when redirecting. */ - protected void doExecute(String finalLocation, ActionInvocation invocation) throws Exception - { + protected void doExecute(String finalLocation, ActionInvocation invocation) throws Exception { ActionContext ctx = invocation.getInvocationContext(); HttpServletRequest request = (HttpServletRequest) ctx.get(ServletActionContext.HTTP_REQUEST); HttpServletResponse response = (HttpServletResponse) ctx.get(ServletActionContext.HTTP_RESPONSE); - if (isPathUrl(finalLocation)) - { - if (!finalLocation.startsWith("/")) - { + if (isPathUrl(finalLocation)) { + if (!finalLocation.startsWith("/")) { ActionMapping mapping = actionMapper.getMapping(request, Dispatcher.getInstance().getConfigurationManager()); String namespace = null; - if (mapping != null) - { + if (mapping != null) { namespace = mapping.getNamespace(); } - if ((namespace != null) && (namespace.length() > 0) && (!"/".equals(namespace))) - { + if ((namespace != null) && (namespace.length() > 0) && (!"/".equals(namespace))) { finalLocation = namespace + "/" + finalLocation; - } - else - { + } else { finalLocation = "/" + finalLocation; } } // if the URL's are relative to the servlet context, append the servlet context path - if (prependServletContext && (request.getContextPath() != null) && (request.getContextPath().length() > 0)) - { + if (prependServletContext && (request.getContextPath() != null) && (request.getContextPath().length() > 0)) { finalLocation = request.getContextPath() + finalLocation; } ResultConfig resultConfig = invocation.getProxy().getConfig().getResults().get(invocation.getResultCode()); - if (resultConfig != null) - { + if (resultConfig != null) { Map resultConfigParams = resultConfig.getParams(); - for (Map.Entry e : resultConfigParams.entrySet()) - { - if (!getProhibitedResultParams().contains(e.getKey())) - { + for (Map.Entry e : resultConfigParams.entrySet()) { + if (!getProhibitedResultParams().contains(e.getKey())) { String potentialValue = e.getValue() == null ? "" : conditionalParse(e.getValue(), invocation); - if (!suppressEmptyParameters || ((potentialValue != null) && (potentialValue.length() > 0))) - { + if (!suppressEmptyParameters || ((potentialValue != null) && (potentialValue.length() > 0))) { requestParameters.put(e.getKey(), potentialValue); } } @@ -231,27 +209,24 @@ public class ServletRedirectResult extends StrutsResultSupport implements Reflec } StringBuilder tmpLocation = new StringBuilder(finalLocation); - UrlHelper.buildParametersString(requestParameters, tmpLocation, "&"); + urlHelper.buildParametersString(requestParameters, tmpLocation, "&"); // add the anchor - if (anchor != null) - { + if (anchor != null) { tmpLocation.append('#').append(anchor); } finalLocation = response.encodeRedirectURL(tmpLocation.toString()); } - if (LOG.isDebugEnabled()) - { + if (LOG.isDebugEnabled()) { LOG.debug("Redirecting to finalLocation " + finalLocation); } sendRedirect(response, finalLocation); } - protected List getProhibitedResultParams() - { + protected List getProhibitedResultParams() { return Arrays.asList(DEFAULT_PARAM, "namespace", "method", "encode", "parse", "location", "prependServletContext", "suppressEmptyParameters", "anchor"); } @@ -259,20 +234,14 @@ public class ServletRedirectResult extends StrutsResultSupport implements Reflec * Sends the redirection. Can be overridden to customize how the redirect is * handled (i.e. to use a different status code) * - * @param response - * The response - * @param finalLocation - * The location URI + * @param response The response + * @param finalLocation The location URI * @throws IOException */ - protected void sendRedirect(HttpServletResponse response, String finalLocation) throws IOException - { - if (SC_FOUND == statusCode) - { + protected void sendRedirect(HttpServletResponse response, String finalLocation) throws IOException { + if (SC_FOUND == statusCode) { response.sendRedirect(finalLocation); - } - else - { + } else { response.setStatus(statusCode); response.setHeader("Location", finalLocation); response.getWriter().write(finalLocation); @@ -281,8 +250,7 @@ public class ServletRedirectResult extends StrutsResultSupport implements Reflec } - private static boolean isPathUrl(String url) - { + private static boolean isPathUrl(String url) { // filter out "http:", "https:", "mailto:", "file:", "ftp:" // since the only valid places for : in URL's is before the path specification // either before the port, or after the protocol @@ -292,31 +260,28 @@ public class ServletRedirectResult extends StrutsResultSupport implements Reflec /** * Sets the suppressEmptyParameters option * - * @param suppressEmptyParameters - * The new value for this option + * @param suppressEmptyParameters The new value for this option */ - public void setSuppressEmptyParameters(boolean suppressEmptyParameters) - { + public void setSuppressEmptyParameters(boolean suppressEmptyParameters) { this.suppressEmptyParameters = suppressEmptyParameters; } /** * Adds a request parameter to be added to the redirect url * - * @param key - * The parameter name - * @param value - * The parameter value + * @param key The parameter name + * @param value The parameter value */ - public ServletRedirectResult addParameter(String key, Object value) - { + public ServletRedirectResult addParameter(String key, Object value) { requestParameters.put(key, String.valueOf(value)); return this; } - public void handle(ReflectionException ex) - { + public void handle(ReflectionException ex) { // Only log as debug as they are probably parameters to be appended to the url - LOG.debug(ex.getMessage(), ex); + if (LOG.isDebugEnabled()) { + LOG.debug(ex.getMessage(), ex); + } } + } diff --git a/core/src/main/java/org/apache/struts2/util/StrutsUtil.java b/core/src/main/java/org/apache/struts2/util/StrutsUtil.java index c5f25e88e..f55e6f0c6 100644 --- a/core/src/main/java/org/apache/struts2/util/StrutsUtil.java +++ b/core/src/main/java/org/apache/struts2/util/StrutsUtil.java @@ -45,53 +45,49 @@ import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; import java.util.Hashtable; -import java.util.Iterator; import java.util.List; import java.util.Map; - /** * Struts base utility class, for use in Velocity and Freemarker templates - * */ public class StrutsUtil { protected static final Logger LOG = LoggerFactory.getLogger(StrutsUtil.class); - protected HttpServletRequest request; protected HttpServletResponse response; - protected Map classes = new Hashtable(); + protected Map classes = new Hashtable(); protected OgnlTool ognl; protected ValueStack stack; + private UrlHelper urlHelper; + private ObjectFactory objectFactory; public StrutsUtil(ValueStack stack, HttpServletRequest request, HttpServletResponse response) { this.stack = stack; this.request = request; this.response = response; this.ognl = ((Container)stack.getContext().get(ActionContext.CONTAINER)).getInstance(OgnlTool.class); + this.urlHelper = ((Container)stack.getContext().get(ActionContext.CONTAINER)).getInstance(UrlHelper.class); + this.objectFactory = ((Container)stack.getContext().get(ActionContext.CONTAINER)).getInstance(ObjectFactory.class); } - public Object bean(Object aName) throws Exception { String name = aName.toString(); - Class c = (Class) classes.get(name); + Class c = classes.get(name); if (c == null) { c = ClassLoaderUtil.loadClass(name, StrutsUtil.class); classes.put(name, c); } - return ObjectFactory.getObjectFactory().buildBean(c, stack.getContext()); + return objectFactory.buildBean(c, stack.getContext()); } public boolean isTrue(String expression) { Boolean retVal = (Boolean) stack.findValue(expression, Boolean.class); - if (retVal == null) { - return false; - } - return retVal.booleanValue(); + return retVal != null && retVal; } public Object findString(String name) { @@ -134,7 +130,7 @@ public class StrutsUtil { } public String buildUrl(String url) { - return UrlHelper.buildUrl(url, request, response, null); + return urlHelper.buildUrl(url, request, response, null); } public Object findValue(String expression, String className) throws ClassNotFoundException { @@ -190,9 +186,8 @@ public class StrutsUtil { Collection items = (Collection) stack.findValue(list); if (items != null) { - for (Iterator iter = items.iterator(); iter.hasNext();) { - Object element = (Object) iter.next(); - Object key = null; + for (Object element : items) { + Object key; if ((listKey == null) || (listKey.length() == 0)) { key = element; @@ -284,4 +279,5 @@ public class StrutsUtil { writer.write(aByte); } } + } diff --git a/core/src/main/java/org/apache/struts2/util/URLBean.java b/core/src/main/java/org/apache/struts2/util/URLBean.java index 795f37fe1..232c73e38 100644 --- a/core/src/main/java/org/apache/struts2/util/URLBean.java +++ b/core/src/main/java/org/apache/struts2/util/URLBean.java @@ -21,13 +21,14 @@ package org.apache.struts2.util; -import java.util.HashMap; -import java.util.Map; +import org.apache.struts2.ServletActionContext; +import org.apache.struts2.views.util.DefaultUrlHelper; +import org.apache.struts2.views.util.UrlHelper; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; - -import org.apache.struts2.views.util.UrlHelper; +import java.util.HashMap; +import java.util.Map; /** @@ -36,11 +37,12 @@ import org.apache.struts2.views.util.UrlHelper; */ public class URLBean { - HashMap params; + HashMap params; HttpServletRequest request; HttpServletResponse response; String page; + private UrlHelper urlHelper; public URLBean setPage(String page) { this.page = page; @@ -49,6 +51,7 @@ public class URLBean { public void setRequest(HttpServletRequest request) { this.request = request; + urlHelper = ServletActionContext.getContext().getInstance(DefaultUrlHelper.class); } public void setResponse(HttpServletResponse response) { @@ -57,10 +60,10 @@ public class URLBean { public String getURL() { // all this trickier with maps is to reduce the number of objects created - Map fullParams = null; + Map fullParams = null; if (params != null) { - fullParams = new HashMap(); + fullParams = new HashMap(); } if (page == null) { @@ -78,12 +81,12 @@ public class URLBean { fullParams.putAll(params); } - return UrlHelper.buildUrl(page, request, response, fullParams); + return urlHelper.buildUrl(page, request, response, fullParams); } public URLBean addParameter(String name, Object value) { if (params == null) { - params = new HashMap(); + params = new HashMap(); } if (value == null) { 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 new file mode 100644 index 000000000..7c41e4ffe --- /dev/null +++ b/core/src/main/java/org/apache/struts2/views/util/DefaultUrlHelper.java @@ -0,0 +1,335 @@ +/* + * $Id$ + * + * 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.views.util; + +import com.opensymphony.xwork2.ActionContext; +import com.opensymphony.xwork2.inject.Container; +import com.opensymphony.xwork2.inject.Inject; +import com.opensymphony.xwork2.util.TextParseUtil; +import com.opensymphony.xwork2.util.ValueStack; +import com.opensymphony.xwork2.util.logging.Logger; +import com.opensymphony.xwork2.util.logging.LoggerFactory; +import org.apache.commons.lang3.StringEscapeUtils; +import org.apache.commons.lang3.StringUtils; +import org.apache.struts2.ServletActionContext; +import org.apache.struts2.StrutsConstants; + +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import java.io.UnsupportedEncodingException; +import java.net.URLDecoder; +import java.net.URLEncoder; +import java.util.Arrays; +import java.util.Iterator; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; + +/** + * Default implementation of UrlHelper + */ +public class DefaultUrlHelper implements UrlHelper { + + private static final Logger LOG = LoggerFactory.getLogger(DefaultUrlHelper.class); + + private String encoding = "UTF-8"; + + @Inject(StrutsConstants.STRUTS_I18N_ENCODING) + public void setEncoding(String encoding) { + if (StringUtils.isNotEmpty(encoding)) { + this.encoding = encoding; + } + } + + public String buildUrl(String action, HttpServletRequest request, HttpServletResponse response, Map params) { + return buildUrl(action, request, response, params, null, true, true); + } + + public String buildUrl(String action, HttpServletRequest request, HttpServletResponse response, Map params, String scheme, + boolean includeContext, boolean encodeResult) { + return buildUrl(action, request, response, params, scheme, includeContext, encodeResult, false); + } + + public String buildUrl(String action, HttpServletRequest request, HttpServletResponse response, Map params, String scheme, + boolean includeContext, boolean encodeResult, boolean forceAddSchemeHostAndPort) { + return buildUrl(action, request, response, params, scheme, includeContext, encodeResult, forceAddSchemeHostAndPort, true); + } + + public String buildUrl(String action, HttpServletRequest request, HttpServletResponse response, Map params, String scheme, + boolean includeContext, boolean encodeResult, boolean forceAddSchemeHostAndPort, boolean escapeAmp) { + StringBuilder link = new StringBuilder(); + + boolean changedScheme = false; + + // FIXME: temporary hack until class is made a properly injected bean + Container cont = ActionContext.getContext().getContainer(); + int httpPort = Integer.parseInt(cont.getInstance(String.class, StrutsConstants.STRUTS_URL_HTTP_PORT)); + int httpsPort = Integer.parseInt(cont.getInstance(String.class, StrutsConstants.STRUTS_URL_HTTPS_PORT)); + + // only append scheme if it is different to the current scheme *OR* + // if we explicity want it to be appended by having forceAddSchemeHostAndPort = true + if (forceAddSchemeHostAndPort) { + String reqScheme = request.getScheme(); + changedScheme = true; + link.append(scheme != null ? scheme : reqScheme); + link.append("://"); + link.append(request.getServerName()); + + if (scheme != null) { + // If switching schemes, use the configured port for the particular scheme. + if (!scheme.equals(reqScheme)) { + if ((scheme.equals("http") && (httpPort != DEFAULT_HTTP_PORT)) || (scheme.equals("https") && httpsPort != DEFAULT_HTTPS_PORT)) { + link.append(":"); + link.append(scheme.equals("http") ? httpPort : httpsPort); + } + // Else use the port from the current request. + } else { + int reqPort = request.getServerPort(); + + if ((scheme.equals("http") && (reqPort != DEFAULT_HTTP_PORT)) || (scheme.equals("https") && reqPort != DEFAULT_HTTPS_PORT)) { + link.append(":"); + link.append(reqPort); + } + } + } + } + else if ((scheme != null) && !scheme.equals(request.getScheme())) { + changedScheme = true; + link.append(scheme); + link.append("://"); + link.append(request.getServerName()); + + if ((scheme.equals("http") && (httpPort != DEFAULT_HTTP_PORT)) || (scheme.equals("https") && httpsPort != DEFAULT_HTTPS_PORT)) + { + link.append(":"); + link.append(scheme.equals("http") ? httpPort : httpsPort); + } + } + + if (action != null) { + // Check if context path needs to be added + // Add path to absolute links + if (action.startsWith("/") && includeContext) { + String contextPath = request.getContextPath(); + if (!contextPath.equals("/")) { + link.append(contextPath); + } + } else if (changedScheme) { + + // (Applicable to Servlet 2.4 containers) + // If the request was forwarded, the attribute below will be set with the original URL + String uri = (String) request.getAttribute("javax.servlet.forward.request_uri"); + + // If the attribute wasn't found, default to the value in the request object + if (uri == null) { + uri = request.getRequestURI(); + } + + link.append(uri.substring(0, uri.lastIndexOf('/') + 1)); + } + + // Add page + link.append(action); + } else { + // Go to "same page" + String requestURI = (String) request.getAttribute("struts.request_uri"); + + // (Applicable to Servlet 2.4 containers) + // If the request was forwarded, the attribute below will be set with the original URL + if (requestURI == null) { + requestURI = (String) request.getAttribute("javax.servlet.forward.request_uri"); + } + + // If neither request attributes were found, default to the value in the request object + if (requestURI == null) { + requestURI = request.getRequestURI(); + } + + link.append(requestURI); + } + + //if the action was not explicitly set grab the params from the request + if (escapeAmp) { + buildParametersString(params, link); + } else { + buildParametersString(params, link, "&"); + } + + String result = link.toString(); + + if (StringUtils.containsIgnoreCase(result, " params, StringBuilder link) { + buildParametersString(params, link, AMP); + } + + public void buildParametersString(Map params, StringBuilder link, String paramSeparator) { + if ((params != null) && (params.size() > 0)) { + if (!link.toString().contains("?")) { + link.append("?"); + } else { + link.append(paramSeparator); + } + + // Set params + Iterator> iter = params.entrySet().iterator(); + while (iter.hasNext()) { + Map.Entry entry = iter.next(); + String name = entry.getKey(); + Object value = entry.getValue(); + + if (value instanceof Iterable) { + for (Iterator iterator = ((Iterable) value).iterator(); iterator + .hasNext();) { + Object paramValue = iterator.next(); + link.append(buildParameterSubstring(name, paramValue + .toString())); + + if (iterator.hasNext()) + link.append(paramSeparator); + } + } else if (value instanceof Object[]) { + Object[] array = (Object[]) value; + for (int i = 0; i < array.length; i++) { + Object paramValue = array[i]; + link.append(buildParameterSubstring(name, paramValue + .toString())); + + if (i < array.length - 1) + link.append(paramSeparator); + } + } else { + link.append(buildParameterSubstring(name, value != null ? value.toString() : StringUtils.EMPTY)); + } + + if (iter.hasNext()) { + link.append(paramSeparator); + } + } + } + } + + + private String buildParameterSubstring(String name, String value) { + StringBuilder builder = new StringBuilder(); + builder.append(translateAndEncode(name)); + builder.append('='); + builder.append(translateAndEncode(value)); + return builder.toString(); + } + + /** + * Translates any script expressions using {@link com.opensymphony.xwork2.util.TextParseUtil#translateVariables} and + * encodes the URL using {@link java.net.URLEncoder#encode} with the encoding specified in the configuration. + * + * @param input + * @return the translated and encoded string + */ + public String translateAndEncode(String input) { + String translatedInput = translateVariable(input); + try { + return URLEncoder.encode(translatedInput, encoding); + } catch (UnsupportedEncodingException e) { + if (LOG.isWarnEnabled()) { + LOG.warn("Could not encode URL parameter '" + input + "', returning value un-encoded"); + } + return translatedInput; + } + } + + public String translateAndDecode(String input) { + String translatedInput = translateVariable(input); + try { + return URLDecoder.decode(translatedInput, encoding); + } catch (UnsupportedEncodingException e) { + if (LOG.isWarnEnabled()) { + LOG.warn("Could not encode URL parameter '" + input + "', returning value un-encoded"); + } + return translatedInput; + } + } + + private String translateVariable(String input) { + ValueStack valueStack = ServletActionContext.getContext().getValueStack(); + return TextParseUtil.translateVariables(input, valueStack); + } + + public Map parseQueryString(String queryString) { + return parseQueryString(queryString, false); + } + + public Map parseQueryString(String queryString, boolean forceValueArray) { + Map 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 = translateAndDecode(paramName); + String translatedParamValue = translateAndDecode(paramValue); + + 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 paramList = Arrays.asList(currentParamValues); + paramList.add(translatedParamValue); + queryParams.put(paramName, paramList.toArray(new String[paramList.size()])); + } else { + queryParams.put(paramName, new String[]{translatedParamValue}); + } + } + } else { + queryParams.put(paramName, translatedParamValue); + } + } + } + } + } + return queryParams; + } +} 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 f294a4b65..f4052e1e0 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 @@ -1,364 +1,43 @@ -/* - * $Id$ - * - * 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.views.util; -import com.opensymphony.xwork2.ActionContext; -import com.opensymphony.xwork2.inject.Container; -import com.opensymphony.xwork2.util.TextParseUtil; -import com.opensymphony.xwork2.util.ValueStack; -import com.opensymphony.xwork2.util.logging.Logger; -import com.opensymphony.xwork2.util.logging.LoggerFactory; -import org.apache.commons.lang3.StringEscapeUtils; -import org.apache.commons.lang3.StringUtils; -import org.apache.struts2.ServletActionContext; -import org.apache.struts2.StrutsConstants; - import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; -import java.io.UnsupportedEncodingException; -import java.net.URLDecoder; -import java.net.URLEncoder; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Iterator; -import java.util.LinkedHashMap; -import java.util.List; import java.util.Map; - /** - * UrlHelper - * + * Helper class used to build Urls or parse request params */ -public class UrlHelper { - private static final Logger LOG = LoggerFactory.getLogger(UrlHelper.class); +public interface UrlHelper { /** * Default HTTP port (80). */ - private static final int DEFAULT_HTTP_PORT = 80; + static final int DEFAULT_HTTP_PORT = 80; /** * Default HTTPS port (443). */ - private static final int DEFAULT_HTTPS_PORT = 443; + static final int DEFAULT_HTTPS_PORT = 443; - private static final String AMP = "&"; + static final String AMP = "&"; - public static String buildUrl(String action, HttpServletRequest request, HttpServletResponse response, Map params) { - return buildUrl(action, request, response, params, null, true, true); - } + String buildUrl(String action, HttpServletRequest request, HttpServletResponse response, Map params); - public static String buildUrl(String action, HttpServletRequest request, HttpServletResponse response, Map params, String scheme, boolean includeContext, boolean encodeResult) { - return buildUrl(action, request, response, params, scheme, includeContext, encodeResult, false); - } + String buildUrl(String action, HttpServletRequest request, HttpServletResponse response, Map params, String scheme, + boolean includeContext, boolean encodeResult); - public static String buildUrl(String action, HttpServletRequest request, HttpServletResponse response, Map params, String scheme, boolean includeContext, boolean encodeResult, boolean forceAddSchemeHostAndPort) { - return buildUrl(action, request, response, params, scheme, includeContext, encodeResult, forceAddSchemeHostAndPort, true); - } + String buildUrl(String action, HttpServletRequest request, HttpServletResponse response, Map params, String scheme, + boolean includeContext, boolean encodeResult, boolean forceAddSchemeHostAndPort); - public static String buildUrl(String action, HttpServletRequest request, HttpServletResponse response, Map params, String scheme, boolean includeContext, boolean encodeResult, boolean forceAddSchemeHostAndPort, boolean escapeAmp) { - StringBuilder link = new StringBuilder(); + String buildUrl(String action, HttpServletRequest request, HttpServletResponse response, Map params, String scheme, + boolean includeContext, boolean encodeResult, boolean forceAddSchemeHostAndPort, boolean escapeAmp); - boolean changedScheme = false; + void buildParametersString(Map params, StringBuilder link); - // FIXME: temporary hack until class is made a properly injected bean - Container cont = ActionContext.getContext().getContainer(); - int httpPort = Integer.parseInt(cont.getInstance(String.class, StrutsConstants.STRUTS_URL_HTTP_PORT)); - int httpsPort = Integer.parseInt(cont.getInstance(String.class, StrutsConstants.STRUTS_URL_HTTPS_PORT)); + void buildParametersString(Map params, StringBuilder link, String paramSeparator); - // only append scheme if it is different to the current scheme *OR* - // if we explicity want it to be appended by having forceAddSchemeHostAndPort = true - if (forceAddSchemeHostAndPort) { - String reqScheme = request.getScheme(); - changedScheme = true; - link.append(scheme != null ? scheme : reqScheme); - link.append("://"); - link.append(request.getServerName()); + Map parseQueryString(String queryString); - if (scheme != null) { - // If switching schemes, use the configured port for the particular scheme. - if (!scheme.equals(reqScheme)) { - if ((scheme.equals("http") && (httpPort != DEFAULT_HTTP_PORT)) || (scheme.equals("https") && httpsPort != DEFAULT_HTTPS_PORT)) { - link.append(":"); - link.append(scheme.equals("http") ? httpPort : httpsPort); - } - // Else use the port from the current request. - } else { - int reqPort = request.getServerPort(); + Map parseQueryString(String queryString, boolean forceValueArray); - if ((scheme.equals("http") && (reqPort != DEFAULT_HTTP_PORT)) || (scheme.equals("https") && reqPort != DEFAULT_HTTPS_PORT)) { - link.append(":"); - link.append(reqPort); - } - } - } - } - else if ((scheme != null) && !scheme.equals(request.getScheme())) { - changedScheme = true; - link.append(scheme); - link.append("://"); - link.append(request.getServerName()); - - if ((scheme.equals("http") && (httpPort != DEFAULT_HTTP_PORT)) || (scheme.equals("https") && httpsPort != DEFAULT_HTTPS_PORT)) - { - link.append(":"); - link.append(scheme.equals("http") ? httpPort : httpsPort); - } - } - - if (action != null) { - // Check if context path needs to be added - // Add path to absolute links - if (action.startsWith("/") && includeContext) { - String contextPath = request.getContextPath(); - if (!contextPath.equals("/")) { - link.append(contextPath); - } - } else if (changedScheme) { - - // (Applicable to Servlet 2.4 containers) - // If the request was forwarded, the attribute below will be set with the original URL - String uri = (String) request.getAttribute("javax.servlet.forward.request_uri"); - - // If the attribute wasn't found, default to the value in the request object - if (uri == null) { - uri = request.getRequestURI(); - } - - link.append(uri.substring(0, uri.lastIndexOf('/') + 1)); - } - - // Add page - link.append(action); - } else { - // Go to "same page" - String requestURI = (String) request.getAttribute("struts.request_uri"); - - // (Applicable to Servlet 2.4 containers) - // If the request was forwarded, the attribute below will be set with the original URL - if (requestURI == null) { - requestURI = (String) request.getAttribute("javax.servlet.forward.request_uri"); - } - - // If neither request attributes were found, default to the value in the request object - if (requestURI == null) { - requestURI = request.getRequestURI(); - } - - link.append(requestURI); - } - - //if the action was not explicitly set grab the params from the request - if (escapeAmp) { - buildParametersString(params, link); - } else { - buildParametersString(params, link, "&"); - } - - String result = link.toString(); - - if (StringUtils.containsIgnoreCase(result, " 0)) { - if (link.toString().indexOf("?") == -1) { - link.append("?"); - } else { - link.append(paramSeparator); - } - - // Set params - Iterator iter = params.entrySet().iterator(); - - - while (iter.hasNext()) { - Map.Entry entry = (Map.Entry) iter.next(); - String name = (String) entry.getKey(); - Object value = entry.getValue(); - - - if (value instanceof Iterable) { - for (Iterator iterator = ((Iterable) value).iterator(); iterator - .hasNext();) { - Object paramValue = iterator.next(); - link.append(buildParameterSubstring(name, paramValue - .toString())); - - if (iterator.hasNext()) - link.append(paramSeparator); - } - } else if (value instanceof Object[]) { - Object[] array = (Object[]) value; - for (int i = 0; i < array.length; i++) { - Object paramValue = array[i]; - link.append(buildParameterSubstring(name, paramValue - .toString())); - - if (i < array.length - 1) - link.append(paramSeparator); - } - } else { - link.append(buildParameterSubstring(name, value != null ? value.toString() : StringUtils.EMPTY)); - } - - if (iter.hasNext()) - link.append(paramSeparator); - } - } - } - - - private static String buildParameterSubstring(String name, String value) { - StringBuilder builder = new StringBuilder(); - builder.append(translateAndEncode(name)); - builder.append('='); - builder.append(translateAndEncode(value)); - - return builder.toString(); - } - - /** - * Translates any script expressions using {@link com.opensymphony.xwork2.util.TextParseUtil#translateVariables} and - * encodes the URL using {@link java.net.URLEncoder#encode} with the encoding specified in the configuration. - * - * @param input - * @return the translated and encoded string - */ - public static String translateAndEncode(String input) { - String translatedInput = translateVariable(input); - String encoding = getEncodingFromConfiguration(); - - try { - return URLEncoder.encode(translatedInput, encoding); - } catch (UnsupportedEncodingException e) { - if (LOG.isWarnEnabled()) { - LOG.warn("Could not encode URL parameter '" + input + "', returning value un-encoded"); - } - return translatedInput; - } - } - - public static String translateAndDecode(String input) { - String translatedInput = translateVariable(input); - String encoding = getEncodingFromConfiguration(); - - try { - return URLDecoder.decode(translatedInput, encoding); - } catch (UnsupportedEncodingException e) { - if (LOG.isWarnEnabled()) { - LOG.warn("Could not encode URL parameter '" + input + "', returning value un-encoded"); - } - return translatedInput; - } - } - - private static String translateVariable(String input) { - ValueStack valueStack = ServletActionContext.getContext().getValueStack(); - String output = TextParseUtil.translateVariables(input, valueStack); - return output; - } - - private static String getEncodingFromConfiguration() { - final String encoding; - - // FIXME: temporary hack until class is made a properly injected bean - Container cont = ActionContext.getContext().getContainer(); - String customEncoding = cont.getInstance(String.class, StrutsConstants.STRUTS_I18N_ENCODING); - - if (customEncoding != null) { - encoding = customEncoding; - } else { - encoding = "UTF-8"; - } - return encoding; - } - - public static Map parseQueryString(String queryString) { - return parseQueryString(queryString, false); - } - - public static Map parseQueryString(String queryString, boolean forceValueArray) { - Map queryParams = new LinkedHashMap(); - if (queryString != null) { - String[] params = queryString.split("&"); - for (int a=0; a< params.length; a++) { - if (params[a].trim().length() > 0) { - String[] tmpParams = params[a].split("="); - String paramName = null; - String paramValue = ""; - if (tmpParams.length > 0) { - paramName = tmpParams[0]; - } - if (tmpParams.length > 1) { - paramValue = tmpParams[1]; - } - if (paramName != null) { - paramName = translateAndDecode(paramName); - String translatedParamValue = translateAndDecode(paramValue); - - 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 paramList = new ArrayList(Arrays - .asList(currentParamValues)); - paramList.add(translatedParamValue); - String newParamValues[] = new String[paramList - .size()]; - queryParams.put(paramName, paramList - .toArray(newParamValues)); - } else { - queryParams.put(paramName, new String[] {translatedParamValue}); - } - } - } else { - queryParams.put(paramName, translatedParamValue); - } - } - } - } - } - return queryParams; - } } diff --git a/core/src/main/resources/struts-default.xml b/core/src/main/resources/struts-default.xml index 9a6d26ae1..dba42c02d 100644 --- a/core/src/main/resources/struts-default.xml +++ b/core/src/main/resources/struts-default.xml @@ -73,6 +73,7 @@ + diff --git a/core/src/test/java/org/apache/struts2/dispatcher/ServletActionRedirectResultTest.java b/core/src/test/java/org/apache/struts2/dispatcher/ServletActionRedirectResultTest.java index 2fd6eb512..7ead95b31 100644 --- a/core/src/test/java/org/apache/struts2/dispatcher/ServletActionRedirectResultTest.java +++ b/core/src/test/java/org/apache/struts2/dispatcher/ServletActionRedirectResultTest.java @@ -31,6 +31,7 @@ import com.opensymphony.xwork2.util.ValueStack; import org.apache.struts2.ServletActionContext; import org.apache.struts2.StrutsTestCase; import org.apache.struts2.dispatcher.mapper.ActionMapper; +import org.apache.struts2.views.util.DefaultUrlHelper; import org.easymock.IMocksControl; import org.springframework.mock.web.MockHttpServletRequest; import org.springframework.mock.web.MockHttpServletResponse; @@ -89,6 +90,7 @@ public class ServletActionRedirectResultTest extends StrutsTestCase { result.setEncode(false); result.setPrependServletContext(false); result.setAnchor("fragment"); + result.setUrlHelper(new DefaultUrlHelper()); IMocksControl control = createControl(); ActionProxy mockActionProxy = control.createMock(ActionProxy.class); @@ -143,6 +145,7 @@ public class ServletActionRedirectResultTest extends StrutsTestCase { result.setEncode(false); result.setPrependServletContext(false); result.setAnchor("fragment"); + result.setUrlHelper(new DefaultUrlHelper()); IMocksControl control = createControl(); ActionProxy mockActionProxy = control.createMock(ActionProxy.class); @@ -177,7 +180,7 @@ public class ServletActionRedirectResultTest extends StrutsTestCase { .build(); ObjectFactory factory = container.getInstance(ObjectFactory.class); - ServletActionRedirectResult result = (ServletActionRedirectResult) factory.buildResult(resultConfig, new HashMap()); + ServletActionRedirectResult result = (ServletActionRedirectResult) factory.buildResult(resultConfig, new HashMap()); assertNotNull(result); } diff --git a/core/src/test/java/org/apache/struts2/dispatcher/ServletRedirectResultTest.java b/core/src/test/java/org/apache/struts2/dispatcher/ServletRedirectResultTest.java index 9e88c1b3f..7d8606353 100644 --- a/core/src/test/java/org/apache/struts2/dispatcher/ServletRedirectResultTest.java +++ b/core/src/test/java/org/apache/struts2/dispatcher/ServletRedirectResultTest.java @@ -35,19 +35,24 @@ import org.apache.struts2.ServletActionContext; import org.apache.struts2.StrutsStatics; import org.apache.struts2.StrutsTestCase; import org.apache.struts2.dispatcher.mapper.ActionMapper; -import static org.easymock.EasyMock.*; +import org.apache.struts2.views.util.DefaultUrlHelper; import org.easymock.IMocksControl; import org.springframework.mock.web.MockHttpServletRequest; import org.springframework.mock.web.MockHttpServletResponse; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; -import static javax.servlet.http.HttpServletResponse.SC_SEE_OTHER; import java.io.PrintWriter; import java.io.StringWriter; import java.util.HashMap; import java.util.Map; +import static javax.servlet.http.HttpServletResponse.SC_SEE_OTHER; +import static org.easymock.EasyMock.createControl; +import static org.easymock.EasyMock.createNiceMock; +import static org.easymock.EasyMock.expect; +import static org.easymock.EasyMock.replay; + /** */ @@ -188,6 +193,7 @@ public class ServletRedirectResultTest extends StrutsTestCase implements StrutsS result.setEncode(false); result.setPrependServletContext(false); result.setAnchor("fragment"); + result.setUrlHelper(new DefaultUrlHelper()); IMocksControl control = createControl(); ActionProxy mockActionProxy = control.createMock(ActionProxy.class); diff --git a/core/src/test/java/org/apache/struts2/views/util/UrlHelperTest.java b/core/src/test/java/org/apache/struts2/views/util/DefaultUrlHelperTest.java similarity index 90% rename from core/src/test/java/org/apache/struts2/views/util/UrlHelperTest.java rename to core/src/test/java/org/apache/struts2/views/util/DefaultUrlHelperTest.java index ca32a6f4a..e5010fa6a 100644 --- a/core/src/test/java/org/apache/struts2/views/util/UrlHelperTest.java +++ b/core/src/test/java/org/apache/struts2/views/util/DefaultUrlHelperTest.java @@ -38,12 +38,13 @@ import java.util.TreeMap; /** - * Test case for UrlHelper. + * Test case for DefaultUrlHelper. * */ -public class UrlHelperTest extends StrutsTestCase { +public class DefaultUrlHelperTest extends StrutsTestCase { private StubContainer stubContainer; + private DefaultUrlHelper urlHelper; public void testForceAddSchemeHostAndPort() throws Exception { String expectedUrl = "http://localhost/contextPath/path1/path2/myAction.action"; @@ -57,7 +58,7 @@ public class UrlHelperTest extends StrutsTestCase { Mock mockHttpServletResponse = new Mock(HttpServletResponse.class); mockHttpServletResponse.expectAndReturn("encodeURL", expectedUrl, expectedUrl); - String result = UrlHelper.buildUrl("/path1/path2/myAction.action", (HttpServletRequest) mockHttpServletRequest.proxy(), (HttpServletResponse)mockHttpServletResponse.proxy(), null, "http", true, true, true); + String result = urlHelper.buildUrl("/path1/path2/myAction.action", (HttpServletRequest) mockHttpServletRequest.proxy(), (HttpServletResponse) mockHttpServletResponse.proxy(), null, "http", true, true, true); assertEquals(expectedUrl, result); mockHttpServletRequest.verify(); } @@ -73,7 +74,7 @@ public class UrlHelperTest extends StrutsTestCase { Mock mockHttpServletResponse = new Mock(HttpServletResponse.class); mockHttpServletResponse.expectAndReturn("encodeURL", expectedUrl, expectedUrl); - String result = UrlHelper.buildUrl("/path1/path2/myAction.action", (HttpServletRequest)mockHttpServletRequest.proxy(), (HttpServletResponse)mockHttpServletResponse.proxy(), null, "http", true, true, false); + String result = urlHelper.buildUrl("/path1/path2/myAction.action", (HttpServletRequest) mockHttpServletRequest.proxy(), (HttpServletResponse) mockHttpServletResponse.proxy(), null, "http", true, true, false); assertEquals(expectedUrl, result); } @@ -90,7 +91,7 @@ public class UrlHelperTest extends StrutsTestCase { Mock mockHttpServletResponse = new Mock(HttpServletResponse.class); mockHttpServletResponse.expectAndReturn("encodeURL", expectedUrl, expectedUrl); - String result = UrlHelper.buildUrl("/path1/path2/myAction.action", (HttpServletRequest) mockHttpServletRequest.proxy(), (HttpServletResponse)mockHttpServletResponse.proxy(), null, "http", true, true, true); + String result = urlHelper.buildUrl("/path1/path2/myAction.action", (HttpServletRequest) mockHttpServletRequest.proxy(), (HttpServletResponse) mockHttpServletResponse.proxy(), null, "http", true, true, true); assertEquals(expectedUrl, result); mockHttpServletRequest.verify(); } @@ -105,7 +106,7 @@ public class UrlHelperTest extends StrutsTestCase { StringBuilder url = new StringBuilder("http://localhost:8080/myContext/myPage.jsp?initParam=initValue"); - UrlHelper.buildParametersString(parameters, url); + urlHelper.buildParametersString(parameters, url); assertEquals( expectedUrl, url.toString()); @@ -121,7 +122,7 @@ public class UrlHelperTest extends StrutsTestCase { StringBuilder url = new StringBuilder("http://localhost:8080/myContext/myPage.jsp?initParam=initValue"); - UrlHelper.buildParametersString(parameters, url); + urlHelper.buildParametersString(parameters, url); assertEquals( expectedUrl, url.toString()); @@ -140,10 +141,10 @@ public class UrlHelperTest extends StrutsTestCase { mockHttpServletResponse.expectAndReturn("encodeURL", expectedUrl, expectedUrl); - String result = UrlHelper.buildUrl("/path1/path2/myAction.action", - (HttpServletRequest) mockHttpServletRequest.proxy(), - (HttpServletResponse) mockHttpServletResponse.proxy(), null, - null, true, true, true); + String result = urlHelper.buildUrl("/path1/path2/myAction.action", + (HttpServletRequest) mockHttpServletRequest.proxy(), + (HttpServletResponse) mockHttpServletResponse.proxy(), null, + null, true, true, true); assertEquals(expectedUrl, result); mockHttpServletRequest.verify(); } @@ -158,7 +159,7 @@ public class UrlHelperTest extends StrutsTestCase { Mock mockHttpServletResponse = new Mock(HttpServletResponse.class); mockHttpServletResponse.expectAndReturn("encodeURL", expectedUrl, expectedUrl); - String actualUrl = UrlHelper.buildUrl(expectedUrl, (HttpServletRequest) mockHttpServletRequest.proxy(), + String actualUrl = urlHelper.buildUrl(expectedUrl, (HttpServletRequest) mockHttpServletRequest.proxy(), (HttpServletResponse) mockHttpServletResponse.proxy(), new HashMap()); assertEquals(expectedUrl, actualUrl); } @@ -178,7 +179,7 @@ public class UrlHelperTest extends StrutsTestCase { params.put("hello", "world"); params.put("foo", "bar"); - String urlString = UrlHelper.buildUrl(actionName, (HttpServletRequest) mockHttpServletRequest.proxy(), (HttpServletResponse) mockHttpServletResponse.proxy(), params); + String urlString = urlHelper.buildUrl(actionName, (HttpServletRequest) mockHttpServletRequest.proxy(), (HttpServletResponse) mockHttpServletResponse.proxy(), params); assertEquals(expectedString, urlString); } @@ -197,7 +198,7 @@ public class UrlHelperTest extends StrutsTestCase { params.put("hello", "world"); params.put("foo", "bar"); - String urlString = UrlHelper.buildUrl(actionName, (HttpServletRequest) mockHttpServletRequest.proxy(), (HttpServletResponse) mockHttpServletResponse.proxy(), params, null, true, true, false, false); + String urlString = urlHelper.buildUrl(actionName, (HttpServletRequest) mockHttpServletRequest.proxy(), (HttpServletResponse) mockHttpServletResponse.proxy(), params, null, true, true, false, false); assertEquals(expectedString, urlString); } @@ -213,7 +214,7 @@ public class UrlHelperTest extends StrutsTestCase { params.put("hello", new String[]{"earth", "mars"}); params.put("foo", "bar"); - String urlString = UrlHelper.buildUrl(actionName, (HttpServletRequest) mockHttpServletRequest.proxy(), (HttpServletResponse) mockHttpServletResponse.proxy(), params); + String urlString = urlHelper.buildUrl(actionName, (HttpServletRequest) mockHttpServletRequest.proxy(), (HttpServletResponse) mockHttpServletResponse.proxy(), params); assertEquals(expectedString, urlString); } @@ -238,7 +239,7 @@ public class UrlHelperTest extends StrutsTestCase { params.put("hello", new String[]{"earth", "mars"}); params.put("foo", "bar"); - String urlString = UrlHelper.buildUrl(actionName, (HttpServletRequest) mockHttpServletRequest.proxy(), (HttpServletResponse) mockHttpServletResponse.proxy(), params, "https", true, true); + String urlString = urlHelper.buildUrl(actionName, (HttpServletRequest) mockHttpServletRequest.proxy(), (HttpServletResponse) mockHttpServletResponse.proxy(), params, "https", true, true); assertEquals(expectedString, urlString); } @@ -263,7 +264,7 @@ public class UrlHelperTest extends StrutsTestCase { params.put("hello", new String[]{"earth", "mars"}); params.put("foo", "bar"); - String urlString = UrlHelper.buildUrl(actionName, (HttpServletRequest) mockHttpServletRequest.proxy(), (HttpServletResponse) mockHttpServletResponse.proxy(), params, "http", true, true); + String urlString = urlHelper.buildUrl(actionName, (HttpServletRequest) mockHttpServletRequest.proxy(), (HttpServletResponse) mockHttpServletResponse.proxy(), params, "http", true, true); assertEquals(expectedString, urlString); } @@ -292,7 +293,7 @@ public class UrlHelperTest extends StrutsTestCase { params.put("hello", new String[]{"earth", "mars"}); params.put("foo", "bar"); - String urlString = UrlHelper.buildUrl(actionName, (HttpServletRequest) mockHttpServletRequest.proxy(), (HttpServletResponse) mockHttpServletResponse.proxy(), params, "https", true, true); + String urlString = urlHelper.buildUrl(actionName, (HttpServletRequest) mockHttpServletRequest.proxy(), (HttpServletResponse) mockHttpServletResponse.proxy(), params, "https", true, true); assertEquals(expectedString, urlString); } @@ -321,7 +322,7 @@ public class UrlHelperTest extends StrutsTestCase { params.put("hello", new String[]{"earth", "mars"}); params.put("foo", "bar"); - String urlString = UrlHelper.buildUrl(actionName, (HttpServletRequest) mockHttpServletRequest.proxy(), (HttpServletResponse) mockHttpServletResponse.proxy(), params, "http", true, true); + String urlString = urlHelper.buildUrl(actionName, (HttpServletRequest) mockHttpServletRequest.proxy(), (HttpServletResponse) mockHttpServletResponse.proxy(), params, "http", true, true); assertEquals(expectedString, urlString); } @@ -348,13 +349,13 @@ public class UrlHelperTest extends StrutsTestCase { String actionName = "promo.html"; Map params = new TreeMap(); - String urlString = UrlHelper.buildUrl(actionName, (HttpServletRequest) mockHttpServletRequest.proxy(), (HttpServletResponse) mockHttpServletResponse.proxy(), params, "https", true, true); + String urlString = urlHelper.buildUrl(actionName, (HttpServletRequest) mockHttpServletRequest.proxy(), (HttpServletResponse) mockHttpServletResponse.proxy(), params, "https", true, true); assertEquals(expectedString, urlString); } public void testParseQuery() throws Exception { - Map result = UrlHelper.parseQueryString("aaa=aaaval&bbb=bbbval&ccc=&%3Ca%22%3E=%3Cval%3E"); + Map result = urlHelper.parseQueryString("aaa=aaaval&bbb=bbbval&ccc=&%3Ca%22%3E=%3Cval%3E"); assertEquals(result.get("aaa"), "aaaval"); assertEquals(result.get("bbb"), "bbbval"); @@ -363,14 +364,14 @@ public class UrlHelperTest extends StrutsTestCase { } public void testParseEmptyQuery() throws Exception { - Map result = UrlHelper.parseQueryString(""); + Map result = urlHelper.parseQueryString(""); assertNotNull(result); assertEquals(result.size(), 0); } public void testParseNullQuery() throws Exception { - Map result = UrlHelper.parseQueryString(null); + Map result = urlHelper.parseQueryString(null); assertNotNull(result); assertEquals(result.size(), 0); @@ -379,7 +380,7 @@ public class UrlHelperTest extends StrutsTestCase { public void testTranslateAndEncode() throws Exception { setProp(StrutsConstants.STRUTS_I18N_ENCODING, "UTF-8"); - String result = UrlHelper.translateAndEncode("\u65b0\u805e"); + String result = urlHelper.translateAndEncode("\u65b0\u805e"); String expectedResult = "%E6%96%B0%E8%81%9E"; assertEquals(result, expectedResult); @@ -387,7 +388,7 @@ public class UrlHelperTest extends StrutsTestCase { public void testTranslateAndDecode() throws Exception { setProp(StrutsConstants.STRUTS_I18N_ENCODING, "UTF-8"); - String result = UrlHelper.translateAndDecode("%E6%96%B0%E8%81%9E"); + String result = urlHelper.translateAndDecode("%E6%96%B0%E8%81%9E"); String expectedResult = "\u65b0\u805e"; assertEquals(result, expectedResult); @@ -397,6 +398,7 @@ public class UrlHelperTest extends StrutsTestCase { super.setUp(); stubContainer = new StubContainer(container); ActionContext.getContext().put(ActionContext.CONTAINER, stubContainer); + urlHelper = new DefaultUrlHelper(); } private void setProp(String key, String val) { 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 31d33519d..71c008b09 100644 --- a/plugins/embeddedjsp/src/main/java/org/apache/struts2/JSPRuntime.java +++ b/plugins/embeddedjsp/src/main/java/org/apache/struts2/JSPRuntime.java @@ -48,13 +48,14 @@ public abstract class JSPRuntime { public static void handle(String location, boolean flush) throws Exception { final HttpServletResponse response = ServletActionContext.getResponse(); final HttpServletRequest request = ServletActionContext.getRequest(); + final UrlHelper urlHelper = ServletActionContext.getContext().getInstance(UrlHelper.class); int i = location.indexOf("?"); if (i > 0) { //extract params from the url and add them to the request - Map parameters = ActionContext.getContext().getParameters(); + Map parameters = ActionContext.getContext().getParameters(); String query = location.substring(i + 1); - Map queryParams = UrlHelper.parseQueryString(query, true); + Map queryParams = urlHelper.parseQueryString(query, true); if (queryParams != null && !queryParams.isEmpty()) parameters.putAll(queryParams); location = location.substring(0, i); 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 c5b48c7b3..a53a692f4 100644 --- a/plugins/embeddedjsp/src/test/java/org/apache/struts2/EmbeddedJSPResultTest.java +++ b/plugins/embeddedjsp/src/test/java/org/apache/struts2/EmbeddedJSPResultTest.java @@ -28,6 +28,8 @@ import com.opensymphony.xwork2.util.finder.ClassLoaderInterface; import com.opensymphony.xwork2.util.finder.ClassLoaderInterfaceDelegate; import junit.framework.TestCase; import org.apache.commons.lang3.StringUtils; +import org.apache.struts2.views.util.DefaultUrlHelper; +import org.apache.struts2.views.util.UrlHelper; import org.easymock.EasyMock; import org.easymock.IAnswer; import org.springframework.mock.web.MockHttpServletResponse; @@ -266,6 +268,10 @@ public class EmbeddedJSPResultTest extends TestCase { //mock container Container container = EasyMock.createNiceMock(Container.class); EasyMock.expect(container.getInstance(XWorkConverter.class)).andReturn(converter).anyTimes(); + + UrlHelper urlHelper = new DefaultUrlHelper(); + EasyMock.expect(container.getInstance(UrlHelper.class)).andReturn(urlHelper).anyTimes(); + EasyMock.replay(container); stackContext.put(ActionContext.CONTAINER, container); actionContext.setContainer(container); 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 47cbca8b2..7eba0e9d9 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 @@ -28,15 +28,16 @@ import java.util.Map; import javax.portlet.PortletMode; +import org.apache.struts2.StrutsConstants; import org.apache.struts2.dispatcher.ServletActionRedirectResult; import org.apache.struts2.dispatcher.mapper.ActionMapper; import org.apache.struts2.dispatcher.mapper.ActionMapping; import org.apache.struts2.portlet.PortletActionConstants; -import org.apache.struts2.views.util.UrlHelper; import com.opensymphony.xwork2.ActionInvocation; import com.opensymphony.xwork2.config.entities.ResultConfig; import com.opensymphony.xwork2.inject.Inject; +import org.apache.struts2.views.util.UrlHelper; /** * @@ -133,14 +134,12 @@ public class PortletActionRedirectResult extends PortletResult { public static final String DEFAULT_PARAM = "actionName"; protected String actionName; - protected String namespace; - protected String method; - private Map requestParameters = new LinkedHashMap(); - + private Map requestParameters = new LinkedHashMap(); private ActionMapper actionMapper; + private UrlHelper urlHelper; public PortletActionRedirectResult() { super(); @@ -161,23 +160,27 @@ public class PortletActionRedirectResult extends PortletResult { this.method = method; } - protected List prohibitedResultParam = Arrays.asList(new String[] { DEFAULT_PARAM, "namespace", "method", - "encode", "parse", "location", "prependServletContext" }); + protected List prohibitedResultParam = Arrays.asList(DEFAULT_PARAM, "namespace", "method", "encode", "parse", + "location", "prependServletContext"); @Inject public void setActionMapper(ActionMapper actionMapper) { this.actionMapper = actionMapper; } - /** + @Inject + public void setUrlHelper(UrlHelper urlHelper) { + this.urlHelper = urlHelper; + } + + /** * @see com.opensymphony.xwork2.Result#execute(com.opensymphony.xwork2.ActionInvocation) */ public void execute(ActionInvocation invocation) throws Exception { actionName = conditionalParse(actionName, invocation); String portletNamespace = (String)invocation.getInvocationContext().get(PortletActionConstants.PORTLET_NAMESPACE); if (portletMode != null) { - Map namespaceMap = (Map) invocation.getInvocationContext().get( - PortletActionConstants.MODE_NAMESPACE_MAP); + Map namespaceMap = getNamespaceMap(invocation); namespace = namespaceMap.get(portletMode); } if (namespace == null) { @@ -194,30 +197,32 @@ public class PortletActionRedirectResult extends PortletResult { String resultCode = invocation.getResultCode(); if (resultCode != null) { ResultConfig resultConfig = invocation.getProxy().getConfig().getResults().get(resultCode); - Map resultConfigParams = resultConfig.getParams(); - for (Iterator i = resultConfigParams.entrySet().iterator(); i.hasNext();) { - Map.Entry e = (Map.Entry) i.next(); - if (!prohibitedResultParam.contains(e.getKey())) { - requestParameters.put(e.getKey().toString(), e.getValue() == null ? "" : conditionalParse(e - .getValue().toString(), invocation)); - } - } + Map resultConfigParams = resultConfig.getParams(); + for (Map.Entry 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, "&"); + urlHelper.buildParametersString(requestParameters, tmpLocation, "&"); setLocation(tmpLocation.toString()); super.execute(invocation); } - /** + @SuppressWarnings("unchecked") + private Map getNamespaceMap(ActionInvocation invocation) { + return (Map) invocation.getInvocationContext().get(PortletActionConstants.MODE_NAMESPACE_MAP); + } + + /** * Sets the action name * - * @param actionName - * The name + * @param actionName The name */ public void setActionName(String actionName) { this.actionName = actionName; @@ -226,8 +231,7 @@ public class PortletActionRedirectResult extends PortletResult { /** * Sets the namespace * - * @param namespace - * The namespace + * @param namespace The namespace */ public void setNamespace(String namespace) { this.namespace = namespace; @@ -236,8 +240,7 @@ public class PortletActionRedirectResult extends PortletResult { /** * Sets the method * - * @param method - * The method + * @param method The method */ public void setMethod(String method) { this.method = method; @@ -246,10 +249,8 @@ public class PortletActionRedirectResult extends PortletResult { /** * Adds a request parameter to be added to the redirect url * - * @param key - * The parameter name - * @param value - * The parameter value + * @param key The parameter name + * @param value The parameter value */ public PortletActionRedirectResult addParameter(String key, Object value) { requestParameters.put(key, String.valueOf(value));