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