Skip to content

Commit 4579884

Browse files
authored
Merge pull request #591 from jooby-project/590
mongodb session store doesn't work fix #590
2 parents edb049c + b0f0547 commit 4579884

File tree

4 files changed

+301
-148
lines changed

4 files changed

+301
-148
lines changed

jooby-mongodb/pom.xml

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -85,6 +85,20 @@
8585
<scope>test</scope>
8686
</dependency>
8787

88+
<dependency>
89+
<groupId>org.jooby</groupId>
90+
<artifactId>jooby-netty</artifactId>
91+
<version>${project.version}</version>
92+
<scope>test</scope>
93+
</dependency>
94+
95+
<dependency>
96+
<groupId>org.jooby</groupId>
97+
<artifactId>jooby-jackson</artifactId>
98+
<version>${project.version}</version>
99+
<scope>test</scope>
100+
</dependency>
101+
88102
</dependencies>
89103

90104
</project>

jooby-mongodb/src/main/java/org/jooby/mongodb/MongoSessionStore.java

Lines changed: 73 additions & 54 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
import static java.util.Objects.requireNonNull;
2222

2323
import java.util.Date;
24+
import java.util.LinkedHashMap;
2425
import java.util.Map;
2526
import java.util.Optional;
2627
import java.util.concurrent.TimeUnit;
@@ -29,16 +30,19 @@
2930
import javax.inject.Inject;
3031
import javax.inject.Named;
3132

33+
import org.bson.Document;
34+
import org.bson.conversions.Bson;
3235
import org.jooby.Session;
3336
import org.jooby.Session.Builder;
3437
import org.slf4j.Logger;
3538
import org.slf4j.LoggerFactory;
3639

