package io.vertx.ext.web.sstore.impl;
import io.vertx.core.AsyncResult;
import io.vertx.core.Future;
import io.vertx.core.Handler;
import io.vertx.core.Vertx;
import io.vertx.core.json.JsonObject;
import io.vertx.core.shareddata.AsyncMap;
import io.vertx.ext.auth.VertxContextPRNG;
import io.vertx.ext.web.Session;
import io.vertx.ext.web.sstore.AbstractSession;
import io.vertx.ext.web.sstore.ClusteredSessionStore;
import io.vertx.ext.web.sstore.SessionStore;
public class ClusteredSessionStoreImpl implements SessionStore, ClusteredSessionStore {
private static final String DEFAULT_SESSION_MAP_NAME = "vertx-web.sessions";
private static final long DEFAULT_RETRY_TIMEOUT = 5 * 1000;
private Vertx vertx;
private VertxContextPRNG random;
private String sessionMapName;
private long retryTimeout;
private volatile AsyncMap<String, Session> sessionMap;
@Override
public SessionStore init(Vertx vertx, JsonObject options) {
this.vertx = vertx;
this.sessionMapName = options.getString("mapName", DEFAULT_SESSION_MAP_NAME);
this.retryTimeout = options.getLong("retryTimeout", DEFAULT_RETRY_TIMEOUT);
this.random = VertxContextPRNG.current(vertx);
return this;
}
@Override
public long retryTimeout() {
return retryTimeout;
}
@Override
public Session createSession(long timeout) {
return new SharedDataSessionImpl(random, timeout, DEFAULT_SESSIONID_LENGTH);
}
@Override
public Session createSession(long timeout, int length) {
return new SharedDataSessionImpl(random, timeout, length);
}
@Override
public void get(String id, Handler<AsyncResult<Session>> resultHandler) {
getMap(res -> {
if (res.succeeded()) {
res.result().get(id, res2 -> {
if (res2.succeeded()) {
AbstractSession session = (AbstractSession) res2.result();
if (session != null) {
session.setPRNG(random);
}
resultHandler.handle(Future.succeededFuture(res2.result()));
} else {
resultHandler.handle(Future.failedFuture(res2.cause()));
}
});
} else {
resultHandler.handle(Future.failedFuture(res.cause()));
}
});
}
@Override
public void delete(String id, Handler<AsyncResult<Void>> resultHandler) {
getMap(res -> {
if (res.succeeded()) {
res.result().remove(id, res2 -> {
if (res2.succeeded()) {
resultHandler.handle(Future.succeededFuture());
} else {
resultHandler.handle(Future.failedFuture(res2.cause()));
}
});
} else {
resultHandler.handle(Future.failedFuture(res.cause()));
}
});
}
@Override
public void put(Session session, Handler<AsyncResult<Void>> resultHandler) {
getMap(res -> {
if (res.succeeded()) {
res.result().get(session.id(), old -> {
final AbstractSession oldSession;
final AbstractSession newSession = (AbstractSession) session;
if (old.succeeded()) {
oldSession = (AbstractSession) old.result();
} else {
oldSession = null;
}
if (oldSession != null) {
if (oldSession.version() != newSession.version()) {
resultHandler.handle(Future.failedFuture("Version mismatch"));
return;
}
}
newSession.incrementVersion();
res.result().put(session.id(), session, session.timeout(), res2 -> {
if (res2.succeeded()) {
resultHandler.handle(Future.succeededFuture());
} else {
resultHandler.handle(Future.failedFuture(res2.cause()));
}
});
});
} else {
resultHandler.handle(Future.failedFuture(res.cause()));
}
});
}
@Override
public void clear(Handler<AsyncResult<Void>> resultHandler) {
getMap(res -> {
if (res.succeeded()) {
res.result().clear(res2 -> {
if (res2.succeeded()) {
resultHandler.handle(Future.succeededFuture());
} else {
resultHandler.handle(Future.failedFuture(res2.cause()));
}
});
} else {
resultHandler.handle(Future.failedFuture(res.cause()));
}
});
}
@Override
public void size(Handler<AsyncResult<Integer>> resultHandler) {
getMap(res -> {
if (res.succeeded()) {
res.result().size(res2 -> {
if (res2.succeeded()) {
resultHandler.handle(Future.succeededFuture(res2.result()));
} else {
resultHandler.handle(Future.failedFuture(res2.cause()));
}
});
} else {
resultHandler.handle(Future.failedFuture(res.cause()));
}
});
}
@Override
public void close() {
}
private void getMap(Handler<AsyncResult<AsyncMap<String, Session>>> resultHandler) {
if (sessionMap == null) {
vertx.sharedData().<String, Session>getClusterWideMap(sessionMapName, res -> {
if (res.succeeded()) {
sessionMap = res.result();
resultHandler.handle(Future.succeededFuture(res.result()));
} else {
resultHandler.handle(res);
}
});
} else {
resultHandler.handle(Future.succeededFuture(sessionMap));
}
}
}