/* * Copyright (C) 2002-2004 Red Hat Inc. All Rights Reserved. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License * as published by the Free Software Foundation; either version 2.1 of * the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * */ package com.arsdigita.web; import com.arsdigita.dispatcher.DispatcherHelper; import com.arsdigita.kernel.security.Util; import com.arsdigita.util.Assert; import com.arsdigita.util.servlet.HttpHost; import java.util.Map; import javax.servlet.http.HttpServletRequest; import org.apache.log4j.Logger; /** *
URL models a future request according to the servlet worldview. * Its principal uses are two: * *
Each URL has the following accessors, here set next to an
* example URL instance,
* http://example.com:8080/ccmapp/forum/index.jsp?cat=2&cat=5:
Atomic parts: * *
* * * ** getScheme() -> "http" * getServerName() -> "example.com" * getServerPort() -> 8080 * getContextPath() -> "/ccmapp" * getServletPath() -> "/forum" * getPathInfo() -> "/index.jsp" * getParameter("cat") -> "2" * getParameterValues("cat") -> {"2", "5"} *
Composite parts: * *
* * * ** toString() -> "/ccmapp/forum/index.jsp?cat=2&cat=5" * getURL() -> "http://example.com:8080/ccmapp/forum/index.jsp?cat=2&cat=5 * getServerURI() -> "http://example.com:8080" // No trailing "/" * getRequestURI() -> "/ccmapp/forum/index.jsp" * getQueryString() -> "cat=2&cat=5" // No leading "?" * getParameterMap() -> {cat={"2", "5"}} *
The toString() method returns a URL suitable for
* use in hyperlinks; since in the common case, the scheme, server
* name, and port are best left off, toString() omits
* them. The getURL() method returns a
* String URL which is fully qualified. Both
* getURL() and getServerURI() omit the port
* from their return values if the server port is the default, port
* 80.
Creating URLs will usually be done via one of the static create * methods:
* *URL.root() creates a URL pointing at the server's
* root path, "/".
URL.request(req, params) creates a URL reflecting
* the request the client made but using the passed-in parameters
* instead.
URL.there(req, path, params) and its variants
* produce URLs that go through the CCM main dispatcher. The variant
* URL.there(req, app, pathInfo, params) dispatches to
* pathInfo under the specified application. The variant
* URL.here(req, pathInfo, params) dispatches to
* pathInfo under the current application.
URL.excursion(req, path, params) produces URLs that
* go through the dispatcher to a destination but also encode and
* store the origin. This is used by LoginSignal and
* ReturnSignal to implement UI excursions.
All static create methods taking an
* HttpServletRequest (1) preserve the request's scheme,
* server name, and port and (2) run parameter listeners if the URL's
* parameter map is not null.
Those methods not taking an HttpServletRequest use
* the scheme, server name, and port defined in
* WebConfig.
All static create methods taking a ParameterMap
* take null to mean no query string at all. URLs defined this way
* will have no query string and no "?".
Those methods not taking a ParameterMap argument implicitly
* create an empty parameter map. Note that this is different from
* creating a URL with a null parameter map, which produces a URL with
* no query string.
Assembles a fully qualified URL from its fundamental pieces.
* The contract of URL dictates that once params is
* passed in to this constructor, no parameters should be added or
* removed. This is to make URL in practice a
* read-only object.
"http", for example; see {@link
* javax.servlet.ServletRequest#getScheme()}
*
* @param serverName a valid domain name, for example
* "ccm.redhat.com"; see {@link
* javax.servlet.ServletRequest#getServerName()}
*
* @param serverPort 8080, for instance; see {@link
* javax.servlet.ServletRequest#getServerPort()}
*
* @param contextPath the path to your web app; empty string
* indicates the default context; any other values for contextPath
* must start with "/" but not end in
* "/"; contextPath cannot be null; see {@link
* javax.servlet.http.HttpServletRequest#getContextPath()}
*
* @param servletPath the path to your servlet; empty string and
* values starting with "/" are valid, but null is
* not; see {@link
* javax.servlet.http.HttpServletRequest#getServletPath()}
*
* @param pathInfo the path data remaining after the servlet path
* but before the query string; pathInfo may be null; see {@link
* javax.servlet.http.HttpServletRequest#getPathInfo()}
*
* @param params a ParameterMap representing a set of
* query parameters
*
* @return a fully specified URL
*/
public URL(final String scheme,
final String serverName,
final int serverPort,
final String contextPath,
final String servletPath,
final String pathInfo,
final ParameterMap params) {
HttpServletRequest req = Web.getRequest();
String dispatcherPrefix = req == null ? null :
DispatcherHelper.getDispatcherPrefix(req);
init(scheme,
serverName,
serverPort,
contextPath,
servletPath,
dispatcherPrefix,
pathInfo,
params);
}
/**
* (private) Constructor.
*
* @param sreq
* @param params
*/
private URL(final HttpServletRequest sreq,
final ParameterMap params) {
final String dispatcherPrefix =
DispatcherHelper.getDispatcherPrefix(sreq);
final HttpHost host = new HttpHost(sreq);
init(sreq.getScheme(),
host.getName(),
host.getPort(),
sreq.getContextPath(),
sreq.getServletPath(),
dispatcherPrefix,
sreq.getPathInfo(),
params);
}
/**
* Constructor, produce a URL representation of the given request.
* * @param sreq anHttpServletRequest from which to copy
*
* @return a URL whose contents correspond to the request used to
* create it
*/
public URL(final HttpServletRequest sreq) {
this(sreq, new ParameterMap(sreq));
}
/**
* Produces a short description of a URL suitable for * debugging.
* * @return a debugging representation of this URL */ public final String toDebugString() { return super.toString() + " " + "[" + getScheme() + "," + getServerName() + "," + getServerPort() + "," + getContextPath() + "," + getServletPath() + "," + getDispatcherPrefix() + "," + getPathInfo() + "," + getQueryString() + "]"; } /** * Returns aString representation of the URL, fully
* qualified. The port is omitted if it is the standard HTTP
* port, 80.
*
* @return a String URL, with all of its parts
*/
public final String getURL() {
if (m_params == null) {
return m_url.toString();
} else {
return m_url.toString() + m_params;
}
}
/**
* Returns the scheme (sometimes called the protocol) of the
* URL. Examples are "http" and
* "https".
String representing the URL's scheme
*/
public final String getScheme() {
return m_url.substring(0, m_schemeEnd);
}
/**
* Returns the domain name part of the URL. For instance,
* "ccm.redhat.com".
String representing the URL's server
* name
*/
public final String getServerName() {
return m_url.substring(m_schemeEnd + 3, m_serverNameEnd);
}
/**
* Returns the port number of the URL. 8080, for
* example.
int for the URL's port number
*/
public final int getServerPort() {
final String port = m_url.substring(m_serverNameEnd, m_serverPortEnd);
if (port.equals("")) {
return 80;
} else {
return Integer.parseInt(port.substring(1));
}
}
/**
* Returns the server half of the URL, as opposed to the "file" * half. For example, "http://ccm.redhat.com:8080". Note that there * is no trailing slash; any characters following the server port * are considered part of the {@link #getRequestURI() request * URI}.
* *This method has no equivalent in the Servlet API, but it is * similar in spirit to {@link * javax.servlet.http.HttpServletRequest#getRequestURI()}.
* *It is defined to return * *
getScheme() + "://" + getServerName() + ":"
* + getServerPort()
*
* or, if the server port is 80,
*
* getScheme() + "://" +
* getServerName()
*
*
*
* @see #getRequestURI()
* @return a String comprised of the scheme, server
* name, and server port plus connecting bits
*/
public final String getServerURI() {
return m_url.substring(0, m_serverPortEnd);
}
/**
* Returns the context path of the URL. The value cannot be
* null, and values starting with "/" do not end in
* "/"; empty string is a valid return value that
* stands for the default web app. Example values are
* "" and "/ccm-app".
String path to a web app context
*/
public final String getContextPath() {
return m_url.substring(m_serverPortEnd, m_contextPathEnd);
}
/**
* Experimental
*Returns the dispatcher prefix of this request as * set by the InternalPrefixerServlet */ public final String getDispatcherPrefix() { if (m_dispatcherPrefixEnd < m_servletPathEnd) { //there is no dispatcher prefix return ""; } else { return m_url.substring(m_servletPathEnd, m_dispatcherPrefixEnd); } } /** *
Returns the servlet path of the URL. The value cannot be * null.
* * @see javax.servlet.http.HttpServletRequest#getServletPath() * @return aString path to a servlet
*/
public final String getServletPath() {
return m_url.substring(m_dispatcherPrefixEnd, m_servletPathEnd);
}
/**
* Returns the servlet-local path data of the URL. The value
* may be null. If it is not null, the value begins with a "/".
* Examples are null, "/", and
* "/remove.jsp".
String of path data addressed to a
* servlet
*/
public final String getPathInfo() {
final String pathInfo = m_url.substring(m_servletPathEnd);
if (pathInfo.equals("")) {
return null;
} else {
return pathInfo;
}
}
/**
* Returns the "file" part of the URL, in contrast to the
* {@link #getServerURI() server part}. The value cannot be null
* and always starts with a "/". For example,
* "/ccm/forum/thread.jsp".
This method is defined to return the equivalent of
* getContextPath() + getServletPath() +
* getPathInfo().
String comprised of the context path,
* servlet path, and path info
*/
public final String getRequestURI() {
return m_url.substring(m_serverPortEnd);
}
/**
* Returns the query string of the URL. If the URL was
* constructed with a null ParameterMap, this method
* returns null. If the URL was constructed with an empty
* ParameterMap, this method returns the empty
* string. Example values are null, "",
* and "ticket-id=56&user-id=24".
String representing the query parameters
* of the URL
*/
public final String getQueryString() {
if (m_params == null) {
return null;
} else {
return m_params.getQueryString();
}
}
/**
* Returns the value of one query parameter. If the URL was
* constructed with a null ParameterMap, this method
* returns null. If the parameter requested has multiple values,
* this method will only return the first; use {@link
* #getParameterValues(String)} to get all of the values.
String value of the parameter
*/
public final String getParameter(final String name) {
if (m_params == null) {
return null;
} else {
return m_params.getParameter(name);
}
}
/**
* Returns the values for a parameter. If the URL was
* constructed with a null ParameterMap, this method
* returns null.
String[] of values for the parameter
*/
public final String[] getParameterValues(final String name) {
if (m_params == null) {
return null;
} else {
return m_params.getParameterValues(name);
}
}
/**
* Returns an immutable map of the query parameters. The map's
* keys are Strings and the map's values are
* String[]s. If the URL was constructed with a null
* ParameterMap, this method returns null.
Map of the URL's query parameters
*/
public final Map getParameterMap() {
if (m_params == null) {
return null;
} else {
return m_params.getParameterMap();
}
}
/**
* Creates a URL to the site's root path. For example,
* http://somewhere.net/.
URL to your server's root path
*/
public static final URL root() {
final WebConfig config = Web.getConfig();
URL url = new URL
(config.getDefaultScheme(),
config.getServer().getName(),
config.getServer().getPort(),
"",
"/",
null,
null);
return url;
}
/**
* Creates a URL using the elements of the user's original * request but with the given set of parameters instead of the * original ones.
* * @param sreq the servlet request * @param params aParameterMap of params to replace
* those of the request
* @return a URL representing the original request
* except for its parameters
*/
public static final URL request(final HttpServletRequest sreq,
final ParameterMap params) {
if (params != null) {
params.runListeners(sreq);
}
final URL url = Web.getContext().getRequestURL();
if (url == null) {
// If the URL is being generated outside of a WebContext,
// use the request to fill out the URL.
return new URL( sreq, params );
} else {
return new URL
(url.getScheme(),
url.getServerName(),
url.getServerPort(),
url.getContextPath(),
url.getServletPath(),
url.getPathInfo(),
params);
}
}
/**
* Creates a URL to path under the CCM main
* dispatcher and with the given parameters. A null
* ParameterMap indicates that the URL has no query
* string at all. If the parameter map is not null, its parameter
* listeners are run and may further edit the parameter map.
String path to which to dispatch
* @param params a ParameterMap of parameters to use;
* this value may be null
* @return a URL with a path to dispatch to
*/
public static final URL there(final HttpServletRequest sreq,
final String path,
final ParameterMap params) {
final WebConfig config = Web.getConfig();
Assert.exists(sreq, "HttpServletRequest sreq");
Assert.exists(config, "WebConfig config");
if (params != null) {
params.runListeners(sreq);
}
final HttpHost host = new HttpHost(sreq);
return new URL
(sreq.getScheme(),
host.getName(),
host.getPort(),
config.getDispatcherContextPath(),
config.getDispatcherServletPath(),
path,
params);
}
/**
* Method similar to there(), but which checks the
* waf.web.dynamic_host_provider parameter to generate
* the site name and port dynamically.
*
* @see com.arsdigita.web.DispatcherServlet
* @param sreq the servlet request
* @param path a String path to which to dispatch
* @param params a ParameterMap of parameters to use;
* this value may be null
* @return a URL with a path to dispatch to
*/
public static final URL dynamicHostThere(final HttpServletRequest sreq,
final String path,
final ParameterMap params) {
final WebConfig config = Web.getConfig();
DynamicHostProvider provider = Web.getConfig().getDynamicHostProvider();
if (provider == null) {
return there(sreq, path, params);
}
Assert.exists(sreq, "HttpServletRequest sreq");
Assert.exists(config, "WebConfig config");
if (params != null) {
params.runListeners(sreq);
}
final HttpHost host = new HttpHost(sreq);
return new URL(sreq.getScheme(),
provider.getName(),
provider.getPort(),
config.getDispatcherContextPath(),
config.getDispatcherServletPath(),
path,
params);
}
/**
* Creates a URL with no local parameters to path
* under the CCM main dispatcher. This method implicitly creates
* an empty parameter map (not a null one); this empty map may be
* altered by parameter listeners, for instance to include global
* parameters.
String path to dispatch to
* @return a URL to a path under the dispatcher and
* with an empty parameter map
*/
public static final URL there(final HttpServletRequest sreq,
final String path) {
final WebConfig config = Web.getConfig();
Assert.exists(sreq, "HttpServletRequest sreq");
Assert.exists(config, "WebConfig config");
final HttpHost host = new HttpHost(sreq);
return new URL
(sreq.getScheme(),
host.getName(),
host.getPort(),
config.getDispatcherContextPath(),
config.getDispatcherServletPath(),
path,
(ParameterMap) s_empty.get());
}
/**
* Creates a URL to pathInfo under the specified
* application and using the given parameters. The parmeter map
* argument may be null, indicating that the URL has no query
* string.
Application to dispatch to
* @param pathInfo a String of extra path info for
* the application
* @param params a ParameterMap of parameters to use
* @return a URL to an application with a particular
* pathInfo
*/
public static final URL there(final HttpServletRequest sreq,
final Application app,
final String pathInfo,
final ParameterMap params) {
if (Assert.isEnabled() && pathInfo != null) {
Assert.isTrue(pathInfo.startsWith("/"),
"pathInfo, if not null, must " +
"start with a slash");
}
if (pathInfo == null) {
return URL.there(sreq, app.getPath(), params);
} else {
return URL.there(sreq, app.getPath() + pathInfo, params);
}
}
/**
* Creates a URL with no local parameters to
* pathInfo under the specified application.
*
* @param sreq the servlet request
* @param app the Application to dispatch to
* @param pathInfo a String of extra path info for
* the application
* @return a URL to an application with a particular
* pathInfo
*/
public static final URL there(final HttpServletRequest sreq,
final Application app,
final String pathInfo) {
if (Assert.isEnabled() && pathInfo != null) {
Assert.isTrue(pathInfo.startsWith("/"),
"pathInfo, if not null, must " +
"start with a slash");
}
if (pathInfo == null) {
return URL.there(sreq, app.getPath());
} else {
return URL.there(sreq, app.getPath() + pathInfo);
}
}
/**
*
Creates a URL with local parameters.
* *This function should not be used unless you really don't have an
* HttpServletRequest object as it will ignore any Host header
* given by the client.
Create a URL with local parameters to pathInfo under the
* specified application.
This function should not be used unless you really don't have an
* HttpServletRequest object as it will ignore any Host header
* given by the client.
String representation of the URL
* suitable for use as a hyperlink. The scheme, server name, and
* port are omitted.
*
* @return a String URL
*/
public final String toString() {
if (m_params == null) {
return m_url.substring(m_serverPortEnd);
} else {
return m_url.substring(m_serverPortEnd) + m_params;
}
}
public static final String getDispatcherPath() {
final WebConfig config = Web.getConfig();
final HttpServletRequest req = Web.getRequest();
final String context = config.getDispatcherContextPath();
final String servlet = config.getDispatcherServletPath();
if (req == null) {
return context + servlet;
} else {
final String prefix = DispatcherHelper.getDispatcherPrefix(req);
if (prefix == null) {
return context + servlet;
} else {
return context + prefix + servlet;
}
}
}
private static class EmptyParameterMap extends InternalRequestLocal {
protected final Object initialValue() {
return new ParameterMap();
}
protected final void prepareValue(final HttpServletRequest sreq) {
((ParameterMap) get()).runListeners(sreq);
}
protected final void clearValue() {
((ParameterMap) get()).clear();
}
}
}