37-
import com.mongodb.BasicDBObject;
38-
import com.mongodb.BasicDBObjectBuilder;
39-
import com.mongodb.DB;
40-
import com.mongodb.DBCollection;
41-
import com.mongodb.MongoException;
40+
import com.mongodb.client.MongoCollection;
41+
import com.mongodb.client.MongoCursor;
42+
import com.mongodb.client.MongoDatabase;
43+
import com.mongodb.client.model.Filters;
44+
import com.mongodb.client.model.IndexOptions;
45+
import com.mongodb.client.model.UpdateOptions;
4246
import com.typesafe.config.Config;
4347
import com.typesafe.config.ConfigFactory;
4448
import com.typesafe.config.ConfigValueFactory;
@@ -104,28 +108,31 @@
104108
*/
105109
public class MongoSessionStore implements Session.Store {
106110

111+
private static final String SESSION_IDX = "_sessionIdx_";
112+
107113
/** The logging system. */
108114
private final Logger log = LoggerFactory.getLogger(getClass());
109115

110-
protected final DBCollection sessions;
116+
protected final MongoCollection<Document> sessions;
111117

112-
protected final int timeout;
118+
protected final long timeout;
113119

114120
protected final String collection;
115121

116122
private final AtomicBoolean ttlSync = new AtomicBoolean(false);
117123

118-
protected final DB db;
124+
protected final MongoDatabase db;
119125

120-
public MongoSessionStore(final DB db, final String collection, final int timeout) {
126+
public MongoSessionStore(final MongoDatabase db, final String collection,
127+
final long timeoutInSeconds) {
121128
this.db = requireNonNull(db, "Mongo db is required.");
122129
this.collection = requireNonNull(collection, "Collection is required.");
123130
this.sessions = db.getCollection(collection);
124-
this.timeout = timeout;
131+
this.timeout = timeoutInSeconds;
125132
}
126133

127134
@Inject
128-
public MongoSessionStore(final DB db,
135+
public MongoSessionStore(final MongoDatabase db,
129136
final @Named("mongodb.session.collection") String collection,
130137
final @Named("session.timeout") String timeout) {
131138
this(db, collection, seconds(timeout));
@@ -134,36 +141,40 @@ public MongoSessionStore(final DB db,
134141
@SuppressWarnings({"unchecked", "rawtypes" })
135142
@Override
136143
public Session get(final Builder builder) {
137-
return Optional.ofNullable(sessions.findOne(builder.sessionId())).map(dbobj -> {
138-
Map session = dbobj.toMap();
139-
140-
Date accessedAt = (Date) session.remove("_accessedAt");
141-
Date createdAt = (Date) session.remove("_createdAt");
142-
Date savedAt = (Date) session.remove("_savedAt");
143-
session.remove("_id");
144-
145-
return builder
146-
.accessedAt(accessedAt.getTime())
147-
.createdAt(createdAt.getTime())
148-
.savedAt(savedAt.getTime())
149-
.set(session)
150-
.build();
151-
}).orElse(null);
144+
return Optional.ofNullable(sessions.find(Filters.eq("_id", builder.sessionId())).first())
145+
.map(doc -> {
146+
Map session = new LinkedHashMap<>(doc);
147+
148+
Date accessedAt = (Date) session.remove("_accessedAt");
149+
Date createdAt = (Date) session.remove("_createdAt");
150+
Date savedAt = (Date) session.remove("_savedAt");
151+
session.remove("_id");
152+
153+
return builder
154+
.accessedAt(accessedAt.getTime())
155+
.createdAt(createdAt.getTime())
156+
.savedAt(savedAt.getTime())
157+
.set(session)
158+
.build();
159+
}).orElse(null);
152160
}
153161

154162
@Override
155163
public void save(final Session session) {
156164
syncTtl();
157165

158-
BasicDBObjectBuilder ob = BasicDBObjectBuilder.start()
159-
.add("_id", session.id())
160-
.add("_accessedAt", new Date(session.accessedAt()))
161-
.add("_createdAt", new Date(session.createdAt()))
162-
.add("_savedAt", new Date(session.savedAt()));
166+
String id = session.id();
167+
Bson filter = Filters.eq("_id", id);
168+
169+
Document doc = new Document()
170+
.append("_id", id)
171+
.append("_accessedAt", new Date(session.accessedAt()))
172+
.append("_createdAt", new Date(session.createdAt()))
173+
.append("_savedAt", new Date(session.savedAt()));
163174
// dump attributes
164-
session.attributes().forEach((k, v) -> ob.add(k, v));
175+
session.attributes().forEach((k, v) -> doc.append(k, v));
165176

166-
sessions.save(ob.get());
177+
sessions.updateOne(filter, new Document("$set", doc), new UpdateOptions().upsert(true));
167178
}
168179

169180
@Override
@@ -179,41 +190,49 @@ private void syncTtl() {
179190
return;
180191
}
181192

182-
try {
183-
log.debug("creating session timeout index");
193+
log.debug("creating session timeout index");
194+
if (existsIdx(SESSION_IDX)) {
195+
Document command = new Document("collMod", collection)
196+
.append("index",
197+
new Document("keyPattern", new Document("_accessedAt", 1))
198+
.append("expireAfterSeconds", timeout));
199+
log.debug("{}", command);
200+
Document result = db.runCommand(command);
201+
log.debug("{}", result);
202+
} else {
184203
sessions.createIndex(
185-
new BasicDBObject("_accessedAt", 1),
186-
new BasicDBObject("expireAfterSeconds", timeout)
187-
);
188-
} catch (MongoException ex) {
189-
log.debug("Couldn't update session timeout, we are going to update session timeout", ex);
190-
// TODO: allow to customize? ... not sure
191-
db.command(BasicDBObjectBuilder.start()
192-
.add("collMod", collection)
193-
.add("index", BasicDBObjectBuilder.start()
194-
.add("keyPattern", new BasicDBObject("_accessedAt", 1))
195-
.add("expireAfterSeconds", timeout)
196-
.get()
197-
)
198-
.get()
199-
);
204+
new Document("_accessedAt", 1),
205+
new IndexOptions()
206+
.name(SESSION_IDX)
207+
.expireAfter(timeout, TimeUnit.SECONDS));
200208
}
201209
}
202210
}
203211

204212
@Override
205213
public void delete(final String id) {
206-
sessions.remove(new BasicDBObject("_id", id));
214+
sessions.deleteOne(new Document("_id", id));
207215
}
208216

209-
private static int seconds(final String value) {
217+
private static long seconds(final String value) {
210218
try {
211-
return Integer.parseInt(value);
219+
return Long.parseLong(value);
212220
} catch (NumberFormatException ex) {
213221
Config config = ConfigFactory.empty()
214222
.withValue("timeout", ConfigValueFactory.fromAnyRef(value));
215-
return (int) config.getDuration("timeout", TimeUnit.SECONDS);
223+
return config.getDuration("timeout", TimeUnit.SECONDS);
224+
}
225+
}
226+
227+
private boolean existsIdx(final String name) {
228+
MongoCursor<Document> iterator = sessions.listIndexes().iterator();
229+
while (iterator.hasNext()) {
230+
Document doc = iterator.next();
231+
if (doc.getString("name").equals(name)) {
232+
return true;
233+
}
216234
}
235+
return false;
217236
}
218237

219238
}
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
package apps;
2+
3+
import java.util.concurrent.atomic.AtomicInteger;
4+
5+
import org.jooby.Jooby;
6+
import org.jooby.Session;
7+
import org.jooby.mongodb.MongoSessionStore;
8+
import org.jooby.mongodb.Mongodb;
9+
10+
import com.typesafe.config.ConfigFactory;
11+
import com.typesafe.config.ConfigValueFactory;
12+
13+
public class MongodbApp extends Jooby {
14+
15+
{
16+
use(ConfigFactory.empty()
17+
.withValue("db", ConfigValueFactory.fromAnyRef("mongodb://localhost/mongodbapp"))
18+
.withValue("session.timeout", ConfigValueFactory.fromAnyRef("2m")));
19+
20+
use(new Mongodb());
21+
22+
AtomicInteger inc = new AtomicInteger(0);
23+
session(MongoSessionStore.class);
24+
25+
get("/", req -> {
26+
Session session = req.ifSession().orElseGet(() -> {
27+
Session newSession = req.session();
28+
int next = newSession.get("inc").intValue(inc.getAndIncrement());
29+
newSession.set("inc", next);
30+
return newSession;
31+
});
32+
return session.get("inc");
33+
});
34+
35+
}
36+
37+
public static void main(final String[] args) {
38+
run(MongodbApp::new, args);
39+
}
40+
}

0 commit comments

Comments
 (0)