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 catch (Exception e)
445 {
446 LOG.warn(e);
447 }
448 }
449
450
451 @Override
452 protected AbstractSession newSession(HttpServletRequest request)
453 {
454 return new HashedSession(this, request);
455 }
456
457
458 protected AbstractSession newSession(long created, long accessed, String clusterId)
459 {
460 return new HashedSession(this, created,accessed, clusterId);
461 }
462
463
464 @Override
465 protected boolean removeSession(String clusterId)
466 {
467 return _sessions.remove(clusterId)!=null;
468 }
469
470
471 public void setStoreDirectory (File dir) throws IOException
472 {
473
474
475 _storeDir=dir.getCanonicalFile();
476 }
477
478
479 public File getStoreDirectory ()
480 {
481 return _storeDir;
482 }
483
484
485 public void setLazyLoad(boolean lazyLoad)
486 {
487 _lazyLoad = lazyLoad;
488 }
489
490
491 public boolean isLazyLoad()
492 {
493 return _lazyLoad;
494 }
495
496
497 public boolean isDeleteUnrestorableSessions()
498 {
499 return _deleteUnrestorableSessions;
500 }
501
502
503 public void setDeleteUnrestorableSessions(boolean deleteUnrestorableSessions)
504 {
505 _deleteUnrestorableSessions = deleteUnrestorableSessions;
506 }
507
508
509 public void restoreSessions () throws Exception
510 {
511 _sessionsLoaded = true;
512
513 if (_storeDir==null || !_storeDir.exists())
514 {
515 return;
516 }
517
518 if (!_storeDir.canRead())
519 {
520 LOG.warn ("Unable to restore Sessions: Cannot read from Session storage directory "+_storeDir.getAbsolutePath());
521 return;
522 }
523
524 String[] files = _storeDir.list();
525 for (int i=0;files!=null&&i<files.length;i++)
526 {
527 restoreSession(files[i]);
528 }
529 }
530
531
532 protected synchronized HashedSession restoreSession(String idInCuster)
533 {
534 File file = new File(_storeDir,idInCuster);
535
536 FileInputStream in = null;
537 Exception error = null;
538 try
539 {
540 if (file.exists())
541 {
542 in = new FileInputStream(file);
543 HashedSession session = restoreSession(in, null);
544 addSession(session, false);
545 session.didActivate();
546 return session;
547 }
548 }
549 catch (Exception e)
550 {
551 error = e;
552 }
553 finally
554 {
555 if (in != null) IO.close(in);
556
557 if (error != null)
558 {
559 if (isDeleteUnrestorableSessions() && file.exists() && file.getParentFile().equals(_storeDir) )
560 {
561 file.delete();
562 LOG.warn("Deleting file for unrestorable session "+idInCuster, error);
563 }
564 else
565 {
566 __log.warn("Problem restoring session "+idInCuster, error);
567 }
568 }
569 else
570 file.delete();
571 }
572 return null;
573 }
574
575
576 public void saveSessions(boolean reactivate) throws Exception
577 {
578 if (_storeDir==null || !_storeDir.exists())
579 {
580 return;
581 }
582
583 if (!_storeDir.canWrite())
584 {
585 LOG.warn ("Unable to save Sessions: Session persistence storage directory "+_storeDir.getAbsolutePath()+ " is not writeable");
586 return;
587 }
588
589 for (HashedSession session : _sessions.values())
590 session.save(reactivate);
591 }
592
593
594
595 public HashedSession restoreSession (InputStream is, HashedSession session) throws Exception
596 {
597 DataInputStream di = new DataInputStream(is);
598
599 String clusterId = di.readUTF();
600 di.readUTF();
601
602 long created = di.readLong();
603 long accessed = di.readLong();
604 int requests = di.readInt();
605
606 if (session == null)
607 session = (HashedSession)newSession(created, accessed, clusterId);
608 session.setRequests(requests);
609
610 int size = di.readInt();
611
612 restoreSessionAttributes(di, size, session);
613
614 try
615 {
616 int maxIdle = di.readInt();
617 session.setMaxInactiveInterval(maxIdle);
618 }
619 catch (EOFException e)
620 {
621 LOG.debug("No maxInactiveInterval persisted for session "+clusterId);
622 LOG.ignore(e);
623 }
624
625 return session;
626 }
627
628
629 private void restoreSessionAttributes (InputStream is, int size, HashedSession session)
630 throws Exception
631 {
632 if (size>0)
633 {
634 ClassLoadingObjectInputStream ois = new ClassLoadingObjectInputStream(is);
635
636 for (int i=0; i<size;i++)
637 {
638 String key = ois.readUTF();
639 Object value = ois.readObject();
640 session.setAttribute(key,value);
641 }
642 }
643 }
644 }