package actor
- Alphabetic
- Public
- Protected
Value Members
- object typed
Contains the com.thoughtworks.dsl.Dsl instances in a typed actor.
Contains the com.thoughtworks.dsl.Dsl instances in a typed actor.
Installation
This typed object supports !-notation for keywords.akka.actor.ReceiveMessage in the typed actor domains, which requires BangNotation and ResetEverywhere compiler plugins along with this
typed
library. For sbt, add the following settings to yourbuild.sbt
:addCompilerPlugin("com.thoughtworks.dsl" %% "compilerplugins-bangnotation" % "latest.release") addCompilerPlugin("com.thoughtworks.dsl" %% "compilerplugins-reseteverywhere" % "latest.release") libraryDependencies += "com.yang-bo.dsl.domains.akka.actor" %% "typed" % "latest.release"
Imports
Then, add the following import statement to enable
ReceiveMessage
in the akka.actor.typed.Behavior domain.import com.yang_bo.dsl.domains.akka.actor.typed._ import com.yang_bo.dsl.keywords.akka.actor.ReceiveMessage
Author:
杨博 (Yang Bo)
This library can be used as an alternative to akka.actor.FSM, for creating state machines in simple Scala control flow. The following state machine contains two states and two transitions between them.
It can be created as a simplewhile
loop with the help of keywords.akka.actor.ReceiveMessage.Partial:import akka.actor.typed._ sealed trait State case object Opened extends State case object Closed extends State sealed trait Transition case class Open(response: ActorRef[State]) extends Transition case class Close(response: ActorRef[State]) extends Transition def doorActor: Behavior[Transition] = { while (true) { val open = !ReceiveMessage.Partial[Open] open.response ! Opened val close = !ReceiveMessage.Partial[Close] close.response ! Closed } throw new Exception("Unreachable code!") }
The door should reply an
Opened
state after performing anOpen
transition,import akka.actor.testkit.typed.scaladsl._ val door = BehaviorTestKit(doorActor) val state = TestInbox[State]() door.run(Open(state.ref)) state.expectMessage(Opened)
and the state of the door can be switched between
Opend
andClosed
according toOpen
andClose
transition.door.run(Close(state.ref)) state.expectMessage(Closed) door.run(Open(state.ref)) state.expectMessage(Opened) door.run(Close(state.ref)) state.expectMessage(Closed)
- Note
To use
try
/catch
/finally
expressions with !-notation, the return type of enclosing function should beBehavior[?] !! Throwable
, as shown in the followingcreateDecoderActor
method. It will open an java.io.InputStream, read String from the stream, and close the stream in afinally
block.import akka.actor.typed._ import akka.actor.typed.scaladsl._ import com.thoughtworks.dsl.Dsl.!! import java.io._ import java.net._ sealed trait Command case class Open(open: () => InputStream) extends Command case class ReadObject(response: ActorRef[String]) extends Command case object Close extends Command class DecoderException(cause: Throwable) extends Exception(cause) def createDecoderActor: Behavior[Command] !! Throwable = { while (true) { val inputStream = (!ReceiveMessage.Partial[Open]).open() try { val ReadObject(replyTo) = !ReceiveMessage.Partial[ReadObject] replyTo ! new java.io.DataInputStream(inputStream).readUTF() !ReceiveMessage.Partial[Close.type] } catch { case e: IOException => throw new DecoderException(e) } finally { inputStream.close() } } throw new AssertionError("Unreachable code!") }
Since
createDecoderActor
returnsBehavior[Command] !! Throwable
, it receives message of typeCommand
, and accepts an additional callback function to handle exceptions that are not handled increateDecoderActor
.import akka.actor.testkit.typed.scaladsl._ val errorHandler = mockFunction[Throwable, Behavior[Command]] val decoderActor = BehaviorTestKit(createDecoderActor(errorHandler))
Given an
InputStream
that throws an java.io.IOException when read from it,val inputStream: InputStream = mock[InputStream] toMockFunction0(inputStream.read _).expects().throws(new IOException()) decoderActor.run(Open(() => inputStream))
when the
decoderActor
try to read a String from the stream, it should close the stream due tofinally
block triggered by the exception.val inbox = TestInbox[String]() errorHandler.expects(where[Throwable](_.isInstanceOf[DecoderException])).returns(Behaviors.stopped) toMockFunction0(inputStream.close _).expects().returns(()).once() decoderActor.run(ReadObject(inbox.ref)) inbox.receiveAll() should be(empty)
Example: