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