Scala object Action in package mvc is deprecated - scala

I'm currently working on a new project and face a warning I haven't seen in older projects yet.
The warning says that Action from the mvc package is deprecated since 2.6.0. I guess it has something to do with Play or Scala itself. So this is my code responsible for the warning:
def getLists: Action[AnyContent] = Action.async {
listRepo.getLists.map(lists => Ok(Json.obj("lists" -> lists)))
}
My buildt.sbt looks like this:
And this is how I import the Action object:
import play.api.mvc.{Action, AnyContent, Controller}
Is there an equivalent alternative or am I doing something wrong?

The warning message tells you what to do (more information is in the linked sections of the Play 2.6 migration guide):
Inject an ActionBuilder (e.g. DefaultActionBuilder) or
extend BaseController/AbstractController/InjectedController

Related

How can I log in Play Framework using Scala?

I have updated Play Framework up to 2.7 and I got following warning:
method info in object Logger is deprecated (since 2.7.0): Create an instance of via Logger(...) and use the same-named method. Or use SLF4J directly.
So my questions are:
Should I create an instance of Logger and pass it to each component
when I want to use it?
P.S.:
In project not based on Play Framework, I used to use scala-logging that is a wrapper for SLF4J. Can this be a solution?
Play 2.7 provides a trait for Scala similar to scala-logging. It creates a val which you can use it in you class/object:
import play.api.Logging
class MyClass extends Logging {
// `logger` is automaticaly defined by the `Logging` trait:
logger.info("hello!")
}
So there is another way to log in Play now.

IntelliJ IDEA Scala inspection : import play.api.xxx conflict with com.company.play package

I want to make a helper class at the root of my core project using play-json from typesafe, something like
package com.company
import play.api.libs.json.JsValue
object Helper {
implicit class RichJson(json: JsValue) {
def doStuff() //...
}
}
The problem is that I have somewhere else in the project a package com.company.play
package com.company.play
class Foo() { //...
}
In IntelliJ IDEA 2018.2.4 CE, the line import play.api.libs.json.JsValue is in error with telling me "cannot resolve symbol api" and when Ctrl+Click on the play it goes to the folder containing my Foo.scala file
If I compile the solution with sbt outside of IDEA, there is no problem.
If I put the Helper object in a subpackage (eg com.company.common) there is no error (which also means the dependency is correct in my build.sbt)
I don't understand why IDEA miss this, com.company.play isn't even in the dependencies of the core project. I already tried to invalidate cache, and it doesn't help.
The problem is that Intellij gives precedence to the package play into your project, instead of the package coming from the framework, when resolving the import of JsValue inside com.company scope.
If you really want to keep that name for com.company.play, there is a simple workaround using a fully-qualified import, just prefix like this:
import _root_.play.api.libs.json.JsValue

How do make a `CustomExecutionContext` available for dependency injection in a Play 2.6 controller?

I'm following along with Play 2.6's Scala documentation and sample code for creating non-blocking actions, and am running into some runtime issues. I have created a new Play application using the Scala template (sbt new playframework/play-scala-seed.g8).
The code that the Play documentation suggests should work in a new controller is (this code is taken verbatim from the Play documentation page, with some extra imports from me):
// some imports added by me to get the code to compile
import javax.inject.Inject
import scala.concurrent.ExecutionContext
import scala.concurrent.Future
import akka.actor.ActorSystem
import play.api.libs.concurrent.CustomExecutionContext
import play.api.mvc._
import play.api.mvc.ControllerComponents
// end imports added by me
import play.api.libs.concurrent.CustomExecutionContext
trait MyExecutionContext extends ExecutionContext
class MyExecutionContextImpl #Inject()(system: ActorSystem)
extends CustomExecutionContext(system, "my.executor") with MyExecutionContext
class HomeController #Inject()(myExecutionContext: MyExecutionContext, val controllerComponents: ControllerComponents) extends BaseController {
def index = Action.async {
Future {
// Call some blocking API
Ok("result of blocking call")
}(myExecutionContext)
}
}
Then, according to the documentation for using other thread pools, I've defined the my.executor thread pool in the application.conf file of my application:
my.executor {
fork-join-executor {
parallelism-factor = 20.0
parallelism-max = 200
}
}
I should note that I do not want to use the default execution context as I want to prepare for running futures in a separate context that may be used for a limited resource like a database connection pool.
All of this compiles just fine with sbt compile. However, when I run this with sbt run and access my app in a web browser, I get this error:
CreationException: Unable to create injector, see the following errors:
1) No implementation for controllers.MyExecutionContext was bound.
while locating controllers.MyExecutionContext
for the 1st parameter of controllers.NewController.(NewController.scala:17)
while locating controllers.NewController
for the 2nd parameter of router.Routes.(Routes.scala:29)
at play.api.inject.RoutesProvider$.bindingsFromConfiguration(BuiltinModule.scala:121):
Binding(class router.Routes to self) (via modules: com.google.inject.util.Modules$OverrideModule -> play.api.inject.guice.GuiceableModuleConversions$$anon$1)
I've used Play 2.3 in the past, and know that dependency injection works when you define an instance of an object (via #Singleton or in a module); however, Play 2.6's documentation on DI indicates that "Guice is able to automatically instantiate any class with an #Inject on its constructor without having to explicitly bind it. This feature is called just in time bindings is described in more detail in the Guice documentation."
My question is: what specific lines of code or configuration do I need to add to Play's own sample to make this work, and why?
I found one possible solution when reading further in the Binding Annotations section of the Scala Dependency Injection documentation page. In particular, it states:
The simplest way to bind an implementation to an interface is to use the Guice #ImplementedBy annotation.
So, by adding that to the my MyExecutionContext trait, like so:
import com.google.inject.ImplementedBy
#ImplementedBy(classOf[MyExecutionContextImpl])
trait MyExecutionContext extends ExecutionContext
an instance of the MyExecutionContextImpl is instantiated and properly injected into the controller.
Too bad that this #ImplementedBy annotation isn't listed in the sample code for the non-blocking action documentation!

