Scala : How to use Global Config case class across application - scala

I am new to scala, just started with my scala first application.
I have defined my config file under the resources folder, application.conf
projectname{
"application" {
"host":127.0.0.1
"port":8080
}
}
I have wrote one config parser file to parse from config file to case class
case class AppConfig (config: Config) {
val host = config.getString("projectname.application.host")
val port = config.getInt("projectname.application.port")
}
In my grpc server file, i have declared config as
val config = AppConfig(ConfigFactory.load("application.conf"))
I want to use this config variable across application, rather than loading application.conf file everytime.
I want to have one bootstrap function which will parse this config one time, making it available across application

You can do this automatically with PureConfig.
Add Pure Config to you build.sbt with:
libraryDependencies += "com.github.pureconfig" %% "pureconfig" % "0.11.0"
and reload the sbt shell and update your dependencies.
Now, let's say you have the following resource.conf file:
host: example.com
port: 80
user: admin
password: admin_password
You can define a case class called AppConfig:
case class AppConfig(
host: String,
port: Int,
user: String,
password: String
)
And create an instance of it, populated with the application config, using the loadConfig method:
import pureconfig.generic.auto._
val errorsOrConfig: Either[ConfigReaderFailures, AppConfig] = pureconfig.loadConfig[AppConfig]
This returns Either an error or your AppConfig, depending on the values in the config itself.
For example, if the value of port above will be eighty, instead of 80, you will get a detailed error, saying that the second config line (with the port: eighty) contained a string, but the only valid expected type is a number:
ConfigReaderFailures(
ConvertFailure(
reason = WrongType(
foundType = STRING,
expectedTypes = Set(NUMBER)
),
location = Some(
ConfigValueLocation(
new URL("file:~/repos/example-project/target/scala-2.12/classes/application.conf"),
lineNumber = 2
)
),
path = "port"
)
)
You can use loadConfigOrThrow if you want to get AppConfig instead of an Either.
After you load this config once at the start of your application (as close as possible to your main function), you can use dependency injection to pass it along to all the other classes, simply by passing the AppConfig in the constructor.
If you would like to wire up your Logic class (and other services) with the config case class using MacWire, as Krzysztof suggested in one of his options, you can see my answer here.
The plain example (without MacWire), looks like this:
package com.example
import com.example.config.AppConfig
object HelloWorld extends App {
val config: AppConfig = pureconfig.loadConfigOrThrow[AppConfig]
val logic = new Logic(config)
}
class Logic(config: AppConfig) {
// do something with config
}
Where the AppConfig is defined in AppConfig.scala
package com.example.config
case class AppConfig(
host: String,
port: Int,
user: String,
password: String
)
As a bonus, when you use this config variable in your IDE, you will get code completion.
Moreover, your config may be built from the supported types, such as String, Boolean, Int, etc, but also from other case classes that are build from the supported types (this is since a case class represents a value object, that contains data), as well as lists and options of supported types.
This allows you to "class up" a complicated config file and get code completion. For instance, in application.conf:
name: hotels_best_dishes
host: "https://example.com"
port: 80
hotels: [
"Club Hotel Lutraky Greece",
"Four Seasons",
"Ritz",
"Waldorf Astoria"
]
min-duration: 2 days
currency-by-location {
us = usd
england = gbp
il = nis
}
accepted-currency: [usd, gbp, nis]
application-id: 00112233-4455-6677-8899-aabbccddeeff
ssh-directory: /home/whoever/.ssh
developer: {
name: alice,
age: 20
}
Then define a config case class in your code:
import java.net.URL
import java.util.UUID
import scala.concurrent.duration.FiniteDuration
import pureconfig.generic.EnumCoproductHint
import pureconfig.generic.auto._
case class Person(name: String, age: Int)
sealed trait Currency
case object Usd extends Currency
case object Gbp extends Currency
case object Nis extends Currency
object Currency {
implicit val currencyHint: EnumCoproductHint[Currency] = new EnumCoproductHint[Currency]
}
case class Config(
name: String,
host: URL,
port: Int,
hotels: List[String],
minDuration: FiniteDuration,
currencyByLocation: Map[String, Currency],
acceptedCurrency: List[Currency],
applicationId: UUID,
sshDirectory: java.nio.file.Path,
developer: Person
)
And load it with:
val config: Config = pureconfig.loadConfigOrThrow[Config]

