All Downloads are FREE. Search and download functionalities are using the official Maven repository.

io.vertx.tp.plugin.redis.RedisStore Maven / Gradle / Ivy

The newest version!
package io.vertx.tp.plugin.redis;

import io.vertx.core.*;
import io.vertx.core.buffer.Buffer;
import io.vertx.core.json.JsonObject;
import io.vertx.core.shareddata.LocalMap;
import io.vertx.ext.auth.PRNG;
import io.vertx.ext.web.Session;
import io.vertx.ext.web.sstore.SessionStore;
import io.vertx.redis.client.*;
import io.vertx.tp.error._409SessionVersionException;
import io.horizon.exception.WebException;
import io.horizon.uca.log.Annal;
import io.vertx.up.util.Ut;

import java.util.*;

public class RedisStore implements SessionStore {

    private static final Annal LOGGER = Annal.get(RedisStore.class);
    private static final String MAP_NAME = "vertx-web.session";
    private static transient PRNG random;
    /* Local Map */
    private final transient List sessionIds = new Vector<>();
    /* Client reference */
    private transient Redis client;
    private transient LocalMap localMap;

    /* Configuration of additional */
    private transient RedisExtra extra;

    @Override
    public SessionStore init(final Vertx vertx, final JsonObject options) {
        /* RedisClient options here */
        random = new PRNG(vertx);
        this.localMap = vertx.sharedData().getLocalMap(MAP_NAME);

        /* Redis options */
        final RedisOptions opts = new RedisOptions(options);
        LOGGER.info(RedisMsg.RD_OPTS,
            /* Endpoint information for current */
            opts.getEndpoint(),
            opts.toJson().encode());

        /* Client init, old version */
        this.client = Redis.createClient(vertx, opts);

        /* Extra configuration options */
        this.extra = Ut.deserialize(options, RedisExtra.class);
        return this;
    }

    @Override
    public long retryTimeout() {
        /* Get retry timeout field */
        return this.extra.getRetryTimeout();
    }

    @Override
    public Session createSession(final long timeout) {
        return new RedisSession(random, timeout, DEFAULT_SESSIONID_LENGTH);
    }

    @Override
    public Session createSession(final long timeout, final int length) {
        return new RedisSession(random, timeout, length);
    }

    private RedisSession createSession() {
        return new RedisSession(random, this.extra.getTimeout(), DEFAULT_SESSIONID_LENGTH);
    }

    @Override
    public SessionStore clear(final Handler> handler) {
        handler.handle(this.clear());
        return this;
    }

    @Override
    public Future clear() {
        /*
         * To avoid: java.util.ConcurrentModificationException
         */
        final Promise promise = Promise.promise();
        final Set idSet = new HashSet<>(this.sessionIds);
        idSet.forEach(sessionId -> this.client.send(Request.cmd(Command.DEL), res -> {
            if (res.succeeded()) {
                /*
                 * This sessionId should be removed
                 */
                this.sessionIds.remove(sessionId);
                promise.complete();
            }
        }));
        return promise.future();
    }

    @Override
    public SessionStore size(final Handler> handler) {
        /*
         * Session IDs
         */
        handler.handle(this.size());
        return this;
    }

    @Override
    public Future size() {
        return Future.succeededFuture(this.sessionIds.size());
    }

    @Override
    public void close() {
        /*
         * Close Handler
         */
        this.client.close();
    }

    @Override
    public SessionStore delete(final String id, final Handler> handler) {
        handler.handle(this.delete(id));
        return this;
    }

    @Override
    public Future delete(final String id) {
        final Promise promise = Promise.promise();
        LOGGER.debug(RedisMsg.RS_MESSAGE, id, "delete(String)");
        this.client.send(Request.cmd(Command.DEL), res -> {
            if (res.succeeded()) {
                /*
                 * Synced SessionIds
                 */
                this.sessionIds.remove(id);

                LOGGER.info(RedisMsg.RD_CLEAR, id);
                promise.complete();
            } else {
                /*
                 * ERROR throw out
                 */
                res.cause().printStackTrace();
                promise.fail(res.cause());
            }
        });
        return promise.future();
    }

    @Override
    public SessionStore get(final String id, final Handler> handler) {
        handler.handle(this.get(id));
        return this;
    }

