1
2
3
4
5
6
7
8
9
10
11
12
13
14
15 package org.mortbay.jetty.servlet;
16
17 import java.io.DataInputStream;
18 import java.io.DataOutputStream;
19 import java.io.File;
20 import java.io.FileInputStream;
21 import java.io.FileOutputStream;
22 import java.io.IOException;
23 import java.io.ObjectInputStream;
24 import java.io.ObjectOutputStream;
25 import java.util.ArrayList;
26 import java.util.Collections;
27 import java.util.HashMap;
28 import java.util.Iterator;
29 import java.util.Map;
30 import java.util.Timer;
31 import java.util.TimerTask;
32
33 import javax.servlet.http.HttpServletRequest;
34
35 import org.mortbay.log.Log;
36 import org.mortbay.util.LazyList;
37
38
39
40
41
42
43
44 public class HashSessionManager extends AbstractSessionManager
45 {
46 private Timer _timer;
47 private TimerTask _task;
48 private int _scavengePeriodMs=30000;
49 private int _savePeriodMs=0;
50 private TimerTask _saveTask;
51 protected Map _sessions;
52 private File _storeDir;
53 private boolean _lazyLoad=false;
54 private boolean _sessionsLoaded=false;
55
56
57 public HashSessionManager()
58 {
59 super();
60 }
61
62
63
64
65
66 public void doStart() throws Exception
67 {
68 _sessions=new HashMap();
69 super.doStart();
70
71 _timer=new Timer(true);
72
73 setScavengePeriod(getScavengePeriod());
74
75 if (_storeDir!=null)
76 {
77 if (!_storeDir.exists())
78 _storeDir.mkdir();
79
80 if (!_lazyLoad)
81 restoreSessions();
82 }
83
84 setSavePeriod(getSavePeriod());
85 }
86
87
88
89
90
91 public void doStop() throws Exception
92 {
93
94 if (_storeDir != null)
95 saveSessions();
96
97 super.doStop();
98
99 _sessions.clear();
100 _sessions=null;
101
102
103 synchronized(this)
104 {
105 if (_saveTask!=null)
106 _saveTask.cancel();
107 if (_task!=null)
108 _task.cancel();
109 if (_timer!=null)
110 _timer.cancel();
111 _timer=null;
112 }
113 }
114
115
116
117
118
119 public int getScavengePeriod()
120 {
121 return _scavengePeriodMs/1000;
122 }
123
124
125
126 public Map getSessionMap()
127 {
128 return Collections.unmodifiableMap(_sessions);
129 }
130
131
132
133 public int getSessions()
134 {
135 return _sessions.size();
136 }
137
138
139
140 public void setMaxInactiveInterval(int seconds)
141 {
142 super.setMaxInactiveInterval(seconds);
143 if (_dftMaxIdleSecs>0&&_scavengePeriodMs>_dftMaxIdleSecs*1000)
144 setScavengePeriod((_dftMaxIdleSecs+9)/10);
145 }
146
147
148 public void setSavePeriod (int seconds)
149 {
150 int oldSavePeriod = _savePeriodMs;
151 int period = (seconds * 1000);
152 if (period < 0)
153 period=0;
154 _savePeriodMs=period;
155
156 if (_timer!=null)
157 {
158 synchronized (this)
159 {
160 if (_saveTask!=null)
161 _saveTask.cancel();
162 if (_savePeriodMs > 0 && _storeDir!=null)
163 {
164 _saveTask = new TimerTask()
165 {
166 public void run()
167 {
168 try
169 {
170 saveSessions();
171 }
172 catch (Exception e)
173 {
174 Log.warn(e);
175 }
176 }
177 };
178 _timer.schedule(_saveTask,_savePeriodMs,_savePeriodMs);
179 }
180 }
181 }
182 }
183
184
185 public int getSavePeriod ()
186 {
187 if (_savePeriodMs<=0)
188 return 0;
189
190 return _savePeriodMs/1000;
191 }
192
193
194
195
196 public void setScavengePeriod(int seconds)
197 {
198 if (seconds==0)
199 seconds=60;
200
201 int old_period=_scavengePeriodMs;
202 int period=seconds*1000;
203 if (period>60000)
204 period=60000;
205 if (period<1000)
206 period=1000;
207
208 _scavengePeriodMs=period;
209 if (_timer!=null && (period!=old_period || _task==null))
210 {
211 synchronized (this)
212 {
213 if (_task!=null)
214 _task.cancel();
215 _task = new TimerTask()
216 {
217 public void run()
218 {
219 scavenge();
220 }
221 };
222 _timer.schedule(_task,_scavengePeriodMs,_scavengePeriodMs);
223 }
224 }
225 }
226
227
228
229
230
231
232 private void scavenge()
233 {
234
235 if (isStopping() || isStopped())
236 return;
237
238 Thread thread=Thread.currentThread();
239 ClassLoader old_loader=thread.getContextClassLoader();
240 try
241 {
242 if (_loader!=null)
243 thread.setContextClassLoader(_loader);
244
245 long now=System.currentTimeMillis();
246
247 try
248 {
249 if (!_sessionsLoaded && _lazyLoad)
250 restoreSessions();
251 }
252 catch(Exception e)
253 {
254 Log.debug(e);
255 }
256
257
258
259
260 Object stale=null;
261
262 synchronized (HashSessionManager.this)
263 {
264
265 for (Iterator i=_sessions.values().iterator(); i.hasNext();)
266 {
267 Session session=(Session)i.next();
268 long idleTime=session._maxIdleMs;
269 if (idleTime>0&&session._accessed+idleTime<now)
270 {
271
272 stale=LazyList.add(stale,session);
273 }
274 }
275 }
276
277
278 for (int i=LazyList.size(stale); i-->0;)
279 {
280
281 Session session=(Session)LazyList.get(stale,i);
282 long idleTime=session._maxIdleMs;
283 if (idleTime>0&&session._accessed+idleTime<System.currentTimeMillis())
284 {
285 ((Session)session).timeout();
286 int nbsess=this._sessions.size();
287 if (nbsess<this._minSessions)
288 this._minSessions=nbsess;
289 }
290 }
291 }
292 catch (Throwable t)
293 {
294 if (t instanceof ThreadDeath)
295 throw ((ThreadDeath)t);
296 else
297 Log.warn("Problem scavenging sessions", t);
298 }
299 finally
300 {
301 thread.setContextClassLoader(old_loader);
302 }
303 }
304
305
306 protected void addSession(AbstractSessionManager.Session session)
307 {
308 _sessions.put(session.getClusterId(),session);
309 }
310
311
312 public AbstractSessionManager.Session getSession(String idInCluster)
313 {
314 try
315 {
316 if (!_sessionsLoaded && _lazyLoad)
317 restoreSessions();
318 }
319 catch(Exception e)
320 {
321 Log.warn(e);
322 }
323
324 if (_sessions==null)
325 return null;
326
327 return (Session)_sessions.get(idInCluster);
328 }
329
330
331 protected void invalidateSessions()
332 {
333
334 ArrayList sessions=new ArrayList(_sessions.values());
335 for (Iterator i=sessions.iterator(); i.hasNext();)
336 {
337 Session session=(Session)i.next();
338 session.invalidate();
339 }
340 _sessions.clear();
341
342 }
343
344
345 protected AbstractSessionManager.Session newSession(HttpServletRequest request)
346 {
347 return new Session(request);
348 }
349
350
351 protected void removeSession(String clusterId)
352 {
353 _sessions.remove(clusterId);
354 }
355
356
357 public void setStoreDirectory (File dir)
358 {
359 _storeDir=dir;
360 }
361
362
363 public File getStoreDirectory ()
364 {
365 return _storeDir;
366 }
367
368
369 public void setLazyLoad(boolean lazyLoad)
370 {
371 _lazyLoad = lazyLoad;
372 }
373
374
375 public boolean isLazyLoad()
376 {
377 return _lazyLoad;
378 }
379
380
381 public void restoreSessions () throws Exception
382 {
383 if (_storeDir==null || !_storeDir.exists())
384 {
385 return;
386 }
387
388 if (!_storeDir.canRead())
389 {
390 Log.warn ("Unable to restore Sessions: Cannot read from Session storage directory "+_storeDir.getAbsolutePath());
391 return;
392 }
393
394 File[] files = _storeDir.listFiles();
395 for (int i=0;files!=null&&i<files.length;i++)
396 {
397 try
398 {
399 FileInputStream in = new FileInputStream(files[i]);
400 Session session = restoreSession(in);
401 in.close();
402 addSession(session, false);
403 files[i].delete();
404 }
405 catch (Exception e)
406 {
407 Log.warn("Problem restoring session "+files[i].getName(), e);
408 }
409 }
410
411 _sessionsLoaded = true;
412 }
413
414
415 public void saveSessions () throws Exception
416 {
417 if (_storeDir==null || !_storeDir.exists())
418 {
419 return;
420 }
421
422 if (!_storeDir.canWrite())
423 {
424 Log.warn ("Unable to save Sessions: Session persistence storage directory "+_storeDir.getAbsolutePath()+ " is not writeable");
425 return;
426 }
427
428 synchronized (this)
429 {
430 Iterator itor = _sessions.entrySet().iterator();
431 while (itor.hasNext())
432 {
433 Map.Entry entry = (Map.Entry)itor.next();
434 String id = (String)entry.getKey();
435 Session session = (Session)entry.getValue();
436 try
437 {
438 File file = new File (_storeDir, id);
439 if (file.exists())
440 file.delete();
441 file.createNewFile();
442 FileOutputStream fos = new FileOutputStream (file);
443 session.save(fos);
444 fos.close();
445 }
446 catch (Exception e)
447 {
448 Log.warn("Problem persisting session "+id, e);
449 }
450 }
451 }
452 }
453
454
455 public Session restoreSession (FileInputStream fis)
456 throws Exception
457 {
458
459
460
461
462
463
464 DataInputStream in = new DataInputStream(fis);
465 String clusterId = in.readUTF();
466 String nodeId = in.readUTF();
467 boolean idChanged = in.readBoolean();
468 long created = in.readLong();
469 long cookieSet = in.readLong();
470 long accessed = in.readLong();
471 long lastAccessed = in.readLong();
472
473
474
475
476 int requests = in.readInt();
477
478 Session session = new Session (created, clusterId);
479 session._cookieSet = cookieSet;
480 session._lastAccessed = lastAccessed;
481
482 int size = in.readInt();
483 if (size > 0)
484 {
485 ArrayList keys = new ArrayList();
486 for (int i=0; i<size; i++)
487 {
488 String key = in.readUTF();
489 keys.add(key);
490 }
491 ClassLoadingObjectInputStream ois = new ClassLoadingObjectInputStream(in);
492 for (int i=0;i<size;i++)
493 {
494 Object value = ois.readObject();
495 session.setAttribute((String)keys.get(i),value);
496 }
497 ois.close();
498 }
499 else
500 session.initValues();
501 in.close();
502 return session;
503 }
504
505
506
507
508
509 protected class Session extends AbstractSessionManager.Session
510 {
511
512 private static final long serialVersionUID=-2134521374206116367L;
513
514
515
516
517 protected Session(HttpServletRequest request)
518 {
519 super(request);
520 }
521
522 protected Session(long created, String clusterId)
523 {
524 super(created, clusterId);
525 }
526
527 public void setMaxInactiveInterval(int secs)
528 {
529 super.setMaxInactiveInterval(secs);
530 if (_maxIdleMs>0&&(_maxIdleMs/10)<_scavengePeriodMs)
531 HashSessionManager.this.setScavengePeriod((secs+9)/10);
532 }
533
534
535 protected Map newAttributeMap()
536 {
537 return new HashMap(3);
538 }
539
540
541 public void invalidate ()
542 throws IllegalStateException
543 {
544 super.invalidate();
545 remove(getId());
546 }
547
548 public void remove (String id)
549 {
550 if (id==null)
551 return;
552
553
554
555 if (isStopping() || isStopped())
556 return;
557
558 if (_storeDir==null || !_storeDir.exists())
559 {
560 return;
561 }
562
563 File f = new File(_storeDir, id);
564 f.delete();
565 }
566
567 public void save(FileOutputStream fos) throws IOException
568 {
569 DataOutputStream out = new DataOutputStream(fos);
570 out.writeUTF(_clusterId);
571 out.writeUTF(_nodeId);
572 out.writeBoolean(_idChanged);
573 out.writeLong( _created);
574 out.writeLong(_cookieSet);
575 out.writeLong(_accessed);
576 out.writeLong(_lastAccessed);
577
578
579
580
581
582
583
584
585 out.writeInt(_requests);
586 if (_values != null)
587 {
588 out.writeInt(_values.size());
589 Iterator itor = _values.keySet().iterator();
590 while (itor.hasNext())
591 {
592 String key = (String)itor.next();
593 out.writeUTF(key);
594 }
595 itor = _values.values().iterator();
596 ObjectOutputStream oos = new ObjectOutputStream(out);
597 while (itor.hasNext())
598 {
599 oos.writeObject(itor.next());
600 }
601 oos.close();
602 }
603 else
604 out.writeInt(0);
605 out.close();
606 }
607
608 }
609
610 protected class ClassLoadingObjectInputStream extends ObjectInputStream
611 {
612 public ClassLoadingObjectInputStream(java.io.InputStream in)
613 throws IOException
614 {
615 super(in);
616 }
617
618 public ClassLoadingObjectInputStream ()
619 throws IOException
620 {
621 super();
622 }
623
624 public Class resolveClass (java.io.ObjectStreamClass cl)
625 throws IOException, ClassNotFoundException
626 {
627 Class clazz;
628 try
629 {
630 return Class.forName(cl.getName(), false, Thread.currentThread().getContextClassLoader());
631 }
632 catch (ClassNotFoundException e)
633 {
634 return super.resolveClass(cl);
635 }
636 }
637 }
638
639
640 }