I want to use DI google guice which works perfectly fine in Java but doesnt work in case of scala. Here is my code:
Module:
class ConfigModule extends AbstractModule{
override def configure(): Unit = {
}
#Provides
#Named("config")
def getConfig(): Config = {
new Config
}
}
Config
class Config {
val config1: String = "Sample"
}
Service
class Service(#Named("config") config:Config) {
def read(): Unit = {
println("Reading")
}
}
Main Class
object SampleJob {
def main(args: Array[String]): Unit = {
val injector = Guice.createInjector(new ConfigModule)
val service = injector.getInstance(classOf[Service])
service.read()
}
}
Error:
1) Could not find a suitable constructor in com.job.Service. Classes must have either one (and only one) constructor annotated with #Inject or a zero-argument constructor that is not private.
at com.job.Service.class(Service.scala:7)
while locating com.job.Service
Where am I mistaken?
UPDATE:
class Service(#Inject #Named("config") config:Config) {
def read(): Unit = {
println("Reading")
}
}
This also returns the same error
Could not find a suitable constructor in com.job.Service. Classes must have either one (and only one) constructor annotated with #Inject or a zero-argument constructor that is not private.
at com.job.Service.class(Service.scala:8)
while locating com.job.Service
The error tells you what happened:
Classes must have either one (and only one) constructor annotated with #Inject or a zero-argument constructor that is not private
So you need to add #Inject annotation... on your constructor like in tutorial.
Instead of
class Service(#Inject #Named("config") config:Config) {
def read(): Unit = {
println("Reading")
}
}
it should be
class Service #Inject() (#Named("config") config:Config) {
def read(): Unit = {
println("Reading")
}
}
Related
I want to bind a certain class, lets call it injected either to its real implementation real or to its mock.
Also side effects should happen and therefor a function would be preferable to encapsulate this work.
Doing that outside of a function is known. But that also requires the side effects to be coded multiple times
The functionality iam looking for could be outlined as this (none working approach!)
private def bindMocksOptional(configSettingKey: String, injected: Class[_], real: Class[_] , mock: Class[_]) {
configuration.getOptional[Boolean](configSettingKey) match {
case Some(true) => {
bind(injected).to(mock)
val message = s"Using a mock (${mock.getCanonicalName})for ${injected.getCanonicalName}"
Logger.warn(message)
println(Console.MAGENTA + message)
}
case _ => bind(injected).to(real)
}
}
The function should take the outlined types as parameters, look up some config settings and based on those bind either to mock or real implementation.
You can use Provider:
import com.google.inject.{AbstractModule, Guice, Inject, Provider}
class Configuration {
def getOptional[T](key: String): Option[T] = None
}
trait DatabaseClient
class DatabaseClientMock extends DatabaseClient
class DatabaseClientReal extends DatabaseClient
// ---
// 1. Define Guice Provider:
class DatabaseClientGuiceProvide #Inject()(configuration: Configuration)
extends Provider[DatabaseClient] {
override def get(): DatabaseClient = {
configuration.getOptional[Boolean]("mock") match {
case Some(true) =>
println("used mock")
new DatabaseClientMock
case _ =>
println("used real")
new DatabaseClientReal
}
}
}
class MainModule extends AbstractModule {
override def configure(): Unit = {
// 2. Bind dependencies of provider
bind(classOf[Configuration]).toInstance(new Configuration)
// 3. Bind provider
bind(classOf[DatabaseClient])
.toProvider(classOf[DatabaseClientGuiceProvide])
}
}
// 4. Test it:
object GuiceMain extends App {
val module = Guice.createInjector(new MainModule)
println(module.getInstance(classOf[DatabaseClient]))
}
I have an external library (scala-redis) that requires an implicit ActorSystem when initializing the client. I would like to have my RedisClient as a Singleton inside my Play (2.6) Application because it would make sense having it as a Singleton.
class CustomAppModule(environment: Environment,
configuration: Configuration) extends AbstractModule {
def configure() = {
//val system = getProvider(classOf[ActorSystem]).get()
//val system = ActorSystem()
//bind(classOf[ActorSystem]).toInstance(system)
val redis = RedisClient(configuration.get[String]("redis.host"))(system)
bind(classOf[RedisClient]).toInstance(redis)
}
}
First system fails because of "Provider cannot be used until the Injector has been created", and second system fails because Play Framework initializes the ActorSystem itself when the application starts, and second system fails because of "binding to akka.actor.ActorSystem was already configured at play.api.inject.BuiltinModule".
So what would be the idiomatic way with Guice/DI to proceed with this kind of situation? Do I need a wrapper Singleton that has the RedisClient as a value, and where the ActorSystem can be injected?
I think the provides method will solve your problem. Write your module as
class MyModule extends AbstractModule {
def configure() = {
}
#Provides
#Singleton
def givePrecious() : MyClass = {
new MyClass()
}
}
Here my Class looks like
#Singleton
class MyClass(a: String) {
def this() = {
this("a")
println("constructor called")
}
}
Now I try to create 3 instances of this class
val injector = Guice.createInjector(new MyModule())
val precious1 = injector.getInstance(classOf[MyClass])
val precious2 = injector.getInstance(classOf[MyClass])
val precious3 = injector.getInstance(classOf[MyClass])
You will see that the string "constructor called" is printed only once.
For sake of simplicity I have make a as a string. you can try to make it an instance of ActorSystem.
I am using Guice injections and Finatra with my service.
When trying to build a small test app I am getting this error:
Could not find a suitable constructor in com.twitter.inject.Injector. Classes must have either one (and only one) constructor annotated with #Inject or a zero-argument constructor that is not private.
at com.twitter.inject.Injector.class(Injector.scala:9)
My Module with the injectors looks like this
object ServiceModule extends TwitterModule {
#Provides
#Singleton
def provideS2SAuthServiceConfig(): S2SAuthServiceConfig = {
val servicePath = DynamicProperty.getInstance("myorg.module.auth.servicePath").getString
val serviceUrl = DynamicProperty.getInstance("myorg.module.auth.serviceUrl").getString
val httpClient: Service[Request, Response] = Http.client.withTls(serviceUrl).newService(serviceUrl)
S2SAuthServiceConfig(httpClient, servicePath)
}
#Provides
#Singleton
def provideS2SAuthClient(injector: Injector): S2SAuthClient = {
val s2sAuthClientClass = DynamicProperty.getInstance("myorg.mymodule.s2s.s2sAuthClient").getString
val s2sAuthClientInstance = injector.instance(Class.forName(s2sAuthClientClass))
s2sAuthClientInstance.asInstanceOf[S2SAuthClient]
}
}
It works well when I inject these objects in the constructor of my classes, but I get the error when trying to get an object instance like this:
def main (args: Array[String]): Unit = {
val injector = new Injector(Guice.createInjector(ServiceModule))
val authClient = injector.instance[S2SAuthClientImpl](classOf[S2SAuthClientImpl])
val token = authClient.getToken("MyClientID", "MySecret", "MyScope")
println(token)
}
Any ideas why Guice is not able to find the constructor for the Twitter Injector class?
I am writing Play 2.5 application using Scala. I have following piece of code:
#ImplementedBy(classOf[BarRepositoryImpl])
trait BarRepository {
def bar = //some actions
}
class BarRepositoryImpl extends BarRepository
case class Foo( /*some fields*/) {
#Inject private var barRepository: BarRepository = null
def foo1 = {
val a = barRepository.bar //here barRepository is always null
// some actions with 'a' and returning some result which depends on 'a'
}
}
I also have a controller where I inject BarRepository as well, but through constructor and there everything works well while in the class Foo on the line val a = barRepository.bar I get a NullPointerException. Could someone help to figure out what's the problem? Is it forbidden to use injection in case class?
If you don't want to pollute your case class signature with Guice injected annotation and fields then simply add an implicit dependency on the method that needs it instead:
case class Foo( /*some fields*/) {
def bar1(someField: Int)(implicit barRepository: BarRepository) = {
// some code that interacts with barRepository
}
}
The calling class will have to have the BarRepository as an implicitly injected parameter. E.g. a Play controller like:
#Singleton
class HomeController #Inject()(cc: ControllerComponents)
(implicit barRepository: BarRepository)
extends AbstractController(cc) {
def index() = Action { implicit request =>
val foo = Foo("field")
val bar = foo.bar1
// ...
}
}
I would have assumed that you inject the object in your class signature?
case class Foo #Inject()(barRepository:BarRepository, /* your fields */){
/** some stuff **/
}
I'm trying to figure out how to inject my classes with Google Guice into a play.api.Plugin.
I have implemented Guice to work with my controllers and it works great.
I use:
"com.google.inject" % "guice" % "4.0-beta",
"com.tzavellas" % "sse-guice" % "0.7.1"
When a Controller instance is needed the getControllerInstance method in Global will load the appropriate implementation thanks to the injector.
Global:
object Global extends GlobalSettings {
/**
* Currently we only want to load a different module when test.
*/
private lazy val injector = {
Logger.info("Is Test: "+Play.isTest)
Play.isTest match {
case true => Guice.createInjector(new TestModule)
case false => Guice.createInjector(new CommonModule)
}
}
override def onStart(app: Application) {
Logger.info("Application has started")
}
override def onStop(app: Application) {
Logger.info("Application shutdown...")
}
override def getControllerInstance[A](clazz: Class[A]) = {
Logger.info("getControllerInstance")
injector.getInstance(clazz)
}
}
Common:
package modules
import com.tzavellas.sse.guice.ScalaModule
import services.{CallServiceImpl, CallService}
/**
* User: jakob
* Date: 11/5/13
* Time: 10:04 AM
*/
class CommonModule extends ScalaModule {
def configure() {
bind[CallService].to[CallServiceImpl]
}
}
class TestModule extends ScalaModule {
def configure() {
// Test modules!
}
}
Controller:
#Singleton
class StatsController #Inject()(callService: CallService) extends Controller with securesocial.core.SecureSocial with ProvidesHeader {
def doSomething = {
callService.call()
}
}
Now I would like to inject the same service into my Plugin, but I can't make use of the Global implementation since the plugins do not load with the getControllerInstance
class CallerPlugin (application: Application) extends Plugin {
val secondsToWait = {
import scala.concurrent.duration._
10 seconds
}
val defaultInterval = 60
val intervalKey = "csv.job.interval"
val csvParserEnabled = "csv.job.enabled"
val newDir = "csv.job.new.file.path"
val doneDir = "csv.job.done.file.path"
var cancellable: Option[Cancellable] = None
override def onStop() {
cancellable.map(_.cancel())
}
override def onStart() {
// do some cool injection of callService here!!!
import scala.concurrent.duration._
import play.api.libs.concurrent.Execution.Implicits._
val i = current.configuration.getInt(intervalKey).getOrElse(defaultInterval)
cancellable = if (current.configuration.getBoolean(csvParserEnabled).getOrElse(false)) {
Some(
Akka.system.scheduler.schedule(0 seconds, i minutes) {
callService.call()
})
} else None
}
}
I guess there should be a way of implementing the injection in the onStart method somehow and there is probably some nice easy way of doing this but I can't figure it out.
Thank you!
If I understood your question correctly, you're wondering how to instantiate and use a Guice injector. Well it's really simple:
val injector = Guice.createInjector(new CommonModule)
val callService = injector.getInstance(classOf[CallService])
And like that you have an instance of CallServiceImpl. If you look at your Global.scala, this is exactly what you do there. I haven't used Play plugins, so I'm not sure how you instantiate them, but I think a more idiomatic way would be, instead of putting it in plugin's onStart, to inject this CallService as a parameter to CallerPlugin (like you do for the controller). That way you could pass the responsibility of dependency injection down the tree, so that ideally you would end up with only one injector (probably in Global).
From what I found so far the best way to achieve this is to set plugin's dependency in Global.onStart().
public class Global extends GlobalSettings{
public Injector injector = createInjector();
private Injector createInjector(){
return Guice.createInjector(new SomeGuiceModule());
}
#Override
public void onStart(Application application) {
CallerPlugin plugin = application.plugin(CallerPlugin.class);
plugin.setCallService(injector.getInstance(CallService.class));
}
}
Make sure that plugin number is lower that 10000. Global has 10000, so, your plugin will be started before Global.onStart() gets called.