Reputation: 831
I have play web application with conf/application.conf
(nothing unusual). Guice is used for dependency injection. How can property value be injected in class constructor? The code is below.
class MyController @Inject() (private val foo: Foo) extends Controller {
...
}
@ImplementedBy(classOf[FooImpl])
trait Foo {
def bar: String
}
class FooImpl extends Foo {
override val bar = current.configuration.getString("my.bar").get
...
}
In the current configuration FooImpl
can't be tested without running application. I want to be able instantiate FooImpl
in unit tests. The perfect solution [from my point of view] should look like that:
class FooImpl @Inject() (@Named("my.bar") override val bar: String) extends Foo {
...
}
Unfortunately, this code doesn't work, because Guice doesn't have 'my.bar' binding:
No implementation for java.lang.String annotated with @com.google.inject.name.Named(value=my.bar) was bound.
The only solution that I came up with is writing my own module, which iterates through configuration properties and binds them as named dependencies (a variation of the example from this doc). But I believe that a better approach exists.
Upvotes: 1
Views: 4002
Reputation: 831
I encountered the same problem after about a year, and this time come up with the following solution (very similar to the one proposed by @stranger-in-the-q and @droidman):
class InjectionModule extends AbstractModule {
override def configure(): Unit = {
val config: Config = TypesafeConfigReader.config
config.entrySet().asScala.foreach { entry =>
val path = entry.getKey
entry.getValue.valueType() match {
case ConfigValueType.NUMBER =>
bind(classOf[Int])
.annotatedWith(Names.named(path))
.toInstance(config.getInt(path))
case ConfigValueType.BOOLEAN =>
bind(classOf[Boolean])
.annotatedWith(Names.named(path))
.toInstance(config.getBoolean(path))
case ConfigValueType.STRING =>
bind(classOf[String])
.annotatedWith(Names.named(path))
.toInstance(config.getString(path))
case _ =>
}
}
}
}
Also, this approach can be extended by appending prefixes to system properties (which key-value pairs are part of the loaded config):
private def getPrefix(configValue: ConfigValue): String = {
val description = configValue.origin().description()
if (description.contains("system properties")) {
"sys."
} else {
""
}
}
In this case instead of writing Names.named(path)
one should use Names.named(getPrefix(entry.getValue) + path)
.
Upvotes: 1
Reputation: 540
To inject multiple properties from a play configuration you could do this way. Create a map out of Play configuration and pass it as Properties to Guice binder.
public class Module extends AbstractModule {
private Environment environment;
private Configuration configuration;
public Module(Environment environment,Configuration configuration){
this.environment = environment;
this.configuration = configuration;
}
@Override
public void configure() {
Configuration helloConf = configuration.getConfig("myconfig");
Map<String, Object> map = helloConf.asMap();
Properties properties = new Properties();
properties.putAll(map);
Names.bindProperties(binder(), properties);
}
}
Upvotes: 0
Reputation: 1721
I implemented that using Java. I hope you can use it as reference for your Scala implementation.
At first, I created a Module:
public class MainModule extends AbstractModule {
public final static String TIMEOUT_IN_MILLISECONDS_ANNOTATION = "timeout-promise";
private final Configuration configuration;
public MainModule(@SuppressWarnings("unused") Environment environment, Configuration configuration) {
this.configuration = configuration;
}
@Override
protected void configure() {
long timeoutInMilliseconds = configuration.getLong("application.promise.timeout.in.milliseconds", 0L);
bindConstant().annotatedWith(Names.named(TIMEOUT_IN_MILLISECONDS_ANNOTATION)).to(timeoutInMilliseconds);
}
}
After that, I just used the annotation on different places:
class Service {
@Inject
@Named(MainModule.TIMEOUT_IN_MILLISECONDS_ANNOTATION)
protected long timeoutInMilliseconds;
}
Hope this helps.
Upvotes: 2
Reputation: 3898
Some time ago i was developed small guice extention for simple injection configuration variables mapped on Enum
Upvotes: 1