How to schedule an hourly job with Play Framework 2.1? - scala

In Play 1 it was simply:
#Every(value = "1h")
public class WebsiteStatusReporter extends Job {
#Override
public void doJob() throws Exception {
// do something
}
}
What is the equivalent for Play 2.1?
I know that Play uses akka and I found this code sample:
import play.api.libs.concurrent.Execution.Implicits._
Akka.system.scheduler.schedule(0.seconds, 30.minutes, testActor, "tick")
But being a Scala noob I don't understand how works. Can someone provide a complete, working example (end to end)?

Here is an extract from a code of mine:
import scala.concurrent.duration.DurationInt
import akka.actor.Props.apply
import play.api.Application
import play.api.GlobalSettings
import play.api.Logger
import play.api.Play
import play.api.libs.concurrent.Execution.Implicits.defaultContext
import play.api.libs.concurrent.Akka
import akka.actor.Props
import actor.ReminderActor
object Global extends GlobalSettings {
override def onStart(app: Application) {
val controllerPath = controllers.routes.Ping.ping.url
play.api.Play.mode(app) match {
case play.api.Mode.Test => // do not schedule anything for Test
case _ => reminderDaemon(app)
}
}
def reminderDaemon(app: Application) = {
Logger.info("Scheduling the reminder daemon")
val reminderActor = Akka.system(app).actorOf(Props(new ReminderActor()))
Akka.system(app).scheduler.schedule(0 seconds, 5 minutes, reminderActor, "reminderDaemon")
}
}
It simply starts a daemon at the start of the app, and then, every 5 minutes. It uses Play 2.1 and it works as expected.
Note that this code uses the Global object which allows to run some code on application startup.

An example:
case object Tick
class TestActor extends Actor {
def receive = {
case Tick => //...
}
}
val testActor = Akka.system.actorOf(Props[TestActor], name = "testActor")
Akka.system.scheduler.schedule(0.seconds, 30.minutes, testActor, Tick)

Take a look into Akka's doc
sample you gave is:
def schedule(
initialDelay: Duration,
frequency: Duration,
receiver: ActorRef,
message: Any): Cancellable
Means: start 0 seconds from now, at every 30 minutes send to the actor testActor message Tick
what's more for simple tasks you probably don;t need to use Actors - you can just schedule the new Runnable:
def schedule(
initialDelay: Duration, frequency: Duration, runnable: Runnable): Cancellable
More detailed description in other answer

A simple play scheduler without using Actors.
It can be done using org.quartz.scheduler and calling the scheduler from the Global class.
Sample scheduler

Related

How to test controllers that has akka system injected for Play framework?

Below is my controller:
package controllers
import java.util.TimeZone
import akka.actor.{ActorNotFound, ActorSystem}
import akka.util.Timeout
import com.google.inject.Inject
import com.typesafe.akka.extension.quartz.QuartzSchedulerExtension
import play.api.Logger
import play.api.libs.concurrent.Execution.Implicits.defaultContext
import play.api.libs.json.{JsValue, JsError, JsSuccess}
import play.api.mvc._
import scala.concurrent.Future
import scala.concurrent.duration._
class ScheduleController #Inject()(system: ActorSystem) extends Controller {
val scheduler = QuartzSchedulerExtension.get(system)
implicit val timeout = new Timeout(5.seconds)
def index = Action.async {
Future.successful(Ok("hi"))
}
def run = Action.async { request =>
// find actor and "start" it by sending message to it, return Ok() if found
// if actor is not found, return BadRequest()
// if error return InternalServerError()
}
}
I've been looking for tutorials but most are outdated since they deal with Play 2.3.
I solved a very similar problem with Play2 controllers and actors except in my case I'm not injecting an ActorSystem into my controller. Rather, I use Play's default actor system and create actors from my global settings object when my application starts. For a given actor I provide a selection method in its companion object. For example:
class MyActor extends Actor {
def receive = {
case Request => sender ! Response
}
}
object MyActor {
val path = // assuming fixed actor path
val sys = // reference to play actor system
def select: ActorSelection = sys.actorSelection(path)
// my actor messages
case object Request
case object Response
}
The way I have this actor setup I plan on asking it from inside my controller method using a Request and expect to process a Response object back from it. The controller is a non-actor sender in this scenario. By calling ask on the actor selection, I close over the future and map the result to a partial function that matches the expected response. Here's what my controller looks like:
class MyController extends Controller {
implicit val timeout = // set your timeout duration
def index = Action.async {
MyActor.select ? Request map {
case Response => Ok("success")
}
}
}
Testing this controller endpoint is VERY challenging because your actor might not be in a ready state to start handling messages. I spent about two days trying to figure out how to properly block on the my actor's lifecycle state using the actor selection I've exposed through its companion object. What I ended up doing inside my test class that worked for me was to create a method that takes in a function parameter and loops over my actor state until I can resolve its ActorRef. I'm a fan of ScalaTest so this is what my unit tests amounted to:
class MySpec extends FlatSpec with Matchers with BeforeAndAfterAll {
implicit val fakeApp: FakeApplication = new FakeApplication()
override def beforeAll() = Play.start(fakeApp)
override def afterAll() = Play.stop(fakeApp)
def awaitableActorTest(assertions: => Any): Unit = {
val timeout = 1.second
var isActorReady = false
while(!isActorReady) {
val futureRef = Await.ready(MyActor.select.resolveOne(timeout), timeout)
futureRef.value.get match {
case Success(_) =>
assertions
isActorReady = true
case Failure(_) =>
Thread.sleep(2000)
}
}
}
it should "process actor request and response via controller endpoint" in {
awaitableActorTest {
val result = route(routes.MyController.index).get
status(result) shouldBe OK
contentAsString(result) shouldBe "success"
}
}
}
What I get out of this awaitableActorTest pattern is a really clean way of reliably hitting my controller endpoint only when my actor is alive and available to process messages. If it's not, my ActorRef using its selection companion won't resolve in the future with success -- instead it completes with failure. When it fails I sleep a few seconds and repeat the loop. I break the loop when I its selection successfully resolved an ActorRef.
I'm unfamiliar with the QuartzSchedulerExtension to which you refer in your code snippet, but I hope my example is useful.

