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