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 __log.warn("Problem scavenging sessions", t);
315 }
316 finally
317 {
318 thread.setContextClassLoader(old_loader);
319 }
320 }
321
322
323 @Override
324 protected void addSession(AbstractSession session)
325 {
326 if (isRunning())
327 _sessions.put(session.getClusterId(),(HashedSession)session);
328 }
329
330
331 @Override
332 public AbstractSession getSession(String idInCluster)
333 {
334 if ( _lazyLoad && !_sessionsLoaded)
335 {
336 try
337 {
338 restoreSessions();
339 }
340 catch(Exception e)
341 {
342 __log.warn(e);
343 }
344 }
345
346 Map<String,HashedSession> sessions=_sessions;
347 if (sessions==null)
348 return null;
349
350 HashedSession session = sessions.get(idInCluster);
351
352 if (session == null && _lazyLoad)
353 session=restoreSession(idInCluster);
354 if (session == null)
355 return null;
356
357 if (_idleSavePeriodMs!=0)
358 session.deIdle();
359
360 return session;
361 }
362
363
364 @Override
365 protected void invalidateSessions() throws Exception
366 {
367
368 ArrayList<HashedSession> sessions=new ArrayList<HashedSession>(_sessions.values());
369 int loop=100;
370 while (sessions.size()>0 && loop-->0)
371 {
372
373 if (isStopping() && _storeDir != null && _storeDir.exists() && _storeDir.canWrite())
374 {
375
376 for (HashedSession session : sessions)
377 {
378 session.save(false);
379 removeSession(session,false);
380 }
381 }
382 else
383 {
384 for (HashedSession session : sessions)
385 session.invalidate();
386 }
387
388
389 sessions=new ArrayList<HashedSession>(_sessions.values());
390 }
391 }
392
393
394 @Override
395 protected AbstractSession newSession(HttpServletRequest request)
396 {
397 return new HashedSession(this, request);
398 }
399
400
401 protected AbstractSession newSession(long created, long accessed, String clusterId)
402 {
403 return new HashedSession(this, created,accessed, clusterId);
404 }
405
406
407 @Override
408 protected boolean removeSession(String clusterId)
409 {
410 return _sessions.remove(clusterId)!=null;
411 }
412
413
414 public void setStoreDirectory (File dir)
415 {
416 _storeDir=dir;
417 }
418
419
420 public File getStoreDirectory ()
421 {
422 return _storeDir;
423 }
424
425
426 public void setLazyLoad(boolean lazyLoad)
427 {
428 _lazyLoad = lazyLoad;
429 }
430
431
432 public boolean isLazyLoad()
433 {
434 return _lazyLoad;
435 }
436
437
438 public void restoreSessions () throws Exception
439 {
440 _sessionsLoaded = true;
441
442 if (_storeDir==null || !_storeDir.exists())
443 {
444 return;
445 }
446
447 if (!_storeDir.canRead())
448 {
449 __log.warn ("Unable to restore Sessions: Cannot read from Session storage directory "+_storeDir.getAbsolutePath());
450 return;
451 }
452
453 String[] files = _storeDir.list();
454 for (int i=0;files!=null&&i<files.length;i++)
455 {
456 restoreSession(files[i]);
457 }
458 }
459
460
461 protected synchronized HashedSession restoreSession(String idInCuster)
462 {
463 try
464 {
465 File file = new File(_storeDir,idInCuster);
466 if (file.exists())
467 {
468 FileInputStream in = new FileInputStream(file);
469 HashedSession session = restoreSession(in, null);
470 in.close();
471 addSession(session, false);
472 session.didActivate();
473 file.delete();
474 return session;
475 }
476 }
477 catch (Exception e)
478 {
479 __log.warn("Problem restoring session "+idInCuster, e);
480 }
481 return null;
482 }
483
484
485 public void saveSessions(boolean reactivate) throws Exception
486 {
487 if (_storeDir==null || !_storeDir.exists())
488 {
489 return;
490 }
491
492 if (!_storeDir.canWrite())
493 {
494 __log.warn ("Unable to save Sessions: Session persistence storage directory "+_storeDir.getAbsolutePath()+ " is not writeable");
495 return;
496 }
497
498 for (HashedSession session : _sessions.values())
499 session.save(true);
500 }
501
502
503 public HashedSession restoreSession (InputStream is, HashedSession session) throws Exception
504 {
505
506
507
508
509 DataInputStream in = new DataInputStream(is);
510 String clusterId = in.readUTF();
511 in.readUTF();
512 long created = in.readLong();
513 long accessed = in.readLong();
514 int requests = in.readInt();
515
516 if (session == null)
517 session = (HashedSession)newSession(created, accessed, clusterId);
518 session.setRequests(requests);
519 int size = in.readInt();
520 if (size>0)
521 {
522 ClassLoadingObjectInputStream ois = new ClassLoadingObjectInputStream(in);
523 for (int i=0; i<size;i++)
524 {
525 String key = ois.readUTF();
526 Object value = ois.readObject();
527 session.setAttribute(key,value);
528 }
529 ois.close();
530 }
531 else
532 in.close();
533 return session;
534 }
535
536
537
538
539 protected class ClassLoadingObjectInputStream extends ObjectInputStream
540 {
541
542 public ClassLoadingObjectInputStream(java.io.InputStream in) throws IOException
543 {
544 super(in);
545 }
546
547
548 public ClassLoadingObjectInputStream () throws IOException
549 {
550 super();
551 }
552
553
554 @Override
555 public Class<?> resolveClass (java.io.ObjectStreamClass cl) throws IOException, ClassNotFoundException
556 {
557 try
558 {
559 return Class.forName(cl.getName(), false, Thread.currentThread().getContextClassLoader());
560 }
561 catch (ClassNotFoundException e)
562 {
563 return super.resolveClass(cl);
564 }
565 }
566 }
567 }