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