KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > com > caucho > server > session > SessionManager


1 /*
2  * Copyright (c) 1998-2006 Caucho Technology -- all rights reserved
3  *
4  * This file is part of Resin(R) Open Source
5  *
6  * Each copy or derived work must preserve the copyright notice and this
7  * notice unmodified.
8  *
9  * Resin Open Source is free software; you can redistribute it and/or modify
10  * it under the terms of the GNU General Public License as published by
11  * the Free Software Foundation; either version 2 of the License, or
12  * (at your option) any later version.
13  *
14  * Resin Open Source is distributed in the hope that it will be useful,
15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE, or any warranty
17  * of NON-INFRINGEMENT. See the GNU General Public License for more
18  * details.
19  *
20  * You should have received a copy of the GNU General Public License
21  * along with Resin Open Source; if not, write to the
22  *
23  * Free Software Foundation, Inc.
24  * 59 Temple Place, Suite 330
25  * Boston, MA 02111-1307 USA
26  *
27  * @author Scott Ferguson
28  */

29
30 package com.caucho.server.session;
31
32 import com.caucho.config.Config;
33 import com.caucho.config.ConfigException;
34 import com.caucho.config.types.JndiBuilder;
35 import com.caucho.config.types.Period;
36 import com.caucho.management.server.SessionManagerMXBean;
37 import com.caucho.server.cluster.Cluster;
38 import com.caucho.server.cluster.ClusterObject;
39 import com.caucho.server.cluster.ClusterServer;
40 import com.caucho.server.cluster.ObjectManager;
41 import com.caucho.server.cluster.Store;
42 import com.caucho.server.cluster.StoreManager;
43 import com.caucho.server.dispatch.DispatchServer;
44 import com.caucho.server.dispatch.InvocationDecoder;
45 import com.caucho.server.security.ServletAuthenticator;
46 import com.caucho.server.webapp.WebApp;
47 import com.caucho.util.Alarm;
48 import com.caucho.util.AlarmListener;
49 import com.caucho.util.L10N;
50 import com.caucho.util.LruCache;
51 import com.caucho.util.RandomUtil;
52 import com.caucho.vfs.Path;
53 import com.caucho.vfs.Vfs;
54
55 import javax.naming.Context JavaDoc;
56 import javax.naming.InitialContext JavaDoc;
57 import javax.servlet.http.HttpServletRequest JavaDoc;
58 import javax.servlet.http.HttpSessionActivationListener JavaDoc;
59 import javax.servlet.http.HttpSessionAttributeListener JavaDoc;
60 import javax.servlet.http.HttpSessionEvent JavaDoc;
61 import javax.servlet.http.HttpSessionListener JavaDoc;
62 import java.io.IOException JavaDoc;
63 import java.io.ObjectInputStream JavaDoc;
64 import java.io.ObjectOutputStream JavaDoc;
65 import java.util.ArrayList JavaDoc;
66 import java.util.Iterator JavaDoc;
67 import java.util.logging.Level JavaDoc;
68 import java.util.logging.Logger JavaDoc;
69
70 // import com.caucho.server.http.ServletServer;
71
// import com.caucho.server.http.VirtualHost;
72

73 /**
74  * Manages sessions in a web-app.
75  */

