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