/*
* 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
* 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
* exitOnError
* initFailure
will set
* any of the handlers fail to
* initialize. No handler prefix is required.
* prefix, suffix, glob, match
* 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 Handler
s -- this method can be
* used to initialize those embedded Handler
s.
*
* 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:
Handler
.
* This Handler
will be initialized using the
* prefix
specified above.
*
* 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 ".").
* 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 Handler
s in turn until one of them
* returns true
.
*
* @param request
* The HTTP request.
*
* @return true
if one of the Handler
s returns
* true
, false
otherwise.
*
* @throws IOException
* if one of the Handler
s 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;
}
}