76 public final class SessionManager implements ObjectManager, AlarmListener
77 {
78   static protected final L10N L = new L10N(SessionManager.class);
79   static protected final Logger JavaDoc log
80     = Logger.getLogger(SessionManager.class.getName());
81
82   private static final int FALSE = 0;
83   private static final int COOKIE = 1;
84   private static final int TRUE = 2;
85
86   private static final int UNSET = 0;
87   private static final int SET_TRUE = 1;
88   private static final int SET_FALSE = 2;
89
90   private static final int SAVE_BEFORE_HEADERS = 0x1;
91   private static final int SAVE_BEFORE_FLUSH = 0x2;
92   private static final int SAVE_AFTER_REQUEST = 0x4;
93   private static final int SAVE_ON_SHUTDOWN = 0x8;
94   
95   private static final int DECODE[];
96   
97   private WebApp _webApp;
98   private final SessionManagerAdmin _admin;
99
100   // factory for creating sessions
101
// private SessionFactory _sessionFactory;
102

103   // active sessions
104
private LruCache<String JavaDoc,SessionImpl> _sessions;
105   // total sessions
106
private int _totalSessions;
107   
108   // iterator to purge sessions (to reduce gc)
109
private Iterator JavaDoc<SessionImpl> _sessionIter;
110   // array list for session timeout
111
private ArrayList JavaDoc<SessionImpl> _sessionList = new ArrayList JavaDoc<SessionImpl>();
112   // generate cookies
113
private boolean _enableSessionCookies = true;
114   // allow session rewriting
115
private boolean _enableSessionUrls = true;
116
117   private boolean _isModuloSessionId = false;
118   private boolean _isAppendServerIndex = false;
119   private boolean _isTwoDigitSessionIndex = false;
120   
121   // invalidate the session after the listeners have been called
122
private boolean _isInvalidateAfterListener;
123
124   // maximum number of sessions
125
private int _sessionMax = 4096;
126   // how long a session will be inactive before it times out
127
private long _sessionTimeout = 30 * 60 * 1000;
128
129   private String JavaDoc _cookieName = "JSESSIONID";
130   private String JavaDoc _sslCookieName;
131   
132   // Rewriting strings.
133
private String JavaDoc _sessionSuffix = ";jsessionid=";
134   private String JavaDoc _sessionPrefix;
135   
136   // default cookie version
137
private int _cookieVersion;
138   private String JavaDoc _cookieDomain;
139   private long _cookieMaxAge;
140   private boolean _cookieSecure;
141   private int _isCookieHttpOnly;
142   private String JavaDoc _cookiePort;
143   private int _reuseSessionId = COOKIE;
144   private int _cookieLength = 18;
145
146   private int _sessionSaveMode = SAVE_AFTER_REQUEST;
147
148   //private SessionStore sessionStore;
149
private StoreManager _storeManager;
150
151   // If true, serialization errors should not be logged
152
private boolean _ignoreSerializationErrors = false;
153
154   // List of the HttpSessionListeners from the configuration file
155
private ArrayList JavaDoc<HttpSessionListener JavaDoc> _listeners;
156   
157   // List of the HttpSessionListeners from the configuration file
158
private ArrayList JavaDoc<HttpSessionActivationListener JavaDoc> _activationListeners;
159   
160   // List of the HttpSessionAttributeListeners from the configuration file
161
private ArrayList JavaDoc<HttpSessionAttributeListener JavaDoc> _attributeListeners;
162
163   //
164
// Compatibility fields
165
//
166

167   private boolean _isWebAppStore; // i.e. for old-style compatibility
168
private Store _sessionStore;
169   private int _alwaysLoadSession;
170   private int _alwaysSaveSession;
171
172   private boolean _distributedRing;
173   private Path _persistentPath;
174
175   private boolean _isClosed;
176
177   private String JavaDoc _distributionId;
178   private Cluster _cluster;
179   private ClusterServer _selfServer;
180   private ClusterServer []_srunGroup = new ClusterServer[0];
181
182   private int _srunIndex;
183   private int _srunLength;
184
185   private Alarm _alarm = new Alarm(this);
186
187   // statistics
188
private Object JavaDoc _statisticsLock = new Object JavaDoc();
189   private long _sessionCreateCount;
190   private long _sessionTimeoutCount;
191   private long _sessionInvalidateCount;
192
193   /**
194    * Creates and initializes a new session manager
195    *
196    * @param app the web-app webApp
197    * @param registry the web-app configuration node
198    */

199   public SessionManager(WebApp app)
200     throws Exception JavaDoc
201   {
202     _webApp = app;
203
204     DispatchServer server = app.getDispatchServer();
205     if (server != null) {
206       InvocationDecoder decoder = server.getInvocationDecoder();
207
208       _sessionSuffix = decoder.getSessionURLPrefix();
209       _sessionPrefix = decoder.getAlternateSessionURLPrefix();
210
211       _cookieName = decoder.getSessionCookie();
212       _sslCookieName = decoder.getSSLSessionCookie();
213     }
214     
215     // this.server = app.getVirtualHost().getServer();
216
// this.srunIndex = server.getSrunIndex();
217

218     String JavaDoc hostName = app.getHostName();
219     String JavaDoc contextPath = app.getContextPath();
220     
221     if (hostName == null || hostName.equals(""))
222       hostName = "default";
223
224     String JavaDoc name = hostName + contextPath;
225
226     _distributionId = name;
227
228     _persistentPath = Vfs.lookup("WEB-INF/sessions");
229
230     _admin = new SessionManagerAdmin(this);
231   }
232
233   /**
234    * Returns the admin.
235    */

236   public SessionManagerMXBean getAdmin()
237   {
238     return _admin;
239   }
240
241   /**
242    * Gets the cluster.
243    */

244   protected Cluster getCluster()
245   {
246     synchronized (this) {
247       if (_cluster == null) {
248     _cluster = Cluster.getLocal();
249     ClusterServer selfServer = null;
250
251     if (_cluster != null) {
252       _srunLength = _cluster.getServerList().length;
253       
254       selfServer = _cluster.getSelfServer();
255       _selfServer = selfServer;
256
257       if (selfServer != null) {
258         _srunGroup = _cluster.getServerList();
259         _srunIndex = selfServer.getIndex();
260       }
261     }
262       }
263     }
264
265     return _cluster;
266   }
267
268   /**
269    * Returns the session prefix, ie.. ";jsessionid=".
270    */

271   public String JavaDoc getSessionPrefix()
272   {
273     return _sessionSuffix;
274   }
275
276   /**
277    * Returns the alternate session prefix, before the URL for wap.
278    */

279   public String JavaDoc getAlternateSessionPrefix()
280   {
281     return _sessionPrefix;
282   }
283
284   /**
285    * Returns the cookie version.
286    */

287   public int getCookieVersion()
288   {
289     return _cookieVersion;
290   }
291
292   /**
293    * Sets the cookie version.
294    */

295   public void setCookieVersion(int cookieVersion)
296   {
297     _cookieVersion = cookieVersion;
298   }
299
300   /**
301    * Sets the cookie ports.
302    */

303   public void setCookiePort(String JavaDoc port)
304   {
305     _cookiePort = port;
306   }
307
308   /**
309    * Gets the cookie ports.
310    */

311   public String JavaDoc getCookiePort()
312   {
313     return _cookiePort;
314   }
315
316   /**
317    * Returns the debug log
318    */

319   public Logger JavaDoc getDebug()
320   {
321     return log;
322   }
323
324   /**
325    * Returns the SessionManager's webApp
326    */

327   WebApp getWebApp()
328   {
329     return _webApp;
330   }
331
332   /**
333    * Returns the SessionManager's authenticator
334    */

335   ServletAuthenticator getAuthenticator()
336   {
337     return _webApp.getAuthenticator();
338   }
339
340   /**
341    * Sets the persistent store.
342    */

343   public void setPersistentStore(JndiBuilder store)
344     throws javax.naming.NamingException JavaDoc, ConfigException
345   {
346     _storeManager = (StoreManager) store.getObject();
347
348     if (_storeManager == null)
349       throw new ConfigException(L.l("{0} is an unknown persistent store.",
350                     store.getJndiName()));
351   }
352
353   /**
354    * True if sessions should always be loadd.
355    */

356   boolean getAlwaysLoadSession()
357   {
358     return _alwaysLoadSession == SET_TRUE;
359   }
360
361   /**
362    * True if sessions should always be loadd.
363    */

364   public void setAlwaysLoadSession(boolean load)
365   {
366     _alwaysLoadSession = load ? SET_TRUE : SET_FALSE;
367   }
368
369   /**
370    * True if sessions should always be saved.
371    */

372   boolean getAlwaysSaveSession()
373   {
374     return _alwaysSaveSession == SET_TRUE;
375   }
376
377   /**
378    * True if sessions should always be saved.
379    */

380   public void setAlwaysSaveSession(boolean save)
381   {
382     _alwaysSaveSession = save ? SET_TRUE : SET_FALSE;
383   }
384
385   /**
386    * True if sessions should be saved on shutdown.
387    */

388   public boolean isSaveOnShutdown()
389   {
390     return (_sessionSaveMode & SAVE_ON_SHUTDOWN) != 0;
391   }
392
393   /**
394    * True if sessions should only be saved on shutdown.
395    */

396   public boolean isSaveOnlyOnShutdown()
397   {
398     return (_sessionSaveMode & SAVE_ON_SHUTDOWN) == SAVE_ON_SHUTDOWN;
399   }
400
401   /**
402    * True if sessions should be saved before the HTTP headers.
403    */

404   public boolean isSaveBeforeHeaders()
405   {
406     return (_sessionSaveMode & SAVE_BEFORE_HEADERS) != 0;
407   }
408
409   /**
410    * True if sessions should be saved before each flush.
411    */

412   public boolean isSaveBeforeFlush()
413   {
414     return (_sessionSaveMode & SAVE_BEFORE_FLUSH) != 0;
415   }
416
417   /**
418    * True if sessions should be saved after the request.
419    */

420   public boolean isSaveAfterRequest()
421   {
422     return (_sessionSaveMode & SAVE_AFTER_REQUEST) != 0;
423   }
424
425   /**
426    * Sets the save-mode: before-flush, before-headers, after-request,
427    * on-shutdown
428    */

429   public void setSaveMode(String JavaDoc mode)
430     throws ConfigException
431   {
432     /* XXX: probably don't want to implement this.
433     if ("before-flush".equals(mode)) {
434       _sessionSaveMode = (SAVE_BEFORE_FLUSH|
435               SAVE_BEFORE_HEADERS|
436               SAVE_AFTER_REQUEST|
437               SAVE_ON_SHUTDOWN);
438     }
439     else
440     */

441     
442     if ("before-headers".equals(mode)) {
443       _sessionSaveMode = (SAVE_BEFORE_HEADERS|
444               SAVE_AFTER_REQUEST|
445               SAVE_ON_SHUTDOWN);
446     }
447     else if ("after-request".equals(mode)) {
448       _sessionSaveMode = (SAVE_AFTER_REQUEST|
449               SAVE_ON_SHUTDOWN);
450     }
451     else if ("on-shutdown".equals(mode)) {
452       _sessionSaveMode = (SAVE_ON_SHUTDOWN);
453     }
454     else
455       throw new ConfigException(L.l("'{0}' is an unknown session save-mode. Values are: before-headers, after-request, and on-shutdown.",
456                     mode));
457
458   }
459
460   /**
461    * Returns the string value of the save-mode.
462    */

463   public String JavaDoc getSaveMode()
464   {
465     if (isSaveBeforeFlush())
466       return "before-flush";
467     else if (isSaveBeforeHeaders())
468       return "before-headers";
469     else if (isSaveAfterRequest())
470       return "after-request";
471     else if (isSaveOnShutdown())
472       return "on-shutdown";
473     else
474       return "unknown";
475   }
476
477   /**
478    * True if sessions should only be saved on shutdown.
479    */

480   public void setSaveOnlyOnShutdown(boolean save)
481   {
482     log.warning("<save-only-on-shutdown> is deprecated. Use <save-mode>on-shutdown</save-mode> instead");
483     
484     if (save)
485       _sessionSaveMode = SAVE_ON_SHUTDOWN;
486   }
487
488   /**
489    * True if sessions should only be saved on shutdown.
490    */

491   public void setSaveOnShutdown(boolean save)
492   {
493     log.warning("<save-on-shutdown> is deprecated. Use <save-only-on-shutdown> instead");
494
495     setSaveOnlyOnShutdown(save);
496   }
497
498   /**
499    * True if the session should be invalidated after the listener.
500    */

501   public void setInvalidateAfterListener(boolean inv)
502   {
503     _isInvalidateAfterListener = inv;
504   }
505
506   /**
507    * True if the session should be invalidated after the listener.
508    */

509   public boolean isInvalidateAfterListener()
510   {
511     return _isInvalidateAfterListener;
512   }
513
514   /**
515    * Returns the current number of active sessions.
516    */

517   public int getActiveSessionCount()
518   {
519     if (_sessions == null)
520       return -1;
521     else
522       return _sessions.size();
523   }
524
525   /**
526    * Returns the active sessions.
527    */

528   public int getSessionActiveCount()
529   {
530     return getActiveSessionCount();
531   }
532
533   /**
534    * Returns the created sessions.
535    */

536   public long getSessionCreateCount()
537   {
538     return _sessionCreateCount;
539   }
540
541   /**
542    * Returns the timeout sessions.
543    */

544   public long getSessionTimeoutCount()
545   {
546     return _sessionTimeoutCount;
547   }
548
549   /**
550    * Returns the invalidate sessions.
551    */

552   public long getSessionInvalidateCount()
553   {
554     return _sessionInvalidateCount;
555   }
556
557   /**
558    * Adds a new HttpSessionListener.
559    */

560   public void addListener(HttpSessionListener JavaDoc listener)
561   {
562     if (_listeners == null)
563       _listeners = new ArrayList JavaDoc<HttpSessionListener JavaDoc>();
564
565     _listeners.add(listener);
566   }
567
568   /**
569    * Adds a new HttpSessionListener.
570    */

571   ArrayList JavaDoc<HttpSessionListener JavaDoc> getListeners()
572   {
573     return _listeners;
574   }
575
576   /**
577    * Adds a new HttpSessionActivationListener.
578    */

579   public void addActivationListener(HttpSessionActivationListener JavaDoc listener)
580   {
581     if (_activationListeners == null)
582       _activationListeners = new ArrayList JavaDoc<HttpSessionActivationListener JavaDoc>();
583
584     _activationListeners.add(listener);
585   }
586
587   /**
588    * Returns the activation listeners.
589    */

590   ArrayList JavaDoc<HttpSessionActivationListener JavaDoc> getActivationListeners()
591   {
592     return _activationListeners;
593   }
594
595   /**
596    * Adds a new HttpSessionAttributeListener.
597    */

598   public void addAttributeListener(HttpSessionAttributeListener JavaDoc listener)
599   {
600     if (_attributeListeners == null)
601       _attributeListeners = new ArrayList JavaDoc<HttpSessionAttributeListener JavaDoc>();
602
603     _attributeListeners.add(listener);
604   }
605
606   /**
607    * Gets the HttpSessionAttributeListener.
608    */

609   ArrayList JavaDoc<HttpSessionAttributeListener JavaDoc> getAttributeListeners()
610   {
611     return _attributeListeners;
612   }
613
614   /**
615    * True if serialization errors should just fail silently.
616    */

617   boolean getIgnoreSerializationErrors()
618   {
619     return _ignoreSerializationErrors;
620   }
621
622   /**
623    * True if serialization errors should just fail silently.
624    */

625   public void setIgnoreSerializationErrors(boolean ignore)
626   {
627     _ignoreSerializationErrors = ignore;
628   }
629
630   /**
631    * True if the server should reuse the current session id if the
632    * session doesn't exist.
633    */

634   public int getReuseSessionId()
635   {
636     return _reuseSessionId;
637   }
638
639   /**
640    * True if the server should reuse the current session id if the
641    * session doesn't exist.
642    */

643   public boolean reuseSessionId(boolean fromCookie)
644   {
645     int reuseSessionId = _reuseSessionId;
646     
647     return reuseSessionId == TRUE || fromCookie && reuseSessionId == COOKIE;
648   }
649
650   /**
651    * True if the server should reuse the current session id if the
652    * session doesn't exist.
653    */

654   public void setReuseSessionId(String JavaDoc reuse)
655     throws ConfigException
656   {
657     if (reuse == null)
658       _reuseSessionId = COOKIE;
659     else if (reuse.equalsIgnoreCase("true") ||
660          reuse.equalsIgnoreCase("yes") ||
661          reuse.equalsIgnoreCase("cookie"))
662       _reuseSessionId = COOKIE;
663     else if (reuse.equalsIgnoreCase("false") || reuse.equalsIgnoreCase("no"))
664       _reuseSessionId = FALSE;
665     else if (reuse.equalsIgnoreCase("all"))
666       _reuseSessionId = TRUE;
667     else
668       throw new ConfigException(L.l("'{0}' is an invalid value for reuse-session-id. 'true' or 'false' are the allowed values.",
669                     reuse));
670   }
671
672   /**
673    * Returns the owning server.
674    */

675   ClusterServer getServer(int index)
676   {
677     Cluster cluster = getCluster();
678     
679     if (cluster != null)
680       return cluster.getServer(index);
681     else
682       return null;
683   }
684   
685   /**
686    * Returns the index of this JVM in the ring.
687    */

688   public int getSrunIndex()
689   {
690     return _srunIndex;
691   }
692   
693   /**
694    * Returns the number of sruns in the cluster
695    */

696   public int getSrunLength()
697   {
698     return _srunLength;
699   }
700
701   /**
702    * Returns true if the sessions are closed.
703    */

704   public boolean isClosed()
705   {
706     return _isClosed;
707   }
708
709   /**
710    * Sets the file store.
711    */

712   public StoreManager createFileStore()
713     throws ConfigException
714   {
715     Cluster cluster = getCluster();
716
717     if (cluster == null)
718       throw new ConfigException(L.l("<file-store> needs a defined <cluster>."));
719     
720     if (cluster.getStore() != null)
721       throw new ConfigException(L.l("<file-store> may not be used with a defined <persistent-store>. Use <use-persistent-store> instead."));
722
723     StoreManager fileStore = cluster.createPrivateFileStore();
724     
725     _storeManager = fileStore;
726
727     _isWebAppStore = true;
728
729     return fileStore;
730   }
731
732   /**
733    * Sets the jdbc store.
734    */

735   public StoreManager createJdbcStore()
736     throws ConfigException
737   {
738     Cluster cluster = getCluster();
739
740     if (cluster == null)
741       throw new ConfigException(L.l("<jdbc-store> needs a defined <cluster>."));
742     
743     if (cluster.getStore() != null)
744       throw new ConfigException(L.l("<jdbc-store> may not be used with a defined <persistent-store>. Use <use-persistent-store> instead."));
745     
746     _storeManager = cluster.createJdbcStore();
747
748     _isWebAppStore = true;
749
750     return _storeManager;
751   }
752
753   /**
754    * Sets the tcp store.
755    */

756   public void setTcpStore(boolean isEnable)
757     throws Exception JavaDoc
758   {
759     setClusterStore(isEnable);
760   }
761
762   /**
763    * Sets the cluster store.
764    */

765   public void setClusterStore(boolean isEnable)
766     throws Exception JavaDoc
767   {
768     if (! isEnable)
769       return;
770     
771     Cluster cluster = getCluster();
772
773     if (cluster == null)
774       throw new ConfigException(L.l("<cluster-store> needs a defined <cluster>."));
775     
776     StoreManager store = cluster.getStore();
777
778     if (store == null)
779       throw new ConfigException(L.l("cluster-store in <session-config> requires a configured cluster-store in the <cluster>"));
780     
781     _storeManager = store;
782   }
783
784   /**
785    * Sets the cluster store.
786    */

787   public void setUsePersistentStore(boolean enable)
788     throws Exception JavaDoc
789   {
790     if (! enable)
791       return;
792
793     Cluster cluster = getCluster();
794
795     if (cluster == null)
796       throw new ConfigException(L.l("<use-persistent-store> needs a defined <cluster>."));
797     
798     StoreManager store = cluster.getStore();
799
800     if (store == null) {
801       try {
802     Context JavaDoc ic = new InitialContext JavaDoc();
803     store = (StoreManager) ic.lookup("java:comp/env/caucho/persistent-store");
804       } catch (Throwable JavaDoc e) {
805     log.log(Level.FINER, e.toString(), e);
806       }
807     }
808
809     if (store != null) {
810     }
811     else if (! Config.evalBoolean("${resin.isProfessional()}")) {
812       throw new ConfigException(L.l("use-persistent-store in <session-config> requires Resin professional."));
813     }
814     else
815       throw new ConfigException(L.l("use-persistent-store in <session-config> requires a configured <persistent-store> in the <server>"));
816     
817     if (_isWebAppStore)
818       throw new ConfigException(L.l("use-persistent-store may not be used with <jdbc-store> or <file-store>."));
819     
820     _storeManager = store;
821   }
822
823   /**
824    * Returns the session factory.
825    */

826   public void setPersistentPath(Path path)
827   {
828     _persistentPath = path;
829   }
830
831   public String JavaDoc getDistributionId()
832   {
833     return _distributionId;
834   }
835
836   /**
837    * Returns the default session timeout in milliseconds.
838    */

839   public long getSessionTimeout()
840   {
841     return _sessionTimeout;
842   }
843
844   /**
845    * Set the default session timeout in minutes
846    */

847   public void setSessionTimeout(long timeout)
848   {
849     if (timeout <= 0 || Integer.MAX_VALUE / 2 < timeout)
850       _sessionTimeout = Long.MAX_VALUE / 2;
851     else
852       _sessionTimeout = 60000L * timeout;
853   }
854
855   /**
856    * Returns the idle time.
857    */

858   public long getMaxIdleTime()
859   {
860     return _sessionTimeout;
861   }
862
863   /**
864    * Returns the maximum number of sessions.
865    */

866   public int getSessionMax()
867   {
868     return _sessionMax;
869   }
870
871   /**
872    * Returns the maximum number of sessions.
873    */

874   public void setSessionMax(int max)
875   {
876     _sessionMax = max;
877   }
878
879   /**
880    * Returns true if sessions use the cookie header.
881    */

882   public boolean enableSessionCookies()
883   {
884     return _enableSessionCookies;
885   }
886
887   /**
888    * Returns true if sessions use the cookie header.
889    */

890   public void setEnableCookies(boolean enableCookies)
891   {
892     _enableSessionCookies = enableCookies;
893   }
894   
895   /**
896    * Returns true if sessions can use the session rewriting.
897    */

898   public boolean enableSessionUrls()
899   {
900     return _enableSessionUrls;
901   }
902   
903   /**
904    * Returns true if sessions can use the session rewriting.
905    */

906   public void setEnableUrlRewriting(boolean enableUrls)
907   {
908     _enableSessionUrls = enableUrls;
909   }
910
911   /**
912    * Returns the default cookie name.
913    */

914   public String JavaDoc getCookieName()
915   {
916     return _cookieName;
917   }
918
919   /**
920    * Returns the SSL cookie name.
921    */

922   public String JavaDoc getSSLCookieName()
923   {
924     if (_sslCookieName != null)
925       return _sslCookieName;
926     else
927       return _cookieName;
928   }
929
930   /**
931    * Returns the default session cookie domain.
932    */

933   public String JavaDoc getCookieDomain()
934   {
935     return _cookieDomain;
936   }
937
938   /**
939    * Sets the default session cookie domain.
940    */

941   public void setCookieDomain(String JavaDoc domain)
942   {
943     _cookieDomain = domain;
944   }
945
946   /**
947    * Returns the max-age of the session cookie.
948    */

949   public long getCookieMaxAge()
950   {
951     return _cookieMaxAge;
952   }
953
954   /**
955    * Sets the max-age of the session cookie.
956    */

957   public void setCookieMaxAge(Period maxAge)
958   {
959     _cookieMaxAge = maxAge.getPeriod();
960   }
961
962   /**
963    * Returns the secure of the session cookie.
964    */

965   public boolean getCookieSecure()
966   {
967     if (_cookieSecure)
968       return true;
969     else
970       return ! _cookieName.equals(_sslCookieName);
971   }
972
973   /**
974    * Sets the secure of the session cookie.
975    */

976   public void setCookieSecure(boolean secure)
977   {
978     _cookieSecure = secure;
979   }
980
981   /**
982    * Returns the http-only of the session cookie.
983    */

984   public boolean isCookieHttpOnly()
985   {
986     if (_isCookieHttpOnly == SET_TRUE)
987       return true;
988     else if (_isCookieHttpOnly == SET_FALSE)
989       return true;
990     else
991       return getWebApp().getCookieHttpOnly();
992   }
993
994   /**
995    * Sets the http-only of the session cookie.
996    */

997   public void setCookieHttpOnly(boolean httpOnly)
998   {
999     _isCookieHttpOnly = httpOnly ? SET_TRUE : SET_FALSE;
1000  }
1001
1002  /**
1003   * Sets the cookie length
1004   */

1005  public void setCookieLength(int cookieLength)
1006  {
1007    if (cookieLength < 7)
1008      cookieLength = 7;
1009
1010    _cookieLength = cookieLength;
1011  }
1012
1013  /**
1014   * Returns the cookie length.
1015   */

1016  public long getCookieLength()
1017  {
1018    return _cookieLength;
1019  }
1020
1021  /**
1022   * Sets module session id generation.
1023   */

1024  public void setCookieModuloCluster(boolean isModulo)
1025  {
1026    _isModuloSessionId = isModulo;
1027  }
1028
1029  /**
1030   * Sets module session id generation.
1031   */

1032  public void setCookieAppendServerIndex(boolean isAppend)
1033  {
1034    _isAppendServerIndex = isAppend;
1035  }
1036
1037  /**
1038   * Sets module session id generation.
1039   */

1040  public boolean isCookieAppendServerIndex()
1041  {
1042    return _isAppendServerIndex;
1043  }
1044
1045  public void init()
1046  {
1047    if (_sessionSaveMode == SAVE_ON_SHUTDOWN
1048    && (_alwaysSaveSession == SET_TRUE
1049        || _alwaysLoadSession == SET_TRUE))
1050      throw new ConfigException(L.l("save-mode='on-shutdown' cannot be used with <always-save-session/> or <always-load-session/>"));
1051  }
1052
1053  public void start()
1054    throws Exception JavaDoc
1055  {
1056    _sessions = new LruCache<String JavaDoc,SessionImpl>(_sessionMax);
1057    _sessionIter = _sessions.values();
1058
1059    if (_cluster == null)
1060      getCluster();
1061
1062    if (_isWebAppStore) {
1063      // for backward compatibility
1064

1065      if (_alwaysLoadSession == SET_TRUE)
1066    _storeManager.setAlwaysLoad(true);
1067      else if (_alwaysLoadSession == SET_FALSE)
1068    _storeManager.setAlwaysLoad(false);
1069      
1070      if (_alwaysSaveSession == SET_TRUE)
1071    _sessionStore.setAlwaysSave(true);
1072      else if (_alwaysSaveSession == SET_FALSE)
1073    _sessionStore.setAlwaysSave(false);
1074
1075      _storeManager.init();
1076
1077      _storeManager.updateIdleCheckInterval(_sessionTimeout);
1078    }
1079
1080    if (_storeManager != null) {
1081      _sessionStore = _storeManager.createStore(_distributionId, this);
1082      _sessionStore.setMaxIdleTime(_sessionTimeout);
1083      
1084      if (_alwaysLoadSession == SET_TRUE)
1085    _sessionStore.setAlwaysLoad(true);
1086      else if (_alwaysLoadSession == SET_FALSE)
1087    _sessionStore.setAlwaysLoad(false);
1088      
1089      if (_alwaysSaveSession == SET_TRUE)
1090    _sessionStore.setAlwaysSave(true);
1091      else if (_alwaysSaveSession == SET_FALSE)
1092    _sessionStore.setAlwaysSave(false);
1093    }
1094
1095    _alarm.queue(60000);
1096  }
1097
1098  /**
1099   * Returns the session store.
1100   */

1101  public Store getSessionStore()
1102  {
1103    return _sessionStore;
1104  }
1105
1106  /**
1107   * Create a new session.
1108   *
1109   * @param oldId the id passed to the request. Reuse if possible.
1110   * @param now the current date
1111   * @param sessionGroup the srun index for this machine
1112   */

1113  public SessionImpl createSession(String JavaDoc oldId, long now,
1114                                   HttpServletRequest JavaDoc request,
1115                   boolean fromCookie)
1116  {
1117    String JavaDoc id = oldId;
1118
1119    if (id == null || id.length() < 4 ||
1120    ! isInSessionGroup(id) || ! reuseSessionId(fromCookie)) {
1121      id = createSessionId(request, true);
1122    }
1123
1124    SessionImpl session = create(id, now, true);
1125
1126    if (session == null)
1127      return null;
1128
1129    session.addUse();
1130
1131    synchronized (_statisticsLock) {
1132      _sessionCreateCount++;
1133    }
1134
1135    synchronized (session) {
1136      if (_sessionStore != null && id.equals(oldId))
1137        load(session, now);
1138      else
1139    session.create(now);
1140    }
1141
1142    // after load so a reset doesn't clear any setting
1143
handleCreateListeners(session);
1144    
1145    return session;
1146  }
1147
1148  /**
1149   * Creates a pseudo-random session id. If there's an old id and the
1150   * group matches, then use it because different webApps on the
1151   * same matchine should use the same cookie.
1152   *
1153   * @param sessionGroup possibly assigned by the web server
1154   */

1155  public String JavaDoc createSessionId(HttpServletRequest JavaDoc request)
1156  {
1157    return createSessionId(request, false);
1158  }
1159
1160  /**
1161   * Creates a pseudo-random session id. If there's an old id and the
1162   * group matches, then use it because different webApps on the
1163   * same machine should use the same cookie.
1164   *
1165   * @param sessionGroup possibly assigned by the web server
1166   */

1167  public String JavaDoc createSessionId(HttpServletRequest JavaDoc request,
1168                                boolean create)
1169  {
1170    String JavaDoc id;
1171
1172    do {
1173      id = createSessionIdImpl();
1174    } while (create && getSession(id, 0, create, true) != null);
1175
1176    if (id == null || id.equals(""))
1177      throw new RuntimeException JavaDoc();
1178
1179    return id;
1180  }
1181
1182  public String JavaDoc createSessionIdImpl()
1183  {
1184    StringBuffer JavaDoc cb = new StringBuffer JavaDoc();
1185    // this section is the host specific session index
1186
// the most random bit is the high bit
1187
int index = _srunIndex;
1188
1189    if (index < 0)
1190      index = 0;
1191
1192    int length = _cookieLength;
1193
1194    addBackup(cb, index);
1195
1196    long random = RandomUtil.getRandomLong();
1197
1198    for (int i = 0; i < 11 && length-- > 0; i++) {
1199      cb.append(convert(random));
1200      random = random >> 6;
1201    }
1202
1203    if (length > 0) {
1204      long time = Alarm.getCurrentTime();
1205      for (int i = 0; i < 7 && length-- > 0; i++) {
1206        cb.append(convert(time));
1207        time = time >> 6;
1208      }
1209    }
1210
1211    while (length > 0) {
1212      random = RandomUtil.getRandomLong();
1213      for (int i = 0; i < 11 && length-- > 0; i++) {
1214        cb.append(convert(random));
1215        random = random >> 6;
1216      }
1217    }
1218
1219    if (_isAppendServerIndex) {
1220      cb.append('.');
1221      cb.append((index + 1));
1222    }
1223
1224    return cb.toString();
1225  }
1226
1227  /**
1228   * Adds the primary/backup/third digits to the session id.
1229   */

1230  private void addBackup(StringBuffer JavaDoc cb, int index)
1231  {
1232    long backupCode;
1233
1234    if (_selfServer != null)
1235      backupCode = _selfServer.generateBackupCode();
1236    else
1237      backupCode = 0x000200010000L;
1238    
1239    addDigit(cb, (int) (backupCode & 0xffff));
1240    addDigit(cb, (int) ((backupCode >> 16) & 0xffff));
1241    addDigit(cb, (int) ((backupCode >> 32) & 0xffff));
1242  }
1243
1244  private void addDigit(StringBuffer JavaDoc cb, int digit)
1245  {
1246    if (_srunLength <= 64 && ! _isTwoDigitSessionIndex)
1247      cb.append(convert(digit));
1248    else {
1249      cb.append(convert(digit / 64));
1250      cb.append(convert(digit));
1251    }
1252  }
1253
1254  /**
1255   * Returns a session from the session store, returning null if there's
1256   * no cached session.
1257   *
1258   * @param key the session id
1259   * @param now the time in milliseconds
1260   *
1261   * @return the cached session.
1262   */

1263  public SessionImpl getSession(String JavaDoc key, long now,
1264                boolean create, boolean fromCookie)
1265  {
1266    SessionImpl session;
1267    boolean isNew = false;
1268    boolean killSession = false;
1269
1270    if (_sessions == null)
1271      return null;
1272
1273    session = _sessions.get(key);
1274
1275    if (session != null && ! session.getId().equals(key))
1276      throw new IllegalStateException JavaDoc(key + " != " + session.getId());
1277
1278    if (now <= 0) // just generating id
1279
return session;
1280
1281    if (session != null && ! session.addUse()) {
1282      session = null;
1283    }
1284    
1285    if (session == null && _sessionStore != null) {
1286      if (! isInSessionGroup(key))
1287    return null;
1288
1289      session = create(key, now, create);
1290
1291      if (! session.addUse())
1292    session = null;
1293      isNew = true;
1294    }
1295
1296    if (session == null)
1297      return null;
1298    
1299    if (isNew) {
1300      killSession = ! load(session, now);
1301      isNew = killSession;
1302    }
1303    else if (! session.load()) {
1304      // if the load failed, then the session died out from underneath
1305
session.reset(now);
1306      isNew = true;
1307    }
1308
1309    if (killSession && (! create || ! reuseSessionId(fromCookie))) {
1310      // XXX: session.setClosed();
1311
session.endUse();
1312      session._isValid = false;
1313      _sessions.remove(key);
1314        
1315      return null;
1316    }
1317    else if (isNew)
1318      handleCreateListeners(session);
1319    else
1320      session.setAccess(now);
1321    
1322    return session;
1323  }
1324
1325  public boolean isInSessionGroup(String JavaDoc id)
1326  {
1327    if (_srunLength == 0 || _srunGroup.length == 0)
1328      return true;
1329
1330    int group = decode(id.charAt(0)) % _srunLength;
1331
1332    for (int i = _srunGroup.length - 1; i >= 0; i--) {
1333      ClusterServer server = _srunGroup[i];
1334      
1335      if (server != null && group == server.getIndex())
1336        return true;
1337    }
1338    
1339    return false;
1340  }
1341
1342  /**
1343   * Creates a session. It's already been established that the
1344   * key does not currently have a session.
1345   */

1346  private SessionImpl create(String JavaDoc key, long now, boolean isCreate)
1347  {
1348    SessionImpl session = new SessionImpl(this, key, now);
1349
1350    // If another thread has created and stored a new session,
1351
// putIfNew will return the old session
1352
session = _sessions.putIfNew(key, session);
1353
1354    if (! key.equals(session.getId()))
1355      throw new IllegalStateException JavaDoc(key + " != " + session.getId());
1356
1357    Store sessionStore = _sessionStore;
1358    if (sessionStore != null) {
1359      ClusterObject clusterObject = sessionStore.createClusterObject(key);
1360      session.setClusterObject(clusterObject);
1361    }
1362
1363    return session;
1364  }
1365
1366  /**
1367   * Notification from the cluster.
1368   */

1369  public void notifyRemove(String JavaDoc id)
1370  {
1371    SessionImpl session = _sessions.remove(id);
1372
1373    if (session != null)
1374      session.invalidateImpl(true);
1375  }
1376
1377  /**
1378   * Notification from the cluster.
1379   */

1380  public void notifyUpdate(String JavaDoc id)
1381  {
1382  }
1383
1384  /**
1385   * Converts an integer to a printable character
1386   */

1387  private static char convert(long code)
1388  {
1389    code = code & 0x3f;
1390    
1391    if (code < 26)
1392      return (char) ('a' + code);
1393    else if (code < 52)
1394      return (char) ('A' + code - 26);
1395    else if (code < 62)
1396      return (char) ('0' + code - 52);
1397    else if (code == 62)
1398      return '_';
1399    else
1400      return '-';
1401  }
1402
1403  public static int decode(int code)
1404  {
1405    return DECODE[code & 0x7f];
1406  }
1407
1408  private void handleCreateListeners(SessionImpl session)
1409  {
1410    if (_listeners != null) {
1411      HttpSessionEvent JavaDoc event = new HttpSessionEvent JavaDoc(session);
1412      
1413      for (int i = 0; i < _listeners.size(); i++) {
1414        HttpSessionListener JavaDoc listener = _listeners.get(i);
1415
1416        listener.sessionCreated(event);
1417      }
1418    }
1419  }
1420
1421  /**
1422   * Loads the session from the backing store. The caller must synchronize
1423   * the session.
1424   *
1425   * @param session the session to load.
1426   * @param now current time in milliseconds.
1427   */

1428  private boolean load(SessionImpl session, long now)
1429  {
1430    try {
1431      // XXX: session.setNeedsLoad(false);
1432

1433      /*
1434      if (session.getUseCount() > 1) {
1435    // if used by more than just us,
1436    return true;
1437      }
1438      else*/
if (now <= 0) {
1439        return false;
1440      }
1441      else if (session.load()) {
1442        session.setAccess(now);
1443        return true;
1444      }
1445      else {
1446        session.create(now);
1447      }
1448    } catch (Exception JavaDoc e) {
1449      e.printStackTrace();
1450      log.log(Level.FINE, e.toString(), e);
1451      session.reset(now);
1452    }
1453    
1454    return false;
1455  }
1456
1457  /**
1458   * Adds a session from the cache.
1459   */

1460  void addSession(SessionImpl session)
1461  {
1462    _sessions.put(session.getId(), session);
1463  }
1464
1465  /**
1466   * Invalidates a session from the cache.
1467   */

1468  public void invalidateSession(SessionImpl session)
1469  {
1470    removeSession(session);
1471
1472    synchronized (_statisticsLock) {
1473      _sessionInvalidateCount++;
1474    }
1475
1476    if (_sessionStore != null) {
1477      try {
1478    _sessionStore.remove(session.getId());
1479      } catch (Exception JavaDoc e) {
1480    log.log(Level.WARNING, e.toString(), e);
1481      }
1482    }
1483  }
1484
1485  /**
1486   * Removes a session from the cache.
1487   */

1488  public void removeSession(SessionImpl session)
1489  {
1490    _sessions.remove(session.getId());
1491  }
1492
1493  /**
1494   * Loads the session.
1495   *
1496   * @param in the input stream containing the serialized session
1497   * @param obj the session object to be deserialized
1498   */

1499  public void load(ObjectInputStream JavaDoc in, Object JavaDoc obj)
1500    throws IOException JavaDoc
1501  {
1502    SessionImpl session = (SessionImpl) obj;
1503
1504    session.load(in);
1505  }
1506
1507  /**
1508   * Checks if the session is empty.
1509   */

1510  public boolean isEmpty(Object JavaDoc obj)
1511  {
1512    SessionImpl session = (SessionImpl) obj;
1513
1514    return session.isEmpty();
1515  }
1516
1517  /**
1518   * Saves the session.
1519   */

1520  public void store(ObjectOutputStream JavaDoc out, Object JavaDoc obj)
1521    throws IOException JavaDoc
1522  {
1523    SessionImpl session = (SessionImpl) obj;
1524
1525    session.store(out);
1526  }
1527
1528  /**
1529   * Timeout for reaping old sessions
1530   *
1531   * @return number of live sessions for stats
1532   */

1533  public void handleAlarm(Alarm alarm)
1534  {
1535    try {
1536      _sessionList.clear();
1537
1538      int liveSessions = 0;
1539
1540      if (_isClosed)
1541    return;
1542
1543      long now = Alarm.getCurrentTime();
1544      long accessWindow = 0;
1545
1546      if (_sessionStore != null)
1547    accessWindow = _sessionStore.getAccessWindowTime();
1548      
1549      synchronized (_sessions) {
1550    _sessionIter = _sessions.values(_sessionIter);
1551    while (_sessionIter.hasNext()) {
1552      SessionImpl session = _sessionIter.next();
1553
1554      long maxIdleTime = session._maxInactiveInterval + accessWindow;
1555
1556      if (session.inUse())
1557        liveSessions++;
1558      else if (session._accessTime + maxIdleTime < now)
1559        _sessionList.add(session);
1560      else
1561        liveSessions++;
1562    }
1563      }
1564
1565      synchronized (_statisticsLock) {
1566    _sessionTimeoutCount += _sessionList.size();
1567      }
1568
1569      for (int i = 0; i < _sessionList.size(); i++) {
1570    SessionImpl session = _sessionList.get(i);
1571
1572    try {
1573      long maxIdleTime = session._maxInactiveInterval;
1574
1575      if (_storeManager == null) {
1576        // if no persistent store then invalidate
1577
// XXX: server/12cg - single signon shouldn't logout
1578
session.invalidate();
1579      }
1580      else if (session.getSrunIndex() != _srunIndex && _srunIndex >= 0) {
1581        // if not the owner, then just remove
1582
_sessions.remove(session.getId());
1583      }
1584      else {
1585        session.invalidate();
1586      }
1587    } catch (Throwable JavaDoc e) {
1588      log.log(Level.FINER, e.toString(), e);
1589    }
1590      }
1591    } finally {
1592      if (! _isClosed)
1593    _alarm.queue(60000);
1594    }
1595  }
1596
1597  /**
1598   * Cleans up the sessions when the WebApp shuts down gracefully.
1599   */

1600  public void close()
1601  {
1602    synchronized (this) {
1603      if (_isClosed)
1604        return;
1605      _isClosed = true;
1606    }
1607
1608    if (_sessions == null)
1609      return;
1610
1611    _alarm.dequeue();
1612    
1613    _sessionList.clear();
1614
1615    ArrayList JavaDoc<SessionImpl> list = new ArrayList JavaDoc<SessionImpl>();
1616    
1617    boolean isError = false;
1618    // XXX: messy way of dealing with saveOnlyOnShutdown
1619
synchronized (_sessions) {
1620      _sessionIter = _sessions.values(_sessionIter);
1621      while (_sessionIter.hasNext()) {
1622        SessionImpl session = _sessionIter.next();
1623
1624        if (session.isValid())
1625          list.add(session);
1626      }
1627
1628      // XXX: if cleared here, will remove the session
1629
// _sessions.clear();
1630
}
1631
1632    for (int i = list.size() - 1; i >= 0; i--) {
1633      SessionImpl session = list.get(i);
1634      
1635      if (log.isLoggable(Level.FINE))
1636        log.fine("close session " + session.getId());
1637      
1638      try {
1639        if (session.isValid()) {
1640          synchronized (session) {
1641        // server/016i, server/018x
1642
if (! session.isEmpty())
1643          session.saveOnShutdown();
1644          }
1645        }
1646
1647        _sessions.remove(session.getId());
1648      } catch (Exception JavaDoc e) {
1649        if (! isError)
1650          log.log(Level.WARNING, "Can't store session: " + e, e);
1651        isError = true;
1652      }
1653    }
1654
1655    if (_admin != null)
1656      _admin.unregister();
1657
1658    /*
1659    if (_clusterManager != null)
1660      _clusterManager.removeContext(_distributionId);
1661    */

1662  }
1663
1664  static {
1665    DECODE = new int[128];
1666    for (int i = 0; i < 64; i++)
1667      DECODE[(int) convert(i)] = i;
1668  }
1669}
1670
Popular Tags