Grails Environment Aware Framework

One of the awesome features Grails provides is its ability to configure multiple environments to run your application. It comes with three initial environment setups – Development, Test, Production. Besides these environments,  you are able to setup your own custom environment.

The advantage of having multiple environments is the ability to execute your application with different settings, different data sources and other specifics. For example, you may wish to run tests against different database than one used by actual application. Likewise, you may wish not load application data (User, etc) when testing to avoid tests to break. Despite the need, it is accomplished by having different settings in DataSource, Config, BootStrap, BuildConfig and Searchable configurations(grails-app/conf/) each executed per specified environment.

In this post, first I will briefly  cover how to run different environments. Afterwards, I will demonstrate how to declare and configure environments, all of which in relation to practical problem/solution examples from my development process

Run Apps Different Environment

To run app or execute any command(i.e grails war, etc.) in different environment it is needed to specify the environment after ‘grails’ command (i.e. ‘grails [env] run-app’). Here is some examples:

>grails dev run-app
>grails -Dgrails.env=mycustomenv war
>grails test dbm-status

As you can see, the enviroment is specified right after ‘grails’ command. If you don’t specify, it is using the default which is ‘dev’ or Development environment. For your custom environments, you add ‘Dgrails.enf=’ with the name of your custom environment you wish to run

Practical Usages

1. Loading App Data in Bootstrap

At some point, your application will need to have some data like users, roles, etc. One of the ways to import data is to BootStrap(specify in BootStrap.groovy file) the data into your application, however. At the time of running tests, that is in test environment, you probably don’t want to have any data bootstraped, otherwise, it will break your tests(i.e. assert User.count() == 0 fails). So, the solution is to look up environment before importing or not importing data in BootStrap.groovy. Here is example:

class BootStrap {

    def loadDomainDataService

