My environment is eclipse indigo and I am trying to add a custom dispatcher.
import akka.actor.Actor
import akka.actor.ActorRef
import akka.dispatch.MessageDispatcher
import akka.dispatch._
import akka.event.Logging
import akka.actor.ActorSystem
import akka.actor.Props
import akka.dispatch;
class MyActor extends Actor {
Console.println("World!");
val log = Logging(context.system, this)
def receive = {
case "test" => log.info("received test")
case _ => log.info("received unknown message")
}
}
object Main extends App {
val system = ActorSystem("MySystem")
Console.println("Hello");
val myActor = system.actorOf(Props[MyActor].withDispatcher("my-dispatcher"), name = "myactor")
}
I am following this example. http://doc.akka.io/docs/akka/2.0.2/scala/dispatchers.html
I've already tried adding it to application.config in akka-2.0.2/config/application.config
In your project just put application.conf in the src/main/resources folder. That should work.
Related
Basically I trying to get data from database using akka http. if i pass (EmployeeRepo.findAll()) directly in api then it show all data but while using actor it shows cast error....
here problem for data fetching only please solve it
this is my EmployeeRepo-----------------------------------
package org.repo
import org.data.Employee
import org.db.DbConfig
import org.mongodb.scala.MongoCollection
import org.utils.JsonUtils
import org.mongodb.scala.bson.conversions.Bson
import org.mongodb.scala.model.Filters.equal
import org.mongodb.scala.model.FindOneAndUpdateOptions
import org.mongodb.scala.model.Updates.{combine, set}
import scala.concurrent.Future
object EmployeeRepo extends JsonUtils{
private val employeeDoc:MongoCollection[Employee]=DbConfig.employees
def createCollection()={
DbConfig.database.createCollection("employee").subscribe(
(result)=>println(s"$result"),
e=>println(e.getLocalizedMessage),
()=>println("complete")
)
}
def insertData(emp:Employee)={
employeeDoc.insertOne(emp).toFuture()
}
def findAll()= employeeDoc.find().toFuture()
def update(emp:Employee,id:String)=
employeeDoc
.findOneAndUpdate(equal("_id", id),
setBsonValue(emp),
FindOneAndUpdateOptions().upsert(true)).toFuture()
def delete(id:String)=
employeeDoc.deleteOne(equal("_id",id)).toFuture()
private def setBsonValue(emp:Employee)={
combine(
set("name",emp.name),
set("dateOfBirth",emp.dateOfBirth)
)
}
}
============EMployeeService------------
package org.service
import java.time.LocalDate
import java.time.format.DateTimeFormatter
import java.util.UUID
import akka.NotUsed
import akka.actor.ActorSystem
import akka.stream.ActorMaterializer
import akka.stream.scaladsl.Source
import org.data.Employee
import org.domain.EmployeeRequest
import org.repo.EmployeeRepo
import scala.concurrent.Future
class EmployeeService {
implicit val system=ActorSystem("Employeee")
implicit val materializer=ActorMaterializer
import system.dispatcher
def saveEmployeeData= (employeeRequest:EmployeeRequest) => {
val employeeDoc:Employee=employeeMapperWithNewId(employeeRequest)
EmployeeRepo.insertData(employeeDoc)
}
def findAll={
EmployeeRepo.findAll()
}
def update(employeeRequest: EmployeeRequest,id:String)={
val employeeDoc=employeeMapperWithNewId(employeeRequest)
EmployeeRepo.update(emp = employeeDoc,id)
}
def delete(id:String)=EmployeeRepo.delete(id)
private def employeeMapperWithNewId(employee:EmployeeRequest)={
Employee(name=employee.name,dateOfBirth = LocalDate.parse(employee.dateOfBirth, DateTimeFormatter.ISO_DATE),
_id=UUID.randomUUID.toString)
}
}
-----------------EmployeeActor------
package org.actor
import akka.actor.{Actor, ActorLogging}
import org.actor.EmployeeActor.{Delete, Save, SearchAll, Update}
import org.data.Employee
import org.domain.EmployeeRequest
import org.service.EmployeeService
object EmployeeActor {
case class Save(emp: EmployeeRequest)
case object SearchAll
case class Update(emp: EmployeeRequest, id: String)
case class Delete(id: String)
}
class
EmployeeActor extends Actor with ActorLogging {
private val employeeService: EmployeeService = new EmployeeService()
override def receive: Receive = {
case Save(emp) =>
log.info(s"recevied msg saved with employee :$emp")
sender() ! employeeService.saveEmployeeData(emp)
case SearchAll =>
log.info("received msg find ALL")
sender() ! employeeService.findAll
case Update(emp, id) =>
log.info(s"received update msg for id $id and employee $emp")
sender() ! employeeService.update(emp, id)
case Delete(id) =>
log.info(s"received msg for deleting employee of id $id")
sender() ! employeeService.delete(id)
case _ =>
log.debug("Unhandled msg")
}
}
---------------EmployeeRoute----
package org
import akka.actor.{ActorSystem, Props}
import akka.http.scaladsl.model.{ContentTypes, HttpEntity, StatusCodes}
import akka.stream.ActorMaterializer
import org.actor.EmployeeActor
import org.utils.{JsonUtils, TimeUtils}
import akka.http.scaladsl.server.Directives._
import akka.{NotUsed, util}
import akka.util.{ByteString, Timeout}
import org.data.Employee
import org.domain.EmployeeRequest
import akka.pattern.{Patterns, ask}
import akka.stream.scaladsl.Source
import org.actor.EmployeeActor.{Delete, Save, SearchAll, Update}
import org.service.EmployeeService
import scala.concurrent.duration._
import spray.json._
import scala.concurrent.{Await, Future}
class EmployeeRoute extends JsonUtils{
implicit val system=ActorSystem("Employee")
implicit val materializer=ActorMaterializer
import system.dispatcher
val actor=system.actorOf(Props[EmployeeActor],"employeeActor")
val employeeService=new EmployeeService()
implicit val timeOut=Timeout(5.seconds)
val getRoute={
pathPrefix("employee"){
(pathEndOrSingleSlash & get){
complete((actor ? SearchAll).mapTo[Seq[EmployeeRequest]])
}~
( path("update") & put & parameter("id".as[String])){id=>
entity(as[EmployeeRequest]){employee=>
complete((actor ? Update(employee,id)).map(_=>StatusCodes.OK))
}
}~
post{
entity(as[EmployeeRequest]) { employee =>
complete((actor ? Save(employee)).map(_ => StatusCodes.OK))
}
}~
delete{
(path(Segment) |parameter("id".as[String])){id=>
complete((actor ? Delete(id)).map(_=>StatusCodes.OK))
}
}
}
}
}
=================Error===============
[ERROR] [09/09/2020 19:46:48.551] [web-app-akka.actor.default-dispatcher-4] [akka.actor.ActorSystemImpl(web-app)] Error during processing of request: 'Cannot cast scala.concurrent.impl.Promise$Transformation to scala.collection.immutable.Seq'. Completing with 500 Internal Server Error response. To change default exception handling behavior, provide a custom ExceptionHandler.
java.lang.ClassCastException: Cannot cast scala.concurrent.impl.Promise$Transformation to scala.collection.immutable.Seq
at java.base/java.lang.Class.cast(Class.java:3734)
at scala.concurrent.Future.$anonfun$mapTo$1(Future.scala:464)
at scala.concurrent.impl.Promise$Transformation.run(Promise.scala:430)
at scala.concurrent.ExecutionContext$parasitic$.execute(ExecutionContext.scala:164)
at scala.concurrent.impl.Promise$Transformation.submitWithValue(Promise.scala:392)
at scala.concurrent.impl.Promise$DefaultPromise.submitWithValue(Promise.scala:299)
at scala.concurrent.impl.Promise$DefaultPromise.tryComplete0(Promise.scala:249)
at scala.concurrent.impl.Promise$DefaultPromise.tryComplete(Promise.scala:242)
at akka.pattern.PromiseActorRef.$bang(AskSupport.scala:615)
at org.actor.EmployeeActor$$anonfun$receive$1.applyOrElse(EmployeeActor.scala:32)
at akka.actor.Actor.aroundReceive(Actor.scala:537)
at akka.actor.Actor.aroundReceive$(Actor.scala:535)
at org.actor.EmployeeActor.aroundReceive(EmployeeActor.scala:22)
at akka.actor.ActorCell.receiveMessage(ActorCell.scala:577)
at akka.actor.ActorCell.invoke(ActorCell.scala:547)
at akka.dispatch.Mailbox.processMailbox(Mailbox.scala:270)
at akka.dispatch.Mailbox.run(Mailbox.scala:231)
at akka.dispatch.Mailbox.exec(Mailbox.scala:243)
at java.base/java.util.concurrent.ForkJoinTask.doExec(ForkJoinTask.java:290)
at java.base/java.util.concurrent.ForkJoinPool$WorkQueue.topLevelExec(ForkJoinPool.java:1016)
at java.base/java.util.concurrent.ForkJoinPool.scan(ForkJoinPool.java:1665)
at java.base/java.util.concurrent.ForkJoinPool.runWorker(ForkJoinPool.java:1598)
at java.base/java.util.concurrent.ForkJoinWorkerThread.run(ForkJoinWorkerThread.java:183)
please help me out..
You analyzed almost everything. EmployeeRepo.findAll is your problem. You should not use Futures in akka actors. pipeTo should be used instead.
Please try to update EmployeeActor
case SearchAll =>
log.info("received msg find ALL")
employeeService.findAll.pipeTo(sender())
I'm using a Silhouette v4.0 library with play framework 2.5.
And have been trying to write test code using play specs2.
But, I get the following error with my test class as below.
Error Message
[error] could not find implicit value for parameter env: com.mohiva.play.silhouette.api.Environment[utils.auth.DefaultEnv]
.withAuthenticator[DefaultEnv](identity.loginInfo)
^
Here's the test class
package controllers
import com.google.inject.AbstractModule
import org.joda.time.DateTime
import org.specs2.specification.Scope
import org.specs2.matcher._
import org.specs2.mock._
import play.api.test._
import play.api.libs.json._
import play.api.libs.json.Json
import play.api.libs.json.Reads._
import play.api.libs.functional.syntax._
import play.api.libs.concurrent.Execution.Implicits._
import play.api.libs.mailer.{ MailerClient, Email }
import play.api.inject.guice.GuiceApplicationBuilder
import play.api.inject.bind
import com.mohiva.play.silhouette.test._
import com.mohiva.play.silhouette.api._
import com.mohiva.play.silhouette.api.repositories.AuthInfoRepository
import com.mohiva.play.silhouette.api.util._
import com.mohiva.play.silhouette.impl.providers._
import net.codingwell.scalaguice.ScalaModule
import utils.auth.DefaultEnv
class TestControllerSpec extends PlaySpecification with Mockito {
"case" in new Context {
new WithApplication(application) {
val request = FakeRequest(POST, "/api/test")
.withAuthenticator[DefaultEnv](identity.loginInfo) // <-
val result = route(app, request).get
status(result) must be equalTo OK
}
}
trait Context extends Scope {
val identity = User(
loginInfo = LoginInfo(..)
..
)
implicit val env = FakeEnvironment[DefaultEnv](Seq(identity.loginInfo -> identity))
class FakeModule extends AbstractModule with ScalaModule {
def configure() = {
bind[Environment[DefaultEnv]].toInstance(env)
}
}
lazy val application = new GuiceApplicationBuilder()
.overrides(new FakeModule)
.build
}
}
There are some other test classes similar to this class are properly able to compile and execute.
It's kind of implicit problem with scope..
Therefore, I tried to import all the same as another test class which's able to compile properly. But, still unable to compile.
Missing some import?
As the compiler states, you're missing an implicit value. Use the following, which is modeled after one of Silhouette's specs:
class TestControllerSpec extends PlaySpecification with Mockito {
"the POST request" should {
"return an OK response" in new Context {
new WithApplication(application) {
val identity = User(LoginInfo(...))
implicit val env = FakeEnvironment[DefaultEnv](Seq(identity.loginInfo -> identity))
val request = FakeRequest(POST, "/api/test")
.withAuthenticator(identity.loginInfo)
val result = route(app, request).get
status(result) must be equalTo OK
}
}
}
trait Context extends Scope {
...
}
}
First of all, I'm relatively new to scala.
Consider the following simple class NullLogService which logs results to console.
package services
import akka.actor._
import com.amazonaws.services.simpleemail.model.SendRawEmailResult
import model.Email
import scala.util.Try
object NullLogService {
case class Log(email: Email, amazonResult: Try[_])
case class LogError(email: Email, amazonException: Try[_])
}
class NullLogService extends Actor {
import NullLogService._
def receive = {
case Log(email, amazonResult) => println("Email to: " + email.to + ". AmazonResultSucess:" + amazonResult.isSuccess.toString + ". AmazonResult: " + amazonResult.get.toString)
case LogError(email, amazonException) => println("Error: Email to: " + email.to + ". AmazonException:" + amazonException.get.toString)
case default#_ => println("Default case: "+default.toString)
}
}
It prints Default case: Log(TO:email#email.com,Success({MessageId: Fake_Sent_OK})) always.
I don't know what is happening, because the types are the same (Log(Email,Try(_))) ! It should go to the "Log" case but always fall to the default case!
Driving me crazy.
Code involved
Main class:
import java.io.File
import java.util.concurrent.TimeUnit
import akka.actor.SupervisorStrategy.Resume
import akka.actor._
import akka.routing.RoundRobinPool
import akka.util.Timeout
import com.thenewmotion.akka.rabbitmq._
import com.typesafe.config.{ConfigFactory, ConfigParseOptions}
import model.Email
import services.EmailService.EmailInfo
import services.{EmailService, LogService, NullLogService}
import utils.StringUtils
import scala.concurrent.duration.Duration
object Main extends App {
implicit val system = ActorSystem()
val emailServiceRef: ActorRef = system.actorOf(RoundRobinPool(rate).withSupervisorStrategy(supervisorStrategy).props(Props(new EmailService)), "emailWorkerPool")
val logServiceRef = system.actorOf(RoundRobinPool(1).props(Props(new NullLogService)), "logWorker")
val email = new Email(stringMap.apply("USEREMAIL"), stringMap.apply("REPLYTO"), stringMap.apply("SUBJECT"), stringMap.apply("BODY"), unsubscribeURL)
emailServiceRef ! EmailInfo(email, logServiceRef)
}
EmailService:
package services
import akka.actor._
import com.amazonaws.services.simpleemail.model.SendRawEmailResult
import model.Email
import services.LogService.{Log, LogError}
object EmailService {
case class EmailInfo(email: Email, logServiceRef: ActorRef)
}
class EmailService extends Actor {
import EmailService._
def receive = {
case EmailInfo(email, logServiceRef) =>
val emailResult = new SendRawEmailResult
emailResult.setMessageId("Fake_Sent_OK" )
val amazonResult = Try( emailResult )
logServiceRef ! Log(email, amazonResult)
}
}
Presumably you haven't removed LogService.Log and LogService.LogError, or else you would have gotten a compilation error for EmailService. In this case NullLogService shouldn't define its own messages, but use LogService's ones:
package services
import akka.actor._
import com.amazonaws.services.simpleemail.model.SendRawEmailResult
import model.Email
import scala.util.Try
import LogService.{Log, LogError}
// no object NullLogService unless you need it for something else
class NullLogService extends Actor {
def receive = {
case Log(email, amazonResult) => println("Email to: " + email.to + ". AmazonResultSucess:" + amazonResult.isSuccess.toString + ". AmazonResult: " + amazonResult.get.toString)
case LogError(email, amazonException) => println("Error: Email to: " + email.to + ". AmazonException:" + amazonException.get.toString)
case default#_ => println("Default case: "+default.toString)
}
}
Then you can switch between different services which use the same protocol (say, NullLogService, Slf4jLogService, InMemoryLogService) without changing the imports every time.
Found the error. I had to change the logger class I was importing.
I use NullLogService now, not LogService.
package services
import akka.actor._
import com.amazonaws.services.simpleemail.model.SendRawEmailResult
import model.Email
//Change this --> import services.LogService.{Log, LogError} <---
//To the class you are using --v
import services.NullLogService.{Log, LogError}
object EmailService {
case class EmailInfo(email: Email, logServiceRef: ActorRef)
}
class EmailService extends Actor {
import EmailService._
def receive = {
case EmailInfo(email, logServiceRef) =>
val emailResult = new SendRawEmailResult
emailResult.setMessageId("Fake_Sent_OK" )
val amazonResult = Try( emailResult )
logServiceRef ! Log(email, amazonResult)
}
}
It is weird because there was no runtime errors (like "NullLogService not imported")
I know this question has been asked many times before, however all of them use spray-json. I wanted to use play-json. This might be why the proposed solutions don't solve the problem.
The trait with the route is in a separate file named RestApi.scala. The Http.bindAndHandle which makes use of it is in the file named Main.scala. Both files without irrelevant element removed are presented below. If you want to see the whole file please click at the links above.
RestApi.scala
package restApi
import akka.actor.{ActorSystem, Props}
import akka.http.scaladsl.model.StatusCodes
import akka.http.scaladsl.server.Directives._
import akka.pattern.ask
import akka.stream.ActorMaterializer
import akka.util.Timeout
import scala.concurrent.ExecutionContext
import scala.concurrent.duration._
trait RestApi {
import models._
import cassandraDB.{WriterActor, ReaderActor}
implicit val system: ActorSystem
implicit val materializer: ActorMaterializer
implicit val ec: ExecutionContext
implicit val timeout = Timeout(20 seconds)
val cassandraWriterWorker = system.actorOf(Props[WriterActor], "cassandra-writer-actor")
val cassandraReaderWorker = system.actorOf(Props[ReaderActor], "cassandra-reader-actor")
val route =
pathPrefix("api") {
pathSuffix("contact") {
// the line below has the error
(post & entity(as[Contact])) { contact =>
complete {
cassandraWriterWorker ! contact
StatusCodes.OK
}
}
} ~
pathSuffix("gps"/ "log") {
// an analogous error message is shown in the line below
(post & entity(as[GpsLog])) { gpsLog =>
complete {
cassandraWriterWorker ! gpsLog
StatusCodes.OK
}
}
}
}
}
Main.scala
package initialization
import akka.actor.{ActorSystem, Props}
import akka.http.scaladsl.Http
import akka.stream.ActorMaterializer
import cassandraDB.{ConfigCassandraCluster, ReaderActor, WriterActor}
import gcm.GcmServer
import restApi.RestApi
object Main extends App with ConfigCassandraCluster with RestApi {
override implicit val system = ActorSystem()
override implicit val materializer = ActorMaterializer()
override implicit val ec = system.dispatcher
val write = system.actorOf(Props(new WriterActor(cluster)))
val read = system.actorOf(Props(new ReaderActor(cluster)))
val gcmServer = system.actorOf(Props(new GcmServer(11054...,"AIzaSyCOnVK...")), "gcm-server")
val bindingFuture = Http().bindAndHandle(route, "localhost", 8080)
}
I point out the line where the error occurs in RestApi.scala
Error Message:
Error:(31, 26) could not find implicit value for parameter um: akka.http.scaladsl.unmarshalling.FromRequestUnmarshaller[models.Contact]
(post & entity(as[Contact])) { contact =>
^
The Contact is a data model defined in Contact.scala.
I hope this isn't a duplicate, but I really don't think so. Any help is much appreciated.
It turns out there is an easy solution. Simply add resolvers += Resolver.bintrayRepo("hseeberger", "maven") to your build file and then "de.heikoseeberger" %% "akka-http-play-json" % "1.5.3" to your libraryDependencies and import import de.heikoseeberger.akkahttpplayjson.PlayJsonSupport._ into your code. You can now use play-json like you would use spray-json.
I am trying to replicate the basic example proposed in the Integrating with Akka, Play 2.4 for Scala doc. But I have difficulties in placing the final pieces together...
I have defined the actor (see paragraph Writing actors) at app/actors/HelloActor.scala with the following code:
package actors
import akka.actor._
object HelloActor {
def props = Props[HelloActor]
case class SayHello(name: String)
}
class HelloActor extends Actor {
import HelloActor._
def receive = {
case SayHello(name: String) =>
sender() ! "Hello, " + name
}
}
Then (see Creating and using actors) I suppose I should create a controller at app/controllers/Hello.scala with something like:
package controllers
import play.api.mvc._
import akka.actor._
import javax.inject._
import actors.HelloActor
#Singleton
class Hello #Inject() (system: ActorSystem) extends Controller {
val helloActor = system.actorOf(HelloActor.props, "hello-actor")
...
}
The question: where and how I utilize the code in the following paragraph Asking things of actors to have a working solution? I have tried to add it to the above Hello.scala controller but without success.
import play.api.libs.concurrent.Execution.Implicits.defaultContext
import scala.concurrent.duration._
import akka.pattern.ask
implicit val timeout = 5.seconds
def sayHello(name: String) = Action.async {
(helloActor ? SayHello(name)).mapTo[String].map { message =>
Ok(message)
}
}
Found the solution, I had some problems with defining the implicit timeout, this is the working controller:
package controllers
import play.api.mvc._
import akka.actor._
import javax.inject._
import actors.HelloActor
import actors.HelloActor.SayHello
import play.api.libs.concurrent.Execution.Implicits.defaultContext
import scala.concurrent.duration._
import akka.pattern.ask
import akka.util.Timeout
#Singleton
class Hello #Inject() (system: ActorSystem) extends Controller {
val helloActor = system.actorOf(HelloActor.props, "hello-actor")
implicit val timeout: Timeout = 5.seconds
def sayHello(name: String) = Action.async {
(helloActor ? SayHello(name)).mapTo[String].map { message ⇒
Ok(message)
}
}
}
Plus I added the following route in app/conf/routes:
# Actor test
GET /hello/:name controllers.Hello.sayHello(name)