08 April 2014

I wanted to play around with Spring Boot the other day and found it pretty easy to get up and running using their Gradle plugin. After only a few minutes, I had a sample Spring-based web application running as a stand-alone process. Next, I thought I would add in New Relic support so I could see some performance stats. Normally, you would install the New Relic agent in your application container or add a javaagent JVM option pointing at the New Relic agent to enable metrics gathering. In my first attempt, I added the javaagent JVM option pointing at the New Relic agent to the DEFAULT_JVM_OPTS variable in my gradlew script and restarted the application. It failed with a weird error about not being able to find some of the classes provided by the New Relic agent after claiming that it started the agent and found the YAML configuration file. It seemed like there was obviously some issue between Gradle and the Java agent. After a few quick minutes searching on the internets, I found that the Spring Boot Gradle plugin supports passing an agent to the Spring Boot process. I made the following change to my build.gradle file:

build.gradle
apply plugin: 'spring-boot'

...

springBoot {
    mainClass = 'com.sample.Application'
    agent = file('/opt/newrelic/newrelic.jar')			(1)
}
1 This is the path to where ever you downloaded and the New Relic agent JAR to that should include your customized YAML configuration file.

I restarted my application using the Spring Boot Gradle task (run) and metrics started flowing! This is great, but it depends on a hard-coded path to the installed New Relic agent (yuck). Because you can do anything with Gradle (slight exaggeration), I figured that with some Gradle-foo, I could include the New Relic agent as a dependency, download it and point to the downloaded file for the javaagent JVM option. After a little playing around, I came up with the following:

build.gradle
apply plugin: 'groovy'
apply plugin: 'spring-boot'

group = 'com.test'
version = '1.0.0-SNAPSHOT'

description = """Test application"""

sourceCompatibility = 1.7
targetCompatibility = 1.7

buildscript {
    repositories {
        mavenLocal()
        mavenCentral()
        maven { url "http://repo.spring.io/snapshot" }
        maven { url "http://repo.spring.io/milestone" }
    }
    dependencies {
        classpath("org.springframework.boot:spring-boot-gradle-plugin:1.0.1.BUILD-SNAPSHOT")
    }
}

configurations {
    newrelic	// Custom configuration to allow for the copying of the New Relic agent at run time.
}

ext {
    groovyVersion='2.2.1'
    newrelicVersion='3.5.1'
    springBootVersion='1.0.1.RELEASE'
    springVersion='4.0.3.RELEASE'
}

repositories {
    mavenLocal()
    mavenCentral()
}

dependencies {
    newrelic "com.newrelic.agent.java:newrelic-agent:${newrelicVersion}"

    compile("org.codehaus.groovy:groovy-all:${groovyVersion}")
    compile("org.springframework:spring-beans:${springVersion}")
    compile("org.springframework:spring-context:${springVersion}")
    compile("org.springframework.boot:spring-boot-starter-web:${springBootVersion}")

    testCompile("org.springframework.boot:spring-boot-starter-test:${springBootVersion}")
}

task copyAgent(type: Copy) {
    from {
        configurations.newrelic				(1)
    }
    into "$buildDir/lib"
    rename ("newrelic-agent-${newrelicVersion}.jar", 'newrelic.jar')
}

springBoot {
    mainClass = 'com.test.sample.Application'
    agent = file("$buildDir/lib/newrelic.jar")		(2)
}

project.tasks.run.dependsOn([copyAgent])			(3)
1 Per the dependencies block, the only file in the newrelic configuration is the agent JAR itself.
2 Tell Spring Boot to use the the downloaded, copied New Relic agent JAR instead of the previously hard-coded value.
3 Make the Spring Boot run task depend on the copy task to ensure that the agent is copied before we attempt to launch the application.

Finally, the last thing that I did was set up a symbolic link from my project’s directory to where my New Relic YAML file resides. The New Relic agent will automatically look for the newrelic.yml file in the same location as the agent JAR or in the current working directory. With this last piece in place, all hard-coded paths are now removed from the equation.

comments powered by Disqus