Background:
I've simplified my web client
actor to test out become
and unbecome
behaviour.
Here is the simplified actor:
class TestClient extends TimerActor {
implicit val system: ActorSystem = context.system
private val ONLINE:AtomicBoolean = AtomicBoolean(false)
private val HTTP_TOKEN:AtomicBoolean = AtomicBoolean(false)
timers.startSingleTimer(TickKey, FirstTick, FiniteDuration(1, TimeUnit.SECONDS))
override def receive: Receive = {
case FirstTick =>
timers.startTimerWithFixedDelay(TickKey, Tick, FiniteDuration(1, TimeUnit.SECONDS))
case Tick =>
system.log.info("becoming pingService")
context.become(pingService)
if (ONLINE.get()) {
system.log.info("becoming httpTokenFetcher")
context.become(httpTokenFetcher)
if (HTTP_TOKEN.get()) {
system.log.info("HTTP token set.")
}
}
}
private def httpTokenFetcher: Receive = {
case Tick =>
system.log.info("httpTokenFetcher becoming pingService")
context.become(pingService, false) // always do a ping first
if (ONLINE.get()) {
system.log.info("Fetching http token")
HTTP_TOKEN.set(true)
context.unbecome()
}
}
private def pingService: Receive = {
case Tick =>
system.log.info("Doing ping")
ONLINE.set(true)
context.unbecome()
}
}
Expected result:
What I expect to see is that when the httpTokenFetcher
behaviour is the actors behaviour, it "becomes" a pingService
first, performs a ping (prints "doing ping"
), then puts the behaviour back to httpPingService
to continue with fetching the token.
So I expect the output:
becoming pingService
Doing ping
becoming pingService
Doing ping <- this time, ONLINE is true, so become httpTokenFetcher
becoming httpTokenFetcher
httpTokenFetcher becoming pingService
Doing ping
Fetching http token
httpTokenFetcher becoming pingService
Doing ping
Fetching http token
HTTP token set. <- This print doesn't even hit
...
Actual results:
becoming pingService
Doing ping
becoming pingService
becoming httpTokenFetcher
httpTokenFetcher becoming pingService
Fetching http token
httpTokenFetcher becoming pingService
Fetching http token
httpTokenFetcher becoming pingService
...
What I Tried:
In the httpTokenFetcher
receive, I tried using context.become(pingService, false)
vs context.become(pingService)
With context.become(pingService)
I get the output:
becoming pingService
Doing ping
becoming pingService
becoming httpTokenFetcher
httpTokenFetcher becoming pingService
Fetching http token
becoming pingService
becoming httpTokenFetcher
HTTP token set. <- actually completed
But still does not print "Doing ping" where I expect it to.
Any suggestions?
I played around some more and determined I can pass the "next behaviour" to the pingService
, which calls become
if the ping service was successful. This gives the desired result.
New TestClient
class:
class TestClient extends TimerActor {
implicit val system: ActorSystem = context.system
private val ONLINE: AtomicBoolean = AtomicBoolean(false)
private val HTTP_TOKEN: AtomicBoolean = AtomicBoolean(false)
timers.startSingleTimer(TickKey, FirstTick, FiniteDuration(1, TimeUnit.SECONDS))
override def receive: Receive = {
case FirstTick =>
timers.startTimerWithFixedDelay(TickKey, Tick, FiniteDuration(1, TimeUnit.SECONDS))
case Tick =>
val nextBehaviour: Receive = if (HTTP_TOKEN.get()) null else httpTokenFetcher
context.become(pingService(nextBehaviour))
}
private def httpTokenFetcher: Receive = {
case Tick =>
system.log.info("Fetching http token")
HTTP_TOKEN.set(true)
system.log.info("HTTP Token Set")
context.unbecome()
}
private def pingService(nextBehaviour: Receive = null): Receive = {
case Tick =>
system.log.info("Doing ping")
ONLINE.set(true)
if (nextBehaviour != null) {
if (ONLINE.get()) {
context.become(nextBehaviour)
}
}
}
}
I now get the output:
Doing ping
Fetching http token
HTTP Token Set
Doing ping
Doing ping
Doing ping
Doing ping
...
This will allow me to expand the behaviour too, setting different behaviours to pass to pingService
to perform if the server is still online.