pekko/akka-docs/modules/code/docs/actor/mailbox/DurableMailboxDocSpec.scala
Roland b60210362e make system.actorOf() non-blocking (and working), see #2031
- introducing RepointableActorRef, which starts out with an
  UnstartedActorCell which can cheaply be created; the Supervise()
  message will trigger child.activate() in the supervisor, which means
  that the actual creation (now with normal ActorCell) happens exactly
  in the right place and with the right semantics. Messages which were
  enqueued to the dummy cell are transferred atomically into the
  ActorCell (using normal .tell()), so message sends keep working
  exactly as they used to
- this enables getting rid of the brittle synchronization around
  RoutedActorRef by replacing that one with a RepointableActorRef
  subclass which creates RoutedActorCells upon activate(), with the nice
  benefit that there is no hurry then to get it right because the new
  cell is constructed “on the side”

misc fixes:

- InvalidMessageException is now actually enforced when trying to send
  “null”
- Mailboxes may be created without having an ActorCell, which can come
  in handy later, because the cell is only needed when this mailbox is
  going to be scheduled on some executor
- remove occurrences of Props(), which is equivalent to Props[Nothing],
  which is equivalent to «bug»
- add test case which verifies that context.actorOf is still synchronous
- plus all the stuff I have forgotten.
2012-06-13 17:57:56 +02:00

137 lines
No EOL
3.7 KiB
Scala

/**
* Copyright (C) 2009-2012 Typesafe Inc. <http://www.typesafe.com>
*/
package docs.actor.mailbox
//#imports
import akka.actor.Props
//#imports
import org.scalatest.{ BeforeAndAfterAll, WordSpec }
import org.scalatest.matchers.MustMatchers
import akka.testkit.AkkaSpec
import akka.actor.{ Actor, ExtendedActorSystem }
class MyActor extends Actor {
def receive = {
case x
}
}
object DurableMailboxDocSpec {
val config = """
//#dispatcher-config
my-dispatcher {
mailbox-type = akka.actor.mailbox.FileBasedMailboxType
}
//#dispatcher-config
"""
}
class DurableMailboxDocSpec extends AkkaSpec(DurableMailboxDocSpec.config) {
"configuration of dispatcher with durable mailbox" in {
//#dispatcher-config-use
val myActor = system.actorOf(Props[MyActor].
withDispatcher("my-dispatcher"), name = "myactor")
//#dispatcher-config-use
}
}
//#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
import akka.pattern.CircuitBreaker
import akka.util.duration._
class MyMailboxType(systemSettings: ActorSystem.Settings, config: Config)
extends MailboxType {
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 None throw new IllegalArgumentException(
"requires an owner (i.e. does not work with BalancingDispatcher)")
}
}
class MyMessageQueue(_owner: ActorRef, _system: ExtendedActorSystem)
extends DurableMessageQueue(_owner, _system) with DurableMessageSerialization {
val storage = new QueueStorage
// A real-world implmentation would use configuration to set the last
// three parameters below
val breaker = CircuitBreaker(system.scheduler, 5, 30.seconds, 1.minute)
def enqueue(receiver: ActorRef, envelope: Envelope): Unit = breaker.withSyncCircuitBreaker {
val data: Array[Byte] = serialize(envelope)
storage.push(data)
}
def dequeue(): Envelope = breaker.withSyncCircuitBreaker {
val data: Option[Array[Byte]] = storage.pull()
data.map(deserialize).orNull
}
def hasMessages: Boolean = breaker.withSyncCircuitBreaker { !storage.isEmpty }
def numberOfMessages: Int = breaker.withSyncCircuitBreaker { storage.size }
/**
* 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.
*/
def cleanUp(owner: ActorRef, deadLetters: MessageQueue): Unit = ()
}
//#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 {
mailbox-type = docs.actor.mailbox.MyMailboxType
}
"""
}
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
}
}