    @Override
    public Future get(final String id) {
        LOGGER.debug(RedisMsg.RS_MESSAGE, id, "get(String)");
        final Request request = Request.cmd(Command.GETBIT);
        request.arg(id);
        final Promise promise = Promise.promise();
        this.client.send(request, res -> {
            /*
             * Whether get buffer data after read data from redis.
             * If successful continue to process buffer data
             */
            if (res.succeeded()) {
                final Response response = res.result();
                final Buffer buffer = response.toBuffer();
                if (Objects.nonNull(buffer)) {
                    final RedisSession session = this.createSession();
                    if (0 < buffer.length()) {
                        session.readFromBuffer(0, buffer);
                    }
                    promise.complete(session);
                    // handler.handle(Future.succeededFuture(session));
                } else {
                    /*
                     * LocalMap of vertx-web.sessions
                     */
                    promise.complete(this.localMap.get(id));
                    // handler.handle(Future.succeededFuture(this.localMap.get(id)));
                }
            } else {
                /*
                 * ERROR throw out
                 */
                res.cause().printStackTrace();
                promise.fail(res.cause());
                // handler.handle(Future.failedFuture(res.cause()));
            }
        });
        return promise.future();
    }

    @Override
    public SessionStore put(final Session session, final Handler> handler) {
        handler.handle(this.put(session));
        return this;
    }

    @Override
    @SuppressWarnings("all")
    public Future put(final Session session) {
        final String id = session.id();
        LOGGER.debug(RedisMsg.RS_MESSAGE, id, "put(Session)");
        final Request request = Request.cmd(Command.SET);
        request.arg(id);
        final Promise promise = Promise.promise();
        client.send(request, res -> {
            if (res.succeeded()) {
                final Response response = res.result();
                final Buffer buffer = response.toBuffer();
                final RedisSession finalSession;
                final RedisSession oldSession = createSession();
                if (Objects.isNull(buffer)) {
                    /*
                     * Data null
                     */
                    RedisSession sessionImpl = (RedisSession) session;
                    finalSession = sessionImpl;
                } else {
                    /*
                     * Data Existing
                     */
                    final RedisSession newSession = (RedisSession) session;
                    oldSession.readFromBuffer(0, buffer);
                    /*
                     * Version matching here for comparing
                     */
                    if (oldSession.version() != newSession.version()) {
                        final WebException error = new _409SessionVersionException(getClass(),
                            oldSession.version(), newSession.version());
                        promise.fail(error);
                        // handler.handle(Future.failedFuture(error));
                        return;
                    }
                    finalSession = newSession;
                }
                finalSession.incrementVersion();
                /*
                 * Write session data
                 */
                writeSession(session, res2 -> {
                    if (res2.succeeded()) {
                        if (Objects.nonNull(res2.result())) {
                            final String added = res2.result();
                            if (!sessionIds.contains(added)) {
                                sessionIds.add(res2.result());
                            }
                            /*
                             * Append data into localMap to cache
                             */
                            localMap.put(added, session);
                            promise.complete();
                        }
                        LOGGER.info(RedisMsg.RS_AFTER, finalSession.id(),
                            null == oldSession ? null : oldSession.id());
                    } else {
                        /*
                         * ERROR throw out
                         */
                        res2.cause().printStackTrace();
                        promise.fail(res.cause());
                        // handler.handle(Future.failedFuture(res.cause()));
                    }
                });
            } else {
                /*
                 * ERROR throw out
                 */
                res.cause().printStackTrace();
                promise.fail(res.cause());
                // handler.handle(Future.failedFuture(res.cause()));
            }
        });
        return promise.future();
    }

    private void writeSession(final Session session, final Handler> handler) {
        /* Write buffer */
        final Buffer buffer = Buffer.buffer();
        final RedisSession sessionImpl = (RedisSession) session;
        sessionImpl.writeToBuffer(buffer);

        /* Synced timeout here */
        final Request request = Request.cmd(Command.SETBIT);
        final String key = sessionImpl.id();
        request.arg(key);
        request.arg(buffer);
        this.client.send(request, res -> {
            if (res.succeeded()) {
                LOGGER.info(RedisMsg.RD_KEYS, Ut.fromJoin(session.data().keySet()));
                /*
                 * Add data if this id does not exist
                 */
                handler.handle(Future.succeededFuture(key));
            } else {
                /*
                 * ERROR throw out
                 *
                 */
                res.cause().printStackTrace();
                handler.handle(Future.failedFuture(res.cause()));
            }
        });
        /*
        final SetOptions options = new SetOptions().setPX(session.timeout());
        final String key = sessionImpl.id();
        this.client.setBinaryWithOptions(key, buffer, options, res -> {
            if (res.succeeded()) {
                LOGGER.info(RedisMsg.RD_KEYS, Ut.fromJoin(session.data().keySet()));
                /*
                 * Add data if this id does not exist
                handler.handle(Future.succeededFuture(key));
            } else {
                /*
                 * ERROR throw out
                res.cause().printStackTrace();
                handler.handle(Future.failedFuture(res.cause()));
            }
        });*/
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy