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