001: /*
002: * SerialPersist.java
003: *
004: * Brazil project web application Framework,
005: * export version: 1.1
006: * Copyright (c) 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, suhler.
022: *
023: * Version: 1.11
024: * Created by suhler on 00/02/01
025: * Last modified by suhler on 00/12/11 13:32:22
026: */
027:
028: package sunlabs.brazil.session;
029:
030: import sunlabs.brazil.handler.ResourceHandler;
031: import sunlabs.brazil.server.Handler;
032: import sunlabs.brazil.server.Request;
033: import sunlabs.brazil.server.Server;
034:
035: import java.lang.reflect.Array;
036: import java.io.FileInputStream;
037: import java.io.FileNotFoundException;
038: import java.io.FileOutputStream;
039: import java.io.ObjectInputStream;
040: import java.io.ObjectOutputStream;
041: import java.io.Serializable;
042: import java.util.Enumeration;
043: import java.util.Hashtable;
044: import java.util.Properties;
045: import java.util.Vector;
046:
047: import sun.misc.Signal;
048: import sun.misc.SignalHandler;
049:
050: /**
051: * The <code>SerialPersist</code> class is a <code>SessionManager</code>
052: * that uses Java object serialization to make a persistent store of the
053: * session information for a server. See {@link SessionManager}
054: * for a description of sessions.
055: * <p>
056: * This class uses the signal-handling capabilities of the Solaris-specific
057: * <code>sun.misc</code> package. When the program is interrupted by a
058: * Ctrl-C (SIGINT), the current session information is serialized to
059: * a "store" file. When the program is restarted, the session information
060: * is restored from the store file.
061: * <p>
062: * To use this <code>SessionManager</code> effectively, the developer should
063: * try to ensure that only <code>Serializable</code> objects are stored in
064: * the session manager. With default object serialization, the developer
065: * will get a runtime IOException if an object in the hierarchy is not
066: * actually serializable, for example, if a (<code>Serializable</code>)
067: * <code>Hashtable</code> contains an arbitrary non-serializable object.
068: * <p>
069: * This class will traverse the session hierarchy and attempt to remove
070: * all non-serializable objects before writing to the store file. This
071: * means that after restoring, the session information may not correspond
072: * exactly to what it was present before. Consumers of session information
073: * should be prepared to deal with some session information disappearing.
074: * For instance, the <code>TemplateRunner</code> keeps the templates for each
075: * session in the <code>SessionManager</code> and will reconstruct the
076: * templates if some or all of them disappear during a restore.
077: * <p>
078: * This class will recursively traverse the elements of (specifically) any
079: * <code>Hashtable</code>, <code>Vector</code>, or array it finds in the
080: * session hierarchy to ensure that the elements themselves are
081: * <code>Serializable</code>. If the element is not, it is removed from
082: * its container. If the container becomes zero-length because all its
083: * elements were removed, the container is then itself removed. For
084: * arrays, the offending non-serializable element is instead replaced with
085: * <code>null</code>, since arrays cannot be resized.
086: * <hr>
087: * This <code>SessionManager</code> implements the <code>Handler</code>
088: * interface,and examines the following request properties at init time:
089: * <dl class=props>
090: * <dt> store
091: * <dd> The name of the store file to hold the session information. The
092: * default value is <code>"store"</code>.
093: * <dt> saveUrl
094: * <dd> The name of a url that will cause the session information to be
095: * saved without terminating the server. WARNING! this has the side
096: * effect of removing non serializable elements from the session
097: * table, which can lead to disturbing behavior.
098: * </dl>
099: *
100: * @author Stephen Uhler (stephen.uhler@sun.com)
101: * @author Colin Stevens (colin.stevens@sun.com)
102: * @version 1.11, 00/12/11
103: */
104: public class SerialPersist extends SessionManager implements Handler,
105: SignalHandler {
106: private static final String STORE = "store";
107:
108: String store = "store";
109:
110: Server server;
111: String prefix;
112: String save;
113:
114: public boolean init(Server server, String prefix) {
115: this .server = server;
116: this .prefix = prefix;
117:
118: Properties props = server.props;
119:
120: store = props.getProperty(prefix + STORE, store);
121: save = props.getProperty(prefix + "saveUrl");
122: // store = ResourceHandler.getResourcePath(props, prefix, store);
123:
124: readStore();
125:
126: Signal.handle(new Signal("INT"), this );
127: SessionManager.setSessionManager(this );
128: return true;
129: }
130:
131: public boolean respond(Request request) {
132: if (save != null && request.url.equals(save)) {
133: server
134: .log(Server.LOG_LOG, store,
135: "saving persistent store");
136: saveStore();
137: }
138: return false;
139: }
140:
141: public void handle(Signal sig) {
142: saveStore();
143: System.exit(0);
144: }
145:
146: /**
147: * Call this first, to read in the store, if available.
148: * One store per application
149: */
150:
151: void readStore() {
152: try {
153: ObjectInputStream in = new ObjectInputStream(
154: new FileInputStream(store));
155:
156: server.log(Server.LOG_DIAGNOSTIC, prefix,
157: "loading persistent store: " + store);
158:
159: sessions = (Hashtable) in.readObject();
160: } catch (FileNotFoundException e) {
161: server.log(Server.LOG_DIAGNOSTIC, prefix,
162: "no persistent store: " + store);
163: } catch (Exception e) {
164: server.log(Server.LOG_ERROR, prefix,
165: "Attempting to read store: " + store);
166: }
167: }
168:
169: /**
170: * Call this to "save" the store, typically before exiting.
171: * This should be done on a signal handler
172: *
173: * Before calling writeObject, we need to iterate through the
174: * sessions table, and remove all values that are not serializable
175: */
176:
177: void saveStore() {
178: server.log(Server.LOG_DIAGNOSTIC, prefix,
179: "writing persistent store: " + store);
180:
181: cleanForSerialization(sessions);
182:
183: try {
184: ObjectOutputStream out = new ObjectOutputStream(
185: new FileOutputStream(store));
186: out.writeObject(sessions);
187: out.close();
188: } catch (Exception e) {
189: server.log(Server.LOG_ERROR, prefix,
190: "Attempting to serialize: " + sessions);
191: e.printStackTrace();
192: }
193: }
194:
195: /**
196: * Recursively decend through an object "Cleaning" it for serialization.
197: * For the container objects "Hashtable", "Vector", and "Array", look at
198: * every object contained within it, removing all those that are not
199: * serializable by either removing them, or setting them to null.
200: * This allows successful serialization even if some elements of the
201: * base object, or its desendents can't be serialized.
202: *
203: * This is intended for saving and restoring typical session information.
204: * Care must be taken by the caller that data integrety is maintained
205: * when non-serializable objects are zapped.
206: *
207: * @param obj The object to Clean
208: * @returns The cleaned object, or null if nothing is serializable.
209: */
210:
211: public static Object cleanForSerialization(Object obj) {
212: return clean(obj, obj);
213: }
214:
215: static Object clean(Object root, Object obj) {
216: if (obj instanceof Properties) {
217: return obj;
218: } else if (obj instanceof Hashtable) {
219: Hashtable h = (Hashtable) obj;
220: Enumeration e = h.keys();
221: while (e.hasMoreElements()) {
222: Object key = e.nextElement();
223: Object value = h.get(key);
224: if ((clean(root, key) == null)
225: || (clean(root, value) == null)) {
226: if (obj == root) {
227: continue;
228: }
229: h.remove(key);
230: }
231: }
232: if (h.size() > 0) {
233: return obj;
234: }
235: } else if (obj instanceof Vector) {
236: Vector v = (Vector) obj;
237: for (int i = v.size(); --i >= 0;) {
238: if (clean(root, v.elementAt(i)) == null) {
239: v.removeElementAt(i);
240: }
241: }
242: if (v.size() > 0) {
243: return obj;
244: }
245: } else if (obj.getClass().isArray()) {
246: if (obj.getClass().getComponentType().isPrimitive()) {
247: return obj;
248: }
249: for (int i = Array.getLength(obj); --i >= 0;) {
250: if (clean(root, Array.get(obj, i)) == null) {
251: Array.set(obj, i, null);
252: }
253: }
254: } else if (obj instanceof Serializable) {
255: return obj;
256: }
257: return null;
258: }
259: }
|