| /* |
| * 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 com.opensymphony.xwork2.ActionContext; |
| import com.opensymphony.xwork2.ActionInvocation; |
| import com.opensymphony.xwork2.config.entities.ActionConfig; |
| import com.opensymphony.xwork2.inject.Inject; |
| import com.opensymphony.xwork2.util.ValueStack; |
| import org.apache.commons.lang3.StringUtils; |
| import org.apache.logging.log4j.LogManager; |
| import org.apache.logging.log4j.Logger; |
| import org.apache.struts2.StrutsException; |
| import org.apache.struts2.dispatcher.mapper.ActionMapper; |
| import org.apache.struts2.dispatcher.mapper.ActionMapping; |
| import org.apache.struts2.views.TagAttribute; |
| import org.apache.struts2.views.util.UrlHelper; |
| |
| import java.io.IOException; |
| import java.io.Writer; |
| import java.util.Collections; |
| import java.util.LinkedHashMap; |
| import java.util.Map; |
| |
| /** |
| * Implementation of the {@link UrlRenderer} interface that creates URLs suitable in a servlet environment. |
| */ |
| public class ServletUrlRenderer implements UrlRenderer { |
| /** |
| * Provide a logging instance. |
| */ |
| private static final Logger LOG = LogManager.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} |
| */ |
| public void renderUrl(Writer writer, UrlProvider urlComponent) { |
| String scheme = urlComponent.getHttpServletRequest().getScheme(); |
| |
| if (urlComponent.getScheme() != null) { |
| ValueStack vs = ActionContext.getContext().getValueStack(); |
| scheme = vs.findString(urlComponent.getScheme()); |
| if (scheme == null) { |
| scheme = urlComponent.getScheme(); |
| } |
| } |
| |
| String result; |
| ActionInvocation ai = ActionContext.getContext().getActionInvocation(); |
| if (urlComponent.getValue() == null && urlComponent.getAction() != null) { |
| result = urlComponent.determineActionURL(urlComponent.getAction(), urlComponent.getNamespace(), urlComponent.getMethod(), urlComponent.getHttpServletRequest(), urlComponent.getHttpServletResponse(), urlComponent.getParameters(), scheme, urlComponent.isIncludeContext(), urlComponent.isEncode(), urlComponent.isForceAddSchemeHostAndPort(), urlComponent.isEscapeAmp()); |
| } else if (urlComponent.getValue() == null && urlComponent.getAction() == null && ai != null) { |
| // both are null, we will default to the current action |
| |
| final String action = ai.getProxy().getActionName(); |
| final String namespace = ai.getProxy().getNamespace(); |
| final String method = urlComponent.getMethod() != null || !ai.getProxy().isMethodSpecified() ? urlComponent.getMethod() : ai.getProxy().getMethod(); |
| result = urlComponent.determineActionURL(action, namespace, method, urlComponent.getHttpServletRequest(), urlComponent.getHttpServletResponse(), urlComponent.getParameters(), scheme, urlComponent.isIncludeContext(), urlComponent.isEncode(), urlComponent.isForceAddSchemeHostAndPort(), urlComponent.isEscapeAmp()); |
| } else { |
| String _value = urlComponent.getValue(); |
| |
| // 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, urlComponent.getHttpServletRequest(), urlComponent.getHttpServletResponse(), urlComponent.getParameters(), scheme, urlComponent.isIncludeContext(), urlComponent.isEncode(), urlComponent.isForceAddSchemeHostAndPort(), urlComponent.isEscapeAmp()); |
| } |
| String anchor = urlComponent.getAnchor(); |
| if (StringUtils.isNotEmpty(anchor)) { |
| result += '#' + urlComponent.findString(anchor); |
| } |
| |
| if (urlComponent.isPutInContext()) { |
| String var = urlComponent.getVar(); |
| if (StringUtils.isNotEmpty(var)) { |
| urlComponent.putInContext(result); |
| |
| // add to the request and page scopes as well |
| urlComponent.getHttpServletRequest().setAttribute(var, result); |
| } else { |
| try { |
| writer.write(result); |
| } catch (IOException e) { |
| throw new StrutsException("IOError: " + e.getMessage(), e); |
| } |
| } |
| } else { |
| try { |
| writer.write(result); |
| } catch (IOException e) { |
| throw new StrutsException("IOError: " + e.getMessage(), e); |
| } |
| } |
| } |
| |
| /** |
| * {@inheritDoc} |
| */ |
| public void renderFormUrl(Form formComponent) { |
| String namespace = formComponent.determineNamespace(formComponent.namespace, formComponent.getStack(), formComponent.request); |
| String action; |
| |
| ValueStack vs = ActionContext.getContext().getValueStack(); |
| String scheme = vs.findString("scheme"); |
| |
| if (formComponent.action != null) { |
| action = formComponent.findString(formComponent.action); |
| } else { |
| // no action supplied? ok, then default to the current request |
| // (action or general URL) |
| ActionInvocation ai = formComponent.getStack().getActionContext().getActionInvocation(); |
| if (ai != null) { |
| action = ai.getProxy().getActionName(); |
| namespace = ai.getProxy().getNamespace(); |
| } else { |
| // hmm, ok, we need to just assume the current URL cut down |
| String uri = formComponent.request.getRequestURI(); |
| action = uri.substring(uri.lastIndexOf('/')); |
| } |
| } |
| |
| Map actionParams = null; |
| if (action != null && action.indexOf('?') > 0) { |
| String queryString = action.substring(action.indexOf('?') + 1); |
| actionParams = urlHelper.parseQueryString(queryString, false); |
| action = action.substring(0, action.indexOf('?')); |
| } |
| |
| ActionMapping nameMapping = actionMapper.getMappingFromActionName(action); |
| String actionName = nameMapping.getName(); |
| String actionMethod = nameMapping.getMethod(); |
| |
| final ActionConfig actionConfig = formComponent.configuration.getRuntimeConfiguration().getActionConfig( |
| namespace, actionName); |
| if (actionConfig != null) { |
| |
| ActionMapping mapping = new ActionMapping(actionName, namespace, actionMethod, formComponent.parameters); |
| String result = urlHelper.buildUrl(formComponent.actionMapper.getUriFromActionMapping(mapping), |
| formComponent.request, formComponent.response, actionParams, scheme, formComponent.includeContext, true, false, false); |
| formComponent.addParameter("action", result); |
| |
| // let's try to get the actual action class and name |
| // this can be used for getting the list of validators |
| formComponent.addParameter("actionName", actionName); |
| try { |
| Class clazz = formComponent.objectFactory.getClassInstance(actionConfig.getClassName()); |
| formComponent.addParameter("actionClass", clazz); |
| } catch (ClassNotFoundException e) { |
| // this is OK, we'll just move on |
| } |
| |
| formComponent.addParameter("namespace", namespace); |
| |
| // if the name isn't specified, use the action name |
| if (formComponent.name == null) { |
| formComponent.addParameter("name", actionName); |
| } |
| |
| // if the id isn't specified, use the action name |
| TagAttribute id = formComponent.getId(); |
| if (id.isNull() && actionName != null) { |
| formComponent.addParameter("id", TagAttribute.evaluated(actionName).escaped()); |
| } |
| } else if (action != null) { |
| // Since we can't find an action alias in the configuration, we just |
| // assume the action attribute supplied is the path to be used as |
| // the URI this form is submitting to. |
| |
| // Warn user that the specified namespace/action combo |
| // was not found in the configuration. |
| if (namespace != null && LOG.isWarnEnabled()) { |
| LOG.warn("No configuration found for the specified action: '{}' in namespace: '{}'. Form action defaulting to 'action' attribute's literal value.", actionName, namespace); |
| } |
| |
| String result = urlHelper.buildUrl(action, formComponent.request, formComponent.response, actionParams, scheme, formComponent.includeContext, true); |
| formComponent.addParameter("action", result); |
| |
| // namespace: cut out anything between the start and the last / |
| int slash = result.lastIndexOf('/'); |
| if (slash != -1) { |
| formComponent.addParameter("namespace", result.substring(0, slash)); |
| } else { |
| formComponent.addParameter("namespace", ""); |
| } |
| |
| // name/id: cut out anything between / and . should be the id and |
| // name |
| TagAttribute id = formComponent.getId(); |
| if (id == null || id.isNull()) { |
| slash = result.lastIndexOf('/'); |
| int dot = result.indexOf('.', slash); |
| if (dot != -1) { |
| id = TagAttribute.evaluated(result.substring(slash + 1, dot)); |
| } else { |
| id = TagAttribute.evaluated(result.substring(slash + 1)); |
| } |
| formComponent.addParameter("id", id.escaped()); |
| } |
| } |
| |
| // WW-1284 |
| // evaluate if client-side js is to be enabled. (if validation |
| // interceptor does allow validation eg. method is not filtered out) |
| formComponent.evaluateClientSideJsEnablement(actionName, namespace, actionMethod); |
| } |
| |
| |
| public void beforeRenderUrl(UrlProvider urlComponent) { |
| if (urlComponent.getValue() != null) { |
| urlComponent.setValue(urlComponent.findString(urlComponent.getValue())); |
| } |
| |
| // 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 = (urlComponent.getUrlIncludeParams() != null ? urlComponent.getUrlIncludeParams().toLowerCase() : UrlProvider.GET); |
| |
| if (urlComponent.getIncludeParams() != null) { |
| includeParams = urlComponent.findString(urlComponent.getIncludeParams()); |
| } |
| |
| if (UrlProvider.NONE.equalsIgnoreCase(includeParams)) { |
| mergeRequestParameters(urlComponent.getValue(), urlComponent.getParameters(), Collections.<String, Object>emptyMap()); |
| } else if (UrlProvider.ALL.equalsIgnoreCase(includeParams)) { |
| mergeRequestParameters(urlComponent.getValue(), urlComponent.getParameters(), urlComponent.getHttpServletRequest().getParameterMap()); |
| |
| // for ALL also include GET parameters |
| includeGetParameters(urlComponent); |
| includeExtraParameters(urlComponent); |
| } else if (UrlProvider.GET.equalsIgnoreCase(includeParams) || (includeParams == null && urlComponent.getValue() == null && urlComponent.getAction() == null)) { |
| includeGetParameters(urlComponent); |
| includeExtraParameters(urlComponent); |
| } 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 ({}) into parameter map.", urlComponent.getHttpServletRequest().getQueryString(), e); |
| } |
| } |
| |
| private void includeExtraParameters(UrlProvider urlComponent) { |
| if (urlComponent.getExtraParameterProvider() != null) { |
| mergeRequestParameters(urlComponent.getValue(), urlComponent.getParameters(), urlComponent.getExtraParameterProvider().getExtraParameters()); |
| } |
| } |
| |
| private void includeGetParameters(UrlProvider urlComponent) { |
| String query = extractQueryString(urlComponent); |
| mergeRequestParameters(urlComponent.getValue(), urlComponent.getParameters(), urlHelper.parseQueryString(query, false)); |
| } |
| |
| private String extractQueryString(UrlProvider urlComponent) { |
| // Parse the query string to make sure that the parameters come from the query, and not some posted data |
| String query = urlComponent.getHttpServletRequest().getQueryString(); |
| if (query == null) { |
| query = (String) urlComponent.getHttpServletRequest().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; |
| } |
| |
| /** |
| * 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<String, Object> parameters, Map<String, Object> contextParameters) { |
| |
| Map<String, Object> 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 (StringUtils.contains(value, "?")) { |
| String queryString = value.substring(value.indexOf('?') + 1); |
| |
| mergedParams = urlHelper.parseQueryString(queryString, false); |
| for (Map.Entry<String, Object> 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. <param name="id" value="%{'someId'}" /> |
| // where parameters specified through param tag takes priority. |
| |
| for (Map.Entry<String, Object> entry : mergedParams.entrySet()) { |
| if (!parameters.containsKey(entry.getKey())) { |
| parameters.put(entry.getKey(), entry.getValue()); |
| } |
| } |
| } |
| |
| } |