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