Play framework with Akka and WS
1 Set Up Akka in Playframework
In my controller which I believe it is single, I set up the AKKA system.
package controllers
import actors._
import actors.jobs.{JobServiceActor, JobParseActor}
import actors.resumes.{ResumeServiceActor, ResumeParseActor, ResumeAttachActor}
import akka.actor.Props
import com.sillycat.dao.{IpplyJobDAO, DAO}
import com.sillycat.util.IncludeLogger
import com.wordnik.swagger.annotations._
import models.ScanTriggerRequest
import play.api.libs.concurrent.Akka
import play.api.libs.json.{JsError, Json}
import play.api.mvc.{BodyParsers, Action, Controller}
import play.api.Play.current
import akka.contrib.throttle.TimerBasedThrottler
import akka.contrib.throttle.Throttler._
import scala.concurrent.duration._
import com.wordnik.swagger.annotations._
object ScanTriggerController extends Controller with IncludeLogger with IncludeWSConfig{
implicit val scanTriggerWrites = Json.writes[ScanTriggerRequest]
implicit val scanTriggerReads = Json.reads[ScanTriggerRequest]
Akka.system.actorOf(JobParseActor.props, name = "job-parse-actor")
Akka.system.actorOf(JobServiceActor.props, name = "job-service-actor")
Akka.system.actorOf(ResumeAttachActor.props, name = "resume-attach-actor")
Akka.system.actorOf(ResumeParseActor.props, name = "resume-parse-actor")
Akka.system.actorOf(ResumeServiceActor.props, name = "resume-service-actor")
Akka.system.actorOf(TriggerActor.props, name = "trigger-actor")
val contextIOActor = Akka.system.actorOf(ContextIOActor.props, name = "contextio-actor")
val contextIOThrottler = Akka.system.actorOf(Props(classOf[TimerBasedThrottler], contextio_throttle msgsPer 60.second), name = "conetxt-io-throttler")
contextIOThrottler ! SetTarget(Some(contextIOActor))
def trigger = Action(BodyParsers.parse.json) { request =>
val b = request.body.validate[ScanTriggerRequest]
logger.debug("Processing request with param = " + request.body)
b.fold(
errors => {
BadRequest(Json.obj("status" -> "OK", "message" -> JsError.toJson(errors)))
},
trigger => {
//TODO more validation on the params
logger.debug("Param validation success, param = " + trigger)
val triggerActor = Akka.system.actorSelection("/user/trigger-actor")
logger.debug("Prepare the trigger action = " + triggerActor + ", and fire message!")
triggerActor ! trigger
Ok(Json.obj("status" -> "OK"))
}
)
}
}
In any other actors or any other controllers, we can do as follow:
val jobServiceActor = context.actorSelection("/user/job-service-actor")
jobServiceActor ! JobContentPersistMessage(msg.jobId, jobCity, msg.emailContent)
In my case I have one controller to trigger my actions, maybe in the future I need to set up the AKKA system in global class or some where.
2 WS to call other Micro Service
Here is how I call other micro service
import akka.actor.{Actor, Props}
import com.sillycat.dao.{DAO, IpplyJobDAO, ResumeDAO}
import com.sillycat.util.IncludeLogger
import models.messages.jobs.{JobPersistMessage, JobScanMessage, JobContentParseMessage, JobContentScanMessage}
import models.messages.resumes._
import models.{ResumeScanResponse, ResumeScanAttachmentResponse, JobContentResponse, JobScanResponse}
import play.api.libs.json.Json
import play.api.libs.ws.{WS, WSResponse}
import utils.IncludeRegex
import play.api.Play.current
import scala.concurrent.ExecutionContext.Implicits.global
import scala.concurrent.Future
…snip...
case msg:JobContentScanMessage => {
logger.debug("receiving the message JobContentScanMessage" + msg)
//context.io fetch job content, actually it is email content, I need the city and location info
val future:Future[WSResponse] = WS.url(contextio_host + "/jobs/" + msg.accountCode +"/" + msg.messageCode ).get()
//scan jobs success
future onSuccess {
case response if response.status == 200 => {
logger.trace("Scanning job content = " + response.json)
response.json.asOpt[JobContentResponse].map { job =>
logger.trace("Scanning result job content = " + job)
val jobContentParseMessage = JobContentParseMessage(msg.jobId, job.content)
logger.debug("fire the message to job-parse-actor to parse email content.")
jobParseActor ! jobContentParseMessage
}
}
case response => {
//handling error
logger.error("Error handle with status = " + response.status + " message = " + response.body)
}
}
//scan jobs fail
future onFailure {
case t => logger.error("An error has occured: " + t.getMessage)
}
}
For post
val request_data = Json.obj(
"provider" -> "CRAIGSLIST",
"accountCode" -> msg.accountCode,
"dateBefore" -> msg.dateBefore.toString(default_date_time_format),
"dateAfter" -> msg.dateAfter.toString(default_date_time_format),
"limit" -> msg.limit,
"offset" -> msg.offset
)
logger.debug("Send json request parameter = " + request_data)
val future:Future[WSResponse] = WS.url(contextio_host + "/jobs/scan").post(request_data)
References:
https://www.playframework.com/documentation/2.4.3/ScalaWS
https://www.playframework.com/documentation/2.4.3/ScalaAkka
akka system
http://sillycat.iteye.com/blog/1767866
http://sillycat.iteye.com/blog/1768625
http://sillycat.iteye.com/blog/1768626
http://sillycat.iteye.com/blog/2099267
http://sillycat.iteye.com/blog/2100232
http://sillycat.iteye.com/blog/2102694
http://sillycat.iteye.com/blog/2175999