craigmcc 01/02/09 11:33:11 Modified: src/doc struts-html.xml struts-logic.xml src/share/org/apache/struts/taglib/logic LocalStrings.properties RedirectTag.java Log: Enhance the <logic:redirect> tag so that it has the same three options for choosing the destination URL that the <html:link> tag has: - "href" attribute, used unchanged - "forward" attribute, picks context-relative path by logical name - "page" attribute, specifies context-relative path starting with "/" Revision Changes Path 1.7 +3 -2 jakarta-struts/src/doc/struts-html.xml Index: struts-html.xml =================================================================== RCS file: /home/cvs/jakarta-struts/src/doc/struts-html.xml,v retrieving revision 1.6 retrieving revision 1.7 diff -u -r1.6 -r1.7 --- struts-html.xml 2001/02/07 23:10:42 1.6 +++ struts-html.xml 2001/02/09 19:33:10 1.7 @@ -1409,8 +1409,8 @@ image will be taken from the attributes of this tag.</p> <p>The base URL for this image is calculated based on - the value specified in <code>src</code> or <code>page</code>. - You <strong>must</strong> specify exactly one of these + the value specified in <code>src</code> or <code>page</code>. + You <strong>must</strong> specify exactly one of these attributes.</p> <p>Normally, the <code>src</code> or <code>page</code> that @@ -2956,6 +2956,7 @@ </tag> + <tag> <name>reset</name> 1.6 +216 -22 jakarta-struts/src/doc/struts-logic.xml Index: struts-logic.xml =================================================================== RCS file: /home/cvs/jakarta-struts/src/doc/struts-logic.xml,v retrieving revision 1.5 retrieving revision 1.6 diff -u -r1.5 -r1.6 --- struts-logic.xml 2000/12/29 20:36:54 1.5 +++ struts-logic.xml 2001/02/09 19:33:10 1.6 @@ -1189,30 +1189,224 @@ </tag> - <tag> + <tag> - <name>redirect</name> - <summary> - Perform an HTTP redirect to the specified URL. - </summary> - <tagclass>org.apache.struts.taglib.logic.RedirectTag</tagclass> - <bodycontent>empty</bodycontent> - <info> - <p>Performs a <code>HttpServletResponse.sendRedirect()</code> to the - specified URL, after applying URL rewriting as needed.</p> - </info> - - <attribute> - <name>href</name> - <required>true</required> - <rtexprvalue>true</rtexprvalue> - <info> - <p>The relative or absolute URL to which the client should be - redirected.</p> - </info> - </attribute> + <name>redirect</name> + <summary>Render an HTTP Redirect</summary> + <tagclass>org.apache.struts.taglib.logic.RedirectTag</tagclass> + <body-content>empty</body-content> + <info> - </tag> + <p>Performs an <code>HttpServletResponse.sendRedirect()</code> + call to the hyperlink specified by the attributes to this + tag. URL rewriting will be applied automatically, to + maintain session state in the absence of cookies.</p> + + <p>The base URL for this redirect is calculated based on + which of the following attributes you specify (you must + specify exactly one of them):</p> + <ul> + <li><em>forward</em> - Use the value of this attribute as the + name of a global <code>ActionForward</code> to be looked + up, and use the context-relative URI found there.</li> + <li><em>href</em> - Use the value of this attribute unchanged. + </li> + <li><em>page</em> - Use the value of this attribute as a + context-relative URI, and generate a server-relative URI + by including the context path.</li> + </ul> + + <p>Normally, the redirect you specify with one of the + attributes described in the previous paragraph will be left + unchanged (other than URL rewriting if necessary). However, + there are two ways you can append one or more dynamically + defined query parameters to the hyperlink -- specify a single + parameter with the <code>paramId</code> attribute (and its + associated attributes to select the value), or specify the + <code>name</code> (and optional <code>property</code>) + attributes to select a <code>java.util.Map</code> bean that + contains one or more parameter ids and corresponding values. + </p> + + <p>To specify a single parameter, use the <code>paramId</code> + attribute to define the name of the request parameter to be + submitted. To specify the corresponding value, use one of the + following approaches:</p> + <ul> + <li><em>Specify only the <code>paramName</code> attribute</em> + - The named JSP bean (optionally scoped by the value of the + <code>paramScope</code> attribute) must identify a value + that can be converted to a String.</li> + <li><em>Specify both the <code>paramName</code> and + <code>paramProperty</code> attributes</em> - The specified + property getter method will be called on the JSP bean + identified by the <code>paramName</code> (and optional + <code>paramScope</code>) attributes, in order to select + a value that can be converted to a String.</li> + </ul> + + <p>If you prefer to specify a <code>java.util.Map</code> that + contains all of the request parameters to be added to the + hyperlink, use one of the following techniques:</p> + <ul> + <li><em>Specify only the <code>name</code> attribute</em> - + The named JSP bean (optionally scoped by the value of + the <code>scope</code> attribute) must identify a + <code>java.util.Map</code> containing the parameters.</li> + <li><em>Specify both <code>name</code> and + <code>property</code> attributes</em> - The specified + property getter method will be called on the bean + identified by the <code>name</code> (and optional + <code>scope</code>) attributes, in order to return the + <code>java.util.Map</code> containing the parameters.</li> + </ul> + + <p>As the <code>Map</code> is processed, the keys are assumed + to be the names of query parameters to be appended to the + hyperlink. The value associated with each key must be either + a String or a String array representing the parameter value(s). + If a String array is specified, more than one value for the + same query parameter name will be created.</p> + </info> + + <attribute> + <name>forward</name> + <required>false</required> + <rtexprvalue>true</rtexprvalue> + <info> + <p>Logical name of a global <code>ActionForward</code> that + contains the actual content-relative URI of the destination + of this redirect. This URI may be dynamically + modified by the inclusion of query parameters, as described + in the tag description. You <strong>must</strong> specify + exactly one of the <code>forward</code> attribute, the + <code>href</code> attribute, the <code>linkName</code> + attribute, or the <code>page</code> attribute.</p> + </info> + </attribute> + + <attribute> + <name>href</name> + <required>false</required> + <rtexprvalue>true</rtexprvalue> + <info> + <p>The URL to which this redirect will transfer control. + This URL may be dynamically modified + by the inclusion of query parameters, as described in the + tag description. You <strong>must</strong> specify + exactly one of the <code>forward</code> attribute, the + <code>href</code> attribute, the <code>linkName</code> + attribute, or the <code>page</code> attribute.</p> + </info> + </attribute> + + <attribute> + <name>name</name> + <required>false</required> + <rtexprvalue>true</rtexprvalue> + <info> + <p>The name of a JSP bean that contains a <code>Map</code> + representing the query parameters (if <code>property</code> + is not specified), or a JSP bean whose property getter is + called to return a <code>Map</code> (if <code>property</code> + is specified).</p> + </info> + </attribute> + + <attribute> + <name>page</name> + <required>false</required> + <rtexprvalue>true</rtexprvalue> + <info> + <p>The context-relative path (beginning with a "/" + character) to which this hyperlink will transfer control + if activated. This hyperlink may be dynamically modified + by the inclusion of query parameters, as described in the + tag description. You <strong>must</strong> specify exactly + one of the <code>forward</code> attribute, the + <code>href</code> attribute, the <code>linkName</code> + attribute, or the <code>page</code> attribute.</p> + </info> + </attribute> + + <attribute> + <name>paramId</name> + <required>false</required> + <rtexprvalue>true</rtexprvalue> + <info> + <p>The name of the request parameter that will be dynamically + added to the generated hyperlink. The corresponding value is + defined by the <code>paramName</code> and (optional) + <code>paramProperty</code> attributes, optionally scoped by + the <code>paramScope</code> attributel</p> + </info> + </attribute> + + <attribute> + <name>paramName</name> + <required>false</required> + <rtexprvalue>true</rtexprvalue> + <info> + <p>The name of a JSP bean that is a String containing the + value for the request parameter named by <code>paramId</code> + (if <code>paramProperty</code> is not specified), or a JSP + bean whose property getter is called to return a String + (if <code>paramProperty</code> is specified). The JSP bean + is constrained to the bean scope specified by the + <code>paramScope</code> property, if it is specified.</p> + </info> + </attribute> + + <attribute> + <name>paramProperty</name> + <required>false</required> + <rtexprvalue>true</rtexprvalue> + <info> + <p>The name of a property of the bean specified by the + <code>paramName</code> attribute, whose return value must + be a String containing the value of the request parameter + (named by the <code>paramId</code> attribute) that will be + dynamically added to this hyperlink.</p> + </info> + </attribute> + + <attribute> + <name>paramScope</name> + <required>false</required> + <rtexprvalue>true</rtexprvalue> + <info> + <p>The scope within which to search for the bean specified + by the <code>paramName</code> attribute. If not specified, + all scopes are searched.</p> + </info> + </attribute> + + <attribute> + <name>property</name> + <required>false</required> + <rtexprvalue>true</rtexprvalue> + <info> + <p>The name of a property of the bean specified by the + <code>name</code> attribute, whose return value must be + a <code>java.util.Map</code> containing the query parameters + to be added to the hyperlink. You <strong>must</strong> + specify the <code>name</code> attribute if you specify + this attribute.</p> + </info> + </attribute> + + <attribute> + <name>scope</name> + <required>false</required> + <rtexprvalue>true</rtexprvalue> + <info> + <p>The scope within which to search for the bean specified + by the <code>name</code> attribute. If not specified, all + scopes are searched.</p> + </info> + </attribute> + + </tag> </taglib> 1.3 +1 -0 jakarta-struts/src/share/org/apache/struts/taglib/logic/LocalStrings.properties Index: LocalStrings.properties =================================================================== RCS file: /home/cvs/jakarta-struts/src/share/org/apache/struts/taglib/logic/LocalStrings.properties,v retrieving revision 1.2 retrieving revision 1.3 diff -u -r1.2 -r1.3 --- LocalStrings.properties 2001/01/16 01:08:18 1.2 +++ LocalStrings.properties 2001/02/09 19:33:11 1.3 @@ -13,4 +13,5 @@ logic.property=Exception accessing property {1} for bean {0}: {2} logic.selector=No selector attribute (cookie/header/name/parameter) was specified logic.variable=Cannot compare null variable to value {0} +redirect.destination=You must specify exactly one of 'forward', 'href', or 'page' redirect.redirect=Exception redirecting to {0}: {1} 1.5 +388 -29 jakarta-struts/src/share/org/apache/struts/taglib/logic/RedirectTag.java Index: RedirectTag.java =================================================================== RCS file: /home/cvs/jakarta-struts/src/share/org/apache/struts/taglib/logic/RedirectTag.java,v retrieving revision 1.4 retrieving revision 1.5 diff -u -r1.4 -r1.5 --- RedirectTag.java 2000/10/30 06:02:23 1.4 +++ RedirectTag.java 2001/02/09 19:33:11 1.5 @@ -1,7 +1,7 @@ /* - * $Header: /home/cvs/jakarta-struts/src/share/org/apache/struts/taglib/logic/RedirectTag.java,v 1.4 2000/10/30 06:02:23 craigmcc Exp $ - * $Revision: 1.4 $ - * $Date: 2000/10/30 06:02:23 $ + * $Header: /home/cvs/jakarta-struts/src/share/org/apache/struts/taglib/logic/RedirectTag.java,v 1.5 2001/02/09 19:33:11 craigmcc Exp $ + * $Revision: 1.5 $ + * $Date: 2001/02/09 19:33:11 $ * * ==================================================================== * @@ -64,22 +64,30 @@ import java.io.IOException; +import java.lang.reflect.InvocationTargetException; +import java.net.URLEncoder; +import java.util.Iterator; +import java.util.Map; +import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import javax.servlet.jsp.JspException; import javax.servlet.jsp.JspWriter; import javax.servlet.jsp.PageContext; import javax.servlet.jsp.tagext.TagSupport; import org.apache.struts.action.Action; +import org.apache.struts.action.ActionForward; +import org.apache.struts.action.ActionForwards; import org.apache.struts.util.BeanUtils; import org.apache.struts.util.MessageResources; +import org.apache.struts.util.PropertyUtils; +import org.apache.struts.util.RequestUtils; /** - * Perform a sendRedirect() to the specified URL, and skip evaluating - * the remainder of the current page. + * Generate a URL-encoded redirect to the specified URI. * * @author Craig R. McClanahan - * @version $Revision: 1.4 $ $Date: 2000/10/30 06:02:23 $ + * @version $Revision: 1.5 $ $Date: 2001/02/09 19:33:11 $ */ public class RedirectTag extends TagSupport { @@ -89,16 +97,30 @@ /** - * The relative or absolute URL to which the client should be redirected. + * The logical forward name from which to retrieve the redirect URI. */ + protected String forward = null; + + public String getForward() { + return (this.forward); + } + + public void setForward(String forward) { + this.forward = forward; + } + + + /** + * The redirect URI. + */ protected String href = null; public String getHref() { - return (this.href); + return (this.href); } public void setHref(String href) { - this.href = href; + this.href = href; } @@ -106,58 +128,395 @@ * The message resources for this package. */ protected static MessageResources messages = - MessageResources.getMessageResources - ("org.apache.struts.taglib.logic.LocalStrings"); + MessageResources.getMessageResources + ("org.apache.struts.taglib.logic.LocalStrings"); + + + /** + * The JSP bean name for query parameters. + */ + protected String name = null; + + public String getName() { + return (this.name); + } + + public void setName(String name) { + this.name = name; + } + + + /** + * The context-relative page URL (beginning with a slash) to which + * this redirect will be rendered. + */ + protected String page = null; + + public String getPage() { + return (this.page); + } + + public void setPage(String page) { + this.page = page; + } + + /** + * The single-parameter request parameter name to generate. + */ + protected String paramId = null; + + public String getParamId() { + return (this.paramId); + } + + public void setParamId(String paramId) { + this.paramId = paramId; + } + + /** + * The single-parameter JSP bean name. + */ + protected String paramName = null; + + public String getParamName() { + return (this.paramName); + } + + public void setParamName(String paramName) { + this.paramName = paramName; + } + + + /** + * The single-parameter JSP bean property. + */ + protected String paramProperty = null; + + public String getParamProperty() { + return (this.paramProperty); + } + + public void setParamProperty(String paramProperty) { + this.paramProperty = paramProperty; + } + + + /** + * The single-parameter JSP bean scope. + */ + protected String paramScope = null; + + public String getParamScope() { + return (this.paramScope); + } + + public void setParamScope(String paramScope) { + this.paramScope = paramScope; + } + + + /** + * The JSP bean property name for query parameters. + */ + protected String property = null; + + public String getProperty() { + return (this.property); + } + + public void setProperty(String property) { + this.property = property; + } + + + /** + * The scope of the bean specified by the name property, if any. + */ + protected String scope = null; + + public String getScope() { + return (this.scope); + } + + public void setScope(String scope) { + this.scope = scope; + } + + // --------------------------------------------------------- Public Methods /** - * Defer processing until the end of this tag is encountered. + * Defer generation until the end of this tag is encountered. * * @exception JspException if a JSP exception has occurred */ public int doStartTag() throws JspException { - return (SKIP_BODY); + return (SKIP_BODY); } /** - * Render a redirect to the specified hyperlink, and skip the - * remainder of the current page. + * Render the redirect and skip the remainder of this page. * * @exception JspException if a JSP exception has occurred */ public int doEndTag() throws JspException { - // Perform the requested redirect + // Perform the redirection HttpServletResponse response = (HttpServletResponse) pageContext.getResponse(); - try { - response.sendRedirect(response.encodeRedirectURL(href)); - } catch (IOException e) { - pageContext.setAttribute(Action.EXCEPTION_KEY, e, - PageContext.REQUEST_SCOPE); - throw new JspException - (messages.getMessage("redirect.redirect", href, e.toString())); - } + String hyperlink = BeanUtils.filter(hyperlink()); + try { + response.sendRedirect(response.encodeRedirectURL(hyperlink)); + } catch (IOException e) { + throw new JspException + (messages.getMessage("common.io")); + } - // Skip the remainder of the current page - return (SKIP_PAGE); + // Skip the remainder of this apge + return (SKIP_PAGE); } /** - * Release all allocated resources. + * Release any acquired resources. */ public void release() { + + super.release(); + forward = null; + href = null; + name = null; + page = null; + paramId = null; + paramName = null; + paramProperty = null; + paramScope = null; + property = null; + scope = null; + + } + + + // ------------------------------------------------------ Protected Methods + + + /** + * Return the specified hyperlink, modified as necessary with optional + * request parameters. Return <code>null</code> if we are generating + * a name anchor rather than a hyperlink. + * + * @exception JspException if an error occurs preparing the hyperlink + */ + protected String hyperlink() throws JspException { + + // Validate the number of href specifiers that were specified + int n = 0; + if (forward != null) + n++; + if (href != null) + n++; + if (page != null) + n++; + if (n != 1) { + JspException e = new JspException + (messages.getMessage("redirect.destination")); + pageContext.setAttribute(Action.EXCEPTION_KEY, e, + PageContext.REQUEST_SCOPE); + throw e; + } + + // Start with an unadorned "href" + String href = null; + + // If "href" was specified, use it as is + if (this.href != null) { + href = this.href; + } + + // If "forward" was specified, compute the "href" to forward to + else if (forward != null) { + ActionForwards forwards = (ActionForwards) + pageContext.getAttribute(Action.FORWARDS_KEY, + PageContext.APPLICATION_SCOPE); + if (forwards == null) + throw new JspException + (messages.getMessage("linkTag.forwards")); + ActionForward forward = forwards.findForward(this.forward); + if (forward == null) + throw new JspException + (messages.getMessage("linkTag.forward")); + HttpServletRequest request = + (HttpServletRequest) pageContext.getRequest(); + href = RequestUtils.absoluteURL(request, forward.getPath()); + } + + // If "page" was specified, compute the "href" to forward to + else if (page != null) { + HttpServletRequest request = + (HttpServletRequest) pageContext.getRequest(); + href = RequestUtils.absoluteURL(request, page); + } + + // Append a single-parameter name and value, if requested + if ((paramId != null) && (paramName != null)) { + if (href.indexOf('?') < 0) + href += '?'; + else + href += '&'; + href += paramId; + href += '='; + Object bean = RequestUtils.lookup(pageContext, + paramName, paramScope); + if (bean != null) { + if (paramProperty == null) + href += bean.toString(); + else { + try { + Object value = + PropertyUtils.getProperty(bean, paramProperty); + if (value != null) + href += value.toString(); + } catch (IllegalAccessException e) { + pageContext.setAttribute(Action.EXCEPTION_KEY, e, + PageContext.REQUEST_SCOPE); + throw new JspException + (messages.getMessage("getter.access", + paramProperty, paramName)); + } catch (InvocationTargetException e) { + Throwable t = e.getTargetException(); + pageContext.setAttribute(Action.EXCEPTION_KEY, t, + PageContext.REQUEST_SCOPE); + throw new JspException + (messages.getMessage("getter.result", + paramProperty, t.toString())); + } catch (NoSuchMethodException e) { + pageContext.setAttribute(Action.EXCEPTION_KEY, e, + PageContext.REQUEST_SCOPE); + throw new JspException + (messages.getMessage("getter.method", + paramProperty, paramName)); + } + } + } + } + + // Just return the "href" attribute if there is no bean to look up + if ((property != null) && (name == null)) { + JspException e = new JspException + (messages.getMessage("getter.name")); + pageContext.setAttribute(Action.EXCEPTION_KEY, e, + PageContext.REQUEST_SCOPE); + throw e; + } + if (name == null) + return (href); + + // Look up the map we will be using + Object bean = RequestUtils.lookup(pageContext, name, scope); + if (bean == null) { + JspException e = new JspException + (messages.getMessage("getter.bean", name)); + pageContext.setAttribute(Action.EXCEPTION_KEY, e, + PageContext.REQUEST_SCOPE); + throw e; + } + Map map = null; + if (property == null) { + try { + map = (Map) bean; + } catch (ClassCastException e) { + pageContext.setAttribute(Action.EXCEPTION_KEY, e, + PageContext.REQUEST_SCOPE); + throw new JspException + (messages.getMessage("linkTag.type")); + } + } else { + try { + map = (Map) PropertyUtils.getProperty(bean, property); + if (map == null) { + JspException e = new JspException + (messages.getMessage("getter.property", property)); + pageContext.setAttribute(Action.EXCEPTION_KEY, e, + PageContext.REQUEST_SCOPE); + throw e; + } + } catch (IllegalAccessException e) { + pageContext.setAttribute(Action.EXCEPTION_KEY, e, + PageContext.REQUEST_SCOPE); + throw new JspException + (messages.getMessage("getter.access", property, name)); + } catch (InvocationTargetException e) { + Throwable t = e.getTargetException(); + pageContext.setAttribute(Action.EXCEPTION_KEY, t, + PageContext.REQUEST_SCOPE); + throw new JspException + (messages.getMessage("getter.result", + property, t.toString())); + } catch (ClassCastException e) { + pageContext.setAttribute(Action.EXCEPTION_KEY, e, + PageContext.REQUEST_SCOPE); + throw new JspException + (messages.getMessage("linkTag.type")); + } catch (NoSuchMethodException e) { + pageContext.setAttribute(Action.EXCEPTION_KEY, e, + PageContext.REQUEST_SCOPE); + throw new JspException + (messages.getMessage("getter.method", property, name)); + } + } + + // Append the required query parameters + StringBuffer sb = new StringBuffer(href); + boolean question = (href.indexOf("?") >= 0); + Iterator keys = map.keySet().iterator(); + while (keys.hasNext()) { + String key = (String) keys.next(); + Object value = map.get(key); + if (value == null) { + if (question) + sb.append('&'); + else { + sb.append('?'); + question = true; + } + sb.append(key); + sb.append('='); + // Interpret null as "no value specified" + } else if (value instanceof String[]) { + String values[] = (String[]) value; + for (int i = 0; i < values.length; i++) { + if (question) + sb.append('&'); + else { + sb.append('?'); + question = true; + } + sb.append(key); + sb.append('='); + sb.append(URLEncoder.encode(values[i])); + } + } else { + if (question) + sb.append('&'); + else { + sb.append('?'); + question = true; + } + sb.append(key); + sb.append('='); + sb.append(URLEncoder.encode(value.toString())); + } + } - super.release(); - href = null; + // Return the final result + return (sb.toString()); }