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