Play framework overriding `application.conf` values based on environment - scala

Play 2.6.x Scala
I have a default application.conf within the folder {project}/conf/ but I'd like to override some values depending on the environment by passing in the respective file as command-line arguments (as detailed in the docs):
sbt run -Dconfig.file=/conf/qa.conf or sbt run -Dconfig.resource=qa.conf
But I'm not able to get play to pick up the overrides. Here's my file directory:
application
|- playApp1
|- playApp2
|-- conf
|-- application.conf
|-- qa.conf
My build.sbt makes playApp2 the default project on load. And I have confirmed that the defulat application.conf is working -- just the override is not.
Thanks for any ideas!
--
Update
Here are the HOCON files play uses. application.conf
platform {
scheme = "http"
host = "localhost:8080"
}
and the overrides as provided in qa.conf
include "application.conf"
platform {
scheme = "https"
host = "ea311.34.com"
}

Your question is about HOCON, in case you did not realize it.
Without seeing your application.conf I can only provide a generic answer. Here is an example of providing a default value for akka.log-config-on-start, which will be overridden by a Java system property or an environment variable called CONFIG_DUMP, if defined:
akka {
log-config-on-start = false
log-config-on-start = ${?CONFIG_DUMP}
}
This feature of HOCON is documented here.

This works if you provide the command line argument first
sbt -Dconfig.resource=qa.conf run

Related

SBT - How can I add/modify values to application.conf file based on an external source

I read that SBT has functionality to generate source code and resource files.
In my case I want to add/modify a field in an application.conf file during compilation/packaging of the project (leaving the others in place)
For instance my application.conf file has something like:
A {
B = "Some Value"
C = "Some value to be modified"
}
I would like in the SBT to read an external file and change or add the value of A.B or A.C
So if it is possible to do something along the lines of:
build.sbt
lazy val myProject = project.in(file('myproject')
// pseudo code - How do I do this?
.sourceGenerators in Compile += "Read file /path/to/external/file and add or replace the value of application.conf A.B = some external value"
You can replace the values with environment variable values provided while compiling / building your project. For that you'd have to
A {
B = "Some Value"
B = ${?B_ENV}
C = "Some value to be modified"
C = ${?C_ENV}
}
Where B_ENV and C_ENV are the environment variables you set in your terminal either before build or within the build command (before it)
$ B_ENV=1 C_ENV=2 sbt run
Source: https://www.playframework.com/documentation/2.6.x/ProductionConfiguration#using-environment-variables
In this case you can do without sbt and this approach would also work with maven or cradle.
The *.conf support orignates from typesafe config (https://github.com/lightbend/config).
There is a feature to get environment variables to be used in the configuration which should be a good fit to solve the problem.
There are two approaches I would suggest to use
1.) Fail on missing configuration
If configuration of this vallue is important and to prevent the deplyment of misconfigurated application the startup should fail on missing environment variables.
in application.conf
key=${TEST} // expects "TEST" to be set, fails otherwise
2.) Hardcoded value with override
If there is a sensible default behaviour that only in some circumstances should be changed.
in application.conf
key="test" // hardcoded key
key=${?TEST} // override "key" with 3nv "$TEST" value, when it is given

No configuration setting found for key typesafe config

