Coverage Report - org.apache.shiro.web.util.RedirectView
 
Classes in this File Line Coverage Branch Coverage Complexity
RedirectView
51%
31/60
35%
7/20
1.643
 
 1  
 /*
 2  
  * Licensed to the Apache Software Foundation (ASF) under one
 3  
  * or more contributor license agreements.  See the NOTICE file
 4  
  * distributed with this work for additional information
 5  
  * regarding copyright ownership.  The ASF licenses this file
 6  
  * to you under the Apache License, Version 2.0 (the
 7  
  * "License"); you may not use this file except in compliance
 8  
  * with the License.  You may obtain a copy of the License at
 9  
  *
 10  
  *     http://www.apache.org/licenses/LICENSE-2.0
 11  
  *
 12  
  * Unless required by applicable law or agreed to in writing,
 13  
  * software distributed under the License is distributed on an
 14  
  * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
 15  
  * KIND, either express or implied.  See the License for the
 16  
  * specific language governing permissions and limitations
 17  
  * under the License.
 18  
  */
 19  
 package org.apache.shiro.web.util;
 20  
 
 21  
 import javax.servlet.http.HttpServletRequest;
 22  
 import javax.servlet.http.HttpServletResponse;
 23  
 import java.io.IOException;
 24  
 import java.io.UnsupportedEncodingException;
 25  
 import java.net.URLEncoder;
 26  
 import java.util.Map;
 27  
 
 28  
 /**
 29  
  * View that redirects to an absolute, context relative, or current request
 30  
  * relative URL, exposing all model attributes as HTTP query parameters.
 31  
  * <p/>
 32  
  * A URL for this view is supposed to be a HTTP redirect URL, i.e.
 33  
  * suitable for HttpServletResponse's <code>sendRedirect</code> method, which
 34  
  * is what actually does the redirect if the HTTP 1.0 flag is on, or via sending
 35  
  * back an HTTP 303 code - if the HTTP 1.0 compatibility flag is off.
 36  
  * <p/>
 37  
  * Note that while the default value for the "contextRelative" flag is off,
 38  
  * you will probably want to almost always set it to true. With the flag off,
 39  
  * URLs starting with "/" are considered relative to the web server root, while
 40  
  * with the flag on, they are considered relative to the web application root.
 41  
  * Since most web apps will never know or care what their context path actually
 42  
  * is, they are much better off setting this flag to true, and submitting paths
 43  
  * which are to be considered relative to the web application root.
 44  
  * <p/>
 45  
  * Note that in a Servlet 2.2 environment, i.e. a servlet container which
 46  
  * is only compliant to the limits of this spec, this class will probably fail
 47  
  * when feeding in URLs which are not fully absolute, or relative to the current
 48  
  * request (no leading "/"), as these are the only two types of URL that
 49  
  * <code>sendRedirect</code> supports in a Servlet 2.2 environment.
 50  
  * <p/>
 51  
  * <em>This class was borrowed from a nearly identical version found in
 52  
  * the <a href="http://www.springframework.org/">Spring Framework</a>, with minor modifications to
 53  
  * avoid a dependency on Spring itself for a very small amount of code - we couldn't have done it better, and
 54  
  * don't want to repeat all of their great effort ;).
 55  
  * The original author names and copyright (Apache 2.0) has been left in place.  A special
 56  
  * thanks to Rod Johnson, Juergen Hoeller, and Colin Sampaleanu for making this available.</em>
 57  
  *
 58  
  * @see #setContextRelative
 59  
  * @see #setHttp10Compatible
 60  
  * @see javax.servlet.http.HttpServletResponse#sendRedirect
 61  
  * @since 0.2
 62  
  */
 63  
 public class RedirectView {
 64  
 
 65  
     //TODO - complete JavaDoc
 66  
 
 67  
     /**
 68  
      * The default encoding scheme: UTF-8
 69  
      */
 70  
     public static final String DEFAULT_ENCODING_SCHEME = "UTF-8";
 71  
 
 72  
     private String url;
 73  
 
 74  6
     private boolean contextRelative = false;
 75  
 
 76  6
     private boolean http10Compatible = true;
 77  
 
 78  6
     private String encodingScheme = DEFAULT_ENCODING_SCHEME;
 79  
 
 80  
     /**
 81  
      * Constructor for use as a bean.
 82  
      */
 83  
     @SuppressWarnings({"UnusedDeclaration"})
 84  0
     public RedirectView() {
 85  0
     }
 86  
 
 87  
     /**
 88  
      * Create a new RedirectView with the given URL.
 89  
      * <p>The given URL will be considered as relative to the web server,
 90  
      * not as relative to the current ServletContext.
 91  
      *
 92  
      * @param url the URL to redirect to
 93  
      * @see #RedirectView(String, boolean)
 94  
      */
 95  6
     public RedirectView(String url) {
 96  6
         setUrl(url);
 97  6
     }
 98  
 
 99  
     /**
 100  
      * Create a new RedirectView with the given URL.
 101  
      *
 102  
      * @param url             the URL to redirect to
 103  
      * @param contextRelative whether to interpret the given URL as
 104  
      *                        relative to the current ServletContext
 105  
      */
 106  
     public RedirectView(String url, boolean contextRelative) {
 107  0
         this(url);
 108  0
         this.contextRelative = contextRelative;
 109  0
     }
 110  
 
 111  
     /**
 112  
      * Create a new RedirectView with the given URL.
 113  
      *
 114  
      * @param url              the URL to redirect to
 115  
      * @param contextRelative  whether to interpret the given URL as
 116  
      *                         relative to the current ServletContext
 117  
      * @param http10Compatible whether to stay compatible with HTTP 1.0 clients
 118  
      */
 119  
     public RedirectView(String url, boolean contextRelative, boolean http10Compatible) {
 120  6
         this(url);
 121  6
         this.contextRelative = contextRelative;
 122  6
         this.http10Compatible = http10Compatible;
 123  6
     }
 124  
 
 125  
 
 126  
     public String getUrl() {
 127  18
         return url;
 128  
     }
 129  
 
 130  
     public void setUrl(String url) {
 131  6
         this.url = url;
 132  6
     }
 133  
 
 134  
     /**
 135  
      * Set whether to interpret a given URL that starts with a slash ("/")
 136  
      * as relative to the current ServletContext, i.e. as relative to the
 137  
      * web application root.
 138  
      * <p/>
 139  
      * Default is "false": A URL that starts with a slash will be interpreted
 140  
      * as absolute, i.e. taken as-is. If true, the context path will be
 141  
      * prepended to the URL in such a case.
 142  
      *
 143  
      * @param contextRelative whether to interpret a given URL that starts with a slash ("/")
 144  
      *                        as relative to the current ServletContext, i.e. as relative to the
 145  
      *                        web application root.
 146  
      * @see javax.servlet.http.HttpServletRequest#getContextPath
 147  
      */
 148  
     public void setContextRelative(boolean contextRelative) {
 149  0
         this.contextRelative = contextRelative;
 150  0
     }
 151  
 
 152  
     /**
 153  
      * Set whether to stay compatible with HTTP 1.0 clients.
 154  
      * <p>In the default implementation, this will enforce HTTP status code 302
 155  
      * in any case, i.e. delegate to <code>HttpServletResponse.sendRedirect</code>.
 156  
      * Turning this off will send HTTP status code 303, which is the correct
 157  
      * code for HTTP 1.1 clients, but not understood by HTTP 1.0 clients.
 158  
      * <p>Many HTTP 1.1 clients treat 302 just like 303, not making any
 159  
      * difference. However, some clients depend on 303 when redirecting
 160  
      * after a POST request; turn this flag off in such a scenario.
 161  
      *
 162  
      * @param http10Compatible whether to stay compatible with HTTP 1.0 clients.
 163  
      * @see javax.servlet.http.HttpServletResponse#sendRedirect
 164  
      */
 165  
     public void setHttp10Compatible(boolean http10Compatible) {
 166  0
         this.http10Compatible = http10Compatible;
 167  0
     }
 168  
 
 169  
     /**
 170  
      * Set the encoding scheme for this view. Default is UTF-8.
 171  
      *
 172  
      * @param encodingScheme the encoding scheme for this view. Default is UTF-8.
 173  
      */
 174  
     @SuppressWarnings({"UnusedDeclaration"})
 175  
     public void setEncodingScheme(String encodingScheme) {
 176  0
         this.encodingScheme = encodingScheme;
 177  0
     }
 178  
 
 179  
 
 180  
     /**
 181  
      * Convert model to request parameters and redirect to the given URL.
 182  
      *
 183  
      * @param model    the model to convert
 184  
      * @param request  the incoming HttpServletRequest
 185  
      * @param response the outgoing HttpServletResponse
 186  
      * @throws java.io.IOException if there is a problem issuing the redirect
 187  
      * @see #appendQueryProperties
 188  
      * @see #sendRedirect
 189  
      */
 190  
     public final void renderMergedOutputModel(
 191  
             Map model, HttpServletRequest request, HttpServletResponse response) throws IOException {
 192  
 
 193  
         // Prepare name URL.
 194  6
         StringBuilder targetUrl = new StringBuilder();
 195  6
         if (this.contextRelative && getUrl().startsWith("/")) {
 196  
             // Do not apply context path to relative URLs.
 197  0
             targetUrl.append(request.getContextPath());
 198  
         }
 199  6
         targetUrl.append(getUrl());
 200  
         //change the following method to accept a StringBuilder instead of a StringBuilder for Shiro 2.x:
 201  6
         appendQueryProperties(targetUrl, model, this.encodingScheme);
 202  
 
 203  6
         sendRedirect(request, response, targetUrl.toString(), this.http10Compatible);
 204  6
     }
 205  
 
 206  
     /**
 207  
      * Append query properties to the redirect URL.
 208  
      * Stringifies, URL-encodes and formats model attributes as query properties.
 209  
      *
 210  
      * @param targetUrl      the StringBuffer to append the properties to
 211  
      * @param model          Map that contains model attributes
 212  
      * @param encodingScheme the encoding scheme to use
 213  
      * @throws java.io.UnsupportedEncodingException if string encoding failed
 214  
      * @see #urlEncode
 215  
      * @see #queryProperties
 216  
      * @see #urlEncode(String, String)
 217  
      */
 218  
     protected void appendQueryProperties(StringBuilder targetUrl, Map model, String encodingScheme)
 219  
             throws UnsupportedEncodingException {
 220  
 
 221  
         // Extract anchor fragment, if any.
 222  
         // The following code does not use JDK 1.4's StringBuffer.indexOf(String)
 223  
         // method to retain JDK 1.3 compatibility.
 224  6
         String fragment = null;
 225  6
         int anchorIndex = targetUrl.toString().indexOf('#');
 226  6
         if (anchorIndex > -1) {
 227  0
             fragment = targetUrl.substring(anchorIndex);
 228  0
             targetUrl.delete(anchorIndex, targetUrl.length());
 229  
         }
 230  
 
 231  
         // If there aren't already some parameters, we need a "?".
 232  6
         boolean first = (getUrl().indexOf('?') < 0);
 233  6
         Map queryProps = queryProperties(model);
 234  
 
 235  6
         if (queryProps != null) {
 236  0
             for (Object o : queryProps.entrySet()) {
 237  0
                 if (first) {
 238  0
                     targetUrl.append('?');
 239  0
                     first = false;
 240  
                 } else {
 241  0
                     targetUrl.append('&');
 242  
                 }
 243  0
                 Map.Entry entry = (Map.Entry) o;
 244  0
                 String encodedKey = urlEncode(entry.getKey().toString(), encodingScheme);
 245  0
                 String encodedValue =
 246  0
                         (entry.getValue() != null ? urlEncode(entry.getValue().toString(), encodingScheme) : "");
 247  0
                 targetUrl.append(encodedKey).append('=').append(encodedValue);
 248  0
             }
 249  
         }
 250  
 
 251  
         // Append anchor fragment, if any, to end of URL.
 252  6
         if (fragment != null) {
 253  0
             targetUrl.append(fragment);
 254  
         }
 255  6
     }
 256  
 
 257  
     /**
 258  
      * URL-encode the given input String with the given encoding scheme, using
 259  
      * {@link URLEncoder#encode(String, String) URLEncoder.encode(input, enc)}.
 260  
      *
 261  
      * @param input          the unencoded input String
 262  
      * @param encodingScheme the encoding scheme
 263  
      * @return the encoded output String
 264  
      * @throws UnsupportedEncodingException if thrown by the JDK URLEncoder
 265  
      * @see java.net.URLEncoder#encode(String, String)
 266  
      * @see java.net.URLEncoder#encode(String)
 267  
      */
 268  
     protected String urlEncode(String input, String encodingScheme) throws UnsupportedEncodingException {
 269  0
         return URLEncoder.encode(input, encodingScheme);
 270  
     }
 271  
 
 272  
     /**
 273  
      * Determine name-value pairs for query strings, which will be stringified,
 274  
      * URL-encoded and formatted by appendQueryProperties.
 275  
      * <p/>
 276  
      * This implementation returns all model elements as-is.
 277  
      *
 278  
      * @param model the model elements for which to determine name-value pairs.
 279  
      * @return the name-value pairs for query strings.
 280  
      * @see #appendQueryProperties
 281  
      */
 282  
     protected Map queryProperties(Map model) {
 283  6
         return model;
 284  
     }
 285  
 
 286  
     /**
 287  
      * Send a redirect back to the HTTP client
 288  
      *
 289  
      * @param request          current HTTP request (allows for reacting to request method)
 290  
      * @param response         current HTTP response (for sending response headers)
 291  
      * @param targetUrl        the name URL to redirect to
 292  
      * @param http10Compatible whether to stay compatible with HTTP 1.0 clients
 293  
      * @throws IOException if thrown by response methods
 294  
      */
 295  
     @SuppressWarnings({"UnusedDeclaration"})
 296  
     protected void sendRedirect(HttpServletRequest request, HttpServletResponse response,
 297  
                                 String targetUrl, boolean http10Compatible) throws IOException {
 298  6
         if (http10Compatible) {
 299  
             // Always send status code 302.
 300  6
             response.sendRedirect(response.encodeRedirectURL(targetUrl));
 301  
         } else {
 302  
             // Correct HTTP status code is 303, in particular for POST requests.
 303  0
             response.setStatus(303);
 304  0
             response.setHeader("Location", response.encodeRedirectURL(targetUrl));
 305  
         }
 306  6
     }
 307  
 
 308  
 }