001: /*
002: * Copyright (c) 1998-2001 Caucho Technology -- all rights reserved
003: *
004: * This file is part of Resin(R) Open Source
005: *
006: * Each copy or derived work must preserve the copyright notice and this
007: * notice unmodified.
008: *
009: * Resin Open Source is free software; you can redistribute it and/or modify
010: * it under the terms of the GNU General Public License as published by
011: * the Free Software Foundation; either version 2 of the License, or
012: * (at your option) any later version.
013: *
014: * Resin Open Source is distributed in the hope that it will be useful,
015: * but WITHOUT ANY WARRANTY; without even the implied warranty of
016: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE, or any warranty
017: * of NON-INFRINGEMENT. See the GNU General Public License for more
018: * details.
019: *
020: * You should have received a copy of the GNU General Public License
021: * along with Resin Open Source; if not, write to the
022: *
023: * Free Software Foundation, Inc.
024: * 59 Temple Place, Suite 330
025: * Boston, MA 02111-1307 USA
026: *
027: * @author Scott Ferguson
028: */
029:
030: package javax.servlet.http;
031:
032: import javax.servlet.GenericServlet;
033: import javax.servlet.ServletException;
034: import javax.servlet.ServletRequest;
035: import javax.servlet.ServletResponse;
036: import java.io.IOException;
037: import java.io.Serializable;
038:
039: /**
040: * HttpServlet is a convenient abstract class for creating servlets.
041: * Normally, servlet writers will only need to override
042: * <code>doGet</code> or <code>doPost</code>.
043: *
044: * <h4>Caching</h4>
045: *
046: * HttpServlet makes caching simple. Just override
047: * <code>getLastModified</code>. As long as the page hasn't changed,
048: * it can avoid the overhead of any heavy processing or database queries.
049: * You cannot use <code>getLastModified</code> if the response depends
050: * on sessions, cookies, or any headers in the servlet request.
051: *
052: * <h4>Hello, world</h4>
053: *
054: * The Hello.java belongs in myapp/WEB-INF/classes/test/Hello.java under the
055: * application's root. Normally, it will be called as
056: * http://myhost.com/myapp/servlet/test.Hello. If the server doesn't use
057: * applications, then use /servlet/test.Hello.
058: *
059: * <code><pre>
060: * package test;
061: *
062: * import java.io.*;
063: * import javax.servlet.*;
064: * import javax.servlet.http.*;
065: *
066: * public class Hello extends HttpServlet {
067: * public void doGet(HttpServletRequest request,
068: * HttpServletResponse response)
069: * throws ServletException, IOException
070: * {
071: * response.setContentType("text/html");
072: * PrintWriter out = response.getWriter();
073: * out.println("Hello, World");
074: * out.close();
075: * }
076: * }
077: * </pre></code>
078: */
079: public abstract class HttpServlet extends GenericServlet implements
080: Serializable {
081: /**
082: * Service a request. Normally not overridden. If you need to override
083: * this, use GenericServlet instead.
084: */
085: public void service(ServletRequest request, ServletResponse response)
086: throws ServletException, IOException {
087: HttpServletRequest req = (HttpServletRequest) request;
088: HttpServletResponse res = (HttpServletResponse) response;
089:
090: service(req, res);
091: }
092:
093: /**
094: * Services a HTTP request. Automatically dispatches based on the
095: * request method and handles "If-Modified-Since" headers. Normally
096: * not overridden.
097: *
098: * @param req request information
099: * @param res response object for returning data to the client.
100: */
101: protected void service(HttpServletRequest req,
102: HttpServletResponse res) throws ServletException,
103: IOException {
104: String method = req.getMethod();
105: boolean isHead = false;
106:
107: if (method.equals("GET") || (isHead = method.equals("HEAD"))) {
108: long lastModified = getLastModified(req);
109: if (lastModified <= 0) {
110: if (isHead)
111: doHead(req, res);
112: else
113: doGet(req, res);
114: return;
115: }
116:
117: char[] newETag = null;
118: String etag = req.getHeader("If-None-Match");
119: if (etag != null) {
120: newETag = generateETag(lastModified);
121: int len = etag.length();
122:
123: if (len == newETag.length) {
124: for (len--; len >= 0; len--) {
125: if (etag.charAt(len) != newETag[len])
126: break;
127: }
128: }
129:
130: if (len < 0) {
131: res.sendError(res.SC_NOT_MODIFIED);
132: return;
133: }
134: }
135:
136: long requestLastModified = req
137: .getDateHeader("If-Modified-Since");
138: if ((lastModified / 1000) == (requestLastModified / 1000)) {
139: res.sendError(res.SC_NOT_MODIFIED);
140: return;
141: }
142:
143: if (newETag == null)
144: newETag = generateETag(lastModified);
145: res.setHeader("ETag", new String(newETag));
146: res.setDateHeader("Last-Modified", lastModified);
147: if (isHead)
148: doHead(req, res);
149: else
150: doGet(req, res);
151: } else if (method.equals("POST")) {
152: doPost(req, res);
153: } else if (method.equals("PUT")) {
154: doPut(req, res);
155: } else if (method.equals("DELETE")) {
156: doDelete(req, res);
157: } else if (method.equals("OPTIONS")) {
158: doOptions(req, res);
159: } else if (method.equals("TRACE")) {
160: doTrace(req, res);
161: } else {
162: res.sendError(res.SC_NOT_IMPLEMENTED,
163: "Method not implemented");
164: }
165: }
166:
167: private static char[] generateETag(long data) {
168: char[] buf = new char[13];
169:
170: buf[0] = '"';
171: buf[1] = encodeBase64(data >> 60);
172: buf[2] = encodeBase64(data >> 54);
173: buf[3] = encodeBase64(data >> 48);
174: buf[4] = encodeBase64(data >> 42);
175:
176: buf[5] = encodeBase64(data >> 36);
177: buf[6] = encodeBase64(data >> 30);
178: buf[7] = encodeBase64(data >> 24);
179: buf[8] = encodeBase64(data >> 18);
180:
181: buf[9] = encodeBase64(data >> 12);
182: buf[10] = encodeBase64(data >> 6);
183: buf[11] = encodeBase64(data);
184: buf[12] = '"';
185:
186: return buf;
187: }
188:
189: private static char encodeBase64(long d) {
190: d &= 0x3f;
191: if (d < 26)
192: return (char) (d + 'A');
193: else if (d < 52)
194: return (char) (d + 'a' - 26);
195: else if (d < 62)
196: return (char) (d + '0' - 52);
197: else if (d == 62)
198: return '+';
199: else
200: return '/';
201: }
202:
203: /**
204: * Returns the last-modified time for the page for caching.
205: * If at all possible, pages should override <code>getLastModified</code>
206: * to improve performance. Servlet engines like Resin can
207: * cache the results of the page, resulting in near-static performance.
208: *
209: * @param req the request
210: * @return the last-modified time of the page.
211: */
212: protected long getLastModified(HttpServletRequest req) {
213: return -1;
214: }
215:
216: /**
217: * Process a HEAD request. By default, uses doGet.
218: *
219: * @param req the client request
220: * @param res response to the client
221: */
222: protected void doHead(HttpServletRequest req,
223: HttpServletResponse res) throws ServletException,
224: IOException {
225: doGet(req, res);
226: }
227:
228: /**
229: * Process a GET or HEAD request
230: *
231: * @param req the client request
232: * @param res response to the client
233: */
234: protected void doGet(HttpServletRequest req, HttpServletResponse res)
235: throws ServletException, IOException {
236: String protocol = req.getProtocol();
237: String msg = req.getMethod() + " not supported";
238: if (protocol.endsWith("1.1")) {
239: res.sendError(HttpServletResponse.SC_METHOD_NOT_ALLOWED,
240: msg);
241: } else {
242: res.sendError(HttpServletResponse.SC_BAD_REQUEST, msg);
243: }
244: }
245:
246: /**
247: * Process a POST request
248: *
249: * @param req the client request
250: * @param res response to the client
251: */
252: protected void doPost(HttpServletRequest req,
253: HttpServletResponse res) throws ServletException,
254: IOException {
255: String protocol = req.getProtocol();
256: String msg = req.getMethod() + " not supported";
257: if (protocol.endsWith("1.1")) {
258: res.sendError(HttpServletResponse.SC_METHOD_NOT_ALLOWED,
259: msg);
260: } else {
261: res.sendError(HttpServletResponse.SC_BAD_REQUEST, msg);
262: }
263: }
264:
265: /**
266: * Process a PUT request
267: *
268: * @param req the client request
269: * @param res response to the client
270: */
271: protected void doPut(HttpServletRequest req, HttpServletResponse res)
272: throws ServletException, IOException {
273: String protocol = req.getProtocol();
274: String msg = req.getMethod() + " not supported";
275: if (protocol.endsWith("1.1")) {
276: res.sendError(HttpServletResponse.SC_METHOD_NOT_ALLOWED,
277: msg);
278: } else {
279: res.sendError(HttpServletResponse.SC_BAD_REQUEST, msg);
280: }
281: }
282:
283: /**
284: * Process a DELETE request
285: *
286: * @param req the client request
287: * @param res response to the client
288: */
289: protected void doDelete(HttpServletRequest req,
290: HttpServletResponse res) throws ServletException,
291: IOException {
292: String protocol = req.getProtocol();
293: String msg = req.getMethod() + " not supported";
294: if (protocol.endsWith("1.1")) {
295: res.sendError(HttpServletResponse.SC_METHOD_NOT_ALLOWED,
296: msg);
297: } else {
298: res.sendError(HttpServletResponse.SC_BAD_REQUEST, msg);
299: }
300: }
301:
302: /**
303: * Process an OPTIONS request
304: *
305: * @param req the client request
306: * @param res response to the client
307: */
308: protected void doOptions(HttpServletRequest req,
309: HttpServletResponse res) throws ServletException,
310: IOException {
311: String protocol = req.getProtocol();
312: String msg = req.getMethod() + " not supported";
313: if (protocol.endsWith("1.1")) {
314: res.sendError(HttpServletResponse.SC_METHOD_NOT_ALLOWED,
315: msg);
316: } else {
317: res.sendError(HttpServletResponse.SC_BAD_REQUEST, msg);
318: }
319: }
320:
321: /**
322: * Process a TRACE request
323: *
324: * @param req the client request
325: * @param res response to the client
326: */
327: protected void doTrace(HttpServletRequest req,
328: HttpServletResponse res) throws ServletException,
329: IOException {
330: String protocol = req.getProtocol();
331: String msg = req.getMethod() + " not supported";
332: if (protocol.endsWith("1.1")) {
333: res.sendError(HttpServletResponse.SC_METHOD_NOT_ALLOWED,
334: msg);
335: } else {
336: res.sendError(HttpServletResponse.SC_BAD_REQUEST, msg);
337: }
338: }
339: }
|