| /* |
| * $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.components; |
| |
| import java.io.IOException; |
| import java.io.Writer; |
| import java.util.Collections; |
| import java.util.Iterator; |
| import java.util.LinkedHashMap; |
| import java.util.Map; |
| |
| import javax.servlet.http.HttpServletRequest; |
| import javax.servlet.http.HttpServletResponse; |
| |
| import org.apache.commons.logging.Log; |
| import org.apache.commons.logging.LogFactory; |
| import org.apache.struts2.views.annotations.StrutsTag; |
| import org.apache.struts2.views.annotations.StrutsTagAttribute; |
| import org.apache.struts2.StrutsException; |
| import org.apache.struts2.StrutsConstants; |
| import org.apache.struts2.dispatcher.Dispatcher; |
| import org.apache.struts2.portlet.context.PortletActionContext; |
| import org.apache.struts2.portlet.util.PortletUrlHelper; |
| import org.apache.struts2.views.util.UrlHelper; |
| |
| import com.opensymphony.xwork2.inject.Inject; |
| import com.opensymphony.xwork2.util.ValueStack; |
| |
| /** |
| * <!-- START SNIPPET: javadoc --> |
| * |
| * <p>This tag is used to create a URL.</p> |
| * |
| * <p>You can use the "param" tag inside the body to provide |
| * additional request parameters.</p> |
| * |
| * <b>NOTE:</b> |
| * <p>By default request parameters will be separated using escaped ampersands (i.e., &amp;). |
| * This is necessary for XHTML compliance, however, when using the URL generated by this tag |
| * with the <s:property> tag, the <b>escapeAmp</b> attribute should be used to disable |
| * ampersand escaping.</p> |
| * |
| * <b>NOTE:</b> |
| * <p>When includeParams is 'all' or 'get', the parameter defined in param tag will take |
| * precedence and will not be overriden if they exists in the parameter submitted. For |
| * example, in Example 3 below, if there is a id parameter in the url where the page this |
| * tag is included like http://<host>:<port>/<context>/editUser.action?id=3333&name=John |
| * the generated url will be http://<host>:<port>/context>/editUser.action?id=22&name=John |
| * cause the parameter defined in the param tag will take precedence.</p> |
| * |
| * <!-- END SNIPPET: javadoc --> |
| * |
| * |
| * <!-- START SNIPPET: params --> |
| * |
| * <ul> |
| * <li>action (String) - (value or action choose either one, if both exist value takes precedence) action's name (alias) <li> |
| * <li>value (String) - (value or action choose either one, if both exist value takes precedence) the url itself</li> |
| * <li>scheme (String) - http scheme (http, https) defaults to the scheme this request is in</li> |
| * <li>namespace - action's namespace</li> |
| * <li>method (String) - action's method name, defaults to 'execute'</li> |
| * <li>encode (Boolean) - url encode the generated url. Defaults to 'true'.</li> |
| * <li>includeParams (String) - The includeParams attribute may have the value 'none', 'get' or 'all'. Defaults to 'get'. |
| * none - include no parameters in the URL |
| * get - include only GET parameters in the URL (default) |
| * all - include both GET and POST parameters in the URL |
| * </li> |
| * <li>includeContext (Boolean) - Specifies whether to include the web app context path. Defaults to 'true'.</li> |
| * <li>escapeAmp (Boolean) - Specifies whether to escape ampersand (&) to (&amp;) or not. Defaults to 'true'.</li> |
| * <li>portletMode (String) - The resulting portlet mode.</li> |
| * <li>windowState (String) - The resulting portlet window state.</li> |
| * <li>portletUrlType (String) - Specifies if this should be a portlet render or action URL.</li> |
| * <li>forceAddSchemeHostAndPort (Boolean) - Specifies whether to force the addition of scheme, host and port or not.</li> |
| * </ul> |
| * |
| * <!-- END SNIPPET: params --> |
| * |
| * <p/> <b>Examples</b> |
| * <pre> |
| * <!-- START SNIPPET: example --> |
| * |
| * <-- Example 1 --> |
| * <s:url value="editGadget.action"> |
| * <s:param name="id" value="%{selected}" /> |
| * </s:url> |
| * |
| * <-- Example 2 --> |
| * <s:url action="editGadget"> |
| * <s:param name="id" value="%{selected}" /> |
| * </s:url> |
| * |
| * <-- Example 3--> |
| * <s:url includeParams="get" > |
| * <s:param name="id" value="%{'22'}" /> |
| * </s:url> |
| * |
| * <!-- END SNIPPET: example --> |
| * </pre> |
| * |
| * @see Param |
| * |
| */ |
| @StrutsTag(name="url", tldTagClass="org.apache.struts2.views.jsp.URLTag", description="This tag is used to create a URL") |
| public class URL extends Component { |
| private static final Log LOG = LogFactory.getLog(URL.class); |
| |
| /** |
| * The includeParams attribute may have the value 'none', 'get' or 'all'. |
| * It is used when the url tag is used without a value attribute. |
| * Its value is looked up on the ValueStack |
| * If no includeParams is specified then 'get' is used. |
| * none - include no parameters in the URL |
| * get - include only GET parameters in the URL (default) |
| * all - include both GET and POST parameters in the URL |
| */ |
| public static final String NONE = "none"; |
| public static final String GET = "get"; |
| public static final String ALL = "all"; |
| |
| private HttpServletRequest req; |
| private HttpServletResponse res; |
| |
| protected String includeParams; |
| protected String scheme; |
| protected String value; |
| protected String action; |
| protected String namespace; |
| protected String method; |
| protected boolean encode = true; |
| protected boolean includeContext = true; |
| protected boolean escapeAmp = true; |
| protected String portletMode; |
| protected String windowState; |
| protected String portletUrlType; |
| protected String anchor; |
| protected boolean forceAddSchemeHostAndPort; |
| protected String urlIncludeParams; |
| protected ExtraParameterProvider extraParameterProvider; |
| |
| public URL(ValueStack stack, HttpServletRequest req, HttpServletResponse res) { |
| super(stack); |
| this.req = req; |
| this.res = res; |
| } |
| |
| @Inject(StrutsConstants.STRUTS_URL_INCLUDEPARAMS) |
| public void setUrlIncludeParams(String urlIncludeParams) { |
| this.urlIncludeParams = urlIncludeParams; |
| } |
| |
| @Inject(required=false) |
| public void setExtraParameterProvider(ExtraParameterProvider provider) { |
| this.extraParameterProvider = provider; |
| } |
| |
| public boolean start(Writer writer) { |
| boolean result = super.start(writer); |
| |
| if (value != null) { |
| value = findString(value); |
| } |
| |
| // no explicit url set so attach params from current url, do |
| // this at start so body params can override any of these they wish. |
| try { |
| // ww-1266 |
| String includeParams = (urlIncludeParams != null ? urlIncludeParams.toLowerCase() : GET); |
| |
| if (this.includeParams != null) { |
| includeParams = findString(this.includeParams); |
| } |
| |
| if (NONE.equalsIgnoreCase(includeParams)) { |
| mergeRequestParameters(value, parameters, Collections.EMPTY_MAP); |
| } else if (ALL.equalsIgnoreCase(includeParams)) { |
| mergeRequestParameters(value, parameters, req.getParameterMap()); |
| |
| // for ALL also include GET parameters |
| includeGetParameters(); |
| includeExtraParameters(); |
| } else if (GET.equalsIgnoreCase(includeParams) || (includeParams == null && value == null && action == null)) { |
| includeGetParameters(); |
| includeExtraParameters(); |
| } else if (includeParams != null) { |
| LOG.warn("Unknown value for includeParams parameter to URL tag: " + includeParams); |
| } |
| } catch (Exception e) { |
| LOG.warn("Unable to put request parameters (" + req.getQueryString() + ") into parameter map.", e); |
| } |
| |
| |
| return result; |
| } |
| |
| private void includeExtraParameters() { |
| if (extraParameterProvider != null) { |
| mergeRequestParameters(value, parameters, extraParameterProvider.getExtraParameters()); |
| } |
| } |
| private void includeGetParameters() { |
| if(!(Dispatcher.getInstance().isPortletSupportActive() && PortletActionContext.isPortletRequest())) { |
| String query = extractQueryString(); |
| mergeRequestParameters(value, parameters, UrlHelper.parseQueryString(query)); |
| } |
| } |
| |
| private String extractQueryString() { |
| // Parse the query string to make sure that the parameters come from the query, and not some posted data |
| String query = req.getQueryString(); |
| if (query == null) { |
| query = (String) req.getAttribute("javax.servlet.forward.query_string"); |
| } |
| |
| if (query != null) { |
| // Remove possible #foobar suffix |
| int idx = query.lastIndexOf('#'); |
| |
| if (idx != -1) { |
| query = query.substring(0, idx); |
| } |
| } |
| return query; |
| } |
| |
| public boolean end(Writer writer, String body) { |
| String scheme = req.getScheme(); |
| |
| if (this.scheme != null) { |
| scheme = this.scheme; |
| } |
| |
| String result; |
| if (value == null && action != null) { |
| if (Dispatcher.getInstance().isPortletSupportActive() && PortletActionContext.isPortletRequest()) { |
| result = PortletUrlHelper.buildUrl(action, namespace, method, parameters, portletUrlType, portletMode, windowState); |
| } |
| else { |
| result = determineActionURL(action, namespace, method, req, res, parameters, scheme, includeContext, encode, forceAddSchemeHostAndPort, escapeAmp); |
| } |
| } else { |
| if (Dispatcher.getInstance().isPortletSupportActive() && PortletActionContext.isPortletRequest()) { |
| result = PortletUrlHelper.buildResourceUrl(value, parameters); |
| } |
| else { |
| String _value = value; |
| |
| // We don't include the request parameters cause they would have been |
| // prioritised before this [in start(Writer) method] |
| if (_value != null && _value.indexOf("?") > 0) { |
| _value = _value.substring(0, _value.indexOf("?")); |
| } |
| result = UrlHelper.buildUrl(_value, req, res, parameters, scheme, includeContext, encode, forceAddSchemeHostAndPort, escapeAmp); |
| } |
| } |
| if ( anchor != null && anchor.length() > 0 ) { |
| result += '#' + anchor; |
| } |
| |
| String id = getId(); |
| |
| if (id != null) { |
| getStack().getContext().put(id, result); |
| |
| // add to the request and page scopes as well |
| req.setAttribute(id, result); |
| } else { |
| try { |
| writer.write(result); |
| } catch (IOException e) { |
| throw new StrutsException("IOError: " + e.getMessage(), e); |
| } |
| } |
| return super.end(writer, body); |
| } |
| |
| @StrutsTagAttribute(description="The includeParams attribute may have the value 'none', 'get' or 'all'", defaultValue="get") |
| public void setIncludeParams(String includeParams) { |
| this.includeParams = includeParams; |
| } |
| |
| @StrutsTagAttribute(description="Set scheme attribute") |
| public void setScheme(String scheme) { |
| this.scheme = scheme; |
| } |
| |
| @StrutsTagAttribute(description="The target value to use, if not using action") |
| public void setValue(String value) { |
| this.value = value; |
| } |
| |
| @StrutsTagAttribute(description="The action to generate the URL for, if not using value") |
| public void setAction(String action) { |
| this.action = action; |
| } |
| |
| @StrutsTagAttribute(description="The namespace to use") |
| public void setNamespace(String namespace) { |
| this.namespace = namespace; |
| } |
| |
| @StrutsTagAttribute(description="The method of action to use") |
| public void setMethod(String method) { |
| this.method = method; |
| } |
| |
| @StrutsTagAttribute(description="Whether to encode parameters", type="Boolean", defaultValue="true") |
| public void setEncode(boolean encode) { |
| this.encode = encode; |
| } |
| |
| @StrutsTagAttribute(description="Whether actual context should be included in URL", type="Boolean", defaultValue="true") |
| public void setIncludeContext(boolean includeContext) { |
| this.includeContext = includeContext; |
| } |
| |
| @StrutsTagAttribute(description="The resulting portlet mode") |
| public void setPortletMode(String portletMode) { |
| this.portletMode = portletMode; |
| } |
| |
| @StrutsTagAttribute(description="The resulting portlet window state") |
| public void setWindowState(String windowState) { |
| this.windowState = windowState; |
| } |
| |
| @StrutsTagAttribute(description="Specifies if this should be a portlet render or action URL. Default is \"render\". To create an action URL, use \"action\".") |
| public void setPortletUrlType(String portletUrlType) { |
| this.portletUrlType = portletUrlType; |
| } |
| |
| @StrutsTagAttribute(description="The anchor for this URL") |
| public void setAnchor(String anchor) { |
| this.anchor = anchor; |
| } |
| |
| @StrutsTagAttribute(description="Specifies whether to escape ampersand (&) to (&amp;) or not", type="Boolean", defaultValue="true") |
| public void setEscapeAmp(boolean escapeAmp) { |
| this.escapeAmp = escapeAmp; |
| } |
| |
| @StrutsTagAttribute(description="Specifies whether to force the addition of scheme, host and port or not", type="Boolean", defaultValue="false") |
| public void setForceAddSchemeHostAndPort(boolean forceAddSchemeHostAndPort) { |
| this.forceAddSchemeHostAndPort = forceAddSchemeHostAndPort; |
| } |
| |
| |
| /** |
| * Merge request parameters into current parameters. If a parameter is |
| * already present, than the request parameter in the current request and value atrribute |
| * will not override its value. |
| * |
| * The priority is as follows:- |
| * <ul> |
| * <li>parameter from the current request (least priority)</li> |
| * <li>parameter form the value attribute (more priority)</li> |
| * <li>parameter from the param tag (most priority)</li> |
| * </ul> |
| * |
| * @param value the value attribute (url to be generated by this component) |
| * @param parameters component parameters |
| * @param contextParameters request parameters |
| */ |
| protected void mergeRequestParameters(String value, Map parameters, Map 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" |
| // where the parameters specified in value attribute takes priority. |
| |
| if (value != null && value.trim().length() > 0 && value.indexOf("?") > 0) { |
| mergedParams = new LinkedHashMap(); |
| |
| 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()); |
| } |
| } |
| } |
| |
| |
| // Merge parameters specified in value attribute |
| // eg. value="someAction.action?id=someId&venue=someVenue" |
| // with parameters specified though param tag |
| // eg. <param name="id" value="%{'someId'}" /> |
| // 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()); |
| } |
| } |
| } |
| |
| public static interface ExtraParameterProvider { |
| public Map getExtraParameters(); |
| } |
| } |