001: /*
002: * ChainHandler.java
003: *
004: * Brazil project web application Framework,
005: * export version: 1.1
006: * Copyright (c) 1998-2000 Sun Microsystems, Inc.
007: *
008: * Sun Public License Notice
009: *
010: * The contents of this file are subject to the Sun Public License Version
011: * 1.0 (the "License"). You may not use this file except in compliance with
012: * the License. A copy of the License is included as the file "license.terms",
013: * and also available at http://www.sun.com/
014: *
015: * The Original Code is from:
016: * Brazil project web application Framework release 1.1.
017: * The Initial Developer of the Original Code is: suhler.
018: * Portions created by suhler are Copyright (C) Sun Microsystems, Inc.
019: * All Rights Reserved.
020: *
021: * Contributor(s): cstevens, rinaldo, suhler.
022: *
023: * Version: 1.25
024: * Created by suhler on 98/09/14
025: * Last modified by suhler on 00/12/11 13:32:06
026: */
027:
028: package sunlabs.brazil.server;
029:
030: import java.io.IOException;
031: import java.util.Properties;
032: import java.util.StringTokenizer;
033: import java.util.Vector;
034:
035: /**
036: * Allows multiple handlers to be invoked sequentially for a single HTTP
037: * request. A list of handlers is supplied when this
038: * <code>ChainHandler</code> is initialized. When an HTTP request is
039: * received by this <code>ChainHandler</code>, each of the handlers from the
040: * list is called in turn until one of them responds and returns
041: * <code>true</code>.
042: * <p>
043: * A useful trick is that some handlers can be run by a
044: * <code>ChainHandler</code> for their side effects. The handler can modify
045: * the <code>Request</code> object and then return <code>false</code>; the
046: * next handler in the list will get a crack at the modified request.
047: * <p>
048: * The following configuration parameters eare used to initialize this
049: * <code>Handler</code>: <dl class=props>
050: *
051: * <dt> <code>handlers</code>
052: * <dd> A list of <code>Handler</code> names that will be invoked in the
053: * given order to handle the request. These are considered the
054: * "wrapped" handlers. These handlers will all be initialized at
055: * startup by {@link #init}. For each name in the list, the property
056: * <code><i>name</i>.class</code> is examined to determine which class
057: * to use for this handler. Then <code>name</code> is used as the prefix
058: * in the handler's init() method.
059: * <dt> <code>report</code>
060: * <dd> If set, this property will be set to the name of the handler
061: * that handled the request (e.g. returned true).
062: * <dt> <code>exitOnError</code>
063: * <dd> If set, the server's <code>initFailure</code> will set
064: * any of the handlers fail to
065: * initialize. No handler prefix is required.
066: * </dl>
067: *
068: * @see Handler
069: *
070: * @author Stephen Uhler (stephen.uhler@sun.com)
071: * @author Colin Stevens (colin.stevens@sun.com)
072: * @version 1.25, 00/12/11
073: */
074: public class ChainHandler implements Handler {
075: private static final String HANDLERS = "handlers";
076: private static final String EXIT_ON_ERROR = "exitOnError";
077:
078: /**
079: * The array of handlers that will be invoked to handle
080: * the request.
081: */
082: public Handler[] handlers;
083:
084: /**
085: * The names of the above <code>handlers</code> as specified by the
086: * configuration parameters. Used for logging the names of each
087: * <code>Handler</code> as it is invoked.
088: */
089: public String[] names;
090:
091: /**
092: * The prefix used to initialize this <code>ChainHandler</code>, used
093: * for logging.
094: */
095: public String prefix;
096:
097: /**
098: * The name (if any) of the property to receive the name of the handler
099: * that handled the request.
100: */
101: public String report;
102:
103: /**
104: * A flag to require the successfull initialization of all
105: * handlers.
106: */
107: public boolean exitOnError = false;
108:
109: /**
110: * Initializes this <code>ChainHandler</code> by initializing all the
111: * "wrapped" handlers in the list of handlers. If a wrapped handler
112: * cannot be initialized, this method logs a message and skips it. If no
113: * handlers were specified, or no handlers were successfully initialized,
114: * then the initialization of this <code>ChainHandler</code> is
115: * considered to have failed.
116: *
117: * @param server
118: * The HTTP server that created this <code>ChainHandler</code>.
119: *
120: * @param prefix
121: * The prefix for this <code>ChainHandler</code>'s properties.
122: *
123: * @return <code>true</code> if at least one of the wrapped handlers
124: * was successfully initialized.
125: */
126: public boolean init(Server server, String prefix) {
127: this .prefix = prefix;
128:
129: Properties props = server.props;
130:
131: exitOnError = (props.getProperty(prefix + EXIT_ON_ERROR, (props
132: .getProperty(EXIT_ON_ERROR))) != null);
133: String str = props.getProperty(prefix + HANDLERS, "");
134: report = props.getProperty(prefix + "report");
135: StringTokenizer names = new StringTokenizer(str);
136:
137: Vector handlerVec = new Vector();
138: Vector nameVec = new Vector();
139:
140: while (names.hasMoreTokens()) {
141: String name = names.nextToken();
142: server.log(Server.LOG_DIAGNOSTIC, prefix,
143: "starting handler: " + name);
144:
145: Handler h = initHandler(server, prefix, name);
146: if (h != null) {
147: handlerVec.addElement(h);
148: nameVec.addElement(name);
149: } else if (exitOnError) {
150: server.initFailure = true;
151: System.err
152: .println("Handler initialization failure in ("
153: + prefix + ") for handler: " + name);
154: }
155: }
156: if (handlerVec.size() == 0) {
157: server.log(Server.LOG_DIAGNOSTIC, prefix, "no handlers");
158: return false;
159: }
160:
161: this .handlers = new Handler[handlerVec.size()];
162: handlerVec.copyInto(this .handlers);
163:
164: this .names = new String[nameVec.size()];
165: nameVec.copyInto(this .names);
166:
167: return true;
168: }
169:
170: /**
171: * Helper function that allocates and initializes a new
172: * <code>Handler</code>, given its name. In addition to the
173: * <code>ChainHandler</code>, several other handlers
174: * contain embedded <code>Handler</code>s -- this method can be
175: * used to initialize those embedded <code>Handler</code>s.
176: * <p>
177: * If there is an error initializing the specified <code>Handler</code>,
178: * this method will log a dignostic message to the server and return
179: * <code>null</code>. This happens if the specified class cannot be
180: * found or instantiated, if the specified class is not actually a
181: * <code>Handler</code>, if the <code>Handler.init</code> method
182: * returns <code>false</code>, or if there is any other exception.
183: *
184: * @param server
185: * The server that will own the new <code>Handler</code>.
186: * Mainly used for the server's properties, which contain
187: * the configuration parameters for the new handler.
188: *
189: * @param prefix
190: * The prefix in the server's properties for the new
191: * <code>Handler</code>'s configuration parameters. The prefix
192: * is prepended to the configuation parameters used by the
193: * <code>Handler</code>.
194: *
195: * @param name
196: * The name of the new <code>Handler</code>. The name can
197: * be one of two forms: <ol>
198: *
199: * <li> The name of the Java class for the <code>Handler</code>.
200: * This <code>Handler</code> will be initialized using the
201: * <code>prefix</code> specified above.
202: *
203: * <li> A symbolic <code>name</code>. The configuration
204: * parameter <code><i>name</i>.class</code> is the name of the
205: * Java class for the <code>Handler</code>. The above
206: * <code>prefix</code> will be ignored and this
207: * <code>Handler</code> will be initialized with the prefix
208: * "<code><i>name</i>.</code>" (the symbolic name followed by
209: * a ".").
210: * </ol>
211: *
212: * @return The newly allocated <code>Handler</code>, or <code>null</code>
213: * if the <code>Handler</code> could not be allocated.
214: */
215: public static Handler initHandler(Server server, String prefix,
216: String name) {
217: String className = server.props.getProperty(name + ".class");
218: if (className == null) {
219: className = name;
220: } else {
221: prefix = null;
222: }
223: if (prefix == null) {
224: prefix = name + ".";
225: }
226:
227: try {
228: Handler h = (Handler) Class.forName(className)
229: .newInstance();
230: if (h.init(server, prefix)) {
231: return h;
232: }
233: server.log(Server.LOG_WARNING, name, "did not initialize");
234: } catch (ClassNotFoundException e) {
235: server.log(Server.LOG_WARNING, className, "no such class");
236: } catch (IllegalArgumentException e) {
237: server.log(Server.LOG_WARNING, className, "no such class");
238: } catch (ClassCastException e) {
239: server.log(Server.LOG_WARNING, className,
240: "is not a Handler");
241: } catch (Exception e) {
242: server.log(Server.LOG_WARNING, name, "error initializing");
243: e.printStackTrace();
244: }
245: return null;
246: }
247:
248: /**
249: * Calls each of the <code>Handler</code>s in turn until one of them
250: * returns <code>true</code>.
251: *
252: * @param request
253: * The HTTP request.
254: *
255: * @return <code>true</code> if one of the <code>Handler</code>s returns
256: * <code>true</code>, <code>false</code> otherwise.
257: *
258: * @throws IOException
259: * if one of the <code>Handler</code>s throws an
260: * <code>IOException</code> while responding.
261: */
262: public boolean respond(Request request) throws IOException {
263: for (int i = 0; i < handlers.length; i++) {
264: request.log(Server.LOG_DIAGNOSTIC, prefix,
265: "invoking handler: " + names[i]);
266:
267: if (handlers[i].respond(request)) {
268: if (report != null) {
269: request.props.put(report, names[i]);
270: }
271: return true;
272: }
273: }
274: return false;
275: }
276: }
|