001: package org.gomba;
002:
003: import java.io.IOException;
004:
005: import javax.servlet.RequestDispatcher;
006: import javax.servlet.ServletConfig;
007: import javax.servlet.ServletException;
008: import javax.servlet.http.HttpServlet;
009: import javax.servlet.http.HttpServletRequest;
010: import javax.servlet.http.HttpServletResponse;
011:
012: /**
013: * This Servlet forwards HTTP requests to other servlets. Target servlets are
014: * mapped to HTTP request methods: GET, POST, PUT, DELETE. This servlet is
015: * useful to build read/write/update/delete web services by compositing servlets
016: * that manage only a single operation.
017: *
018: * <p>
019: * Init params:
020: * <dl>
021: * <dt>GET</dt>
022: * <dd>The name of the servlet to handle GET requests. This method is normally
023: * used for read (SELECT in SQL) operations. (Optional)</dd>
024: * <dt>POST</dt>
025: * <dd>The name of the servlet to handle POST requests. This method is normally
026: * used for creation (INSERT in SQL) operations. (Optional)</dd>
027: * <dt>PUT</dt>
028: * <dd>The name of the servlet to handle PUT requests. This method is normally
029: * used for update (UPDATE in SQL) operations. (Optional)</dd>
030: * <dt>DELETE</dt>
031: * <dd>The name of the servlet to handle DELETE requests. This method is
032: * obviously used for deletion (DELETE in SQL) operations. (Optional)</dd>
033: * </dl>
034: * </p>
035: *
036: * <p>
037: * Design note. While this 'dispatcher' approach may seem to break the Servlet
038: * contract by splitting the processing of HTTP methods among multiple servlets,
039: * we designed this way with the explicit purpose of remaining inside the
040: * Servlet API and not creating our own framework inside the Servlet framework.
041: * The benefits of this design are:
042: * <ul>
043: * <li>All configuration is done through standard mechanisms (web.xml).</li>
044: * <li>A web service can smoothly grow from a read-only service (which is
045: * fairly easy to configure: 1 servlet, 1 servlet-mapping), to a more
046: * sophisticated read/write/update/delete service (which involves the
047: * configuration of 4 servlets and 1 servlet-mapping).</li>
048: * </ul>
049: * An alternative design would be having a single Servlet handling all HTTP
050: * methods and invoking a "service" mapped to it. This would require:
051: * <ul>
052: * <li>Creating our own "service" abstraction.</li>
053: * <li>Having our own XML configuration file besides the web.xml</li>
054: * </ul>
055: * Which we do not like at all.
056: * </p>
057: *
058: * @author Flavio Tordini
059: * @version $Id: DispatcherServlet.java,v 1.3 2004/09/16 09:23:16 flaviotordini Exp $
060: */
061: public final class DispatcherServlet extends HttpServlet {
062:
063: private final static String METHOD_GET = "GET";
064:
065: private final static String METHOD_HEAD = "HEAD";
066:
067: private final static String METHOD_POST = "POST";
068:
069: private final static String METHOD_PUT = "PUT";
070:
071: private final static String METHOD_DELETE = "DELETE";
072:
073: private String getResourceName;
074:
075: private String postResourceName;
076:
077: private String putResourceName;
078:
079: private String deleteResourceName;
080:
081: /**
082: * @see javax.servlet.Servlet#init(javax.servlet.ServletConfig)
083: */
084: public void init(ServletConfig config) throws ServletException {
085: super .init(config);
086:
087: this .getResourceName = config.getInitParameter(METHOD_GET);
088: this .postResourceName = config.getInitParameter(METHOD_POST);
089: this .putResourceName = config.getInitParameter(METHOD_PUT);
090: this .deleteResourceName = config
091: .getInitParameter(METHOD_DELETE);
092:
093: }
094:
095: /**
096: * @see javax.servlet.http.HttpServlet#doDelete(javax.servlet.http.HttpServletRequest,
097: * javax.servlet.http.HttpServletResponse)
098: */
099: protected void doDelete(HttpServletRequest request,
100: HttpServletResponse response) throws ServletException,
101: IOException {
102: forwardTo(this .deleteResourceName, request, response);
103: }
104:
105: /**
106: * @see javax.servlet.http.HttpServlet#doGet(javax.servlet.http.HttpServletRequest,
107: * javax.servlet.http.HttpServletResponse)
108: */
109: protected void doGet(HttpServletRequest request,
110: HttpServletResponse response) throws ServletException,
111: IOException {
112: forwardTo(this .getResourceName, request, response);
113: }
114:
115: /**
116: * @see javax.servlet.http.HttpServlet#doHead(javax.servlet.http.HttpServletRequest,
117: * javax.servlet.http.HttpServletResponse)
118: */
119: protected void doHead(HttpServletRequest request,
120: HttpServletResponse response) throws ServletException,
121: IOException {
122: forwardTo(this .getResourceName, request, response);
123: }
124:
125: /**
126: * @see javax.servlet.http.HttpServlet#doPost(javax.servlet.http.HttpServletRequest,
127: * javax.servlet.http.HttpServletResponse)
128: */
129: protected void doPost(HttpServletRequest request,
130: HttpServletResponse response) throws ServletException,
131: IOException {
132: forwardTo(this .postResourceName, request, response);
133: }
134:
135: /**
136: * @see javax.servlet.http.HttpServlet#doPut(javax.servlet.http.HttpServletRequest,
137: * javax.servlet.http.HttpServletResponse)
138: */
139: protected void doPut(HttpServletRequest request,
140: HttpServletResponse response) throws ServletException,
141: IOException {
142: forwardTo(this .putResourceName, request, response);
143: }
144:
145: /**
146: * Forward the request to the specified named resource.
147: */
148: private void forwardTo(String resourceName,
149: HttpServletRequest request, HttpServletResponse response)
150: throws ServletException, IOException {
151:
152: if (resourceName == null) {
153: // we don't have any target resource mapped to this HTTP method
154: methodNotAllowed(response);
155: return;
156: }
157:
158: // get the dispatcher
159: RequestDispatcher dispatcher = getServletContext()
160: .getNamedDispatcher(resourceName);
161: if (dispatcher == null) {
162: throw new ServletException(
163: "Cannot get a RequestDispatcher for name: "
164: + resourceName);
165: }
166:
167: // forward to the resource
168: dispatcher.forward(request, response);
169:
170: }
171:
172: /**
173: * @see <a
174: * href="http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.4.6">Method
175: * Not Allowed HTTP status </a>
176: * @see <a
177: * href="http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.7>Allow
178: * HTTP header </a>
179: */
180: private void methodNotAllowed(HttpServletResponse response)
181: throws IOException {
182:
183: // build the Allow header value
184: final StringBuffer allowedMethods = new StringBuffer(
185: "OPTIONS, TRACE");
186: if (this .getResourceName != null) {
187: allowedMethods.append(", ").append(METHOD_GET);
188: allowedMethods.append(", ").append(METHOD_HEAD);
189: }
190: if (this .postResourceName != null) {
191: allowedMethods.append(", ").append(METHOD_POST);
192: }
193: if (this .putResourceName != null) {
194: allowedMethods.append(", ").append(METHOD_PUT);
195: }
196: if (this .deleteResourceName != null) {
197: allowedMethods.append(", ").append(METHOD_DELETE);
198: }
199:
200: response.setHeader("Allow", allowedMethods.toString());
201: response.sendError(HttpServletResponse.SC_METHOD_NOT_ALLOWED);
202: }
203: }
|