1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19 package org.eclipse.jetty.nosql.mongodb;
20
21
22 import java.net.UnknownHostException;
23 import java.util.HashSet;
24 import java.util.Random;
25 import java.util.Set;
26 import java.util.Timer;
27 import java.util.TimerTask;
28 import java.util.concurrent.TimeUnit;
29
30 import javax.servlet.http.HttpServletRequest;
31 import javax.servlet.http.HttpSession;
32
33 import org.eclipse.jetty.server.Handler;
34 import org.eclipse.jetty.server.Server;
35 import org.eclipse.jetty.server.SessionManager;
36 import org.eclipse.jetty.server.handler.ContextHandler;
37 import org.eclipse.jetty.server.session.AbstractSessionIdManager;
38 import org.eclipse.jetty.server.session.SessionHandler;
39 import org.eclipse.jetty.util.log.Log;
40 import org.eclipse.jetty.util.log.Logger;
41 import org.eclipse.jetty.util.thread.ScheduledExecutorScheduler;
42 import org.eclipse.jetty.util.thread.Scheduler;
43
44 import com.mongodb.BasicDBObject;
45 import com.mongodb.BasicDBObjectBuilder;
46 import com.mongodb.DBCollection;
47 import com.mongodb.DBCursor;
48 import com.mongodb.DBObject;
49 import com.mongodb.Mongo;
50 import com.mongodb.MongoException;
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66 public class MongoSessionIdManager extends AbstractSessionIdManager
67 {
68 private final static Logger __log = Log.getLogger("org.eclipse.jetty.server.session");
69
70 final static DBObject __version_1 = new BasicDBObject(MongoSessionManager.__VERSION,1);
71 final static DBObject __valid_false = new BasicDBObject(MongoSessionManager.__VALID,false);
72 final static DBObject __valid_true = new BasicDBObject(MongoSessionManager.__VALID,true);
73
74 final static long __defaultScavengePeriod = 30 * 60 * 1000;
75
76
77 final DBCollection _sessions;
78 protected Server _server;
79 private Scheduler _scheduler;
80 private boolean _ownScheduler;
81 private Scheduler.Task _scavengerTask;
82 private Scheduler.Task _purgerTask;
83
84
85
86 private long _scavengePeriod = __defaultScavengePeriod;
87
88
89
90
91
92 private boolean _purge = true;
93
94
95
96
97 private long _purgeDelay = 24 * 60 * 60 * 1000;
98
99
100
101
102
103 private long _purgeInvalidAge = 24 * 60 * 60 * 1000;
104
105
106
107
108
109 private long _purgeValidAge = 7 * 24 * 60 * 60 * 1000;
110
111
112
113
114
115
116
117 protected final Set<String> _sessionsIds = new HashSet<String>();
118
119
120
121
122
123
124 protected class Scavenger implements Runnable
125 {
126 @Override
127 public void run()
128 {
129 try
130 {
131 scavenge();
132 }
133 finally
134 {
135 if (_scheduler != null && _scheduler.isRunning())
136 _scavengerTask = _scheduler.schedule(this, _scavengePeriod, TimeUnit.MILLISECONDS);
137 }
138 }
139 }
140
141
142
143
144
145
146 protected class Purger implements Runnable
147 {
148 @Override
149 public void run()
150 {
151 try
152 {
153 purge();
154 }
155 finally
156 {
157 if (_scheduler != null && _scheduler.isRunning())
158 _purgerTask = _scheduler.schedule(this, _purgeDelay, TimeUnit.MILLISECONDS);
159 }
160 }
161 }
162
163
164
165
166
167 public MongoSessionIdManager(Server server) throws UnknownHostException, MongoException
168 {
169 this(server, new Mongo().getDB("HttpSessions").getCollection("sessions"));
170 }
171
172
173 public MongoSessionIdManager(Server server, DBCollection sessions)
174 {
175 super(new Random());
176
177 _server = server;
178 _sessions = sessions;
179
180 _sessions.ensureIndex(
181 BasicDBObjectBuilder.start().add("id",1).get(),
182 BasicDBObjectBuilder.start().add("unique",true).add("sparse",false).get());
183 _sessions.ensureIndex(
184 BasicDBObjectBuilder.start().add("id",1).add("version",1).get(),
185 BasicDBObjectBuilder.start().add("unique",true).add("sparse",false).get());
186
187 }
188
189
190
191
192
193
194
195 protected void scavenge()
196 {
197 long now = System.currentTimeMillis();
198 __log.debug("SessionIdManager:scavenge:at {}", now);
199 synchronized (_sessionsIds)
200 {
201
202
203
204
205
206
207
208 BasicDBObject query = new BasicDBObject();
209 query.put(MongoSessionManager.__ID,new BasicDBObject("$in", _sessionsIds ));
210 query.put(MongoSessionManager.__EXPIRY, new BasicDBObject("$gt", 0));
211 query.put(MongoSessionManager.__EXPIRY, new BasicDBObject("$lt", now));
212
213
214 DBCursor checkSessions = _sessions.find(query, new BasicDBObject(MongoSessionManager.__ID, 1));
215
216 for ( DBObject session : checkSessions )
217 {
218 __log.debug("SessionIdManager:scavenge: expiring session {}", (String)session.get(MongoSessionManager.__ID));
219 expireAll((String)session.get(MongoSessionManager.__ID));
220 }
221 }
222 }
223
224
225
226
227
228
229
230
231 protected void scavengeFully()
232 {
233 __log.debug("SessionIdManager:scavengeFully");
234
235 DBCursor checkSessions = _sessions.find();
236
237 for (DBObject session : checkSessions)
238 {
239 expireAll((String)session.get(MongoSessionManager.__ID));
240 }
241
242 }
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262 protected void purge()
263 {
264 BasicDBObject invalidQuery = new BasicDBObject();
265
266 invalidQuery.put(MongoSessionManager.__ACCESSED, new BasicDBObject("$lt",System.currentTimeMillis() - _purgeInvalidAge));
267 invalidQuery.put(MongoSessionManager.__VALID, false);
268
269 DBCursor oldSessions = _sessions.find(invalidQuery, new BasicDBObject(MongoSessionManager.__ID, 1));
270
271 for (DBObject session : oldSessions)
272 {
273 String id = (String)session.get("id");
274
275 __log.debug("MongoSessionIdManager:purging invalid session {}", id);
276
277 _sessions.remove(session);
278 }
279
280 if (_purgeValidAge != 0)
281 {
282 BasicDBObject validQuery = new BasicDBObject();
283
284 validQuery.put(MongoSessionManager.__ACCESSED,new BasicDBObject("$lt",System.currentTimeMillis() - _purgeValidAge));
285 validQuery.put(MongoSessionManager.__VALID, true);
286
287 oldSessions = _sessions.find(validQuery,new BasicDBObject(MongoSessionManager.__ID,1));
288
289 for (DBObject session : oldSessions)
290 {
291 String id = (String)session.get(MongoSessionManager.__ID);
292
293 __log.debug("MongoSessionIdManager:purging valid session {}", id);
294
295 _sessions.remove(session);
296 }
297 }
298
299 }
300
301
302
303
304
305
306
307 protected void purgeFully()
308 {
309 BasicDBObject invalidQuery = new BasicDBObject();
310 invalidQuery.put(MongoSessionManager.__VALID, false);
311
312 DBCursor oldSessions = _sessions.find(invalidQuery, new BasicDBObject(MongoSessionManager.__ID, 1));
313
314 for (DBObject session : oldSessions)
315 {
316 String id = (String)session.get(MongoSessionManager.__ID);
317
318 __log.debug("MongoSessionIdManager:purging invalid session {}", id);
319
320 _sessions.remove(session);
321 }
322
323 }
324
325
326
327 public DBCollection getSessions()
328 {
329 return _sessions;
330 }
331
332
333
334 public boolean isPurgeEnabled()
335 {
336 return _purge;
337 }
338
339
340 public void setPurge(boolean purge)
341 {
342 this._purge = purge;
343 }
344
345
346
347
348
349
350
351
352 public void setScavengePeriod(long scavengePeriod)
353 {
354 if (scavengePeriod <= 0)
355 _scavengePeriod = __defaultScavengePeriod;
356 else
357 _scavengePeriod = TimeUnit.SECONDS.toMillis(scavengePeriod);
358 }
359
360
361 public void setPurgeDelay(long purgeDelay)
362 {
363 if ( isRunning() )
364 {
365 throw new IllegalStateException();
366 }
367
368 this._purgeDelay = purgeDelay;
369 }
370
371
372 public long getPurgeInvalidAge()
373 {
374 return _purgeInvalidAge;
375 }
376
377
378
379
380
381
382 public void setPurgeInvalidAge(long purgeValidAge)
383 {
384 this._purgeInvalidAge = purgeValidAge;
385 }
386
387
388 public long getPurgeValidAge()
389 {
390 return _purgeValidAge;
391 }
392
393
394
395
396
397
398
399
400 public void setPurgeValidAge(long purgeValidAge)
401 {
402 this._purgeValidAge = purgeValidAge;
403 }
404
405
406 @Override
407 protected void doStart() throws Exception
408 {
409 __log.debug("MongoSessionIdManager:starting");
410
411
412 synchronized (this)
413 {
414
415 _scheduler =_server.getBean(Scheduler.class);
416 if (_scheduler == null)
417 {
418 _scheduler = new ScheduledExecutorScheduler();
419 _ownScheduler = true;
420 _scheduler.start();
421 }
422
423
424 if (_scavengePeriod > 0)
425 {
426 if (_scavengerTask != null)
427 {
428 _scavengerTask.cancel();
429 _scavengerTask = null;
430 }
431
432 _scavengerTask = _scheduler.schedule(new Scavenger(), _scavengePeriod, TimeUnit.MILLISECONDS);
433 }
434
435
436
437 if ( _purge )
438 {
439 if (_purgerTask != null)
440 {
441 _purgerTask.cancel();
442 _purgerTask = null;
443 }
444 _purgerTask = _scheduler.schedule(new Purger(), _purgeDelay, TimeUnit.MILLISECONDS);
445 }
446 }
447 }
448
449
450 @Override
451 protected void doStop() throws Exception
452 {
453 synchronized (this)
454 {
455 if (_scavengerTask != null)
456 {
457 _scavengerTask.cancel();
458 _scavengerTask = null;
459 }
460
461 if (_purgerTask != null)
462 {
463 _purgerTask.cancel();
464 _purgerTask = null;
465 }
466
467 if (_ownScheduler && _scheduler != null)
468 {
469 _scheduler.stop();
470 _scheduler = null;
471 }
472 }
473 super.doStop();
474 }
475
476
477
478
479
480 @Override
481 public boolean idInUse(String sessionId)
482 {
483
484
485
486 DBObject o = _sessions.findOne(new BasicDBObject("id",sessionId), __valid_true);
487
488 if ( o != null )
489 {
490 Boolean valid = (Boolean)o.get(MongoSessionManager.__VALID);
491 if ( valid == null )
492 {
493 return false;
494 }
495
496 return valid;
497 }
498
499 return false;
500 }
501
502
503 @Override
504 public void addSession(HttpSession session)
505 {
506 if (session == null)
507 {
508 return;
509 }
510
511
512
513
514
515 __log.debug("MongoSessionIdManager:addSession {}", session.getId());
516
517 synchronized (_sessionsIds)
518 {
519 _sessionsIds.add(session.getId());
520 }
521
522 }
523
524
525 @Override
526 public void removeSession(HttpSession session)
527 {
528 if (session == null)
529 {
530 return;
531 }
532
533 synchronized (_sessionsIds)
534 {
535 _sessionsIds.remove(session.getId());
536 }
537 }
538
539
540
541
542
543
544
545 @Override
546 public void invalidateAll(String sessionId)
547 {
548 synchronized (_sessionsIds)
549 {
550 _sessionsIds.remove(sessionId);
551
552
553
554 Handler[] contexts = _server.getChildHandlersByClass(ContextHandler.class);
555 for (int i=0; contexts!=null && i<contexts.length; i++)
556 {
557 SessionHandler sessionHandler = ((ContextHandler)contexts[i]).getChildHandlerByClass(SessionHandler.class);
558 if (sessionHandler != null)
559 {
560 SessionManager manager = sessionHandler.getSessionManager();
561
562 if (manager != null && manager instanceof MongoSessionManager)
563 {
564 ((MongoSessionManager)manager).invalidateSession(sessionId);
565 }
566 }
567 }
568 }
569 }
570
571
572
573
574
575
576
577 public void expireAll (String sessionId)
578 {
579 synchronized (_sessionsIds)
580 {
581 _sessionsIds.remove(sessionId);
582
583
584
585
586 Handler[] contexts = _server.getChildHandlersByClass(ContextHandler.class);
587 for (int i=0; contexts!=null && i<contexts.length; i++)
588 {
589 SessionHandler sessionHandler = ((ContextHandler)contexts[i]).getChildHandlerByClass(SessionHandler.class);
590 if (sessionHandler != null)
591 {
592 SessionManager manager = sessionHandler.getSessionManager();
593
594 if (manager != null && manager instanceof MongoSessionManager)
595 {
596 ((MongoSessionManager)manager).expire(sessionId);
597 }
598 }
599 }
600 }
601 }
602
603
604 @Override
605 public void renewSessionId(String oldClusterId, String oldNodeId, HttpServletRequest request)
606 {
607
608 String newClusterId = newSessionId(request.hashCode());
609
610 synchronized (_sessionsIds)
611 {
612 _sessionsIds.remove(oldClusterId);
613 _sessionsIds.add(newClusterId);
614
615
616 Handler[] contexts = _server.getChildHandlersByClass(ContextHandler.class);
617 for (int i=0; contexts!=null && i<contexts.length; i++)
618 {
619 SessionHandler sessionHandler = ((ContextHandler)contexts[i]).getChildHandlerByClass(SessionHandler.class);
620 if (sessionHandler != null)
621 {
622 SessionManager manager = sessionHandler.getSessionManager();
623
624 if (manager != null && manager instanceof MongoSessionManager)
625 {
626 ((MongoSessionManager)manager).renewSessionId(oldClusterId, oldNodeId, newClusterId, getNodeId(newClusterId, request));
627 }
628 }
629 }
630 }
631 }
632
633 }