'How to deploy Grails (Tomcat 7) application to Cloud Foundry' post illustration

How to deploy Grails (Tomcat 7) application to Cloud Foundry

avatar

I had an issue with deploying my Grails project to Cloud Foundry with using cloudfoundry-grails-plugin. My application was using Tomcat 7 unlike Cloud Foundry, which use Tomcat 6. In other words, to have a Grails application with Tomcat 7 run, I had to deploy the entire Tomcat 7 directory, but before it should be slightly reconfigured.

Here's a link with a great tutorial on how to get Tomcat 7 up and running on Cloud Foundry.

After doing those tricky steps, I had to configure mysql.

Cloud Foundry doesn't give you information about your available database services. Instead it has an environment variable named VCAP_SERVICES, which contains all needed information on how to connect to them from your application. I.e. you have to teach your application how to connect to Cloud Foundry services.

I decided to create util class which would gather database configuration for me, and append it to DataSource.groovy, here it goes:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
class CloudFoundryUtils {
    private static final def log = LogFactory.getLog(CloudFoundryUtils)

    static ConfigObject getDataSource(Map args) {
        final String VCAP_SERVICES = System.getenv('VCAP_SERVICES')

        if (!VCAP_SERVICES) {
            log.error("No environment variable VCAP_SERVICES present!")
      	    return
        }

        def servicesConfig = JSON.parse(VCAP_SERVICES)
        def mysqlConfig = servicesConfig."${args.type}"
        ConfigObject dataSource = new ConfigObject()
        def mysqlInstance = mysqlConfig.find { it.name == args.service }

        def credentials = mysqlInstance.credentials
        dataSource.username = credentials.username
        dataSource.password = credentials.password
        dataSource.url = "jdbc:mysql://${credentials.host}:${credentials.port}" +
            "/${credentials.name}?autoReconnect=true&useUnicode=true&characterEncoding=utf8"
        dataSource.driverClassName = "com.mysql.jdbc.Driver"
        dataSource.removeAbandoned = true
        dataSource.removeAbandonedTimeout = 300 // 5 minutes
        dataSource.testOnBorrow = true
        dataSource.validationQuery = '/* ping */ SELECT 1'
        log.info("Applied dataSource configuration to DataSource.groovy")
        return dataSource
    }
}

Call to util class from DataSource.groovy:

1
2
3
cf {
  dataSource = CloudFoundryUtils.getDataSource(service: 'mysql-opht', type: 'mysql-5.1')
}

After doing the previous steps, you're done with configuring, but I've also written a script, which packages war file and copies it's content to tomcat/webapps/ROOT directory. (this is optional, as you can 'war' your project and extract all packaged files to tomcat/webapps/ROOT manually):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
/** ExtractArtefact.groovy (script)
*	run: -Dgrails.env=cf extract-artefact
**/

includeTargets << grailsScript("_GrailsWar")
includeTargets << grailsScript("_GrailsEvents")


target(extract: "Copy war to tomcat directory and deploy to cloudfoundry") {
    depends(war)

    def metadata = grails.util.Metadata.getCurrent()

    event("StatusUpdate", ["Cleaning deployment directory"])
    ant.delete(dir: "${basedir}/tomcat/webapps/ROOT")
    event("StatusUpdate", ["Extracting artefact to deploy directory"])
    def src = "${basedir}/target/${metadata."app.name"}-${metadata."app.version"}.war"
    def dest = "${basedir}/tomcat/webapps/ROOT"
    ant.unwar(src: src, dest: dest)
    event("StatusFinal", ["Successfully deployed to target directory"])
}

setDefaultTarget(extract)

My Tomcat settles in base directory of the project, but you can change the destination url to be an absolute path anywhere in your system.

When script is executed (or you had extracted content of war file to ROOT directory), the last and final step is to execute "vmc push" command from tomcat directory.

Hope this would be helpful!

If you're looking for a developer or considering starting a new project,
we are always ready to help!