What is the best way to split routes that need implicit ActorSystem to different files? Suppose I have this (non-working) code
// in UserRoutes.scala
object UserRoutes {
val route: Route = ???
}
// in OrderRoutes.scala
object OrderRoutes {
val route: Route = ???
}
// In MyApp.scala
object MyApp extends App {
implicit val system = ActorSystem(Behaviors.empty, "my-system")
implicit val ec = system.executionContext
val route = UserRoutes.route ~ OrderRoutes.route
Http().newServerAt("localhost", 8888).bind(route)
}
All these routes make HTTP requests so they require the presence of ActorSystem, what is the best way to pass it? Make objects into classes and pass it to constructor or is there something more clever?
You can have something like
// in UserRoutes.scala
object UserRoutes {
def route(implicit sys: ActorSystem): Route = ???
}
// in OrderRoutes.scala
object OrderRoutes {
def route(implicit sys: ActorSystem): Route = ???
}
In your app, you have the actor system implicitly and then you will be able ton keep val route = UserRoutes.route ~ OrderRoutes.route as it is.
I would usually use a class if I have to use some services
class UserRoutes(auth: AuthService, profile: ProfileService)(implicit sys: ActorSystem) {
val route : Route = ???
}
Related
I have a simple Main object, which create routes for my two services this way:
object GameApp extends App {
val config = ConfigFactory.load()
val host = config.getString("http.host")
val port = config.getInt("http.port")
implicit val system = ActorSystem("game-service")
implicit val materializer = ActorMaterializer()
implicit val executionContext = system.dispatcher
val game = new GameRouting
val rate = new RateRouting
val routes: Route = game.route ~ rate.route
Http().bindAndHandle(routes, host, port) map {
binding => println(s"Server start on ${binding.localAddress}")
} recover {
case ex => println(s"Server could not start on ${host}:${port}", ex.getMessage)
}
}
Now, I would like to refactor this code and move this piece into separate method/class/object etc.:
val game = new GameRouting
val rate = new RateRouting
val routes: Route = game.route ~ rate.route
But this classes (GameRouting and RateRouting) uses implicit in constructors and I cannot simply move this code to separate place.
How should I refactor this code to get what I want?
My another question is - routes classes (GameRouting and RateRouting) should be class or case class? This is GameRouting class:
trait Protocols extends SprayJsonSupport with DefaultJsonProtocol {
implicit val gameFormat = jsonFormat4(Game)
}
class GameRouting(implicit executionContext: ExecutionContext) extends Protocols {
val gameService = new GameService
val route: Route = {
pathPrefix("games") {
pathEndOrSingleSlash {
get {
complete {
gameService.getGames()
}
} ~
post {
entity(as[Game]) { gameForCreate =>
createGame(gameForCreate)
}
}
} ~
pathPrefix(LongNumber) { id =>
get {
complete {
gameService.getGame(id)
}
} ~ put {
entity(as[Game]) { gameForUpdate =>
complete {
gameService.update(gameForUpdate)
}
}
}
}
}
}
private def createGame(gameForCreate: Game) = {
onComplete(gameService.createGame(gameForCreate)) {
case Success(created) => complete(StatusCodes.Created, created)
case Failure(exception) => complete(StatusCodes.BadRequest, s"An error: $exception")
}
}
}
And if I make it case I cannot access field routes from Main object. How to fix this or write it in better way? Maybe there are basic questions, but I've been still learning Scala and Akka since few weeks.
You can just move your implicits to separate object:
object GameAppImplicits {
implicit val system = ActorSystem("game-service")
implicit val materializer = ActorMaterializer()
implicit val executionContext = system.dispatcher
}
and then import when needed:
import yourpackage.GameAppImplicits._
For your second question, case classes are usually used for modeling immutable data. In this case you don't really need any feature, that case classes give you (like automatic toString, equals, copy etc.), so I would say it would be best to just use plain class.
If I have a controller named HomeController that receives a request like GET /foo with a header X-Foo: Bar, I would like to create a WS client filter that will read the RequestHeader in the context and copy the header value to the outgoing WS request.
Example Controller:
import play.api.libs.ws.{StandaloneWSRequest, WSClient, WSRequest, WSRequestExecutor, WSRequestFilter}
import play.api.mvc._
import scala.concurrent.ExecutionContext
#Singleton
class HomeController #Inject()(cc: ControllerComponents,
myWsClient: MyWSClient)
(implicit executionContext: ExecutionContext)
extends AbstractController(cc) {
def index = Action.async {
myWsClient.url("http://www.example.com")
.get()
.map(res => Ok(s"${res.status} ${res.statusText}"))(executionContext)
}
}
The wrapper around WSClient that introduces the filter:
#Singleton
class MyWSClient #Inject()(delegate: WSClient, fooBarFilter: FooBarFilter) extends WSClient {
override def underlying[T]: T = delegate.underlying.asInstanceOf[T]
override def url(url: String): WSRequest = {
delegate.url(url)
.withRequestFilter(fooBarFilter)
}
override def close(): Unit = delegate.close()
}
And finally the WS filter itself:
#Singleton
class FooBarFilter extends WSRequestFilter {
override def apply(executor: WSRequestExecutor): WSRequestExecutor = {
(request: StandaloneWSRequest) => {
request.addHttpHeaders(("X-Foo", "<...>")) // INSERT CORRECT VALUE HERE!
executor.apply(request)
}
}
}
In the end, the expectation is that the request GET http://www.example.com contains the header X-Foo: Bar.
The special requirements that make this more interesting are:
You can modify the MyWsClient class.
You can modify the FooBarFilter class
You can create HTTP controller filters (play.api.mvc.(Essential)Filterif it helps.
You can create other classes/objects/etc
You cannot modify the controller (because in our situation, we can't expect all existing controllers to be modified.
The solution should work even if there's a a "service" layer between the controller and the WSClient invocation and doesn't involve passing down objects everywhere.
The solution can alter other Play/Akka mechanisms, like the default Dispatcher
I haven't tried to put it into actual code and test if this works but here is an idea: it looks like since Play 2.1 Http.Context is propagated even across async call. And there is Http.Context._requestHeader. So what you can try to do is to change MyWSClient and FooBarFilter like this:
#Singleton
class MyWSClient #Inject()(delegate: WSClient) extends WSClient {
override def underlying[T]: T = delegate.underlying.asInstanceOf[T]
override def url(url: String): WSRequest = {
val fooHeaderOption = Http.Context.current()._requestHeader().headers.get(FooHeaderFilter.fooHeaderName)
val baseRequest = delegate.url(url)
if (fooHeaderOption.isDefined)
baseRequest.withRequestFilter(new FooHeaderFilter(fooHeaderOption.get))
else
baseRequest
}
override def close(): Unit = delegate.close()
class FooHeaderFilter(headerValue: String) extends WSRequestFilter {
import FooHeaderFilter._
override def apply(executor: WSRequestExecutor): WSRequestExecutor = {
(request: StandaloneWSRequest) => {
request.addHttpHeaders((fooHeaderName, headerValue))
executor.apply(request)
}
}
}
object FooHeaderFilter {
val fooHeaderName = "X-Foo"
}
}
The idea is simple: extract the header from the Http.Context.current() when WSRequest is created and attach it to the request using a WSRequestFilter
Update: make it work in Scala API
As it was pointed out in the comment, this approach doesn't work in Scala API because Http.Context is not initialized and is not passed between threads. To make it work a higher level magic is required. Namely you need:
Easy: A Filter that will init Http.Context for Scala-handled requests
Hard: Override ExecutorServiceConfigurator for Akka's default dispatcher to create a custom ExecutorService that will pass Http.Context between thread switches.
The filter is trivial:
import play.mvc._
#Singleton
class HttpContextFilter #Inject()(implicit ec: ExecutionContext) extends EssentialFilter {
override def apply(next: EssentialAction) = EssentialAction { request => {
Http.Context.current.set(new Http.Context(new Http.RequestImpl(request), null))
next(request)
}
}
}
And the add it to the play.filters.enabled in the application.conf
The hard part is something like this:
class HttpContextWrapperExecutorService(val delegateEc: ExecutorService) extends AbstractExecutorService {
override def isTerminated = delegateEc.isTerminated
override def awaitTermination(timeout: Long, unit: TimeUnit) = delegateEc.awaitTermination(timeout, unit)
override def shutdownNow() = delegateEc.shutdownNow()
override def shutdown() = delegateEc.shutdown()
override def isShutdown = delegateEc.isShutdown
override def execute(command: Runnable) = {
val newContext = Http.Context.current.get()
delegateEc.execute(() => {
val oldContext = Http.Context.current.get() // might be null!
Http.Context.current.set(newContext)
try {
command.run()
}
finally {
Http.Context.current.set(oldContext)
}
})
}
}
class HttpContextExecutorServiceConfigurator(config: Config, prerequisites: DispatcherPrerequisites) extends ExecutorServiceConfigurator(config, prerequisites) {
val delegateProvider = new ForkJoinExecutorConfigurator(config.getConfig("fork-join-executor"), prerequisites)
override def createExecutorServiceFactory(id: String, threadFactory: ThreadFactory): ExecutorServiceFactory = new ExecutorServiceFactory {
val delegateFactory = delegateProvider.createExecutorServiceFactory(id, threadFactory)
override def createExecutorService: ExecutorService = new HttpContextWrapperExecutorService(delegateFactory.createExecutorService)
}
}
and register at using
akka.actor.default-dispatcher.executor = "so.HttpContextExecutorServiceConfigurator"
Don't forget to update the "so" with you real package. Also if you use more custom executors or ExecutionContexts, you should patch (wrap) them as well to pass Http.Context along the asynchronous calls.
I am trying some example in Play Framework (Scala). I'm injecting an actor into controller.
Configuration
Java (1.8.0_144)
Scala (2.12.3)
Play (2.6.5)
I have following code:
ExampleController
class ExampleController #Inject() (
controllerComponents: ControllerComponents,
#Named("injected-router") injectedRouterActor: ActorRef,
#Named("injected") injectedActor: ActorRef)
extends AbstractController(controllerComponents) {
def alive = Action {
injectedActor ! "Hi from Example"
injectedRouterActor ! "Hi From Example to Router"
Ok("Alive")
}
}
InjectedActor
class InjectedActor extends Actor {
val name = s"IA-${self.path}-${System.nanoTime}"
println(s"constructor: $name")
def receive = {
case m => println(s"$name received: $m")
}
override def preStart() = {
println(s"preStart: $name")
super.preStart()
}
}
InjectedRouterActor
class InjectedRouterActor extends Actor {
val name = s"IRA-${self.path}-${System.nanoTime}"
println(s"constructor: $name")
def receive = {
case m => println(s"$name received: $m")
}
override def preStart() = {
println(s"preStart: $name")
super.preStart()
}
}
ChildActor
class ChildActor extends Actor {
val name = s"CH-${self.path}-${System.nanoTime}"
println(s"constructor: $name")
def receive = {
case m => println(s"$name received: $m")
}
}
Module
class BindingModule extends AbstractModule with AkkaGuiceSupport {
def configure = {
bindActor[InjectedActor]("injected")
bindActor[InjectedRouterActor]("injected-router", _ => RoundRobinPool(5).props(Props[ChildActor]))
}
}
When I run this, and hit route for alive, I see printlns from all actors visible on console, except for InjectedRouterActor.
Unable to understand why? Any help is appreciated.
Thanks
In Akka routers are special implementation actors that you don't explicitly implement.
In your example RoundRobinPool(5).props(Props[ChildActor]) creates Props that would create round robin pool router with routees being actors of type ChildActor. You don't really need InjectedRouterActor. You might still want to have it if you want to create router based on configuration (see examples in Akka docs).
If you look at the source of ActorRefProvider that creates actors when you use Guice helper
class ActorRefProvider[T <: Actor: ClassTag](name: String, props: Props => Props) extends Provider[ActorRef] {
#Inject private var actorSystem: ActorSystem = _
#Inject private var injector: Injector = _
lazy val get = {
val creation = Props(injector.instanceOf[T])
actorSystem.actorOf(props(creation), name)
}
}
you can see that it creates default Props that gets an instance of InjectedRouterActor (the type informations is passed as T type parameter) from the injector but, since you provide props as a function where the function argument is ignored (_ => RoundRobinPool(5).props(Props[ChildActor])), it will just ignore creation variable. Also injector.instanceOf[T] is passed to Props#apply as by-name parameter so it is not called right away, so your InjectedRouterActor is not created.
Instead of creating binding for the router actor in configure you can create it manually:
#Provides
#Named("injected-router")
#Singleton
def routerProvider(system: ActorSystem): ActorRef = {
system.actorOf(RoundRobinPool(5).props(Props[ChildActor]), name = "injected-router")
}
I'm using Akka in my project and pull config values in my MainActor class. I want to be able to use commit, version, author tag inside of another file in order to build an avro response, but I can't just simply make MainActor the parent class of my Avro response interface. Is there a workaround?
My MainActor class
class MainActor extends Actor with ActorLogging with ConfigComponent with ExecutionContextComponent with DatabaseComponent with DefaultCustomerProfiles {
override lazy val config: Config = context.system.settings.config
override implicit lazy val executionContext: ExecutionContext = context.dispatcher
override val db: Database = Database.fromConfig(config.getConfig("com.ojolabs.customer-profile.database"))
private val avroServer = context.watch {
val binding = ReflectiveBinding[CustomerService.Async](customerProfileManager)
val host = config.getString("com.ojolabs.customer-profile.avro.bindAddress")
val port = config.getInt("com.ojolabs.customer-profile.avro.port")
context.actorOf(AvroServer.socketServer(binding, host, port))
}
val commit = config.getString("com.ojolabs.customer-profile.version.commit")
val author = config.getString("com.ojolabs.customer-profile.version.author")
val tag = config.getString("com.ojolabs.customer-profile.version.tag")
val buildId = config.getString("com.ojolabs.customer-profile.version.buildId")
override def postStop(): Unit = {
db.close()
super.postStop()
}
//This toplevel actor does nothing by default
override def receive: Receive = Actor.emptyBehavior
}
The class I want to pull values into
trait DefaultCustomerProfiles extends CustomerProfilesComponent {
self: DatabaseComponent with ExecutionContextComponent =>
lazy val customerProfileManager = new CustomerService.Async {
import db.api._
override def customerById(id: String): Future[AvroCustomer] = {
db.run(Customers.byId(UUID.fromString(id)).result.headOption)
.map(_.map(AvroConverters.toAvroCustomer).orNull)
}
override def customerByPhone(phoneNumber: String): Future[AvroCustomer] = {
db.run(Customers.byPhoneNumber(phoneNumber).result.headOption)
.map(_.map(AvroConverters.toAvroCustomer).orNull)
}
override def findOrCreate(phoneNumber: String, creationReason: String): Future[AvroCustomer] = {
db.run(Customers.findOrCreate(phoneNumber, creationReason)).map(AvroConverters.toAvroCustomer)
}
override def createEvent(customerId: String, eventType: String, version: Double, data: String, metadata: String): Future[AvroCustomerEvent] = {
val action = CustomerEvents.create(
UUID.fromString(customerId),
eventType,
Json.parse(data),
version,
Json.parse(metadata)
)
db.run(action).map(AvroConverters.toAvroEvent)
}
override def getVersion() : Version = {
}
}
Create another trait that defines the values, and mix it in with your MainActor and DefaultCustomerProfiles traits.
trait AnvroConfig {
self: ConfigComponent
val commit = config.getString("com.ojolabs.customer-profile.version.commit")
val author = config.getString("com.ojolabs.customer-profile.version.author")
val tag = config.getString("com.ojolabs.customer-profile.version.tag")
val buildId = config.getString("com.ojolabs.customer-profile.version.buildId")
}
I think what you really need is an Akka Extension, which enables you to add features, like custom config, to your Akka system in an elegant way. This way, you would have access to those config values within all your actors from the actor system. As an example, check out this nice blog post.
As for the other class from your example, you should pass them as parameters - it should be concerned with retrieving and parsing the config itself.
I'm setting up a Play! app for our API. This API encapsulates different services. I want to inject these services inside an action but only the ones required for that particular endpoint. Something like:
object Application extends Controller {
def index = (UsersAction andThen OrdersAction) {
// boom UsersService and OrdersService is available here
for {
users <- usersService.list
orders <- ordersService.list
} yield "whatever"
}
}
I've been playing with this idea and using ActionTransformers I'm able to transform the incoming Request to a request that has a given service, but I don't see how I can make that generic enough so I can compose these actions in an arbitrary order without create ActionTransformers for all the possible combinations of WrapperRequests.
Maybe action composition is not the best way to achieve this. I'm all ears.
Thank you
UPDATE:
To clarify, the code above is pseudocode, the ideal scenario, in which usersService and ordersService are made available to that scope (implicits? I don't know). If that's not possible, then whatever adds the less amount of noise of top of that sample that would work. Thanks
The closest I could get to your question is this:
def index =
new UsersAction with OrdersAction {
def body =
for {
users <- userService.list
orders <- orderService.list
} yield Ok("whatever")
}
The implementation is quite straight forward
trait CustomAction extends Action[AnyContent] {
def body: Future[Result]
def apply(request: Request[AnyContent]): Future[Result] = body
val parser = BodyParsers.parse.anyContent
}
trait UsersAction extends CustomAction {
val userService: UserService = ???
}
trait OrdersAction extends CustomAction {
val orderService: OrderService = ???
}
These are the other parts I used to get it to compile:
trait User
trait Order
trait UserService {
def list: Future[Seq[User]]
}
trait OrderService {
def list: Future[Seq[Order]]
}
You can inject by guice, spring or what you want.
Example for guice.
Just change the object to class:
class Application #Inject(userAction:UsersAction,ordersAction:OrdersAction) extends Controller {
def index = (UsersAction andThen OrdersAction) {
// boom UsersService and OrdersService is available here
for {
users <- usersService.list
orders <- ordersService.list
} yield "whatever"
}
}
You have to override in Global:
object Global extends GlobalSettings{
private lazy val injector = Guice.createInjector(new CommonModule)
override def getControllerInstance[A](clazz: Class[A]) = {
injector.getInstance(clazz)
}
}
class CommonModule extends AbstractModule{
protected def configure() {
bind(classOf[UsersAction]).to(classOf[UsersActionImpl])
bind(classOf[OrdersAction]).to(classOf[OrdersActionImpl])
}
}
In route file add # to controllers:
GET /service #controllers.Application.index