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