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 _timer=null;
188 }
189
190
191
192 super.doStop();
193
194 _sessions.clear();
195
196 }
197
198
199
200
201
202 public int getScavengePeriod()
203 {
204 return (int)(_scavengePeriodMs/1000);
205 }
206
207
208
209 @Override
210 public int getSessions()
211 {
212 int sessions=super.getSessions();
213 if (LOG.isDebugEnabled())
214 {
215 if (_sessions.size()!=sessions)
216 LOG.warn("sessions: "+_sessions.size()+"!="+sessions);
217 }
218 return sessions;
219 }
220
221
222
223
224
225 public int getIdleSavePeriod()
226 {
227 if (_idleSavePeriodMs <= 0)
228 return 0;
229
230 return (int)(_idleSavePeriodMs / 1000);
231 }
232
233
234
235
236
237
238
239
240
241
242 public void setIdleSavePeriod(int seconds)
243 {
244 _idleSavePeriodMs = seconds * 1000L;
245 }
246
247
248 @Override
249 public void setMaxInactiveInterval(int seconds)
250 {
251 super.setMaxInactiveInterval(seconds);
252 if (_dftMaxIdleSecs>0&&_scavengePeriodMs>_dftMaxIdleSecs*1000L)
253 setScavengePeriod((_dftMaxIdleSecs+9)/10);
254 }
255
256
257
258
259
260 public void setSavePeriod (int seconds)
261 {
262 long period = (seconds * 1000L);
263 if (period < 0)
264 period=0;
265 _savePeriodMs=period;
266
267 if (_timer!=null)
268 {
269 synchronized (this)
270 {
271 if (_saveTask!=null)
272 _saveTask.cancel();
273 _saveTask = null;
274 if (_savePeriodMs > 0 && _storeDir!=null)
275 {
276 _saveTask = _timer.schedule(new Saver(),_savePeriodMs,TimeUnit.MILLISECONDS);
277 }
278 }
279 }
280 }
281
282
283
284
285
286 public int getSavePeriod ()
287 {
288 if (_savePeriodMs<=0)
289 return 0;
290
291 return (int)(_savePeriodMs/1000);
292 }
293
294
295
296
297
298 public void setScavengePeriod(int seconds)
299 {
300 if (seconds==0)
301 seconds=60;
302
303 long old_period=_scavengePeriodMs;
304 long period=seconds*1000L;
305 if (period>60000)
306 period=60000;
307 if (period<1000)
308 period=1000;
309
310 _scavengePeriodMs=period;
311
312 synchronized (this)
313 {
314 if (_timer!=null && (period!=old_period || _task==null))
315 {
316 if (_task!=null)
317 {
318 _task.cancel();
319 _task = null;
320 }
321
322 _task = _timer.schedule(new Scavenger(),_scavengePeriodMs, TimeUnit.MILLISECONDS);
323 }
324 }
325 }
326
327
328
329
330
331
332 protected void scavenge()
333 {
334
335 if (isStopping() || isStopped())
336 return;
337
338 Thread thread=Thread.currentThread();
339 ClassLoader old_loader=thread.getContextClassLoader();
340 try
341 {
342 if (_loader!=null)
343 thread.setContextClassLoader(_loader);
344
345
346 long now=System.currentTimeMillis();
347 __log.debug("Scavenging sessions at {}", now);
348
349 for (Iterator<HashedSession> i=_sessions.values().iterator(); i.hasNext();)
350 {
351 HashedSession session=i.next();
352 long idleTime=session.getMaxInactiveInterval()*1000L;
353 if (idleTime>0&&session.getAccessed()+idleTime<now)
354 {
355
356 try
357 {
358 session.timeout();
359 }
360 catch (Exception e)
361 {
362 __log.warn("Problem scavenging sessions", e);
363 }
364 }
365 else if (_idleSavePeriodMs > 0 && session.getAccessed()+_idleSavePeriodMs < now)
366 {
367 try
368 {
369 session.idle();
370 }
371 catch (Exception e)
372 {
373 __log.warn("Problem idling session "+ session.getId(), e);
374 }
375 }
376 }
377 }
378 finally
379 {
380 thread.setContextClassLoader(old_loader);
381 }
382 }
383
384
385 @Override
386 protected void addSession(AbstractSession session)
387 {
388 if (isRunning())
389 _sessions.put(session.getClusterId(),(HashedSession)session);
390 }
391
392
393 @Override
394 public AbstractSession getSession(String idInCluster)
395 {
396 if ( _lazyLoad && !_sessionsLoaded)
397 {
398 try
399 {
400 restoreSessions();
401 }
402 catch(Exception e)
403 {
404 LOG.warn(e);
405 }
406 }
407
408 Map<String,HashedSession> sessions=_sessions;
409 if (sessions==null)
410 return null;
411
412 HashedSession session = sessions.get(idInCluster);
413
414 if (session == null && _lazyLoad)
415 session=restoreSession(idInCluster);
416 if (session == null)
417 return null;
418
419 if (_idleSavePeriodMs!=0)
420 session.deIdle();
421
422 return session;
423 }
424
425
426 @Override
427 protected void shutdownSessions() throws Exception
428 {
429
430 ArrayList<HashedSession> sessions=new ArrayList<HashedSession>(_sessions.values());
431 int loop=100;
432 while (sessions.size()>0 && loop-->0)
433 {
434
435 if (isStopping() && _storeDir != null && _storeDir.exists() && _storeDir.canWrite())
436 {
437
438 for (HashedSession session : sessions)
439 {
440 session.save(false);
441 _sessions.remove(session.getClusterId());
442 }
443 }
444 else
445 {
446 for (HashedSession session : sessions)
447 session.invalidate();
448 }
449
450
451 sessions=new ArrayList<HashedSession>(_sessions.values());
452 }
453 }
454
455
456
457
458
459
460
461 @Override
462 public void renewSessionId(String oldClusterId, String oldNodeId, String newClusterId, String newNodeId)
463 {
464 try
465 {
466 Map<String,HashedSession> sessions=_sessions;
467 if (sessions == null)
468 return;
469
470 HashedSession session = sessions.remove(oldClusterId);
471 if (session == null)
472 return;
473
474 session.remove();
475 session.setClusterId(newClusterId);
476 session.setNodeId(newNodeId);
477 session.save();
478 sessions.put(newClusterId, session);
479
480 super.renewSessionId(oldClusterId, oldNodeId, newClusterId, newNodeId);
481 }
482 catch (Exception e)
483 {
484 LOG.warn(e);
485 }
486 }
487
488
489 @Override
490 protected AbstractSession newSession(HttpServletRequest request)
491 {
492 return new HashedSession(this, request);
493 }
494
495
496 protected AbstractSession newSession(long created, long accessed, String clusterId)
497 {
498 return new HashedSession(this, created,accessed, clusterId);
499 }
500
501
502 @Override
503 protected boolean removeSession(String clusterId)
504 {
505 return _sessions.remove(clusterId)!=null;
506 }
507
508
509 public void setStoreDirectory (File dir) throws IOException
510 {
511
512
513 _storeDir=dir.getCanonicalFile();
514 }
515
516
517 public File getStoreDirectory ()
518 {
519 return _storeDir;
520 }
521
522
523 public void setLazyLoad(boolean lazyLoad)
524 {
525 _lazyLoad = lazyLoad;
526 }
527
528
529 public boolean isLazyLoad()
530 {
531 return _lazyLoad;
532 }
533
534
535 public boolean isDeleteUnrestorableSessions()
536 {
537 return _deleteUnrestorableSessions;
538 }
539
540
541 public void setDeleteUnrestorableSessions(boolean deleteUnrestorableSessions)
542 {
543 _deleteUnrestorableSessions = deleteUnrestorableSessions;
544 }
545
546
547 public void restoreSessions () throws Exception
548 {
549 _sessionsLoaded = true;
550
551 if (_storeDir==null || !_storeDir.exists())
552 {
553 return;
554 }
555
556 if (!_storeDir.canRead())
557 {
558 LOG.warn ("Unable to restore Sessions: Cannot read from Session storage directory "+_storeDir.getAbsolutePath());
559 return;
560 }
561
562 String[] files = _storeDir.list();
563 for (int i=0;files!=null&&i<files.length;i++)
564 {
565 restoreSession(files[i]);
566 }
567 }
568
569
570 protected synchronized HashedSession restoreSession(String idInCuster)
571 {
572 File file = new File(_storeDir,idInCuster);
573
574 Exception error = null;
575 if (!file.exists())
576 {
577 if (LOG.isDebugEnabled())
578 {
579 LOG.debug("Not loading: {}",file);
580 }
581 return null;
582 }
583
584 try (FileInputStream in = new FileInputStream(file))
585 {
586 HashedSession session = restoreSession(in,null);
587 addSession(session,false);
588 session.didActivate();
589 return session;
590 }
591 catch (Exception e)
592 {
593 error = e;
594 }
595 finally
596 {
597 if (error != null)
598 {
599 if (isDeleteUnrestorableSessions() && file.exists() && file.getParentFile().equals(_storeDir) )
600 {
601 file.delete();
602 LOG.warn("Deleting file for unrestorable session "+idInCuster, error);
603 }
604 else
605 {
606 __log.warn("Problem restoring session "+idInCuster, error);
607 }
608 }
609 else
610 {
611
612 file.delete();
613 }
614 }
615 return null;
616 }
617
618
619 public void saveSessions(boolean reactivate) throws Exception
620 {
621 if (_storeDir==null || !_storeDir.exists())
622 {
623 return;
624 }
625
626 if (!_storeDir.canWrite())
627 {
628 LOG.warn ("Unable to save Sessions: Session persistence storage directory "+_storeDir.getAbsolutePath()+ " is not writeable");
629 return;
630 }
631
632 for (HashedSession session : _sessions.values())
633 session.save(reactivate);
634 }
635
636
637
638 public HashedSession restoreSession (InputStream is, HashedSession session) throws Exception
639 {
640 DataInputStream di = new DataInputStream(is);
641
642 String clusterId = di.readUTF();
643 di.readUTF();
644
645 long created = di.readLong();
646 long accessed = di.readLong();
647 int requests = di.readInt();
648
649 if (session == null)
650 session = (HashedSession)newSession(created, accessed, clusterId);
651
652 session.setRequests(requests);
653
654
655 int size = di.readInt();
656
657 restoreSessionAttributes(di, size, session);
658
659 try
660 {
661 int maxIdle = di.readInt();
662 session.setMaxInactiveInterval(maxIdle);
663 }
664 catch (IOException e)
665 {
666 LOG.debug("No maxInactiveInterval persisted for session "+clusterId);
667 LOG.ignore(e);
668 }
669
670 return session;
671 }
672
673
674 @SuppressWarnings("resource")
675 private void restoreSessionAttributes (InputStream is, int size, HashedSession session)
676 throws Exception
677 {
678 if (size>0)
679 {
680
681 ClassLoadingObjectInputStream ois = new ClassLoadingObjectInputStream(is);
682 for (int i=0; i<size;i++)
683 {
684 String key = ois.readUTF();
685 Object value = ois.readObject();
686 session.setAttribute(key,value);
687 }
688 }
689 }
690 }