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.File;
23 import java.io.FileInputStream;
24 import java.io.IOException;
25 import java.io.InputStream;
26 import java.util.ArrayList;
27 import java.util.Iterator;
28 import java.util.Map;
29 import java.util.concurrent.ConcurrentHashMap;
30 import java.util.concurrent.ConcurrentMap;
31 import java.util.concurrent.TimeUnit;
32
33 import javax.servlet.ServletContext;
34 import javax.servlet.http.HttpServletRequest;
35
36 import org.eclipse.jetty.server.handler.ContextHandler;
37 import org.eclipse.jetty.util.ClassLoadingObjectInputStream;
38 import org.eclipse.jetty.util.log.Logger;
39 import org.eclipse.jetty.util.thread.ScheduledExecutorScheduler;
40 import org.eclipse.jetty.util.thread.Scheduler;
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56 public class HashSessionManager extends AbstractSessionManager
57 {
58 final static Logger LOG = SessionHandler.LOG;
59
60 protected final ConcurrentMap<String,HashedSession> _sessions=new ConcurrentHashMap<String,HashedSession>();
61 private Scheduler _timer;
62 private Scheduler.Task _task;
63 long _scavengePeriodMs=30000;
64 long _savePeriodMs=0;
65 long _idleSavePeriodMs = 0;
66 private Scheduler.Task _saveTask;
67 File _storeDir;
68 private boolean _lazyLoad=false;
69 private volatile boolean _sessionsLoaded=false;
70 private boolean _deleteUnrestorableSessions=false;
71
72
73
74
75
76
77 protected class Scavenger implements Runnable
78 {
79 @Override
80 public void run()
81 {
82 try
83 {
84 scavenge();
85 }
86 finally
87 {
88 if (_timer != null && _timer.isRunning()) {
89 _task = _timer.schedule(this, _scavengePeriodMs, TimeUnit.MILLISECONDS);
90 }
91 }
92 }
93 }
94
95
96
97
98
99 protected class Saver implements Runnable
100 {
101 @Override
102 public void run()
103 {
104 try
105 {
106 saveSessions(true);
107 }
108 catch (Exception e)
109 {
110 LOG.warn(e);
111 }
112 finally
113 {
114 if (_timer != null && _timer.isRunning())
115 _saveTask = _timer.schedule(this, _savePeriodMs, TimeUnit.MILLISECONDS);
116 }
117 }
118 }
119
120
121
122 public HashSessionManager()
123 {
124 super();
125 }
126
127
128
129
130
131 @Override
132 public void doStart() throws Exception
133 {
134
135 _timer = getSessionHandler().getServer().getBean(Scheduler.class);
136 if (_timer == null)
137 {
138
139 ServletContext context = ContextHandler.getCurrentContext();
140 if (context!=null)
141 _timer = (Scheduler)context.getAttribute("org.eclipse.jetty.server.session.timer");
142 }
143
144 if (_timer == null)
145 {
146
147 _timer=new ScheduledExecutorScheduler(toString()+"Timer",true);
148 addBean(_timer,true);
149 }
150 else
151 addBean(_timer,false);
152
153 super.doStart();
154
155 setScavengePeriod(getScavengePeriod());
156
157 if (_storeDir!=null)
158 {
159 if (!_storeDir.exists())
160 _storeDir.mkdirs();
161
162 if (!_lazyLoad)
163 restoreSessions();
164 }
165
166 setSavePeriod(getSavePeriod());
167 }
168
169
170
171
172
173 @Override
174 public void doStop() throws Exception
175 {
176
177 synchronized(this)
178 {
179 if (_saveTask!=null)
180 _saveTask.cancel();
181
182 _saveTask=null;
183 if (_task!=null)
184 _task.cancel();
185
186 _task=null;
187
188
189 if (isManaged(_timer))
190 removeBean(_timer);
191
192 _timer=null;
193 }
194
195
196
197 super.doStop();
198
199 _sessions.clear();
200 }
201
202
203
204
205
206 public int getScavengePeriod()
207 {
208 return (int)(_scavengePeriodMs/1000);
209 }
210
211
212
213 @Override
214 public int getSessions()
215 {
216 int sessions=super.getSessions();
217 if (LOG.isDebugEnabled())
218 {
219 if (_sessions.size()!=sessions)
220 LOG.warn("sessions: "+_sessions.size()+"!="+sessions);
221 }
222 return sessions;
223 }
224
225
226
227
228
229 public int getIdleSavePeriod()
230 {
231 if (_idleSavePeriodMs <= 0)
232 return 0;
233
234 return (int)(_idleSavePeriodMs / 1000);
235 }
236
237
238
239
240
241
242
243
244
245
246 public void setIdleSavePeriod(int seconds)
247 {
248 _idleSavePeriodMs = seconds * 1000L;
249 }
250
251
252 @Override
253 public void setMaxInactiveInterval(int seconds)
254 {
255 super.setMaxInactiveInterval(seconds);
256 if (_dftMaxIdleSecs>0&&_scavengePeriodMs>_dftMaxIdleSecs*1000L)
257 setScavengePeriod((_dftMaxIdleSecs+9)/10);
258 }
259
260
261
262
263
264 public void setSavePeriod (int seconds)
265 {
266 long period = (seconds * 1000L);
267 if (period < 0)
268 period=0;
269 _savePeriodMs=period;
270
271 if (_timer!=null)
272 {
273 synchronized (this)
274 {
275 if (_saveTask!=null)
276 _saveTask.cancel();
277 _saveTask = null;
278 if (_savePeriodMs > 0 && _storeDir!=null)
279 {
280 _saveTask = _timer.schedule(new Saver(),_savePeriodMs,TimeUnit.MILLISECONDS);
281 }
282 }
283 }
284 }
285
286
287
288
289
290 public int getSavePeriod ()
291 {
292 if (_savePeriodMs<=0)
293 return 0;
294
295 return (int)(_savePeriodMs/1000);
296 }
297
298
299
300
301
302 public void setScavengePeriod(int seconds)
303 {
304 if (seconds==0)
305 seconds=60;
306
307 long old_period=_scavengePeriodMs;
308 long period=seconds*1000L;
309 if (period>60000)
310 period=60000;
311 if (period<1000)
312 period=1000;
313
314 _scavengePeriodMs=period;
315
316 synchronized (this)
317 {
318 if (_timer!=null && (period!=old_period || _task==null))
319 {
320 if (_task!=null)
321 {
322 _task.cancel();
323 _task = null;
324 }
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 Exception error = null;
579 if (!file.exists())
580 {
581 if (LOG.isDebugEnabled())
582 {
583 LOG.debug("Not loading: {}",file);
584 }
585 return null;
586 }
587
588 try (FileInputStream in = new FileInputStream(file))
589 {
590 HashedSession session = restoreSession(in,null);
591 addSession(session,false);
592 session.didActivate();
593 return session;
594 }
595 catch (Exception e)
596 {
597 error = e;
598 }
599 finally
600 {
601 if (error != null)
602 {
603 if (isDeleteUnrestorableSessions() && file.exists() && file.getParentFile().equals(_storeDir) )
604 {
605 file.delete();
606 LOG.warn("Deleting file for unrestorable session {} {}",idInCuster,error);
607 __log.debug(error);
608 }
609 else
610 {
611 __log.warn("Problem restoring session {} {}",idInCuster, error);
612 __log.debug(error);
613 }
614 }
615 else
616 {
617
618 file.delete();
619 }
620 }
621 return null;
622 }
623
624
625 public void saveSessions(boolean reactivate) throws Exception
626 {
627 if (_storeDir==null || !_storeDir.exists())
628 {
629 return;
630 }
631
632 if (!_storeDir.canWrite())
633 {
634 LOG.warn ("Unable to save Sessions: Session persistence storage directory "+_storeDir.getAbsolutePath()+ " is not writeable");
635 return;
636 }
637
638 for (HashedSession session : _sessions.values())
639 session.save(reactivate);
640 }
641
642
643
644 public HashedSession restoreSession (InputStream is, HashedSession session) throws Exception
645 {
646 DataInputStream di = new DataInputStream(is);
647
648 String clusterId = di.readUTF();
649 di.readUTF();
650
651 long created = di.readLong();
652 long accessed = di.readLong();
653 int requests = di.readInt();
654
655 if (session == null)
656 session = (HashedSession)newSession(created, accessed, clusterId);
657
658 session.setRequests(requests);
659
660
661 int size = di.readInt();
662
663 restoreSessionAttributes(di, size, session);
664
665 try
666 {
667 int maxIdle = di.readInt();
668 session.setMaxInactiveInterval(maxIdle);
669 }
670 catch (IOException e)
671 {
672 LOG.debug("No maxInactiveInterval persisted for session "+clusterId);
673 LOG.ignore(e);
674 }
675
676 return session;
677 }
678
679
680 @SuppressWarnings("resource")
681 private void restoreSessionAttributes (InputStream is, int size, HashedSession session)
682 throws Exception
683 {
684 if (size>0)
685 {
686
687 ClassLoadingObjectInputStream ois = new ClassLoadingObjectInputStream(is);
688 for (int i=0; i<size;i++)
689 {
690 String key = ois.readUTF();
691 Object value = ois.readObject();
692 session.setAttribute(key,value);
693 }
694 }
695 }
696 }