2009-03-22 17:26:42 +01:00
|
|
|
/**
|
|
|
|
|
* Copyright (C) 2009 Scalable Solutions.
|
|
|
|
|
*/
|
|
|
|
|
|
2009-06-21 14:08:43 +02:00
|
|
|
package se.scalablesolutions.akka.kernel.util
|
2009-03-22 17:26:42 +01:00
|
|
|
|
2009-07-23 20:01:37 +02:00
|
|
|
import java.io.UnsupportedEncodingException
|
|
|
|
|
import java.security.{NoSuchAlgorithmException, MessageDigest}
|
2009-03-22 17:26:42 +01:00
|
|
|
import java.util.concurrent.locks.ReentrantReadWriteLock
|
|
|
|
|
|
|
|
|
|
import scala.actors._
|
|
|
|
|
import scala.actors.Actor._
|
|
|
|
|
|
|
|
|
|
import net.lag.logging.Logger
|
|
|
|
|
|
|
|
|
|
class SystemFailure(cause: Throwable) extends RuntimeException(cause)
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* @author <a href="http://jonasboner.com">Jonas Bonér</a>
|
|
|
|
|
*/
|
|
|
|
|
object Helpers extends Logging {
|
|
|
|
|
|
2009-07-23 20:01:37 +02:00
|
|
|
def getDigestFor(s: String) = {
|
|
|
|
|
val digest = MessageDigest.getInstance("MD5")
|
|
|
|
|
digest.update(s.getBytes("ASCII"))
|
|
|
|
|
val bytes = digest.digest
|
|
|
|
|
|
|
|
|
|
val sb = new StringBuilder
|
|
|
|
|
val hex = "0123456789ABCDEF"
|
|
|
|
|
bytes.foreach(b => {
|
|
|
|
|
val n = b.asInstanceOf[Int]
|
|
|
|
|
sb.append(hex.charAt((n & 0xF) >> 4)).append(hex.charAt(n & 0xF))
|
|
|
|
|
})
|
|
|
|
|
sb.toString
|
|
|
|
|
}
|
|
|
|
|
|
2009-03-22 17:26:42 +01:00
|
|
|
// ================================================
|
2009-06-25 23:47:30 +02:00
|
|
|
@serializable
|
2009-03-22 17:26:42 +01:00
|
|
|
class ReadWriteLock {
|
|
|
|
|
private val rwl = new ReentrantReadWriteLock
|
|
|
|
|
private val readLock = rwl.readLock
|
|
|
|
|
private val writeLock = rwl.writeLock
|
|
|
|
|
|
|
|
|
|
def withWriteLock[T](body: => T): T = {
|
|
|
|
|
writeLock.lock
|
|
|
|
|
try {
|
|
|
|
|
body
|
|
|
|
|
} finally {
|
|
|
|
|
writeLock.unlock
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
def withReadLock[T](body: => T): T = {
|
|
|
|
|
readLock.lock
|
|
|
|
|
try {
|
|
|
|
|
body
|
|
|
|
|
} finally {
|
|
|
|
|
readLock.unlock
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// ================================================
|
|
|
|
|
// implicit conversion between regular actor and actor with a type future
|
|
|
|
|
implicit def actorWithFuture(a: Actor) = new ActorWithTypedFuture(a)
|
|
|
|
|
|
2009-04-27 19:55:57 +02:00
|
|
|
abstract class FutureWithTimeout[T](ch: InputChannel[T]) extends Future[T](ch) {
|
2009-03-22 17:26:42 +01:00
|
|
|
def receiveWithin(timeout: Int) : Option[T]
|
|
|
|
|
override def respond(f: T => Unit): Unit = throw new UnsupportedOperationException("Does not support the Responder API")
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
def receiveOrFail[T](future: => FutureWithTimeout[T], timeout: Int, errorHandler: => T): T = {
|
|
|
|
|
future.receiveWithin(timeout) match {
|
|
|
|
|
case None => errorHandler
|
|
|
|
|
case Some(reply) => reply
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
class ActorWithTypedFuture(a: Actor) {
|
|
|
|
|
require(a != null)
|
|
|
|
|
|
|
|
|
|
def !!: FutureWithTimeout[A] = {
|
2009-04-27 19:55:57 +02:00
|
|
|
val ftch = new Channel[A](Actor.self)
|
|
|
|
|
a.send(msg, ftch.asInstanceOf[OutputChannel[Any]])
|
2009-03-22 17:26:42 +01:00
|
|
|
new FutureWithTimeout[A](ftch) {
|
|
|
|
|
def apply() =
|
|
|
|
|
if (isSet) value.get.asInstanceOf[A]
|
|
|
|
|
else ch.receive {
|
|
|
|
|
case a =>
|
|
|
|
|
value = Some(a)
|
|
|
|
|
value.get.asInstanceOf[A]
|
|
|
|
|
}
|
|
|
|
|
def isSet = receiveWithin(0).isDefined
|
|
|
|
|
def receiveWithin(timeout: Int): Option[A] = value match {
|
|
|
|
|
case None => ch.receiveWithin(timeout) {
|
|
|
|
|
case TIMEOUT =>
|
2009-03-26 20:22:49 +01:00
|
|
|
log.debug("Future timed out while waiting for actor [%s]", a)
|
2009-03-22 17:26:42 +01:00
|
|
|
None
|
|
|
|
|
case a =>
|
|
|
|
|
value = Some(a)
|
|
|
|
|
value.asInstanceOf[Option[A]]
|
|
|
|
|
}
|
|
|
|
|
case a => a.asInstanceOf[Option[A]]
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|