1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18 package org.apache.struts2.components;
19
20 import java.io.IOException;
21 import java.io.Writer;
22 import java.util.Collections;
23 import java.util.Iterator;
24 import java.util.LinkedHashMap;
25 import java.util.Map;
26
27 import javax.servlet.http.HttpServletRequest;
28 import javax.servlet.http.HttpServletResponse;
29
30 import org.apache.commons.logging.Log;
31 import org.apache.commons.logging.LogFactory;
32 import org.apache.struts2.StrutsException;
33 import org.apache.struts2.StrutsConstants;
34 import org.apache.struts2.config.Settings;
35 import org.apache.struts2.dispatcher.Dispatcher;
36 import org.apache.struts2.portlet.context.PortletActionContext;
37 import org.apache.struts2.portlet.util.PortletUrlHelper;
38 import org.apache.struts2.views.util.UrlHelper;
39
40 import com.opensymphony.xwork2.ActionContext;
41 import com.opensymphony.xwork2.util.ValueStack;
42 import com.opensymphony.xwork2.util.XWorkContinuationConfig;
43
44 /***
45 * <!-- START SNIPPET: javadoc -->
46 *
47 * <p>This tag is used to create a URL.</p>
48 *
49 * <p>You can use the "param" tag inside the body to provide
50 * additional request parameters.</p>
51 *
52 * <b>NOTE:</b>
53 * <p>When includeParams is 'all' or 'get', the parameter defined in param tag will take
54 * precedence and will not be overriden if they exists in the parameter submitted. For
55 * example, in Example 3 below, if there is a id parameter in the url where the page this
56 * tag is included like http://<host>:<port>/<context>/editUser.action?id=3333&name=John
57 * the generated url will be http://<host>:<port>/context>/editUser.action?id=22&name=John
58 * cause the parameter defined in the param tag will take precedence.</p>
59 *
60 * <!-- END SNIPPET: javadoc -->
61 *
62 *
63 * <!-- START SNIPPET: params -->
64 *
65 * <ul>
66 * <li>action (String) - (value or action choose either one, if both exist value takes precedence) action's name (alias) <li>
67 * <li>value (String) - (value or action choose either one, if both exist value takes precedence) the url itself</li>
68 * <li>scheme (String) - http scheme (http, https) default to the scheme this request is in</li>
69 * <li>namespace - action's namespace</li>
70 * <li>method (String) - action's method, default to execute() </li>
71 * <li>encode (Boolean) - url encode the generated url. Default is true</li>
72 * <li>includeParams (String) - The includeParams attribute may have the value 'none', 'get' or 'all'. Default is 'get'.
73 * none - include no parameters in the URL
74 * get - include only GET parameters in the URL (default)
75 * all - include both GET and POST parameters in the URL
76 * </li>
77 * <li>includeContext (Boolean) - determine wheather to include the web app context path. Default is true.</li>
78 * </ul>
79 *
80 * <!-- END SNIPPET: params -->
81 *
82 * <p/> <b>Examples</b>
83 * <pre>
84 * <!-- START SNIPPET: example -->
85 *
86 * <-- Example 1 -->
87 * <s:url value="editGadget.action">
88 * <s:param name="id" value="%{selected}" />
89 * </s:url>
90 *
91 * <-- Example 2 -->
92 * <s:url action="editGadget">
93 * <s:param name="id" value="%{selected}" />
94 * </s:url>
95 *
96 * <-- Example 3-->
97 * <s:url includeParams="get" >
98 * <:param name="id" value="%{'22'}" />
99 * </s:url>
100 *
101 * <!-- END SNIPPET: example -->
102 * </pre>
103 *
104 * @see Param
105 *
106 * @s.tag name="url" tld-body-content="JSP" tld-tag-class="org.apache.struts2.views.jsp.URLTag"
107 * description="This tag is used to create a URL"
108 */
109 public class URL extends Component {
110 private static final Log LOG = LogFactory.getLog(URL.class);
111
112 /***
113 * The includeParams attribute may have the value 'none', 'get' or 'all'.
114 * It is used when the url tag is used without a value attribute.
115 * Its value is looked up on the ValueStack
116 * If no includeParams is specified then 'get' is used.
117 * none - include no parameters in the URL
118 * get - include only GET parameters in the URL (default)
119 * all - include both GET and POST parameters in the URL
120 */
121 public static final String NONE = "none";
122 public static final String GET = "get";
123 public static final String ALL = "all";
124
125 private HttpServletRequest req;
126 private HttpServletResponse res;
127
128 protected String includeParams;
129 protected String scheme;
130 protected String value;
131 protected String action;
132 protected String namespace;
133 protected String method;
134 protected boolean encode = true;
135 protected boolean includeContext = true;
136 protected String portletMode;
137 protected String windowState;
138 protected String portletUrlType;
139 protected String anchor;
140
141 public URL(ValueStack stack, HttpServletRequest req, HttpServletResponse res) {
142 super(stack);
143 this.req = req;
144 this.res = res;
145 }
146
147 public boolean start(Writer writer) {
148 boolean result = super.start(writer);
149
150 if (value != null) {
151 value = findString(value);
152 }
153
154
155
156 try {
157
158 String includeParams =
159 Settings.isSet(StrutsConstants.STRUTS_URL_INCLUDEPARAMS) ?
160 Settings.get(StrutsConstants.STRUTS_URL_INCLUDEPARAMS).toLowerCase() : GET;
161
162
163 if (this.includeParams != null) {
164 includeParams = findString(this.includeParams);
165 }
166
167 if (NONE.equalsIgnoreCase(includeParams)) {
168 mergeRequestParameters(value, parameters, Collections.EMPTY_MAP);
169 ActionContext.getContext().put(XWorkContinuationConfig.CONTINUE_KEY, null);
170 } else if (ALL.equalsIgnoreCase(includeParams)) {
171 mergeRequestParameters(value, parameters, req.getParameterMap());
172
173
174 includeGetParameters();
175 } else if (GET.equalsIgnoreCase(includeParams) || (includeParams == null && value == null && action == null)) {
176 includeGetParameters();
177 } else if (includeParams != null) {
178 LOG.warn("Unknown value for includeParams parameter to URL tag: " + includeParams);
179 }
180 } catch (Exception e) {
181 LOG.warn("Unable to put request parameters (" + req.getQueryString() + ") into parameter map.", e);
182 }
183
184
185 return result;
186 }
187
188 private void includeGetParameters() {
189 if(!(Dispatcher.getInstance().isPortletSupportActive() && PortletActionContext.isPortletRequest())) {
190 String query = extractQueryString();
191 mergeRequestParameters(value, parameters, UrlHelper.parseQueryString(query));
192 }
193 }
194
195 private String extractQueryString() {
196
197 String query = req.getQueryString();
198
199 if (query != null) {
200
201 int idx = query.lastIndexOf('#');
202
203 if (idx != -1) {
204 query = query.substring(0, idx);
205 }
206 }
207 return query;
208 }
209
210 public boolean end(Writer writer, String body) {
211 String scheme = req.getScheme();
212
213 if (this.scheme != null) {
214 scheme = this.scheme;
215 }
216
217 String result;
218 if (value == null && action != null) {
219 if(Dispatcher.getInstance().isPortletSupportActive() && PortletActionContext.isPortletRequest()) {
220 result = PortletUrlHelper.buildUrl(action, namespace, parameters, portletUrlType, portletMode, windowState);
221 }
222 else {
223 result = determineActionURL(action, namespace, method, req, res, parameters, scheme, includeContext, encode);
224 }
225 } else {
226 if(Dispatcher.getInstance().isPortletSupportActive() && PortletActionContext.isPortletRequest()) {
227 result = PortletUrlHelper.buildResourceUrl(value, parameters);
228 }
229 else {
230 String _value = value;
231
232
233
234 if (_value != null && _value.indexOf("?") > 0) {
235 _value = _value.substring(0, _value.indexOf("?"));
236 }
237 result = UrlHelper.buildUrl(_value, req, res, parameters, scheme, includeContext, encode);
238 }
239 }
240 if ( anchor != null && anchor.length() > 0 ) {
241 result += '#' + anchor;
242 }
243
244 String id = getId();
245
246 if (id != null) {
247 getStack().getContext().put(id, result);
248
249
250 req.setAttribute(id, result);
251 } else {
252 try {
253 writer.write(result);
254 } catch (IOException e) {
255 throw new StrutsException("IOError: " + e.getMessage(), e);
256 }
257 }
258 return super.end(writer, body);
259 }
260
261 /***
262 * The includeParams attribute may have the value 'none', 'get' or 'all'.
263 * @s.tagattribute required="false" default="get"
264 */
265 public void setIncludeParams(String includeParams) {
266 this.includeParams = includeParams;
267 }
268
269 /***
270 * Set scheme attribute
271 * @s.tagattribute required="false"
272 */
273 public void setScheme(String scheme) {
274 this.scheme = scheme;
275 }
276
277 /***
278 * The target value to use, if not using action
279 * @s.tagattribute required="false"
280 */
281 public void setValue(String value) {
282 this.value = value;
283 }
284
285 /***
286 * The action generate url for, if not using value
287 * @s.tagattribute required="false"
288 */
289 public void setAction(String action) {
290 this.action = action;
291 }
292
293 /***
294 * The namespace to use
295 * @s.tagattribute required="false"
296 */
297 public void setNamespace(String namespace) {
298 this.namespace = namespace;
299 }
300
301 /***
302 * The method of action to use
303 * @s.tagattribute required="false"
304 */
305 public void setMethod(String method) {
306 this.method = method;
307 }
308
309 /***
310 * whether to encode parameters
311 * @s.tagattribute required="false" type="Boolean" default="true"
312 */
313 public void setEncode(boolean encode) {
314 this.encode = encode;
315 }
316
317 /***
318 * whether actual context should be included in url
319 * @s.tagattribute required="false" type="Boolean" default="true"
320 */
321 public void setIncludeContext(boolean includeContext) {
322 this.includeContext = includeContext;
323 }
324
325 /***
326 * The resulting portlet mode
327 * @s.tagattribute required="false"
328 */
329 public void setPortletMode(String portletMode) {
330 this.portletMode = portletMode;
331 }
332
333 /***
334 * The resulting portlet window state
335 * @s.tagattribute required="false"
336 */
337 public void setWindowState(String windowState) {
338 this.windowState = windowState;
339 }
340
341 /***
342 * Specifies if this should be a portlet render or action url
343 * @s.tagattribute required="false"
344 */
345 public void setPortletUrlType(String portletUrlType) {
346 this.portletUrlType = portletUrlType;
347 }
348
349 /***
350 * The anchor for this URL
351 * @s.tagattribute required="false"
352 */
353 public void setAnchor(String anchor) {
354 this.anchor = anchor;
355 }
356
357
358 /***
359 * Merge request parameters into current parameters. If a parameter is
360 * already present, than the request parameter in the current request and value atrribute
361 * will not override its value.
362 *
363 * The priority is as follows:-
364 * <ul>
365 * <li>parameter from the current request (least priority)</li>
366 * <li>parameter form the value attribute (more priority)</li>
367 * <li>parameter from the param tag (most priority)</li>
368 * </ul>
369 *
370 * @param value the value attribute (url to be generated by this component)
371 * @param parameters component parameters
372 * @param contextParameters request parameters
373 */
374 protected void mergeRequestParameters(String value, Map parameters, Map contextParameters){
375
376 Map mergedParams = new LinkedHashMap(contextParameters);
377
378
379
380
381
382 if (value != null && value.trim().length() > 0 && value.indexOf("?") > 0) {
383 mergedParams = new LinkedHashMap();
384
385 String queryString = value.substring(value.indexOf("?")+1);
386
387 mergedParams = UrlHelper.parseQueryString(queryString);
388 for (Iterator iterator = contextParameters.entrySet().iterator(); iterator.hasNext();) {
389 Map.Entry entry = (Map.Entry) iterator.next();
390 Object key = entry.getKey();
391
392 if (!mergedParams.containsKey(key)) {
393 mergedParams.put(key, entry.getValue());
394 }
395 }
396 }
397
398
399
400
401
402
403
404
405 for (Iterator iterator = mergedParams.entrySet().iterator(); iterator.hasNext();) {
406 Map.Entry entry = (Map.Entry) iterator.next();
407 Object key = entry.getKey();
408
409 if (!parameters.containsKey(key)) {
410 parameters.put(key, entry.getValue());
411 }
412 }
413 }
414 }