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