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