1 package org.eclipse.jetty.nosql.mongodb;
2
3
4
5
6
7
8
9
10
11
12
13
14
15 import java.io.ByteArrayInputStream;
16 import java.io.ByteArrayOutputStream;
17 import java.io.IOException;
18 import java.io.ObjectInputStream;
19 import java.io.ObjectOutputStream;
20 import java.net.UnknownHostException;
21 import java.util.Date;
22 import java.util.HashMap;
23 import java.util.Map;
24 import java.util.Set;
25
26 import org.eclipse.jetty.nosql.NoSqlSession;
27 import org.eclipse.jetty.nosql.NoSqlSessionManager;
28 import org.eclipse.jetty.server.SessionIdManager;
29 import org.eclipse.jetty.util.log.Log;
30 import org.eclipse.jetty.util.log.Logger;
31 import org.eclipse.jetty.util.log.Logger;
32 import org.omg.CORBA._IDLTypeStub;
33
34 import com.mongodb.BasicDBObject;
35 import com.mongodb.DBCollection;
36 import com.mongodb.DBObject;
37 import com.mongodb.MongoException;
38
39 public class MongoSessionManager extends NoSqlSessionManager
40 {
41 private static final Logger LOG = Log.getLogger(MongoSessionManager.class);
42
43 private final static Logger __log = Log.getLogger("org.eclipse.jetty.server.session");
44
45
46
47
48 private final static String __METADATA = "__metadata__";
49
50 public final static String __ID = "id";
51 private final static String __CREATED = "created";
52 public final static String __VALID = "valid";
53 public final static String __INVALIDATED = "invalidated";
54 public final static String __ACCESSED = "accessed";
55 private final static String __CONTEXT = "context";
56 public final static String __VERSION = __METADATA + ".version";
57
58
59
60
61 private String _contextId = null;
62
63
64 private DBCollection _sessions;
65 private DBObject __version_1;
66
67
68
69 public MongoSessionManager() throws UnknownHostException, MongoException
70 {
71
72 }
73
74
75
76
77 @Override
78 public void doStart() throws Exception
79 {
80 super.doStart();
81 String[] hosts = getContextHandler().getVirtualHosts();
82 if (hosts == null || hosts.length == 0)
83 hosts = getContextHandler().getConnectorNames();
84 if (hosts == null || hosts.length == 0)
85 hosts = new String[]
86 { "::" };
87
88 String contextPath = getContext().getContextPath();
89 if (contextPath == null || "".equals(contextPath))
90 {
91 contextPath = "*";
92 }
93
94 _contextId = createContextId(hosts,contextPath);
95
96 __version_1 = new BasicDBObject(getContextKey(__VERSION),1);
97 }
98
99
100
101
102
103 @Override
104 public void setSessionIdManager(SessionIdManager metaManager)
105 {
106 MongoSessionIdManager msim = (MongoSessionIdManager)metaManager;
107 _sessions=msim.getSessions();
108 super.setSessionIdManager(metaManager);
109
110 }
111
112
113 @Override
114 protected synchronized Object save(NoSqlSession session, Object version, boolean activateAfterSave)
115 {
116 try
117 {
118 __log.debug("MongoSessionManager:save:" + session);
119 session.willPassivate();
120 ByteArrayOutputStream bout = new ByteArrayOutputStream();
121 ObjectOutputStream out = new ObjectOutputStream(bout);
122
123
124 BasicDBObject key = new BasicDBObject(__ID,session.getClusterId());
125 key.put(__VALID,true);
126
127
128 BasicDBObject update = new BasicDBObject();
129 boolean upsert = false;
130 BasicDBObject sets = new BasicDBObject();
131 BasicDBObject unsets = new BasicDBObject();
132
133
134 if (version == null)
135 {
136
137 upsert = true;
138 version = new Long(1);
139 sets.put(__CREATED,session.getCreationTime());
140 sets.put(getContextKey(__VERSION),version);
141 }
142 else
143 {
144 version = new Long(((Long)version).intValue() + 1);
145 update.put("$inc",__version_1);
146 }
147
148
149 if (session.isValid())
150 {
151 sets.put(__ACCESSED,session.getAccessed());
152 Set<String> names = session.takeDirty();
153 if (isSaveAllAttributes() || upsert)
154 {
155 names.addAll(session.getNames());
156 }
157
158 for (String name : names)
159 {
160 Object value = session.getAttribute(name);
161 if (value == null)
162 unsets.put(getContextKey() + "." + encodeName(name),1);
163 else
164 sets.put(getContextKey() + "." + encodeName(name),encodeName(out,bout,value));
165 }
166 }
167 else
168 {
169 sets.put(__VALID,false);
170 sets.put(__INVALIDATED, System.currentTimeMillis());
171 unsets.put(getContextKey(),1);
172 }
173
174
175 if (!sets.isEmpty())
176 update.put("$set",sets);
177 if (!unsets.isEmpty())
178 update.put("$unset",unsets);
179
180 _sessions.update(key,update,upsert,false);
181 __log.debug("MongoSessionManager:save:db.sessions.update(" + key + "," + update + ",true)");
182
183 if (activateAfterSave)
184 session.didActivate();
185
186 return version;
187 }
188 catch (Exception e)
189 {
190 LOG.warn(e);
191 }
192 return null;
193 }
194
195
196 @Override
197 protected Object refresh(NoSqlSession session, Object version)
198 {
199 __log.debug("MongoSessionManager:refresh " + session);
200
201
202 if (version != null)
203 {
204 DBObject o = _sessions.findOne(new BasicDBObject(__ID,session.getClusterId()),__version_1);
205
206 if (o != null)
207 {
208 Object saved = getNestedValue(o, getContextKey(__VERSION));
209
210 if (saved != null && saved.equals(version))
211 {
212 __log.debug("MongoSessionManager:refresh not needed");
213 return version;
214 }
215 version = saved;
216 }
217 }
218
219
220 DBObject o = _sessions.findOne(new BasicDBObject(__ID,session.getClusterId()));
221
222
223 if (o == null)
224 {
225 __log.debug("MongoSessionManager:refresh:marking invalid, no object");
226 session.invalidate();
227 return null;
228 }
229
230
231 Boolean valid = (Boolean)o.get(__VALID);
232 if (valid == null || !valid)
233 {
234 __log.debug("MongoSessionManager:refresh:marking invalid, valid flag " + valid);
235 session.invalidate();
236 return null;
237 }
238
239
240
241 session.willPassivate();
242 try
243 {
244 session.clearAttributes();
245
246 DBObject attrs = (DBObject)getNestedValue(o,getContextKey());
247
248 if (attrs != null)
249 {
250 for (String name : attrs.keySet())
251 {
252 if ( __METADATA.equals(name) )
253 {
254 continue;
255 }
256
257 String attr = decodeName(name);
258 Object value = decodeValue(attrs.get(name));
259 session.doPutOrRemove(attr,value);
260 session.bindValue(attr,value);
261 }
262 }
263
264 session.didActivate();
265
266
267 return version;
268 }
269 catch (Exception e)
270 {
271 LOG.warn(e);
272 }
273
274 return null;
275 }
276
277
278 @Override
279 protected synchronized NoSqlSession loadSession(String clusterId)
280 {
281 DBObject o = _sessions.findOne(new BasicDBObject(__ID,clusterId));
282
283 __log.debug("MongoSessionManager:loaded " + o);
284
285 if (o == null)
286 {
287 return null;
288 }
289
290 Boolean valid = (Boolean)o.get(__VALID);
291 if (valid == null || !valid)
292 {
293 return null;
294 }
295
296 try
297 {
298 Object version = o.get(getContextKey(__VERSION));
299 Long created = (Long)o.get(__CREATED);
300 Long accessed = (Long)o.get(__ACCESSED);
301
302 NoSqlSession session = new NoSqlSession(this,created,accessed,clusterId,version);
303
304
305 DBObject attrs = (DBObject)getNestedValue(o,getContextKey());
306
307 __log.debug("MongoSessionManager:attrs: " + attrs);
308 if (attrs != null)
309 {
310 for (String name : attrs.keySet())
311 {
312 if ( __METADATA.equals(name) )
313 {
314 continue;
315 }
316
317 String attr = decodeName(name);
318 Object value = decodeValue(attrs.get(name));
319
320 session.doPutOrRemove(attr,value);
321 session.bindValue(attr,value);
322
323 }
324 }
325 session.didActivate();
326
327 return session;
328 }
329 catch (Exception e)
330 {
331 LOG.warn(e);
332 }
333 return null;
334 }
335
336
337 @Override
338 protected boolean remove(NoSqlSession session)
339 {
340 __log.debug("MongoSessionManager:remove:session " + session.getClusterId());
341
342
343
344
345
346 BasicDBObject key = new BasicDBObject(__ID,session.getClusterId());
347
348 DBObject o = _sessions.findOne(key,__version_1);
349
350 if (o != null)
351 {
352 BasicDBObject remove = new BasicDBObject();
353 BasicDBObject unsets = new BasicDBObject();
354 unsets.put(getContextKey(),1);
355 remove.put("$unsets",unsets);
356 _sessions.update(key,remove);
357
358 return true;
359 }
360 else
361 {
362 return false;
363 }
364 }
365
366
367 @Override
368 protected void invalidateSession(String idInCluster)
369 {
370 __log.debug("MongoSessionManager:invalidateSession:invalidating " + idInCluster);
371
372 super.invalidateSession(idInCluster);
373
374
375
376
377
378 DBObject validKey = new BasicDBObject(__VALID, true);
379 DBObject o = _sessions.findOne(new BasicDBObject(__ID,idInCluster), validKey);
380
381 if (o != null && (Boolean)o.get(__VALID))
382 {
383 BasicDBObject update = new BasicDBObject();
384 BasicDBObject sets = new BasicDBObject();
385 sets.put(__VALID,false);
386 sets.put(__INVALIDATED, System.currentTimeMillis());
387 update.put("$set",sets);
388
389 BasicDBObject key = new BasicDBObject(__ID,idInCluster);
390
391 _sessions.update(key,update);
392 }
393 }
394
395
396 protected String encodeName(String name)
397 {
398 return name.replace("%","%25").replace(".","%2E");
399 }
400
401
402 protected String decodeName(String name)
403 {
404 return name.replace("%2E",".").replace("%25","%");
405 }
406
407
408 protected Object encodeName(ObjectOutputStream out, ByteArrayOutputStream bout, Object value) throws IOException
409 {
410 if (value instanceof Number || value instanceof String || value instanceof Boolean || value instanceof Date)
411 {
412 return value;
413 }
414 else if (value.getClass().equals(HashMap.class))
415 {
416 BasicDBObject o = new BasicDBObject();
417 for (Map.Entry<?, ?> entry : ((Map<?, ?>)value).entrySet())
418 {
419 if (!(entry.getKey() instanceof String))
420 {
421 o = null;
422 break;
423 }
424 o.append(encodeName(entry.getKey().toString()),encodeName(out,bout,value));
425 }
426
427 if (o != null)
428 return o;
429 }
430
431 out.reset();
432 out.writeUnshared(value);
433 out.flush();
434 return bout.toByteArray();
435 }
436
437
438 protected Object decodeValue(Object value) throws IOException, ClassNotFoundException
439 {
440 if (value == null || value instanceof Number || value instanceof String || value instanceof Boolean || value instanceof Date)
441 {
442 return value;
443 }
444 else if (value instanceof byte[])
445 {
446 ObjectInputStream in = new ObjectInputStream(new ByteArrayInputStream((byte[])value));
447 return in.readObject();
448 }
449 else if (value instanceof DBObject)
450 {
451 Map<String, Object> map = new HashMap<String, Object>();
452 for (String name : ((DBObject)value).keySet())
453 {
454 String attr = decodeName(name);
455 map.put(attr,decodeValue(((DBObject)value).get(name)));
456 }
457 return map;
458 }
459 else
460 {
461 throw new IllegalStateException(value.getClass().toString());
462 }
463 }
464
465
466
467 private String getContextKey()
468 {
469 return __CONTEXT + "." + _contextId;
470 }
471
472
473 private String getContextKey(String keybit)
474 {
475 return __CONTEXT + "." + _contextId + "." + keybit;
476 }
477
478 public void purge()
479 {
480 ((MongoSessionIdManager)_sessionIdManager).purge();
481 }
482
483 public void purgeFully()
484 {
485 ((MongoSessionIdManager)_sessionIdManager).purgeFully();
486 }
487
488 public void scavenge()
489 {
490 ((MongoSessionIdManager)_sessionIdManager).scavenge();
491 }
492
493 public void scavengeFully()
494 {
495 ((MongoSessionIdManager)_sessionIdManager).scavengeFully();
496 }
497
498
499
500
501
502
503
504
505 public long getSessionStoreCount()
506 {
507 return _sessions.find().count();
508 }
509
510
511
512
513
514
515
516
517
518 private String createContextId(String[] virtualHosts, String contextPath)
519 {
520 String contextId = virtualHosts[0] + contextPath;
521
522 contextId.replace('/', '_');
523 contextId.replace('.','_');
524 contextId.replace('\\','_');
525
526 return contextId;
527 }
528
529
530
531
532 private Object getNestedValue(DBObject dbObject, String nestedKey)
533 {
534 String[] keyChain = nestedKey.split("\\.");
535
536 DBObject temp = dbObject;
537
538 for (int i = 0; i < keyChain.length - 1; ++i)
539 {
540 temp = (DBObject)temp.get(keyChain[i]);
541
542 if ( temp == null )
543 {
544 return null;
545 }
546 }
547
548 return temp.get(keyChain[keyChain.length - 1]);
549 }
550
551 }