KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > com > lutris > appserver > server > sessionEnhydra > StandardSessionManager


1 /*
2  * Enhydra Java Application Server Project
3  *
4  * The contents of this file are subject to the Enhydra Public License
5  * Version 1.1 (the "License"); you may not use this file except in
6  * compliance with the License. You may obtain a copy of the License on
7  * the Enhydra web site ( http://www.enhydra.org/ ).
8  *
9  * Software distributed under the License is distributed on an "AS IS"
10  * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
11  * the License for the specific terms governing rights and limitations
12  * under the License.
13  *
14  * The Initial Developer of the Enhydra Application Server is Lutris
15  * Technologies, Inc. The Enhydra Application Server and portions created
16  * by Lutris Technologies, Inc. are Copyright Lutris Technologies, Inc.
17  * All Rights Reserved.
18  *
19  * Contributor(s):
20  *
21  * $Id: StandardSessionManager.java,v 1.8 2005/04/21 10:03:17 lola Exp $
22  */

23
24
25 package com.lutris.appserver.server.sessionEnhydra;
26
27 import java.lang.reflect.Constructor JavaDoc;
28 import java.util.Date JavaDoc;
29 import java.util.Enumeration JavaDoc;
30
31 import javax.servlet.http.HttpSession JavaDoc;
32 import javax.servlet.http.HttpSessionBindingEvent JavaDoc;
33 import javax.servlet.http.HttpSessionBindingListener JavaDoc;
34
35 import com.lutris.appserver.server.Application;
36 import com.lutris.appserver.server.Enhydra;
37 import com.lutris.appserver.server.httpPresentation.HttpPresentationComms;
38 import com.lutris.appserver.server.httpPresentation.HttpPresentationException;
39 import com.lutris.appserver.server.session.MemoryPersistence;
40 import com.lutris.appserver.server.session.Session;
41 import com.lutris.appserver.server.session.SessionException;
42 import com.lutris.appserver.server.session.SessionManager;
43 import com.lutris.appserver.server.sessionEnhydra.persistent.PersistentSessionHome;
44 import com.lutris.appserver.server.sessionEnhydra.persistent.PersistentSessionUserTable;
45 import com.lutris.appserver.server.user.User;
46 import com.lutris.logging.LogChannel;
47 import com.lutris.logging.Logger;
48 import com.lutris.util.Config;
49 import com.lutris.util.ConfigException;
50 import com.lutris.util.KeywordValueException;
51
52
53 /**
54  * This session manager maintains the mapping between session keys
55  * and sessions. It generates secure session keys that are safe to use
56  * as cookie values in web applications. It also manages the life
57  * of a session. If a session is not used (touched) for a preconfigured
58  * amount of time, then it is expired and removed from the session
59  * manager. The following parameters can be used to configure
60  * the session manager. They should be grouped together in a section,
61  * normally <code>SessionManager</code>, which is specified to
62  * the constructor.<p>
63  * <ul>
64  * <li><code>SessionLifetimeMax: {int}</code><p>
65  *
66  * The maximum number of minutes a session is valid. If this
67  * value us zero, then there is no lifetime limit.
68  * This parameter must be set in the configuration file.<p>
69  *
70  * <li><code>SessionIdleTimeMax: {int}</code><p>
71  *
72  * Maximum number of minutes a session may be idle before
73  * the session is expired (deleted). If this value
74  * is less than or equal to zero, then the session may
75  * be idle indefinitly.<p>
76  *
77  * <li><code>SessionNoUserIdleTimeMax: {int}</code><p>
78  *
79  * Maximum number of minutes a session that does not have a
80  * <CODE>User</CODE> object assocaited with it may be idle before
81  * the session is expired (deleted). If this value
82  * is less than or equal to zero, then the session may
83  * be idle indefinitly. Default is the same as <code>MaxIdleTime</code><p>
84  *
85  * <li><code>IdleScanInterval: {int}</code><p>
86  *
87  * How often, in seconds, the session manager tests if
88  * any sessions should be expired.
89  * This parameter must be greater than zero.<p>
90  *
91  * <li><code>RandomizerIntervals: {int [,int]...}</code><p>
92  *
93  * The set of varying intervals to use for adding user-generated entropy
94  * to the random number generator. It is a good idea to use several
95  * different prime numbers. These intervals are in seconds.<p>
96  *
97  * <li><code>SessionHome.Mode: {BASIC | PAGE_TO_DISK | PAGE_TO_DB | CUSTOM}</code><p>
98  *
99  * Indicates the mode (StandardSessionHome interface) that the standard
100  * session manager will use.
101  * In 'BASIC' mode the standard session manager uses the <code>BasicSessionHome</code>
102  * to manage sessions. In 'PAGE_TO_DISK' the standard session manager uses
103  * the <code>DiskPagedSessionHome</code> to manage sessions. In 'PAGE_TO_DB'
104  * the standard session manager used the <code>PersistentSessionHome</code>
105  * to manage sessions. In 'CUSTOM' the application specifies the classes
106  * that should be loaded and provide the <code>StandardSessionHome</code>
107  * implementation. If set to 'CUSTOM' then the settings
108  * <code>SessionHome.Class</code> and <code>SessionUserTable.Class</code>
109  * should be set.
110  *
111  * <li><code>SessionHome.Class: {String}</code><p>
112  *
113  * The class that implements the StandardSessionHome interface. This
114  * class is loaded by StandardSessionManager and used to managed
115  * the collection of sessions. If this option is not specified then
116  * BasicSessionHome is used. The SessionHome that is
117  * loaded by the session manager is expected to contain a consturctor
118  * that takes the following arguments:
119  * <code>SessionHome(StandardSessionManager mgr,
120  * com.lutris.util.Config config,
121  * ClassLoader loader)</code><p>
122  *
123  * <li><code>SessionUserTable.Class: {String}</code><p>
124  *
125  * The class that implements the StandardSessiondUserTable interface. This
126  * class is loaded by StandardSessionManager and used to managed
127  * the session to user relationships. If this option is not specified then
128  * BasicSessionUserTable is used. The SessionUserTable that is
129  * loaded by the session manager is expected to contain a consturctor
130  * that takes the following arguments:
131  * <code>SessionUserTable(com.lutris.util.Config config)</code><p>
132  *
133  * <li><code>MemoryPersistence: {true|false}</code><p>
134  *
135  * Indicate that should we keep sessions in memory when stop application
136  *
137  * </ul>
138  *
139  * @version $Revision: 1.8 $
140  * @author John Marco
141  * @author Shawn McMurdo
142  * @author Kyle Clark
143  * @author Mark Diekhans
144  * @see com.lutris.util.Config
145  * @see StandardSessionHome
146  * @see StandardSessionUserTable
147  * @see BasicSessionHome
148  * @see DiskPagedSessionHome
149  * @see com.lutris.appserver.server.sessionEnhydra.persistent.PersistentSessionHome
150  */

