/** * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you 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 org.jooby.jedis; import static java.util.Objects.requireNonNull; import java.net.URI; import java.util.concurrent.TimeUnit; import javax.inject.Provider; import org.apache.commons.pool2.impl.GenericObjectPoolConfig; import org.jooby.Env; import org.jooby.Env.ServiceKey; import org.jooby.Jooby; import com.google.inject.Binder; import com.typesafe.config.Config; import com.typesafe.config.ConfigFactory; import redis.clients.jedis.Jedis; import redis.clients.jedis.JedisPool; /** * Redis cache and key/value data store for Jooby. Exposes a {@link Jedis} service. * * <h1>usage</h1> * <p> * It is pretty straightforward: * </p> * * <pre> * # define a database URI * db = "redis://localhost:6379" * </pre> * * <pre> * { * use(new Redis()); * * get("/:key/:value", req {@literal ->} { * try (Jedis jedis = req.require(Jedis.class)) { * jedis.set(req.param("key").value(), req.param("value").value()); * return jedis.get(req.param("key").value()); * } * }); * } * </pre> * * <h1>pool configuration</h1> * <p> * This module creates a {@link JedisPool}. A default pool is created with a max of <code>128</code> * instances. * </p> * * <p> * The pool can be customized from your <code>application.conf</code>: * </p> * * <pre> * db = "redis://localhost:6379" * * # increase pool size to 200 * jedis.pool.maxTotal = 200 * </pre> * * <h2>two or more redis connections</h2> * <p> * In case you need two or more Redis connection, just do: * </p> * * <pre> * { * use(new Redis()); // default is "db" * use(new Redis("db1")); * * get("/:key/:value", req {@literal ->} { * try (Jedis jedis = req.require("db1", Jedis.class)) { * jedis.set(req.param("key").value(), req.param("value").value()); * return jedis.get(req.param("key").value()); * } * }); * } * </pre> * * application.conf: * * <pre> * db = "redis://localhost:6379/0" * * db1 = "redis://localhost:6379/1" * </pre> * * Pool configuration for <code>db1</code> is inherited from <code>jedis.pool</code>. If you need * to tweak the pool configuration for <code>db1</code> just do: * * <pre> * db1 = "redis://localhost:6379/1" * * # ONLY 10 for db1 * jedis.db1.maxTotal = 10 * </pre> * * <p> * For more information about <a href="https://github.com/xetorthio/jedis">Jedis</a> checkout the <a * href="https://github.com/xetorthio/jedis/wiki">wiki</a> * </p> * * <p> * That's all folks! Enjoy it! * </p> * * TBD: Object mapping? https://github.com/xetorthio/johm? * * @author edgar * @since 0.5.0 */ public class Redis implements Jooby.Module { /** * Database name. */ private String name; /** * <p> * Creates a new {@link Redis} instance and connect to the provided database. Please note, the * name is a property in your <code>application.conf</code> file with Redis URI. * </p> * * <pre> * { * use(new Redis("db1")); * } * </pre> * * application.conf * * <pre> * db1 = ""redis://localhost:6379"" * </pre> * * Default database name is: <code>db</code> * * @param name A database name. */ public Redis(final String name) { this.name = requireNonNull(name, "A db property is required."); } /** * A new {@link Redis} instance, with a default database name of: <code>db</code>. */ public Redis() { this("db"); } @Override public void configure(final Env env, final Config config, final Binder binder) { /** * Pool */ GenericObjectPoolConfig poolConfig = poolConfig(config, name); int timeout = (int) config.getDuration("jedis.timeout", TimeUnit.MILLISECONDS); URI uri = URI.create(config.getString(name)); JedisPool pool = new JedisPool(poolConfig, uri, timeout); RedisProvider provider = new RedisProvider(pool, uri, poolConfig); env.onStart(provider::start); env.onStop(provider::stop); Provider<Jedis> jedis = (Provider<Jedis>) () -> pool.getResource(); ServiceKey serviceKey = env.serviceKey(); serviceKey.generate(JedisPool.class, name, k -> binder.bind(k).toInstance(pool)); serviceKey.generate(Jedis.class, name, k -> binder.bind(k).toProvider(jedis)); } private GenericObjectPoolConfig poolConfig(final Config config, final String name) { Config poolConfig = config.getConfig("jedis.pool"); String override = "jedis." + name; if (config.hasPath(override)) { poolConfig = config.getConfig(override).withFallback(poolConfig); } return poolConfig(poolConfig); } private GenericObjectPoolConfig poolConfig(final Config config) { GenericObjectPoolConfig poolConfig = new GenericObjectPoolConfig(); poolConfig.setBlockWhenExhausted(config.getBoolean("blockWhenExhausted")); poolConfig.setEvictionPolicyClassName(config.getString("evictionPolicyClassName")); poolConfig.setJmxEnabled(config.getBoolean("jmxEnabled")); poolConfig.setJmxNamePrefix(config.getString("jmxNamePrefix")); poolConfig.setLifo(config.getBoolean("lifo")); poolConfig.setMaxIdle(config.getInt("maxIdle")); poolConfig.setMaxTotal(config.getInt("maxTotal")); poolConfig.setMaxWaitMillis(config.getDuration("maxWait", TimeUnit.MILLISECONDS)); poolConfig.setMinEvictableIdleTimeMillis(config.getDuration("minEvictableIdle", TimeUnit.MILLISECONDS)); poolConfig.setMinIdle(config.getInt("minIdle")); poolConfig.setNumTestsPerEvictionRun(config.getInt("numTestsPerEvictionRun")); poolConfig.setSoftMinEvictableIdleTimeMillis( config.getDuration("softMinEvictableIdle", TimeUnit.MILLISECONDS)); poolConfig.setTestOnBorrow(config.getBoolean("testOnBorrow")); poolConfig.setTestOnReturn(config.getBoolean("testOnReturn")); poolConfig.setTestWhileIdle(config.getBoolean("testWhileIdle")); poolConfig.setTimeBetweenEvictionRunsMillis(config.getDuration("timeBetweenEvictionRuns", TimeUnit.MILLISECONDS)); return poolConfig; } @Override public Config config() { return ConfigFactory.parseResources(getClass(), "jedis.conf"); } }