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: * Authors: Eric Wagner <eric@xcf.berkeley.edu>
022: * Matt Welsh <mdw@cs.berkeley.edu>
023: */
024:
025: package seda.apps.Haboob.hdapi;
026:
027: import seda.apps.Haboob.hdapi.*;
028:
029: import seda.apps.Haboob.*;
030: import seda.apps.Haboob.http.*;
031: import seda.sandStorm.api.*;
032: import seda.sandStorm.core.*;
033: import seda.sandStorm.lib.http.*;
034: import seda.sandStorm.lib.aDisk.*;
035: import seda.util.*;
036: import java.io.*;
037: import java.util.*;
038:
039: public class DynamicHttp implements EventHandlerIF, HaboobConst {
040:
041: private static final boolean DEBUG = false;
042:
043: // Whether to close the HTTP connection after each request
044: private static final boolean CLOSE_CONNECTION = false;
045:
046: // Whether each URL should be handled by its own stage
047: private static final boolean SEPARATE_STAGES = true;
048:
049: private SinkIF mysink;
050: private static ConfigDataIF config;
051: private static SinkIF mainsink;
052: private static Hashtable dynPages, handlerCache, stageCache;
053: private String myurl;
054:
055: public DynamicHttp() {
056: myurl = null;
057: }
058:
059: private DynamicHttp(String myurl) {
060: this .myurl = myurl;
061: }
062:
063: public void init(ConfigDataIF config) throws Exception {
064: this .config = config;
065: mysink = config.getStage().getSink();
066:
067: if (myurl == null) {
068: mainsink = mysink;
069:
070: /* Read HDAPI configuration file */
071: String conffname = config.getString("configfile");
072: if (conffname == null)
073: throw new IllegalArgumentException(
074: "Must specify DynamicHttp.configfile");
075: AFile af = new AFile(conffname, mysink, false, true);
076: BufferElement configfile = new BufferElement((int) af
077: .stat().length);
078:
079: dynPages = new Hashtable();
080: handlerCache = new Hashtable();
081: stageCache = new Hashtable();
082: af.read(configfile);
083:
084: System.err.println("DynamicHttp: Started");
085:
086: } else {
087: System.err.println("DynamicHttp handlerStage [" + myurl
088: + "]: Started");
089: }
090: }
091:
092: public void destroy() {
093: }
094:
095: /**
096: * Handle the given request, passing it to the appropriate handler
097: * (and stage, if necessary). Returns true if the request can be
098: * processed, or false if the request was not for a dynamic URL.
099: */
100: public static boolean handleRequest(httpRequest req)
101: throws Exception {
102: HaboobStats.numRequests++;
103: String url = req.getURL();
104: if (dynPages.get(url) == null)
105: return false;
106:
107: if (SEPARATE_STAGES) {
108: SinkIF thesink;
109: synchronized (stageCache) {
110: thesink = (SinkIF) stageCache.get(url);
111: if (thesink == null)
112: thesink = makeStage(url);
113: }
114: thesink.enqueue(req);
115: } else {
116: mainsink.enqueue(req);
117: }
118: return true;
119: }
120:
121: public void handleEvent(QueueElementIF item) {
122: if (DEBUG) {
123: if (myurl == null)
124: System.err.println("DynamicHttp: GOT QEL: " + item);
125: else
126: System.err.println("DynamicHttp [" + myurl
127: + "]: GOT QEL: " + item);
128: }
129:
130: if (item instanceof httpRequest) {
131: httpRequest req = (httpRequest) item;
132: try {
133: doRequest(req);
134: } catch (Exception e) {
135: HttpSend.sendResponse(new httpResponder(
136: new httpInternalServerErrorResponse(req,
137: "The following exception occurred:<p><pre>"
138: + e + "</pre>"), req, true));
139: }
140:
141: } else if (item instanceof AFileIOCompleted) {
142: AFileIOCompleted comp = (AFileIOCompleted) item;
143: process_config(comp);
144: System.err
145: .println("DynamicHttp: finished reading config file");
146:
147: } else {
148: System.err
149: .println("DynamicHttp: Don't know what to do with "
150: + item);
151: }
152: }
153:
154: public void handleEvents(QueueElementIF items[]) {
155: for (int i = 0; i < items.length; i++) {
156: handleEvent(items[i]);
157: }
158: }
159:
160: private void doRequest(httpRequest req) {
161: String url;
162: String classname;
163:
164: url = req.getURL();
165: classname = (String) dynPages.get(url);
166:
167: // No class registered for this URL -- shouldn't happen as we are
168: // screened by handleRequest()
169: if (classname == null) {
170: HttpSend
171: .sendResponse(new httpResponder(
172: new httpInternalServerErrorResponse(
173: req,
174: "Got dynamic URL with no class -- this is a bug, please contact mdw@cs.berkeley.edu"),
175: req, true));
176: System.err
177: .println("DynamicHttp: Warning: Got dynamic URL with no class: "
178: + url);
179: return;
180: }
181:
182: try {
183: handlerPool pool;
184: synchronized (this ) {
185: pool = (handlerPool) handlerCache.get(classname);
186: if (pool == null) {
187: try {
188: pool = new handlerPool(classname);
189: System.err.println("DynamicHttp: Loaded class "
190: + classname + " for url " + url);
191: } catch (ClassNotFoundException cnfe) {
192: HttpSend.sendResponse(new httpResponder(
193: new httpInternalServerErrorResponse(
194: req, cnfe.toString()), req,
195: true));
196: return;
197: }
198: handlerCache.put(classname, pool);
199: }
200: }
201:
202: httpRequestHandlerIF handler = pool.getHandler();
203: httpResponse resp;
204: resp = handler.handleRequest(req);
205:
206: httpResponder respd = new httpResponder(resp, req,
207: CLOSE_CONNECTION);
208: HttpSend.sendResponse(respd);
209: pool.doneWithHandler(handler);
210: } catch (Exception e) {
211: HttpSend.sendResponse(new httpResponder(
212: new httpInternalServerErrorResponse(req, e
213: .toString()), req, true));
214: return;
215: }
216: }
217:
218: class handlerPool {
219: Class theclass;
220: Vector pool;
221:
222: handlerPool(String classname) throws ClassNotFoundException {
223: this .theclass = Class.forName(classname);
224: this .pool = new Vector();
225: }
226:
227: httpRequestHandlerIF getHandler()
228: throws InstantiationException, IllegalAccessException {
229: httpRequestHandlerIF handler;
230: synchronized (pool) {
231: if (pool.size() == 0) {
232: handler = (httpRequestHandlerIF) theclass
233: .newInstance();
234: } else {
235: handler = (httpRequestHandlerIF) pool.remove(pool
236: .size() - 1);
237: }
238: }
239: return handler;
240: }
241:
242: void doneWithHandler(httpRequestHandlerIF handler) {
243: synchronized (pool) {
244: pool.addElement(handler);
245: }
246: }
247: }
248:
249: private static SinkIF makeStage(String url) throws Exception {
250: StageIF thestage;
251: thestage = config.getManager()
252: .createStage("DynamicHttp [" + url + "]",
253: new DynamicHttp(url), null);
254: stageCache.put(url, thestage.getSink());
255: return thestage.getSink();
256: }
257:
258: private void addURL(String url, String classname) {
259: System.err.println("DynamicHttp: Adding URL [" + url
260: + "] class [" + classname + "]");
261: dynPages.put(url, classname);
262: }
263:
264: private void process_config(AFileIOCompleted comp) {
265: BufferElement conf_buf = ((AFileReadRequest) comp.getRequest())
266: .getBuffer();
267:
268: String s = new String(conf_buf.data);
269: BufferedReader buf_reader = new BufferedReader(
270: new StringReader(s));
271: String tmp;
272: try {
273: while ((tmp = buf_reader.readLine()) != null) {
274: if (tmp.startsWith("#"))
275: continue; // Skip comment lines
276: StringTokenizer st = new StringTokenizer(tmp);
277: String url, class_name;
278: if (st.hasMoreElements()) {
279: url = st.nextToken();
280: } else {
281: // Ignore empty lines
282: continue;
283: }
284: if (st.hasMoreElements()) {
285: class_name = st.nextToken();
286: } else {
287: System.out
288: .println("DynamicHttp: Bad line format in configuration file: "
289: + tmp);
290: continue;
291: }
292:
293: addURL(url, class_name);
294: }
295: } catch (IOException ioe) {
296: System.err
297: .println("DynamicHttp: IOException processing configuration file:"
298: + ioe);
299: }
300: }
301: }
|