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
Related
trait paymentTasks{
def calculatePayment()
def getData()
}
class paymentcalculator{
override def calculatePayment() ={
//Implementation }
}
override defGetData() ={
}
}
How do I call the method to keep the code less dependent and don't want to use new keyword
class MyApp{
val payment = new paymentcalculator
//I don't want to instantiate like this wants this to be decoupled
}
Please suggest how do I implement this ,also new Changes in future can be done without changing existing code.Also,suggest if doing this functionally would be more effective
If you want compile-time selection then just put the code into an object
trait PaymentTasks {
def calculatePayment()
def getData()
}
object PaymentCalculator extends PaymentTasks {
def calculatePayment() = ???
def getData() = ???
}
class MyApp {
val payment = PaymentCalculator.calculatePayment()
}
If you want run-time selection than the best way is to use dependency injection and create the payment object at the top level and pass it down to the code that requires it.
object SimplePaymentCalculator extends PaymentTasks
object ComplexPaymentCalculator extends PaymentTasks
class MyApp {
def myImplementation(tasks: PaymentTasks) = {
val payment = tasks.calculatePayment()
}
val payment =
if (???) {
SimplePaymentCalculator
} else {
ComplexPaymentCalculator
}
myImplementation(payment)
}
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.
My app hits many different dbs, which db depends on a query string parameter. I have a DatabaseConfigLocator that takes the string and returns the config and it works great. My problem is I want to make the config for each request available implicitly in my controller. I have tried two approaches.
class MyController extends Controller{
implicit def dbConfig(implicit request: RequestHeader): DatabaseConfig[JdbcProfile] = DatabaseConfigLocator.get[JdbcProfile](request.getQueryString("dbName")
}
This dosn't compile unless I change it to an implicit val with the same type but I need to recheck the query string every request not just once so I don't think an implicit val will work
The other approach was to create an action
object IODBAction extends ActionBuilder[Request]{
def invokeBlock[A](request: Request[A], block: (Request[A]) => Future[Result]) = {
implicit def dbConfig(implicit request: RequestHeader): DatabaseConfig[JdbcProfile] = DatabaseConfigLocator.get[JdbcProfile]("blah")
block(request)
}
}
but that implicit is not available in the scope of the block and I cant find any way to pass it in as an implicit.
My goal is to be able to do something like this
class MyController extends Controller {
def create = {
Action.async {
request =>
ApiResponse {
for {
id <- aProvider.save(validRegistrationRequest.toVisitor)
} yield id
}
}
}
}
class aProvider {
def save(v: Visitor)(implicit dbConfig: DatabaseConfig[JdbcProfile]): ApiResponse[VisitorId]
}
or if the provider can get the implicit at instantiation time all the better
class aProvider(implicit dbConfig: DatabaseConfig[JdbcPRofile]) {
def save(v: Visitor): ApiResponse[VisitorId]
}
Any advice on how to approach this or if its possible with play framework?
Unfortunately, I fear you're stuck with an action receiving a single parameter, so you need to stick to the standard play "Action Composition" pattern. This is documented pretty extensively in the play docs.
I'd define yourself a a "Context" like this:
case class Context(dbConfig: DatabaseConfig[JDBCProfile], request: Request[A])
extends WrappedRequest(request)
Then create a custom action builder like this:
object DBIOAction extends ActionBuilder[Context]{
def invokeBlock[A](request: Request[A], block: (Context[A]) => Future[Result]) = {
val dbConfig = DatabaseConfigLocator.get[JdbcProfile]("blah")
val context = Context(dbConfig, request)
block(context)
}
}
You then should be able to use it like this:
def index = DBIOAction { implicit context =>
// do some stuff. return a result
}
In order to make things simple, I'd pass the implicit context into your service methods, perhaps extracting the dbConfig from the context and passing it the rest of the way down to your DAO's.
class FunService {
def getSomeData(param1: String)(implicit context: Context) = {
// do some work, perhaps using context.dbConfig
}
I'm new to scala and akka and I came across code that looked something like this:
object Foo extends App {
class Bar extends Actor with ActorLogging{
val graph: TitanGraph = _
private def setGraph() = {
graph = TitanFactory.open(...)
}
setGraph()
...
}
def startBar() {
val barSystem = ActorSystem("BarSystem")
for(i <- 0 until numActors) {
val barActor = barSystem.actorOf(Props[Bar], name="BarActor" + i)
...
barActor ! Start
}
}
startBar
}
Does this have any effect on performance as compared to?:
object Foo extends App {
override def main(args: Array[String]): Unit = {
val barSystem = ActorSystem("BarSystem")
for(i <- 0 until numActors) {
val barActor = barSystem.actorOf(Props[Bar], name="BarActor" + i)
...
barActor ! Start
}
}
}
object Bar {
val graph: TitanGraph = _
private def setGraph() = {
graph = TitanFactory.open(...)
}
setGraph()
def props = Props[Bar]
...
}
class Bar extends Actor with ActorLogging{
...
}
In the first case, you're creating multiple instances of the graph but in the second case, I assume that you're using a single instance shared across workers?
Also, I read somewhere that it is good practice to keep the Actor's props factory in the companion object but not sure if the previous code breaks actor encapsulation or if it affects performance at all.
Whether you place an Actor inside an object or outside does not change anything besides the class file name. Concerning sharing a single Actor object instance, that is illegal and will not work, but luckily you cannot easily fall into this trap.
I have a CategoryRepository class which implements several methods such that saves a category to database.
I also have an object Product that contains a list of categories.
What I want to do is to trigger an event that the Product object will listen to, and will update the product itself with the new data of its category.
In C# I know I can use delegates but I don't know if I can do something like this in Scala.
I don't want the CategoryRepository class to know the class Product so I won't call some method in Product that will update it through CategoryRepository.
My CategoryRepository class:
trait CategoryRepositoryComponentImpl extends CategoryRepositoryComponent {
val categoryRepository = new categoryRepositoryImpl
class CategoryRepositoryImpl extends CategoryRepository {
val dbRepository = CategoryDbRepository
def updateAttribute(id:String, request:UpdateCategoryItem): String = {
val cat = dbRepository.get(id)
cat.update(request)
dbRepository.save(cat)
}
}
}
The product repository looks the same as this category's repository.
Now I want to add a line after dbRepository.save(cat) that will trigger an event that will call the updateProduct() function within ProductRepository.
Please give an implementation example.
Thanks.
a (not so) basic implementation for an update channel based on events.
I took care to only generalize to the bare bone, as to give a hint to future evolution and code reuse.
Base infrastructure
We introduce an updates channel
//Listens for updates to A's and notifies interested listeners
class UpdateChannel[A] {
//simplified register
var listenMap: Map[A, Listening[A]] = Map()
//update call
def apply(a: A): Unit = listenMap.get(a).foreach(_.event(a))
//update call
def registerFor(a: value, listen: Listening[A]) = listenMap += (a, listen)
}
and a generic listener interested in corrisponding updates
//Listens to changes for type A
trait Listening[A] {
def event(upd: A): Unit
}
Application
Now we adapt the Repo Component to inject the channel
trait CategoryRepositoryComponentImpl extends CategoryRepositoryComponent {
val categoryRepository = new categoryRepositoryImpl
/************** NEW CODE HERE **************
* define a channel to send category updates
*******************************************/
def updateChannel: UpdateChannel[Category]
class CategoryRepositoryImpl extends CategoryRepository {
val dbRepository = CategoryDbRepository
def updateAttribute(id:String, request:UpdateCategoryItem): String = {
val cat = dbRepository.get(id)
cat.update(request)
dbRepository.save(cat)
//send the update to the channel
updateChannel(cat) //***************** << AND HERE
}
}
}
We also need to enable the product to event listening
//product must be listening to category updates
class Product(val category: Category) extends Listening[Category] {
def event(cat: Category) = ??? //strut your stuff here
...business stuff here too
}
Finally, here we put the ingredients together
//put the pieces together
def wireup() = {
//the channel
val catChan: UpdateChannel[Category] = new UpdateChannel[Category]
//the Repository component wired to the channel
val catRep = new CategoryRepositoryComponentImpl {
val updateChannel = catChan
}
//a nice cat
val myCat: Category = ???
//a nice prod with her nice cat
val p: Product = new Product(myCat)
//prod wants to know what happens to her cat
catChan.registerFor(myCat, p)
}
Remarks
we can make the Product independent of the whole framework by using refinement types
val product = new Product(myCat) with Listening[Category] {
def event(cat: Category) = ??? //strut your stuff here
}
a different solution would be to avoiding all the wirings and simply register a list of update closures in the RepositoryComponent
trait CategoryRepositoryComponentImpl extends CategoryRepositoryComponent {
val categoryRepository = new categoryRepositoryImpl
//public listeners, they should be encapsulated
var categoryUpdates: Seq[Category => Unit]
[...]
def updateAttribute(id:String, request:UpdateCategoryItem): String = {
val cat = dbRepository.get(id)
cat.update(request)
dbRepository.save(cat)
//send the update to the channel
categoryUpdates.foreach(_.apply(cat))
}
}
}
and the product needs only to add his own update function
catRep.categoryUpdates +:= (cat) => p.event(cat)
I think this would be more or less how it's done in C# with events and delegates.
When you define Event like this you'd have to use tuples (Event[(Any,EventArgs)] for instance) if you want to pass more than one parameter to the event listeners.
class Event[Arg] {
type L = Arg => Unit
private val listeners = scala.collection.mutable.ListBuffer.empty[L]
def +=(listener: L) {
listeners.append(listener)
}
def apply(arg: Arg) {
listeners.foreach(_(arg))
}
}
class CategoryRepository {
val event = new Event[String]
def fireEvent(data: String) {
event(data)
}
}
object Product {
def update(data: String) {
println(s"updating: $data")
}
}
object Main extends App {
val repo = new CategoryRepository()
repo.event += Product.update
repo.fireEvent("new data")
}