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 __log.debug("PURGING");
263 BasicDBObject invalidQuery = new BasicDBObject();
264
265 invalidQuery.put(MongoSessionManager.__ACCESSED, new BasicDBObject("$lt",System.currentTimeMillis() - _purgeInvalidAge));
266 invalidQuery.put(MongoSessionManager.__VALID, false);
267
268 DBCursor oldSessions = _sessions.find(invalidQuery, new BasicDBObject(MongoSessionManager.__ID, 1));
269
270 for (DBObject session : oldSessions)
271 {
272 String id = (String)session.get("id");
273
274 __log.debug("MongoSessionIdManager:purging invalid session {}", id);
275
276 _sessions.remove(session);
277 }
278
279 if (_purgeValidAge != 0)
280 {
281 BasicDBObject validQuery = new BasicDBObject();
282
283 validQuery.put(MongoSessionManager.__ACCESSED,new BasicDBObject("$lt",System.currentTimeMillis() - _purgeValidAge));
284 validQuery.put(MongoSessionManager.__VALID, true);
285
286 oldSessions = _sessions.find(validQuery,new BasicDBObject(MongoSessionManager.__ID,1));
287
288 for (DBObject session : oldSessions)
289 {
290 String id = (String)session.get(MongoSessionManager.__ID);
291
292 __log.debug("MongoSessionIdManager:purging valid session {}", id);
293
294 _sessions.remove(session);
295 }
296 }
297
298 }
299
300
301
302
303
304
305
306 protected void purgeFully()
307 {
308 BasicDBObject invalidQuery = new BasicDBObject();
309 invalidQuery.put(MongoSessionManager.__VALID, false);
310
311 DBCursor oldSessions = _sessions.find(invalidQuery, new BasicDBObject(MongoSessionManager.__ID, 1));
312
313 for (DBObject session : oldSessions)
314 {
315 String id = (String)session.get(MongoSessionManager.__ID);
316
317 __log.debug("MongoSessionIdManager:purging invalid session {}", id);
318
319 _sessions.remove(session);
320 }
321
322 }
323
324
325
326 public DBCollection getSessions()
327 {
328 return _sessions;
329 }
330
331
332
333 public boolean isPurgeEnabled()
334 {
335 return _purge;
336 }
337
338
339 public void setPurge(boolean purge)
340 {
341 this._purge = purge;
342 }
343
344
345
346
347
348
349
350
351 public void setScavengePeriod(long scavengePeriod)
352 {
353 if (scavengePeriod <= 0)
354 _scavengePeriod = __defaultScavengePeriod;
355 else
356 _scavengePeriod = TimeUnit.SECONDS.toMillis(scavengePeriod);
357 }
358
359
360 public void setPurgeDelay(long purgeDelay)
361 {
362 if ( isRunning() )
363 {
364 throw new IllegalStateException();
365 }
366
367 this._purgeDelay = purgeDelay;
368 }
369
370
371 public long getPurgeInvalidAge()
372 {
373 return _purgeInvalidAge;
374 }
375
376
377
378
379
380
381 public void setPurgeInvalidAge(long purgeValidAge)
382 {
383 this._purgeInvalidAge = purgeValidAge;
384 }
385
386
387 public long getPurgeValidAge()
388 {
389 return _purgeValidAge;
390 }
391
392
393
394
395
396
397
398
399 public void setPurgeValidAge(long purgeValidAge)
400 {
401 this._purgeValidAge = purgeValidAge;
402 }
403
404
405 @Override
406 protected void doStart() throws Exception
407 {
408 __log.debug("MongoSessionIdManager:starting");
409
410
411 synchronized (this)
412 {
413
414 _scheduler =_server.getBean(Scheduler.class);
415 if (_scheduler == null)
416 {
417 _scheduler = new ScheduledExecutorScheduler();
418 _ownScheduler = true;
419 _scheduler.start();
420 }
421 else if (!_scheduler.isStarted())
422 throw new IllegalStateException("Shared scheduler not started");
423
424
425
426 if (_scavengePeriod > 0)
427 {
428 if (_scavengerTask != null)
429 {
430 _scavengerTask.cancel();
431 _scavengerTask = null;
432 }
433
434 _scavengerTask = _scheduler.schedule(new Scavenger(), _scavengePeriod, TimeUnit.MILLISECONDS);
435 }
436 else if (__log.isDebugEnabled())
437 __log.debug("Scavenger disabled");
438
439
440
441 if ( _purge )
442 {
443 if (_purgerTask != null)
444 {
445 _purgerTask.cancel();
446 _purgerTask = null;
447 }
448 _purgerTask = _scheduler.schedule(new Purger(), _purgeDelay, TimeUnit.MILLISECONDS);
449 }
450 else if (__log.isDebugEnabled())
451 __log.debug("Purger disabled");
452 }
453 }
454
455
456 @Override
457 protected void doStop() throws Exception
458 {
459 synchronized (this)
460 {
461 if (_scavengerTask != null)
462 {
463 _scavengerTask.cancel();
464 _scavengerTask = null;
465 }
466
467 if (_purgerTask != null)
468 {
469 _purgerTask.cancel();
470 _purgerTask = null;
471 }
472
473 if (_ownScheduler && _scheduler != null)
474 {
475 _scheduler.stop();
476 _scheduler = null;
477 }
478 }
479 super.doStop();
480 }
481
482
483
484
485
486 @Override
487 public boolean idInUse(String sessionId)
488 {
489
490
491
492 DBObject o = _sessions.findOne(new BasicDBObject("id",sessionId), __valid_true);
493
494 if ( o != null )
495 {
496 Boolean valid = (Boolean)o.get(MongoSessionManager.__VALID);
497 if ( valid == null )
498 {
499 return false;
500 }
501
502 return valid;
503 }
504
505 return false;
506 }
507
508
509 @Override
510 public void addSession(HttpSession session)
511 {
512 if (session == null)
513 {
514 return;
515 }
516
517
518
519
520
521 __log.debug("MongoSessionIdManager:addSession {}", session.getId());
522
523 synchronized (_sessionsIds)
524 {
525 _sessionsIds.add(session.getId());
526 }
527
528 }
529
530
531 @Override
532 public void removeSession(HttpSession session)
533 {
534 if (session == null)
535 {
536 return;
537 }
538
539 synchronized (_sessionsIds)
540 {
541 _sessionsIds.remove(session.getId());
542 }
543 }
544
545
546
547
548
549
550
551 @Override
552 public void invalidateAll(String sessionId)
553 {
554 synchronized (_sessionsIds)
555 {
556 _sessionsIds.remove(sessionId);
557
558
559
560 Handler[] contexts = _server.getChildHandlersByClass(ContextHandler.class);
561 for (int i=0; contexts!=null && i<contexts.length; i++)
562 {
563 SessionHandler sessionHandler = ((ContextHandler)contexts[i]).getChildHandlerByClass(SessionHandler.class);
564 if (sessionHandler != null)
565 {
566 SessionManager manager = sessionHandler.getSessionManager();
567
568 if (manager != null && manager instanceof MongoSessionManager)
569 {
570 ((MongoSessionManager)manager).invalidateSession(sessionId);
571 }
572 }
573 }
574 }
575 }
576
577
578
579
580
581
582
583 public void expireAll (String sessionId)
584 {
585 synchronized (_sessionsIds)
586 {
587 _sessionsIds.remove(sessionId);
588
589
590
591
592 Handler[] contexts = _server.getChildHandlersByClass(ContextHandler.class);
593 for (int i=0; contexts!=null && i<contexts.length; i++)
594 {
595 SessionHandler sessionHandler = ((ContextHandler)contexts[i]).getChildHandlerByClass(SessionHandler.class);
596 if (sessionHandler != null)
597 {
598 SessionManager manager = sessionHandler.getSessionManager();
599
600 if (manager != null && manager instanceof MongoSessionManager)
601 {
602 ((MongoSessionManager)manager).expire(sessionId);
603 }
604 }
605 }
606 }
607 }
608
609
610 @Override
611 public void renewSessionId(String oldClusterId, String oldNodeId, HttpServletRequest request)
612 {
613
614 String newClusterId = newSessionId(request.hashCode());
615
616 synchronized (_sessionsIds)
617 {
618 _sessionsIds.remove(oldClusterId);
619 _sessionsIds.add(newClusterId);
620
621
622 Handler[] contexts = _server.getChildHandlersByClass(ContextHandler.class);
623 for (int i=0; contexts!=null && i<contexts.length; i++)
624 {
625 SessionHandler sessionHandler = ((ContextHandler)contexts[i]).getChildHandlerByClass(SessionHandler.class);
626 if (sessionHandler != null)
627 {
628 SessionManager manager = sessionHandler.getSessionManager();
629
630 if (manager != null && manager instanceof MongoSessionManager)
631 {
632 ((MongoSessionManager)manager).renewSessionId(oldClusterId, oldNodeId, newClusterId, getNodeId(newClusterId, request));
633 }
634 }
635 }
636 }
637 }
638
639 }