Trigger Asynchronous Jobs in Play 2.3

Back

Triggering Asynchronous Jobs in Play 2.3

In the old version of play it was rather easy to create jobs. There was an entire package dedicated to the task in version 1.2 of the framework. But, with an upgrade to Play 2.3 that package disappeared!

So then, how do you perform a job?

Well, in 2.3 this has to be done by integrating with Akka. Luckily, the framework has a default ActorSystem which we can tie into easily.

Long story short, you'll end up with code like this:

 import play.api.libs.concurrent.Akka
  import play.api.libs.concurrent.Execution.Implicits._

  // code ...
  def someControllerOrServiceMethod() {
    val myActor = Akka.system.actorOf(Props( SomeTestActor()))
    Akka.system.scheduler.scheduleOnce(0.microsecond, myActor, SomeTestActor.SomeMessage(args))
  }

This is taking avantage of the Akka scheduler that is integrated into the Playframework. In the code above we would create an actor and immediately send it a message to kick off whatever it needs to do. You can also schedule recurring messages like so:

// code ...
  def someControllerOrServiceMethod() {
    val myActor = Akka.system.actorOf(Props( SomeTestActor()))
    Akka.system.scheduler.scheduleOnce(1000.microsecond, 300.seconds, myActor, SomeTestActor.SomeMessage(args))
  }

This uses the overloaded version of schedule with the following signature:

schedule( initialDelay: Duration, 
    frequency: Duration,
    receiver: ActorRef,
    message: Any
): Cancellable

You could use this for simple clean up processes, starting the scheduled tasks when play boots using GlobalSettings like so:

import play.api._
import play.api.libs.concurrent.Akka
import play.api.libs.concurrent.Execution.Implicits._

object Global extends GlobalSettings {

  override def onStart(app: Application) {
    Logger.info("Application has started")
    val myActor = Akka.system.actorOf(Props( SomeTestActor()))
    Akka.system.scheduler.scheduleOnce(1000.microsecond, 300.seconds, myActor, SomeTestActor.SomeMessage(args))
  }

  override def onStop(app: Application) {
    Logger.info("Application shutdown...")
  }

}

The calling code isn't difficult, but you do need to provide an implementation of an Actor that makes sense. In the work I've done so far I've used self-killing Actors. Here's the basics:

import akka.actor.{Actor, ActorRef, PoisonPill}
import scala.util.{Success, Failure}
import scala.concurrent._
import scala.concurrent.duration._
import ExecutionContext.Implicits.global

trait MsgSender {
    def sendMsg(msg: TestActor.InternalActorMessage)(implicit actor: ActorRef) : Unit
}

trait SelfSender extends MsgSender {
    def sendMsg(msg: TestActor.InternalActorMessage)(implicit actor: ActorRef) = {
        actor ! msg
    }
}

object TestActor {
    class InternalActorMessage() {}
    case class SomeMessage() extends InternalActorMessage
    case class OtherMessage() extends InternalActorMessage

    def apply() = {
        new TestActor() with SelfSender
    }
}

class TestActor extends Actor { this: MsgSender =>
    def receive = {
        case TestActor.SomeMessage => 
            // do stuff
            sendMsg(TestActor.OtherMessage())
        case TestActor.OtherMessage =>
            // do stuff
            self ! PoisonPill
    }
}

This is a heavily stripped down version of some code I'm using. You might wonder why I'd abstract the sending into a trait like I did. The answer is: for testing!

If you have an actor that primarily communicates with itself by sending messages via self ! msg it can be difficult to test. Since when you send it a message, you'll always end up at the end of the chain of events. In the above case, how would you test that handling SomeMessage worked?

With the trait and mixin being done in the apply method of the TestActor companion object, we can grab our "default" Actor for use in the application and mix in a testing MsgSender when we unit test:

trait TestMsgSender extends MsgSender {
    def sendMsg(msg: TestActor.InternalActorMessage)(implicit actor: ActorRef) = {
        //don't forward the message back to the actor, or send it to a 
        //test monitoring actor or whatever you'd like! 
    }
}

val testActor = Props(new TestActor() with TestMsgSender)

// ... do tests with TestKit and whatnot 

By not forwarding the messages along to ourselves, we can execute and test only the cases in the receive method that we want to. There are other ways to do this of course, such as overriding the ! (tell) method and using the test probe, but I find that using trait's is easy to reason and understand within a test. So I've preferred that so far.

while using jobs is not as simple as it was in version 1 of the framework, it still is pretty easy as long as you know the right tricks!

Other Posts

comments powered by Disqus