View Javadoc

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