diff --git a/redis/project/Build.scala b/redis/project/Build.scala index 79527b3..97ab363 100644 --- a/redis/project/Build.scala +++ b/redis/project/Build.scala @@ -9,9 +9,12 @@ object MinimalBuild extends Build { lazy val typesafe = "Typesafe Repository" at "http://repo.typesafe.com/typesafe/releases/" lazy val repo = if (buildVersion.endsWith("SNAPSHOT")) typesafeSnapshot else typesafe lazy val pk11 = "pk11 repo" at "http://pk11-scratch.googlecode.com/svn/trunk" + // for jedis-2.2.2-SNAPSHOT which sedis 1.2.0 depends on + lazy val sonatype = "sonatype repo" at "http://oss.sonatype.org/content/groups/public" lazy val root = Project(id = "play-plugins-redis", base = file("."), settings = Project.defaultSettings).settings( - version := "2.2.0", + version := "2.2.1-SNAPSHOT", scalaVersion := "2.10.2", + //publishTo := Some(Resolver.file("file", new File(Path.userHome.absolutePath+"/.m2/repository"))), publishTo <<= (version) { version: String => val nexus = "https://private-repo.typesafe.com/typesafe/" if (version.trim.endsWith("SNAPSHOT")) Some("snapshots" at nexus + "maven-snapshots/") @@ -20,10 +23,11 @@ object MinimalBuild extends Build { organization := "com.typesafe", resolvers += repo, resolvers += pk11, + resolvers += sonatype, javacOptions += "-Xlint:unchecked", libraryDependencies += "biz.source_code" % "base64coder" % "2010-12-19", libraryDependencies += "com.typesafe" %% "play-plugins-util" % buildVersion, libraryDependencies += "com.typesafe.play" %% "play-cache" % buildVersion % "provided", - libraryDependencies += "org.sedis" % "sedis_2.10.0" % "1.1.1" + libraryDependencies += "org.sedis" % "sedis_2.10.0" % "1.2.0" ) } diff --git a/redis/sample/app/Global.java b/redis/sample/app/Global.java index 7657bea..247cfe3 100644 --- a/redis/sample/app/Global.java +++ b/redis/sample/app/Global.java @@ -10,6 +10,8 @@ public class Global extends GlobalSettings { public void onStart(Application app) { JedisPool p = app.plugin(RedisPlugin.class).jedisPool(); + // uncomment to test sentinel setup + //JedisSentinelPool p = app.plugin(RedisPlugin.class).jedisSentinelPool(); Jedis j = p.getResource(); j.set("foo","yay"); p.returnResource(j); diff --git a/redis/sample/app/controllers/Application.java b/redis/sample/app/controllers/Application.java index c5b0418..f713ac3 100644 --- a/redis/sample/app/controllers/Application.java +++ b/redis/sample/app/controllers/Application.java @@ -13,6 +13,8 @@ public class Application extends Controller { public static Result index() { JedisPool p = Play.application().plugin(RedisPlugin.class).jedisPool(); + // uncomment to test sentinel setup + //JedisSentinelPool p = Play.application().plugin(RedisPlugin.class).jedisSentinelPool(); Jedis j = p.getResource(); String r = j.get("foo") + " - foo2:" + j.get("foo2"); p.returnResource(j); diff --git a/redis/sample/conf/application.conf b/redis/sample/conf/application.conf index 56c883b..7be625f 100644 --- a/redis/sample/conf/application.conf +++ b/redis/sample/conf/application.conf @@ -1,6 +1,12 @@ # This is the main configuration file for the application. # ~~~~~ +# enable to test sentinel setup +redis.sentinel.mode=false +redis.sentinel.hosts=["localhost:26379", "localhost:26380"] +redis.master.name=mymaster +#redis.key.prefix=AppName + # Secret key # ~~~~~ # The secret key is used to secure cryptographics functions. diff --git a/redis/sample/project/Build.scala b/redis/sample/project/Build.scala index e0cdaec..69afee5 100644 --- a/redis/sample/project/Build.scala +++ b/redis/sample/project/Build.scala @@ -7,7 +7,7 @@ object ApplicationBuild extends Build { val appVersion = "1.0-SNAPSHOT" val appDependencies = Seq( - "com.typesafe" %% "play-plugins-redis" % "2.2.0", + "com.typesafe" %% "play-plugins-redis" % "2.2.1-SNAPSHOT", "com.typesafe.play" %% "play-cache" % "2.2.0" ) diff --git a/redis/src/main/scala/com/typesafe/plugin/RedisPlugin.scala b/redis/src/main/scala/com/typesafe/plugin/RedisPlugin.scala index 9d9a718..aaf2c12 100644 --- a/redis/src/main/scala/com/typesafe/plugin/RedisPlugin.scala +++ b/redis/src/main/scala/com/typesafe/plugin/RedisPlugin.scala @@ -4,13 +4,12 @@ import play.api._ import org.sedis._ import redis.clients.jedis._ import play.api.cache._ -import java.util._ import java.io._ import java.net.URI import biz.source_code.base64Coder._ import org.apache.commons.lang3.builder._ -import org.apache.commons.pool.impl.GenericObjectPool import play.api.mvc.Result +import scala.collection.JavaConversions._ /** * provides a redis client and a CachePlugin implementation @@ -37,6 +36,17 @@ class RedisPlugin(app: Application) extends CachePlugin { private lazy val timeout = app.configuration.getInt("redis.timeout") .getOrElse(2000) + private lazy val sentinelMode = app.configuration.getBoolean("redis.sentinel.mode") + .getOrElse(false) + + private lazy val sentinelHosts : java.util.List[String] = app.configuration.getStringList("redis.sentinel.hosts") + .getOrElse(seqAsJavaList(List("localhost:26379"))) + + private lazy val masterName = app.configuration.getString("redis.master.name") + .getOrElse("mymaster") + + private lazy val keyPrefix = app.configuration.getString("redis.key.prefix") + .getOrElse("") /** * provides access to the underlying jedis Pool @@ -53,36 +63,54 @@ class RedisPlugin(app: Application) extends CachePlugin { */ lazy val sedisPool = new Pool(jedisPool) + /** + * provides access to the underlying jedis sentinel Pool + */ + lazy val jedisSentinelPool = { + val poolConfig = createPoolConfig(app) + Logger.info(s"Redis Plugin enabled. Connecting to Redis sentinels ${sentinelHosts} with timeout ${timeout}.") + Logger.info("Redis Plugin pool configuration: " + new ReflectionToStringBuilder(poolConfig).toString()) + val sentinelSet = new java.util.HashSet[String]() + sentinelSet.addAll(sentinelHosts) + new JedisSentinelPool(masterName, sentinelSet, poolConfig, timeout, password) + } + + /** + * provides access to the sedis sentinel Pool + */ + lazy val sedisSentinelPool = new SentinelPool(jedisSentinelPool) + private def createPoolConfig(app: Application) : JedisPoolConfig = { val poolConfig : JedisPoolConfig = new JedisPoolConfig() - app.configuration.getInt("redis.pool.maxIdle").map { poolConfig.maxIdle = _ } - app.configuration.getInt("redis.pool.minIdle").map { poolConfig.minIdle = _ } - app.configuration.getInt("redis.pool.maxActive").map { poolConfig.maxActive = _ } - app.configuration.getInt("redis.pool.maxWait").map { poolConfig.maxWait = _ } - app.configuration.getBoolean("redis.pool.testOnBorrow").map { poolConfig.testOnBorrow = _ } - app.configuration.getBoolean("redis.pool.testOnReturn").map { poolConfig.testOnReturn = _ } - app.configuration.getBoolean("redis.pool.testWhileIdle").map { poolConfig.testWhileIdle = _ } - app.configuration.getLong("redis.pool.timeBetweenEvictionRunsMillis").map { poolConfig.timeBetweenEvictionRunsMillis = _ } - app.configuration.getInt("redis.pool.numTestsPerEvictionRun").map { poolConfig.numTestsPerEvictionRun = _ } - app.configuration.getLong("redis.pool.minEvictableIdleTimeMillis").map { poolConfig.minEvictableIdleTimeMillis = _ } - app.configuration.getLong("redis.pool.softMinEvictableIdleTimeMillis").map { poolConfig.softMinEvictableIdleTimeMillis = _ } - app.configuration.getBoolean("redis.pool.lifo").map { poolConfig.lifo = _ } - app.configuration.getString("redis.pool.whenExhaustedAction").map { setting => - poolConfig.whenExhaustedAction = setting match { - case "fail" | "0" => GenericObjectPool.WHEN_EXHAUSTED_FAIL - case "block" | "1" => GenericObjectPool.WHEN_EXHAUSTED_BLOCK - case "grow" | "2" => GenericObjectPool.WHEN_EXHAUSTED_FAIL - } - } + app.configuration.getInt("redis.pool.maxIdle").map { poolConfig.setMaxIdle(_) } + app.configuration.getInt("redis.pool.minIdle").map { poolConfig.setMinIdle(_) } + app.configuration.getInt("redis.pool.maxTotal").map { poolConfig.setMaxTotal(_) } + app.configuration.getBoolean("redis.pool.testOnBorrow").map { poolConfig.setTestOnBorrow(_) } + app.configuration.getBoolean("redis.pool.testOnReturn").map { poolConfig.setTestOnReturn(_) } + app.configuration.getBoolean("redis.pool.testWhileIdle").map { poolConfig.setTestWhileIdle(_) } + app.configuration.getLong("redis.pool.timeBetweenEvictionRunsMillis").map { poolConfig.setTimeBetweenEvictionRunsMillis(_) } + app.configuration.getInt("redis.pool.numTestsPerEvictionRun").map { poolConfig.setNumTestsPerEvictionRun(_) } + app.configuration.getLong("redis.pool.minEvictableIdleTimeMillis").map { poolConfig.setMinEvictableIdleTimeMillis(_) } + app.configuration.getLong("redis.pool.softMinEvictableIdleTimeMillis").map { poolConfig.setSoftMinEvictableIdleTimeMillis(_) } + app.configuration.getBoolean("redis.pool.lifo").map { poolConfig.setLifo(_) } + app.configuration.getBoolean("redis.pool.blockWhenExhausted").map { poolConfig.setBlockWhenExhausted(_) } poolConfig } override def onStart() { - sedisPool + if (sentinelMode) { + sedisSentinelPool + } else { + sedisPool + } } override def onStop() { - jedisPool.destroy() + if (sentinelMode) { + jedisSentinelPool.destroy() + } else { + jedisPool.destroy() + } } override lazy val enabled = { @@ -97,6 +125,11 @@ class RedisPlugin(app: Application) extends CachePlugin { lazy val api = new CacheAPI { def set(key: String, value: Any, expiration: Int) { + if (value == null) { + Logger.warn("not setting key:"+ key + " because value is null") + return + } + var oos: ObjectOutputStream = null var dos: DataOutputStream = null try { @@ -132,10 +165,11 @@ class RedisPlugin(app: Application) extends CachePlugin { } val redisV = prefix + "-" + new String( Base64Coder.encode( baos.toByteArray() ) ) Logger.trace(s"Setting key ${key} to ${redisV}") - - sedisPool.withJedisClient { client => - client.set(key,redisV) - if (expiration != 0) client.expire(key,expiration) + + if (sentinelMode) { + sedisSentinelPool.withJedisClient { client => setValue(client, key, redisV, expiration) } + } else { + sedisPool.withJedisClient { client => setValue(client, key, redisV, expiration) } } } catch {case ex: IOException => Logger.warn("could not serialize key:"+ key + " and value:"+ value.toString + " ex:"+ex.toString) @@ -145,7 +179,23 @@ class RedisPlugin(app: Application) extends CachePlugin { } } - def remove(key: String): Unit = sedisPool.withJedisClient { client => client.del(key) } + + private def getFullKey(key: String): String = { + if (keyPrefix.length > 0) keyPrefix + ":" + key else key + } + + private def setValue(client: Jedis, key: String, value: String, expiration: Int) { + client.set(getFullKey(key), value) + if (expiration != 0) client.expire(getFullKey(key), expiration) + } + + def remove(key: String): Unit = { + if (sentinelMode) { + sedisSentinelPool.withJedisClient { client => client.del(getFullKey(key)) } + } else { + sedisPool.withJedisClient { client => client.del(getFullKey(key)) } + } + } class ClassLoaderObjectInputStream(stream:InputStream) extends ObjectInputStream(stream) { override protected def resolveClass(desc: ObjectStreamClass) = { @@ -159,7 +209,13 @@ class RedisPlugin(app: Application) extends CachePlugin { var ois: ObjectInputStream = null var dis: DataInputStream = null try { - val rawData = sedisPool.withJedisClient { client => client.get(key) } + val rawData = { + if (sentinelMode) { + sedisSentinelPool.withJedisClient { client => client.get(getFullKey(key)) } + } else { + sedisPool.withJedisClient { client => client.get(getFullKey(key)) } + } + } rawData match { case null => None