001: /*
002: * ========================================================================
003: *
004: * Copyright 2001-2004 The Apache Software Foundation.
005: *
006: * Licensed under the Apache License, Version 2.0 (the "License");
007: * you may not use this file except in compliance with the License.
008: * You may obtain a copy of the License at
009: *
010: * http://www.apache.org/licenses/LICENSE-2.0
011: *
012: * Unless required by applicable law or agreed to in writing, software
013: * distributed under the License is distributed on an "AS IS" BASIS,
014: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
015: * See the License for the specific language governing permissions and
016: * limitations under the License.
017: *
018: * ========================================================================
019: */
020: package org.apache.cactus;
021:
022: import javax.servlet.http.HttpServletRequest;
023:
024: import org.apache.cactus.internal.server.ServletUtil;
025: import org.apache.commons.logging.Log;
026: import org.apache.commons.logging.LogFactory;
027:
028: /**
029: * Simulate an HTTP URL by breaking it into its different parts.
030: * <br><code><pre><b>
031: * URL = "http://" + serverName (including port) + requestURI ? queryString<br>
032: * requestURI = contextPath + servletPath + pathInfo
033: * </b></pre></code>
034: * From the Servlet 2.2 specification :<br>
035: * <code><pre><ul><li><b>Context Path</b>: The path prefix associated with the
036: * ServletContext that this servlet is a part of. If this context is the
037: * default context rooted at the base of the web server's URL namespace, this
038: * path will be an empty string. Otherwise, this path starts with a "/"
039: * character but does not end with a "/" character.</li>
040: * <li><b>Servlet Path</b>: The path section that directly corresponds to the
041: * mapping which activated this request. This path starts with a "/"
042: * character.</li>
043: * <li><b>PathInfo</b>: The part of the request path that is not part of the
044: * Context Path or the Servlet Path.</li></ul></pre></code>
045: * From the Servlet 2.3 specification :<br>
046: * <code><pre><ul><li><b>Context Path</b>: The path prefix associated with the
047: * ServletContext that this servlet is a part of. If this context is the
048: * default context rooted at the base of the web server's URL namespace, this
049: * path will be an empty string. Otherwise, this path starts with a "/"
050: * character but does not end with a "/" character.</li>
051: * <li><b>Servlet Path</b>: The path section that directly corresponds to the
052: * mapping which activated this request. This path starts with a "/"
053: * character <b>except in the case where the request is matched with the
054: * "/*" pattern, in which case it is the empty string</b>.</li>
055: * <li><b>PathInfo</b>: The part of the request path that is not part of the
056: * Context Path or the Servlet Path. <b>It is either null if there is no
057: * extra path, or is a string with a leading "/"</b>.</li></ul></pre></code>
058: *
059: * @version $Id: ServletURL.java 238991 2004-05-22 11:34:50Z vmassol $
060: */
061: public class ServletURL {
062: /**
063: * Name of the parameter in the HTTP request that represents the protocol
064: * (HTTP, HTTPS, etc) in the URL to simulate. The name is voluntarily long
065: * so that it will not clash with a user-defined parameter.
066: */
067: public static final String URL_PROTOCOL_PARAM = "Cactus_URL_Protocol";
068:
069: /**
070: * Name of the parameter in the HTTP request that represents the Server
071: * name (+ port) in the URL to simulate. The name is voluntarily long so
072: * that it will not clash with a user-defined parameter.
073: */
074: public static final String URL_SERVER_NAME_PARAM = "Cactus_URL_Server";
075:
076: /**
077: * Name of the parameter in the HTTP request that represents the context
078: * path in the URL to simulate. The name is voluntarily long so that it
079: * will not clash with a user-defined parameter.
080: */
081: public static final String URL_CONTEXT_PATH_PARAM = "Cactus_URL_ContextPath";
082:
083: /**
084: * Name of the parameter in the HTTP request that represents the Servlet
085: * Path in the URL to simulate. The name is voluntarily long so that it
086: * will not clash with a user-defined parameter.
087: */
088: public static final String URL_SERVLET_PATH_PARAM = "Cactus_URL_ServletPath";
089:
090: /**
091: * Name of the parameter in the HTTP request that represents the Path Info
092: * in the URL to simulate. The name is voluntarily long so that it will not
093: * clash with a user-defined parameter.
094: */
095: public static final String URL_PATH_INFO_PARAM = "Cactus_URL_PathInfo";
096:
097: /**
098: * Name of the parameter in the HTTP request that represents the Query
099: * String in the URL to simulate. The name is voluntarily long so that it
100: * will not clash with a user-defined parameter.
101: */
102: public static final String URL_QUERY_STRING_PARAM = "Cactus_URL_QueryString";
103:
104: /**
105: * Http protocol.
106: */
107: public static final String PROTOCOL_HTTP = "http";
108:
109: /**
110: * Https protocol.
111: */
112: public static final String PROTOCOL_HTTPS = "https";
113:
114: /**
115: * Default port of the HTTP protocol.
116: */
117: private static final int DEFAULT_PORT_HTTP = 80;
118:
119: /**
120: * Default port of HTTP over SSL.
121: */
122: private static final int DEFAULT_PORT_HTTPS = 443;
123:
124: /**
125: * The logger
126: */
127: private static final Log LOGGER = LogFactory
128: .getLog(ServletURL.class);
129:
130: /**
131: * The server name to simulate (including port number)
132: */
133: private String serverName;
134:
135: /**
136: * The context path to simulate
137: */
138: private String contextPath;
139:
140: /**
141: * The servlet path to simulate
142: */
143: private String servletPath;
144:
145: /**
146: * The Path Info to simulate
147: */
148: private String pathInfo;
149:
150: /**
151: * The Query string
152: */
153: private String queryString;
154:
155: /**
156: * The protocol to use. Default to HTTP.
157: */
158: private String protocol = PROTOCOL_HTTP;
159:
160: /**
161: * Default constructor. Need to call the different setters to make this
162: * a valid object.
163: */
164: public ServletURL() {
165: }
166:
167: /**
168: * Creates the URL to simulate.
169: *
170: * @param theProtocol the protocol to simulate (either
171: * <code>ServletURL.PROTOCOL_HTTP</code> or
172: * <code>ServletURL.PROTOCOL_HTTPS</code>.
173: * @param theServerName the server name (and port) in the URL to simulate,
174: * i.e. this is the name that will be returned by the
175: * <code>HttpServletRequest.getServerName()</code> and
176: * <code>HttpServletRequest.getServerPort()</code>. Can
177: * be null. If null, then the server name and port from
178: * the Servlet Redirector will be returned.
179: * @param theContextPath the webapp context path in the URL to simulate,
180: * i.e. this is the name that will be returned by the
181: * <code>HttpServletRequest.getContextPath()</code>.
182: * Can be null. If null, then the context from the
183: * Servlet Redirector will be used.
184: * Format: "/" + name or an empty string for the
185: * default context. Must not end with a "/" character.
186: * @param theServletPath the servlet path in the URL to simulate,
187: * i.e. this is the name that will be returned by the
188: * <code>HttpServletRequest.getServletPath()</code>.
189: * Can be null. If null, then the servlet path from
190: * the Servlet Redirector will be used.
191: * Format : "/" + name or an empty string.
192: * @param thePathInfo the path info in the URL to simulate, i.e. this is
193: * the name that will be returned by the
194: * <code>HttpServletRequest.getPathInfo()</code>. Can
195: * be null. Format : "/" + name.
196: * @param theQueryString the Query string in the URL to simulate, i.e. this
197: * is the string that will be returned by the
198: * <code>HttpServletResquest.getQueryString()</code>.
199: * Can be null.
200: */
201: public ServletURL(String theProtocol, String theServerName,
202: String theContextPath, String theServletPath,
203: String thePathInfo, String theQueryString) {
204: setProtocol(theProtocol);
205: setServerName(theServerName);
206: setContextPath(theContextPath);
207: setServletPath(theServletPath);
208: setPathInfo(thePathInfo);
209: setQueryString(theQueryString);
210: }
211:
212: /**
213: * Creates the URL to simulate, using the default HTTP protocol.
214: *
215: * @param theServerName the server name (and port) in the URL to simulate,
216: * i.e. this is the name that will be returned by the
217: * <code>HttpServletRequest.getServerName()</code> and
218: * <code>HttpServletRequest.getServerPort()</code>. Can
219: * be null. If null, then the server name and port from
220: * the Servlet Redirector will be returned.
221: * @param theContextPath the webapp context path in the URL to simulate,
222: * i.e. this is the name that will be returned by the
223: * <code>HttpServletRequest.getContextPath()</code>.
224: * Can be null. If null, then the context from the
225: * Servlet Redirector will be used.
226: * Format: "/" + name or an empty string for the
227: * default context. Must not end with a "/" character.
228: * @param theServletPath the servlet path in the URL to simulate,
229: * i.e. this is the name that will be returned by the
230: * <code>HttpServletRequest.getServletPath()</code>.
231: * Can be null. If null, then the servlet path from
232: * the Servlet Redirector will be used.
233: * Format : "/" + name or an empty string.
234: * @param thePathInfo the path info in the URL to simulate, i.e. this is
235: * the name that will be returned by the
236: * <code>HttpServletRequest.getPathInfo()</code>. Can
237: * be null. Format : "/" + name.
238: * @param theQueryString the Query string in the URL to simulate, i.e. this
239: * is the string that will be returned by the
240: * <code>HttpServletResquest.getQueryString()</code>.
241: * Can be null.
242: */
243: public ServletURL(String theServerName, String theContextPath,
244: String theServletPath, String thePathInfo,
245: String theQueryString) {
246: this (PROTOCOL_HTTP, theServerName, theContextPath,
247: theServletPath, thePathInfo, theQueryString);
248: }
249:
250: /**
251: * @return the protocol used to connect to the URL (HTTP, HTTPS, etc).
252: */
253: public String getProtocol() {
254: return this .protocol;
255: }
256:
257: /**
258: * Sets the protocol to simulate (either
259: * <code>ServletURL.PROTOCOL_HTTP</code> or
260: * <code>ServletURL.PROTOCOL_HTTPS</code>. If parameter is null then
261: * PROTOCOL_HTTP is assumed.
262: *
263: * @param theProtocol the protocol to simulate
264: */
265: public void setProtocol(String theProtocol) {
266: // Only HTTP and HTTPS are currently supported.
267: if ((!theProtocol.equals(PROTOCOL_HTTP))
268: && (!theProtocol.equals(PROTOCOL_HTTPS))) {
269: throw new RuntimeException("Invalid protocol ["
270: + theProtocol
271: + "]. Currently supported protocols are ["
272: + PROTOCOL_HTTP + "] and [" + PROTOCOL_HTTPS + "].");
273: }
274:
275: this .protocol = theProtocol;
276: }
277:
278: /**
279: * @return the simulated URL server name (including the port number)
280: */
281: public String getServerName() {
282: return this .serverName;
283: }
284:
285: /**
286: * Sets the server name (and port) in the URL to simulate, ie this is the
287: * name that will be returned by the
288: * <code>HttpServletRequest.getServerName()</code> and
289: * <code>HttpServletRequest.getServerPort()</code>. Does not need to be
290: * set. If not set or null, then the server name and port from the Servlet
291: * Redirector will be returned.
292: *
293: * @param theServerName the server name and port (ex:
294: * "jakarta.apache.org:80")
295: */
296: public void setServerName(String theServerName) {
297: this .serverName = theServerName;
298: }
299:
300: /**
301: * Returns the host name.
302: *
303: * <p>
304: * The host name is extracted from the specified server name (as in
305: * <code><strong>jakarta.apache.org</strong>:80</code>). If the server
306: * name has not been set, this method will return <code>null</code>.
307: * </p>
308: *
309: * @return the simulated URL server name (excluding the port number)
310: */
311: public String getHost() {
312: String host = getServerName();
313:
314: if (host != null) {
315: int pos = host.indexOf(":");
316:
317: if (pos > 0) {
318: host = host.substring(0, pos);
319: }
320: }
321:
322: return host;
323: }
324:
325: /**
326: * Returns the port.
327: *
328: * <p>
329: * The port is extracted from the specified server name (as in
330: * <code>jakarta.apache.org:<strong>80</strong></code>). If the server
331: * name doesn't contain a port number, the default port number is returned
332: * (80 for HTTP, 443 for HTTP over SSL). If a port number is specified but
333: * illegal, or the server name has not been set, this method will return
334: * -1.
335: * </p>
336: *
337: * @return the simulated port number or -1 if an illegal port has been
338: * specified
339: */
340: public int getPort() {
341: int port = -1;
342:
343: if (getServerName() != null) {
344: int pos = getServerName().indexOf(":");
345:
346: if (pos < 0) {
347: // the server name doesn't contain a port specification, so use
348: // the default port for the protocol
349: port = getDefaultPort();
350: } else {
351: // parse the port encoded in the server name
352: try {
353: port = Integer.parseInt(getServerName().substring(
354: pos + 1));
355: if (port < 0) {
356: port = -1;
357: }
358: } catch (NumberFormatException e) {
359: port = -1;
360: }
361: }
362: }
363:
364: return port;
365: }
366:
367: /**
368: * @return the simulated URL context path
369: */
370: public String getContextPath() {
371: return this .contextPath;
372: }
373:
374: /**
375: * Sets the webapp context path in the URL to simulate, ie this is the
376: * name that will be returned by the
377: * <code>HttpServletRequest.getContextPath()</code>. If not set, the
378: * context from the Servlet Redirector will be returned. Format: "/" +
379: * name or an empty string for the default context. If not an empty
380: * string the last character must not be "/".
381: *
382: * @param theContextPath the context path to simulate
383: */
384: public void setContextPath(String theContextPath) {
385: if ((theContextPath != null) && (theContextPath.length() > 0)) {
386: if (!theContextPath.startsWith("/")) {
387: throw new IllegalArgumentException(
388: "The Context Path must"
389: + " start with a \"/\" character.");
390: }
391: if (theContextPath.endsWith("/")) {
392: throw new IllegalArgumentException(
393: "The Context Path must not"
394: + " end with a \"/\" character.");
395: }
396: }
397:
398: this .contextPath = theContextPath;
399: }
400:
401: /**
402: * @return the simulated URL servlet path
403: */
404: public String getServletPath() {
405: return this .servletPath;
406: }
407:
408: /**
409: * Sets the servlet path in the URL to simulate, ie this is the name that
410: * will be returned by the <code>HttpServletRequest.getServletPath()</code>.
411: * If null then the servlet path from the Servlet Redirector will be
412: * returned. Format : "/" + name or an empty string.
413: *
414: * @param theServletPath the servlet path to simulate
415: */
416: public void setServletPath(String theServletPath) {
417: if ((theServletPath != null) && (theServletPath.length() > 0)) {
418: if (!theServletPath.startsWith("/")) {
419: throw new IllegalArgumentException(
420: "The Servlet Path must"
421: + " start with a \"/\" character.");
422: }
423: }
424:
425: this .servletPath = theServletPath;
426: }
427:
428: /**
429: * @return the simulated URL path info
430: */
431: public String getPathInfo() {
432: return this .pathInfo;
433: }
434:
435: /**
436: * Sets the path info in the URL to simulate, ie this is the name that will
437: * be returned by the <code>HttpServletRequest.getPathInfo()</code>.
438: * If null then no path info will be set (and the Path Info from the
439: * Servlet Redirector will <b>not</b> be used).
440: * Format : "/" + name.
441: *
442: * @param thePathInfo the path info to simulate
443: */
444: public void setPathInfo(String thePathInfo) {
445: if ((thePathInfo != null) && (thePathInfo.length() == 0)) {
446: throw new IllegalArgumentException("The Path Info must"
447: + " not be an empty string. Use null if you don't"
448: + " want to have a path info.");
449: } else if (thePathInfo != null) {
450: if (!thePathInfo.startsWith("/")) {
451: throw new IllegalArgumentException("The Path Info must"
452: + " start with a \"/\" character.");
453: }
454: }
455:
456: this .pathInfo = thePathInfo;
457: }
458:
459: /**
460: * @return the simulated Query String
461: */
462: public String getQueryString() {
463: return this .queryString;
464: }
465:
466: /**
467: * Sets the Query string in the URL to simulate, ie this is the string that
468: * will be returned by the
469: * <code>HttpServletResquest.getQueryString()</code>. If not set, the
470: * query string from the Servlet Redirector will be returned.
471: *
472: * @param theQueryString the query string to simulate
473: */
474: public void setQueryString(String theQueryString) {
475: this .queryString = theQueryString;
476: }
477:
478: /**
479: * @return the path (contextPath + servletPath + pathInfo) or null if
480: * not set
481: */
482: public String getPath() {
483: String path;
484:
485: path = (getContextPath() == null) ? "" : getContextPath();
486: path += ((getServletPath() == null) ? "" : getServletPath());
487: path += ((getPathInfo() == null) ? "" : getPathInfo());
488:
489: if (path.length() == 0) {
490: path = null;
491: }
492:
493: return path;
494: }
495:
496: /**
497: * Saves the current URL to a <code>WebRequest</code> object.
498: *
499: * @param theRequest the object to which the current URL should be saved to
500: */
501: public void saveToRequest(WebRequest theRequest) {
502: // Note: All these pareameters are passed in the URL. This is to allow
503: // the user to send whatever he wants in the request body. For example
504: // a file, ...
505: theRequest.addParameter(URL_PROTOCOL_PARAM, getProtocol(),
506: WebRequest.GET_METHOD);
507:
508: if (getServerName() != null) {
509: theRequest.addParameter(URL_SERVER_NAME_PARAM,
510: getServerName(), WebRequest.GET_METHOD);
511: }
512:
513: if (getContextPath() != null) {
514: theRequest.addParameter(URL_CONTEXT_PATH_PARAM,
515: getContextPath(), WebRequest.GET_METHOD);
516: }
517:
518: if (getServletPath() != null) {
519: theRequest.addParameter(URL_SERVLET_PATH_PARAM,
520: getServletPath(), WebRequest.GET_METHOD);
521: }
522:
523: if (getPathInfo() != null) {
524: theRequest.addParameter(URL_PATH_INFO_PARAM, getPathInfo(),
525: WebRequest.GET_METHOD);
526: }
527:
528: if (getQueryString() != null) {
529: theRequest.addParameter(URL_QUERY_STRING_PARAM,
530: getQueryString(), WebRequest.GET_METHOD);
531: }
532: }
533:
534: /**
535: * Creates a <code>ServletURL</code> object by loading it's values from the
536: * HTTP request.
537: *
538: * @param theRequest the incoming HTTP request.
539: * @return the <code>ServletURL</code> object unserialized from the HTTP
540: * request
541: */
542: public static ServletURL loadFromRequest(
543: HttpServletRequest theRequest) {
544: String qString = theRequest.getQueryString();
545: boolean isDefined = false;
546:
547: ServletURL url = new ServletURL();
548:
549: String protocol = ServletUtil.getQueryStringParameter(qString,
550: URL_PROTOCOL_PARAM);
551:
552: if (protocol != null) {
553: isDefined = true;
554: url.setProtocol(protocol);
555: }
556:
557: String serverName = ServletUtil.getQueryStringParameter(
558: qString, URL_SERVER_NAME_PARAM);
559:
560: if (serverName != null) {
561: isDefined = true;
562: url.setServerName(serverName);
563: }
564:
565: String contextPath = ServletUtil.getQueryStringParameter(
566: qString, URL_CONTEXT_PATH_PARAM);
567:
568: if (contextPath != null) {
569: isDefined = true;
570: url.setContextPath(contextPath);
571: }
572:
573: String servletPath = ServletUtil.getQueryStringParameter(
574: qString, URL_SERVLET_PATH_PARAM);
575:
576: if (servletPath != null) {
577: isDefined = true;
578: url.setServletPath(servletPath);
579: }
580:
581: String pathInfo = ServletUtil.getQueryStringParameter(qString,
582: URL_PATH_INFO_PARAM);
583:
584: if (pathInfo != null) {
585: isDefined = true;
586: url.setPathInfo(pathInfo);
587: }
588:
589: String queryString = ServletUtil.getQueryStringParameter(
590: qString, URL_QUERY_STRING_PARAM);
591:
592: if (queryString != null) {
593: isDefined = true;
594: url.setQueryString(queryString);
595: }
596:
597: if (!isDefined) {
598: LOGGER.debug("Undefined simulation URL");
599: url = null;
600: } else {
601: LOGGER.debug("Simulation URL = [" + url + "]");
602: }
603:
604: return url;
605: }
606:
607: /**
608: * @return a string representation
609: */
610: public String toString() {
611: StringBuffer buffer = new StringBuffer();
612:
613: buffer.append("protocol = [" + getProtocol() + "], ");
614: buffer.append("host name = [" + getHost() + "], ");
615: buffer.append("port = [" + getPort() + "], ");
616: buffer.append("context path = [" + getContextPath() + "], ");
617: buffer.append("servlet path = [" + getServletPath() + "], ");
618: buffer.append("path info = [" + getPathInfo() + "], ");
619: buffer.append("query string = [" + getQueryString() + "]");
620:
621: return buffer.toString();
622: }
623:
624: /**
625: * Returns the default port for the protocol.
626: *
627: * @return the default port (80 for HTTP, 443 for HTTP over SSL)
628: */
629: private int getDefaultPort() {
630: if (PROTOCOL_HTTPS.equals(getProtocol())) {
631: return DEFAULT_PORT_HTTPS;
632: } else {
633: return DEFAULT_PORT_HTTP;
634: }
635: }
636:
637: }
|