KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > com > caucho > quercus > lib > session > QuercusSessionManager


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 Emil Ong
28  */

29
30 package com.caucho.quercus.lib.session;
31
32 import com.caucho.config.ConfigException;
33 import com.caucho.quercus.env.Env;
34 import com.caucho.quercus.env.SessionArrayValue;
35 import com.caucho.util.*;
36
37 import java.io.IOException JavaDoc;
38 import java.io.ObjectInputStream JavaDoc;
39 import java.io.ObjectOutputStream JavaDoc;
40 import java.util.ArrayList JavaDoc;
41 import java.util.Iterator JavaDoc;
42 import java.util.logging.Level JavaDoc;
43 import java.util.logging.Logger JavaDoc;
44
45 /**
46  * Stripped down version of com.caucho.server.session.SessionManager,
47  * customized to PHP instead of J2EE sessions.
48  */

49 public class QuercusSessionManager {
50   static protected final L10N L = new L10N(QuercusSessionManager.class);
51   static protected final Logger JavaDoc log
52     = Logger.getLogger(QuercusSessionManager.class.getName());
53
54   private static int FALSE = 0;
55   private static int COOKIE = 1;
56   private static int TRUE = 2;
57
58   private static int UNSET = 0;
59   private static int SET_TRUE = 1;
60   private static int SET_FALSE = 2;
61   
62   // active sessions
63
protected LruCache<String JavaDoc,SessionArrayValue> _sessions;
64   // total sessions
65
private int _totalSessions;
66
67   // iterator to purge sessions (to reduce gc)
68
protected Iterator JavaDoc<SessionArrayValue> _sessionIter;
69   // array list for session timeout
70
protected ArrayList JavaDoc<SessionArrayValue> _sessionList
71     = new ArrayList JavaDoc<SessionArrayValue>();
72
73   // maximum number of sessions
74
protected int _sessionMax = 4096;
75   private long _sessionTimeout = 30 * 60 * 1000L;
76
77   private int _reuseSessionId = COOKIE;
78   private int _cookieLength = 18;
79
80   private int _alwaysLoadSession;
81   private boolean _alwaysSaveSession;
82   private boolean _saveOnlyOnShutdown;
83
84   private boolean _isModuloSessionId = false;
85   private boolean _isAppendServerIndex = false;
86   private boolean _isTwoDigitSessionIndex = false;
87   
88   protected boolean _isClosed;
89
90   //private Alarm _alarm = new Alarm(this);
91

92   // statistics
93
protected Object JavaDoc _statisticsLock = new Object JavaDoc();
94   protected long _sessionCreateCount;
95   protected long _sessionTimeoutCount;
96
97   /**
98    * Creates and initializes a new session manager.
99    */

100   public QuercusSessionManager()
101   {
102     _sessions = new LruCache<String JavaDoc,SessionArrayValue>(_sessionMax);
103     _sessionIter = _sessions.values();
104   }
105
106   /**
107    * True if sessions should always be saved.
108    */

109   boolean getAlwaysSaveSession()
110   {
111     return _alwaysSaveSession;
112   }
113
114   /**
115    * True if sessions should always be saved.
116    */

117   public void setAlwaysSaveSession(boolean save)
118   {
119     _alwaysSaveSession = save;
120   }
121
122   /**
123    * True if sessions should always be loadd.
124    */

125   boolean getAlwaysLoadSession()
126   {
127     return _alwaysLoadSession == SET_TRUE;
128   }
129
130   /**
131    * True if sessions should always be loadd.
132    */

133   public void setAlwaysLoadSession(boolean load)
134   {
135     _alwaysLoadSession = load ? SET_TRUE : SET_FALSE;
136   }
137
138   /**
139    * True if sessions should only be saved on shutdown.
140    */

141   public boolean getSaveOnlyOnShutdown()
142   {
143     return _saveOnlyOnShutdown;
144   }
145
146   /**
147    * True if sessions should only be saved on shutdown.
148    */

149   public void setSaveOnlyOnShutdown(boolean save)
150   {
151     _saveOnlyOnShutdown = save;
152   }
153
154   /**
155    * True if sessions should only be saved on shutdown.
156    */

157   public void setSaveOnShutdown(boolean save)
158   {
159     log.warning("<save-on-shutdown> is deprecated. Use <save-only-on-shutdown> instead");
160
161     setSaveOnlyOnShutdown(save);
162   }
163
164   /**
165    * Sets the cookie length
166    */

167   public void setCookieLength(int cookieLength)
168   {
169     if (cookieLength < 7)
170       cookieLength = 7;
171
172     _cookieLength = cookieLength;
173   }
174
175   /**
176    * Returns the current number of active sessions.
177    */

178   public int getActiveSessionCount()
179   {
180     if (_sessions == null)
181       return -1;
182     else
183       return _sessions.size();
184   }
185
186   /**
187    * Returns the created sessions.
188    */

189   public long getSessionCreateCount()
190   {
191     return _sessionCreateCount;
192   }
193
194   /**
195    * Returns the timeout sessions.
196    */

197   public long getSessionTimeoutCount()
198   {
199     return _sessionTimeoutCount;
200   }
201
202   /**
203    * True if the server should reuse the current session id if the
204    * session doesn't exist.
205    */

206   public int getReuseSessionId()
207   {
208     return _reuseSessionId;
209   }
210
211   /**
212    * True if the server should reuse the current session id if the
213    * session doesn't exist.
214    */

215   public void setReuseSessionId(String JavaDoc reuse)
216     throws ConfigException
217   {
218     if (reuse == null)
219       _reuseSessionId = COOKIE;
220     else if (reuse.equalsIgnoreCase("true") ||
221          reuse.equalsIgnoreCase("yes") ||
222          reuse.equalsIgnoreCase("cookie"))
223       _reuseSessionId = COOKIE;
224     else if (reuse.equalsIgnoreCase("false") || reuse.equalsIgnoreCase("no"))
225       _reuseSessionId = FALSE;
226     else if (reuse.equalsIgnoreCase("all"))
227       _reuseSessionId = TRUE;
228     else
229       throw new ConfigException(L.l("'{0}' is an invalid value for reuse-session-id. 'true' or 'false' are the allowed values.",
230                     reuse));
231   }
232
233   /**
234    * Returns true if the sessions are closed.
235    */

236   public boolean isClosed()
237   {
238     return _isClosed;
239   }
240
241   /**
242    * Returns the maximum number of sessions.
243    */

244   public int getSessionMax()
245   {
246     return _sessionMax;
247   }
248
249   /**
250    * Returns the maximum number of sessions.
251    */

252   public void setSessionMax(int max)
253   {
254     _sessionMax = max;
255   }
256   
257   /**
258    * Removes a session from the cache and deletes it from the backing store,
259    * if applicable.
260    */

261   public void removeSession(String JavaDoc sessionId)
262   {
263     _sessions.remove(sessionId);
264
265     remove(sessionId);
266   }
267
268   protected void remove(String JavaDoc sessionId)
269   {
270   }
271
272   /**
273    * Loads the session.
274    *
275    * @param in the input stream containing the serialized session
276    * @param obj the session object to be deserialized
277    */

278   public void load(ObjectInputStream JavaDoc in, Object JavaDoc obj)
279     throws IOException JavaDoc
280   {
281     SessionArrayValue session = (SessionArrayValue) obj;
282
283     session.load(null, in);
284   }
285
286   /**
287    * Checks if the session is empty.
288    */

289   public boolean isEmpty(Object JavaDoc obj)
290   {
291     SessionArrayValue session = (SessionArrayValue) obj;
292
293     return session.isEmpty();
294   }
295
296   /**
297    * Sets module session id generation.
298    */

299   public void setCookieAppendServerIndex(boolean isAppend)
300   {
301     _isAppendServerIndex = isAppend;
302   }
303
304   /**
305    * Create a new session.
306    *
307    * @param oldId the id passed to the request. Reuse if possible.
308    * @param now the current date
309    *
310    */

311   public SessionArrayValue createSession(Env env, String JavaDoc oldId, long now)
312   {
313     String JavaDoc id = oldId;
314
315     if (id == null || id.length() < 4)
316       id = createSessionId(env);
317
318     SessionArrayValue session = create(env, id, now);
319
320     if (session == null)
321       return null;
322     
323     synchronized (_statisticsLock) {
324       _sessionCreateCount++;
325     }
326     
327     return session;
328   }
329
330   /**
331    * Creates a pseudo-random session id. If there's an old id and the
332    * group matches, then use it because different applications on the
333    * same matchine should use the same cookie.
334    */

335   public String JavaDoc createSessionId(Env env)
336   {
337     String JavaDoc id;
338
339     do {
340       CharBuffer sb = new CharBuffer();
341
342       Base64.encode(sb, RandomUtil.getRandomLong());
343       Base64.encode(sb, Alarm.getCurrentTime());
344
345       id = sb.toString();
346     } while (getSession(env, id, 0) != null);
347
348     if (id == null || id.equals(""))
349       throw new RuntimeException JavaDoc();
350
351     return id;
352   }
353
354   /**
355    * Returns a session from the session store, returning null if there's
356    * no cached session.
357    *
358    * @param key the session id
359    * @param now the time in milliseconds. now == 0 implies
360    * that we're just checking for the existence of such a session in
361    * the cache and do not intend actually to load it if it is not.
362    *
363    * @return the cached session.
364    *
365    */

366   public SessionArrayValue getSession(Env env, String JavaDoc key, long now)
367   {
368     SessionArrayValue session;
369     boolean isNew = false;
370     boolean killSession = false;
371
372     if (_sessions == null)
373       return null;
374
375     // Check the cache first
376
session = _sessions.get(key);
377
378     if (session != null && ! session.getId().equals(key))
379       throw new IllegalStateException JavaDoc(key + " != " + session.getId());
380
381     if (session != null) {
382       if (session.inUse())
383         return (SessionArrayValue)session.copy(env);
384     }
385
386     if (session == null)
387       return null;
388
389     if (isNew) {
390       isNew = ! load(env, session, now);
391     }
392     else if (! getSaveOnlyOnShutdown() && ! session.load()) {
393       // if the load failed, then the session died out from underneath
394
session.reset(now);
395       isNew = true;
396     }
397
398     if (! isNew)
399       session.setAccess(now);
400
401     return (SessionArrayValue)session.copy(env);
402   }
403
404   public void saveSession(Env env, SessionArrayValue session)
405   {
406     SessionArrayValue copy = (SessionArrayValue) session.copy(env);
407
408     _sessions.put(session.getId(), copy);
409     session.finish();
410   }
411
412   /**
413    * Creates a session. It's already been established that the
414    * key does not currently have a session.
415    */

416   protected SessionArrayValue create(Env env, String JavaDoc key, long now)
417   {
418     SessionArrayValue session
419       = new SessionArrayValue(key, now, _sessionTimeout);
420
421     // If another thread has created and stored a new session,
422
// putIfNew will return the old session
423
session = _sessions.putIfNew(key, session);
424
425     if (! key.equals(session.getId()))
426       throw new IllegalStateException JavaDoc(key + " != " + session.getId());
427
428     return (SessionArrayValue)session.copy(env);
429   }
430
431   /**
432    * Loads the session from the backing store.
433    *
434    * @param session the session to load.
435    * @param now current time in milliseconds. now == 0 implies
436    * that we're just checking for the existence of such a session in
437    * the cache and do not intend actually to load it if it is not.
438    *
439    */

440   protected boolean load(Env env, SessionArrayValue session, long now)
441   {
442     try {
443       if (session.inUse()) {
444         return true;
445       }
446       else if (now <= 0) {
447         return false;
448       }
449       else if (session.load()) {
450         session.setAccess(now);
451         return true;
452       }
453       else {
454         session.reset(now);
455       }
456     } catch (Exception JavaDoc e) {
457       log.log(Level.FINE, e.toString(), e);
458       session.reset(now);
459     }
460
461     return false;
462   }
463
464   /**
465    * Timeout for reaping old sessions.
466    */

467   public void handleAlarm(Alarm alarm)
468   {
469     try {
470       _sessionList.clear();
471
472       int liveSessions = 0;
473
474       if (_isClosed)
475         return;
476
477       long now = Alarm.getCurrentTime();
478       long accessWindow = 0;
479
480       synchronized (_sessions) {
481         _sessionIter = _sessions.values(_sessionIter);
482
483         while (_sessionIter.hasNext()) {
484           SessionArrayValue session = _sessionIter.next();
485
486           long maxIdleTime = session.getMaxInactiveInterval() + accessWindow;
487
488           if (session.inUse())
489             liveSessions++;
490           else if (session.getAccessTime() + maxIdleTime < now)
491             _sessionList.add(session);
492           else
493             liveSessions++;
494         }
495       }
496
497       synchronized (_statisticsLock) {
498         _sessionTimeoutCount += _sessionList.size();
499       }
500
501       for (int i = 0; i < _sessionList.size(); i++) {
502         SessionArrayValue session = _sessionList.get(i);
503
504         try {
505           long maxIdleTime = session.getMaxInactiveInterval();
506           _sessions.remove(session.getId());
507
508       session.invalidate();
509         } catch (Throwable JavaDoc e) {
510           log.log(Level.FINER, e.toString(), e);
511         }
512       }
513     } finally {
514       /*
515       if (! _isClosed)
516         _alarm.queue(60000);
517       */

518     }
519   }
520
521   /**
522    * Cleans up the sessions when the Application shuts down gracefully.
523    */

524   public void close()
525   {
526     synchronized (this) {
527       if (_isClosed)
528         return;
529       _isClosed = true;
530     }
531
532     if (_sessions == null)
533       return;
534
535     //_alarm.dequeue();
536

537     _sessionList.clear();
538
539     ArrayList JavaDoc<SessionArrayValue> list = new ArrayList JavaDoc<SessionArrayValue>();
540
541     boolean isError = false;
542
543     synchronized (_sessions) {
544       _sessionIter = _sessions.values(_sessionIter);
545
546       while (_sessionIter.hasNext()) {
547         SessionArrayValue session = _sessionIter.next();
548
549         if (session.isValid())
550           list.add(session);
551       }
552     }
553
554     for (int i = list.size() - 1; i >= 0; i--) {
555       SessionArrayValue session = list.get(i);
556
557       try {
558         if (session.isValid()) {
559           synchronized (session) {
560             if (! session.isEmpty())
561               session.storeOnShutdown();
562           }
563         }
564
565         _sessions.remove(session.getId());
566       } catch (Exception JavaDoc e) {
567         if (! isError)
568           log.log(Level.WARNING, "Can't store session: " + e, e);
569         isError = true;
570       }
571     }
572   }
573
574   /**
575    * Notification from the cluster.
576    */

577   public void notifyRemove(String JavaDoc id)
578   {
579     SessionArrayValue session = _sessions.remove(id);
580
581     if (session != null)
582       session.invalidate();
583   }
584
585   /**
586    * Notification from the cluster.
587    */

588   public void notifyUpdate(String JavaDoc id)
589   {
590   }
591
592   /**
593    * Saves the session.
594    */

595   public void store(ObjectOutputStream JavaDoc out, Object JavaDoc obj)
596     throws IOException JavaDoc
597   {
598     SessionArrayValue session = (SessionArrayValue) obj;
599
600     session.store(out);
601   }
602 }
603
Popular Tags