sunlabs.brazil.session.PropertiesCacheManager Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of sunlabs.brazil Show documentation
Show all versions of sunlabs.brazil Show documentation
Extremely small footprint Java HTTP stack.
The newest version!
/*
* PropertiesCacheManager.java
*
* Brazil project web application toolkit,
* export version: 2.3
* Copyright (c) 2000-2002 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): suhler.
*
* Version: 2.2
* Created by suhler on 00/11/16
* Last modified by suhler on 02/11/25 15:50:49
*
* Version Histories:
*
* 2.2 02/11/25-15:50:49 (suhler)
* Merged changes between child workspace "/home/suhler/brazil/naws" and
* parent workspace "/net/mack.eng/export/ws/brazil/naws".
*
* 1.11.1.1 02/11/25-15:44:17 (suhler)
* expose "Saveable" interface to allow non properties objects to be saved
*
* 2.1 02/10/01-16:39:09 (suhler)
* version change
*
* 1.11 02/07/24-10:48:09 (suhler)
* doc updates
*
* 1.10 02/06/05-16:15:26 (suhler)
* add "defer" to defer loading sessions until we need them
*
* 1.9 02/01/29-14:35:56 (suhler)
* doc lint
*
* 1.8 01/05/01-11:02:09 (suhler)
* Update for new SessionManager Interface
*
* 1.7 01/04/04-16:58:24 (suhler)
* better diagnostics
*
* 1.6 01/03/15-10:15:40 (suhler)
* Prune empty properties objects before saving
*
* 1.5 00/12/11-13:32:32 (suhler)
* add class=props for automatic property extraction
*
* 1.4 00/12/08-16:48:02 (suhler)
* doc fixes
*
* 1.3 00/11/20-13:22:26 (suhler)
* doc fixes
*
* 1.2 00/11/16-11:56:35 (suhler)
* stupid typo
*
* 1.2 00/11/16-10:32:35 (Codemgr)
* SunPro Code Manager data about conflicts, renames, etc...
* Name history : 1 0 session/PropertiesCacheManager.java
*
* 1.1 00/11/16-10:32:34 (suhler)
* date and time created 00/11/16 10:32:34 by suhler
*
*/
package sunlabs.brazil.session;
import java.io.File;
import java.io.FileOutputStream;
import java.io.FileInputStream;
import java.io.OutputStream;
import java.io.InputStream;
import java.io.IOException;
import java.io.Serializable;
import java.util.Enumeration;
import java.util.Hashtable;
import java.util.Properties;
import sunlabs.brazil.server.Handler;
import sunlabs.brazil.server.Request;
import sunlabs.brazil.server.Server;
import sunlabs.brazil.util.Glob;
/**
* A version of the CacheManager that saves out any session state
* that is either a "java properties" object, or implements "Saveable"
* into a directory in the filesystem,
* one file per entry, then
* restores them on server startup. This is a "poor man's" serialization,
* that saves only ascii state represented in properties files.
* This permits a wider variety of changes to be made to the
* server code, yet still have the ability to read in the proper
* session information.
*
* Classes that are not properties files may implement "Saveable", which
* has the store() and load() methods from Properties; they are expected to
* generate and restore the state of the object in Properties format.
*
* This handler/sessionManager can take an ascii-readable "snapshot" of
* the server state, for all state that is a java properties object (or
* implements Saveable).
* It doesn't perturb the existing state.
*
Properties:
*
* - storeDir
*
- The directory to use to store the state files. It is created as needed
* when the state is saved. Defalts to "store".
*
- match
*
- A glob pattern that matches the url (or url?query if a query is used.
* Defaults to "*\?*save=true".
*
- filePrefix
*
- A prefix pattern to use for all session files. Defaults to the
* handler prefix.
*
- defer
*
- If set, the saved session information is not reconstructed upon
* startup. Only the list of sessions is read in; the session information
* is restored only when needed.
*
*
* @author Stephen Uhler ([email protected])
* @version %V% PropertiesCacheManager.java
*/
public class
PropertiesCacheManager extends CacheManager
implements Handler, Serializable {
private static final String STORE = "storeDir";
private static final String MATCH = "match";
private static final String PREFIX = "filePrefix";
private static final String DEFER = "defer";
/*
* Change VERSION to indicate in-compatibility across code versions
*/
private static final String VERSION = "types.1";
String storeDir; // diretory for stored state
String match; // glob match for "save" url
String filePrefix; // prefix on saved state files
Properties types = new Properties(); // saved sessions
Server server; // server reference for logging
boolean defer; // true to defer loading of sessioninfo
File dir; // store directory
public boolean
init(Server server, String prefix) {
super.init(server, prefix);
this.server = server;
storeDir = server.props.getProperty(prefix + STORE, "store");
match = server.props.getProperty(prefix + MATCH, "*\\?*save=true");
filePrefix = server.props.getProperty(prefix + PREFIX, prefix);
defer = (server.props.getProperty(prefix + DEFER) != null);
/*
* Read in the directory of saved state, if any.
* Each file name is in the form [prefix][key].
* A separate file, [prefix]types contains the names and
* types of each session.
*/
dir = new File(storeDir);
try {
FileInputStream in =
new FileInputStream(new File(dir, filePrefix + VERSION));
types.load(in);
in.close();
server.log(Server.LOG_WARNING, prefix,
"Loading saved state from: " + storeDir);
} catch (Exception ex) {
server.log(Server.LOG_WARNING, prefix,
"Can't find existing state file: " + filePrefix +
VERSION + " in: " + dir);
defer=false; // no point
}
if (defer) {
server.log(Server.LOG_DIAGNOSTIC, prefix,
"Deferring Loading " + types.size() + " sessions");
} else {
Enumeration el = types.keys();
while (el.hasMoreElements()) {
String key = (String) el.nextElement();
loadKey(key);
}
}
return true;
}
/**
* Load a properties object from a file, put into the session mgr
*/
Object loadKey(String key) {
String type = types.getProperty(key);
Object data = null;
if (type != null) {
server.log(Server.LOG_DIAGNOSTIC, prefix,
"Loading session " + key + ", type " + type);
try {
data = Class.forName(type).newInstance();
FileInputStream in = new FileInputStream(
new File(dir,filePrefix + key));
load(data,in);
putObj(key, data);
in.close();
} catch (Exception e) {
server.log(Server.LOG_WARNING, prefix,
"Error reading state file " + dir + "/" +
key + ": " + e.getMessage());
}
}
return data;
}
public boolean
respond(Request request) {
String check;
if (request.query.equals("")) {
check = request.url;
} else {
check = request.url + "?" + request.query;
}
if (Glob.match(match, check)) {
save(request);
}
return false;
}
/**
* If we have deferred session loading, check here and get it!
*/
protected Object
getObj(Object session, Object ident) {
Object result = super.getObj(session, ident);
if (defer && result == null) {
String key = makeKey(session, ident);
result = loadKey(key);
}
return result;
}
/**
* Save all of the properties files!
* We make a TOC file that has type info, so we can reconstitue the
* proper subclasses of Properties.
*/
int
save(Request request) {
File dir = new File(storeDir);
dir.mkdirs(); // make sure directories exist
int count = 0;
int deleted = 0;
Properties types = new Properties();
/*
* If we read in the existing types file to start with, we can
* "augment" the state from a previous save.
*/
request.log(Server.LOG_LOG, prefix, "saving all session state");
try {
FileInputStream in =
new FileInputStream(new File(dir, filePrefix + VERSION));
types.load(in);
in.close();
request.log(Server.LOG_DIAGNOSTIC, prefix,
"reading existing session state file");
} catch (Exception ex) {
request.log(Server.LOG_DIAGNOSTIC, prefix,
"No existing state files found");
}
/*
* Iterate through the tables, saving every session that is a
* properties file.
*/
synchronized (pool) {
for (int i = 0; i < maxTables; i++) {
Hashtable h = pool[i];
if (h != null) {
Enumeration e = h.keys();
while (e.hasMoreElements()) {
Object key = e.nextElement();
Object value = h.get(key);
if (value instanceof Properties ||
value instanceof Saveable) {
if (isEmpty(value)) {
h.remove(key);
deleted++;
request.log(Server.LOG_DIAGNOSTIC, prefix,
"deleting session " + key);
continue;
}
request.log(Server.LOG_DIAGNOSTIC, prefix,
"Saving state for session " + key);
String type = value.getClass().getName();
types.put(key, type);
try {
FileOutputStream props = new FileOutputStream(
new File(dir,filePrefix +
key.toString()));
save(value, props,
"Brazil server state from " +
request.serverUrl());
count++;
props.close();
} catch (IOException ex) {
request.log(Server.LOG_LOG, prefix,
"Failed: " + ex.getMessage());
}
} else {
// System.out.println("Not a properties: " + key);
}
}
}
}
/*
* Now write out the TOC file
*/
try {
FileOutputStream out = new FileOutputStream(
new File(dir, filePrefix + VERSION));
types.save(out, "Brazil server state from " +
request.serverUrl());
out.close();
} catch (IOException ex) {
request.log(Server.LOG_DIAGNOSTIC, prefix,
"Saving TOC file");
}
}
request.log(Server.LOG_LOG, prefix,
"sessions saved: " + count + " deleted: " + deleted);
return count;
}
/**
* convenience methods for hiding the differences between saveable and
* Properties.
*/
boolean isEmpty(Object o) {
if (o instanceof Saveable) {
return ((Saveable)o).isEmpty();
} else {
return ((Properties)o).isEmpty();
}
}
void save(Object o, OutputStream out, String header) throws IOException {
if (o instanceof Saveable) {
((Saveable)o).save(out, header);
} else {
((Properties)o).save(out, header);
}
}
void load(Object o, InputStream in) throws IOException {
if (o instanceof Saveable) {
((Saveable)o).load(in);
} else {
((Properties)o).load(in);
}
}
/**
* This interface allows for persistence of non-properties session objects.
* These methods should behave precisely like the corrosponding methods
* of the Properties class.
*/
public interface Saveable {
/**
* Recreate the object from the ascii representation stored as a
* Properties format file.
*/
public void load(InputStream in) throws IOException;
/**
* Create an ascii representation of this object in a Java Properties
* format.
*/
public void save(OutputStream out, String header) throws IOException;
/**
* The current object state is the "default"; "save" does not need to
* write out any state.
*/
public boolean isEmpty();
}
}