There are some possibilities to handle your problem:
Use runtime dependency injection framework like guice. You can use extension for scala.
Use implicits to handle it. You just need to create an object, which will hold your implicit config:
object Implicits {
implicit val config = AppConfig(ConfigFactory.load("application.conf"))
}
And then you can just add implicit config: Config to your arguments list when you need it:
def process(n: Int)(implicit val config: Config) = ??? //as method parameter
case class Processor(n: Int)(implicit val config: AppConfig) //or as class field
And use it like:
import Implicits._
process(5) //config passed implicitly here
Processor(10) //and here
A great advantage of it is you can pass config manually for tests:
process(5)(config)
The downside of this approach is, that having a lot of implicit resolution in your app, will make compilation slow, but it shouldn't be a problem if your app is not humongous.
Make config a field of your classes (it is called constructor injection).
class Foo(config: Config).
Then you can wire-up your dependencies manually, like:
val config: AppConfig = AppConfig()
val foo = Foo(config) //you need to pass config manually to constructors in your object graph
or you can use a framework which can automate it for you, like macwire:
val config = wire[AppConfig]
val foo = wire[Foo]
You can use a pattern called cake-pattern. It works fine for small-sized applications, but the bigger your app is, the clunkier this approach gets.
What is NOT a good approach is using global singleton like this:
object ConfigHolder {
val Config: AppConfig = ???
}
And then use it like:
def process(n: Int) = {
val host = ConfigHolder.Config.host // anti-pattern
}
It is bad because it makes mocking your config for tests very difficult and the whole testing process becomes clunky.
In my opinion, if your app is not very big, you should use implicits.
If you want to learn more on this topic, check this great guide.

You should define the fields as parameters of you case class.
final case class AppConfig(host: String, port: Int)
Then you overload the apply method of your companion object
object AppConfig {
def apply(config: Config): AppConfig = {
val host = config.getString("projectname.application.host")
val port = config.getInt("projectname.application.port")
AppConfig(host, port)
}
}
However the simplest way to process configuration with case classes is to use pureconfig.

I want to use this config variable across application, rather than loading application.conf file everytime.
Just put it in an object, like
object MyConfig {
lazy val config = AppConfig(ConfigFactory.load("application.conf"))
}
I want to have one bootstrap function which will parse this config one time, making it available across application
As soon as you call MyConfig.config it is loaded just once - as object is a Singleton. So no special bootstrap is needed.

The pattern you're trying to achieve is called Dependency Injection. From Martin Fowler's post on this topic
The basic idea of the Dependency Injection is to have a separate object, an assembler, that populates a field in the lister class with an appropriate implementation for the finder interface.
Register this configuration instance in a Dependency Injection tool like Guice.
class AppModule(conf: AppConfiguration) extends AbstractModule {
override def configure(): Unit = {
bind(classOf[AppConfiguration]).toInstance(conf)
}
}
....
// somewhere in the code
import com.google.inject.Inject
class FooClass #Inject() (config: AppConfiguration)

Related

How to inject correctly the play.api.Configuration inside an object in play framework2.5?

