1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19 package org.eclipse.jetty.server.session;
20
21 import java.io.DataInputStream;
22 import java.io.EOFException;
23 import java.io.File;
24 import java.io.FileInputStream;
25 import java.io.IOException;
26 import java.io.InputStream;
27 import java.util.ArrayList;
28 import java.util.Iterator;
29 import java.util.Map;
30 import java.util.concurrent.ConcurrentHashMap;
31 import java.util.concurrent.ConcurrentMap;
32 import java.util.concurrent.TimeUnit;
33
34 import javax.servlet.ServletContext;
35 import javax.servlet.http.HttpServletRequest;
36
37 import org.eclipse.jetty.server.SessionIdManager;
38 import org.eclipse.jetty.server.handler.ContextHandler;
39 import org.eclipse.jetty.util.ClassLoadingObjectInputStream;
40 import org.eclipse.jetty.util.IO;
41 import org.eclipse.jetty.util.log.Logger;
42 import org.eclipse.jetty.util.thread.ScheduledExecutorScheduler;
43 import org.eclipse.jetty.util.thread.Scheduler;
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59 public class HashSessionManager extends AbstractSessionManager
60 {
61 final static Logger LOG = SessionHandler.LOG;
62
63 protected final ConcurrentMap<String,HashedSession> _sessions=new ConcurrentHashMap<String,HashedSession>();
64 private Scheduler _timer;
65 private Scheduler.Task _task;
66 long _scavengePeriodMs=30000;
67 long _savePeriodMs=0;
68 long _idleSavePeriodMs = 0;
69 private Scheduler.Task _saveTask;
70 File _storeDir;
71 private boolean _lazyLoad=false;
72 private volatile boolean _sessionsLoaded=false;
73 private boolean _deleteUnrestorableSessions=false;
74
75
76
77
78
79
80 protected class Scavenger implements Runnable
81 {
82 @Override
83 public void run()
84 {
85 try
86 {
87 scavenge();
88 }
89 finally
90 {
91 if (_timer != null && _timer.isRunning())
92 _timer.schedule(this, _scavengePeriodMs, TimeUnit.MILLISECONDS);
93 }
94 }
95 }
96
97
98
99
100
101 protected class Saver implements Runnable
102 {
103 @Override
104 public void run()
105 {
106 try
107 {
108 saveSessions(true);
109 }
110 catch (Exception e)
111 {
112 LOG.warn(e);
113 }
114 finally
115 {
116 if (_timer != null && _timer.isRunning())
117 _timer.schedule(this, _savePeriodMs, TimeUnit.MILLISECONDS);
118 }
119 }
120 }
121
122
123
124 public HashSessionManager()
125 {
126 super();
127 }
128
129
130
131
132
133 @Override
134 public void doStart() throws Exception
135 {
136
137 _timer = getSessionHandler().getServer().getBean(Scheduler.class);
138 if (_timer == null)
139 {
140
141 ServletContext context = ContextHandler.getCurrentContext();
142 if (context!=null)
143 _timer = (Scheduler)context.getAttribute("org.eclipse.jetty.server.session.timer");
144 }
145
146 if (_timer == null)
147 {
148
149 _timer=new ScheduledExecutorScheduler(toString()+"Timer",true);
150 addBean(_timer,true);
151 }
152 else
153 addBean(_timer,false);
154
155 super.doStart();
156
157 setScavengePeriod(getScavengePeriod());
158
159 if (_storeDir!=null)
160 {
161 if (!_storeDir.exists())
162 _storeDir.mkdirs();
163
164 if (!_lazyLoad)
165 restoreSessions();
166 }
167
168 setSavePeriod(getSavePeriod());
169 }
170
171
172
173
174
175 @Override
176 public void doStop() throws Exception
177 {
178
179 synchronized(this)
180 {
181 if (_saveTask!=null)
182 _saveTask.cancel();
183 _saveTask=null;
184 if (_task!=null)
185 _task.cancel();
186 _task=null;
187 _timer=null;
188 }
189
190
191 super.doStop();
192
193 _sessions.clear();
194
195 }
196
197
198
199
200
201 public int getScavengePeriod()
202 {
203 return (int)(_scavengePeriodMs/1000);
204 }
205
206
207
208 @Override
209 public int getSessions()
210 {
211 int sessions=super.getSessions();
212 if (LOG.isDebugEnabled())
213 {
214 if (_sessions.size()!=sessions)
215 LOG.warn("sessions: "+_sessions.size()+"!="+sessions);
216 }
217 return sessions;
218 }
219
220
221
222
223
224 public int getIdleSavePeriod()
225 {
226 if (_idleSavePeriodMs <= 0)
227 return 0;
228
229 return (int)(_idleSavePeriodMs / 1000);
230 }
231
232
233
234
235
236
237
238
239
240
241 public void setIdleSavePeriod(int seconds)
242 {
243 _idleSavePeriodMs = seconds * 1000L;
244 }
245
246
247 @Override
248 public void setMaxInactiveInterval(int seconds)
249 {
250 super.setMaxInactiveInterval(seconds);
251 if (_dftMaxIdleSecs>0&&_scavengePeriodMs>_dftMaxIdleSecs*1000L)
252 setScavengePeriod((_dftMaxIdleSecs+9)/10);
253 }
254
255
256
257
258
259 public void setSavePeriod (int seconds)
260 {
261 long period = (seconds * 1000L);
262 if (period < 0)
263 period=0;
264 _savePeriodMs=period;
265
266 if (_timer!=null)
267 {
268 synchronized (this)
269 {
270 if (_saveTask!=null)
271 _saveTask.cancel();
272 _saveTask = null;
273 if (_savePeriodMs > 0 && _storeDir!=null)
274 {
275 _saveTask = _timer.schedule(new Saver(),_savePeriodMs,TimeUnit.MILLISECONDS);
276 }
277 }
278 }
279 }
280
281
282
283
284
285 public int getSavePeriod ()
286 {
287 if (_savePeriodMs<=0)
288 return 0;
289
290 return (int)(_savePeriodMs/1000);
291 }
292
293
294
295
296
297 public void setScavengePeriod(int seconds)
298 {
299 if (seconds==0)
300 seconds=60;
301
302 long old_period=_scavengePeriodMs;
303 long period=seconds*1000L;
304 if (period>60000)
305 period=60000;
306 if (period<1000)
307 period=1000;
308
309 _scavengePeriodMs=period;
310
311 if (_timer!=null && (period!=old_period || _task==null))
312 {
313 synchronized (this)
314 {
315 if (_task!=null)
316 {
317 _task.cancel();
318 _task = null;
319 }
320 _task = _timer.schedule(new Scavenger(),_scavengePeriodMs, TimeUnit.MILLISECONDS);
321 }
322 }
323 }
324
325
326
327
328
329
330 protected void scavenge()
331 {
332
333 if (isStopping() || isStopped())
334 return;
335
336 Thread thread=Thread.currentThread();
337 ClassLoader old_loader=thread.getContextClassLoader();
338 try
339 {
340 if (_loader!=null)
341 thread.setContextClassLoader(_loader);
342
343
344 long now=System.currentTimeMillis();
345 __log.debug("Scavenging sessions at {}", now);
346
347 for (Iterator<HashedSession> i=_sessions.values().iterator(); i.hasNext();)
348 {
349 HashedSession session=i.next();
350 long idleTime=session.getMaxInactiveInterval()*1000L;
351 if (idleTime>0&&session.getAccessed()+idleTime<now)
352 {
353
354 try
355 {
356 session.timeout();
357 }
358 catch (Exception e)
359 {
360 __log.warn("Problem scavenging sessions", e);
361 }
362 }
363 else if (_idleSavePeriodMs > 0 && session.getAccessed()+_idleSavePeriodMs < now)
364 {
365 try
366 {
367 session.idle();
368 }
369 catch (Exception e)
370 {
371 __log.warn("Problem idling session "+ session.getId(), e);
372 }
373 }
374 }
375 }
376 finally
377 {
378 thread.setContextClassLoader(old_loader);
379 }
380 }
381
382
383 @Override
384 protected void addSession(AbstractSession session)
385 {
386 if (isRunning())
387 _sessions.put(session.getClusterId(),(HashedSession)session);
388 }
389
390
391 @Override
392 public AbstractSession getSession(String idInCluster)
393 {
394 if ( _lazyLoad && !_sessionsLoaded)
395 {
396 try
397 {
398 restoreSessions();
399 }
400 catch(Exception e)
401 {
402 LOG.warn(e);
403 }
404 }
405
406 Map<String,HashedSession> sessions=_sessions;
407 if (sessions==null)
408 return null;
409
410 HashedSession session = sessions.get(idInCluster);
411
412 if (session == null && _lazyLoad)
413 session=restoreSession(idInCluster);
414 if (session == null)
415 return null;
416
417 if (_idleSavePeriodMs!=0)
418 session.deIdle();
419
420 return session;
421 }
422
423
424 @Override
425 protected void shutdownSessions() throws Exception
426 {
427
428 ArrayList<HashedSession> sessions=new ArrayList<HashedSession>(_sessions.values());
429 int loop=100;
430 while (sessions.size()>0 && loop-->0)
431 {
432
433 if (isStopping() && _storeDir != null && _storeDir.exists() && _storeDir.canWrite())
434 {
435
436 for (HashedSession session : sessions)
437 {
438 session.save(false);
439 _sessions.remove(session.getClusterId());
440 }
441 }
442 else
443 {
444 for (HashedSession session : sessions)
445 session.invalidate();
446 }
447
448
449 sessions=new ArrayList<HashedSession>(_sessions.values());
450 }
451 }
452
453
454
455
456
457
458
459 @Override
460 public void renewSessionId(String oldClusterId, String oldNodeId, String newClusterId, String newNodeId)
461 {
462 try
463 {
464 Map<String,HashedSession> sessions=_sessions;
465 if (sessions == null)
466 return;
467
468 HashedSession session = sessions.remove(oldClusterId);
469 if (session == null)
470 return;
471
472 session.remove();
473 session.setClusterId(newClusterId);
474 session.setNodeId(newNodeId);
475 session.save();
476 sessions.put(newClusterId, session);
477
478 super.renewSessionId(oldClusterId, oldNodeId, newClusterId, newNodeId);
479 }
480 catch (Exception e)
481 {
482 LOG.warn(e);
483 }
484 }
485
486
487 @Override
488 protected AbstractSession newSession(HttpServletRequest request)
489 {
490 return new HashedSession(this, request);
491 }
492
493
494 protected AbstractSession newSession(long created, long accessed, String clusterId)
495 {
496 return new HashedSession(this, created,accessed, clusterId);
497 }
498
499
500 @Override
501 protected boolean removeSession(String clusterId)
502 {
503 return _sessions.remove(clusterId)!=null;
504 }
505
506
507 public void setStoreDirectory (File dir) throws IOException
508 {
509
510
511 _storeDir=dir.getCanonicalFile();
512 }
513
514
515 public File getStoreDirectory ()
516 {
517 return _storeDir;
518 }
519
520
521 public void setLazyLoad(boolean lazyLoad)
522 {
523 _lazyLoad = lazyLoad;
524 }
525
526
527 public boolean isLazyLoad()
528 {
529 return _lazyLoad;
530 }
531
532
533 public boolean isDeleteUnrestorableSessions()
534 {
535 return _deleteUnrestorableSessions;
536 }
537
538
539 public void setDeleteUnrestorableSessions(boolean deleteUnrestorableSessions)
540 {
541 _deleteUnrestorableSessions = deleteUnrestorableSessions;
542 }
543
544
545 public void restoreSessions () throws Exception
546 {
547 _sessionsLoaded = true;
548
549 if (_storeDir==null || !_storeDir.exists())
550 {
551 return;
552 }
553
554 if (!_storeDir.canRead())
555 {
556 LOG.warn ("Unable to restore Sessions: Cannot read from Session storage directory "+_storeDir.getAbsolutePath());
557 return;
558 }
559
560 String[] files = _storeDir.list();
561 for (int i=0;files!=null&&i<files.length;i++)
562 {
563 restoreSession(files[i]);
564 }
565 }
566
567
568 protected synchronized HashedSession restoreSession(String idInCuster)
569 {
570 File file = new File(_storeDir,idInCuster);
571
572 FileInputStream in = null;
573 Exception error = null;
574 try
575 {
576 if (file.exists())
577 {
578 in = new FileInputStream(file);
579 HashedSession session = restoreSession(in, null);
580 addSession(session, false);
581 session.didActivate();
582 return session;
583 }
584 }
585 catch (Exception e)
586 {
587 error = e;
588 }
589 finally
590 {
591 if (in != null) IO.close(in);
592
593 if (error != null)
594 {
595 if (isDeleteUnrestorableSessions() && file.exists() && file.getParentFile().equals(_storeDir) )
596 {
597 file.delete();
598 LOG.warn("Deleting file for unrestorable session "+idInCuster, error);
599 }
600 else
601 {
602 __log.warn("Problem restoring session "+idInCuster, error);
603 }
604 }
605 else
606 file.delete();
607 }
608 return null;
609 }
610
611
612 public void saveSessions(boolean reactivate) throws Exception
613 {
614 if (_storeDir==null || !_storeDir.exists())
615 {
616 return;
617 }
618
619 if (!_storeDir.canWrite())
620 {
621 LOG.warn ("Unable to save Sessions: Session persistence storage directory "+_storeDir.getAbsolutePath()+ " is not writeable");
622 return;
623 }
624
625 for (HashedSession session : _sessions.values())
626 session.save(reactivate);
627 }
628
629
630
631 public HashedSession restoreSession (InputStream is, HashedSession session) throws Exception
632 {
633 DataInputStream di = new DataInputStream(is);
634
635 String clusterId = di.readUTF();
636 di.readUTF();
637
638 long created = di.readLong();
639 long accessed = di.readLong();
640 int requests = di.readInt();
641
642 if (session == null)
643 session = (HashedSession)newSession(created, accessed, clusterId);
644 session.setRequests(requests);
645
646 int size = di.readInt();
647
648 restoreSessionAttributes(di, size, session);
649
650 try
651 {
652 int maxIdle = di.readInt();
653 session.setMaxInactiveInterval(maxIdle);
654 }
655 catch (EOFException e)
656 {
657 LOG.debug("No maxInactiveInterval persisted for session "+clusterId);
658 LOG.ignore(e);
659 }
660
661 return session;
662 }
663
664
665 private void restoreSessionAttributes (InputStream is, int size, HashedSession session)
666 throws Exception
667 {
668 if (size>0)
669 {
670 ClassLoadingObjectInputStream ois = new ClassLoadingObjectInputStream(is);
671 for (int i=0; i<size;i++)
672 {
673 String key = ois.readUTF();
674 Object value = ois.readObject();
675 session.setAttribute(key,value);
676 }
677 }
678 }
679 }