sunlabs.brazil.handler.ConfigFileHandler Maven / Gradle / Ivy
Show all versions of sunlabs.brazil Show documentation
/*
* ConfigFileHandler.java
*
* Brazil project web application toolkit,
* export version: 2.3
* Copyright (c) 1999-2004 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.2
* Created by suhler on 99/06/28
* Last modified by suhler on 04/08/30 09:02:07
*
* Version Histories:
*
* 2.2 04/08/30-09:02:07 (suhler)
* "enum" became a reserved word, change to "enumer".
*
* 2.1 02/10/01-16:36:34 (suhler)
* version change
*
* 1.35 02/07/24-10:45:56 (suhler)
* doc updates
*
* 1.34 01/12/10-16:11:12 (suhler)
* doc lint
*
* 1.33 01/05/18-09:11:40 (suhler)
* work with either CookieSessionHAndler -or- SessionFilter
*
* 1.32 01/01/22-19:02:30 (cstevens)
* Remove RechainableProperties from Request. In order to insert shared
* Properties objects, use request.addSharedProps() instead of manually
* rechaining the properties using props.setDefaults().
*
* 1.31 00/12/11-13:28:21 (suhler)
* add class=props for automatic property extraction
*
* 1.30 00/11/20-13:28:06 (suhler)
* lint
* D
*
* 1.29 00/11/20-13:21:35 (suhler)
* doc fixes
*
* 1.28 00/10/05-11:06:46 (suhler)
* The property "glob" can be used to match a patterns of request.props
* that may be set by the user that are NOT in the default config file. If
* "glob" is specified, no default file is required.
*
* 1.27 00/05/31-13:47:26 (suhler)
* doc cleanup
*
* 1.26 00/05/24-11:35:36 (suhler)
* added a "copy" property to implement old behavior for backward compatibility
*
* 1.25 00/05/22-14:03:48 (suhler)
* doc updates
*
* 1.24 00/05/15-12:04:29 (suhler)
* added chaining back in, removing (impossible) behind option
* added more diags
*
* 1.23 00/05/12-15:25:10 (suhler)
* temporarily back out properties chaining
*
* 1.22 00/05/10-10:54:06 (suhler)
* No longer add config properties directly to request.props.
* Instead, use the ChainedProperties.
* added "noOverride" option to prevent properties from ovverriding
* values that already exist
*
* 1.21 00/04/20-11:50:15 (cstevens)
* copyright.
*
* 1.20 00/04/12-15:53:16 (cstevens)
* imports
*
* 1.19 00/03/29-14:35:55 (cstevens)
* pedantic use of Request.getQueryData()
*
* 1.18 00/02/11-12:42:44 (suhler)
* arguments to Match wrong
*
* 1.17 00/02/03-14:24:40 (suhler)
* added "no content" option
*
* 1.16 99/12/15-12:02:39 (suhler)
* Properties we not being passed on if a "set" was atempted, and no
* values were changed
*
* 1.15 99/12/07-10:55:17 (suhler)
* set request properties *after* processing SET query parameters
*
* 1.14 99/11/30-09:47:38 (suhler)
* Redo. Only generate session based files when really needed
*
* 1.12.1.1 99/11/14-10:34:55 (rinaldo)
* Fix Error with Mime Type
*
* 1.13 99/11/01-11:56:37 (suhler)
* ConfigFile handler gets root property at each request
*
* 1.12 99/10/26-17:11:10 (cstevens)
* Get rid of public variables Request.server and Request.sock:
* A. In all cases, Request.server was not necessary; it was mainly used for
* constructing the absolute URL for a redirect, so Request.redirect() was
* rewritten to take an absolute or relative URL and do the right thing.
* B. Request.sock was changed to Request.getSock(); it is still rarely used
* for diagnostics and logging (e.g., ChainSawHandler).
*
* 1.10.1.1 99/10/23-19:47:18 (rinaldo)
*
* 1.11 99/10/11-12:30:44 (suhler)
* change POST to GET when done processing.
*
* 1.10 99/10/06-12:28:41 (suhler)
* fix log
*
* 1.9 99/10/06-12:20:32 (suhler)
* Merged changes between child workspace "/home/suhler/brazil/naws" and
* parent workspace "/net/mack.eng/export/ws/brazil/naws".
*
* 1.8 99/10/01-11:26:36 (cstevens)
* Change logging to show prefix of Handler generating the log message.
*
* 1.7.1.2 99/09/27-17:47:30 (suhler)
* Entirely new semantics. This will break all existing uses:
* - the "config" file is always merged into the request properties
* - a separate url is used to set the properties
* - a default properties file is required, and the existing properties limit
* what is allowed to be set.
*
* 1.7.1.1 99/09/10-11:02:53 (suhler)
* ???
*
* 1.7 99/08/06-12:11:06 (suhler)
* Merged changes between child workspace "/home/suhler/brazil/naws" and
* parent workspace "/net/mack.eng/export/ws/brazil/naws".
*
* 1.5.1.2 99/08/06-12:05:18 (suhler)
* use glob in util directory
* ,
* .
* D
*
* 1.5.1.1 99/08/04-18:44:31 (suhler)
* Changes to allow session specific file names
*
* 1.6 99/07/27-22:02:32 (rinaldo)
* Stephen, you will probably not like what I did but it is late and I have to
* have the properties I just set be available to all sunsequent url requests tak
* a look at XXX.
*
* 1.5 99/07/14-11:37:20 (rinaldo)
* Allow absolute URLS.
*
* 1.4 99/07/13-09:37:41 (suhler)
* look up the redirect property in the form/query data
*
* 1.3 99/06/29-14:38:20 (suhler)
* use new redirector
*
* 1.2 99/06/29-09:37:12 (suhler)
* re-wrote config handler to be more useful.
* It now works in conjuction with the form template handler
* see config.config for an example
* .
*
* 1.2 99/06/28-10:46:26 (Codemgr)
* SunPro Code Manager data about conflicts, renames, etc...
* Name history : 1 0 handlers/ConfigFileHandler.java
*
* 1.1 99/06/28-10:46:25 (suhler)
* date and time created 99/06/28 10:46:25 by suhler
*
*/
package sunlabs.brazil.handler;
import sunlabs.brazil.session.SessionManager;
import sunlabs.brazil.server.Handler;
import sunlabs.brazil.server.Request;
import sunlabs.brazil.server.Server;
import sunlabs.brazil.util.Glob;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.Enumeration;
import java.util.Dictionary;
import java.util.Hashtable;
import java.util.Properties;
/**
* Handler for manipulating per-user session state that can be
* represented as ASCII name/value pairs. The state for each session
* is stored in a file, which is expected to be in java properties format.
*
* If "prefix" is matched, the contents of the (usually cached) config file
* for the current session is added to the request properties.
*
* If the url matches the "set" property, the contents of the config
* file are changed based on the supplied query parameters (either GET
* of POST). If no config file exists for the session, one is created
* from a default properties file. Only properties already in the
* config file may be changed using the "set" method.
*
* If a "%" is specified in the file name, it is replaced by the
* SessionID property, if any, or "common" if sessions aren't used.
* This should be replaced with something more general, so we can have
* more arbitrary mappings between request and the session info.
*
* The following request properties are used:
*
* - prefix
- The URL prefix required for all documents
*
- set
- The url pattern to match setting properties.
* Currently, it must also match "prefix".
*
- noContent
- a url, matching the "set" pattern that causes
* a "204 no content" to be returned to the client
* (experimental).
*
- name
- The name of the config file. the first "%" is replaced
* by the current SessionID.
*
- default
- The default properties file to "seed" session properties
*
- glob
- Properties that match this "glob" pattern may be set
* using the "set" pattern. If this property is specified,
* the "default" property is optional.
*
- root
- The document root (no properties prefix required). If the
* "name" or "default" properties don't start with a "/",
* this is used as the current directory.
*
*
* If "%" is specified in the file name, a new session file is
* created only if 1) a property is changed from the default, and 2)
* A cookie was received by the browser.
*
* See also: {@link sunlabs.brazil.template.SetTemplate}
* which is preferrable in most cases, providing a templated based
* (instead of URL based)
* mechanism for maintaining persistent properties.
*
* @author Stephen Uhler
* @version 2.2, 04/08/30
*/
public class ConfigFileHandler implements Handler {
static final String SET = "set"; // glob matching url setting props
static final String PREFIX = "prefix"; // prefix for adding to props
static final String NAME = "name"; // name of the config file
static final String DEFAULT = "default"; // the default config file
static final String ROOT = "root"; // root of config file
static final String GLOB = "glob";
String propsPrefix;
String name; // name of the config file
String urlPrefix; // prefix for any request we handle
String set; // glob pattern to match url needed to modify props
String nc; // glob pattern to match url needed for no content
String match; // glob pattern to match settable props not in deflt file
String root; // where to find the root of the config file
Properties defaultProperties; // where to find the defaults
boolean copy = false; // undocumented backwart compat flag
/**
* Make sure default properties exist before starting this handler,
* or that "match" is specified".
*/
public boolean
init(Server server, String prefix) {
propsPrefix = prefix;
urlPrefix = server.props.getProperty(prefix + PREFIX, "/");
set = server.props.getProperty(prefix + SET, urlPrefix + prefix);
nc = server.props.getProperty(prefix + "noContent", "X");
match = server.props.getProperty(prefix + GLOB);
name = server.props.getProperty(prefix + NAME, prefix + ".cfg");
copy = (server.props.getProperty(prefix + "copy") != null);
root = server.props.getProperty(ROOT, ".");
String defaultName = server.props.getProperty(prefix + DEFAULT);
File defaultFile = null;
if (defaultName != null) {
if (defaultName.startsWith("/")) {
defaultFile = new File(defaultName);
} else {
defaultFile = new File(root, defaultName);
}
}
defaultProperties = new Properties();
try {
FileInputStream in = new FileInputStream(defaultFile);
defaultProperties.load(in);
in.close();
} catch (IOException e) {
if (match == null) {
server.log(Server.LOG_WARNING, prefix, "No default file: " +
defaultFile + " ConfigFileHandler NOT installed!");
return false;
}
}
server.log(Server.LOG_DIAGNOSTIC, prefix, "\n set=" + set +
"\n name=" + name + "\n default=" + defaultFile);
return true;
}
/**
* Extract the session state into the request object, optionally
* modifying the properties. If the properties are modified,
* they are stored in a file for safe keeping.
*/
public boolean
respond(Request request) throws IOException {
if (!request.url.startsWith(urlPrefix)) {
return false;
}
/*
* Get the existing properties.
*/
root = request.props.getProperty(ROOT, root);
String id = request.props.getProperty("SessionID", "common");
Properties p = (Properties) SessionManager.getSession(id,
propsPrefix, Properties.class);
/*
* If no properties exist, read in the session
* properties, otherwise use the default properties
*/
if (p.isEmpty()) {
File file = getPropsFile(name, id);
request.log(Server.LOG_DIAGNOSTIC, propsPrefix,
"No properties, looking in: " + file);
try {
FileInputStream in = new FileInputStream(file);
p.load(in);
in.close();
} catch (IOException e) {
request.log(Server.LOG_DIAGNOSTIC, propsPrefix,
"Can't find config file: " +
file + " using default properties");
Enumeration enumer = defaultProperties.propertyNames();
while(enumer.hasMoreElements()) {
String key = (String) enumer.nextElement();
p.put(key, defaultProperties.getProperty(key));
}
}
}
/*
* If "SET" then update the properties file
*/
if (Glob.match(set, request.url)) {
Dictionary update = request.getQueryData(null);
// System.out.println("Setting from: " + update);
Enumeration enumer = update.keys();
boolean changed = false;
synchronized (p) {
while(enumer.hasMoreElements()) {
String key = (String) enumer.nextElement();
if (p.containsKey(key) || isMatch(key)) {
p.put(key, update.get(key));
changed = true; // XXX not quite
} else {
request.log(Server.LOG_DIAGNOSTIC, propsPrefix,
"Can't set key " + key +
", name doesn't exist in default file" +
" or doesn;t match: " + match);
}
}
if (!changed) {
request.log(Server.LOG_DIAGNOSTIC, propsPrefix,
"Set called, nothing changed!!");
}
/*
* If the client has cookies enabled, try to save the
* properties. This depends upon a side effect of the
* "sessionHandler" to let us know if the browser
* actually sent us our cookie.
*/
if (changed && (name.indexOf("%") < 0 ||
request.props.containsKey("UrlID") ||
request.props.containsKey("gotCookie"))) {
File file = getPropsFile(name, id);
try {
FileOutputStream out = new FileOutputStream(file);
p.save(out, request.serverUrl() + request.url +
" (from ConfigFileHandler: " + propsPrefix + ")");
out.close();
request.log(Server.LOG_DIAGNOSTIC, propsPrefix,
"Saving configuration properties to " + file);
} catch (IOException e) {
request.log(Server.LOG_WARNING, propsPrefix,
"Can't save properties to: " + file);
}
}
}
/*
* If this is a post, trash the input and change to a get.
* XXX This is probably incorrect.
*/
if (request.method.equals("POST")) {
request.method = "GET";
request.postData = null;
request.headers.put("Content-Length", "0");
}
}
if (copy) {
Enumeration enumer = p.keys();
while(enumer.hasMoreElements()) {
String key = (String) enumer.nextElement();
request.props.put(key, p.getProperty(key));
}
} else {
request.addSharedProps(p);
// request.log(Server.LOG_DIAGNOSTIC, propsPrefix, "Chaining " +
// p + " onto request.props");
}
return false;
}
/**
* True if key "s" matches glob pattern in "match". If
* match is null, then no match.
*/
private boolean
isMatch(String s) {
return (match != null && Glob.match(match, s));
}
/**
* Find the config file name
*/
private File
getPropsFile(String name, String id) {
int index;
if ((index = name.indexOf("%")) >= 0) {
name = name.substring(0,index) + id + name.substring(index+1);
}
File file;
if (name.startsWith("/")) {
file = new File(name);
} else {
file = new File(root, name);
}
return file;
}
}