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