Akka Http Route Test: Request was neither completed nor rejected within 1 second

I am trying to write a test case for my application with akka-http. One of the testcases is given below:
import akka.http.scaladsl.model.headers.RawHeader
import akka.http.scaladsl.testkit.{ ScalatestRouteTest}
import com.reactore.common.core.{CommonCoreSystem, CommonActors, BootedCommonCoreSystem}
import scala.concurrent.duration._
import com.reactore.common.feature.referencedata.{DepartmentRepository, DepartmentService, DepartmentController, DepartmentRest}
import org.scalatest.concurrent.AsyncAssertions
import org.scalatest.time.Span
import org.scalatest.{WordSpec, Matchers}
import akka.http.scaladsl.model.StatusCodes._
/**
* Created by krishna on 18/6/15.
*/
class DepartmentITTest extends WordSpec with Matchers with ScalatestRouteTest with CommonCoreSystem with CommonActors {
// override val departmentRouter = system.actorOf(Props(classOf[DepartmentService], DepartmentRepository), Constants.DEPARTMENT_ROUTER_NAME)
val deptRoute = (new DepartmentRest(departmentRouter)(DepartmentController).deptRoutes)
implicit val timeout = AsyncAssertions.timeout(Span.convertDurationToSpan(5.second))
val departmentJson = """{"id":13,"name":"ENGP22","shortCode":"ENGP2","parentDepartmentId":null,"mineId":null,"photoName":null,"isRemoved":false,"isPendingForApproval":false,"createdBy":0,"createDate":"2015-03-09 00:00:00","modifiedBy":0,"modifiedDate":"2015-03-09 00:00:00"}"""
val header = RawHeader("apiKey", "xxxxx")
"Service" should {
"return department by id" in {
Get("/departments/13").withHeaders(header) ~> deptRoute ~> check {
// Thread.sleep(500)
status shouldBe OK
responseAs[String] shouldBe departmentJson
}
}
}
}
When I run this, it works correctly sometimes, and sometimes I get the error as Request was neither completed nor rejected within 1 second. I added a Thread.sleep for making it work now. I know that is not the correct solution. Could anyone tell me how to make the test wait for more than 1 second?
The following is working for me:
import akka.actor.ActorSystem
import scala.concurrent.duration._
import spray.testkit.ScalatestRouteTest
class RouteSpec extends ScalatestRouteTest {
implicit def default(implicit system: ActorSystem) = RouteTestTimeout(2.second)
...
You can use the "eventually" matcher in ScalaTest to wait for a condition to become true:
eventually { status shouldBe OK }
http://www.artima.com/docs-scalatest-2.0.M5/org/scalatest/concurrent/Eventually.html
That should suffice if the Thread.sleep you commented out above fixes things for you.
However, it looks to me like the actual error is that the timeout that the RouteTest trait is using is too short. The error message "Request was neither completed nor rejected within 1 second." comes from RouteTestResultComponent via akka.http.scaladsl.testkit.RouteTest.
I think that the Thread.sleep is a distraction. The default timeout for routing tests is 1 second; see akka.http.scaladsl.testkit.RouteTestTimeout.default. You provide a 5 second implicit timeout in your code, but I think it's of a different type. Try making a RouteTestTimeout implicitly available with a longer timeout.
you can simply update the config to dilate the timeout
akka {
test {
# factor by which to scale timeouts during tests, e.g. to account for shared
# build system load
timefactor = 3.0
}
}

Scala: unit-testing a method called asynchronously and returns a Future

I have a Scala method that reads as follows:
def doAnAsynchOperation(bunchedFilesUnit:BunchOfFilesUnit):
Future[FilesHandled] = {
val handleFuture = future {
BunchOfFilesUnit.unit.map(file => {
//FileParsing code - instantiate a class for file parsing
//Open a fileInputStream
//get an OutputStream
try {
//do some parsing, pass in a "file" invoke a parsing method
} catch {
case e:Throwable =>
//some code
} finally {
fileInputStream.close()
outStream.close()
}
})
}
I am calling this method in my Unit Test written in a "describe" and inside of it, I am going to have an "it".
There are 5 threads. Each thread is assigned the task of handling a single BunchedFileUnit .
These 5 threads are calling doAnAsynchOperation method asynchronously - each on their own - to process their own BunchedFileUnit and this method returns a Future[BunchedFileUnit].
What I want to accomplish is this:
I would like to write a test or tests to test this scenario. I would like to incorporate a timeout.
I am not very clear about the timeout concept here, but I assume I have to set a timeout and anticipate a TimeOutException. How can I lay out my test for such a scenario? This is what I want to accomplish.
Currently, I am laying out my test as follows:
import org.scalatest._
import org.scalatest.concurrent.ScalaFutures
import scala.concurrent.{Await, ExecutionContext, duration, Future}
import duration._
import akka.actor.{Actor, ActorRef, ActorSystem}
import org.scalatest.MustMatchers
import java.io.File
class FileHandlerTest extends FunSpec
with MustMatchers
with ScalaFutures
with BeforeAndAfter with SomeTraitThatINeeed {
describe("------------"){
}
}
I'll go out on a limb and guess you're looking for the "whenReady" helper. I see you've already imported ScalaFutures, so you'd do something like this inside your example:
val files = Seq(...)
whenReady(doAnAsynchOperation(files), timeout(Span(6, Seconds))) { result =>
result mustEqual "the correct value"
}
If you're trying to test the failure condition, you could try giving it a really short timeout, but I'm not sure what that would accomplish another than ensuring it doesn't complete too fast or take too long.

Calling Controller DBAction with scheduled Akka Runnable (in Scala / Play 2.2)

I'm attempting to schedule an Akka job when my Play 2.2 application starts.
In it's simplest form, this is what my code looks like:
import play.api.Application
import play.api.Play.current
import play.api.GlobalSettings
import play.api.Logger
import play.api.db.DB
import scala.concurrent.duration._
import play.api.libs.concurrent.Akka
import play.api.libs.concurrent.Execution.Implicits._
import scala.slick.driver.MySQLDriver.simple._
object Global extends GlobalSettings {
override def onStart(app: Application) {
lazy val database = Database.forDataSource(DB.getDataSource())
scheduleTheThing(app)
}
private def scheduleTheThing(app: Application) {
Akka.system.scheduler.scheduleOnce(1.minute, new Runnable {
override def run() {
Logger.info("running the thing!")
controllers.Application.runTheThing
}
})
}
}
As you can see, I'm creating a new Runnable that will be executed 1 minute after the application starts. Logger.info("Running the thing!") gets executed just fine... I'm able to see "Running the thing!" in the application log 1 minute after starting the server. However, controllers.Application.runTheThing doesn't seem to get called:
object Application extends Controller {
def runTheThing = DBAction {
implicit session => {
Logger.info("It's happening!")
DBThing.doSomeDBStuff()
}
}
// ...
}
My "It's happening!" log statement never shows up, and the stuff that DBThing.doSomeDBStuff() is supposed to do never happens. The curious thing is that there are no errors in the console or log files.
How is one supposed to call a controller DBAction from a scheduled runnable? Or How should I rework my scheduler so that this works? Any help would be greatly appreciated, thanks.
Calling the action method will do nothing but return an instance of Action, to actually execute it you would have to call it like play normally does with a request. I would recommend to extract this to non-controller code and call that from your scheduled task instead.

Starting Akka actors in Play

I have a Play! application with some tasks I need to run periodically. I can schedule the tasks using Akka, but I am not sure how to start the scheduler itself. What I am doing right now is having a Scheduler object and starting it from Global.scala, like this
// app/jobs/Scheduler.scala
package jobs
import akka.util.duration._
import play.api.libs.concurrent.Akka
import play.api.Play.current
object Scheduler {
def start() {
Akka.system.scheduler.schedule(0 seconds, 1 minutes) {
SomeTask.start()
}
}
}
and then
// app/Global.scala
import play.api._
import jobs.Scheduler
object Global extends GlobalSettings {
override def onStart(app: Application) {
Scheduler.start()
}
}
The problem is that in this, the task runs even in development mode and during tests, that becomes soon very annoying.
Is there a way to schedule jobs with Akka only in production mode?
Methods isProd, isDev and isTest on Play object could be helpful. Even if you doesn't have implicit Application in scope, you can pass it explicitly
override def onStart(app: Application) {
if (isProd(app)) Scheduler.start()
}