From the Grails documentation and this question:
For general configuration Grails provides two files:
- grails-app/conf/BuildConfig.groovy
- grails-app/conf/Config.groovy
Both of them use Groovy's ConfigSlurper syntax. The first, BuildConfig.groovy, is for settings that are used when running Grails commands, such as compile, doc, etc. The second file, Config.groovy, is for settings that are used when your application is running. This means that Config.groovy is packaged with your application, but BuildConfig.groovy is not.
And here is an extract about the log4j
framework:
Grails uses its common configuration mechanism to provide the settings for the underlying Log4j log system, so all you have to do is add a log4j setting to the file grails-app/conf/Config.groovy.
I have a project with these two files: grails-app/conf/BuildConfig.groovy
and grails-app/conf/Config.groovy
. The project uses the log4j
utility, so some settings (including a function) are placed in the Config.groovy file. Now, according to the documentation I quoted, it is the right file, since I want to use the logging utilities for running the application, not compiling it.
What's interesting, these settings are used when I run mvn package
on my project - the function inside the log4j
settings is executed.
According to both the documentation and the question this should not be the case.
I know that it is possible to use the grailsApplication
to access these settings:
def recipient = grailsApplication.config.foo.bar.hello
So I searched my project and found some usages of the grailsApplication
, but none related to the log4j settings.
What are other possible reasons of having the log4j settings that are placed in the Config.groovy file used during mvn package
? What am I missing?
Update: the mentioned configuration seems to work when I use mvn package
to build my project for the first time. Next time I run mvn package
, the log4j
configuration from the Config.groovy
file is used. And if I delete the job workspace, it works well again.
There is one important thing worth mentioning. Even though Config.groovy
is used by application at the runtime, it has to be compiled to Java's Config.class
in the first place. When you package your application compiler has to access this file and compile it to the bytecode. Take a look at the listing I've pasted below, it comes from one Grails application I have in my workspace:
ls -l target/classes | awk '{print $8}'
application.properties
BootStrap.class
BootStrap$_closure1.class
BootStrap$_closure2.class
BuildConfig.class
BuildConfig$_run_closure1.class
BuildConfig$_run_closure1_closure2.class
BuildConfig$_run_closure1_closure3.class
BuildConfig$_run_closure1_closure4.class
BuildConfig$_run_closure1_closure5.class
com
Config.class
Config$_run_closure1.class
Config$_run_closure1_closure4.class
Config$_run_closure1_closure4_closure5.class
Config$_run_closure1_closure4_closure5_closure6.class
Config$_run_closure2.class
Config$_run_closure2_closure7.class
Config$_run_closure2_closure8.class
Config$_run_closure3.class
DataSource.class
DataSource$_run_closure1.class
DataSource$_run_closure2.class
DataSource$_run_closure3.class
DataSource$_run_closure3_closure4.class
DataSource$_run_closure3_closure4_closure7.class
DataSource$_run_closure3_closure5.class
DataSource$_run_closure3_closure5_closure8.class
DataSource$_run_closure3_closure6.class
DataSource$_run_closure3_closure6_closure9.class
DataSource$_run_closure3_closure6_closure9_closure10.class
resources.class
resources$_run_closure1.class
UrlMappings.class
UrlMappings$__clinit__closure1.class
UrlMappings$__clinit__closure1_closure2.class
UrlMappings$__clinit__closure1_closure2_closure3.class
You can see that Config.groovy
file was compiled to Config.class
file and all closures that were used inside Config.groovy
were compiled to Java's anonymous classes (e.g. Config$_run_closure1_closure4.class
). That's why if you put some code that executes any logic to Config.groovy
you have to expect it will be compiled and executed since compiled class extends groovy.lang.Script
and it executes body of a Groovy script file. Below you can find what does Groovy.class
file looks like:
javap -l target/classes/Config.class
Compiled from "Config.groovy"
public class Config extends groovy.lang.Script {
public static transient boolean __$stMC;
public static long __timeStamp;
public static long __timeStamp__239_neverHappen1501076781354;
public Config();
LocalVariableTable:
Start Length Slot Name Signature
4 4 0 this LConfig;
public Config(groovy.lang.Binding);
LocalVariableTable:
Start Length Slot Name Signature
4 21 0 this LConfig;
4 21 1 context Lgroovy/lang/Binding;
public static void main(java.lang.String...);
LocalVariableTable:
Start Length Slot Name Signature
0 19 0 args [Ljava/lang/String;
public java.lang.Object run();
LineNumberTable:
line 14: 4
line 17: 43
line 18: 126
line 24: 194
line 26: 233
line 30: 296
line 31: 323
line 38: 376
line 42: 419
line 45: 453
line 63: 473
line 65: 507
line 68: 550
line 70: 595
line 72: 631
line 74: 679
line 77: 724
line 80: 777
line 84: 822
line 86: 867
line 88: 912
line 99: 932
LocalVariableTable:
Start Length Slot Name Signature
0 964 0 this LConfig;
public java.lang.Object this$dist$invoke$3(java.lang.String, java.lang.Object);
LocalVariableTable:
Start Length Slot Name Signature
0 68 0 this LConfig;
0 68 1 name Ljava/lang/String;
0 68 2 args Ljava/lang/Object;
public void this$dist$set$3(java.lang.String, java.lang.Object);
LocalVariableTable:
Start Length Slot Name Signature
0 53 0 this LConfig;
0 53 1 name Ljava/lang/String;
0 53 2 value Ljava/lang/Object;
public java.lang.Object this$dist$get$3(java.lang.String);
LocalVariableTable:
Start Length Slot Name Signature
0 46 0 this LConfig;
0 46 1 name Ljava/lang/String;
protected groovy.lang.MetaClass $getStaticMetaClass();
public static void __$swapInit();
static {};
public int super$1$hashCode();
public void super$3$printf(java.lang.String, java.lang.Object);
public void super$3$printf(java.lang.String, java.lang.Object[]);
public void super$3$setProperty(java.lang.String, java.lang.Object);
public boolean super$1$equals(java.lang.Object);
public void super$1$finalize();
public groovy.lang.Binding super$3$getBinding();
public void super$3$print(java.lang.Object);
public void super$3$setBinding(groovy.lang.Binding);
public java.lang.Object super$3$evaluate(java.io.File);
public java.lang.String super$1$toString();
public java.lang.Object super$3$evaluate(java.lang.String);
public void super$2$setMetaClass(groovy.lang.MetaClass);
public void super$1$notify();
public java.lang.Object super$3$invokeMethod(java.lang.String, java.lang.Object);
public java.lang.Object super$1$clone();
public void super$1$wait(long, int);
public void super$1$wait(long);
public void super$1$wait();
public groovy.lang.MetaClass super$2$getMetaClass();
public java.lang.Class super$1$getClass();
public void super$3$run(java.io.File, java.lang.String[]);
public void super$3$println(java.lang.Object);
public void super$1$notifyAll();
public java.lang.Object super$3$getProperty(java.lang.String);
public void super$3$println();
static java.lang.Class class$(java.lang.String);
}
Now to understand why Config.groovy
is executed we need to dig into what happens when grails package
is being executed. Running grails package
command makes _GrailsPackage.groovy
script from grails-core being executed. packageApp
target calls GrailsProjectPackager.packageApplication()
https://github.com/grails/grails-core/blob/2.4.x/grails-scripts/src/main/scripts/_GrailsPackage.groovy#L48
This method calls createConfig()
helper class:
https://github.com/grails/grails-core/blob/2.4.x/grails-project-api/src/main/groovy/org/codehaus/groovy/grails/project/packaging/GrailsProjectPackager.groovy#L274
In createConfig()
method implementation we can find ConfigSlurper.parse(script)
execution:
https://github.com/grails/grails-core/blob/2.4.x/grails-project-api/src/main/groovy/org/codehaus/groovy/grails/project/packaging/GrailsProjectPackager.groovy#L345
This is a Groovy class that parses a script and in the end it calls script.run()
on the script that was parsed. It's easy to find out with a debugger - check my video where I show how to do it in this particular example: https://www.youtube.com/watch?v=s2PN6TjFjUI
I hope it helps.
mvn package
will run other maven phases and that includes the test
phase. Grails integration tests will bootstrap your full app so the Config.groovy file will be parsed.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With