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