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