Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
Expand All @@ -18,25 +18,6 @@
*/
package org.jooby.mongodb;

import static java.util.Objects.requireNonNull;

import java.util.Date;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.Optional;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicBoolean;

import javax.inject.Inject;
import javax.inject.Named;

import org.bson.Document;
import org.bson.conversions.Bson;
import org.jooby.Session;
import org.jooby.Session.Builder;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.mongodb.client.MongoCollection;
import com.mongodb.client.MongoCursor;
import com.mongodb.client.MongoDatabase;
Expand All @@ -46,6 +27,23 @@
import com.typesafe.config.Config;
import com.typesafe.config.ConfigFactory;
import com.typesafe.config.ConfigValueFactory;
import org.bson.Document;
import org.bson.conversions.Bson;
import org.jooby.Session;
import org.jooby.Session.Builder;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.inject.Inject;
import javax.inject.Named;
import java.util.Date;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.Optional;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicBoolean;

import static java.util.Objects.requireNonNull;

/**
* A {@link Session.Store} powered by
Expand Down Expand Up @@ -108,6 +106,14 @@
*/
public class MongoSessionStore implements Session.Store {

private static final char DOT = '.';

private static final char UDOT = '\uFF0E';

private static final char DOLLAR = '$';

private static final char UDOLLAR = '\uFF04';

private static final String SESSION_IDX = "_sessionIdx_";

/** The logging system. */
Expand Down Expand Up @@ -138,7 +144,7 @@ public MongoSessionStore(final MongoDatabase db,
this(db, collection, seconds(timeout));
}

@SuppressWarnings({"unchecked", "rawtypes" })
@SuppressWarnings({"unchecked", "rawtypes"})
@Override
public Session get(final Builder builder) {
return Optional.ofNullable(sessions.find(Filters.eq("_id", builder.sessionId())).first())
Expand All @@ -150,12 +156,12 @@ public Session get(final Builder builder) {
Date savedAt = (Date) session.remove("_savedAt");
session.remove("_id");

return builder
builder
.accessedAt(accessedAt.getTime())
.createdAt(createdAt.getTime())
.savedAt(savedAt.getTime())
.set(session)
.build();
.savedAt(savedAt.getTime());
session.forEach((k, v) -> builder.set(decode(k.toString()), v.toString()));
return builder.build();
}).orElse(null);
}

Expand All @@ -172,7 +178,8 @@ public void save(final Session session) {
.append("_createdAt", new Date(session.createdAt()))
.append("_savedAt", new Date(session.savedAt()));
// dump attributes
session.attributes().forEach((k, v) -> doc.append(k, v));
Map<String, String> attributes = session.attributes();
attributes.forEach((k, v) -> doc.append(encode(k), v));

sessions.updateOne(filter, new Document("$set", doc), new UpdateOptions().upsert(true));
}
Expand Down Expand Up @@ -235,4 +242,20 @@ private boolean existsIdx(final String name) {
return false;
}

private String encode(final String key) {
String value = key;
if (value.charAt(0) == DOLLAR) {
value = UDOLLAR + value.substring(1);
}
return value.replace(DOT, UDOT);
}

private String decode(final String key) {
String value = key;
if (value.charAt(0) == UDOLLAR) {
value = DOLLAR + value.substring(1);
}
return value.replace(UDOT, DOT);
}

}
Original file line number Diff line number Diff line change
@@ -1,23 +1,5 @@
package org.jooby.mongodb;

import static org.easymock.EasyMock.expect;
import static org.junit.Assert.assertEquals;

import java.util.Date;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.concurrent.TimeUnit;

import org.bson.Document;
import org.bson.conversions.Bson;
import org.jooby.Session;
import org.jooby.test.MockUnit;
import org.jooby.test.MockUnit.Block;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.powermock.core.classloader.annotations.PrepareForTest;
import org.powermock.modules.junit4.PowerMockRunner;

