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