Skip to content

Commit

Permalink
Fix #32 Support custom classloader for Redis deserialization
Browse files Browse the repository at this point in the history
  • Loading branch information
cb372 committed Mar 9, 2015
1 parent 88aaec6 commit 1033b65
Show file tree
Hide file tree
Showing 3 changed files with 34 additions and 9 deletions.
9 changes: 7 additions & 2 deletions redis/src/main/scala/scalacache/redis/RedisCache.scala
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,10 @@ import scala.concurrent.{ Future, ExecutionContext, blocking }

/**
* Thin wrapper around Jedis
* @param customClassloader a classloader to use when deserializing objects from the cache.
* If you are using Play, you should pass in `app.classloader`.
*/
class RedisCache(client: Jedis)(implicit execContext: ExecutionContext = ExecutionContext.global)
class RedisCache(client: Jedis, override val customClassloader: Option[ClassLoader] = None)(implicit execContext: ExecutionContext = ExecutionContext.global)
extends Cache
with RedisSerialization
with LoggingSupport
Expand Down Expand Up @@ -79,8 +81,11 @@ object RedisCache {
/**
* Create a cache that uses the given Jedis client
* @param client a Jedis client
* @param customClassloader a classloader to use when deserializing objects from the cache.
* If you are using Play, you should pass in `app.classloader`.
*/
def apply(client: Jedis): RedisCache = new RedisCache(client)
def apply(client: Jedis, customClassloader: Option[ClassLoader] = None): RedisCache =
new RedisCache(client, customClassloader)

private val utf8 = Charset.forName("UTF-8")

Expand Down
15 changes: 14 additions & 1 deletion redis/src/main/scala/scalacache/redis/RedisSerialization.scala
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@ import java.io._
*/
trait RedisSerialization {

protected def customClassloader: Option[ClassLoader] = None

object MagicNumbers {
val STRING: Byte = 0
val BYTE_ARRAY: Byte = 1
Expand Down Expand Up @@ -42,7 +44,7 @@ trait RedisSerialization {
def deserialize[A](bytes: Array[Byte]): A = {
val bais = new ByteArrayInputStream(bytes)
val typeId = bais.read().toByte // Read the next byte to discover the type
val ois = new ObjectInputStream(bais) // The rest of the array is in ObjectInputStream format
val ois = createObjectInputStream(bais) // The rest of the array is in ObjectInputStream format
val result = typeId match {
case MagicNumbers.STRING => ois.readUTF()
case MagicNumbers.BYTE_ARRAY => {
Expand All @@ -59,4 +61,15 @@ trait RedisSerialization {
result.asInstanceOf[A]
}

private def createObjectInputStream(inputStream: InputStream): ObjectInputStream = customClassloader match {
case Some(classloader) => new ClassLoaderOIS(inputStream, classloader)
case None => new ObjectInputStream(inputStream)
}

}

class ClassLoaderOIS(stream: InputStream, customClassloader: ClassLoader) extends ObjectInputStream(stream) {
override protected def resolveClass(desc: ObjectStreamClass) = {
Class.forName(desc.getName, false, customClassloader)
}
}
19 changes: 13 additions & 6 deletions redis/src/test/scala/scalacache/redis/PlayIntegrationSpec.scala
Original file line number Diff line number Diff line change
@@ -1,12 +1,13 @@
package scalacache.redis

import org.scalatest.{ TestData, Matchers, FlatSpec }
import org.scalatestplus.play.{ OneAppPerTest, OneAppPerSuite }
import play.api.{ Application, GlobalSettings }
import _root_.redis.clients.jedis.Jedis
import org.scalatest.{ FlatSpec, Matchers, TestData }
import org.scalatestplus.play.OneAppPerTest
import play.api.test.FakeApplication
import play.api.{ Application, GlobalSettings }

import scalacache._
import memoization._
import scalacache.memoization._

class PlayIntegrationSpec extends FlatSpec with Matchers with OneAppPerTest {

Expand All @@ -29,10 +30,16 @@ class PlayIntegrationSpec extends FlatSpec with Matchers with OneAppPerTest {
case class Item(id: Int, name: String)

object Global extends GlobalSettings {
implicit var scalaCache: ScalaCache = _
@volatile implicit var jedis: Jedis = _
@volatile implicit var scalaCache: ScalaCache = _

override def onStart(app: Application): Unit = {
scalaCache = ScalaCache(RedisCache("localhost", 6379))
jedis = new Jedis("localhost", 6379)
scalaCache = ScalaCache(RedisCache(jedis, customClassloader = Some(app.classloader)))
}

override def onStop(app: Application): Unit = {
jedis.close()
}

def getItems(ids: List[Int]): List[Item] = memoize {
Expand Down

0 comments on commit 1033b65

Please sign in to comment.