I have a service class, and the service have one method getSomethingFromApi , now , I want to have play Configuration instance so I can pull stuff from the application.conf, and a play WSClient so I can perform http calls.
this is how I want my service to look:
class MyApiService {
def getSomethingFromApi(whichApi: String): Future[ApiRes] = {
wsClient.url(configuration.getString(whichApi)).withHttpHeaders(("Content-Type", "application/json")).get.map { res =>
response.status match {
case Status.OK => // do something
case _ => throw new Exception
}
}
}
}
and this is the ServicesModule that is wiring my services:
import com.softwaremill.macwire._
trait ServicesModule {
lazy val myService: MyApiService = wire[MyApiService]
}
my question now is what is the right way of using wiring play Configuration and WSClient instances..? cause currently i need those instances in my service but i dont have them, how should i do this the right way?
thanks
With macwire it'll probably look like this
// MyApiService.scala
class MyApiService(wsClient: WSClient) { ... }
// ServicesModule.scala
trait ServicesModule with NingWSComponents {
lazy val wsClient = wire[WSClient]
lazy val apiService = wire[MyApiService]
}
I haven't tried using macwire with play myself, so I have relatively low confidence that it'll work on the first try, but macwire play example suggests mixing in certain Play modules to provide values needed for WSClient. Most likely not all of them are needed, but some might be - soo I'd suggest starting with just NingWSComponents and gradually adding more until it works.
For the configuration I suggest using something like PureConfig and load the configuration as follows
import pureconfig._
import pureconfig.error.ConfigReaderFailures
case class YourConfClass(name: String, quantity: Int)
val config: Either[pureconfig.error.ConfigReaderFailures,YourConfClass] = loadConfig[YourConfClass]
This then can be passed on to any component of your app using macwire.
As of Play 2.6.X one should use AhcWSComponents that are provided by the ws dependency as follows:
In your build.sbt file add the ws dependency to your project
libraryDependencies += ws
In your module trait mix-in the AhcWSComponents trait and wire the WSClient
trait ServicesModule with AhcWSComponents {
lazy val wsClient = wire[WSClient]
lazy val apiService = wire[MyApiService]
}
In your MyApiService add the WSClient as a param. to the constructor
class MyApiService(wsClient: WSClient) { ... }
And now you're done. This general rule applies to all provided dependencies.
Related
I'm trying to create a simple service to send emails using Lagom framework and the Scaladsl. I'm trying to use the Play Mailer Plugin to handle emails but I'm struggling trying to inject it into the service implementation.
I created the service trait and implementation passing the mailerClient as a dependency in the constructor.
trait MailerService extends Service { ... }
class MailerServiceImpl(mailerClient: MailerClient, persistentEntityRegistry: PersistentEntityRegistry) extends MailerService {
...
}
I'm wiring the service in the ApplicationLoader, following the pattern explained in the Lagom documentation and in the hello world application using macwire.
abstract class MailerApplication(context: LagomApplicationContext)
extends LagomApplication(context)
with CassandraPersistenceComponents
with AhcWSComponents {
override lazy val lagomServer: LagomServer = serverFor[MailerService](wire[MailerServiceImpl])
override lazy val jsonSerializerRegistry = MailerSerializerRegistry
persistentEntityRegistry.register(wire[MailEntity])
}
When I try to compile, I get the following error.
[error]
/.../workspace/mailer/mailer-impl/src/main/scala/com/example/mailer/impl/MailerApplicationLoader.scala:92:
Cannot find a value of type: [play.api.libs.mailer.MailerClient]
I thought macwire would be able to sort out the dependencies from the constructor but it looks like it's not really. I've tried different options, like trying to wire it explicitly in the application loader without any success so far.
I'm pretty sure there's something I'm getting wrong about how DI works in Lagom but I cannot figure it out.
Any ideas?
For the MailerClient to be injectable, you need to mix in the MailerComponents trait along with the other traits you use in your service.
For example:
// ...
import play.api.libs.mailer._
abstract class MailerApplication(context: LagomApplicationContext)
extends LagomApplication(context)
with MailerComponents // add this here
with CassandraPersistenceComponents
with AhcWSComponents {
override lazy val lagomServer: LagomServer = serverFor[MailerService](wire[MailerServiceImpl])
override lazy val jsonSerializerRegistry = MailerSerializerRegistry
persistentEntityRegistry.register(wire[MailEntity])
}
This is described in the Play Mailer documentation on compile-time injection
I am trying to use Play 2.5 dependency injection. I have following class which makes a call to REST api and parses the response
class Client #Inject()(ws:WSClient, baseUrl: string) {
def this(ws:WSClient) = this(ws, "<url string>")
def getResponse() = {....}
....
}
The caller of the code looks like below
var client = new Client(WS.client)
client.getResponse()
I am getting following warning.
object WS in package ws is deprecated: Inject WSClient into your
component
I understand that i need to inject WS.Client instead of passing it explicitly to the Client constructor. But how do i do that?
=== Update ===
I don't want to inject Client or WSClient from the Controller. My controller creates objects and classes at run time and i want those objects to create Client Object. When i explicitly pass WS.client object to the Client object i get the above stated warning.
=== Update 2 ===
I have a plugin architecture in my application. When a a controller starts an action. It does not know what set of plugins it is going to execute. Some plugins would not need a WSClient and some of them would. So i dont want to couple the injection of WSClient into my controller. Each plugin independently decides if it wants to call a remote service. When a plugin decides to call the remote service, it should be able to inject WSClient in what ever client it wants to invoke.
Controller Action --> Determine Plugins to Execute --> Execute Plugins ---> Plugin1 (needs to call a remote api, create a client object, per say new Client(WS.Client)). This is where the injection should happen, not at the controller.
Ok. I will assume you have two classes. First we will have your Client class:
#Singleton // this is not necessary, I put it here so you know this is possible
class Client #Inject() (ws:WSClient, baseUrl: String) {
// Since this controller is not annotated with #Inject
// it WILL NOT be used when binding components
def this(ws:WSClient) = this(ws, "<url string>")
def getResponse() = {
// do something using ws object
}
}
Then you have another class that uses Client, per instance, a controller:
class MyController #Inject() (client: Client) extends Controller {
def someAction = Action {
// do something with client object
}
}
The main point here is that the controller did not need to create a Client instance. It was automatically injected by Guice.
Moreover, your client class needs a baseUrl and there is no place telling Play which value is needed there. If this is a configuration, than you can do something like this:
import play.api.Configuration
class Client #Inject() (ws:WSClient, configuration: Configuration) {
def getResponse() = {
val baseUrl = configuration.getString("key.to.baseUrl")
// do something using ws object and baseUrl
}
}
But, if you really want your Client object to receives a String, then we need to tell Play which String needs to be injected:
package com.acme.modules
import com.google.inject.AbstractModule
import com.google.inject.name.Names
class MyModule extends AbstractModule {
def configure() = {
bind(classOf[String])
.annotatedWith(Names.named("baseUrl")) // attention to the name here. It will be used below
.toInstance("http://api.example.com/")
}
}
And then enable this module by adding the following line to your application.conf:
play.modules.enabled += "com.acme.modules.MyModule"
After that, we will change Client to be specific about which String it is expecting:
import play.api.Configuration
// #Named needs to receive the same value defined at the module class.
class Client #Inject() (ws:WSClient, #Named("baseUrl") baseUrl: String) {
def getResponse() = {
val baseUrl = configuration.getString("key.to.baseUrl")
// do something using ws object and baseUrl
}
}
Update after question edit:
Give the structure you want/need:
Controller Action --> Determine Plugins to Execute --> Execute Plugins ---> Plugin1
Your code can also follow that path with classes like this:
MyController -> PluginResolver -> Plugin
-> PluginRunner ->
And, then, you can have:
Controller:
class MyController #Inject() (
pluginResolver: PluginResolver,
pluginRunner: PluginRunner
) extends Controller {
def action = Action {
val plugins = pluginsResolver.resolve(/* give a criteria to select plugins */)
val someResultFromPluginsExecution = pluginsRunner.run(plugins)
// map result from plugins execution to a play play.api.mvc.Result
// return the play.api.mvc.Result
}
}
Plugin classes:
import play.api.inject.Injector
class PluginResolver #Inject()(injector: Injector) {
def resolve(/* some criteria to resolve plugins */): Seq[Plugin] = {
val pluginsClasses = ... // find the necessary plugins based on the criteria
pluginsClasses.map { pluginClass => injector.instanceOf(pluginClass) }
}
}
// ExecutionContext is not really necessary, but maybe you want/need
// another thread pool to execute plugins
class PluginRunner #Inject()(implicit executionContext: ExecutionContext) {
def run(plugins: Seq[Plugin]): Seq[PluginExecutionResult] = {
// run the plugins
// return the result
}
}
trait Plugin {
def execute(): PluginExecutionResult
}
The real magic here happens at the PluginResolver. It uses a play.api.inject.Injector to create plugins instances and then your plugins can use Dependency Injection. Per instance:
class PluginThatNeedsWSClient #Inject(wsClient: WSClient) extends Plugin {
def execute(): PluginExecutionResult = {
// Use wsClient to call a remote service
// return the execution result
}
}
Reference:
Scala: Dependency Injection
Scala: Play WS API
play.api.inject.Injector
I saw this awesome post last week: http://www.schibsted.pl/2016/04/dependency-injection-play-framework-scala/
I'm new to play framework and have limited experience with scala. Using play framework 2.4 I'm trying to write full integrations tests where I want to call controller, and using in-memory db retrieve data from it. I'm using scalatest-plus together with scalatest. I have read about play unit tests in play documentation and now trying to use play.api.test.FakeApplication but i cannot find a way to inject my guice module to this fake application. I'm ussing OneAppPerSuite trait I can override FakeApplication but cannot pass any guice module for injection. Here is FakeApplication source from:
case class FakeApplication(
override val path: java.io.File = new java.io.File("."),
override val classloader: ClassLoader = classOf[FakeApplication].getClassLoader,
additionalPlugins: Seq[String] = Nil,
withoutPlugins: Seq[String] = Nil,
additionalConfiguration: Map[String, _ <: Any] = Map.empty,
withGlobal: Option[play.api.GlobalSettings] = None,
withRoutes: PartialFunction[(String, String), Handler] = PartialFunction.empty) extends Application {
private val app: Application = new GuiceApplicationBuilder()
.in(Environment(path, classloader, Mode.Test))
.global(withGlobal.orNull)
.configure(additionalConfiguration)
.bindings(
bind[FakePluginsConfig] to FakePluginsConfig(additionalPlugins, withoutPlugins),
bind[FakeRouterConfig] to FakeRouterConfig(withRoutes))
.overrides(
bind[Plugins].toProvider[FakePluginsProvider],
bind[Router].toProvider[FakeRouterProvider])
.build
....
So there is no way for me to pass custom module here.
Here is how my test looks like:
class UsersControllerTest extends PlaySpec with OneAppPerSuite {
override implicit lazy val app = FakeApplication(additionalConfiguration = inMemoryDatabase())
"Application " should {
"work" in {
val resp = route(FakeRequest(GET, "/api/users")).get
}
}
}
And here is my controller I want to test:
class UserController #Inject()
(userService: UserService)
(implicit ec: ExecutionContext) extends Controller {
def get = Action.async { implicit request => {
// implementation
}
}
}
Obviously my test now fail since it cannot find UserService instance.
So my question is:
How to properly write end2end integration tests with play using guice context
Is it common to write such tests in play application, since I cannot find any decent examples or documentation of doing something like that. I used to write such tests in java web applications where integration tests load spring context, but can't find such examples in play.
Thanks in advance.
If you want to test your default configuration, starting FakeApplication, is enough. It loads modules specified in application.conf automatically. So, If it can't find UserService, that means you don't have proper module configurations in your application.conf and there for your server can't serve those requests.
https://www.playframework.com/documentation/2.4.x/ScalaDependencyInjection
If you want to use some Mockups, and override default configurations:
https://www.playframework.com/documentation/2.4.x/ScalaTestingWithGuice
I'm following the advice from com.googlegroups.google-guice http://markmail.org/message/ljnhl6rstverrxuj
Well, it's actually almost as referred to you in the link from the other answer:
http://jonasboner.com/2008/10/06/real-world-scala-dependency-injection-di/
class MyClient {
#Inject val toBeInjected: AnotherClass = toBeInjected // !!
}
trait ServiceInjector {
ServiceInjector.inject( this )
}
object ServiceInjector {
private val injector = Guice.createInjector( Array[Module]( new YourModule ) )
def inject( obj: AnyRef ) = injector.injectMembers( obj )
}
Usage:
val client = new MyClient with ServiceInjector
or:
class InjectedMyClient extends MyClient with ServiceInjector
But I'm very new to Scala and trying to work out how I can use the following pattern for dependency injection, when the Guice Module itself needs references to instances passed in from elsewhere.
But since traits can't have constructors, and neither can the Companion Object it looks like I'm screwed?
package au.id.rleach.overmind.guice
import com.google.inject.{Provides, Guice, Binder, Module}
import org.slf4j.Logger
import org.spongepowered.api.service.ServiceManager
import org.spongepowered.api.world.TeleportHelper
import org.spongepowered.api.{GameRegistry, Game}
import org.spongepowered.api.plugin.PluginManager
import org.spongepowered.api.scoreboard.ScoreboardBuilder
import org.spongepowered.api.service.event.EventManager
class OModule(val game: Game, val logger: Logger, val pluginManager: PluginManager, val serviceManager: ServiceManager, val eventManager: EventManager, val gameRegistry: GameRegistry, val teleportHelper: TeleportHelper) extends Module {
override def configure(binder: Binder): Unit = {
binder.bind(classOf[Game]).toInstance(game)
binder.bind(classOf[Logger]).toInstance(logger)
binder.bind(classOf[PluginManager]).toInstance(pluginManager)
binder.bind(classOf[ServiceManager]).toInstance(serviceManager)
binder.bind(classOf[EventManager]).toInstance(eventManager)
binder.bind(classOf[GameRegistry]).toInstance(gameRegistry)
binder.bind(classOf[TeleportHelper]).toInstance(teleportHelper)
//bind(classOf[File]).annotatedWith(new ConfigDirAnnotation(true)).toInstance(Loader.instance.getConfigDir)
}
}
trait ServiceInjector {
ServiceInjector.inject(this)
}
object ServiceInjector {
private val injector = Guice.createInjector(
//####
new OModule()//compilation error.
//####
)
def inject(obj: AnyRef) = injector.injectMembers(obj)
}
I realize that the object is being initialized when it's first used, and that is after I have a copy of the instances to pass to OModule, but I can't seem to find a way to pass them in to the object.
Since I'm using Scala I am not a fan anymore of using DI frameworks since Scala has natively DI-like support already. This is called the Cake Pattern. There are plenty resources available on this, like this blogpost from Cake Solutions.
Both at ScalaDays 2014 and Devoxx 2014 Dick Wall also presented about a more lightweight DI solution which he called the Parfait Pattern. Both talks can be viewed on Parleys.com
If you really want to use a DI framework, Scaldi is a nice looking Scala framework utilising Scala features, but of course you can also keep on using Spring or Guice.
I'm not sure about this:
#Inject val toBeInjected: AnotherClass = toBeInjected
wouldn't work in my experience. It needs to be a var rather than val and the initial value null.
#Inject var toBeInjected: AnotherClass = null
I created a demo on GitHub which is the Play-Scala template with the index method changed as follows:
class Application extends Controller {
#Inject var ws: WSClient = null
def index = Action.async {
ws.url("http://google.com").get.map(r => Ok(r.body))
}
}
which worked well. That injected to a field, rather than as a constructor parameter. The same technique can be used with traits.
A typical service looks like this:
trait BaseService extends LazyLogging {
def getDb() = {
DatabaseHelper.getDb // database for the scala slick library
}
}
abstract class UserService extends BaseService {
def getById(userId: Int): Option[User]
}
class UserServiceImpl #Inject(val userDao: UserDao) extends UserService = {
def getById(userId: Int): Option[User] = {
getDb().withSession { implicit session =>
return userDao.getById(userId)
}
}
}
Using Guice I wire up my objects like:
class ServiceModule extends ScalaModule {
def configure() {
bind[UserDao].to[UserDaoImpl]
bind[UserService].to[UserServiceImpl]
}
}
Now when I am unit testing using scalatest, I am a bit confused how I can de-couple the database access since I want to mock the database responses.
My spec looks like:
class UserServiceSpec extends UnitSpec with MockitoSugar {
val userService = injector.getInstance(classOf[UserService])
describe("UserServiceSpec") {
it("should do someting") {
val abc = userService.doSomething();
abc.name should be("abc")
}
}
}
My UnitSpec class wires up my Guice.
I am confused, where should I create the mock objects (using mockito) and how should I wire them using Guice? In the ServiceModule or?
My design seems wrong since my BaseService has a connection to the database, I need to refactor that out somehow.
Looking for a way to get out of this bad design I currently seem to have, ideas?
You can move the db connection to DAO layer. Your application should have three layers: controller -> service -> DAO. All the service layer needs to know is functionalities provided by DAO, i.e. CRUD operations; it should not know anything about db connection since that is DAO's responsibility.
I am not really sure about Slick framework, but for Play framework with Guice, it allows to disable "real" bindings (injected dependencies) that you expect when the application is running and enable bindings that are only for testing like this:
implicit override lazy val app = new GuiceApplicationBuilder()
.configure(appConfig)
.disable(classOf[ReactiveMongoModule], classOf[CommonModule])
.bindings(bind(classOf[ReactiveMongoApi]).toInstance(api))
.bindings(TestDaoModule())
.build()
This is a complete integration test (controller layer), hope that it helps: https://github.com/luongbalinh/play-mongo/blob/master/test/controllers/UserControllerTest.scala