/* * Copyright 2015 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "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 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package ratpack.session.store.internal; import com.google.inject.Inject; import com.lambdaworks.redis.RedisURI; import com.lambdaworks.redis.api.async.RedisAsyncCommands; import io.netty.buffer.ByteBuf; import io.netty.buffer.Unpooled; import io.netty.util.AsciiString; import ratpack.exec.Execution; import ratpack.exec.Operation; import ratpack.exec.Promise; import ratpack.session.SessionStore; import ratpack.session.store.RedisSessionModule; public class RedisSessionStore implements SessionStore { private final RedisSessionModule.Config config; private TimerExposingRedisClient redisClient; private RedisAsyncCommands<AsciiString, ByteBuf> connection; @Inject public RedisSessionStore(RedisSessionModule.Config config) { this.config = config; } @Override public Operation store(AsciiString sessionId, ByteBuf sessionData) { return Promise.<Boolean>async(d -> connection.set(sessionId, sessionData).handleAsync((value, failure) -> { if (failure == null) { if (value != null && value.equalsIgnoreCase("OK")) { d.success(true); } else { d.error(new RuntimeException("Failed to set session data")); } } else { d.error(new RuntimeException("Failed to set session data.", failure)); } return null; }, Execution.current().getEventLoop()) ).operation(); } @Override public Promise<ByteBuf> load(AsciiString sessionId) { return Promise.<ByteBuf>async(downstream -> { downstream.accept(connection.get(sessionId)); }).map(byteBuf -> { if (byteBuf == null) { //Must return an empty buffer never null return Unpooled.EMPTY_BUFFER; } return byteBuf; }); } @Override public Operation remove(AsciiString sessionId) { return Promise.<Long>async(d -> d.accept(connection.del(sessionId))).operation(); } @Override public Promise<Long> size() { return Promise.value(-1L); } @Override public String getName() { return "Redis Session Store Service"; } @Override public void onStart(@SuppressWarnings("deprecation") ratpack.server.StartEvent event) throws Exception { redisClient = new TimerExposingRedisClient(getRedisURI()); connection = redisClient.connect(new AsciiStringByteBufRedisCodec()).async(); } @Override public void onStop(@SuppressWarnings("deprecation") ratpack.server.StopEvent event) throws Exception { if (redisClient != null) { try { redisClient.getTimer().stop(); redisClient.shutdown(); } finally { redisClient = null; } } } private RedisURI getRedisURI() { RedisURI.Builder builder = RedisURI.Builder.redis(config.getHost()); if (config.getPassword() != null) { builder.withPassword(config.getPassword()); } if (config.getPort() != null) { builder.withPort(config.getPort()); } return builder.build(); } }