    def init = { servletContext ->

        environments {
            test {
                log.info("starting in Test env.....")
            }
            dbgorm {
                log.info("starting in dbgorm env.....")
            }
            development {
                log.info("starting in dev env.....")
                (User.findAll().empty) ? loadDomainDataService.loadSecurityData() : log.info("Security Data already loaded. Skipped loading...")
                (RentalUnit.findAll().empty) ? loadDomainDataService.loadAppData() : log.info("App Data already loaded. Skipped loading...")

                //don't start app if data not loaded
                assert User.count()
                assert RentalUnit.count()
            }
        }
...

Or Another way:

import grails.util.GrailsUtil
class BootStrap {

    def loadDomainDataService

    def init = { servletContext ->

        switch (GrailsUtil.environment) {
            case "test":
                log.info("starting in Test env.....")
                break
            case "dbgorm":
                log.info("starting in dbgorm env.....")
                break
            default:
                (User.findAll().empty) ? loadDomainDataService.loadSecurityData() : log.info("Security Data already loaded. Skipped loading...")
                (RentalUnit.findAll().empty) ? loadDomainDataService.loadAppData() : log.info("App Data already loaded. Skipped loading...")
                //don't start app if data not loaded
                assert User.count()
                assert RentalUnit.count()
                break
        }
...

In the above code snippets, the different environments configured is highlighted. There are the predefined env such as ‘test’, ‘dev’ and there is our custom env – ‘dbgorm’. For the custom env, there is no need declare it separately prior using them. Just use them in any configuration file like any of the predefined environments. The custom env will be referenced by the name you choose to use in the configuration files. It is case sensitive.

From the above you can see how we prevent the app data be loaded in ‘test’ or ‘dbgorm’ environment but do load data if application run in ‘dev’ and other env . On the side note, to load data itself we use Grails Fixture plugin about which another post

2. Data Source Dependent on Env
Once your application is loaded with data needed for application like Users, Roles, etc than this info is stored in Database. Afterwards, you would like run tests, however, many of the tests will fail because your database contains application data that most likely conflicts with assertions in your tests (i.e. Users.count()==0).

One of the solutions is to use a separate database for running tests. You have your ‘dev’ environment configured for one database and then your ‘test’ environment with another database. The database used by each environment is configure in conf/DataSource.groovy file. Here is an example:

// environment specific settings
environments {
    development {
        dataSource {
            dbCreate = "" // one of 'create', 'create-drop', 'update', 'validate', ''
            driverClassName = "com.mysql.jdbc.Driver"
            url = "jdbc:mysql://localhost/minnehaha"
            username = "root"
            password = "mysql"
        }
    }
test {
        dataSource {
            dbCreate = "" // one of 'create', 'create-drop', 'update', 'validate', ''
            driverClassName = "com.mysql.jdbc.Driver"
            url = "jdbc:mysql://localhost/minnehaha_test"
            username = "root"
            password = "mysql"

        }
    }
...

The highlighted lines shows the each different database configured. So, in our situation, if application is started in ‘dev’ it will be using ‘minnehaha’ database. If  tests run(i.e. grails test-app) then it uses by default the ‘test’ environment, thus, the tests are run against database – ‘minnehaha_test’. This way we are sharing the same application to develop and test while separating the data into two different databases to avoid conflicting between process of testing and other

Interestingly to note, both 'test' and 'dev' databases is managed by Grails Database Migration Plugin, so how would you run migration commands against each different database? Make sure you specify environment when calling any of the Database migration plugin commands. Here is an example:
 >grails dbm-update
 >grails test dbm-update
 The first command will run migrations on default environment which is 'dev' updating database - 'minnehaha' in our case. The second command will run migration script in 'test' env that will update database - 'minnehaha_test' only

3. Migration Script Run at Start Up?… Not Always
We use Grails Database Migration Plugin to manage the database updates, new table creation and database roll backs when needed. Every time the branch is changed or  I received a new code changeset, I was finding myself repetitively running ‘dbm-update’ manually to get dev database in sync. It turns out, this step can be automized, so that when the grails application start, it runs ‘dbm-update’ if there is new migration scripts. For more, see section ‘Run Migration Scripts at Start’ in  the post – Grails Database Migration Plugin

In times adding/updating domain objects, this was a problem. This is because, after making changes in GORM, it was in conflict with current DB structure built by migration scripts at the start up. To solve the problem we created custom environment specifically for GORM development as follows:

Step 1. In the custom env – dbgorm the database will not use migration plugin. Here is conf/DataSource.groovy:

environments {
..
    dbgorm {
        dataSource {
            dbCreate = "create-drop" // one of 'create', 'create-drop', 'update', 'validate', ''
//          url = "jdbc:h2:mem:devDb;MVCC=TRUE"
            driverClassName = "com.mysql.jdbc.Driver"
            url = "jdbc:mysql://localhost/minnehaha_gorm"
            username = "root"
            password = "mysql"

        }
    }
...
}

We accomplish this by configuring our custom environment create and drop database, instead of database migration plugin does it(change ‘dbCreate’ attribute to ‘create-drop’ data source as highlighted in above code).

Step 2. The step 1 takes care of building the database structure, but we still need to prevent migration scripts to run at start up in our custom env – dbgorm. To accomplish, we update conf/Config.groovy as follows:

//To dbm-update On Start
grails.plugin.databasemigration.updateOnStart = true
grails.plugin.databasemigration.updateOnStartFileNames = ["changelog.groovy"]
grails.plugin.databasemigration.changelogLocation = 'grails-app/migrations'

environments {
    dbgorm {
        grails.plugin.databasemigration.updateOnStart = false
    }
}

So, if the application is run in env – dbgorm, we set the updateOnStart to false that way preventing migrations scripts to be run at start up.

With this setup, we change/update domain class or GORM in dbgorm environment. Afterwards, when we satisfied with the changes then we run:

 grails dbm-gorm-diff --add filename.groovy

in dev environment for capturing the changes. This generates migration scripts added in the changeset log(for more, see post ‘Grails Database Migration Plugin‘) and that is run next time app starts in environment other than dbgorm.

How To Know What’s default?

You may wonder what is the default settings if none is specified for your custom environment. For example, if you have custom environment but you don’t specify Data Source for it, what Data Source is it going to use?

Grails configuration files – DataSurce, Config, BuildConfig,etc are using Groovy utility – ConfigSlurper. The ConfigSlurper “allows a default setting to exist in the property file that can be superceded by a setting in the appropriate environments closure”(from ConfigSlurper documentation). In another words, following example of Data Source, there is the default property – ‘datasource’  following with declarations of ‘datasource’ for each environment that supercedes the default one.

//default
dataSource {
 ...
}

// environment specific settings
environments {

	development {
		dataSource {
...

Useful Links

One thought on “Grails Environment Aware Framework

Leave a Reply

Your email address will not be published. Required fields are marked *