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