I am upgrading to play framework 2.5, I have objects that are very hard to turn them to classes in order to use dependency injection, so I used this method instead:
object test {
#Inject var config: Configuration = _
def portNumber = config.getInt("server.port")
}
However on runTime i got null pointer exception, the old code used to be like this :
object test {
def portNumber = Play.configuration.getInt("server.port")
}
but it is deperecated and I must change it with DI.
and another question on the fly is it possible to the same if I have got a trait instead of an object
Another way to do is
import com.typesafe.config.ConfigFactory
val restConfig = ConfigFactory.load("rest.conf") //your conf file
val pageSize = restConfig.getInt("pagesize") //the value you want from conf file
You could set the configuration in a Singleton, like:
#Singleton
class ConfigForTest #Inject()(config: Configuration) {
test.config = config
}
And set from here config in the test Object.
So your test object looks like this:
object test {
var config: Configuration = _
def portNumber = config.getInt("server.port")
}
Don't forget to initialise the Singleton in your Module:
class Module
extends AbstractModule {
#Override()
override def configure(): Unit = {
bind(classOf[ConfigForTest])
.asEagerSingleton()
...
Or as Shweta shows, do it without any Injection. As you have a Play app, this would be enough:
import com.typesafe.config.ConfigFactory
object test {
val portNumber = ConfigFactory.load().getInt("server.port")
}
This takes application.conf direct from the Classpath.

ScalaTest : inject implicit variable

I am from Java background and I am trying to write UnitTests using Scala.
My class is as follows :
import com.softwaremill.sttp.{HttpURLConnectionBackend, Uri, sttp}
class MyClient(endpoint: String, principal: String) {
private implicit val serialization = org.json4s.native.Serialization
private implicit val backend = HttpURLConnectionBackend()
def getDataSet(id: String) : Option[DataSet] = {
//sttp.get(url).send <-- will use 'bakend'
}
}
here the implicit variable 'backend' is used to plugin the HTTP Client implementation.
In the UnitTest I am supposed to plugin the SttpBackendStub .
implicit val testingBackend = SttpBackendStub.synchronous
.whenRequestMatches(_.uri.path.startsWith("/dataSet"))
.thenRespond(dataSetJson)
val client = new MyClient("http://dummy", "dummy")
However when I initiate MyClient instance, it will still use HttpURLConnectionBackend instead of SttpBackendStub
Is there workaround to ingest 'testingBackend' into the MyClient during testing ?
The use of implicit here is making you think the problem is more complicated than it is. You're instantiating HttpURLConnectionBackend directly in MyClient so that's the "backend" you're going to get. If you want to use a different one, you'll have to pass it in to MyClient. You can give it a default value for production use but pass in a mock when you instantiate it in your test.
class MyClient(endpoint: String, principal: String,
implicit val backend: BackendInterface = HttpURLConnectionBackend) {
private implicit val serialization = org.json4s.native.Serialization
def getDataSet(id: String) : Option[DataSet] = {
//sttp.get(url).send <-- will use 'bakend'
}
}
And in your test:
val testingBackend = SttpBackendStub.synchronous
.whenRequestMatches(_.uri.path.startsWith("/dataSet"))
.thenRespond(dataSetJson)
val client = new MyClient("http://dummy", "dummy", testingBackend)

Where to put my database access methods when using a DAO with Slick 2.0?

(This question is based on a very similar previous request for help. With the introduction of a DAO and multiple database drivers, the same problem requires a different approach, and I hope warrants a new SO question.)
I have a class and Slick Table defined like this:
import play.api.db.slick.Profile
case class Foo(title: String, description: String, id: Int = 0)
trait FooComponent extends Profile { this: Profile =>
import profile.simple._
class FooTable(tag: Tag) extends Table[Foo](tag, "FOO") {
def id = column[Int]("ID", O.PrimaryKey, O.AutoInc)
def title = column[String]("TITLE", O.NotNull)
def description = column[String]("DESCRIPTION")
def * = (title, description, id) <> (Foo.tupled, Foo.unapply)
}
}
And a data access object:
class DAO(override val profile: JdbcProfile) extends FooComponent with Profile {
val foos = TableQuery[FooTable]
}
object current {
val dao = new DAO(DB(play.api.Play.current).driver)
}
This is pretty awesome, because now I can add something like the following to my application.conf:
db.default.driver=org.h2.Driver
db.default.url="jdbc:h2:mem:play"
db.test.driver=org.postgresql.Driver
db.test.user="testuser"
db.test.password=""
db.test.url="jdbc:postgresql:testdb"
... and if I do the following in a Controller:
import models.current.dao._
import models.current.dao.profile.simple._
I have access to my foos TableQuery, and it automagically gets the driver and database url given for db.default in application.conf.
In a similar, but not-quite-as-nice way, I can do the following in my test Specification:
"test Foos" in new WithApplication() {
val dao = new DAO(play.api.db.slick.DB("test").driver)
import dao._ //import all our database Tables
import dao.profile.simple._ //import specific database methods
play.api.db.slick.DB("test").withSession { implicit s: Session =>
println(s.conn.getMetaData.getURL)
println(foos.list)
}
However, what if I want to define a method which can act on a TableQuery[Foo]? Something like this:
def findByTitle(title: String) = foos.filter(_.id === id).list
Problem
What's the correct way of writing the findByTitle method, and where should I put it so that I can:
Call it in a way such that it won't collide with a method of the same name which acts on TableQuery[Bar]. Coming from OO, I feel like I want to do something like foos.findByTitle("someFoo"), but if there's a better way of doing this functional-style, I'm open to suggestions.
Call it from an application Controller such that the query will work with my db.default h2 driver, and from my test Specification so that it will work with my db.test postgres driver.
As an aside, if I can put this in my DAO:
object current {
val dao = new DAO(DB(play.api.Play.current).driver)
}
and then import models.dao.current._ anywhere I want to use this DAO, how can I extend the same form to the following:
object test {
val dao = new DAO(play.api.db.slick.DB("test").driver)
}
If I try to do this, the compiler complains about not having an implicit Application in scope.
I think you need to read up in implicit conversion and implicit parameters in Scala. There are online Scala books available.
When you get an error message about a missing implicit it either means you ran into a failing type-check provided by a library preventing you from doing something wrong, but that's not the case here. Or you simply forgot to make the implicit available. There are two ways to make an implicit available. Either import it into the scope where you get the error message. Or basically defer the lookup to the callsite of your method. Not sure which one is the right one for play. You either need to import the implicit Application from play, or you need turn val dao into a method and request an implicit application in an implicit argument list def dao(implicit app: Application) = .... You can alternatively turn test into a class and request it there.
If you use the play slick plugin it will need a started play application to be able to call code that uses the DB access from that plugin, you can make sure to start a play app in your tests using WithApplication as described in the docs: http://www.playframework.com/documentation/2.3.x/ScalaFunctionalTestingWithSpecs2

Scala trait mixins for an object/ typesafe Config

This is an OO design Q. Im using typesafe Config in my App. The Config interface is very useful, however there are a couple of fields in my applications; config file that are mandatory. What I wanted to do was create a subInterface of Config and add these 2 top-level methods . Something like this
trait AppConfig extends Config{
def param1:String
def param2:String
}
However creating a real instance of AppConfig given an instance of Config doesnt seem feasible.( I dont want to create wrapper object and duplicate all the methods on the Config interface) . Ideally , Im looking for something that would achieve something close to this
val conf:Config = //get config object from somewhere
return conf with AppConfig { overrider def param1 = {"blah"} }
I do understand the last line is not valid . But Im looking for a pattern/construct with an equivalent functionality.
We've been using Configrity for things like this. Here's an example:
We keep our compiled defaults that we use for unit test/etc in objects
object DefaultConfigs {
val defaultConfig = Configuration(
"param1" -> "blah"
)
val otherConfig = Configuration(
"param2" -> "blah"
)
val appConfig = defaultConfig.include(otherConfig)
}
And then at run time we can include them or not
val appConfig = Configuration.load(pathToConfig) include DefaultConfigs.appConfig
What you are looking for is basically what some call a "dynamic mixin". This is not supported out of the box by scala.
Someone developed a compiler plugin to support this: http://www.artima.com/weblogs/viewpost.jsp?thread=275588
However it's a bit old and the project does not seem active anymore.
A better alternative would be to implement this feature using scala macros (requires scala 2.10, which has no stable release yet).
All of this is probably overkill in your case though. Until some well tested library is available, I would advise to just create the proxy by hand (however dull that may look):
trait ConfigProxy extends Config {
def impl: Config
// Forward to the inner config
def root: ConfigObject = impl.root
def origin: ConfigOrigin = impl.origin
//... and so on
}
val conf:Config = //get config object from somewhere
val myConf: AppConfig = new AppConfig with ConfigProxy {
val impl = conf
val param1:String = "foo"
val param2:String = "bar"
}
How about using a combination of Dynamic and Reflection. Dynamic to handle your convenience methods and reflection to handle the methods on config.
Here's a stab:
class ConfigDynamic(val config: Config) extends Dynamic {
def selectDynamic(name: String)= {
name match {
case "field1" =>
config.getString("field1")
case x # _ =>
// overly simplified here
val meth = configClass.getDeclaredMethod(x)
meth.invoke(config)
}
}
}

DB Plugin is not registered in Play 2.0

I just started working with play, and I modified the way I'm doing a SQL read and I'm now getting the following error:
[Exception: DB plugin is not registered.]
The code I have for this class is:
package models
import play.api.db._
import play.api.Play.current
import anorm._
case class Housing(id: Long, rent: String, address: String, street0: String, street1: String, neighbourhood: String)
object Housing {
def all(): List[Housing] = DB.withConnection { implicit c =>
SQL("select * from housing")().map { row =>
Housing(row[Long]("id"), row[String]("rent"), row[String]("address"), row[String]("street0"),
row[String]("street1"), row[String]("neighbourhood"))
}.toList
}
def create(rent: String, address: String, street0: String, street1: String, neighbourhood: String) {}
def delete(id: Long) {}
}
I'm not sure this is even the best way to do this, but using the ~ chain seemed like I'd just end up duplicating a bunch of stuff.
Turns out that somehow in the application.conf the line:
dbplugin=disabled
had arisen. Not sure, I know I didn't put it in there, but commenting it out and fixing the remaining config errors in the JDBC Url fixed the problem!
Just make sure you provide the database configuration. For example, if you are using Play Framework's tutorial, uncomment this section.
# Database configuration
# ~~~~~
# You can declare as many datasources as you want.
# By convention, the default datasource is named `default`
#
# db.default.driver=org.h2.Driver
# db.default.url="jdbc:h2:mem:play"
# db.default.user=sa
# db.default.password=""**
For more information, see Play Framework Database Configuration