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