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 int _scavengePeriodMs=30000;
58 int _savePeriodMs=0;
59 int _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 _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 _idleSavePeriodMs / 1000;
165 }
166
167
168
169
170
171
172
173
174
175
176 public void setIdleSavePeriod(int seconds)
177 {
178 _idleSavePeriodMs = seconds * 1000;
179 }
180
181
182 @Override
183 public void setMaxInactiveInterval(int seconds)
184 {
185 super.setMaxInactiveInterval(seconds);
186 if (_dftMaxIdleSecs>0&&_scavengePeriodMs>_dftMaxIdleSecs*1000)
187 setScavengePeriod((_dftMaxIdleSecs+9)/10);
188 }
189
190
191
192
193
194 public void setSavePeriod (int seconds)
195 {
196 int period = (seconds * 1000);
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 _savePeriodMs/1000;
240 }
241
242
243
244
245
246 public void setScavengePeriod(int seconds)
247 {
248 if (seconds==0)
249 seconds=60;
250
251 int old_period=_scavengePeriodMs;
252 int period=seconds*1000;
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()*1000;
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 if (t instanceof ThreadDeath)
316 throw ((ThreadDeath)t);
317 else
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 void restoreSessions () throws Exception
443 {
444 _sessionsLoaded = true;
445
446 if (_storeDir==null || !_storeDir.exists())
447 {
448 return;
449 }
450
451 if (!_storeDir.canRead())
452 {
453 __log.warn ("Unable to restore Sessions: Cannot read from Session storage directory "+_storeDir.getAbsolutePath());
454 return;
455 }
456
457 String[] files = _storeDir.list();
458 for (int i=0;files!=null&&i<files.length;i++)
459 {
460 restoreSession(files[i]);
461 }
462 }
463
464
465 protected synchronized HashedSession restoreSession(String idInCuster)
466 {
467 try
468 {
469 File file = new File(_storeDir,idInCuster);
470 if (file.exists())
471 {
472 FileInputStream in = new FileInputStream(file);
473 HashedSession session = restoreSession(in, null);
474 in.close();
475 addSession(session, false);
476 session.didActivate();
477 file.delete();
478 return session;
479 }
480 }
481 catch (Exception e)
482 {
483 __log.warn("Problem restoring session "+idInCuster, e);
484 }
485 return null;
486 }
487
488
489 public void saveSessions(boolean reactivate) throws Exception
490 {
491 if (_storeDir==null || !_storeDir.exists())
492 {
493 return;
494 }
495
496 if (!_storeDir.canWrite())
497 {
498 __log.warn ("Unable to save Sessions: Session persistence storage directory "+_storeDir.getAbsolutePath()+ " is not writeable");
499 return;
500 }
501
502 for (HashedSession session : _sessions.values())
503 session.save(true);
504 }
505
506
507 public HashedSession restoreSession (InputStream is, HashedSession session) throws Exception
508 {
509
510
511
512
513 DataInputStream in = new DataInputStream(is);
514 String clusterId = in.readUTF();
515 in.readUTF();
516 long created = in.readLong();
517 long accessed = in.readLong();
518 int requests = in.readInt();
519
520 if (session == null)
521 session = (HashedSession)newSession(created, accessed, clusterId);
522 session.setRequests(requests);
523 int size = in.readInt();
524 if (size>0)
525 {
526 ClassLoadingObjectInputStream ois = new ClassLoadingObjectInputStream(in);
527 for (int i=0; i<size;i++)
528 {
529 String key = ois.readUTF();
530 Object value = ois.readObject();
531 session.setAttribute(key,value);
532 }
533 ois.close();
534 }
535 else
536 in.close();
537 return session;
538 }
539
540
541
542
543 protected class ClassLoadingObjectInputStream extends ObjectInputStream
544 {
545
546 public ClassLoadingObjectInputStream(java.io.InputStream in) throws IOException
547 {
548 super(in);
549 }
550
551
552 public ClassLoadingObjectInputStream () throws IOException
553 {
554 super();
555 }
556
557
558 @Override
559 public Class<?> resolveClass (java.io.ObjectStreamClass cl) throws IOException, ClassNotFoundException
560 {
561 try
562 {
563 return Class.forName(cl.getName(), false, Thread.currentThread().getContextClassLoader());
564 }
565 catch (ClassNotFoundException e)
566 {
567 return super.resolveClass(cl);
568 }
569 }
570 }
571 }