import com.google.common.collect.ImmutableMap;
import com.mongodb.DBObject;
import com.mongodb.client.FindIterable;
Expand All @@ -29,13 +11,31 @@
import com.mongodb.client.model.IndexOptions;
import com.mongodb.client.model.UpdateOptions;
import com.mongodb.client.result.UpdateResult;
import org.bson.Document;
import org.bson.conversions.Bson;
import org.jooby.Session;
import org.jooby.test.MockUnit;
import org.jooby.test.MockUnit.Block;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.powermock.core.classloader.annotations.PrepareForTest;
import org.powermock.modules.junit4.PowerMockRunner;

import java.util.Date;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.concurrent.TimeUnit;
import java.util.function.BiConsumer;

import static org.easymock.EasyMock.expect;
import static org.junit.Assert.assertEquals;

@RunWith(PowerMockRunner.class)
@PrepareForTest({MongoSessionStore.class, IndexOptions.class, UpdateOptions.class, Filters.class,
LinkedHashMap.class })
LinkedHashMap.class})
public class MongodbSessionStoreTest {

@SuppressWarnings({"unchecked", "rawtypes" })
@SuppressWarnings({"unchecked", "rawtypes"})
MockUnit.Block boot = unit -> {
MongoCollection collection = unit.get(MongoCollection.class);

Expand All @@ -45,7 +45,7 @@ public class MongodbSessionStoreTest {

long now = System.currentTimeMillis();

Map<String, String> attrs = ImmutableMap.<String, String> of("k", "v");
Map<String, String> attrs = ImmutableMap.<String, String>of("k.v", "v", "$d", "d");

@SuppressWarnings("rawtypes")
MockUnit.Block saveSession = unit -> {
Expand All @@ -65,7 +65,8 @@ public class MongodbSessionStoreTest {
.append("_accessedAt", new Date(now))
.append("_createdAt", new Date(now))
.append("_savedAt", new Date(now))
.append("k", "v");
.append("k\uFF0Ev", "v")
.append("\uFF04d", "d");

UpdateOptions options = unit.constructor(UpdateOptions.class)
.build();
Expand Down Expand Up @@ -225,47 +226,98 @@ public void saveSyncTtl() throws Exception {
});
}

@SuppressWarnings({"unchecked", "rawtypes" })
@SuppressWarnings({"unchecked", "rawtypes"})
@Test
public void get() throws Exception {
long now = System.currentTimeMillis();
new MockUnit(Session.class, Session.Builder.class, MongoDatabase.class, MongoCollection.class,
DBObject.class)
.expect(boot)
.expect(unit -> {
Document doc = unit.mock(Document.class);

Map sessionMap = unit.constructor(LinkedHashMap.class)
.args(Map.class)
.build(doc);
expect(sessionMap.remove("_accessedAt")).andReturn(new Date(now));
expect(sessionMap.remove("_createdAt")).andReturn(new Date(now));
expect(sessionMap.remove("_savedAt")).andReturn(new Date(now));
expect(sessionMap.remove("_id")).andReturn("1234");

FindIterable result = unit.mock(FindIterable.class);
expect(result.first()).andReturn(doc);

Session.Builder sb = unit.get(Session.Builder.class);
expect(sb.sessionId()).andReturn("1234");
expect(sb.accessedAt(now)).andReturn(sb);
expect(sb.createdAt(now)).andReturn(sb);
expect(sb.savedAt(now)).andReturn(sb);
expect(sb.set(sessionMap)).andReturn(sb);
expect(sb.build()).andReturn(unit.get(Session.class));

Bson eq = unit.mock(Bson.class);
unit.mockStatic(Filters.class);
expect(Filters.eq("_id", "1234")).andReturn(eq);

MongoCollection collection = unit.get(MongoCollection.class);
expect(collection.find(eq)).andReturn(result);
})
.run(unit -> {
MongoSessionStore mss = new MongoSessionStore(unit.get(MongoDatabase.class), "sess",
"60");
assertEquals(unit.get(Session.class), mss.get(unit.get(Session.Builder.class)));
});
.expect(boot)
.expect(unit -> {
Document doc = unit.mock(Document.class);

Map sessionMap = unit.constructor(LinkedHashMap.class)
.args(Map.class)
.build(doc);
expect(sessionMap.remove("_accessedAt")).andReturn(new Date(now));
expect(sessionMap.remove("_createdAt")).andReturn(new Date(now));
expect(sessionMap.remove("_savedAt")).andReturn(new Date(now));
expect(sessionMap.remove("_id")).andReturn("1234");
sessionMap.forEach(unit.capture(BiConsumer.class));

FindIterable result = unit.mock(FindIterable.class);
expect(result.first()).andReturn(doc);

Session.Builder sb = unit.get(Session.Builder.class);
expect(sb.sessionId()).andReturn("1234");
expect(sb.accessedAt(now)).andReturn(sb);
expect(sb.createdAt(now)).andReturn(sb);
expect(sb.savedAt(now)).andReturn(sb);
expect(sb.set("a.b", "c")).andReturn(sb);
expect(sb.build()).andReturn(unit.get(Session.class));

Bson eq = unit.mock(Bson.class);
unit.mockStatic(Filters.class);
expect(Filters.eq("_id", "1234")).andReturn(eq);

MongoCollection collection = unit.get(MongoCollection.class);
expect(collection.find(eq)).andReturn(result);
})
.run(unit -> {
MongoSessionStore mss = new MongoSessionStore(unit.get(MongoDatabase.class), "sess",
"60");
assertEquals(unit.get(Session.class), mss.get(unit.get(Session.Builder.class)));
}, unit -> {
BiConsumer<String, String> setter = unit.captured(BiConsumer.class).get(0);
setter.accept("a\uFF0Eb", "c");
});
}

@SuppressWarnings({"unchecked", "rawtypes"})
@Test
public void getDollar() throws Exception {
long now = System.currentTimeMillis();
new MockUnit(Session.class, Session.Builder.class, MongoDatabase.class, MongoCollection.class,
DBObject.class)
.expect(boot)
.expect(unit -> {
Document doc = unit.mock(Document.class);

Map sessionMap = unit.constructor(LinkedHashMap.class)
.args(Map.class)
.build(doc);
expect(sessionMap.remove("_accessedAt")).andReturn(new Date(now));
expect(sessionMap.remove("_createdAt")).andReturn(new Date(now));
expect(sessionMap.remove("_savedAt")).andReturn(new Date(now));
expect(sessionMap.remove("_id")).andReturn("1234");
sessionMap.forEach(unit.capture(BiConsumer.class));

FindIterable result = unit.mock(FindIterable.class);
expect(result.first()).andReturn(doc);

Session.Builder sb = unit.get(Session.Builder.class);
expect(sb.sessionId()).andReturn("1234");
expect(sb.accessedAt(now)).andReturn(sb);
expect(sb.createdAt(now)).andReturn(sb);
expect(sb.savedAt(now)).andReturn(sb);
expect(sb.set("$ab", "c")).andReturn(sb);
expect(sb.build()).andReturn(unit.get(Session.class));

Bson eq = unit.mock(Bson.class);
unit.mockStatic(Filters.class);
expect(Filters.eq("_id", "1234")).andReturn(eq);

MongoCollection collection = unit.get(MongoCollection.class);
expect(collection.find(eq)).andReturn(result);
})
.run(unit -> {
MongoSessionStore mss = new MongoSessionStore(unit.get(MongoDatabase.class), "sess",
"60");
assertEquals(unit.get(Session.class), mss.get(unit.get(Session.Builder.class)));
}, unit -> {
BiConsumer<String, String> setter = unit.captured(BiConsumer.class).get(0);
setter.accept("\uFF04ab", "c");
});
}

@SuppressWarnings("rawtypes")
Expand Down