View Javadoc

1   /*
2    * $Id: ServletUrlRenderer.java 651946 2008-04-27 13:41:38Z apetrelli $
3    *
4    * Licensed to the Apache Software Foundation (ASF) under one
5    * or more contributor license agreements.  See the NOTICE file
6    * distributed with this work for additional information
7    * regarding copyright ownership.  The ASF licenses this file
8    * to you under the Apache License, Version 2.0 (the
9    * "License"); you may not use this file except in compliance
10   * with the License.  You may obtain a copy of the License at
11   *
12   *  http://www.apache.org/licenses/LICENSE-2.0
13   *
14   * Unless required by applicable law or agreed to in writing,
15   * software distributed under the License is distributed on an
16   * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
17   * KIND, either express or implied.  See the License for the
18   * specific language governing permissions and limitations
19   * under the License.
20   */
21  
22  package org.apache.struts2.components;
23  
24  import java.io.IOException;
25  import java.io.Writer;
26  import java.util.Collections;
27  import java.util.Iterator;
28  import java.util.LinkedHashMap;
29  import java.util.Map;
30  
31  import org.apache.struts2.StrutsException;
32  import org.apache.struts2.dispatcher.mapper.ActionMapping;
33  import org.apache.struts2.dispatcher.mapper.ActionMapper;
34  import org.apache.struts2.views.util.UrlHelper;
35  
36  import com.opensymphony.xwork2.ActionContext;
37  import com.opensymphony.xwork2.ActionInvocation;
38  import com.opensymphony.xwork2.inject.Inject;
39  import com.opensymphony.xwork2.config.entities.ActionConfig;
40  import com.opensymphony.xwork2.util.logging.Logger;
41  import com.opensymphony.xwork2.util.logging.LoggerFactory;
42  
43  /***
44   * Implementation of the {@link UrlRenderer} interface that creates URLs suitable in a servlet environment.
45   * 
46   */
47  public class ServletUrlRenderer implements UrlRenderer {
48      /***
49       * Provide a logging instance.
50       */
51      private static final Logger LOG = LoggerFactory.getLogger(ServletUrlRenderer.class);
52  
53      private ActionMapper actionMapper;
54  
55      @Inject
56      public void setActionMapper(ActionMapper mapper) {
57          this.actionMapper = mapper;
58      }
59  
60  
61      /***
62  	 * {@inheritDoc}
63  	 */
64  	public void renderUrl(Writer writer, URL urlComponent) {
65  		String scheme = urlComponent.req.getScheme();
66  
67  		if (urlComponent.scheme != null) {
68  			scheme = urlComponent.scheme;
69  		}
70  
71  	       String result;
72  	        if (urlComponent.value == null && urlComponent.action != null) {
73  	                result = urlComponent.determineActionURL(urlComponent.action, urlComponent.namespace, urlComponent.method, urlComponent.req, urlComponent.res, urlComponent.parameters, scheme, urlComponent.includeContext, urlComponent.encode, urlComponent.forceAddSchemeHostAndPort, urlComponent.escapeAmp);
74  	        } else {
75  	                String _value = urlComponent.value;
76  
77  	                // We don't include the request parameters cause they would have been
78  	                // prioritised before this [in start(Writer) method]
79  	                if (_value != null && _value.indexOf("?") > 0) {
80  	                    _value = _value.substring(0, _value.indexOf("?"));
81  	                }
82  	                result = UrlHelper.buildUrl(_value, urlComponent.req, urlComponent.res, urlComponent.parameters, scheme, urlComponent.includeContext, urlComponent.encode, urlComponent.forceAddSchemeHostAndPort, urlComponent.escapeAmp);
83  	        }
84  	        if ( urlComponent.anchor != null && urlComponent.anchor.length() > 0 ) {
85  	            result += '#' + urlComponent.anchor;
86  	        }
87  
88  	        String var = urlComponent.getVar();
89  
90  	        if (var != null) {
91  	        	urlComponent.putInContext(result);
92  
93  	            // add to the request and page scopes as well
94  	        	urlComponent.req.setAttribute(var, result);
95  	        } else {
96  	            try {
97  	                writer.write(result);
98  	            } catch (IOException e) {
99  	                throw new StrutsException("IOError: " + e.getMessage(), e);
100 	            }
101 	        }
102 	}
103 
104 	/***
105 	 * {@inheritDoc}
106 	 */
107 	public void renderFormUrl(Form formComponent) {
108 		String namespace = formComponent.determineNamespace(formComponent.namespace, formComponent.getStack(),
109 				formComponent.request);
110 		String action;
111 
112 		if(formComponent.action != null) {
113 			action = formComponent.findString(formComponent.action);
114 		} else {
115 			// no action supplied? ok, then default to the current request
116 			// (action or general URL)
117 			ActionInvocation ai = (ActionInvocation) formComponent.getStack().getContext().get(
118 					ActionContext.ACTION_INVOCATION);
119 			if (ai != null) {
120 				action = ai.getProxy().getActionName();
121 				namespace = ai.getProxy().getNamespace();
122 			} else {
123 				// hmm, ok, we need to just assume the current URL cut down
124 				String uri = formComponent.request.getRequestURI();
125 				action = uri.substring(uri.lastIndexOf('/'));
126 			}
127 		}
128 
129         ActionMapping nameMapping = actionMapper.getMappingFromActionName(action);
130         String actionName = nameMapping.getName();
131         String actionMethod = nameMapping.getMethod();
132 
133 		final ActionConfig actionConfig = formComponent.configuration.getRuntimeConfiguration().getActionConfig(
134 				namespace, action);
135 		if (actionConfig != null) {
136 
137 			ActionMapping mapping = new ActionMapping(action, namespace, actionMethod, formComponent.parameters);
138 			String result = UrlHelper.buildUrl(formComponent.actionMapper.getUriFromActionMapping(mapping),
139 					formComponent.request, formComponent.response, null);
140 			formComponent.addParameter("action", result);
141 
142 			// let's try to get the actual action class and name
143 			// this can be used for getting the list of validators
144 			formComponent.addParameter("actionName", actionName);
145 			try {
146 				Class clazz = formComponent.objectFactory.getClassInstance(actionConfig.getClassName());
147 				formComponent.addParameter("actionClass", clazz);
148 			} catch (ClassNotFoundException e) {
149 				// this is OK, we'll just move on
150 			}
151 
152 			formComponent.addParameter("namespace", namespace);
153 
154 			// if the name isn't specified, use the action name
155 			if (formComponent.name == null) {
156 				formComponent.addParameter("name", action);
157 			}
158 
159 			// if the id isn't specified, use the action name
160 			if (formComponent.getId() == null  && action!=null ) {
161 				formComponent.addParameter("id", formComponent.escape(action));
162 			}
163 		} else if (action != null) {
164 			// Since we can't find an action alias in the configuration, we just
165 			// assume the action attribute supplied is the path to be used as
166 			// the URI this form is submitting to.
167 
168             // Warn user that the specified namespace/action combo
169             // was not found in the configuration.
170             if (namespace != null) {
171               LOG.warn("No configuration found for the specified action: '" + action + "' in namespace: '" + namespace + "'. Form action defaulting to 'action' attribute's literal value.");
172             }
173 
174 			String result = UrlHelper.buildUrl(action, formComponent.request, formComponent.response, null);
175 			formComponent.addParameter("action", result);
176 
177 			// namespace: cut out anything between the start and the last /
178 			int slash = result.lastIndexOf('/');
179 			if (slash != -1) {
180 				formComponent.addParameter("namespace", result.substring(0, slash));
181 			} else {
182 				formComponent.addParameter("namespace", "");
183 			}
184 
185 			// name/id: cut out anything between / and . should be the id and
186 			// name
187 			String id = formComponent.getId();
188 			if (id == null) {
189 				slash = result.lastIndexOf('/');
190 				int dot = result.indexOf('.', slash);
191 				if (dot != -1) {
192 					id = result.substring(slash + 1, dot);
193 				} else {
194 					id = result.substring(slash + 1);
195 				}
196 				formComponent.addParameter("id", formComponent.escape(id));
197 			}
198 		}
199 
200 		// WW-1284
201 		// evaluate if client-side js is to be enabled. (if validation
202 		// interceptor does allow validation eg. method is not filtered out)
203 		formComponent.evaluateClientSideJsEnablement(actionName, namespace, actionMethod);
204 	}
205 
206 
207 	public void beforeRenderUrl(URL urlComponent) {
208 		if (urlComponent.value != null) {
209             urlComponent.value = urlComponent.findString(urlComponent.value);
210         }
211 
212         // no explicit url set so attach params from current url, do
213         // this at start so body params can override any of these they wish.
214         try {
215             // ww-1266
216             String includeParams = (urlComponent.urlIncludeParams != null ? urlComponent.urlIncludeParams.toLowerCase() : URL.GET);
217 
218             if (urlComponent.includeParams != null) {
219                 includeParams = urlComponent.findString(urlComponent.includeParams);
220             }
221 
222             if (URL.NONE.equalsIgnoreCase(includeParams)) {
223                 mergeRequestParameters(urlComponent.value, urlComponent.parameters, Collections.EMPTY_MAP);
224             } else if (URL.ALL.equalsIgnoreCase(includeParams)) {
225                 mergeRequestParameters(urlComponent.value, urlComponent.parameters, urlComponent.req.getParameterMap());
226 
227                 // for ALL also include GET parameters
228                 includeGetParameters(urlComponent);
229                 includeExtraParameters(urlComponent);
230             } else if (URL.GET.equalsIgnoreCase(includeParams) || (includeParams == null && urlComponent.value == null && urlComponent.action == null)) {
231                 includeGetParameters(urlComponent);
232                 includeExtraParameters(urlComponent);
233             } else if (includeParams != null) {
234                 LOG.warn("Unknown value for includeParams parameter to URL tag: " + includeParams);
235             }
236         } catch (Exception e) {
237             LOG.warn("Unable to put request parameters (" + urlComponent.req.getQueryString() + ") into parameter map.", e);
238         }
239 
240 		
241 	}
242 	
243     private void includeExtraParameters(URL urlComponent) {
244         if (urlComponent.extraParameterProvider != null) {
245             mergeRequestParameters(urlComponent.value, urlComponent.parameters, urlComponent.extraParameterProvider.getExtraParameters());
246         }
247     }
248     private void includeGetParameters(URL urlComponent) {
249     	String query = extractQueryString(urlComponent);
250     	mergeRequestParameters(urlComponent.value, urlComponent.parameters, UrlHelper.parseQueryString(query));
251     }
252 
253     private String extractQueryString(URL urlComponent) {
254         // Parse the query string to make sure that the parameters come from the query, and not some posted data
255         String query = urlComponent.req.getQueryString();
256         if (query == null) {
257             query = (String) urlComponent.req.getAttribute("javax.servlet.forward.query_string");
258         }
259 
260         if (query != null) {
261             // Remove possible #foobar suffix
262             int idx = query.lastIndexOf('#');
263 
264             if (idx != -1) {
265                 query = query.substring(0, idx);
266             }
267         }
268         return query;
269     }
270     
271     /***
272      * Merge request parameters into current parameters. If a parameter is
273      * already present, than the request parameter in the current request and value atrribute
274      * will not override its value.
275      *
276      * The priority is as follows:-
277      * <ul>
278      *  <li>parameter from the current request (least priority)</li>
279      *  <li>parameter form the value attribute (more priority)</li>
280      *  <li>parameter from the param tag (most priority)</li>
281      * </ul>
282      *
283      * @param value the value attribute (url to be generated by this component)
284      * @param parameters component parameters
285      * @param contextParameters request parameters
286      */
287     protected void mergeRequestParameters(String value, Map parameters, Map contextParameters){
288 
289         Map mergedParams = new LinkedHashMap(contextParameters);
290 
291         // Merge contextParameters (from current request) with parameters specified in value attribute
292         // eg. value="someAction.action?id=someId&venue=someVenue"
293         // where the parameters specified in value attribute takes priority.
294 
295         if (value != null && value.trim().length() > 0 && value.indexOf("?") > 0) {
296             mergedParams = new LinkedHashMap();
297 
298             String queryString = value.substring(value.indexOf("?")+1);
299 
300             mergedParams = UrlHelper.parseQueryString(queryString);
301             for (Iterator iterator = contextParameters.entrySet().iterator(); iterator.hasNext();) {
302                 Map.Entry entry = (Map.Entry) iterator.next();
303                 Object key = entry.getKey();
304 
305                 if (!mergedParams.containsKey(key)) {
306                     mergedParams.put(key, entry.getValue());
307                 }
308             }
309         }
310 
311 
312         // Merge parameters specified in value attribute
313         // eg. value="someAction.action?id=someId&venue=someVenue"
314         // with parameters specified though param tag
315         // eg. <param name="id" value="%{'someId'}" />
316         // where parameters specified through param tag takes priority.
317 
318         for (Iterator iterator = mergedParams.entrySet().iterator(); iterator.hasNext();) {
319             Map.Entry entry = (Map.Entry) iterator.next();
320             Object key = entry.getKey();
321 
322             if (!parameters.containsKey(key)) {
323                 parameters.put(key, entry.getValue());
324             }
325         }
326     }
327 }