001: /*
002: * CacheManager.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): suhler.
022: *
023: * Version: 1.6
024: * Created by suhler on 00/08/25
025: * Last modified by suhler on 00/12/11 13:32:27
026: */
027:
028: package sunlabs.brazil.session;
029:
030: import sunlabs.brazil.server.Handler;
031: import sunlabs.brazil.server.Request;
032: import sunlabs.brazil.server.Server;
033: import java.util.Hashtable;
034:
035: /**
036: * This <code>SessionManager</code> associates an object with a Session ID
037: * to give Handlers the ability to maintain state that lasts for the
038: * duration of a session instead of just for the duration of a request.
039: * It should be installed as a handler, whoses init method will replace
040: * the default session manager.
041: * <p>
042: * This version maintains a pool of hashtables. Once they all
043: * fill up - one of them gets tossed, causing any session info in it
044: * to be lost. It uses a simplified approximate LRU scheme.
045: * The default session manager doesn't loose any session information,
046: * but grows the heap without bound as the number of sessions increase.
047: * <P>
048: * properties:
049: * <dl class=props>
050: * <dt>tables <dd>The number of Hashtables in the pool (defaults to 6)
051: * <dt>size <dd>The max number of entries in each table (defaults to 1000).
052: * </dl>
053: *
054: * @author Stephen Uhler (stephen.uhler@sun.com)
055: * @version %V% CacheManager.java
056: */
057:
058: public class CacheManager extends SessionManager implements Handler {
059: ScoredHashtable[] pool; // pool of hashtables
060: int active; // active hashtable for insertions
061: int maxElements; // max elements allowed per table
062: int maxTables; // number of tables in the pool
063: String prefix; // Pattern matching url to do statistics
064:
065: /**
066: * Install this class as the session manager.
067: * Get the number of tables, and the max size per table.
068: */
069:
070: public boolean init(Server server, String prefix) {
071: sessions = null; // don't use table in parent class;
072: this .prefix = prefix;
073: try {
074: String str = server.props.getProperty(prefix + "tables");
075: maxTables = Integer.decode(str).intValue();
076: } catch (Exception e) {
077: maxTables = 6;
078: }
079: try {
080: String str = server.props.getProperty(prefix + "size");
081: maxElements = Integer.decode(str).intValue();
082: } catch (Exception e) {
083: maxElements = 1000;
084: }
085: if (maxTables < 2) {
086: maxTables = 2;
087: }
088: if (maxElements < 1) {
089: maxElements = 1;
090: }
091:
092: server.log(Server.LOG_DIAGNOSTIC, prefix, "\n pool="
093: + maxTables + "\n size=" + maxElements);
094: active = 0;
095: pool = new ScoredHashtable[maxTables];
096: pool[active] = new ScoredHashtable();
097: SessionManager.setSessionManager(this );
098: return true;
099: }
100:
101: /**
102: * Don't handle any URL requests (yet)
103: */
104:
105: public boolean respond(Request request) {
106: return false;
107: }
108:
109: /**
110: * Store entry in a hashtable. Use the concatenation of the
111: * hashcodes for the key.
112: */
113:
114: public Object getSessionObject(Object session, Object ident,
115: Class type) {
116: if (ident == null) {
117: System.out
118: .println("SessionManager ident==null Not Implemented!");
119: return null;
120: }
121: if (session == null) {
122: session = this .getClass();
123: }
124: String key;
125: if (session instanceof String && ident instanceof String) {
126: key = session + "-" + ident;
127: } else {
128: key = "" + session.hashCode() + "-" + ident.hashCode();
129: }
130: Object obj = get(key);
131: if (obj != null) {
132: return obj;
133: } else if (type == null) {
134: return null;
135: } else {
136: try {
137: obj = type.newInstance();
138: // System.out.println(" Creating new: " + type);
139: } catch (Exception e) {
140: throw new IllegalArgumentException(type.getName());
141: }
142: put(key, obj);
143: return obj;
144: }
145: }
146:
147: /**
148: * Find the item in a hashtable.
149: * Looks in the active hashtable first, then searches the
150: * rest in order.
151: */
152:
153: protected Object get(Object key) {
154: Object result = pool[active].get(key);
155: for (int i = 0; result == null && i < maxTables; i++) {
156: if (pool[i] != null && i != active) {
157: result = pool[i].get(key);
158: }
159: }
160: return result;
161: }
162:
163: /**
164: * Puts the item in the active hashtable.
165: * If there is not enough room, switch active tables first.
166: */
167:
168: protected void put(Object key, Object value) {
169: if ((pool[active].size()) >= maxElements) {
170: flush(); // this changes "active" as a side effect
171: }
172: pool[active].put(key, value);
173: /* System.out.println(" Inserting into " + active +
174: " " + pool[active].size() + "/" + maxElements);
175: */
176: }
177:
178: /**
179: * The active hashtable is too big, find the hashtable
180: * with the worst Score, clear it, and set it as the active table.
181: */
182:
183: protected void flush() {
184: int worst = -1; // score of worst table
185: // System.out.println("Table " + active + " is full");
186: for (int i = 0; i < maxTables; i++) {
187: if (i == active) {
188: continue; // never flush "newest" table
189: }
190: if (pool[i] == null) {
191: active = i;
192: pool[i] = new ScoredHashtable();
193: System.out.println(" Created new table: " + active
194: + "/" + maxTables);
195: return;
196: }
197: int check = pool[i].score();
198: if (worst == -1 || check < worst) {
199: worst = check;
200: active = i;
201: }
202: }
203: pool[active].clear();
204: // System.out.println(" clearing " + active + " score: " + worst);
205: }
206:
207: /**
208: * Hashtable that counts successful get requests.
209: * We use this to keep "score".
210: */
211:
212: static class ScoredHashtable extends Hashtable {
213: int hits = 0;
214:
215: public ScoredHashtable() {
216: super ();
217: }
218:
219: public Object get(Object key) {
220: Object result = super .get(key);
221: if (result != null) {
222: hits++;
223: }
224: return result;
225: }
226:
227: /**
228: * return the "score" of a hashtable. Higher numbers represent
229: * "better" tables to keep.
230: */
231:
232: public int score() {
233: return hits;
234: }
235:
236: public void clear() {
237: hits = 0;
238: super.clear();
239: }
240: }
241: }
|