Play 2.5.X dependency injection

I am upgrading play framework app from 2.4.6 to 2.5.x.
There are several occurrences where I call helper methods which belongs to some object. These helper methods use play's built-in classes(for example play.api.Play.current.configuration.underlying.getString) to get the job done.
I get following warning: "method current in object Play is deprecated: This is a static reference to application, use DI instead"
If I face this problem in class method then I can use dependency injection. How to deal with such a situation where method belongs to object and I am warned to use DI?
Play Framework usually provides a class you can inject instead of using the old static references.
For example, the below would mean you can stop using Play.current.configuration and DB:
import javax.inject.Inject
import play.api.db.Database
import play.api.Configuration
class MyClass #Inject() (configuration: Configuration, db: Database) {
...
}

How do I use an unmanaged dependency in this simple Play example?

I am trying to write a Scala Play web service that returns JSON objects and am having trouble calling a function in a dependency. Can someone tell me what I'm doing wrong in this simplified example?
I have a project called SimpleJSONAPI that consists of the following object.
package com.github.wpm.SimpleJSONAPI
import play.api.libs.json.{JsValue, Json}
object SimpleJSONAPI {
def toJson(s: String): JsValue = Json.toJson(Map("value" -> s))
}
Unit tests confirm that given a string it returns a JSON object of the form {"value":"string"}.
I have a separate Play 2.2.3 Scala project that I created by typing play new PlayJSON. I added the following json action to the controller in the generated application.
package controllers
import play.api.mvc._
import com.github.wpm.SimpleJSONAPI._
object Application extends Controller {
def index = Action {
Ok(views.html.index("Your new application is ready."))
}
def json = {
val j = SimpleJSONAPI.toJson("The JSON API")
Action {
Ok(j)
}
}
}
I also added this route.
GET /json controllers.Application.json
In the root of the PlayJSON project I have a lib directory that contains the simplejsonapi_2.11.jar built by SimpleJSONAPI. This appears to contain the correct code.
> jar tf lib/simplejsonapi_2.11.jar
META-INF/MANIFEST.MF
com/
com/github/
com/github/wpm/
com/github/wpm/SimpleJSONAPI/
com/github/wpm/SimpleJSONAPI/SimpleJSONAPI$.class
com/github/wpm/SimpleJSONAPI/SimpleJSONAPI.class
This compiles, but when I try to connect to localhost:9000/json I get the following runtime error in the line with the val j assignment.
java.lang.NoSuchMethodError: scala.Predef$.ArrowAssoc(L/java/lang/Object;)Ljava/lang/Object;
I've also seen the same error in a unit test that exercises the /json route with a FakeRequest.
If I copy the toJson function from the external dependency into the Play application everything works.
As far as I can tell from the documentation I'm doing everything right, and the error message is opaque. Can someone tell me how to get this to work?
I think your import is incorrect given how you are using the API. Either exclude the object name on the import...
import com.github.wpm.SimpleJSONAPI._
Or change your usage to drop the object name...
val j = toJson("The JSON API")
This was a problem with Scala compiler version compatibility. I compiled my SimpleJSONAPI dependency with Scala 2.11, while the Play app was being built with Scala 2.10. When I changed the SimpleJSONAPI dependency to also build with Scala 2.10, I was able to use it in my Play app.
This was confusing because it's not obvious from the project files which version of Scala a Play app is using, and the error message about ArrowAssoc gives no indication that it is a compiler version issue.