Author: lukaszlenart Date: Thu Oct 24 19:44:34 2013 New Revision: 1535511 URL: http://svn.apache.org/r1535511 Log: WW-4229 Implements new PostbackResult
Added: struts/struts2/trunk/core/src/main/java/org/apache/struts2/dispatcher/PostbackResult.java Added: struts/struts2/trunk/core/src/main/java/org/apache/struts2/dispatcher/PostbackResult.java URL: http://svn.apache.org/viewvc/struts/struts2/trunk/core/src/main/java/org/apache/struts2/dispatcher/PostbackResult.java?rev=1535511&view=auto ============================================================================== --- struts/struts2/trunk/core/src/main/java/org/apache/struts2/dispatcher/PostbackResult.java (added) +++ struts/struts2/trunk/core/src/main/java/org/apache/struts2/dispatcher/PostbackResult.java Thu Oct 24 19:44:34 2013 @@ -0,0 +1,232 @@ +/* + * $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.dispatcher; + +import com.opensymphony.xwork2.ActionContext; +import com.opensymphony.xwork2.ActionInvocation; +import com.opensymphony.xwork2.inject.Inject; +import org.apache.struts2.ServletActionContext; +import org.apache.struts2.dispatcher.mapper.ActionMapper; +import org.apache.struts2.dispatcher.mapper.ActionMapping; + +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import java.io.PrintWriter; +import java.io.UnsupportedEncodingException; +import java.net.URLEncoder; +import java.util.Map; + +/** + * <!-- START SNIPPET: description --> + * A result that renders the current request parameters as a form which + * immediately submits a <a href="http://en.wikipedia.org/wiki/Postback">postback</a> + * to the specified destination. + * <!-- END SNIPPET: description --> + * <p/> + * <b>Parameters:</b> + * <!-- START SNIPPET: params --> + * <ul> + * <li>location - http location to post the form</li> + * <li>prependServletContext (true|false) - when location is relative, controls if to add Servlet Context, default "true"</li> + * <li>actionName - action name to post the form (resolved as an expression)</li> + * <li>namespace - action's namespace to use (resolved as an expression)</li> + * <li>method - actions' method to use (resolved as an expression)</li> + * <li>cache (true|false) - when set to true adds cache control headers, default "true"</li> + * <li>parse (true|false) - when set to true actionName, namespace and method are parsed, default "true"</li> + * </ul> + * <!-- END SNIPPET: params --> + * <p/> + * <b>Examples:</b> + * <pre> + * <!-- START SNIPPET: example --> + * <action name="registerThirdParty" > + * <result type="postback">https://www.example.com/register</result> + * </action> + * + * <action name="registerThirdParty" > + * <result type="postback"> + * <param name="namespace">/secure</param> + * <param name="actionName">register2</param> + * </result> + * </action> + * <!-- END SNIPPET: example --> + * </pre> + */ +public class PostbackResult extends StrutsResultSupport { + + private String actionName; + private String namespace; + private String method; + private boolean prependServletContext = true; + private boolean cache = true; + + protected ActionMapper actionMapper; + + @Override + 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); + + // Cache? + if (!cache) { + response.setHeader("Cache-Control", "no-cache, no-store, must-revalidate"); // HTTP 1.1 + response.setHeader("Pragma", "no-cache"); // HTTP 1.0 + response.setDateHeader("Expires", 0); // Proxies + } + + // Render + PrintWriter pw = new PrintWriter(response.getOutputStream()); + pw.write("<!DOCTYPE html><html><body><form action=\"" + finalLocation + "\" method=\"POST\">"); + writeFormElements(request, pw); + writePrologueScript(pw); + pw.write("</html>"); + pw.flush(); + } + + @Override + public void execute(ActionInvocation invocation) throws Exception { + String postbackUri = makePostbackUri(invocation); + setLocation(postbackUri); + super.execute(invocation); + } + + /** + * Determines if the specified form input element should be included. + * + * @param name the input element name + * @param values the input element values + * @return {@code true} if included; otherwise {@code false} + */ + protected boolean isElementIncluded(String name, String[] values) { + return !name.startsWith("action:"); + } + + protected String makePostbackUri(ActionInvocation invocation) { + ActionContext ctx = invocation.getInvocationContext(); + HttpServletRequest request = (HttpServletRequest) ctx.get(ServletActionContext.HTTP_REQUEST); + String postbackUri; + + if (actionName != null) { + actionName = conditionalParse(actionName, invocation); + if (namespace == null) { + namespace = invocation.getProxy().getNamespace(); + } else { + namespace = conditionalParse(namespace, invocation); + } + if (method == null) { + method = ""; + } else { + method = conditionalParse(method, invocation); + } + postbackUri = request.getContextPath() + actionMapper.getUriFromActionMapping(new ActionMapping(actionName, namespace, method, null)); + } else { + String location = getLocation(); + // Do not prepend if the URL is a FQN + if (!location.matches("^([a-zA-z]+:)?//.*")) { + // If the URL is relative to the servlet context, prepend the servlet context path + if (prependServletContext && (request.getContextPath() != null) && (request.getContextPath().length() > 0)) { + location = request.getContextPath() + location; + } + } + postbackUri = location; + } + + return postbackUri; + } + + @Inject + public final void setActionMapper(ActionMapper mapper) { + this.actionMapper = mapper; + } + + /** + * Sets the name of the destination action. + * + * @param actionName the action name + */ + public final void setActionName(String actionName) { + this.actionName = actionName; + } + + /** + * Stores the option to cache the rendered intermediate page. The default + * is {@code true}. + * + * @return {@code true} to cache; otherwise {@code false} + */ + public final void setCache(boolean cache) { + this.cache = cache; + } + + /** + * Sets the method of the destination action. + * + * @param method the method + */ + public final void setMethod(String method) { + this.method = method; + } + + /** + * Sets the namespace of the destination action. + * + * @param namespace the namespace + */ + public final void setNamespace(String namespace) { + this.namespace = namespace; + } + + public final void setPrependServletContext(boolean prependServletContext) { + this.prependServletContext = prependServletContext; + } + + protected void writeFormElement(PrintWriter pw, String name, String[] values) throws UnsupportedEncodingException { + for (String value : values) { + String encName = URLEncoder.encode(name, "UTF-8"); + String encValue = URLEncoder.encode(value, "UTF-8"); + pw.write("<input type=\"hidden\" name=\"" + encName + "\" value=\"" + encValue + "\"/>"); + } + } + + private void writeFormElements(HttpServletRequest request, PrintWriter pw) throws UnsupportedEncodingException { + Map<String, String[]> params = request.getParameterMap(); + for (String name : params.keySet()) { + String[] values = params.get(name); + if (isElementIncluded(name, values)) { + writeFormElement(pw, name, values); + } + } + } + + /** + * Outputs the script after the form has been emitted. The default script + * is to submit the form using a JavaScript time out that immediately expires. + * + * @param pw the print writer + */ + protected void writePrologueScript(PrintWriter pw) { + pw.write("<script>"); + pw.write("setTimeout(function(){document.forms[0].submit();},0);"); + pw.write("</script>"); + } + +}