Im trying to implement a configuration tool typesafehub/config
im using this code
val conf = ConfigFactory.load()
val url = conf.getString("add.prefix") + id + "/?" + conf.getString("add.token")
And the location of the property file is /src/main/resources/application.conf
But for some reason i'm receiving
com.typesafe.config.ConfigException$Missing: No configuration setting found for key 'add'
File content
add {
token = "access_token=6235uhC9kG05ulDtG8DJDA"
prefix = "https://graph.facebook.com/v2.2/"
limit = "&limit=250"
comments="?pretty=0&limit=250&access_token=69kG05ulDtG8DJDA&filter=stream"
feed="/feed?limit=200&access_token=623501EuhC9kG05ulDtG8DJDA&pretty=0"
}
Everything looks configured correctly ?? do i missed something .
thanks,
miki
The error message is telling you that whatever configuration got read, it didn't include a top level setting named add. The ConfigFactory.load function will attempt to load the configuration from a variety of places. By default it will look for a file named application with a suffix of .conf or .json. It looks for that file as a Java resource on your class path. However, various system properties will override this default behavior.
So, it is likely that what you missed is one of these:
Is it possible that src/main/resources is not on your class path?
Are the config.file, config.resource or config.url properties set?
Is your application.conf file empty?
Do you have an application.conf that would be found earlier in your class path?
Is the key: add defined in the application.conf?
Are you using an IDE or sbt?
I had a similar problem while using Eclipse. It simply did not find the application.conf file at first and later on failed to notice edits.
However, once I ran my program via sbt, all worked just fine, including Eclipse. So, I added 'main/resources' to the libraries (Project -> Properties -> Java Build Path -> Libraries", "add class folder"). That might help you as well.
Place your application.conf in the src folder and it should work
I ran into this issue inside a Specs2 test that was driven by SBT. It turned out that the issue was caused by https://github.com/etorreborre/specs2/issues/556. In that case, the Thread's contextClassLoader wasn't using the correct classloader. If you run into a similar error, there are other versions of ConfigFactory.load() that allow you to pass the current class's ClassLoader instead. If you're using Specs2 and you're seeing this issue, use a version <= 3.8.6 or >= 4.0.1.
Check you path. In my case I got the same issue, having application.conf placed in src/main/resources/configuration/common/application.conf
Incorrect:
val conf = ConfigFactory.load(s"/configuration/common/application.conf")
Correct
val conf = ConfigFactory.load(s"configuration/common/application.conf")
it turned out to be a silly mistake i made.
Following that, i does not matter if you use ":" or "=" in .conf file.
Getting the value from example:
server{
proc {
max = "600"
}
}
conf.getString("server.proc.max")
Even you can have the following conf:
proc {
max = "600"
}
proc {
main = "60000"
}
conf.getString("proc.max") //prints 600
conf.getString("proc.min") //prints 60000
I ran into this doing a getString on an integer in my configuration file.
I ran into exactly the same problem and the solution was to replace = with : in the application.conf. Try with the following content in your application.conf:
add {
token: "access_token=6235uhC9kG05ulDtG8DJDA"
prefix: "https://graph.facebook.com/v2.2/"
limit: "&limit=250"
comments: "?pretty=0&limit=250&access_token=69kG05ulDtG8DJDA&filter=stream"
feed: "/feed?limit=200&access_token=623501EuhC9kG05ulDtG8DJDA&pretty=0"
}
Strangely, IntelliJ doesn't detect any formatting or syntax error when using = for me.
in my case it was a stupid mistake,
i m change file name from "application.config" to "application.conf" and its works .
If the application.conf is not getting discovered, you could add this to build.sbt:
unmanagedSourceDirectories in Compile += baseDirectory.value / "main/resources"
Please don't use this to include any custom path. Follow the guidelines and best-practices
As mentioned by others, make sure the application.conf is place in: src/main/resources.
I placed the file there error went away.
Looking at these examples helped me as well:
https://github.com/lightbend/config/tree/main/examples/scala
Use ConfigFactory.parseFile for other locations

Typesafe ConfigFactory load error

I'm trying to load the application.conf that I have under my resources folder using the following line:
val config = ConfigFactory.load(getClass.getResource("application.conf").getPath)
However, it fails and the application.conf is not loaded. There is no error or whatsoever. Any ideas as to what to look for?
ConfigFactory.load takes a resource-name as parameter not a complete path. So it should be enough if you just use "application.conf" as argument, like this:
ConfigFactory.load("application.conf")
As "application.conf is the default name anyways it should actually be enough to just go without arguments:
ConfigFactory.load()
You can make the library produce a nice meaningful error, using this overload of ConfigFactory.load.
val config = ConfigFactory.load(configName,
ConfigParseOptions.defaults().setAllowMissing(false),
ConfigResolveOptions.defaults())
(I was fairly surprised that they didn't make this the default).

Dealing with the same setting in different config files

I was suggested to use 2 or more config files for different modes:
play -Dconfig.file=/conf/dev_application.conf run
Well, how can I deal with some common settings that are the same for different modes? Copy-pasting those is not what I'd use.
I'd use one common config file if it was possible:
play -Dconfig.file=/conf/dev_application.conf /conf/common_application.conf run
As far as I'm concerned, it's not possible. Any idea?
You can import settings in another configuration file via use of an include statement:
# Note that the name of the file being included must be quoted
include "common_application.conf"
This will import all configuration entries from your common configuration. You can then also override the values of any these common keys lower down in your mode-specific config file:
common_application.conf
foo=0
dev_application.conf
# Import common configuration
include "common_application.conf"
# Override common configuration
foo=1
# Dev configuration
bar=0
Good question, look what I found:
GlobalSettings has an onLoadConfig method, so you should be able to do something like this:
import com.typesafe.config.ConfigFactory
override def onLoadConfig(config: Configuration, path: File, classloader: ClassLoader, mode: Mode.Mode): Configuration = {
val richConfig = config ++ Configuration(ConfigFactory.load(s"${mode.toString.toLowerCase}_application.conf"))
super.onLoadConfig(richConfig, path, classloader, mode)
}
This way you can keep your common settings in application.conf and environment-specific settings in prod_application.conf or dev_application.conf (but I did not recheck the values of mode parameter so the names may differ)
EDIT
Yes, I just rechecked the Mode values. Here they are:
val Dev: Value
val Prod: Value
val Test: Value
So using this approach you can name your conf files as dev_application.conf, prod_application.conf and test_application.conf

Akka won't override application.conf with command line parameters

I thought since akka 2.1.4 uses typesafe config, it would just override whatever -D parameter I throw at it via command line, like play framework does. Turns out it doesn't seem to work that way. Just adding a -Dakka.remote.netty.port=2552 doesn't really change anything when added in to the commandline. Do I have to enable anything to make overrides work?
Additional info: I tried using the -D parameters in the Intellij launcher and with java -cp app.jar -Dakka.remote.netty.port=2552 after doing an sbt assembly
Alright, I found out what I was doing wrong. It seems that overrides do not work when only a section of the application.conf is loaded. You cannot override the netty port with -Dakka.remote.netty.port=2553 when you configure your actor system by only loading a specific section from the application.conf like this:
val system = ActorSystem("myActorSystem",ConfigFactory.load.getConfig("client"))
application.conf file:
client{
akka {
log-config-on-start = on
loglevel = "INFO"
actor {
provider = "akka.remote.RemoteActorRefProvider"
include "serialization.conf"
}
remote {
transport = "akka.remote.netty.NettyRemoteTransport"
netty {
hostname = "127.0.0.1"
port = 2552
}
log-sent-messages = off
log-received-messages = off
}
}
}
In this case, although you specify "client" as a subsection to load, you will still have to prepend "client" as a key to your values that will override them by using command line parameters.
Please note though, that the values in the config file will not be prepended by "client" when being loaded that way. So by using -Dclient.akka.remote.netty.port=2553 you can effectively override as you are used to.
I ran into this same issue. I was using 'java -jar project.jar -Dblah=whatever' to run the project which was not overriding the conf file. But, 'java -jar -Dblah=whatever project.jar' did override the conf file.