2011/08/05 - Jakarta Cactus has been retired.

For more information, please explore the Attic.

View Javadoc

1   /* 
2    * ========================================================================
3    * 
4    * Licensed to the Apache Software Foundation (ASF) under one or more
5    * contributor license agreements.  See the NOTICE file distributed with
6    * this work for additional information regarding copyright ownership.
7    * The ASF licenses this file to You under the Apache License, Version 2.0
8    * (the "License"); you may not use this file except in compliance with
9    * the License.  You may obtain a copy of the License at
10   * 
11   *   http://www.apache.org/licenses/LICENSE-2.0
12   * 
13   * Unless required by applicable law or agreed to in writing, software
14   * distributed under the License is distributed on an "AS IS" BASIS,
15   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16   * See the License for the specific language governing permissions and
17   * limitations under the License.
18   * 
19   * ========================================================================
20   */
21  package org.apache.cactus;
22  
23  import javax.servlet.http.HttpServletRequest;
24  
25  import org.apache.cactus.internal.server.ServletUtil;
26  import org.apache.commons.logging.Log;
27  import org.apache.commons.logging.LogFactory;
28  
29  /**
30   * Simulate an HTTP URL by breaking it into its different parts.
31   * <br><code><pre><b>
32   * URL = "http://" + serverName (including port) + requestURI ? queryString<br>
33   * requestURI = contextPath + servletPath + pathInfo
34   * </b></pre></code>
35   * From the Servlet 2.2 specification :<br>
36   * <code><pre><ul><li><b>Context Path</b>: The path prefix associated with the
37   *   ServletContext that this servlet is a part of. If this context is the
38   *   default context rooted at the base of the web server's URL namespace, this
39   *   path will be an empty string. Otherwise, this path starts with a "/"
40   *   character but does not end with a "/" character.</li>
41   * <li><b>Servlet Path</b>: The path section that directly corresponds to the
42   *   mapping which activated this request. This path starts with a "/"
43   *   character.</li>
44   * <li><b>PathInfo</b>: The part of the request path that is not part of the
45   *   Context Path or the Servlet Path.</li></ul></pre></code>
46   * From the Servlet 2.3 specification :<br>
47   * <code><pre><ul><li><b>Context Path</b>: The path prefix associated with the
48   *   ServletContext that this servlet is a part of. If this context is the
49   *   default context rooted at the base of the web server's URL namespace, this
50   *   path will be an empty string. Otherwise, this path starts with a "/"
51   *   character but does not end with a "/" character.</li>
52   * <li><b>Servlet Path</b>: The path section that directly corresponds to the
53   *   mapping which activated this request. This path starts with a "/"
54   *   character <b>except in the case where the request is matched with the 
55   *   "/*" pattern, in which case it is the empty string</b>.</li>
56   * <li><b>PathInfo</b>: The part of the request path that is not part of the
57   *   Context Path or the Servlet Path. <b>It is either null if there is no 
58   *   extra path, or is a string with a leading "/"</b>.</li></ul></pre></code>
59   *
60   * @version $Id: ServletURL.java 238991 2004-05-22 11:34:50Z vmassol $
61   */
62  public class ServletURL
63  {
64      /**
65       * Name of the parameter in the HTTP request that represents the protocol
66       * (HTTP, HTTPS, etc) in the URL to simulate. The name is voluntarily long
67       * so that it will not clash with a user-defined parameter.
68       */
69      public static final String URL_PROTOCOL_PARAM = "Cactus_URL_Protocol";
70  
71      /**
72       * Name of the parameter in the HTTP request that represents the Server
73       * name (+ port) in the URL to simulate. The name is voluntarily long so
74       * that it will not clash with a user-defined parameter.
75       */
76      public static final String URL_SERVER_NAME_PARAM = "Cactus_URL_Server";
77  
78      /**
79       * Name of the parameter in the HTTP request that represents the context
80       * path in the URL to simulate. The name is voluntarily long so that it
81       * will not clash with a user-defined parameter.
82       */
83      public static final String URL_CONTEXT_PATH_PARAM = 
84          "Cactus_URL_ContextPath";
85  
86      /**
87       * Name of the parameter in the HTTP request that represents the Servlet
88       * Path in the URL to simulate. The name is voluntarily long so that it
89       * will not clash with a user-defined parameter.
90       */
91      public static final String URL_SERVLET_PATH_PARAM = 
92          "Cactus_URL_ServletPath";
93  
94      /**
95       * Name of the parameter in the HTTP request that represents the Path Info
96       * in the URL to simulate. The name is voluntarily long so that it will not
97       * clash with a user-defined parameter.
98       */
99      public static final String URL_PATH_INFO_PARAM = "Cactus_URL_PathInfo";
100 
101     /**
102      * Name of the parameter in the HTTP request that represents the Query
103      * String in the URL to simulate. The name is voluntarily long so that it
104      * will not clash with a user-defined parameter.
105      */
106     public static final String URL_QUERY_STRING_PARAM = 
107         "Cactus_URL_QueryString";
108 
109     /**
110      * Http protocol.
111      */
112     public static final String PROTOCOL_HTTP = "http";
113 
114     /**
115      * Https protocol.
116      */
117     public static final String PROTOCOL_HTTPS = "https";
118 
119     /**
120      * Default port of the HTTP protocol.
121      */
122     private static final int DEFAULT_PORT_HTTP = 80;
123 
124     /**
125      * Default port of HTTP over SSL.
126      */
127     private static final int DEFAULT_PORT_HTTPS = 443;
128 
129     /**
130      * The logger.
131      */
132     private static final Log LOGGER = LogFactory.getLog(ServletURL.class);
133 
134     /**
135      * The server name to simulate (including port number).
136      */
137     private String serverName;
138 
139     /**
140      * The context path to simulate.
141      */
142     private String contextPath;
143 
144     /**
145      * The servlet path to simulate.
146      */
147     private String servletPath;
148 
149     /**
150      * The Path Info to simulate.
151      */
152     private String pathInfo;
153 
154     /**
155      * The Query string.
156      */
157     private String queryString;
158 
159     /**
160      * The protocol to use. Default to HTTP.
161      */
162     private String protocol = PROTOCOL_HTTP;
163 
164     /**
165      * Default constructor. Need to call the different setters to make this
166      * a valid object.
167      */
168     public ServletURL()
169     {
170     }
171 
172     /**
173      * Creates the URL to simulate.
174      *
175      * @param theProtocol   the protocol to simulate (either
176      *                      <code>ServletURL.PROTOCOL_HTTP</code> or
177      *                      <code>ServletURL.PROTOCOL_HTTPS</code>.
178      * @param theServerName the server name (and port) in the URL to simulate,
179      *                      i.e. this is the name that will be returned by the
180      *                      <code>HttpServletRequest.getServerName()</code> and
181      *                      <code>HttpServletRequest.getServerPort()</code>. Can
182      *                      be null. If null, then the server name and port from
183      *                      the Servlet Redirector will be returned.
184      * @param theContextPath the webapp context path in the URL to simulate,
185      *                      i.e. this is the name that will be returned by the
186      *                      <code>HttpServletRequest.getContextPath()</code>.
187      *                      Can be null. If null, then the context from the
188      *                      Servlet Redirector will be used.
189      *                      Format: "/" + name or an empty string for the 
190      *                      default context. Must not end with a "/" character.
191      * @param theServletPath the servlet path in the URL to simulate,
192      *                      i.e. this is the name that will be returned by the
193      *                      <code>HttpServletRequest.getServletPath()</code>.
194      *                      Can be null. If null, then the servlet path from 
195      *                      the Servlet Redirector will be used.
196      *                      Format : "/" + name or an empty string.
197      * @param thePathInfo   the path info in the URL to simulate, i.e. this is
198      *                      the name that will be returned by the
199      *                      <code>HttpServletRequest.getPathInfo()</code>. Can
200      *                      be null. Format : "/" + name.
201      * @param theQueryString the Query string in the URL to simulate, i.e. this
202      *                       is the string that will be returned by the
203      *                       <code>HttpServletResquest.getQueryString()</code>.
204      *                       Can be null.
205      */
206     public ServletURL(String theProtocol, String theServerName, 
207         String theContextPath, String theServletPath, String thePathInfo, 
208         String theQueryString)
209     {
210         setProtocol(theProtocol);
211         setServerName(theServerName);
212         setContextPath(theContextPath);
213         setServletPath(theServletPath);
214         setPathInfo(thePathInfo);
215         setQueryString(theQueryString);
216     }
217 
218     /**
219      * Creates the URL to simulate, using the default HTTP protocol.
220      *
221      * @param theServerName the server name (and port) in the URL to simulate,
222      *                      i.e. this is the name that will be returned by the
223      *                      <code>HttpServletRequest.getServerName()</code> and
224      *                      <code>HttpServletRequest.getServerPort()</code>. Can
225      *                      be null. If null, then the server name and port from
226      *                      the Servlet Redirector will be returned.
227      * @param theContextPath the webapp context path in the URL to simulate,
228      *                      i.e. this is the name that will be returned by the
229      *                      <code>HttpServletRequest.getContextPath()</code>.
230      *                      Can be null. If null, then the context from the
231      *                      Servlet Redirector will be used.
232      *                      Format: "/" + name or an empty string for the 
233      *                      default context. Must not end with a "/" character.
234      * @param theServletPath the servlet path in the URL to simulate,
235      *                      i.e. this is the name that will be returned by the
236      *                      <code>HttpServletRequest.getServletPath()</code>.
237      *                      Can be null. If null, then the servlet path from 
238      *                      the Servlet Redirector will be used.
239      *                      Format : "/" + name or an empty string.
240      * @param thePathInfo   the path info in the URL to simulate, i.e. this is
241      *                      the name that will be returned by the
242      *                      <code>HttpServletRequest.getPathInfo()</code>. Can
243      *                      be null. Format : "/" + name.
244      * @param theQueryString the Query string in the URL to simulate, i.e. this
245      *                       is the string that will be returned by the
246      *                       <code>HttpServletResquest.getQueryString()</code>.
247      *                       Can be null.
248      */
249     public ServletURL(String theServerName, String theContextPath, 
250         String theServletPath, String thePathInfo, String theQueryString)
251     {
252         this(PROTOCOL_HTTP, theServerName, theContextPath, theServletPath, 
253             thePathInfo, theQueryString);
254     }
255 
256     /**
257      * @return the protocol used to connect to the URL (HTTP, HTTPS, etc).
258      */
259     public String getProtocol()
260     {
261         return this.protocol;
262     }
263 
264     /**
265      * Sets the protocol to simulate (either
266      * <code>ServletURL.PROTOCOL_HTTP</code> or
267      * <code>ServletURL.PROTOCOL_HTTPS</code>. If parameter is null then
268      * PROTOCOL_HTTP is assumed.
269      *
270      * @param theProtocol the protocol to simulate
271      */
272     public void setProtocol(String theProtocol)
273     {
274         // Only HTTP and HTTPS are currently supported.
275         if ((!theProtocol.equals(PROTOCOL_HTTP))
276             && (!theProtocol.equals(PROTOCOL_HTTPS)))
277         {
278             throw new RuntimeException("Invalid protocol [" + theProtocol
279                 + "]. Currently supported protocols are ["
280                 + PROTOCOL_HTTP + "] and ["
281                 + PROTOCOL_HTTPS + "].");
282         }
283 
284         this.protocol = theProtocol;
285     }
286 
287     /**
288      * @return the simulated URL server name (including the port number)
289      */
290     public String getServerName()
291     {
292         return this.serverName;
293     }
294 
295     /**
296      * Sets the server name (and port) in the URL to simulate, ie this is the
297      * name that will be returned by the
298      * <code>HttpServletRequest.getServerName()</code> and
299      * <code>HttpServletRequest.getServerPort()</code>. Does not need to be
300      * set. If not set or null, then the server name and port from the Servlet
301      * Redirector will be returned.
302      *
303      * @param theServerName the server name and port (ex:
304      *        "jakarta.apache.org:80")
305      */
306     public void setServerName(String theServerName)
307     {
308         this.serverName = theServerName;
309     }
310 
311     /**
312      * Returns the host name.
313      * 
314      * <p>
315      *   The host name is extracted from the specified server name (as in 
316      *   <code><strong>jakarta.apache.org</strong>:80</code>). If the server
317      *   name has not been set, this method will return <code>null</code>.
318      * </p>
319      * 
320      * @return the simulated URL server name (excluding the port number)
321      */
322     public String getHost()
323     {
324         String host = getServerName();
325 
326         if (host != null)
327         {
328             int pos = host.indexOf(":");
329 
330             if (pos > 0)
331             {
332                 host = host.substring(0, pos);
333             }
334         }
335 
336         return host;
337     }
338 
339     /**
340      * Returns the port.
341      * 
342      * <p>
343      *   The port is extracted from the specified server name (as in 
344      *   <code>jakarta.apache.org:<strong>80</strong></code>). If the server
345      *   name doesn't contain a port number, the default port number is returned
346      *   (80 for HTTP, 443 for HTTP over SSL). If a port number is specified but
347      *   illegal, or the server name has not been set, this method will return
348      *   -1.
349      * </p>
350      * 
351      * @return the simulated port number or -1 if an illegal port has been
352      *          specified
353      */
354     public int getPort()
355     {
356         int port = -1;
357 
358         if (getServerName() != null)
359         {
360             int pos = getServerName().indexOf(":");
361 
362             if (pos < 0)
363             {
364                 // the server name doesn't contain a port specification, so use
365                 // the default port for the protocol
366                 port = getDefaultPort();
367             }
368             else
369             {
370                 // parse the port encoded in the server name
371                 try
372                 {
373                     port = Integer.parseInt(getServerName().substring(pos + 1));
374                     if (port < 0)
375                     {
376                         port = -1;
377                     }
378                 }
379                 catch (NumberFormatException e)
380                 {
381                     port = -1;
382                 }
383             }
384         }
385 
386         return port;
387     }
388 
389     /**
390      * @return the simulated URL context path
391      */
392     public String getContextPath()
393     {
394         return this.contextPath;
395     }
396 
397     /**
398      * Sets the webapp context path in the URL to simulate, ie this is the
399      * name that will be returned by the
400      * <code>HttpServletRequest.getContextPath()</code>. If not set, the
401      * context from the Servlet Redirector will be returned. Format: "/" +
402      * name or an empty string for the default context. If not an empty
403      * string the last character must not be "/".
404      *
405      * @param theContextPath the context path to simulate
406      */
407     public void setContextPath(String theContextPath)
408     {
409         if ((theContextPath != null) && (theContextPath.length() > 0))
410         {
411             if (!theContextPath.startsWith("/"))
412             {
413                 throw new IllegalArgumentException("The Context Path must"
414                     + " start with a \"/\" character.");
415             }
416             if (theContextPath.endsWith("/"))
417             {
418                 throw new IllegalArgumentException("The Context Path must not"
419                     + " end with a \"/\" character.");                
420             }
421         }
422 
423         this.contextPath = theContextPath;
424     }
425 
426     /**
427      * @return the simulated URL servlet path
428      */
429     public String getServletPath()
430     {
431         return this.servletPath;
432     }
433 
434     /**
435      * Sets the servlet path in the URL to simulate, ie this is the name that
436      * will be returned by the <code>HttpServletRequest.getServletPath()</code>.
437      * If null then the servlet path from the Servlet Redirector will be
438      * returned. Format : "/" + name or an empty string.
439      *
440      * @param theServletPath the servlet path to simulate
441      */
442     public void setServletPath(String theServletPath)
443     {
444         if ((theServletPath != null) && (theServletPath.length() > 0))
445         {
446             if (!theServletPath.startsWith("/"))
447             {
448                 throw new IllegalArgumentException("The Servlet Path must"
449                     + " start with a \"/\" character.");
450             }            
451         }
452 
453         this.servletPath = theServletPath;
454     }
455 
456     /**
457      * @return the simulated URL path info
458      */
459     public String getPathInfo()
460     {
461         return this.pathInfo;
462     }
463 
464     /**
465      * Sets the path info in the URL to simulate, ie this is the name that will
466      * be returned by the <code>HttpServletRequest.getPathInfo()</code>. 
467      * If null then no path info will be set (and the Path Info from the 
468      * Servlet Redirector will <b>not</b> be used). 
469      * Format : "/" + name.
470      *
471      * @param thePathInfo the path info to simulate
472      */
473     public void setPathInfo(String thePathInfo)
474     {
475         if ((thePathInfo != null) && (thePathInfo.length() == 0))
476         { 
477             throw new IllegalArgumentException("The Path Info must"
478                 + " not be an empty string. Use null if you don't"
479                 + " want to have a path info.");
480         }
481         else if (thePathInfo != null)
482         {
483             if (!thePathInfo.startsWith("/"))
484             {
485                 throw new IllegalArgumentException("The Path Info must"
486                     + " start with a \"/\" character.");
487             }            
488         }
489 
490         this.pathInfo = thePathInfo;
491     }
492 
493     /**
494      * @return the simulated Query String
495      */
496     public String getQueryString()
497     {
498         return this.queryString;
499     }
500 
501     /**
502      * Sets the Query string in the URL to simulate, ie this is the string that
503      * will be returned by the
504      * <code>HttpServletResquest.getQueryString()</code>. If not set, the
505      * query string from the Servlet Redirector will be returned.
506      *
507      * @param theQueryString the query string to simulate
508      */
509     public void setQueryString(String theQueryString)
510     {
511         this.queryString = theQueryString;
512     }
513 
514     /**
515      * @return the path (contextPath + servletPath + pathInfo) or null if
516      *         not set
517      */
518     public String getPath()
519     {
520         String path;
521 
522         path = (getContextPath() == null) ? "" : getContextPath();
523         path += ((getServletPath() == null) ? "" : getServletPath());
524         path += ((getPathInfo() == null) ? "" : getPathInfo());
525 
526         if (path.length() == 0)
527         {
528             path = null;
529         }
530 
531         return path;
532     }
533 
534     /**
535      * Saves the current URL to a <code>WebRequest</code> object.
536      *
537      * @param theRequest the object to which the current URL should be saved to
538      */
539     public void saveToRequest(WebRequest theRequest)
540     {
541         // Note: All these pareameters are passed in the URL. This is to allow
542         // the user to send whatever he wants in the request body. For example
543         // a file, ...
544         theRequest.addParameter(URL_PROTOCOL_PARAM, getProtocol(), 
545             WebRequest.GET_METHOD);
546 
547         if (getServerName() != null)
548         {
549             theRequest.addParameter(URL_SERVER_NAME_PARAM, getServerName(), 
550                 WebRequest.GET_METHOD);
551         }
552 
553         if (getContextPath() != null)
554         {
555             theRequest.addParameter(URL_CONTEXT_PATH_PARAM, getContextPath(), 
556                 WebRequest.GET_METHOD);
557         }
558 
559         if (getServletPath() != null)
560         {
561             theRequest.addParameter(URL_SERVLET_PATH_PARAM, getServletPath(), 
562                 WebRequest.GET_METHOD);
563         }
564 
565         if (getPathInfo() != null)
566         {
567             theRequest.addParameter(URL_PATH_INFO_PARAM, getPathInfo(), 
568                 WebRequest.GET_METHOD);
569         }
570 
571         if (getQueryString() != null)
572         {
573             theRequest.addParameter(URL_QUERY_STRING_PARAM, getQueryString(), 
574                 WebRequest.GET_METHOD);
575         }
576     }
577 
578     /**
579      * Creates a <code>ServletURL</code> object by loading it's values from the
580      * HTTP request.
581      *
582      * @param theRequest the incoming HTTP request.
583      * @return the <code>ServletURL</code> object unserialized from the HTTP
584      *         request
585      */
586     public static ServletURL loadFromRequest(HttpServletRequest theRequest)
587     {
588         String qString = theRequest.getQueryString();
589         boolean isDefined = false;
590 
591         ServletURL url = new ServletURL();
592 
593         String protocol = ServletUtil.getQueryStringParameter(qString, 
594             URL_PROTOCOL_PARAM);
595 
596         if (protocol != null)
597         {
598             isDefined = true;
599             url.setProtocol(protocol);
600         }
601 
602         String serverName = ServletUtil.getQueryStringParameter(qString, 
603             URL_SERVER_NAME_PARAM);
604 
605         if (serverName != null)
606         {
607             isDefined = true;
608             url.setServerName(serverName);
609         }
610 
611         String contextPath = ServletUtil.getQueryStringParameter(qString, 
612             URL_CONTEXT_PATH_PARAM);
613 
614         if (contextPath != null)
615         {
616             isDefined = true;
617             url.setContextPath(contextPath);
618         }
619 
620         String servletPath = ServletUtil.getQueryStringParameter(qString, 
621             URL_SERVLET_PATH_PARAM);
622 
623         if (servletPath != null)
624         {
625             isDefined = true;
626             url.setServletPath(servletPath);
627         }
628 
629         String pathInfo = ServletUtil.getQueryStringParameter(qString, 
630             URL_PATH_INFO_PARAM);
631 
632         if (pathInfo != null)
633         {
634             isDefined = true;
635             url.setPathInfo(pathInfo);
636         }
637 
638         String queryString = ServletUtil.getQueryStringParameter(qString, 
639             URL_QUERY_STRING_PARAM);
640 
641         if (queryString != null)
642         {
643             isDefined = true;
644             url.setQueryString(queryString);
645         }
646 
647         if (!isDefined)
648         {
649             LOGGER.debug("Undefined simulation URL");
650             url = null;
651         }
652         else
653         {
654             LOGGER.debug("Simulation URL = [" + url + "]");
655         }
656 
657         return url;
658     }
659 
660     /**
661      * @return a string representation
662      */
663     public String toString()
664     {
665         StringBuffer buffer = new StringBuffer();
666 
667         buffer.append("protocol = [" + getProtocol() + "], ");
668         buffer.append("host name = [" + getHost() + "], ");
669         buffer.append("port = [" + getPort() + "], ");
670         buffer.append("context path = [" + getContextPath() + "], ");
671         buffer.append("servlet path = [" + getServletPath() + "], ");
672         buffer.append("path info = [" + getPathInfo() + "], ");
673         buffer.append("query string = [" + getQueryString() + "]");
674 
675         return buffer.toString();
676     }
677 
678     /**
679      * Returns the default port for the protocol.
680      * 
681      * @return the default port (80 for HTTP, 443 for HTTP over SSL)
682      */
683     private int getDefaultPort()
684     {
685         if (PROTOCOL_HTTPS.equals(getProtocol()))
686         {
687             return DEFAULT_PORT_HTTPS;
688         }
689         else
690         {
691             return DEFAULT_PORT_HTTP;
692         }
693     }
694 
695 }