2121import static java .util .Objects .requireNonNull ;
2222
2323import java .util .Date ;
24+ import java .util .LinkedHashMap ;
2425import java .util .Map ;
2526import java .util .Optional ;
2627import java .util .concurrent .TimeUnit ;
2930import javax .inject .Inject ;
3031import javax .inject .Named ;
3132
33+ import org .bson .Document ;
34+ import org .bson .conversions .Bson ;
3235import org .jooby .Session ;
3336import org .jooby .Session .Builder ;
3437import org .slf4j .Logger ;
3538import 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 ;
4246import com .typesafe .config .Config ;
4347import com .typesafe .config .ConfigFactory ;
4448import com .typesafe .config .ConfigValueFactory ;
104108 */
105109public 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}
0 commit comments