001: /*
002: * Copyright (c) 2002-2006 by OpenSymphony
003: * All rights reserved.
004: */
005:
006: package com.opensymphony.webwork.components;
007:
008: import com.opensymphony.webwork.portlet.context.PortletActionContext;
009: import com.opensymphony.webwork.portlet.util.PortletUrlHelper;
010: import com.opensymphony.webwork.views.util.UrlHelper;
011: import com.opensymphony.webwork.dispatcher.DispatcherUtils;
012: import com.opensymphony.webwork.WebWorkException;
013: import com.opensymphony.webwork.WebWorkConstants;
014: import com.opensymphony.webwork.config.Configuration;
015: import com.opensymphony.xwork.util.OgnlValueStack;
016: import com.opensymphony.xwork.util.XWorkContinuationConfig;
017: import com.opensymphony.xwork.ActionContext;
018: import org.apache.commons.logging.Log;
019: import org.apache.commons.logging.LogFactory;
020:
021: import javax.servlet.http.HttpServletRequest;
022: import javax.servlet.http.HttpServletResponse;
023: import java.io.IOException;
024: import java.io.Writer;
025: import java.util.Collections;
026: import java.util.Iterator;
027: import java.util.LinkedHashMap;
028: import java.util.Map;
029:
030: /**
031: * <!-- START SNIPPET: javadoc -->
032: *
033: * <p>This tag is used to create a URL.</p>
034: *
035: * <p>You can use the "param" tag inside the body to provide
036: * additional request parameters.</p>
037: *
038: * <b>NOTE:</b>
039: * <p>When includeParams is 'all' or 'get', the parameter defined in param tag will take
040: * precedence and will not be overriden if they exists in the parameter submitted. For
041: * example, in Example 3 below, if there is a id parameter in the url where the page this
042: * tag is included like http://<host>:<port>/<context>/editUser.action?id=3333&name=John
043: * the generated url will be http://<host>:<port>/context>/editUser.action?id=22&name=John
044: * cause the parameter defined in the param tag will take precedence.</p>
045: *
046: * <!-- END SNIPPET: javadoc -->
047: *
048: *
049: * <!-- START SNIPPET: params -->
050: *
051: * <ul>
052: * <li>action (String) - (value or action choose either one, if both exist value takes precedence) action's name (alias) <li>
053: * <li>value (String) - (value or action choose either one, if both exist value takes precedence) the url itself</li>
054: * <li>scheme (String) - http scheme (http, https) default to the scheme this request is in</li>
055: * <li>namespace - action's namespace</li>
056: * <li>method (String) - action's method, default to execute() </li>
057: * <li>encode (Boolean) - url encode the generated url. Default is true</li>
058: * <li>includeParams (String) - The includeParams attribute may have the value 'none', 'get' or 'all'. Default is 'get'.
059: * none - include no parameters in the URL
060: * get - include only GET parameters in the URL (default)
061: * all - include both GET and POST parameters in the URL
062: * </li>
063: * <li>includeContext (Boolean) - determine wheather to include the web app context path. Default is true.</li>
064: * </ul>
065: *
066: * <!-- END SNIPPET: params -->
067: *
068: * <p/> <b>Examples</b>
069: * <pre>
070: * <!-- START SNIPPET: example -->
071: *
072: * <-- Example 1 -->
073: * <ww:url value="editGadget.action">
074: * <ww:param name="id" value="%{selected}" />
075: * </ww:url>
076: *
077: * <-- Example 2 -->
078: * <ww:url action="editGadget">
079: * <ww:param name="id" value="%{selected}" />
080: * </ww:url>
081: *
082: * <-- Example 3-->
083: * <ww:url includeParams="get" >
084: * <:param name="id" value="%{'22'}" />
085: * </ww:url>
086: *
087: * <!-- END SNIPPET: example -->
088: * </pre>
089: *
090: * @author Rickard Öberg (rickard@dreambean.com)
091: * @author Patrick Lightbody
092: * @author Ian Roughley
093: * @author Rene Gielen
094: * @author Rainer Hermanns
095: * @author tm_jee
096: * @version $Revision: 2930 $
097: * @since 2.2
098: *
099: * @see Param
100: *
101: * @ww.tag name="url" tld-body-content="JSP" tld-tag-class="com.opensymphony.webwork.views.jsp.URLTag"
102: * description="This tag is used to create a URL"
103: */
104: public class URL extends Component {
105: private static final Log LOG = LogFactory.getLog(URL.class);
106:
107: /**
108: * The includeParams attribute may have the value 'none', 'get' or 'all'.
109: * It is used when the url tag is used with or without a value attribute.
110: * Its value is looked up on the ValueStack
111: * If no includeParams is specified then 'get' is used.
112: * none - include no parameters in the URL
113: * get - include only GET parameters in the URL (default)
114: * all - include both GET and POST parameters in the URL
115: */
116: public static final String NONE = "none";
117: public static final String GET = "get";
118: public static final String ALL = "all";
119:
120: private HttpServletRequest req;
121: private HttpServletResponse res;
122:
123: protected String includeParams;
124: protected String scheme;
125: protected String value;
126: protected String action;
127: protected String namespace;
128: protected String method;
129: protected boolean encode = true;
130: protected boolean includeContext = true;
131: protected String portletMode;
132: protected String windowState;
133: protected String portletUrlType;
134: protected String anchor;
135: protected boolean escapeAmp = true;
136:
137: public URL(OgnlValueStack stack, HttpServletRequest req,
138: HttpServletResponse res) {
139: super (stack);
140: this .req = req;
141: this .res = res;
142: }
143:
144: public boolean start(Writer writer) {
145: boolean result = super .start(writer);
146:
147: if (value != null) {
148: value = findString(value);
149: }
150:
151: // no explicit url set so attach params from current url, do
152: // this at start so body params can override any of these they wish.
153: try {
154: // ww-1266
155: String includeParams = Configuration
156: .isSet(WebWorkConstants.WEBWORK_URL_INCLUDEPARAMS) ? Configuration
157: .getString(
158: WebWorkConstants.WEBWORK_URL_INCLUDEPARAMS)
159: .toLowerCase()
160: : GET;
161:
162: if (this .includeParams != null) {
163: includeParams = findString(this .includeParams);
164: }
165:
166: if (NONE.equalsIgnoreCase(includeParams)) {
167: mergeRequestParameters(value, parameters,
168: Collections.EMPTY_MAP);
169: ActionContext.getContext().put(
170: XWorkContinuationConfig.CONTINUE_KEY, null);
171: } else if (ALL.equalsIgnoreCase(includeParams)) {
172: mergeRequestParameters(value, parameters, req
173: .getParameterMap());
174:
175: // for ALL also include GET parameters
176: includeGetParameters();
177: } else if (GET.equalsIgnoreCase(includeParams)
178: || (includeParams == null && value == null && action == null)) {
179: includeGetParameters();
180: } else if (includeParams != null) {
181: LOG
182: .warn("Unknown value for includeParams parameter to URL tag: "
183: + includeParams);
184: }
185: } catch (Exception e) {
186: LOG
187: .warn("Unable to put request parameters ("
188: + extractQueryString()
189: + ") into parameter map.", e);
190: }
191:
192: return result;
193: }
194:
195: private void includeGetParameters() {
196: if (!(DispatcherUtils.isPortletSupportActive() && PortletActionContext
197: .isPortletRequest())) {
198: String query = extractQueryString();
199: mergeRequestParameters(value, parameters, UrlHelper
200: .parseQueryString(query));
201: }
202: }
203:
204: private String extractQueryString() {
205: // Parse the query string to make sure that the parameters come from the query, and not some posted data
206: String query = req.getQueryString();
207: if (query == null) {
208: // WW-1400 (Websphere will return null)
209: query = (String) req
210: .getAttribute("javax.servlet.forward.query_string");
211: }
212:
213: if (query != null) {
214: // Remove possible #foobar suffix
215: int idx = query.lastIndexOf('#');
216:
217: if (idx != -1) {
218: query = query.substring(0, idx);
219: }
220: }
221: return query;
222: }
223:
224: public boolean end(Writer writer, String body) {
225: String scheme = req.getScheme();
226:
227: if (this .scheme != null) {
228: scheme = this .scheme;
229: }
230:
231: String result;
232: if (value == null && action != null) {
233: if (DispatcherUtils.isPortletSupportActive()
234: && PortletActionContext.isPortletRequest()) {
235: result = PortletUrlHelper.buildUrl(action, namespace,
236: parameters, portletUrlType, portletMode,
237: windowState);
238: } else {
239: result = determineActionURL(action, namespace, method,
240: req, res, parameters, scheme, includeContext,
241: encode, escapeAmp);
242: }
243: } else {
244: if (DispatcherUtils.isPortletSupportActive()
245: && PortletActionContext.isPortletRequest()) {
246: result = PortletUrlHelper.buildResourceUrl(value,
247: parameters);
248: } else {
249: String _value = value;
250:
251: // We don't include the request parameters cause they would have been
252: // prioritised before this [in start(Writer) method]
253: if (_value != null && _value.indexOf("?") > 0) {
254: _value = _value.substring(0, _value.indexOf("?"));
255: }
256: result = UrlHelper.buildUrl(_value, req, res,
257: parameters, scheme, includeContext, encode,
258: false, escapeAmp);
259: }
260: }
261: if (anchor != null && anchor.length() > 0) {
262: result += '#' + anchor;
263: }
264:
265: String id = getId();
266:
267: if (id != null) {
268: getStack().getContext().put(id, result);
269:
270: // add to the request and page scopes as well
271: req.setAttribute(id, result);
272: } else {
273: try {
274: writer.write(result);
275: } catch (IOException e) {
276: throw new WebWorkException(
277: "IOError: " + e.getMessage(), e);
278: }
279: }
280: return super .end(writer, body);
281: }
282:
283: /**
284: * The includeParams attribute may have the value 'none', 'get' or 'all'.
285: * @ww.tagattribute required="false" default="get"
286: */
287: public void setIncludeParams(String includeParams) {
288: this .includeParams = includeParams;
289: }
290:
291: /**
292: * Set scheme attribute
293: * @ww.tagattribute required="false"
294: */
295: public void setScheme(String scheme) {
296: this .scheme = scheme;
297: }
298:
299: /**
300: * The target value to use, if not using action
301: * @ww.tagattribute required="false"
302: */
303: public void setValue(String value) {
304: this .value = value;
305: }
306:
307: /**
308: * The action generate url for, if not using value
309: * @ww.tagattribute required="false"
310: */
311: public void setAction(String action) {
312: this .action = action;
313: }
314:
315: /**
316: * The namespace to use
317: * @ww.tagattribute required="false"
318: */
319: public void setNamespace(String namespace) {
320: this .namespace = namespace;
321: }
322:
323: /**
324: * The method of action to use
325: * @ww.tagattribute required="false"
326: */
327: public void setMethod(String method) {
328: this .method = method;
329: }
330:
331: /**
332: * whether to encode parameters
333: * @ww.tagattribute required="false" type="Boolean" default="true"
334: */
335: public void setEncode(boolean encode) {
336: this .encode = encode;
337: }
338:
339: /**
340: * whether actual context should be included in url
341: * @ww.tagattribute required="false" type="Boolean" default="true"
342: */
343: public void setIncludeContext(boolean includeContext) {
344: this .includeContext = includeContext;
345: }
346:
347: /**
348: * The resulting portlet mode
349: * @ww.tagattribute required="false"
350: */
351: public void setPortletMode(String portletMode) {
352: this .portletMode = portletMode;
353: }
354:
355: /**
356: * The resulting portlet window state
357: * @ww.tagattribute required="false"
358: */
359: public void setWindowState(String windowState) {
360: this .windowState = windowState;
361: }
362:
363: /**
364: * Specifies if this should be a portlet render or action url
365: * @ww.tagattribute required="false"
366: */
367: public void setPortletUrlType(String portletUrlType) {
368: this .portletUrlType = portletUrlType;
369: }
370:
371: /**
372: * The anchor for this URL
373: * @ww.tagattribute required="false"
374: */
375: public void setAnchor(String anchor) {
376: this .anchor = anchor;
377: }
378:
379: /**
380: * Whether to escape ampersand (&) to (&) or not, default to true.
381: * @ww.tagattribute required="false"
382: */
383: public void setEscapeAmp(boolean escapeAmp) {
384: this .escapeAmp = escapeAmp;
385: }
386:
387: /**
388: * Merge request parameters into current parameters. If a parameter is
389: * already present, than the request parameter in the current request and value atrribute
390: * will not override its value.
391: *
392: * The priority is as follows:-
393: * <ul>
394: * <li>parameter from the current request (least priority)</li>
395: * <li>parameter form the value attribute (more priority)</li>
396: * <li>parameter from the param tag (most priority)</li>
397: * </ul>
398: *
399: * @param value the value attribute (url to be generated by this component)
400: * @param parameters component parameters
401: * @param contextParameters request parameters
402: */
403: protected void mergeRequestParameters(String value, Map parameters,
404: Map contextParameters) {
405:
406: Map mergedParams = new LinkedHashMap(contextParameters);
407:
408: // Merge contextParameters (from current request) with parameters specified in value attribute
409: // eg. value="someAction.action?id=someId&venue=someVenue"
410: // where the parameters specified in value attribute takes priority.
411:
412: if (value != null && value.trim().length() > 0
413: && value.indexOf("?") > 0) {
414: mergedParams = new LinkedHashMap();
415:
416: String queryString = value
417: .substring(value.indexOf("?") + 1);
418:
419: mergedParams = UrlHelper.parseQueryString(queryString);
420: for (Iterator iterator = contextParameters.entrySet()
421: .iterator(); iterator.hasNext();) {
422: Map.Entry entry = (Map.Entry) iterator.next();
423: Object key = entry.getKey();
424:
425: if (!mergedParams.containsKey(key)) {
426: mergedParams.put(key, entry.getValue());
427: }
428: }
429: }
430:
431: // Merge parameters specified in value attribute
432: // eg. value="someAction.action?id=someId&venue=someVenue"
433: // with parameters specified though param tag
434: // eg. <param name="id" value="%{'someId'}" />
435: // where parameters specified through param tag takes priority.
436:
437: for (Iterator iterator = mergedParams.entrySet().iterator(); iterator
438: .hasNext();) {
439: Map.Entry entry = (Map.Entry) iterator.next();
440: Object key = entry.getKey();
441:
442: if (!parameters.containsKey(key)) {
443: parameters.put(key, entry.getValue());
444: }
445: }
446: }
447: }
|