001: /*
002: * Enhydra Java Application Server Project
003: *
004: * The contents of this file are subject to the Enhydra Public License
005: * Version 1.1 (the "License"); you may not use this file except in
006: * compliance with the License. You may obtain a copy of the License on
007: * the Enhydra web site ( http://www.enhydra.org/ ).
008: *
009: * Software distributed under the License is distributed on an "AS IS"
010: * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
011: * the License for the specific terms governing rights and limitations
012: * under the License.
013: *
014: * The Initial Developer of the Enhydra Application Server is Lutris
015: * Technologies, Inc. The Enhydra Application Server and portions created
016: * by Lutris Technologies, Inc. are Copyright Lutris Technologies, Inc.
017: * All Rights Reserved.
018: *
019: * Contributor(s):
020: *
021: * $Id: BasicSessionHome.java,v 1.3 2007-10-19 10:05:39 sinisa Exp $
022: */
023:
024: package com.lutris.appserver.server.sessionEnhydra;
025:
026: import java.util.Enumeration;
027: import java.util.Hashtable;
028: import java.util.Vector;
029:
030: import com.lutris.appserver.server.Enhydra;
031: import com.lutris.appserver.server.session.SessionException;
032: import com.lutris.logging.Logger;
033: import com.lutris.util.Config;
034: import com.lutris.util.ConfigException;
035:
036: /**
037: * BasicSessionHome creates instances of BasicSession and
038: * manages the 'active'/'passive' state of those sessions.
039: * All sessions created by BasicSessionHome are held in
040: * memory.<p>
041: *
042: * The following parameters can be used to configure
043: * the BasicSessionHome. They should be grouped together in a section,
044: * normally <code>SessionManager.SessionHome</code>, which is specified to
045: * the constructor.<p>
046: *
047: * <ul>
048: * <li><code>MaxSessions: {int}</code><p>
049: * Specifies the maximum number of in use concurrent sessions. If this value
050: * is exceeded then CreateSession() will throw a CreateSessionException.
051: * -1 indicates unlimited session. If MaxSessions is not set
052: * in the application's configuration then MaxSessions defaults to
053: * unlimited sessions.<p>
054: * </ul>
055: *
056: * @see BasicSession
057: * @see StandardSession
058: * @see StandardSessionManager
059: * @version $Revision: 1.3 $
060: * @author Kyle Clark
061: */
062: public class BasicSessionHome implements StandardSessionHome {
063:
064: /**
065: * The active cache holds sessions that are in the
066: * 'active' state.
067: */
068: private Hashtable activeCache = new Hashtable();
069:
070: /**
071: * The active thread cache holds all the association
072: * between active sessions and the threads that have
073: * checked them out.
074: */
075: private Hashtable activeThreadCache = new Hashtable();
076:
077: /**
078: * The active cache holds sessions that are in the
079: * 'active' state. There is a single mapping
080: * of keys to session objects: sessionKey -> session
081: */
082: private Hashtable passiveCache = new Hashtable();
083:
084: /**
085: * Maximum number of allowed sessions.
086: */
087: private long maxSessions = -1;
088:
089: /**
090: * Configuration keys.
091: */
092: private static final String MAX_SESSIONS_KEY = "MaxSessions";
093: private static final long UNDEFINED_MAX_SESSIONS = -1;
094:
095: /**
096: * The session manager associated with the session home.
097: */
098: private StandardSessionManager sessionMgr;
099:
100: /**
101: * @param sessionMgr
102: * The session manager associated with this session home.
103: * @param config
104: * Object parsed from configuration file. This should be
105: * for the section constaining the standard session home configuration.
106: * @exception ConfigException
107: * signifies a problem in the configuration file.
108: */
109: public BasicSessionHome(StandardSessionManager sessionMgr,
110: Config config) throws ConfigException {
111: this .sessionMgr = sessionMgr;
112: if (config.containsKey(MAX_SESSIONS_KEY)) {
113: maxSessions = config.getLong(MAX_SESSIONS_KEY);
114: debug(MAX_SESSIONS_KEY + " = " + maxSessions);
115: }
116: if (maxSessions <= 0) {
117: maxSessions = UNDEFINED_MAX_SESSIONS;
118: }
119: }
120:
121: /**
122: * @param sessionMgr
123: * The session manager associated with this session home.
124: * @param config
125: * Object parsed from configuration file. This should be
126: * for the section constaining the standard session home configuration.
127: * @param loader
128: * The class load to use when load objects from persistent store.
129: * @exception ConfigException
130: * signifies a problem in the configuration file.
131: */
132: public BasicSessionHome(StandardSessionManager sessionMgr,
133: Config config, ClassLoader loader) throws ConfigException {
134: this (sessionMgr, config);
135: }
136:
137: /**
138: * Creates and returns a new session instance. The
139: * session is bound to the specified session key. The
140: * session is also associated with the current thread
141: * and is considered in the 'active' state. The
142: * session remains in the 'active' state until the
143: * thread puts the session into the 'passive' state..
144: * Only this thread will be able to put the
145: * session into the 'passive' state.
146: *
147: * @param sessionKey the key to associate with the session.
148: * @return the newly created session.
149: * @exception CreateSessionException if the session cannot be
150: * created.
151: * @exception DuplicateKeyException if the session cannot
152: * be created because the key is already in use.
153: * @exception SessionException if an error occurs.
154: * @see #passivateSession
155: */
156: public synchronized StandardSession createSession(String sessionKey)
157: throws CreateSessionException, DuplicateKeyException,
158: SessionException {
159: if (containsKey(sessionKey)) {
160: throw new DuplicateKeyException("Session key " + sessionKey
161: + " is already in use.");
162: }
163: if ((maxSessions != UNDEFINED_MAX_SESSIONS)
164: && (maxSessions <= size())) {
165: cleanupNewSession();
166: if (maxSessions <= size()) {
167: throw new CreateSessionException(
168: "Maximum session limit (" + maxSessions
169: + ") has been reached.");
170: }
171: }
172: BasicSession session = new BasicSession(sessionMgr, sessionKey);
173: SessionThread activeKey = new SessionThread(Thread
174: .currentThread(), sessionKey);
175: // keep track of # of threads that reference session
176: session.incrementRefCount();
177: activeCache.put(sessionKey, session);
178: activeThreadCache.put(activeKey, session);
179: debug(3, "create session: key = " + sessionKey);
180: return (StandardSession) session;
181: }
182:
183: /**
184: * Returns the session bound to the session key.
185: * The session must already be in the 'active' state
186: * and associated with the current thread,
187: * otherwise null is returned.
188: *
189: * @param sessionKey
190: * the session key for the session. If the session
191: * doesn't exist or is not is not bound to the current
192: * thread then null is returned.
193: * @return
194: * the session.
195: * @exception SessionException
196: * if the session cannot be retrieved.
197: * @see #getSession(Thread, String)
198: */
199: public synchronized StandardSession getSession(String sessionKey)
200: throws SessionException {
201: debug(3, "get session: key = " + sessionKey);
202: SessionThread activeKey = new SessionThread(Thread
203: .currentThread(), sessionKey);
204: return (BasicSession) activeThreadCache.get(activeKey);
205: }
206:
207: /**
208: * Returns the session bound to the specified
209: * session key. The session is put into the 'active' state.
210: * The session is also associated with the specified thread.
211: * Only this thread will be able to put the session
212: * back into the 'passive' state once it is done with the
213: * session.
214: *
215: * @param thread the thread that should be associated with
216: * the session while it is in the active state. Only this
217: * thread can put the session back into the passive state.
218: * @param sessionKey
219: * the session key for the session that will be made
220: * 'active' and returned. If the session doesn't exist
221: * then null is returned.
222: * @return
223: * the session.
224: * @exception SessionException
225: * if the session cannot be retrieved.
226: * @see #passivateSession
227: */
228: public synchronized StandardSession getSession(Thread thread,
229: String sessionKey) throws SessionException {
230: debug(3, "get session: key = " + sessionKey);
231: SessionThread activeKey = new SessionThread(thread, sessionKey);
232: BasicSession s = null;
233: // First check if the session is in the passive state.
234: if (passiveCache.containsKey(sessionKey)) {
235: s = (BasicSession) passiveCache.remove(sessionKey);
236: activeCache.put(sessionKey, s);
237: activeThreadCache.put(activeKey, s);
238: s.incrementRefCount();
239: } else {
240: // The session must already be active.
241: if (activeThreadCache.containsKey(activeKey)) {
242: // It's already checked out by the thread
243: s = (BasicSession) activeThreadCache.get(activeKey);
244: } else {
245: // It's being checked out by a new thread
246: s = (BasicSession) activeCache.get(sessionKey);
247: if (s != null) {
248: activeThreadCache.put(activeKey, s);
249: // Only increment the ref count when a new thread
250: // checks out the session.
251: s.incrementRefCount();
252: }
253: }
254: }
255: return (StandardSession) s;
256: }
257:
258: /**
259: * Removes a session from the cache. If the session
260: * doesn't exist the opration is ignored.
261: *
262: * @param sessionKey
263: * the session key associated with the session.
264: * @exception SessionException
265: * if the session cannot be retrieved.
266: */
267: public synchronized void removeSession(String sessionKey)
268: throws SessionException {
269: debug(3, "remove session: key = " + sessionKey);
270: Enumeration e = activeThreadCache.keys();
271: while (e.hasMoreElements()) {
272: SessionThread key = (SessionThread) e.nextElement();
273: if (key.sessionKey.equals(sessionKey)) {
274: activeThreadCache.remove(key);
275: }
276: }
277: activeCache.remove(sessionKey);
278: passiveCache.remove(sessionKey);
279: }
280:
281: /**
282: * Puts a session into the 'passive' state. A 'passive'
283: * session may be made persistent. Only the thread that
284: * put the session into the 'active' state may put
285: * the session into the 'passive' state.
286: *
287: * @param thread the thread that is currently associated
288: * with the session.
289: * @param sessionKey
290: * the session key for the session that will be made passive.
291: * @exception SessionException
292: * if the session cannot be retrieved.
293: */
294: public synchronized void passivateSession(Thread thread,
295: String sessionKey) throws SessionException {
296: // This is a bad place for this debug() to be.
297: // We should avoid any delay inside the synchronized block
298: //debug(3, "passivate session: key = " + sessionKey);
299: SessionThread activeKey = new SessionThread(thread, sessionKey);
300: if (activeThreadCache.containsKey(activeKey)) {
301: BasicSession s = (BasicSession) activeThreadCache
302: .remove(activeKey);
303: if (s.decrementRefCount() == 0) {
304: activeCache.remove(sessionKey);
305: passiveCache.put(sessionKey, s);
306: }
307: }
308: }
309:
310: /**
311: * Specifies if a key is currently bound to a session.
312: *
313: * @param sessionKey
314: * the session key to be tested.
315: * @return
316: * true if the session key is in use.
317: * @exception SessionException
318: * if the existence of the key cannot be determined.
319: */
320: public boolean containsKey(String sessionKey)
321: throws SessionException {
322: return (activeCache.containsKey(sessionKey) || passiveCache
323: .containsKey(sessionKey));
324: }
325:
326: /**
327: * Returns the current number of sessions.
328: *
329: * @return
330: * the 'active' session count.
331: * @exception SessionException
332: * if the size cannot be determined
333: */
334: public int size() throws SessionException {
335: return (activeCache.size() + passiveCache.size());
336: }
337:
338: /**
339: * Returns the current number of sessions that are paged to
340: * persistent store.
341: *
342: * @return
343: * the 'paged' session count.
344: * @exception SessionException
345: * if the size cannot be determined
346: */
347: public int pagedSize() throws SessionException {
348: return 0;
349: }
350:
351: /**
352: * Returns an enumeration of the keys for all the sessions.
353: *
354: * @return
355: * the enumeration of session keys.
356: * @exception SessionException
357: * if the session enumeration cannot be retrieved.
358: */
359: public synchronized Enumeration keys() throws SessionException {
360: Vector v = new Vector();
361: Enumeration e;
362: e = activeCache.keys();
363: while (e.hasMoreElements()) {
364: v.addElement(e.nextElement());
365: }
366: e = passiveCache.keys();
367: while (e.hasMoreElements()) {
368: v.addElement(e.nextElement());
369: }
370: return v.elements();
371: }
372:
373: /**
374: * Cleans up (removes) the oldest unused session from the passive cache.
375: *
376: * @exception SessionException if an error occurs.
377: */
378: private void cleanupNewSession() throws SessionException {
379: Enumeration e = passiveCache.keys();
380: StandardSession oldest = null;
381: while (e.hasMoreElements()) {
382: StandardSession s = (StandardSession) passiveCache.get(e
383: .nextElement());
384: if (s.isNew()) {
385: if ((oldest == null)
386: || (s.getTimeCreated() < oldest
387: .getTimeCreated())) {
388: oldest = s;
389: }
390: }
391: }
392: if (oldest != null) {
393: removeSession(oldest.getSessionKey());
394: }
395: }
396:
397: /**
398: * Shuts dows the session home.
399: */
400: public void shutdown() {
401: // noop
402: }
403:
404: /**
405: * Prints debug information under Logger.DEBUG.
406: *
407: * @param msg the message to print.
408: */
409: private void debug(String msg) {
410: debug(0, msg);
411: }
412:
413: /**
414: * Prints debug information under Logger.DEBUG.
415: *
416: * @param level the debug level.
417: * @param msg the message to print.
418: */
419: protected void debug(int level, String msg) {
420: int dbg = Logger.DEBUG;
421: switch (level) {
422: case 1:
423: dbg = Logger.DEBUG1;
424: break;
425: case 2:
426: dbg = Logger.DEBUG2;
427: break;
428: case 3:
429: dbg = Logger.DEBUG3;
430: break;
431: case 4:
432: dbg = Logger.DEBUG4;
433: break;
434: case 5:
435: dbg = Logger.DEBUG5;
436: break;
437: case 6:
438: dbg = Logger.DEBUG6;
439: break;
440: case 7:
441: dbg = Logger.DEBUG7;
442: break;
443: case 8:
444: dbg = Logger.DEBUG8;
445: break;
446: case 9:
447: dbg = Logger.DEBUG9;
448: break;
449: default:
450: dbg = Logger.DEBUG;
451: break;
452: }
453: Enhydra.getLogChannel().write(
454: dbg,
455: "PersistentSessionHome("
456: + Thread.currentThread().getName() + "): "
457: + msg);
458: }
459: }
|