151 public class StandardSessionManager
152     implements SessionManager, StandardSessionIdleHandler {
153   /**
154    * Represents the current session management mode.
155    */

156   protected int mode;
157   /**
158    * Indicates that the session manager is using
159    * the basic session home interface to manage sessions.
160    * @see #getMode
161    * @see com.lutris.appserver.server.sessionEnhydra.BasicSessionHome
162    */

163   public static final int MODE_BASIC = 1;
164   /**
165    * Indicates that the session manager is using
166    * the page to disk home interface to manage sessions.
167    * @see #getMode
168    * @see com.lutris.appserver.server.sessionEnhydra.DiskPagedSessionHome
169    */

170   public static final int MODE_PAGE_TO_DISK = 2;
171   /**
172    * Indicates that the session manager is using
173    * the page to database home interface to manage sessions.
174    * @see #getMode
175    * @see com.lutris.appserver.server.sessionEnhydra.persistent.PersistentSessionHome
176    */

177   public static final int MODE_PAGE_TO_DB = 3;
178   /**
179    * Indicates that the session manager is using
180    * the custom home interface to manage sessions.
181    * @see #getMode
182    */

183   public static final int MODE_CUSTOM = 4;
184   /**
185    * Indicates that url encoding of session ids is never preformed.
186    * @see #getEncodeUrlState
187    **/

188   public static final String JavaDoc ENCODE_URL_NEVER = "Never";
189   /**
190    * Indicates that url encoding of session ids is always preformed.
191    * @see #getEncodeUrlState
192    **/

193   public static final String JavaDoc ENCODE_URL_ALWAYS = "Always";
194   /**
195    * Indicates that url encoding of session ids is preformed only when
196    * cookies are disabled on the client browser. This is automatically
197    * detected.
198    * @see #getEncodeUrlState
199    **/

200   public static final String JavaDoc ENCODE_URL_AUTO = "Auto";
201   public static final String JavaDoc ENCODE_RANDOM_YES = "Yes";
202   public static final String JavaDoc ENCODE_RANDOM_NO = "No";
203   /**
204    * Default maximum session life time, in seconds. Zero if there is no
205    * limit. A derived <CODE>SessionManager</CODE> may override this value
206    * to use the default expiration logic or override
207    * <CODE>isSessionExpired</CODE> to define custom expiration logic.
208    *
209    * @see lutris.session.StandardSessionManager#getMaxSessionLifeTime
210    */

211   protected static long defaultMaxSessionLifeTime = 0;
212   /**
213    * Default maximum session idle time, in seconds. A derived
214    * <CODE>SessionManager</CODE> may override this value to use the default
215    * expiration logic or override <CODE>isSessionExpired</CODE>
216    * to define custom expiration logic.
217    * A value less-than or equal to zero disables idle checking.
218    * Default value is 30 minutes.
219    *
220    * @see #getMaxSessionIdleTime
221    */

222   protected static long defaultMaxSessionIdleTime = 30*60;
223   /**
224    * Indicates url encoding status. Assumes that
225    * comms.response.writeHTML(HTMLDocument doc) is used to commit the
226    * response to the client. The default is to Auto.<br>
227    * <code>Never</code> indicates urls are never encoded with session keys.
228    * This indicates that session cookies have to be used or no session state
229    * can be maintained.<br>
230    * <code>Always</code> indicates that urls are always encoded with session
231    * keys. Session cookies are never used.<br>
232    * <code>Auto</code> indicates that session cookies will be if available.
233    * If not, urls will automatically be encoded.<br>
234    * @see #getEncodeUrlState
235    */

236   protected static String JavaDoc defaultEncodeUrlState = "Auto";
237   /**
238    * Default interval, in seconds, to scan for sessions to expire.
239    * A derived <CODE>SessionManager</CODE> may override this value.
240    * Default value is 30 seconds.
241    */

242   protected static long defaultIdleScanInterval = 30;
243   /**
244    * Default list of randomize key generator time intervals, in seconds.
245    * A list of prime numbers is recommended.
246    * A derived <CODE>SessionManager</CODE> may override this value.
247    */

248   protected static long[] defaultRandomizerIntervals = {
249     301, 1001, 5003
250   };
251   /**
252    * The name of the config variable for the max session lifetime.
253    */

254   public static final String JavaDoc CFG_LIFE = "SessionLifetimeMax";
255   /**
256    * The name of the config variable for the max session idle time.
257    */

258   public static final String JavaDoc CFG_IDLE = "SessionIdleTimeMax";
259   /**
260    * The name of the config variable for the url encoding state
261    */

262   public static final String JavaDoc CFG_ENCODE_URL_STATE = "SessionEncodeUrlState";
263   /**
264    * Url encoding for first page state (to force it or not)
265    * CFG_ENCODE_URL_STATE must not be set to NEVER for this parameter to take efect
266    */

267   public static final String JavaDoc CFG_ENCODE_FIRST_URL = "SessionEncodeFirstUrl";
268   /**
269    * The name of the config variable for the max idle time for sessions
270    * with no user.
271    */

272   public static final String JavaDoc CFG_NOUSER_IDLE = "SessionNoUserIdleTimeMax";
273   /**
274    * The name of the config variable for the interval between scans for
275    * idle sessions.
276    */

277   public static final String JavaDoc CFG_SCAN = "IdleScanInterval";
278   /**
279    * The name of the config variable for the interval between introduction
280    * of randomness to the session key generator.
281    */

282   public static final String JavaDoc CFG_RANDOM = "RandomizerIntervals";
283   /**
284    * The name of the config variable for the session home settings.
285    */

286   public static final String JavaDoc CFG_SESSION_HOME = "SessionHome";
287   /**
288    * The name of the config variable for the session home type.
289    */

290   public static final String JavaDoc CFG_SESSION_HOME_TYPE = "SessionHome.Mode";
291   /**
292    * Indicates that a sesson is still active.
293    *
294    * @see #sessionDeleted
295    */

296   public static final int SESSION_ACTIVE = 0;
297   /**
298    * Indicates that a sesson was deleted due to max time
299    * being exceeded.
300    *
301    * @see #sessionDeleted
302    */

303   public static final int SESSION_MAX_TIME = 1;
304   /**
305    * Indicates that a sesson was deleted due to idle time
306    * being exceeded.
307    *
308    * @see #sessionDeleted
309    */

310   public static final int SESSION_IDLE_EXPIRE = 2;
311   /**
312    * Indicates that a sesson was explictly deleted.
313    *
314    * @see #sessionDeleted
315    */

316   public static final int SESSION_EXPLICT_DELETE = 3;
317   /**
318    * Table of currently active user sessions is managed
319    * by the session home. Different implementations of
320    * StandardSessionHome can be loaded by this manager. A specific
321    * implementation can manage failover persistence, paging, etc.
322    */

323   // private StandardSessionHome sessionHome;
324
protected StandardSessionHome sessionHome;
325   /**
326    * Table of users objects. This table tracks the number of simultaneous
327    * sessions a user currently has active. Table entries are of type
328    * <CODE>Vector</CODE>, constaining Session objects, keyed by User
329    * object reference.
330    */

331   private StandardSessionUserTable sessionUserTable;
332   // The maximum number of sessions (highwater mark).
333
protected int maxSessions;
334   protected Date JavaDoc maxSessionsDate;
335   /**
336    * Object that generates random session keys. Manages a thread to
337    * for increase randomization.
338    *
339    * @see StandardSessionKeyGen
340    */

341   private StandardSessionKeyGen keyGenerator;
342   /**
343    * Maximum session life time, in seconds. Zero if there is no
344    * limit.
345    *
346    * @see lutris.session.StandardSessionManager#getMaxSessionLifeTime
347    */

348   private long maxSessionLifeTime;
349   /**
350    * Maximum session idle time, in seconds.
351    * A value less-than or equal to zero disables idle checking.
352    *
353    * @see #getMaxSessionIdleTime
354    */

355   protected long maxSessionIdleTime;
356   /**
357    * The url encoding state. Either <code>Never</code>
358    * <code>Always</code> <code>Auto</code>.
359    */

360   protected String JavaDoc encodeUrlState;
361   /**
362    * Url encoding for first page state (force it or not)
363    */

364   protected boolean encodeFirstUrl;
365   /**
366    * Background thread that keeps track of session idle time.
367    * Sessions that have been idle for more than a configurable time
368    * limit are terminated by this thread.
369    *
370    * @see StandardSessionIdleTimer
371    */

372   private StandardSessionIdleTimer idleTimer;
373   /**
374    * Maximum session idle time, in seconds for sessions
375    * that don't have a <CODE>User</CODE> object associated with them.
376    * A value less-than or equal to zero disables idle checking.
377    *
378    * @see #getMaxNoUserSessionIdleTime
379    */

380   protected long maxNoUserSessionIdleTime;
381   /**
382    * The time in seconds between scans for idle sessions to expire.
383    */

384   protected long scanInterval;
385   /**
386    * Log channel for debugging messages.
387    */

388     LogChannel logChannel = null;
389   /**
390    * The application using this session manager.
391    */

392   Application app = null;
393   /**
394    * The class loader used by this app/context.
395    */

396   ClassLoader JavaDoc classLoader;
397   /**
398    * The supported session home types.
399    */

400   private static final String JavaDoc BASIC = "BASIC";
401   private static final String JavaDoc PAGE_TO_DISK = "PAGE_TO_DISK";
402   private static final String JavaDoc PAGE_TO_DB = "PAGE_TO_DB";
403   private static final String JavaDoc CUSTOM = "CUSTOM";
404   /**
405    * The session home interface type being used.
406    */

407   String JavaDoc sessionHomeType = BASIC;
408   /** DACHA & TUFA
409    * variable which determine should we held sessions in memory during
410    * restart application
411    */

412   private boolean isMemoryPersistence = false;
413
414   /**
415    * Log configuration information.
416    */

417   private void logConfig () {
418       if (logChannel != null) {
419       logChannel.write(Logger.DEBUG,
420                "SessionManager." + CFG_LIFE + " = "
421                + maxSessionLifeTime + " sec");
422       logChannel.write(Logger.DEBUG,
423                "SessionManager." + CFG_IDLE + " = "
424                + maxSessionIdleTime + " sec");
425       logChannel.write(Logger.DEBUG,
426                "SessionManager." + CFG_NOUSER_IDLE + " = "
427                + maxNoUserSessionIdleTime + " sec");
428       logChannel.write(Logger.DEBUG,
429                "SessionManager." + CFG_SCAN + " = "
430                + scanInterval + " sec");
431       logChannel.write(Logger.DEBUG,
432                "SessionManager." + CFG_ENCODE_URL_STATE + " = "
433                + encodeUrlState);
434     logChannel.write(Logger.DEBUG,
435                "SessionManager." + CFG_ENCODE_FIRST_URL + " = "
436                + encodeFirstUrl);
437     }
438   }
439 public StandardSessionManager (){}
440   /**
441    * Creates a new <code>SessionManager</code> object.
442    * This constructor will first looks for the session manager
443    * configuration parameters that have the specified configuration
444    * prefix prepended to the standard session manager configuration
445    * option.<p>
446    *
447    * @param app the application associate with this session
448    * manager.
449    * @param config Object parsed from configuration file. This should be
450    * for the section constaining the session manager configuration.
451    * @param sessionMgrLogChannel If not <CODE>null</CODE>, channel to
452    * log debugging information to.
453    * @exception ConfigException signifies a problem in the
454    * configuration file.
455    * @exception SessionException
456    * if all classes (Home and UserTable) couldn't be loaded
457    * by the session manager.
458    */

459   public StandardSessionManager (Application application, Config config,
460                  LogChannel sessionMgrLogChannel)
461     throws ConfigException, SessionException {
462     //DACHA: first initialize app
463
app = application;
464     initManager(application.getClass().getClassLoader(),config,sessionMgrLogChannel);
465   }
466
467   public StandardSessionManager (ClassLoader JavaDoc classLoader, Config config,
468                  LogChannel sessionMgrLogChannel) throws ConfigException, SessionException {
469       initManager(classLoader,config,sessionMgrLogChannel);
470   }
471
472   private void initManager (ClassLoader JavaDoc classLoader, Config config,
473                 LogChannel sessionMgrLogChannel) throws ConfigException, SessionException {
474       this.classLoader = classLoader;
475       logChannel = sessionMgrLogChannel;
476     // High-water mark
477
maxSessions = 0;
478     maxSessionsDate = new Date JavaDoc();
479     /**DACHA & TUFA
480      *Config file specifies should we keep SessionManager in memory
481      *during restart application
482      */

483     if (config.containsKey("MemoryPersistence")) {
484       String JavaDoc mp = config.getString("MemoryPersistence");
485       if (mp.equals("true"))
486         isMemoryPersistence = true;
487     }
488     /*
489      * Config file specifies minutes, but the values are kept internally
490      * as seconds.
491      */

492     // Session lifetime
493
if (config.containsKey(CFG_LIFE)) {
494       maxSessionLifeTime = config.getLong(CFG_LIFE)*60;
495     }
496     else if (config.containsKey("Lifetime")) {
497       // backwards compatability
498
maxSessionLifeTime = config.getLong("Lifetime")*60;
499     }
500     else {
501       maxSessionLifeTime = defaultMaxSessionLifeTime;
502     }
503     // Idle time.
504
if (config.containsKey(CFG_IDLE)) {
505       maxSessionIdleTime = config.getLong(CFG_IDLE)*60;
506     }
507     else if (config.containsKey("MaxIdleTime")) {
508       // backwards compatability
509
maxSessionIdleTime = config.getLong("MaxIdleTime")*60;
510     }
511     else {
512       maxSessionIdleTime = defaultMaxSessionIdleTime;
513     }
514     // Url encoding state
515
if (config.containsKey(CFG_ENCODE_URL_STATE)) {
516       encodeUrlState = config.getString(CFG_ENCODE_URL_STATE);
517     }
518     else if (config.containsKey("EncodeUrlState")) {
519       // backwards compatability
520
encodeUrlState = config.getString("EncodeUrlState");
521     }
522     else {
523       encodeUrlState = defaultEncodeUrlState;
524     }
525     if (!encodeUrlState.equalsIgnoreCase(ENCODE_URL_NEVER) && !encodeUrlState.equalsIgnoreCase(ENCODE_URL_ALWAYS)
526         && !encodeUrlState.equalsIgnoreCase(ENCODE_URL_AUTO)) {
527       throw new ConfigException("EncodeUrlState must be one of the following: "
528           + ENCODE_URL_NEVER + ", " + ENCODE_URL_ALWAYS + ", or " + ENCODE_URL_AUTO
529           + ".");
530     }
531         
532     /**
533     * Url encoding for first page state (to force it or not)
534     * CFG_ENCODE_URL_STATE must not be set to NEVER for this parameter to take efect
535     */

536     if (config.containsKey(CFG_ENCODE_FIRST_URL)) {
537         encodeFirstUrl = config.getBoolean(CFG_ENCODE_FIRST_URL, false);
538     } else {
539         encodeFirstUrl = false;
540     }
541     
542     /**
543     * if CFG_ENCODE_URL_STATE is set to ALWAYS or NEVER
544     * ignore original and set adequate CFG_ENCODE_FIRST_URL
545     * parameter value
546     */

547     if (encodeUrlState.equalsIgnoreCase(ENCODE_URL_ALWAYS)){
548         encodeFirstUrl = true;
549     } else if (encodeUrlState.equalsIgnoreCase(ENCODE_URL_NEVER)) {
550         encodeFirstUrl = false;
551     }
552         
553     if (config.containsKey(CFG_NOUSER_IDLE)) {
554       maxNoUserSessionIdleTime = config.getLong(CFG_NOUSER_IDLE)*60;
555     }
556     else if (config.containsKey("MaxNoUserIdleTime")) {
557       // backwards compatability
558
maxNoUserSessionIdleTime = config.getLong("MaxNoUserIdleTime")*60;
559     }
560     else {
561       maxNoUserSessionIdleTime = maxSessionIdleTime;
562     }
563     scanInterval = defaultIdleScanInterval;
564     if (config.containsKey(CFG_SCAN)) {
565       scanInterval = config.getLong(CFG_SCAN);
566     }
567     else if (config.containsKey("IdleScanInterval")) {
568       // backwards compatability
569
scanInterval = config.getLong("IdleScanInterval");
570     }
571     if (scanInterval <= 0) {
572       throw new ConfigException("IdleScanInterval must be greater than zero.");
573     }
574     idleTimer = new StandardSessionIdleTimer(this, app, scanInterval);
575     if (config.containsKey(CFG_SESSION_HOME_TYPE)) {
576       sessionHomeType = config.getString(CFG_SESSION_HOME_TYPE);
577     }
578     if (!sessionHomeType.equalsIgnoreCase(BASIC) && !sessionHomeType.equalsIgnoreCase(PAGE_TO_DISK)
579         && !sessionHomeType.equalsIgnoreCase(PAGE_TO_DB) && !sessionHomeType.equalsIgnoreCase(CUSTOM)) {
580       throw new ConfigException("Invalid " + CFG_SESSION_HOME_TYPE + ": '"
581           + sessionHomeType + "'");
582     }
583     // Session Home
584
sessionHome = loadSessionHome(config);
585     // Session User Table
586
sessionUserTable = loadSessionUserTable(config);
587     // Random key generator.
588
long[] intervals = config.getLongs(CFG_RANDOM, defaultRandomizerIntervals);
589     if (intervals.length == 0) {
590       throw new ConfigException(CFG_RANDOM + " must contain some values.");
591     }
592     for (int i = 0; i < intervals.length; i++) {
593       if (intervals[i] <= 0) {
594         throw new ConfigException(CFG_RANDOM + " must contain positive integers.");
595       }
596     }
597     keyGenerator = new StandardSessionKeyGen(intervals);
598     // Start threads.
599
keyGenerator.start();
600     idleTimer.start();
601     logConfig();
602   }
603
604   /**
605    * Loads the StandardSessionHome used by this session manager.
606    *
607    * @param config
608    * This session manager's config section.
609    * @exception
610    * ConfigException if the configuration is invalid.
611    * @exception
612    * SessionException if the StandardSessionHome could not be loaded.
613    */

614   protected StandardSessionHome loadSessionHome (Config config) throws ConfigException,
615       SessionException {
616     try {
617       StandardSessionHome sessionHome = null;
618       Config homeConfig = null;
619       if (config.containsKey(CFG_SESSION_HOME)) {
620         homeConfig = (Config)config.getSection(CFG_SESSION_HOME);
621       }
622       else {
623         homeConfig = new Config();
624       }
625       if (sessionHomeType.equalsIgnoreCase(CUSTOM)) {
626         if (homeConfig.containsKey("Class")) {
627           String JavaDoc homeClassName = homeConfig.getString("Class");
628           try {
629             Class JavaDoc[] paramTypes = new Class JavaDoc[3];
630             Object JavaDoc[] args = new Object JavaDoc[3];
631             paramTypes[0] = Class.forName("com.lutris.appserver.server.sessionEnhydra.StandardSessionManager");
632             paramTypes[1] = Class.forName("com.lutris.util.Config");
633             paramTypes[2] = Class.forName("java.lang.ClassLoader");
634             args[0] = this;
635             args[1] = homeConfig;
636             args[2] = classLoader;
637             Constructor JavaDoc c = Class.forName(homeClassName).getConstructor(paramTypes);
638             sessionHome = (StandardSessionHome)c.newInstance(args);
639         if (logChannel != null) {
640         logChannel.write(Logger.DEBUG, "SessionMgr: "
641                  + "StandardSessionHome: " + homeClassName);
642             }
643           } catch (Exception JavaDoc e) {
644             throw new SessionException("Unable to load " + homeClassName,
645                 e);
646           }
647         }
648         mode = MODE_CUSTOM;
649       }
650       else if (sessionHomeType.equalsIgnoreCase(PAGE_TO_DISK)) {
651         sessionHome = new DiskPagedSessionHome(this, homeConfig, classLoader);
652         mode = MODE_PAGE_TO_DISK;
653       }
654       else if (sessionHomeType.equalsIgnoreCase(PAGE_TO_DB)) {
655         sessionHome = new PersistentSessionHome(this, homeConfig, classLoader);
656         mode = MODE_PAGE_TO_DB;
657       }
658       else {
659         sessionHome = new BasicSessionHome(this, homeConfig);
660         mode = MODE_BASIC;
661       }
662       if (logChannel != null) {
663       logChannel.write(Logger.DEBUG, "SessionMgr: "
664                + "StandardSessionHome: " + sessionHomeType + "\n");
665       }
666       return sessionHome;
667     } catch (KeywordValueException e) {
668       e.printStackTrace();
669       throw new ConfigException("SessionMgr: unable to load StandardSessionHome: "
670           + e);
671     }
672   }
673
674   /**
675    * Loads the StandardSessiondUserTable used by this session manager.
676    *
677    * @param config
678    * This session manager's config section.
679    * @exception
680    * ConfigException if the StandardSessionHome could not be loaded.
681    */

682   protected StandardSessionUserTable loadSessionUserTable (Config config) throws ConfigException,
683       SessionException {
684     try {
685       StandardSessionUserTable sessionUserTable = null;
686       Config userTableConfig = null;
687       if (config.containsKey("SessionUserTable")) {
688         userTableConfig = (Config)config.getSection("SessionUserTable");
689       }
690       else {
691         userTableConfig = new Config();
692       }
693       if (sessionHomeType.equalsIgnoreCase(CUSTOM)) {
694         String JavaDoc tableClassName = userTableConfig.getString("Class");
695         try {
696           Class JavaDoc[] paramTypes = new Class JavaDoc[1];
697           Object JavaDoc[] args = new Object JavaDoc[1];
698           paramTypes[0] = Class.forName("com.lutris.util.Config");
699           args[0] = userTableConfig;
700           Constructor JavaDoc c = Class.forName(tableClassName).getConstructor(paramTypes);
701           sessionUserTable = (StandardSessionUserTable)c.newInstance(args);
702       if (logChannel != null) {
703           logChannel.write(Logger.DEBUG, "SessionMgr: "
704                    + "StandardSessionUserTable: " + tableClassName);
705           }
706         } catch (NoSuchMethodException JavaDoc e) {
707           throw new SessionException("Unable to load " + tableClassName +
708               ": " + "Constructor not found.", e);
709         } catch (Exception JavaDoc e) {
710           throw new SessionException("Unable to create instance of " + tableClassName,
711               e);
712         }
713       }
714       else if (sessionHomeType.equalsIgnoreCase(PAGE_TO_DISK)) {
715         sessionUserTable = new PagedSessionUserTable(userTableConfig);
716       }
717       else if (sessionHomeType.equalsIgnoreCase(PAGE_TO_DB)) {
718         sessionUserTable = new PersistentSessionUserTable(userTableConfig);
719       }
720       else {
721         sessionUserTable = new BasicSessionUserTable(userTableConfig);
722       }
723       return sessionUserTable;
724     } catch (KeywordValueException e) {
725       throw new ConfigException("SessionMgr: unable to load StandardSessionUserTable: "
726           + e);
727     }
728   }
729
730   /**
731    * Shutdown this session manager, stopping threads that are
732    * associated with it.
733    */

734   public synchronized void shutdown () {
735     if (isMemoryPersistence)
736       MemoryPersistence.putSessionManager(app.getName(), this);
737     else {
738       if (keyGenerator != null) {
739         keyGenerator.shutdown();
740         keyGenerator = null;
741       }
742       if (idleTimer != null) {
743         idleTimer.shutdown();
744         idleTimer = null;
745       }
746       if (sessionHome != null) {
747         sessionHome.shutdown();
748         sessionHome = null;
749       }
750       if (sessionUserTable != null) {
751         sessionUserTable.shutdown();
752         sessionUserTable = null;
753       }
754     }
755   }
756
757   /**
758    * Allocate a new <CODE>StandardSession</CODE> object. This maybe
759    * overridden by derived session managers to allocated a
760    * <CODE>Session</CODE> object derived from <CODE>StandardSession</CODE>.
761    * This is called by <CODE>createSession</CODE>.
762    *
763    * @param sessionKey The session key to associate with the
764    * session.
765    * @return session The new <code>StandardSession</code> object.
766    * @see StandardSession
767    * @deprecated The instance of StandardSessionHome should be replaced
768    * instead of extending this method.
769    */

770   protected StandardSession newSession (String JavaDoc sessionKey) throws CreateSessionException,
771       SessionException {
772     return (StandardSession)sessionHome.createSession(sessionKey);
773   }
774
775   /**
776    * Create a new <CODE>Session</CODE> object and associate a unique random
777    * key. No <CODE>User</CODE> is initially associated with
778    * (<CODE>Session.getUser()</CODE> returns <CODE>null</CODE>/
779    *
780    * @return session The new <code>Session</code> object.
781    * @exception SessionException
782    * if the session cannot be created.
783    * @see Session
784    */

785   public Session JavaDoc createSession () throws SessionException {
786     return createSession("");
787   }
788
789   /**
790    * Create a new <CODE>Session</CODE> object and associate a unique random
791    * key. No <CODE>User</CODE> is initially associated with
792    * (<CODE>Session.getUser()</CODE> returns <CODE>null</CODE>/
793    *
794    * @param ipPortToken
795    * The base64 encoded IP and Port number to include in session key
796    * @return session The new <code>Session</code> object.
797    * @exception SessionException
798    * if the session cannot be created.
799    * @see Session
800    */

801   public Session JavaDoc createSession (String JavaDoc ipPortToken) throws SessionException {
802     StandardSession session = null;
803     String JavaDoc sessionKey = null;
804     /*
805      * Create a new key that does not already exist in
806      * the session table. The odds of this loop making more than
807      * one pass are astronomically small, but not zero.
808      */

809     do {
810       try {
811         sessionKey = keyGenerator.newSessionKey();
812         if (ipPortToken != null && ipPortToken.length() > 0) {
813                     sessionKey = sessionKey.substring(0,sessionKey.length()-2)+ipPortToken;
814         }
815         session = sessionHome.createSession(sessionKey);
816       } catch (DuplicateKeyException e) {
817         // TODO - log message?
818
session = null;
819       }
820     } while (session == null);
821     int currentSize = sessionHome.size();
822     if (currentSize > maxSessions) {
823       maxSessions = currentSize;
824       maxSessionsDate = new Date JavaDoc();
825     }
826     keyGenerator.incrementRandomCounter();
827     if (logChannel != null) {
828     logChannel.write(Logger.DEBUG, "SessionMgr: createSession: " + sessionKey);
829     }
830     return session;
831   }
832
833   public Session JavaDoc createSession (HttpPresentationComms comms) throws SessionException {
834     Session JavaDoc s = null;
835     String JavaDoc saTok = null;
836     try {
837       saTok = comms.request.getHeader(":aff:");
838     } catch (HttpPresentationException hpe) {
839       saTok = null;
840     }
841     if (saTok == null) {
842       s = createSession();
843     }
844     else {
845       s = createSession(saTok);
846       if (logChannel != null) {
847       logChannel.write(Logger.DEBUG,
848                "SessionKey created for EnhydraDirector");
849       }
850     }
851     return s;
852   }
853
854   /**
855    * Log a user being registered or unregistered.
856    */

857   private void logRegisterUser (Session JavaDoc session, String JavaDoc which) {
858       if (logChannel != null) {
859       String JavaDoc userName = null;
860       User user = session.getUser();
861       if (user != null) {
862           userName = user.getName();
863       }
864       logChannel.write(Logger.DEBUG, "SessionMgr: "
865                + which + ": " + session.getSessionKey() +
866                " user = \"" + userName + "\"");
867       }
868   }
869
870   /**
871    * Adds a session's user to the session-to-user table.
872    * This is called by the <CODE>Session</CODE> object to associate
873    * a session with a user.
874    *
875    * @param session The now logged-in <code>Session</code> instance to
876    * store in the session table.
877    * @see Session
878    * @see User
879    * @see #unregisterUser
880    * @exception SessionException
881    * if an error occurs.
882    */

883   protected synchronized void registerUser (Session JavaDoc session) throws SessionException {
884     sessionUserTable.add(session.getSessionKey(), session.getUser());
885     logRegisterUser(session, "registerUser");
886   }
887
888   /**
889    * Removes a session's user from the session-to-user table.
890    * This is called by the <CODE>Session</CODE> object to disassoicate
891    * a user from a session.
892    *
893    * @param session The now <code>Session</code> instance
894    * the user is being logged out from.
895    * @see Session
896    * @exception SessionException
897    * if an error occurs.
898    */

899   protected synchronized void unregisterUser (Session JavaDoc session) throws SessionException {
900     logRegisterUser(session, "unregisterUser");
901     sessionUserTable.remove(session.getSessionKey(), session.getUser());
902   }
903
904   /**
905    * Determine if a session should be expired based on the idle
906    * time. This method is designed to be overriden by derived classes that
907    * want to use different idle time checks.
908    *
909    * @param session Session to check.
910    * @return A reason code for the experation.
911    * One of <CODE>SESSION_MAX_TIME</CODE>,
912    * <CODE>SESSION_IDLE_EXPIRE</COD