/* * ChainHandler.java * * Brazil project web application toolkit, * export version: 2.3 * Copyright (c) 1998-2009 Sun Microsystems, Inc. * * Sun Public License Notice * * The contents of this file are subject to the Sun Public License Version * 1.0 (the "License"). You may not use this file except in compliance with * the License. A copy of the License is included as the file "license.terms", * and also available at http://www.sun.com/ * * The Original Code is from: * Brazil project web application toolkit release 2.3. * The Initial Developer of the Original Code is: suhler. * Portions created by suhler are Copyright (C) Sun Microsystems, Inc. * All Rights Reserved. * * Contributor(s): cstevens, rinaldo, suhler. * * Version: 2.6 * Created by suhler on 98/09/14 * Last modified by suhler on 09/02/12 10:37:13 * * Version Histories: * * 2.6 09/02/12-10:37:13 (suhler) * add Information messate when "report" is set * * 2.5 06/11/13-15:06:09 (suhler) * move MatchString to package "util" from "handler" * * 2.4 06/11/13-10:41:04 (suhler) * added MatchString capabilities instead of just prefix * * 2.3 04/11/30-19:31:16 (suhler) * better checking for invalid classes * * 2.2 04/11/30-15:19:41 (suhler) * fixed sccs version string * * 2.1 02/10/01-16:34:49 (suhler) * version change * * 1.29 02/06/13-18:26:54 (suhler) * bogus error message * * 1.28 01/08/03-18:23:11 (suhler) * remove training ws from classnames before trying to instantiate * * 1.27 01/06/13-09:22:06 (suhler) * \add option for url prefix * * 1.26 01/03/05-16:20:08 (cstevens) * Log message consistency * * 1.25 00/12/11-13:32:06 (suhler) * add class=props for automatic property extraction * * 1.24 00/10/31-10:20:18 (suhler) * doc fixes * * 1.23 00/10/17-09:30:24 (suhler) * Added "exitOnError" property to shut the server down on initialization failures * * 1.22 00/10/12-11:31:35 (suhler) * propagate handler generating response, for experimental logging stuff * * 1.21 00/03/29-16:22:48 (cstevens) * ChainHandler was missing documentation. * * 1.20 00/03/10-17:11:04 (cstevens) * Removing unused member variables * * 1.19 00/03/02-17:55:46 (cstevens) * If handler threw an exception while being initialized by ChainHandler, the * ChainHandler would print an error message that there was an error * initializing the handler, but still dispatch HTTP requests to it. * * 1.18 00/02/11-09:19:33 (suhler) * diagnostics * * 1.17 99/11/17-15:36:39 (cstevens) * documentation * * 1.16 99/11/16-19:08:54 (cstevens) * expunge Server.initHandler and Server.initObject. * * 1.15 99/10/14-14:57:30 (cstevens) * resolve wilcard imports. * * 1.14 99/10/14-12:50:26 (cstevens) * crashed if no handlers specified. * * 1.13 99/10/01-13:06:19 (cstevens) * Getting better with server.initHandler(); * * 1.12 99/10/01-11:26:54 (cstevens) * Change logging to show prefix of Handler generating the log message. * * 1.11 99/09/30-12:10:19 (cstevens) * better logging * * 1.10 99/09/29-16:10:03 (cstevens) * Consistent way of initializing Handlers and other things that want to get * attributes from the config file. Convenience method that constructs the * object, sets (via reflection) all the variables in the object that correspond * to values specified in the config file, and then calls init() on the object. * * 1.9 99/03/30-09:22:46 (suhler) * documentation updates * * 1.8 99/02/17-17:13:52 (cstevens) * Consistent printing of Handlers on startup. * * 1.7 98/11/30-15:47:46 (suhler) * remove redundant log messages * * 1.6 98/11/02-23:37:44 (rinaldo) * Better Diagnostics * * 1.5 98/10/30-21:20:33 (rinaldo) * * 1.4 98/10/22-09:22:31 (suhler) * - Rationalized diagnostic messages * - Made sub-classable to enable dynamic handler manipulation * . * * 1.3 98/10/20-22:39:09 (rinaldo) * Diagnostics * * 1.2 98/09/21-14:50:28 (suhler) * changed the package names * * 1.2 98/09/14-18:03:04 (Codemgr) * SunPro Code Manager data about conflicts, renames, etc... * Name history : 2 1 server/ChainHandler.java * Name history : 1 0 ChainHandler.java * * 1.1 98/09/14-18:03:03 (suhler) * date and time created 98/09/14 18:03:03 by suhler * */ package sunlabs.brazil.server; import java.io.IOException; import java.util.Properties; import java.util.StringTokenizer; import java.util.Vector; import sunlabs.brazil.util.MatchString; /** * Allows multiple handlers to be invoked sequentially for a single HTTP * request. A list of handlers is supplied when this * ChainHandler is initialized. When an HTTP request is * received by this ChainHandler, each of the handlers from the * list is called in turn until one of them responds and returns * true. *

* A useful trick is that some handlers can be run by a * ChainHandler for their side effects. The handler can modify * the Request object and then return false; the * next handler in the list will get a crack at the modified request. *

* The following configuration parameters eare used to initialize this * Handler:

* *
handlers *
A list of Handler names that will be invoked in the * given order to handle the request. These are considered the * "wrapped" handlers. These handlers will all be initialized at * startup by {@link #init}. For each name in the list, the property * name.class is examined to determine which class * to use for this handler. Then name is used as the prefix * in the handler's init() method. *
report *
If set, this property will be set to the name of the handler * that handled the request (e.g. returned true). *
exitOnError *
If set, the server's initFailure will set * any of the handlers fail to * initialize. No handler prefix is required. *
prefix, suffix, glob, match *
Specify the URL that triggers this handler. *
* * @see Handler * * @author Stephen Uhler (stephen.uhler@sun.com) * @author Colin Stevens (colin.stevens@sun.com) * @version 2.6 */ public class ChainHandler implements Handler { private static final String HANDLERS = "handlers"; private static final String EXIT_ON_ERROR = "exitOnError"; /** * The array of handlers that will be invoked to handle * the request. */ public Handler[] handlers; /** * The names of the above handlers as specified by the * configuration parameters. Used for logging the names of each * Handler as it is invoked. */ public String[] names; /** * The prefix used to initialize this ChainHandler, used * for logging. */ public String prefix; /** * The URL that must match for this handler to run */ public MatchString isMine; /** * The name (if any) of the property to receive the name of the handler * that handled the request. */ public String report; /** * A flag to require the successfull initialization of all * handlers. */ public boolean exitOnError = false; /** * Initializes this ChainHandler by initializing all the * "wrapped" handlers in the list of handlers. If a wrapped handler * cannot be initialized, this method logs a message and skips it. If no * handlers were specified, or no handlers were successfully initialized, * then the initialization of this ChainHandler is * considered to have failed. * * @param server * The HTTP server that created this ChainHandler. * * @param prefix * The prefix for this ChainHandler's properties. * * @return true if at least one of the wrapped handlers * was successfully initialized. */ public boolean init(Server server, String prefix) { this.prefix = prefix; isMine = new MatchString(prefix, server.props); Properties props = server.props; exitOnError = (props.getProperty(prefix + EXIT_ON_ERROR, (props.getProperty(EXIT_ON_ERROR))) != null); String str = props.getProperty(prefix + HANDLERS, ""); report = props.getProperty(prefix + "report"); StringTokenizer names = new StringTokenizer(str); Vector handlerVec = new Vector(); Vector nameVec = new Vector(); while (names.hasMoreTokens()) { String name = names.nextToken(); server.log(Server.LOG_DIAGNOSTIC, prefix, "starting handler: " + name); Handler h = initHandler(server, prefix, name); if (h != null) { handlerVec.addElement(h); nameVec.addElement(name); } else if (exitOnError) { server.initFailure=true; System.err.println("Handler initialization failure in (" + prefix + ") for handler: " + name); } } if (handlerVec.size() == 0) { server.log(Server.LOG_DIAGNOSTIC, prefix, "no handlers"); return false; } this.handlers = new Handler[handlerVec.size()]; handlerVec.copyInto(this.handlers); this.names = new String[nameVec.size()]; nameVec.copyInto(this.names); return true; } /** * Helper function that allocates and initializes a new * Handler, given its name. In addition to the * ChainHandler, several other handlers * contain embedded Handlers -- this method can be * used to initialize those embedded Handlers. *

* If there is an error initializing the specified Handler, * this method will log a dignostic message to the server and return * null. This happens if the specified class cannot be * found or instantiated, if the specified class is not actually a * Handler, if the Handler.init method * returns false, or if there is any other exception. * * @param server * The server that will own the new Handler. * Mainly used for the server's properties, which contain * the configuration parameters for the new handler. * * @param prefix * The prefix in the server's properties for the new * Handler's configuration parameters. The prefix * is prepended to the configuation parameters used by the * Handler. * * @param name * The name of the new Handler. The name can * be one of two forms:

    * *
  1. The name of the Java class for the Handler. * This Handler will be initialized using the * prefix specified above. * *
  2. A symbolic name. The configuration * parameter name.class is the name of the * Java class for the Handler. The above * prefix will be ignored and this * Handler will be initialized with the prefix * "name." (the symbolic name followed by * a "."). *
* * @return The newly allocated Handler, or null * if the Handler could not be allocated. */ public static Handler initHandler(Server server, String prefix, String name) { String className = server.props.getProperty(name + ".class"); if (className == null) { className = name; } else { prefix = null; } if (prefix == null) { prefix = name + "."; } try { Handler h = (Handler) Class.forName(className.trim()).newInstance(); if (h.init(server, prefix)) { return h; } server.log(Server.LOG_WARNING, name, "handler did not initialize"); } catch (ClassNotFoundException e) { server.log(Server.LOG_WARNING, name, className + ": " + e); } catch (NoClassDefFoundError e) { server.log(Server.LOG_WARNING, name, className + ": " + e); } catch (IllegalArgumentException e) { server.log(Server.LOG_WARNING, className, "Invalid argument during instantiation"); } catch (ClassCastException e) { server.log(Server.LOG_WARNING, className, "is not a Handler"); } catch (Exception e) { server.log(Server.LOG_WARNING, name, "error initializing:" + e); e.printStackTrace(); } return null; } /** * Calls each of the Handlers in turn until one of them * returns true. * * @param request * The HTTP request. * * @return true if one of the Handlers returns * true, false otherwise. * * @throws IOException * if one of the Handlers throws an * IOException while responding. */ public boolean respond(Request request) throws IOException { if (!isMine.match(request.url)) { return false; } for (int i = 0; i < handlers.length; i++) { request.log(Server.LOG_DIAGNOSTIC, prefix, "invoking handler: " + names[i]); if (handlers[i].respond(request)) { if (report != null) { request.log(Server.LOG_INFORMATIONAL, prefix, names[i] + " handled: " + request.url); request.props.put(report, names[i]); } return true; } } return false; } }