2011-12-19 11:07:59 +01:00
|
|
|
/**
|
2012-01-19 18:21:06 +01:00
|
|
|
* Copyright (C) 2009-2012 Typesafe Inc. <http://www.typesafe.com>
|
2011-12-19 11:07:59 +01:00
|
|
|
*/
|
2012-05-22 11:37:09 +02:00
|
|
|
package docs.actor.mailbox
|
2011-12-12 08:37:18 +01:00
|
|
|
|
2012-06-28 15:33:49 +02:00
|
|
|
import language.postfixOps
|
|
|
|
|
|
2011-12-12 08:37:18 +01:00
|
|
|
//#imports
|
|
|
|
|
import akka.actor.Props
|
|
|
|
|
|
|
|
|
|
//#imports
|
|
|
|
|
|
|
|
|
|
import org.scalatest.{ BeforeAndAfterAll, WordSpec }
|
|
|
|
|
import org.scalatest.matchers.MustMatchers
|
|
|
|
|
import akka.testkit.AkkaSpec
|
2012-06-13 17:57:56 +02:00
|
|
|
import akka.actor.{ Actor, ExtendedActorSystem }
|
2011-12-12 08:37:18 +01:00
|
|
|
|
|
|
|
|
class MyActor extends Actor {
|
|
|
|
|
def receive = {
|
|
|
|
|
case x ⇒
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2011-12-19 20:36:06 +01:00
|
|
|
object DurableMailboxDocSpec {
|
|
|
|
|
val config = """
|
|
|
|
|
//#dispatcher-config
|
|
|
|
|
my-dispatcher {
|
2012-09-17 13:47:23 +02:00
|
|
|
mailbox-type = akka.actor.mailbox.filebased.FileBasedMailboxType
|
2011-12-19 20:36:06 +01:00
|
|
|
}
|
|
|
|
|
//#dispatcher-config
|
|
|
|
|
"""
|
|
|
|
|
}
|
2011-12-12 08:37:18 +01:00
|
|
|
|
2011-12-19 20:36:06 +01:00
|
|
|
class DurableMailboxDocSpec extends AkkaSpec(DurableMailboxDocSpec.config) {
|
|
|
|
|
|
|
|
|
|
"configuration of dispatcher with durable mailbox" in {
|
|
|
|
|
//#dispatcher-config-use
|
2012-05-15 16:01:32 +02:00
|
|
|
val myActor = system.actorOf(Props[MyActor].
|
|
|
|
|
withDispatcher("my-dispatcher"), name = "myactor")
|
2011-12-19 20:36:06 +01:00
|
|
|
//#dispatcher-config-use
|
|
|
|
|
}
|
|
|
|
|
|
2011-12-12 08:37:18 +01:00
|
|
|
}
|
2012-05-15 16:01:32 +02:00
|
|
|
|
|
|
|
|
//#custom-mailbox
|
|
|
|
|
import com.typesafe.config.Config
|
|
|
|
|
import akka.actor.ActorContext
|
|
|
|
|
import akka.actor.ActorRef
|
|
|
|
|
import akka.actor.ActorSystem
|
|
|
|
|
import akka.dispatch.Envelope
|
|
|
|
|
import akka.dispatch.MailboxType
|
|
|
|
|
import akka.dispatch.MessageQueue
|
|
|
|
|
import akka.actor.mailbox.DurableMessageQueue
|
|
|
|
|
import akka.actor.mailbox.DurableMessageSerialization
|
2012-06-01 08:24:47 -04:00
|
|
|
import akka.pattern.CircuitBreaker
|
2012-06-29 13:33:20 +02:00
|
|
|
import scala.concurrent.util.duration._
|
2012-05-15 16:01:32 +02:00
|
|
|
|
|
|
|
|
class MyMailboxType(systemSettings: ActorSystem.Settings, config: Config)
|
|
|
|
|
extends MailboxType {
|
|
|
|
|
|
2012-10-01 20:35:19 +02:00
|
|
|
override def create(owner: Option[ActorRef],
|
|
|
|
|
system: Option[ActorSystem]): MessageQueue =
|
|
|
|
|
(owner zip system) headOption match {
|
|
|
|
|
case Some((o, s: ExtendedActorSystem)) ⇒ new MyMessageQueue(o, s)
|
|
|
|
|
case _ ⇒
|
|
|
|
|
throw new IllegalArgumentException("requires an owner " +
|
|
|
|
|
"(i.e. does not work with BalancingDispatcher)")
|
|
|
|
|
}
|
2012-05-15 16:01:32 +02:00
|
|
|
}
|
|
|
|
|
|
2012-06-13 17:57:56 +02:00
|
|
|
class MyMessageQueue(_owner: ActorRef, _system: ExtendedActorSystem)
|
|
|
|
|
extends DurableMessageQueue(_owner, _system) with DurableMessageSerialization {
|
2012-05-15 16:01:32 +02:00
|
|
|
|
|
|
|
|
val storage = new QueueStorage
|
2012-06-01 08:24:47 -04:00
|
|
|
// A real-world implmentation would use configuration to set the last
|
|
|
|
|
// three parameters below
|
2012-06-13 17:57:56 +02:00
|
|
|
val breaker = CircuitBreaker(system.scheduler, 5, 30.seconds, 1.minute)
|
2012-05-15 16:01:32 +02:00
|
|
|
|
2012-10-01 20:35:19 +02:00
|
|
|
def enqueue(receiver: ActorRef, envelope: Envelope): Unit =
|
|
|
|
|
breaker.withSyncCircuitBreaker {
|
|
|
|
|
val data: Array[Byte] = serialize(envelope)
|
|
|
|
|
storage.push(data)
|
|
|
|
|
}
|
2012-05-15 16:01:32 +02:00
|
|
|
|
2012-06-01 21:29:47 +02:00
|
|
|
def dequeue(): Envelope = breaker.withSyncCircuitBreaker {
|
2012-05-15 16:01:32 +02:00
|
|
|
val data: Option[Array[Byte]] = storage.pull()
|
2012-05-21 07:46:48 +02:00
|
|
|
data.map(deserialize).orNull
|
2012-05-15 16:01:32 +02:00
|
|
|
}
|
|
|
|
|
|
2012-06-01 08:24:47 -04:00
|
|
|
def hasMessages: Boolean = breaker.withSyncCircuitBreaker { !storage.isEmpty }
|
2012-05-15 16:01:32 +02:00
|
|
|
|
2012-06-01 21:29:47 +02:00
|
|
|
def numberOfMessages: Int = breaker.withSyncCircuitBreaker { storage.size }
|
2012-05-15 16:01:32 +02:00
|
|
|
|
2012-05-21 07:46:48 +02:00
|
|
|
/**
|
|
|
|
|
* Called when the mailbox is disposed.
|
|
|
|
|
* An ordinary mailbox would send remaining messages to deadLetters,
|
|
|
|
|
* but the purpose of a durable mailbox is to continue
|
|
|
|
|
* with the same message queue when the actor is started again.
|
|
|
|
|
*/
|
2012-06-13 17:57:56 +02:00
|
|
|
def cleanUp(owner: ActorRef, deadLetters: MessageQueue): Unit = ()
|
2012-05-15 16:01:32 +02:00
|
|
|
|
|
|
|
|
}
|
|
|
|
|
//#custom-mailbox
|
|
|
|
|
|
|
|
|
|
// dummy
|
|
|
|
|
class QueueStorage {
|
|
|
|
|
import java.util.concurrent.ConcurrentLinkedQueue
|
|
|
|
|
val queue = new ConcurrentLinkedQueue[Array[Byte]]
|
|
|
|
|
def push(data: Array[Byte]): Unit = queue.offer(data)
|
|
|
|
|
def pull(): Option[Array[Byte]] = Option(queue.poll())
|
|
|
|
|
def isEmpty: Boolean = queue.isEmpty
|
|
|
|
|
def size: Int = queue.size
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//#custom-mailbox-test
|
|
|
|
|
import akka.actor.mailbox.DurableMailboxSpec
|
|
|
|
|
|
|
|
|
|
object MyMailboxSpec {
|
|
|
|
|
val config = """
|
|
|
|
|
MyStorage-dispatcher {
|
2012-05-22 11:37:09 +02:00
|
|
|
mailbox-type = docs.actor.mailbox.MyMailboxType
|
2012-05-15 16:01:32 +02:00
|
|
|
}
|
|
|
|
|
"""
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
class MyMailboxSpec extends DurableMailboxSpec("MyStorage", MyMailboxSpec.config) {
|
|
|
|
|
override def atStartup() {
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
override def atTermination() {
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
"MyMailbox" must {
|
|
|
|
|
"deliver a message" in {
|
|
|
|
|
val actor = createMailboxTestActor()
|
|
|
|
|
implicit val sender = testActor
|
|
|
|
|
actor ! "hello"
|
|
|
|
|
expectMsg("hello")
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// add more tests
|
|
|
|
|
}
|
|
|
|
|
}
|