001: /*
002: * Copyright (c) 2001 by Matt Welsh and The Regents of the University of
003: * California. All rights reserved.
004: *
005: * Permission to use, copy, modify, and distribute this software and its
006: * documentation for any purpose, without fee, and without written agreement is
007: * hereby granted, provided that the above copyright notice and the following
008: * two paragraphs appear in all copies of this software.
009: *
010: * IN NO EVENT SHALL THE UNIVERSITY OF CALIFORNIA BE LIABLE TO ANY PARTY FOR
011: * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES ARISING OUT
012: * OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF THE UNIVERSITY OF
013: * CALIFORNIA HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
014: *
015: * THE UNIVERSITY OF CALIFORNIA SPECIFICALLY DISCLAIMS ANY WARRANTIES,
016: * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
017: * AND FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS
018: * ON AN "AS IS" BASIS, AND THE UNIVERSITY OF CALIFORNIA HAS NO OBLIGATION TO
019: * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
020: *
021: * Author: Matt Welsh <mdw@cs.berkeley.edu>
022: *
023: */
024:
025: package seda.apps.Haboob.cache;
026:
027: import seda.apps.Haboob.*;
028: import seda.sandStorm.api.*;
029: import seda.sandStorm.core.*;
030: import seda.sandStorm.lib.http.*;
031: import seda.sandStorm.lib.aSocket.*;
032: import seda.sandStorm.lib.aDisk.*;
033: import seda.util.*;
034: import java.io.*;
035: import java.util.*;
036:
037: /**
038: * This implementation of the Haboob "cache" does not actually
039: * cache pages, but rather always reads them from files using
040: * the Sandstorm AFile interface.
041: */
042: public class AFileRead implements EventHandlerIF, HaboobConst {
043:
044: private static final boolean DEBUG = false;
045: private static final boolean PROFILE = true;
046:
047: // Don't actually read file; just store empty buffer in cache
048: private static final boolean DEBUG_NO_FILE_READ = true;
049: // Don't even stat file; just allocate buffer of fixed size
050: private static final boolean DEBUG_NO_FILE_READ_SAMESIZE = true;
051: private static final int DEBUG_NO_FILE_READ_SAMESIZE_SIZE = 8192;
052:
053: private String DEFAULT_URL;
054: private String ROOT_DIR;
055:
056: private SinkIF mysink, sendSink;
057: private Hashtable aFileTbl;
058:
059: private Hashtable mimeTbl; // Filename extension -> MIME type
060: private static final String defaultMimeType = "text/plain";
061:
062: public void init(ConfigDataIF config) throws Exception {
063: mysink = config.getStage().getSink();
064: sendSink = config.getManager().getStage(HTTP_SEND_STAGE)
065: .getSink();
066: aFileTbl = new Hashtable();
067:
068: mimeTbl = new Hashtable();
069: mimeTbl.put(".html", "text/html");
070: mimeTbl.put(".gif", "image/gif");
071: mimeTbl.put(".jpg", "image/jpeg");
072: mimeTbl.put(".jpeg", "image/jpeg");
073: mimeTbl.put(".pdf", "application/pdf");
074:
075: DEFAULT_URL = config.getString("defaultURL");
076: if (DEFAULT_URL == null)
077: throw new IllegalArgumentException(
078: "Must specify defaultURL");
079: ROOT_DIR = config.getString("rootDir");
080: if (ROOT_DIR == null)
081: throw new IllegalArgumentException("Must specify rootDir");
082: }
083:
084: public void destroy() {
085: }
086:
087: public void handleEvent(QueueElementIF item) {
088: if (DEBUG)
089: System.err.println("AFileRead: GOT QEL: " + item);
090:
091: if (item instanceof httpRequest) {
092: HaboobStats.numRequests++;
093:
094: httpRequest req = (httpRequest) item;
095: if (req.getRequest() != httpRequest.REQUEST_GET) {
096: HaboobStats.numErrors++;
097: sendSink
098: .enqueue_lossy(new httpResponder(
099: new httpBadRequestResponse(req,
100: "Only GET requests supported at this time"),
101: req, true));
102: return;
103: }
104:
105: handleRequest(req);
106:
107: } else if (item instanceof AFileIOCompleted) {
108: AFileIOCompleted comp = (AFileIOCompleted) item;
109: AFile af = comp.getFile();
110: outstandingRead or = (outstandingRead) aFileTbl.get(af);
111: if (or == null) {
112: throw new RuntimeException(
113: "AFileRead: WARNING: Got AFileIOCompleted for non-entry: "
114: + comp);
115: }
116: if (comp.sizeCompleted != or.length) {
117: throw new RuntimeException("AFileRead: WARNING: Got "
118: + comp.sizeCompleted
119: + " bytes read, expecting " + or.length);
120: }
121: af.close();
122: aFileTbl.remove(af);
123: or.done();
124:
125: } else if (item instanceof SinkClosedEvent) {
126: // Ignore
127:
128: } else {
129: System.err.println("AFileRead: Got unknown event type: "
130: + item);
131: }
132:
133: }
134:
135: public void handleEvents(QueueElementIF items[]) {
136: for (int i = 0; i < items.length; i++) {
137: handleEvent(items[i]);
138: }
139: }
140:
141: private void handleRequest(httpRequest req) {
142: String url;
143: String fname;
144:
145: url = req.getURL();
146: fname = ROOT_DIR + url;
147:
148: AFile af = null;
149: AFileStat stat;
150: BufferElement payload = null;
151: httpOKResponse resp;
152: outstandingRead or;
153:
154: if (DEBUG_NO_FILE_READ && DEBUG_NO_FILE_READ_SAMESIZE) {
155: resp = new httpOKResponse(getMimeType(fname),
156: DEBUG_NO_FILE_READ_SAMESIZE_SIZE);
157: or = new outstandingRead(req, resp, null,
158: DEBUG_NO_FILE_READ_SAMESIZE_SIZE);
159:
160: } else {
161:
162: // Open file and stat it to determine size
163: try {
164: af = new AFile(fname, mysink, false, true);
165: stat = af.stat();
166: if (stat.isDirectory) {
167: af.close();
168: fname = fname + "/" + DEFAULT_URL;
169: af = new AFile(fname, mysink, false, true);
170: stat = af.stat();
171: }
172:
173: resp = new httpOKResponse(getMimeType(fname),
174: (int) stat.length);
175: payload = resp.getPayload();
176:
177: } catch (IOException ioe) {
178: // File not found
179: System.err.println("AFileRead: Could not open file "
180: + fname + ": " + ioe);
181: HaboobStats.numErrors++;
182: httpNotFoundResponse notfound = new httpNotFoundResponse(
183: req, ioe.getMessage());
184: sendSink.enqueue_lossy(new httpResponder(notfound, req,
185: true));
186: return;
187: }
188:
189: or = new outstandingRead(req, resp, af, (int) stat.length);
190: }
191:
192: if (!DEBUG_NO_FILE_READ || !DEBUG_NO_FILE_READ_SAMESIZE) {
193: aFileTbl.put(af, or);
194: }
195:
196: if (!DEBUG_NO_FILE_READ) {
197: try {
198: af.read(payload);
199: } catch (SinkException se) {
200: // XXX Should not really happen
201: System.err
202: .println("AFileRead: Got SinkException attempting read on "
203: + fname + ": " + se);
204: aFileTbl.remove(af);
205: af.close();
206: HaboobStats.numErrors++;
207: httpNotFoundResponse notfound = new httpNotFoundResponse(
208: req, se.getMessage());
209: sendSink.enqueue_lossy(new httpResponder(notfound, req,
210: true));
211: return;
212: }
213: } else {
214: // Pretend we got it already
215: if (!DEBUG_NO_FILE_READ_SAMESIZE) {
216: af.close();
217: aFileTbl.remove(af);
218: }
219: or.done();
220: }
221: }
222:
223: private String getMimeType(String url) {
224: Enumeration e = mimeTbl.keys();
225: while (e.hasMoreElements()) {
226: String key = (String) e.nextElement();
227: if (url.endsWith(key))
228: return (String) mimeTbl.get(key);
229: }
230: return defaultMimeType;
231: }
232:
233: private class outstandingRead {
234: httpOKResponse response;
235: int length;
236: boolean pending;
237: int size;
238: BufferElement buf;
239: AFile af;
240: ssLinkedList waiting;
241: String url;
242:
243: outstandingRead(httpRequest req, httpOKResponse resp, AFile af,
244: int size) {
245: this .response = resp;
246: this .length = resp.getPayload().size;
247: this .url = req.getURL();
248: this .af = af;
249: this .size = size;
250: pending = true;
251: waiting = new ssLinkedList();
252: addWaiter(req);
253: }
254:
255: synchronized void addWaiter(httpRequest req) {
256: waiting.add_to_tail(req);
257: }
258:
259: // Send response to all waiters when done reading
260: synchronized void done() {
261: if (DEBUG)
262: System.err.println("AFileRead: Done with file read");
263: pending = false;
264: httpRequest waiter;
265:
266: while ((waiter = (httpRequest) waiting.remove_head()) != null) {
267: httpResponder respd = new httpResponder(response,
268: waiter);
269: sendSink.enqueue_lossy(respd);
270: }
271: }
272: }
273:
274: }
|